// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package provider import ( "context" "os" "github.com/hashicorp-demoapp/hashicups-client-go" "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" ) // Ensure hashicupsProvider satisfies various provider interfaces. var _ provider.Provider = &hashicupsProvider{} // New is a helper function to simplify provider server and testing implementation. func New(version string) func() provider.Provider { return func() provider.Provider { return &hashicupsProvider{ version: version, } } } // hashicupsProvider defines the provider implementation. type hashicupsProvider 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 } func (p *hashicupsProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { resp.TypeName = "hashicups" resp.Version = p.version } func (p *hashicupsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "host": schema.StringAttribute{ Optional: true, }, "username": schema.StringAttribute{ Optional: true, }, "password": schema.StringAttribute{ Optional: true, Sensitive: true, }, }, } } func (p *hashicupsProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { // 1. Retrieves values from the configuration. // The method will attempt to retrieve values from the provider configuration and convert it to an providerModel struct. var config hashicupsProviderModel diags := req.Config.Get(ctx, &config) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } // 2. Checks for unknown configuration values. // The method prevents an unexpectedly misconfigured client, if Terraform configuration values are only known after another resource is applied. // // If practitioner provided a configuration value for any of the attributes, it must be a known value. // 2.1 host if config.Host.IsUnknown() { resp.Diagnostics.AddAttributeError( path.Root("host"), "Unknown HashiCups API Host", "The provider cannot create the HashiCups API client as there is an unknown configuration value for the HashiCups API host. "+ "Either target apply the source of the value first, set the value statically in the configuration, or use the HASHICUPS_HOST environment variable.", ) } // 2.2 username if config.Username.IsUnknown() { resp.Diagnostics.AddAttributeError( path.Root("username"), "Unknown HashiCups API Username", "The provider cannot create the HashiCups API client as there is an unknown configuration value for the HashiCups API username. "+ "Either target apply the source of the value first, set the value statically in the configuration, or use the HASHICUPS_USERNAME environment variable.", ) } // 2.3 password if config.Password.IsUnknown() { resp.Diagnostics.AddAttributeError( path.Root("password"), "Unknown HashiCups API Password", "The provider cannot create the HashiCups API client as there is an unknown configuration value for the HashiCups API password. "+ "Either target apply the source of the value first, set the value statically in the configuration, or use the HASHICUPS_PASSWORD environment variable.", ) } // 2.4 check results if resp.Diagnostics.HasError() { return } // 3. Retrieves values from environment variables. // The method retrieves values from environment variables, then overrides them with any set Terraform configuration values. // 3.1 Default values to environment variables. host := os.Getenv("HASHICUPS_HOST") // See docker-compose/docker-compose.yml: http://localhost:19090 username := os.Getenv("HASHICUPS_USERNAME") // We used: curl -X POST localhost:19090/signup -d '{"username":"education", "password":"test123"}' password := os.Getenv("HASHICUPS_PASSWORD") // We used: curl -X POST localhost:19090/signup -d '{"username":"education", "password":"test123"}' // 3.2 but override with Terraform configuration value if set. if !config.Host.IsNull() { host = config.Host.ValueString() } if !config.Username.IsNull() { username = config.Username.ValueString() } if !config.Password.IsNull() { password = config.Password.ValueString() } // 3.3 verify values are set if host == "" { resp.Diagnostics.AddAttributeError( path.Root("host"), "Missing HashiCups API Host", "The provider cannot create the HashiCups API client as there is a missing or empty value for the HashiCups API host. "+ "Set the host value in the configuration or use the HASHICUPS_HOST environment variable. "+ "If either is already set, ensure the value is not empty.", ) } if username == "" { resp.Diagnostics.AddAttributeError( path.Root("username"), "Missing HashiCups API Username", "The provider cannot create the HashiCups API client as there is a missing or empty value for the HashiCups API username. "+ "Set the username value in the configuration or use the HASHICUPS_USERNAME environment variable. "+ "If either is already set, ensure the value is not empty.", ) } if password == "" { resp.Diagnostics.AddAttributeError( path.Root("password"), "Missing HashiCups API Password", "The provider cannot create the HashiCups API client as there is a missing or empty value for the HashiCups API password. "+ "Set the password value in the configuration or use the HASHICUPS_PASSWORD environment variable. "+ "If either is already set, ensure the value is not empty.", ) } // 3.4 check results if resp.Diagnostics.HasError() { return } // 4. Creates API client. // The method invokes the HashiCups API client's NewClient function. // // Create a new HashiCups client using the configuration values client, err := hashicups.NewClient(&host, &username, &password) if err != nil { resp.Diagnostics.AddError( "Unable to Create HashiCups API Client", "An unexpected error occurred when creating the HashiCups API client. "+ "If the error is not clear, please contact the provider developers.\n\n"+ "HashiCups Client Error: "+err.Error(), ) return } // 5. Stores configured client for data source and resource usage. // The method sets the DataSourceData and ResourceData fields of the response, // so the client is available for usage by data source and resource implementations. // // Make the HashiCups client available during DataSource and Resource type Configure methods. resp.DataSourceData = client resp.ResourceData = client } func (p *hashicupsProvider) DataSources(_ context.Context) []func() datasource.DataSource { return nil //return []func() datasource.DataSource{ // NewCoffeesDataSource, //} } func (p *hashicupsProvider) Resources(_ context.Context) []func() resource.Resource { return nil } // The Terraform Plugin Framework uses Go struct types with tfsdk struct field tags // to map schema definitions into Go types with the actual data. // The types within the struct must align with the types in the schema. type hashicupsProviderModel struct { Host types.String `tfsdk:"host"` Username types.String `tfsdk:"username"` Password types.String `tfsdk:"password"` } //// Ensure hashicupsProvider satisfies various provider interfaces. //var _ provider.Provider = &hashicupsProvider{} //var _ provider.ProviderWithFunctions = &hashicupsProvider{} // //// hashicupsProvider defines the provider implementation. //type hashicupsProvider 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 //} // //// ScaffoldingProviderModel describes the provider data model. //type ScaffoldingProviderModel struct { // Endpoint types.String `tfsdk:"endpoint"` //} // //func (p *hashicupsProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { // resp.TypeName = "scaffolding" // resp.Version = p.version //} // //func (p *hashicupsProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { // resp.Schema = schema.Schema{ // Attributes: map[string]schema.Attribute{ // "endpoint": schema.StringAttribute{ // MarkdownDescription: "Example provider attribute", // Optional: true, // }, // }, // } //} // //func (p *hashicupsProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { // var data ScaffoldingProviderModel // // resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) // // if resp.Diagnostics.HasError() { // return // } // // // Configuration values are now available. // // if data.Endpoint.IsNull() { /* ... */ } // // // Example client configuration for data sources and resources // client := http.DefaultClient // resp.DataSourceData = client // resp.ResourceData = client //} // //func (p *hashicupsProvider) Resources(ctx context.Context) []func() resource.Resource { // return []func() resource.Resource{ // NewExampleResource, // } //} // //func (p *hashicupsProvider) DataSources(ctx context.Context) []func() datasource.DataSource { // return []func() datasource.DataSource{ // NewExampleDataSource, // } //} // //func (p *hashicupsProvider) Functions(ctx context.Context) []func() function.Function { // return []func() function.Function{ // NewExampleFunction, // } //} // //func New(version string) func() provider.Provider { // return func() provider.Provider { // return &hashicupsProvider{ // version: version, // } // } //}