diff --git a/.changelog/pending.txt b/.changelog/pending.txt new file mode 100644 index 000000000..9b869024e --- /dev/null +++ b/.changelog/pending.txt @@ -0,0 +1,3 @@ +```release-note:breaking-change +provider: The `Provider` interface now requires the `Metadata` method. It can be left empty or set the `MetadataResponse` type `TypeName` field to populate `datasource.MetadataRequest` and `resource.MetadataRequest` type `ProviderTypeName` fields. +``` diff --git a/internal/fwserver/server_getproviderschema.go b/internal/fwserver/server_getproviderschema.go index 2e7f8cab2..0045fab35 100644 --- a/internal/fwserver/server_getproviderschema.go +++ b/internal/fwserver/server_getproviderschema.go @@ -30,18 +30,16 @@ func (s *Server) GetProviderSchema(ctx context.Context, req *GetProviderSchemaRe PlanDestroy: true, } - if providerWithMetadata, ok := s.Provider.(provider.ProviderWithMetadata); ok { - logging.FrameworkTrace(ctx, "Provider implements ProviderWithMetadata") + logging.FrameworkTrace(ctx, "Provider implements ProviderWithMetadata") - metadataReq := provider.MetadataRequest{} - metadataResp := provider.MetadataResponse{} + metadataReq := provider.MetadataRequest{} + metadataResp := provider.MetadataResponse{} - logging.FrameworkDebug(ctx, "Calling provider defined Provider Metadata") - providerWithMetadata.Metadata(ctx, metadataReq, &metadataResp) - logging.FrameworkDebug(ctx, "Called provider defined Provider Metadata") + logging.FrameworkDebug(ctx, "Calling provider defined Provider Metadata") + s.Provider.Metadata(ctx, metadataReq, &metadataResp) + logging.FrameworkDebug(ctx, "Called provider defined Provider Metadata") - s.providerTypeName = metadataResp.TypeName - } + s.providerTypeName = metadataResp.TypeName providerSchema, diags := s.ProviderSchema(ctx) diff --git a/internal/fwserver/server_getproviderschema_test.go b/internal/fwserver/server_getproviderschema_test.go index 32e1f0223..f33446fdc 100644 --- a/internal/fwserver/server_getproviderschema_test.go +++ b/internal/fwserver/server_getproviderschema_test.go @@ -258,31 +258,29 @@ func TestServerGetProviderSchema(t *testing.T) { }, "datasourceschemas-provider-type-name": { server: &fwserver.Server{ - Provider: &testprovider.ProviderWithMetadata{ + Provider: &testprovider.Provider{ MetadataMethod: func(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { resp.TypeName = "testprovidertype" }, - Provider: &testprovider.Provider{ - DataSourcesMethod: func(_ context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{ - func() datasource.DataSource { - return &testprovider.DataSource{ - SchemaMethod: func(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = datasourceschema.Schema{ - Attributes: map[string]datasourceschema.Attribute{ - "test": datasourceschema.StringAttribute{ - Required: true, - }, + DataSourcesMethod: func(_ context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + func() datasource.DataSource { + return &testprovider.DataSource{ + SchemaMethod: func(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = datasourceschema.Schema{ + Attributes: map[string]datasourceschema.Attribute{ + "test": datasourceschema.StringAttribute{ + Required: true, }, - } - }, - MetadataMethod: func(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_data_source" - }, - } - }, - } - }, + }, + } + }, + MetadataMethod: func(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_data_source" + }, + } + }, + } }, }, }, @@ -640,31 +638,29 @@ func TestServerGetProviderSchema(t *testing.T) { }, "resourceschemas-provider-type-name": { server: &fwserver.Server{ - Provider: &testprovider.ProviderWithMetadata{ + Provider: &testprovider.Provider{ MetadataMethod: func(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { resp.TypeName = "testprovidertype" }, - Provider: &testprovider.Provider{ - ResourcesMethod: func(_ context.Context) []func() resource.Resource { - return []func() resource.Resource{ - func() resource.Resource { - return &testprovider.Resource{ - SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = resourceschema.Schema{ - Attributes: map[string]resourceschema.Attribute{ - "test": resourceschema.StringAttribute{ - Required: true, - }, + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = resourceschema.Schema{ + Attributes: map[string]resourceschema.Attribute{ + "test": resourceschema.StringAttribute{ + Required: true, }, - } - }, - MetadataMethod: func(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_resource" - }, - } - }, - } - }, + }, + } + }, + MetadataMethod: func(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_resource" + }, + } + }, + } }, }, }, diff --git a/internal/testing/testprovider/provider.go b/internal/testing/testprovider/provider.go index 01db83ce6..ee92c2c8e 100644 --- a/internal/testing/testprovider/provider.go +++ b/internal/testing/testprovider/provider.go @@ -13,17 +13,14 @@ var _ provider.Provider = &Provider{} // Declarative provider.Provider for unit testing. type Provider struct { // Provider interface methods - ConfigureMethod func(context.Context, provider.ConfigureRequest, *provider.ConfigureResponse) - SchemaMethod func(context.Context, provider.SchemaRequest, *provider.SchemaResponse) - - // ProviderWithDataSources interface methods + MetadataMethod func(context.Context, provider.MetadataRequest, *provider.MetadataResponse) + ConfigureMethod func(context.Context, provider.ConfigureRequest, *provider.ConfigureResponse) + SchemaMethod func(context.Context, provider.SchemaRequest, *provider.SchemaResponse) DataSourcesMethod func(context.Context) []func() datasource.DataSource - - // ProviderWithResources interface methods - ResourcesMethod func(context.Context) []func() resource.Resource + ResourcesMethod func(context.Context) []func() resource.Resource } -// GetSchema satisfies the provider.Provider interface. +// Configure satisfies the provider.Provider interface. func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { if p == nil || p.ConfigureMethod == nil { return @@ -32,7 +29,7 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest, p.ConfigureMethod(ctx, req, resp) } -// DataSources satisfies the provider.ProviderWithDataSources interface. +// DataSources satisfies the provider.Provider interface. func (p *Provider) DataSources(ctx context.Context) []func() datasource.DataSource { if p == nil || p.DataSourcesMethod == nil { return nil @@ -41,6 +38,15 @@ func (p *Provider) DataSources(ctx context.Context) []func() datasource.DataSour return p.DataSourcesMethod(ctx) } +// Metadata satisfies the provider.Provider interface. +func (p *Provider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { + if p == nil || p.SchemaMethod == nil { + return + } + + p.MetadataMethod(ctx, req, resp) +} + // Schema satisfies the provider.Provider interface. func (p *Provider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { if p == nil || p.SchemaMethod == nil { @@ -50,7 +56,7 @@ func (p *Provider) Schema(ctx context.Context, req provider.SchemaRequest, resp p.SchemaMethod(ctx, req, resp) } -// Resources satisfies the provider.ProviderWithResources interface. +// Resources satisfies the provider.Provider interface. func (p *Provider) Resources(ctx context.Context) []func() resource.Resource { if p == nil || p.ResourcesMethod == nil { return nil diff --git a/internal/testing/testprovider/providerwithmetadata.go b/internal/testing/testprovider/providerwithmetadata.go deleted file mode 100644 index 5aaf6fa31..000000000 --- a/internal/testing/testprovider/providerwithmetadata.go +++ /dev/null @@ -1,27 +0,0 @@ -package testprovider - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/provider" -) - -var _ provider.Provider = &ProviderWithMetadata{} -var _ provider.ProviderWithMetadata = &ProviderWithMetadata{} - -// Declarative provider.ProviderWithMetadata for unit testing. -type ProviderWithMetadata struct { - *Provider - - // ProviderWithMetadata interface methods - MetadataMethod func(context.Context, provider.MetadataRequest, *provider.MetadataResponse) -} - -// Metadata satisfies the provider.ProviderWithMetadata interface. -func (p *ProviderWithMetadata) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { - if p.MetadataMethod == nil { - return - } - - p.MetadataMethod(ctx, req, resp) -} diff --git a/provider/provider.go b/provider/provider.go index 1ef681334..60b1893ff 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -15,6 +15,14 @@ import ( // via ProviderWithConfigValidators or ProviderWithValidateConfig. // - Meta Schema: ProviderWithMetaSchema type Provider interface { + // Metadata should return the metadata for the provider, such as + // a type name and version data. + // + // Implementing the MetadataResponse.TypeName will populate the + // datasource.MetadataRequest.ProviderTypeName and + // resource.MetadataRequest.ProviderTypeName fields automatically. + Metadata(context.Context, MetadataRequest, *MetadataResponse) + // Schema should return the schema for this provider. Schema(context.Context, SchemaRequest, *SchemaResponse) @@ -57,21 +65,6 @@ type ProviderWithConfigValidators interface { ConfigValidators(context.Context) []ConfigValidator } -// ProviderWithMetadata is an interface type that extends Provider to -// return its type name, such as examplecloud, and other -// metadata, such as version. -// -// Implementing this method will populate the -// [datasource.MetadataRequest.ProviderTypeName] and -// [resource.MetadataRequest.ProviderTypeName] fields automatically. -type ProviderWithMetadata interface { - Provider - - // Metadata should return the metadata for the provider, such as - // a type name and version data. - Metadata(context.Context, MetadataRequest, *MetadataResponse) -} - // ProviderWithMetaSchema is a provider with a provider meta schema, which // is configured by practitioners via the provider_meta configuration block // and the configuration data is included with certain data source and resource diff --git a/website/docs/plugin/framework/data-sources/index.mdx b/website/docs/plugin/framework/data-sources/index.mdx index b597aedb3..5643f1b14 100644 --- a/website/docs/plugin/framework/data-sources/index.mdx +++ b/website/docs/plugin/framework/data-sources/index.mdx @@ -79,7 +79,7 @@ func (d *ThingDataSource) Metadata(ctx context.Context, req datasource.MetadataR } ``` -To simplify data source implementations, the [`provider.MetadataResponse.TypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#MetadataResponse.TypeName) from the [`provider.ProviderWithMetadata` interface `Metadata` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#ProviderWithMetadata.Metadata) can set the provider name so it is available in the [`datasource.MetadataRequest.ProviderTypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/datasource#MetadataRequest.ProviderTypeName). +To simplify data source implementations, the [`provider.MetadataResponse.TypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#MetadataResponse.TypeName) from the [`provider.Provider` interface `Metadata` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#Provider.Metadata) can set the provider name so it is available in the [`datasource.MetadataRequest.ProviderTypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/datasource#MetadataRequest.ProviderTypeName). In this example, the provider defines the `examplecloud` name for itself, and the data source is named `examplecloud_thing`: diff --git a/website/docs/plugin/framework/providers/index.mdx b/website/docs/plugin/framework/providers/index.mdx index 9ed74ab46..b6cc70b3b 100644 --- a/website/docs/plugin/framework/providers/index.mdx +++ b/website/docs/plugin/framework/providers/index.mdx @@ -35,7 +35,12 @@ type ExampleCloudProvider struct{ Version string } -// GetSchema satisfies the provider.Provider interface for exampleProvider. +// Metadata satisfies the provider.Provider interface for ExampleCloudProvider +func (p *ExampleCloudProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = // provider specific implementation +} + +// Schema satisfies the provider.Provider interface for ExampleCloudProvider. func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ @@ -76,7 +81,19 @@ func New(version string) func() provider.Provider { } ``` -### GetSchema Method +### Metadata Method + +The [`provider.Provider` interface `Metadata` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#Provider.Metadata) defines information about the provider itself, such as its type name and version. This information is used to simplify creating data sources and resources. + +In this example, the provider type name is set to `examplecloud`: + +```go +func (p *ExampleCloudProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "examplecloud" +} +``` + +### Schema Method The [`provider.Provider` interface `Schema` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#Provider.Schema) defines a [schema](/plugin/framework/schemas) describing what data is available in the provider's configuration. This configuration block is used to offer practitioners the opportunity to supply values to the provider and configure its behavior, rather than needing to include those values in every resource and data source. It is usually used to gather credentials, endpoints, and the other data used to authenticate with the API, but it is not limited to those uses. diff --git a/website/docs/plugin/framework/resources/index.mdx b/website/docs/plugin/framework/resources/index.mdx index 81962df97..373dfb644 100644 --- a/website/docs/plugin/framework/resources/index.mdx +++ b/website/docs/plugin/framework/resources/index.mdx @@ -49,7 +49,7 @@ func (r *ThingResource) Metadata(ctx context.Context, req resource.MetadataReque } ``` -To simplify resource implementations, the [`provider.MetadataResponse.TypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#MetadataResponse.TypeName) from the [`provider.ProviderWithMetadata` interface `Metadata` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#ProviderWithMetadata.Metadata) can set the provider name so it is available in the [`resource.MetadataRequest.ProviderTypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#MetadataRequest.ProviderTypeName). +To simplify resource implementations, the [`provider.MetadataResponse.TypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#MetadataResponse.TypeName) from the [`provider.Provider` interface `Metadata` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/provider#Provider.Metadata) can set the provider name so it is available in the [`resource.MetadataRequest.ProviderTypeName` field](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#MetadataRequest.ProviderTypeName). In this example, the provider defines the `examplecloud` name for itself, and the data source is named `examplecloud_thing`: @@ -65,9 +65,9 @@ func (d *ThingDataSource) Metadata(ctx context.Context, req resource.MetadataReq } ``` -### GetSchema Method +### Schema Method -The [`resource.Resource` interface `GetSchema` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource.GetSchema) defines a [schema](/plugin/framework/schemas) describing what data is available in the resource's configuration, plan, and state. +The [`resource.Resource` interface `Schema` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource.Schema) defines a [schema](/plugin/framework/schemas) describing what data is available in the resource's configuration, plan, and state. ## Add Resource to Provider