Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions examples/resources/coolify_application/deploy_key.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Example: Private Deploy Key Application

resource "coolify_application" "deploy_key_example" {
source_type = "private-deploy-key"

project_uuid = "your-project-uuid"
server_uuid = "your-server-uuid"
environment_name = "production"

private_key_uuid = "your-private-key-uuid"
git_repository = "[email protected]:your-org/your-private-repo.git"
git_branch = "main"
build_pack = "nixpacks"
ports_exposes = "80"

name = "Deploy Key Example"
description = "Application from private repository via deploy key"
}

25 changes: 25 additions & 0 deletions examples/resources/coolify_application/dockercompose.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Example: Docker Compose Application

resource "coolify_application" "dockercompose_example" {
source_type = "dockercompose"

project_uuid = "your-project-uuid"
server_uuid = "your-server-uuid"
environment_name = "production"

docker_compose_raw = <<EOF
services:
web:
image: nginx:alpine
ports:
- "80:80"
db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: example
EOF

name = "Docker Compose Example"
description = "Application from Docker Compose"
}

23 changes: 23 additions & 0 deletions examples/resources/coolify_application/dockerfile.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Example: Dockerfile Application

resource "coolify_application" "dockerfile_example" {
source_type = "dockerfile"

project_uuid = "your-project-uuid"
server_uuid = "your-server-uuid"
environment_name = "production"

dockerfile = <<EOF
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
EOF

name = "Dockerfile Example"
description = "Application built from Dockerfile"
}

17 changes: 17 additions & 0 deletions examples/resources/coolify_application/dockerimage.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Example: Docker Image Application

resource "coolify_application" "dockerimage_example" {
source_type = "dockerimage"

project_uuid = "your-project-uuid"
server_uuid = "your-server-uuid"
environment_name = "production"

docker_registry_image_name = "nginx"
docker_registry_image_tag = "alpine"
ports_exposes = "80"

name = "Docker Image Example"
description = "Application from Docker image"
}

19 changes: 19 additions & 0 deletions examples/resources/coolify_application/github_app.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Example: Private GitHub App Application

resource "coolify_application" "github_app_example" {
source_type = "private-github-app"

project_uuid = "your-project-uuid"
server_uuid = "your-server-uuid"
environment_name = "production"

github_app_uuid = "your-github-app-uuid"
git_repository = "https://github.com/your-org/your-private-repo"
git_branch = "main"
build_pack = "nixpacks"
ports_exposes = "80"

name = "GitHub App Example"
description = "Application from private GitHub repository via GitHub App"
}

13 changes: 13 additions & 0 deletions examples/resources/coolify_application/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
# Import script for coolify_application

# Usage: ./import.sh <application-uuid>
# Example: ./import.sh rg8ks8c

if [ -z "$1" ]; then
echo "Usage: $0 <application-uuid>"
exit 1
fi

terraform import coolify_application.example "$1"

18 changes: 18 additions & 0 deletions examples/resources/coolify_application/public.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Example: Public Git Repository Application

resource "coolify_application" "public_example" {
source_type = "public"

project_uuid = "your-project-uuid"
server_uuid = "your-server-uuid"
environment_name = "production"

git_repository = "https://github.com/coollabsio/coolify"
git_branch = "main"
build_pack = "nixpacks"
ports_exposes = "80"

name = "Public App Example"
description = "Application from public Git repository"
}

15 changes: 15 additions & 0 deletions internal/expand/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ func String(value types.String) *string {
return value.ValueStringPointer()
}

// StringOrNil returns nil if the string is null, unknown, or empty.
// This is useful for fields that should not be sent to the API if empty.
func StringOrNil(value types.String) *string {
if value.IsNull() || value.IsUnknown() {
return nil
}

str := value.ValueString()
if str == "" {
return nil
}

return &str
}

func RequiredString(value types.String) string {
if value.IsNull() || value.IsUnknown() {
return ""
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ func (p *CoolifyProvider) Resources(ctx context.Context) []func() resource.Resou
private_key.NewPrivateKeyResource,
service.NewServerResource,
service.NewProjectResource,
service.NewApplicationResource,
service.NewApplicationEnvsResource,
service.NewServiceEnvsResource,
service.NewPostgresqlDatabaseResource,
Expand Down
51 changes: 38 additions & 13 deletions internal/service/application_envs_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,10 @@ func (r *applicationEnvsResource) Create(ctx context.Context, req resource.Creat
uuid := plan.Uuid.ValueString()
for i, env := range plan.Env {
createResp, err := r.client.CreateEnvByApplicationUuidWithResponse(ctx, uuid, api.CreateEnvByApplicationUuidJSONRequestBody{
IsBuildTime: env.IsBuildTime.ValueBoolPointer(),
IsLiteral: env.IsLiteral.ValueBoolPointer(),
IsPreview: env.IsPreview.ValueBoolPointer(),
Key: env.Key.ValueStringPointer(),
Value: env.Value.ValueStringPointer(),
IsLiteral: env.IsLiteral.ValueBoolPointer(),
IsPreview: env.IsPreview.ValueBoolPointer(),
Key: env.Key.ValueStringPointer(),
Value: env.Value.ValueStringPointer(),
})

if err != nil {
Expand All @@ -125,10 +124,43 @@ func (r *applicationEnvsResource) Create(ctx context.Context, req resource.Creat
return
}

// Set the UUID from the response
plan.Env[i].Uuid = types.StringPointerValue(createResp.JSON201.Uuid)
}

var bulkUpdateEnvs = []updateEnvsByApplicationUuidJSONRequestBodyItem{}
for _, env := range plan.Env {
bulkUpdateEnvs = append(bulkUpdateEnvs, updateEnvsByApplicationUuidJSONRequestBodyItem{
IsBuildTime: env.IsBuildTime.ValueBoolPointer(),
IsLiteral: env.IsLiteral.ValueBoolPointer(),
IsMultiline: env.IsMultiline.ValueBoolPointer(),
IsPreview: env.IsPreview.ValueBoolPointer(),
IsShownOnce: env.IsShownOnce.ValueBoolPointer(),
Key: env.Key.ValueStringPointer(),
Value: env.Value.ValueStringPointer(),
})
}

if len(bulkUpdateEnvs) > 0 {
updateResp, err := r.client.UpdateEnvsByApplicationUuidWithResponse(ctx, uuid, api.UpdateEnvsByApplicationUuidJSONRequestBody{
Data: bulkUpdateEnvs,
})

if err != nil {
resp.Diagnostics.AddError(
fmt.Sprintf("Error updating application envs after creation: uuid=%s", uuid),
err.Error(),
)
return
}

if updateResp.StatusCode() != http.StatusCreated {
resp.Diagnostics.AddError(
"Unexpected HTTP status code updating application envs after creation",
fmt.Sprintf("Received %s updating application envs: uuid=%s. Details: %s", updateResp.Status(), uuid, updateResp.Body))
return
}
}

data, _ := r.readFromAPI(ctx, &resp.Diagnostics, uuid)
data.Env = r.filterRelevantEnvs(plan.Env, data.Env)

Expand Down Expand Up @@ -178,26 +210,22 @@ func (r *applicationEnvsResource) Update(ctx context.Context, req resource.Updat

uuid := plan.Uuid.ValueString()

// Update API call logic
tflog.Debug(ctx, "Updating application envs", map[string]interface{}{
"uuid": uuid,
})

// Create a map of current state envs for fast lookup
stateEnvs := make(map[string]resource_application_envs.ApplicationEnvsModel)
for _, env := range state.Env {
key := fmt.Sprintf("%s-%t", env.Key.ValueString(), env.IsPreview.ValueBool())
stateEnvs[key] = env
}

// Create a map of plan envs for fast lookup
planEnvs := make(map[string]resource_application_envs.ApplicationEnvsModel)
for _, env := range plan.Env {
key := fmt.Sprintf("%s-%t", env.Key.ValueString(), env.IsPreview.ValueBool())
planEnvs[key] = env
}

// Delete envs that are in state but not in plan
for key, env := range stateEnvs {
if _, exists := planEnvs[key]; !exists {
_, err := r.client.DeleteEnvByApplicationUuidWithResponse(ctx, uuid, env.Uuid.ValueString())
Expand Down Expand Up @@ -257,7 +285,6 @@ func (r *applicationEnvsResource) Update(ctx context.Context, req resource.Updat
func (r *applicationEnvsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state applicationEnvsResourceModel

// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
Expand All @@ -276,8 +303,6 @@ func (r *applicationEnvsResource) ImportState(ctx context.Context, req resource.
resource.ImportStatePassthroughID(ctx, path.Root("uuid"), req, resp)
}

// MARK: Helper Functions

func (r *applicationEnvsResource) filterRelevantEnvs(
stateEnvs []resource_application_envs.ApplicationEnvsModel,
apiEnvs []resource_application_envs.ApplicationEnvsModel,
Expand Down
Loading
Loading