Six new resources, all backed by endpoints that did not exist before v0.30.0
and verified end-to-end against a live v0.30.2 instance:
dokploy_network Docker networks, now first-class. Services attach
through network_ids, which is what deprecates
Compose's isolated_deployment upstream.
dokploy_dns_provider Cloudflare or Route53, so adding a domain creates
its DNS record.
dokploy_vault_provider Env values resolved from HashiCorp Vault, Infisical,
AWS, Doppler, Azure or Scaleway at deploy time, so
the secret never lands in Dokploy or in state.
dokploy_schedule Cron jobs in a container, a stack, or on a server.
dokploy_volume_backup Scheduled backups of a named volume — the companion
to a mount that persists.
dokploy_libsql The sixth managed database engine.
libsql.create is the strictest endpoint in the API: eleven keys required to
be present, several only meaningfully null, no generated service name, and it
returns `true` rather than the row. CreateDefaults and ListIDs absorb all
three so the resource behaves like every other database.
Also filled the gaps a field-by-field diff against the live schema turned up:
domain gains `enabled` (the v0.30.0 park-a-domain toggle), compose gains
create_env_file, icon and service_networks, application gains icon and
preview_require_collaborator_permissions, and mounts accept libsql.
DNS and vault credentials are masked by Dokploy on read, so `config` is
tagged noread and keeps the configured value, as the basic-auth password
already does.
158 lines
9.9 KiB
Go
158 lines
9.9 KiB
Go
package provider
|
|
|
|
import (
|
|
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
)
|
|
|
|
type composeModel struct {
|
|
ID types.String `tfsdk:"id" dokploy:"composeId,id"`
|
|
Name types.String `tfsdk:"name" dokploy:"name"`
|
|
AppName types.String `tfsdk:"app_name" dokploy:"appName"`
|
|
Description types.String `tfsdk:"description" dokploy:"description,nullable"`
|
|
EnvironmentID types.String `tfsdk:"environment_id" dokploy:"environmentId"`
|
|
ServerID types.String `tfsdk:"server_id" dokploy:"serverId,create"`
|
|
|
|
ComposeType types.String `tfsdk:"compose_type" dokploy:"composeType"`
|
|
ComposeFile types.String `tfsdk:"compose_file" dokploy:"composeFile"`
|
|
ComposePath types.String `tfsdk:"compose_path" dokploy:"composePath"`
|
|
SourceType types.String `tfsdk:"source_type" dokploy:"sourceType"`
|
|
Command types.String `tfsdk:"command" dokploy:"command"`
|
|
Env types.String `tfsdk:"env" dokploy:"env,nullable"`
|
|
Icon types.String `tfsdk:"icon" dokploy:"icon,nullable"`
|
|
|
|
CreateEnvFile types.Bool `tfsdk:"create_env_file" dokploy:"createEnvFile"`
|
|
|
|
// serviceNetworks maps a service name in the stack to the networks it
|
|
// joins, so a stack can attach per-service rather than as a whole.
|
|
ServiceNetworks jsontypes.Normalized `tfsdk:"service_networks" dokploy:"serviceNetworks,nullable"`
|
|
|
|
Repository types.String `tfsdk:"repository" dokploy:"repository,nullable"`
|
|
Owner types.String `tfsdk:"owner" dokploy:"owner,nullable"`
|
|
Branch types.String `tfsdk:"branch" dokploy:"branch,nullable"`
|
|
GithubID types.String `tfsdk:"github_id" dokploy:"githubId,nullable"`
|
|
GitlabID types.String `tfsdk:"gitlab_id" dokploy:"gitlabId,nullable"`
|
|
GitlabProjectID types.Int64 `tfsdk:"gitlab_project_id" dokploy:"gitlabProjectId,nullable"`
|
|
GitlabRepository types.String `tfsdk:"gitlab_repository" dokploy:"gitlabRepository,nullable"`
|
|
GitlabOwner types.String `tfsdk:"gitlab_owner" dokploy:"gitlabOwner,nullable"`
|
|
GitlabBranch types.String `tfsdk:"gitlab_branch" dokploy:"gitlabBranch,nullable"`
|
|
GitlabNamespace types.String `tfsdk:"gitlab_path_namespace" dokploy:"gitlabPathNamespace,nullable"`
|
|
GiteaID types.String `tfsdk:"gitea_id" dokploy:"giteaId,nullable"`
|
|
GiteaRepository types.String `tfsdk:"gitea_repository" dokploy:"giteaRepository,nullable"`
|
|
GiteaOwner types.String `tfsdk:"gitea_owner" dokploy:"giteaOwner,nullable"`
|
|
GiteaBranch types.String `tfsdk:"gitea_branch" dokploy:"giteaBranch,nullable"`
|
|
BitbucketID types.String `tfsdk:"bitbucket_id" dokploy:"bitbucketId,nullable"`
|
|
BitbucketRepo types.String `tfsdk:"bitbucket_repository" dokploy:"bitbucketRepository,nullable"`
|
|
BitbucketSlug types.String `tfsdk:"bitbucket_repository_slug" dokploy:"bitbucketRepositorySlug,nullable"`
|
|
BitbucketOwner types.String `tfsdk:"bitbucket_owner" dokploy:"bitbucketOwner,nullable"`
|
|
BitbucketBranch types.String `tfsdk:"bitbucket_branch" dokploy:"bitbucketBranch,nullable"`
|
|
CustomGitURL types.String `tfsdk:"custom_git_url" dokploy:"customGitUrl,nullable"`
|
|
CustomGitBranch types.String `tfsdk:"custom_git_branch" dokploy:"customGitBranch,nullable"`
|
|
CustomGitSSHKey types.String `tfsdk:"custom_git_ssh_key_id" dokploy:"customGitSSHKeyId,nullable"`
|
|
|
|
EnableSubmodules types.Bool `tfsdk:"enable_submodules" dokploy:"enableSubmodules"`
|
|
AutoDeploy types.Bool `tfsdk:"auto_deploy" dokploy:"autoDeploy,nullable"`
|
|
TriggerType types.String `tfsdk:"trigger_type" dokploy:"triggerType,nullable"`
|
|
WatchPaths types.List `tfsdk:"watch_paths" dokploy:"watchPaths,nullable"`
|
|
|
|
Suffix types.String `tfsdk:"suffix" dokploy:"suffix"`
|
|
Randomize types.Bool `tfsdk:"randomize" dokploy:"randomize"`
|
|
IsolatedDeployment types.Bool `tfsdk:"isolated_deployment" dokploy:"isolatedDeployment"`
|
|
IsolatedDeployVolue types.Bool `tfsdk:"isolated_deployments_volume" dokploy:"isolatedDeploymentsVolume"`
|
|
|
|
ComposeStatus types.String `tfsdk:"compose_status" dokploy:"composeStatus,ro"`
|
|
CreatedAt types.String `tfsdk:"created_at" dokploy:"createdAt,ro"`
|
|
|
|
// Terraform-only: controls the destroy call, never sent on create/update.
|
|
DeleteVolumes types.Bool `tfsdk:"delete_volumes"`
|
|
}
|
|
|
|
func composeResource() ResourceSpec {
|
|
return ResourceSpec{
|
|
Name: "compose",
|
|
CreateProc: "compose.create",
|
|
ReadProc: "compose.one",
|
|
UpdateProc: "compose.update",
|
|
DeleteProc: "compose.delete",
|
|
UpdateAfterCreate: true,
|
|
NewModel: func() any { return &composeModel{} },
|
|
DeleteBody: func(model any) map[string]any {
|
|
// Dokploy requires this field on every compose delete.
|
|
deleteVolumes := false
|
|
if compose, ok := model.(*composeModel); ok && compose.DeleteVolumes.ValueBool() {
|
|
deleteVolumes = true
|
|
}
|
|
return map[string]any{"deleteVolumes": deleteVolumes}
|
|
},
|
|
Schema: schema.Schema{
|
|
MarkdownDescription: "A Docker Compose or Docker Swarm stack managed by Dokploy.\n\n" +
|
|
"Set `compose_file` to manage the stack definition inline (with `source_type = \"raw\"`), or point " +
|
|
"the stack at a Git repository and set `compose_path`.\n\n" +
|
|
"~> Creating this resource does **not** deploy the stack.",
|
|
Attributes: map[string]schema.Attribute{
|
|
"id": computedID("Unique compose identifier."),
|
|
"name": requiredString("Display name of the stack."),
|
|
"app_name": optionalComputedReplaceString("Unique Docker stack name. Generated by Dokploy when " +
|
|
"omitted. Changing it forces a new stack."),
|
|
"description": optionalString("Free-form description."),
|
|
"environment_id": requiredReplaceString("Environment this stack belongs to."),
|
|
"server_id": optionalReplaceString("Remote server to deploy on. Omit to use the Dokploy host itself."),
|
|
|
|
"compose_type": enumString("Whether to run the stack with Docker Compose or Docker Swarm.", composeTypes, false),
|
|
"compose_file": optionalComputedString("Inline Compose file contents. Used when `source_type` is `raw`."),
|
|
"compose_path": optionalComputedString("Path to the Compose file within the repository."),
|
|
"source_type": enumString("Where the Compose file comes from.", composeSources, false),
|
|
"command": optionalComputedString("Custom `docker compose` command to run."),
|
|
"env": optionalString("Environment variables in `KEY=value` format, one per line."),
|
|
"icon": optionalString("Icon shown next to the stack in the Dokploy UI."),
|
|
"create_env_file": optionalComputedBool("Write the environment variables to a `.env` file next to " +
|
|
"the Compose file."),
|
|
"service_networks": optionalJSON("Per-service Docker network attachments, as a JSON object mapping " +
|
|
"each service name in the stack to an array of network IDs."),
|
|
|
|
"repository": optionalString("GitHub repository name."),
|
|
"owner": optionalString("GitHub repository owner."),
|
|
"branch": optionalString("GitHub branch to deploy."),
|
|
"github_id": optionalString("ID of the configured GitHub provider connection."),
|
|
"gitlab_id": optionalString("ID of the configured GitLab provider connection."),
|
|
"gitlab_project_id": schema.Int64Attribute{Optional: true, MarkdownDescription: "Numeric GitLab project ID."},
|
|
"gitlab_repository": optionalString("GitLab repository name."),
|
|
"gitlab_owner": optionalString("GitLab repository owner."),
|
|
"gitlab_branch": optionalString("GitLab branch to deploy."),
|
|
"gitlab_path_namespace": optionalString("Full GitLab namespace path."),
|
|
"gitea_id": optionalString("ID of the configured Gitea provider connection."),
|
|
"gitea_repository": optionalString("Gitea repository name."),
|
|
"gitea_owner": optionalString("Gitea repository owner."),
|
|
"gitea_branch": optionalString("Gitea branch to deploy."),
|
|
"bitbucket_id": optionalString("ID of the configured Bitbucket provider connection."),
|
|
"bitbucket_repository": optionalString("Bitbucket repository name."),
|
|
"bitbucket_repository_slug": optionalString("Bitbucket repository slug."),
|
|
"bitbucket_owner": optionalString("Bitbucket repository owner."),
|
|
"bitbucket_branch": optionalString("Bitbucket branch to deploy."),
|
|
"custom_git_url": optionalString("Git remote URL, when `source_type` is `git`."),
|
|
"custom_git_branch": optionalString("Branch to deploy for a custom Git remote."),
|
|
"custom_git_ssh_key_id": optionalString("SSH key used to clone a private custom Git remote."),
|
|
|
|
"enable_submodules": optionalComputedBool("Clone Git submodules when checking out the repository."),
|
|
"auto_deploy": optionalComputedBool("Deploy automatically when the configured trigger fires."),
|
|
"trigger_type": enumString("What triggers an automatic deployment.", triggerTypes, false),
|
|
"watch_paths": optionalComputedStringList("Glob patterns that limit which changed paths trigger " +
|
|
"an automatic deployment."),
|
|
|
|
"suffix": optionalComputedString("Suffix appended to generated resource names."),
|
|
"randomize": optionalComputedBool("Append a random suffix to service and volume names."),
|
|
"isolated_deployment": optionalComputedBool("Run the stack on its own isolated Docker network."),
|
|
"isolated_deployments_volume": optionalComputedBool("Prefix volume names for isolated deployments. " +
|
|
"Retained for backwards compatibility."),
|
|
|
|
"compose_status": computedString("Current status reported by Dokploy: `idle`, `running`, `done` or `error`."),
|
|
"created_at": computedString("RFC 3339 timestamp of when the stack was created."),
|
|
|
|
"delete_volumes": optionalBool("Whether to delete the stack's Docker volumes when this resource is " +
|
|
"destroyed. Defaults to `false`, which preserves the data."),
|
|
},
|
|
},
|
|
}
|
|
}
|