Files
terraform-provider-dokploy/internal/provider/provider.go
Max Vojtkov a6d8aa8b52 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.
2026-08-09 12:17:26 +03:00

172 lines
5.3 KiB
Go

package provider
import (
"context"
"crypto/tls"
"net/http"
"os"
"strconv"
"time"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/maxvojtkov/terraform-provider-dokploy/internal/client"
)
// New returns the provider factory used by main.go and by acceptance tests.
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &dokployProvider{version: version}
}
}
type dokployProvider struct {
version string
}
type providerModel struct {
Host types.String `tfsdk:"host"`
APIKey types.String `tfsdk:"api_key"`
Timeout types.Int64 `tfsdk:"timeout_seconds"`
SkipVerify types.Bool `tfsdk:"insecure_skip_verify"`
}
func (p *dokployProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "dokploy"
resp.Version = p.version
}
func (p *dokployProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "Manage [Dokploy](https://dokploy.com) projects, environments, applications, " +
"compose stacks, databases and networking with Terraform.",
Attributes: map[string]schema.Attribute{
"host": schema.StringAttribute{
Optional: true,
MarkdownDescription: "Base URL of the Dokploy instance, for example `https://dokploy.example.com`. " +
"A trailing `/api` is optional. May also be set with the `DOKPLOY_HOST` environment variable.",
},
"api_key": schema.StringAttribute{
Optional: true,
Sensitive: true,
MarkdownDescription: "API token generated in Dokploy under *Settings -> Profile -> API/CLI*. " +
"May also be set with the `DOKPLOY_API_KEY` environment variable.",
},
"timeout_seconds": schema.Int64Attribute{
Optional: true,
MarkdownDescription: "Per-request timeout in seconds. Defaults to `60`. May also be set with the " +
"`DOKPLOY_TIMEOUT_SECONDS` environment variable.",
},
"insecure_skip_verify": schema.BoolAttribute{
Optional: true,
MarkdownDescription: "Skip TLS certificate verification. Only use this for instances behind a " +
"self-signed certificate. Defaults to `false`.",
},
},
}
}
func (p *dokployProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var config providerModel
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
host := stringOrEnv(config.Host, "DOKPLOY_HOST")
apiKey := stringOrEnv(config.APIKey, "DOKPLOY_API_KEY")
if host == "" {
resp.Diagnostics.AddAttributeError(
path.Root("host"),
"Missing Dokploy host",
"Set the `host` provider attribute or the DOKPLOY_HOST environment variable.",
)
}
if apiKey == "" {
resp.Diagnostics.AddAttributeError(
path.Root("api_key"),
"Missing Dokploy API key",
"Set the `api_key` provider attribute or the DOKPLOY_API_KEY environment variable.",
)
}
if resp.Diagnostics.HasError() {
return
}
timeout := 60 * time.Second
if !config.Timeout.IsNull() && !config.Timeout.IsUnknown() {
timeout = time.Duration(config.Timeout.ValueInt64()) * time.Second
} else if env := os.Getenv("DOKPLOY_TIMEOUT_SECONDS"); env != "" {
if seconds, err := strconv.Atoi(env); err == nil {
timeout = time.Duration(seconds) * time.Second
}
}
var httpClient *http.Client
if config.SkipVerify.ValueBool() {
httpClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // opt-in
},
}
}
api, err := client.New(host, apiKey, timeout, httpClient)
if err != nil {
resp.Diagnostics.AddError("Invalid Dokploy provider configuration", err.Error())
return
}
resp.DataSourceData = api
resp.ResourceData = api
}
func (p *dokployProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
newGenericResource(projectResource()),
newGenericResource(environmentResource()),
newGenericResource(applicationResource()),
newGenericResource(composeResource()),
newGenericResource(postgresResource()),
newGenericResource(mysqlResource()),
newGenericResource(mariadbResource()),
newGenericResource(mongoResource()),
newGenericResource(redisResource()),
newGenericResource(domainResource()),
newGenericResource(mountResource()),
newGenericResource(portResource()),
newGenericResource(redirectResource()),
newGenericResource(securityResource()),
newGenericResource(registryResource()),
newGenericResource(sshKeyResource()),
newGenericResource(certificateResource()),
newGenericResource(destinationResource()),
}
}
func (p *dokployProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
newProjectDataSource,
newProjectsDataSource,
newEnvironmentDataSource,
newApplicationDataSource,
newServersDataSource,
}
}
func stringOrEnv(value types.String, envVar string) string {
if !value.IsNull() && !value.IsUnknown() && value.ValueString() != "" {
return value.ValueString()
}
return os.Getenv(envVar)
}