Plugin-framework provider covering projects, environments, applications, Compose stacks, managed databases, domains, mounts, ports, redirects, basic auth, registries, SSH keys, certificates and backup destinations, over Dokploy's tRPC-over-REST API. The shim package exposes the provider to other Go modules, which is how pulumi-dokploy bridges it.
88 lines
3.2 KiB
Go
88 lines
3.2 KiB
Go
package provider
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
)
|
|
|
|
type projectModel struct {
|
|
ID types.String `tfsdk:"id" dokploy:"projectId,id"`
|
|
Name types.String `tfsdk:"name" dokploy:"name"`
|
|
Description types.String `tfsdk:"description" dokploy:"description,nullable"`
|
|
Env types.String `tfsdk:"env" dokploy:"env"`
|
|
CreatedAt types.String `tfsdk:"created_at" dokploy:"createdAt,ro"`
|
|
OrgID types.String `tfsdk:"organization_id" dokploy:"organizationId,ro"`
|
|
|
|
// Derived in PostRead from the nested `environments` array.
|
|
DefaultEnvironmentID types.String `tfsdk:"default_environment_id"`
|
|
}
|
|
|
|
func projectResource() ResourceSpec {
|
|
return ResourceSpec{
|
|
Name: "project",
|
|
CreateProc: "project.create",
|
|
ReadProc: "project.one",
|
|
UpdateProc: "project.update",
|
|
DeleteProc: "project.remove",
|
|
CreateResponseKey: "project",
|
|
NewModel: func() any { return &projectModel{} },
|
|
PostRead: projectPostRead,
|
|
Schema: schema.Schema{
|
|
MarkdownDescription: "A Dokploy project: the top-level container for environments and services.\n\n" +
|
|
"Creating a project automatically creates a default `production` environment. Its ID is exposed as " +
|
|
"`default_environment_id`, so services can be attached without declaring a separate " +
|
|
"`dokploy_environment` resource.",
|
|
Attributes: map[string]schema.Attribute{
|
|
"id": computedID("Unique project identifier."),
|
|
"name": requiredString("Display name of the project."),
|
|
"description": optionalString("Free-form description."),
|
|
"env": optionalComputedString("Project-wide environment variables in `KEY=value` format, one per " +
|
|
"line. These are shared with every service in the project."),
|
|
"created_at": computedString("RFC 3339 timestamp of when the project was created."),
|
|
"organization_id": computedString("Organization that owns the project."),
|
|
"default_environment_id": computedString("ID of the `production` environment that Dokploy creates " +
|
|
"automatically with the project."),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// projectPostRead extracts the default environment from a `project.one`
|
|
// response so it can be referenced directly.
|
|
func projectPostRead(raw json.RawMessage, model any) error {
|
|
project, ok := model.(*projectModel)
|
|
if !ok {
|
|
return fmt.Errorf("expected *projectModel, got %T", model)
|
|
}
|
|
|
|
var payload struct {
|
|
Environments []struct {
|
|
EnvironmentID string `json:"environmentId"`
|
|
Name string `json:"name"`
|
|
IsDefault bool `json:"isDefault"`
|
|
} `json:"environments"`
|
|
}
|
|
if err := json.Unmarshal(raw, &payload); err != nil {
|
|
return fmt.Errorf("decoding project environments: %w", err)
|
|
}
|
|
|
|
project.DefaultEnvironmentID = types.StringNull()
|
|
for _, env := range payload.Environments {
|
|
if env.IsDefault {
|
|
project.DefaultEnvironmentID = types.StringValue(env.EnvironmentID)
|
|
return nil
|
|
}
|
|
}
|
|
// Fall back to a conventionally named environment if none is flagged.
|
|
for _, env := range payload.Environments {
|
|
if env.Name == "production" {
|
|
project.DefaultEnvironmentID = types.StringValue(env.EnvironmentID)
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
}
|