Переглянути джерело

Tutorial 2: Configure client.

Frédéric G. MARAND 5 місяців тому
батько
коміт
f930b2814a

+ 5 - 1
examples/provider-install-verification/main.tf

@@ -6,6 +6,10 @@ terraform {
   }
 }
 
-provider "hashicups" {}
+provider "hashicups" {
+  host     = "http://localhost:19090" // See docker-compose/docker-compose.yml
+  username = "education"              // We used: curl -X POST localhost:19090/signup -d '{"username":"education", "password":"test123"}'
+  password = "test123"                // We used: curl -X POST localhost:19090/signup -d '{"username":"education", "password":"test123"}'
+}
 
 data "hashicups_coffees" "example" {}

+ 3 - 2
go.mod

@@ -3,11 +3,11 @@ module terraform-provider-hashicups
 go 1.22.7
 
 require (
+	github.com/hashicorp-demoapp/hashicups-client-go v0.1.0
 	github.com/hashicorp/terraform-plugin-framework v1.13.0
 	github.com/hashicorp/terraform-plugin-go v0.25.0
 	github.com/hashicorp/terraform-plugin-log v0.9.0
 	github.com/hashicorp/terraform-plugin-testing v1.11.0
-	github.com/hashicorp/terraform-provider-scaffolding-framework v0.0.0-20241120140137-0773a77924eb
 )
 
 require (
@@ -37,7 +37,7 @@ require (
 	github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
 	github.com/hashicorp/terraform-svchost v0.1.1 // indirect
 	github.com/hashicorp/yamux v0.1.1 // indirect
-	github.com/kr/text v0.2.0 // indirect
+	github.com/kr/pretty v0.3.0 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -46,6 +46,7 @@ require (
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 	github.com/oklog/run v1.0.0 // indirect
+	github.com/rogpeppe/go-internal v1.12.0 // indirect
 	github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
 	github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect

+ 4 - 2
go.sum

@@ -43,6 +43,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hashicorp-demoapp/hashicups-client-go v0.1.0 h1:5eUmjDEqF4viZHLwS9UKSqwDHJ2T9ZQamqSf5dn+qcE=
+github.com/hashicorp-demoapp/hashicups-client-go v0.1.0/go.mod h1:fJF8CZhWlImByx49t7RZvuoxskStDwqIWi5/GOSJqGI=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
 github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -86,8 +88,6 @@ github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0 h1:wyKCCtn6pBBL46c1uIIBNUOW
 github.com/hashicorp/terraform-plugin-sdk/v2 v2.35.0/go.mod h1:B0Al8NyYVr8Mp/KLwssKXG1RqnTk7FySqSn4fRuLNgw=
 github.com/hashicorp/terraform-plugin-testing v1.11.0 h1:MeDT5W3YHbONJt2aPQyaBsgQeAIckwPX41EUHXEn29A=
 github.com/hashicorp/terraform-plugin-testing v1.11.0/go.mod h1:WNAHQ3DcgV/0J+B15WTE6hDvxcUdkPPpnB1FR3M910U=
-github.com/hashicorp/terraform-provider-scaffolding-framework v0.0.0-20241120140137-0773a77924eb h1:o11BS2nKRc+NBgpCUL5xG0YzWR9SaMHyDHD0DfDPmAY=
-github.com/hashicorp/terraform-provider-scaffolding-framework v0.0.0-20241120140137-0773a77924eb/go.mod h1:eKIuOdboOPb9qylqvBLVYN2tn5fPLAyfAdjvGXcmVMQ=
 github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
 github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
 github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
@@ -132,6 +132,7 @@ github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
 github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
 github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
@@ -218,6 +219,7 @@ google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

+ 27 - 0
internal/provider/coffees_data_source.go

@@ -0,0 +1,27 @@
+package provider
+
+import (
+	"context"
+
+	"github.com/hashicorp/terraform-plugin-framework/datasource"
+	"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+)
+
+func NewCoffeesDataSource() datasource.DataSource {
+	return &coffeesDataSource{}
+}
+
+type coffeesDataSource struct {
+}
+
+func (c coffeesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_coffees"
+}
+
+func (c coffeesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+	resp.Schema = schema.Schema{}
+}
+
+func (c coffeesDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) {
+	return
+}

+ 152 - 2
internal/provider/provider.go

@@ -5,11 +5,15 @@ 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.
@@ -38,20 +42,166 @@ func (p *hashicupsProvider) Metadata(_ context.Context, _ provider.MetadataReque
 }
 
 func (p *hashicupsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
-	resp.Schema = schema.Schema{}
+	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(_ context.Context, _ provider.ConfigureRequest, _ *provider.ConfigureResponse) {
+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{}