internal/provider/provider.go (134 lines of code) (raw):

// Copyright (c) HashiCorp, Inc. package provider import ( "context" "os" "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/hashicorp/terraform-plugin-log/tflog" "terraform-provider-devlake/internal/client" ) // Ensure the implementation satisfies the expected interfaces. var ( _ provider.Provider = &devlakeProvider{} ) // New is a helper function to simplify provider server and testing implementation. func New(version string) func() provider.Provider { return func() provider.Provider { return &devlakeProvider{ version: version, } } } // devlakeProvider is the provider implementation. type devlakeProvider struct { // version is set to the provider version on release, "dev" when the // provider is built and ran locally, and "test" when running acceptance // testing. version string } // devlakeProviderModel maps provider schema data to a Go type. type devlakeProviderModel struct { Host types.String `tfsdk:"host"` Token types.String `tfsdk:"token"` } // Metadata returns the provider type name. func (p *devlakeProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { resp.TypeName = "devlake" resp.Version = p.version } // Schema defines the provider-level schema for configuration data. func (p *devlakeProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "host": schema.StringAttribute{ Optional: true, Description: "URI for Devlake API. May also be provided via DEVLAKE_HOST environment variable.", }, "token": schema.StringAttribute{ Description: "Token for Devlake API. May also be provided via DEVLAKE_TOKEN environment variable.", Optional: true, Sensitive: true, }, }, } } // Configure prepares a devlake API client for data sources and resources. func (p *devlakeProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { tflog.Info(ctx, "Configuring Devlake client") // Retrieve provider data from configuration var config devlakeProviderModel diags := req.Config.Get(ctx, &config) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } // If practitioner provided a configuration value for any of the // attributes, it must be a known value. if config.Host.IsUnknown() { resp.Diagnostics.AddAttributeError( path.Root("host"), "Unknown Devlake API Host", "The provider cannot create the Devlake API client as there is an unknown configuration value for the Devlake API host. "+ "Either target apply the source of the value first, set the value statically in the configuration, or use the DEVLAKE_HOST environment variable.", ) } if config.Token.IsUnknown() { resp.Diagnostics.AddAttributeError( path.Root("token"), "Unknown Devlake API Token", "The provider cannot create the Devlake API client as there is an unknown configuration value for the Devlake API token. "+ "Either target apply the source of the value first, set the value statically in the configuration, or use the DEVLAKE_TOKEN environment variable.", ) } if resp.Diagnostics.HasError() { return } // Default values to environment variables, but override // with Terraform configuration value if set. host := os.Getenv("DEVLAKE_HOST") token := os.Getenv("DEVLAKE_TOKEN") if !config.Host.IsNull() { host = config.Host.ValueString() } if !config.Token.IsNull() { token = config.Token.ValueString() } // If any of the expected configurations are missing, return // errors with provider-specific guidance. if host == "" { resp.Diagnostics.AddAttributeError( path.Root("host"), "Missing Devlake API Host", "The provider cannot create the Devlake API client as there is a missing or empty value for the Devlake API host. "+ "Set the host value in the configuration or use the DEVLAKE_HOST environment variable. "+ "If either is already set, ensure the value is not empty.", ) } if token == "" { resp.Diagnostics.AddAttributeError( path.Root("token"), "Missing Devlake API Token", "The provider cannot create the Devlake API client as there is a missing or empty value for the Devlake API token. "+ "Set the token value in the configuration or use the DEVLAKE_TOKEN environment variable. "+ "If either is already set, ensure the value is not empty.", ) } if resp.Diagnostics.HasError() { return } ctx = tflog.SetField(ctx, "devlake_host", host) ctx = tflog.SetField(ctx, "devlake_token", token) ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "devlake_token") tflog.Debug(ctx, "Creating Devlake client") // Create a new Devlake client using the configuration values client, err := client.NewClient(&host, &token) if err != nil { resp.Diagnostics.AddError( "Unable to Create Devlake API Client", "An unexpected error occurred when creating the Devlake API client. "+ "If the error is not clear, please contact the provider developers.\n\n"+ "Devlake Client Error: "+err.Error(), ) return } // Make the Devlake client available during DataSource and Resource // type Configure methods. resp.DataSourceData = client resp.ResourceData = client tflog.Info(ctx, "Configured Devlake client", map[string]any{"success": true}) } // DataSources defines the data sources implemented in the provider. func (p *devlakeProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewApiKeysDataSource, } } // Resources defines the resources implemented in the provider. func (p *devlakeProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ NewApiKeyResource, NewBitbucketServerConnectionResource, } }