package provider import ( "context" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/maxvojtkov/terraform-provider-dokploy/internal/client" ) // ------------------------------------------------------------------ Registry type registryModel struct { ID types.String `tfsdk:"id" dokploy:"registryId,id"` RegistryName types.String `tfsdk:"registry_name" dokploy:"registryName"` Username types.String `tfsdk:"username" dokploy:"username"` Password types.String `tfsdk:"password" dokploy:"password"` RegistryURL types.String `tfsdk:"registry_url" dokploy:"registryUrl"` RegistryType types.String `tfsdk:"registry_type" dokploy:"registryType"` ImagePrefix types.String `tfsdk:"image_prefix" dokploy:"imagePrefix,nullable"` ServerID types.String `tfsdk:"server_id" dokploy:"serverId,create"` CreatedAt types.String `tfsdk:"created_at" dokploy:"createdAt,ro"` } func registryResource() ResourceSpec { return ResourceSpec{ Name: "registry", CreateProc: "registry.create", ReadProc: "registry.one", UpdateProc: "registry.update", DeleteProc: "registry.remove", NewModel: func() any { return ®istryModel{} }, Schema: schema.Schema{ MarkdownDescription: "A container registry that Dokploy pushes built images to and pulls them from.", Attributes: map[string]schema.Attribute{ "id": computedID("Unique registry identifier."), "registry_name": requiredString("Display name of the registry."), "username": requiredString("Username used to authenticate to the registry."), "password": sensitiveString("Password or access token used to authenticate.", true), "registry_url": requiredString("Registry hostname, for example `ghcr.io`."), "registry_type": enumStringWithDefault("Registry kind. Dokploy currently accepts only `cloud`.", []string{"cloud"}, "cloud"), "image_prefix": optionalString("Prefix prepended to pushed image names, for example an " + "organization or namespace."), "server_id": optionalReplaceString("Server this registry is scoped to."), "created_at": computedString("RFC 3339 timestamp of when the registry was created."), }, }, } } // ------------------------------------------------------------------- SSH key type sshKeyModel struct { ID types.String `tfsdk:"id" dokploy:"sshKeyId,id"` Name types.String `tfsdk:"name" dokploy:"name"` Description types.String `tfsdk:"description" dokploy:"description,nullable"` PublicKey types.String `tfsdk:"public_key" dokploy:"publicKey,create"` PrivateKey types.String `tfsdk:"private_key" dokploy:"privateKey,create"` LastUsedAt types.String `tfsdk:"last_used_at" dokploy:"lastUsedAt,ro"` CreatedAt types.String `tfsdk:"created_at" dokploy:"createdAt,ro"` } func sshKeyResource() ResourceSpec { return ResourceSpec{ Name: "ssh_key", NeedsOrganizationID: true, CreateProc: "sshKey.create", ReadProc: "sshKey.one", UpdateProc: "sshKey.update", DeleteProc: "sshKey.remove", NewModel: func() any { return &sshKeyModel{} }, // `sshKey.create` returns nothing, so the new ID is discovered by // diffing the key list around the call. ListIDs: func(ctx context.Context, api *client.Client, _ any) (map[string]struct{}, error) { raw, err := api.Query(ctx, "sshKey.all", nil) if err != nil { return nil, err } return collectIDs(raw, "sshKeyId") }, Schema: schema.Schema{ MarkdownDescription: "An SSH key pair Dokploy uses to clone private Git repositories and to reach " + "remote servers.\n\n" + "~> The private key is stored in Terraform state. Use a state backend with encryption at rest.\n\n" + "~> Dokploy's update endpoint only accepts `name` and `description`. Changing either key forces " + "a new resource.", Attributes: map[string]schema.Attribute{ "id": computedID("Unique SSH key identifier."), "name": requiredString("Display name of the key pair."), "description": optionalString("Free-form description."), "public_key": requiredReplaceString("OpenSSH-formatted public key."), "private_key": schema.StringAttribute{ Required: true, Sensitive: true, MarkdownDescription: "PEM-encoded private key.", PlanModifiers: requiresReplaceString(), }, "last_used_at": computedString("RFC 3339 timestamp of when the key was last used, if ever."), "created_at": computedString("RFC 3339 timestamp of when the key was created."), }, }, } } // --------------------------------------------------------------- Certificate type certificateModel struct { ID types.String `tfsdk:"id" dokploy:"certificateId,id"` Name types.String `tfsdk:"name" dokploy:"name"` CertificateData types.String `tfsdk:"certificate_data" dokploy:"certificateData"` PrivateKey types.String `tfsdk:"private_key" dokploy:"privateKey"` AutoRenew types.Bool `tfsdk:"auto_renew" dokploy:"autoRenew,create"` ServerID types.String `tfsdk:"server_id" dokploy:"serverId,create"` CertificatePath types.String `tfsdk:"certificate_path" dokploy:"certificatePath,ro"` } func certificateResource() ResourceSpec { return ResourceSpec{ Name: "certificate", NeedsOrganizationID: true, CreateProc: "certificates.create", ReadProc: "certificates.one", UpdateProc: "certificates.update", DeleteProc: "certificates.remove", NewModel: func() any { return &certificateModel{} }, Schema: schema.Schema{ MarkdownDescription: "A TLS certificate uploaded to Dokploy, for domains that use " + "`certificate_type = \"custom\"`.\n\n" + "~> The private key is stored in Terraform state. Use a state backend with encryption at rest.", Attributes: map[string]schema.Attribute{ "id": computedID("Unique certificate identifier."), "name": requiredString("Display name of the certificate."), "certificate_data": requiredString("PEM-encoded certificate chain."), "private_key": sensitiveString("PEM-encoded private key.", true), "auto_renew": optionalComputedBool("Whether Dokploy should renew this certificate automatically."), "server_id": optionalReplaceString("Server this certificate is installed on."), "certificate_path": computedString("Path where Dokploy writes the certificate on disk."), }, }, } } // --------------------------------------------------------------- Destination type destinationModel struct { ID types.String `tfsdk:"id" dokploy:"destinationId,id"` Name types.String `tfsdk:"name" dokploy:"name"` // `provider` is a reserved root attribute name in Terraform, so the // attribute is exposed as `provider_name`. Provider types.String `tfsdk:"provider_name" dokploy:"provider,nullable"` AccessKey types.String `tfsdk:"access_key" dokploy:"accessKey"` SecretAccessKey types.String `tfsdk:"secret_access_key" dokploy:"secretAccessKey"` Bucket types.String `tfsdk:"bucket" dokploy:"bucket"` Region types.String `tfsdk:"region" dokploy:"region"` Endpoint types.String `tfsdk:"endpoint" dokploy:"endpoint"` AdditionalFlags types.List `tfsdk:"additional_flags" dokploy:"additionalFlags,nullable"` ServerID types.String `tfsdk:"server_id" dokploy:"serverId"` CreatedAt types.String `tfsdk:"created_at" dokploy:"createdAt,ro"` } func destinationResource() ResourceSpec { return ResourceSpec{ Name: "destination", CreateProc: "destination.create", ReadProc: "destination.one", UpdateProc: "destination.update", DeleteProc: "destination.remove", NewModel: func() any { return &destinationModel{} }, Schema: schema.Schema{ MarkdownDescription: "An S3-compatible bucket that Dokploy writes database and volume backups to.\n\n" + "~> The secret access key is stored in Terraform state. Use a state backend with encryption at rest.", Attributes: map[string]schema.Attribute{ "id": computedID("Unique destination identifier."), "name": requiredString("Display name of the destination."), "provider_name": optionalString("Provider label, for example `s3` or `cloudflare`. " + "Named `provider_name` because `provider` is reserved by Terraform."), "access_key": requiredString("S3 access key ID."), "secret_access_key": sensitiveString("S3 secret access key.", true), "bucket": requiredString("Bucket name."), "region": requiredString("Bucket region, for example `us-east-1`."), "endpoint": requiredString("S3 endpoint URL."), "additional_flags": optionalComputedStringList("Extra flags passed to the underlying `rclone` invocation."), "server_id": optionalString("Server this destination is scoped to."), "created_at": computedString("Timestamp of when the destination was created."), }, }, } }