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.
191 lines
8.9 KiB
Go
191 lines
8.9 KiB
Go
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."),
|
|
},
|
|
},
|
|
}
|
|
}
|