A Terraform provider for Dokploy
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.
This commit is contained in:
190
internal/provider/resource_account.go
Normal file
190
internal/provider/resource_account.go
Normal file
@@ -0,0 +1,190 @@
|
||||
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."),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user