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) }