Skip to content

Commit

Permalink
provider: Remove DataSourceType and ResourceType types, Provider GetD…
Browse files Browse the repository at this point in the history
…ataSources and GetResources methods (#478)

Reference: #441
Reference: #442

Providers should use the `Provider` interface `DataSources` and `Resources` methods after migrating `DataSourceType` and `ResourceType` implementations into their respective `datasource.DataSource` and `resource.Resource`.
  • Loading branch information
bflad committed Sep 13, 2022
1 parent f957ca7 commit 46b5573
Show file tree
Hide file tree
Showing 44 changed files with 1,344 additions and 3,243 deletions.
15 changes: 15 additions & 0 deletions .changelog/478.txt
@@ -0,0 +1,15 @@
```release-note:breaking-change
datasource: The `DataSource` interface now requires the `GetSchema` and `Metadata` methods.
```

```release-note:breaking-change
provider: The `Provider` interface `GetDataSources` and `GetResources` methods have been removed. Use the `DataSources` and `Resources` methods instead.
```

```release-note:breaking-change
provider: The `DataSourceType` and `ResourceType` types have been removed. Use the `GetSchema`, `Metadata`, and optionally the `Configure` methods on `datasource.DataSource` and `resource.Resource` implementations instead.
```

```release-note:breaking-change
resource: The `Resource` interface now requires the `GetSchema` and `Metadata` methods.
```
31 changes: 7 additions & 24 deletions datasource/data_source.go
Expand Up @@ -16,6 +16,13 @@ import (
// - Validation: Schema-based via tfsdk.Attribute or entire configuration
// via DataSourceWithConfigValidators or DataSourceWithValidateConfig.
type DataSource interface {
// Metadata should return the full name of the data source, such as
// examplecloud_thing.
Metadata(context.Context, MetadataRequest, *MetadataResponse)

// GetSchema returns the schema for this data source.
GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics)

// Read is called when the provider must read data source values in
// order to update state. Config values should be read from the
// ReadRequest and new state values set on the ReadResponse.
Expand Down Expand Up @@ -53,30 +60,6 @@ type DataSourceWithConfigValidators interface {
ConfigValidators(context.Context) []ConfigValidator
}

// DataSourceWithGetSchema is an interface type that extends DataSource to
// return its schema definition.
//
// This method will be required in the DataSource interface in a future
// release.
type DataSourceWithGetSchema interface {
// GetSchema returns the schema for this data source.
GetSchema(context.Context) (tfsdk.Schema, diag.Diagnostics)
}

// DataSourceWithMetadata is an interface type that extends DataSource to
// return metadata, such as its data source type name. For example, if the
// provider is named examplecloud and the data source reads a thing, this
// should return examplecloud_thing.
//
// This method will be required in the DataSource interface a future release.
type DataSourceWithMetadata interface {
DataSource

// Metadata should return the full name of the data source, such as
// examplecloud_thing.
Metadata(context.Context, MetadataRequest, *MetadataResponse)
}

// DataSourceWithValidateConfig is an interface type that extends DataSource to include imperative validation.
//
// Declaring validation using this methodology simplifies one-off
Expand Down
220 changes: 8 additions & 212 deletions internal/fwserver/server.go
Expand Up @@ -48,12 +48,6 @@ type Server struct {
// Provider.DataSources() method.
dataSourceFuncs map[string]func() datasource.DataSource

// dataSourceTypes is the cached DataSourceTypes for RPCs that need to
// access data sources. If not found, it will be fetched from the
// Provider.GetDataSources() method.
//nolint:staticcheck // Internal implementation
dataSourceTypes map[string]provider.DataSourceType

// dataSourceTypesDiags is the cached Diagnostics obtained while populating
// dataSourceTypes. This is to ensure any warnings or errors are also
// returned appropriately when fetching dataSourceTypes.
Expand Down Expand Up @@ -114,12 +108,6 @@ type Server struct {
// Provider.Resources() method.
resourceFuncs map[string]func() resource.Resource

// resourceTypes is the cached ResourceTypes for RPCs that need to
// access resources. If not found, it will be fetched from the
// Provider.GetResources() method.
//nolint:staticcheck // Internal implementation
resourceTypes map[string]provider.ResourceType

// resourceTypesDiags is the cached Diagnostics obtained while populating
// resourceTypes. This is to ensure any warnings or errors are also
// returned appropriately when fetching resourceTypes.
Expand All @@ -136,14 +124,6 @@ func (s *Server) DataSource(ctx context.Context, typeName string) (datasource.Da

dataSourceFunc, ok := dataSourceFuncs[typeName]

if ok {
return dataSourceFunc(), diags
}

dataSourceTypes, diags := s.DataSourceTypes(ctx)

dataSourceType, ok := dataSourceTypes[typeName]

if !ok {
diags.AddError(
"Data Source Type Not Found",
Expand All @@ -153,11 +133,7 @@ func (s *Server) DataSource(ctx context.Context, typeName string) (datasource.Da
return nil, diags
}

logging.FrameworkDebug(ctx, "Calling provider defined DataSourceType NewDataSource")
dataSource, diags := dataSourceType.NewDataSource(ctx, s.Provider)
logging.FrameworkDebug(ctx, "Called provider defined DataSourceType NewDataSource")

return dataSource, diags
return dataSourceFunc(), diags
}

// DataSourceFuncs returns a map of DataSource functions. The results are cached
Expand All @@ -173,36 +149,19 @@ func (s *Server) DataSourceFuncs(ctx context.Context) (map[string]func() datasou

s.dataSourceFuncs = make(map[string]func() datasource.DataSource)

providerWithDataSources, ok := s.Provider.(provider.ProviderWithDataSources)

if !ok {
return s.dataSourceFuncs, nil
}

logging.FrameworkDebug(ctx, "Calling provider defined Provider DataSources")
dataSourceFuncsSlice := providerWithDataSources.DataSources(ctx)
dataSourceFuncsSlice := s.Provider.DataSources(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider DataSources")

for _, dataSourceFunc := range dataSourceFuncsSlice {
dataSource := dataSourceFunc()

dataSourceWithMetadata, ok := dataSource.(datasource.DataSourceWithMetadata)

if !ok {
s.dataSourceTypesDiags.AddError(
"Data Source Type Name Missing",
fmt.Sprintf("The %T DataSource in the provider DataSources method results is missing the Metadata method. ", dataSource)+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}

dataSourceTypeNameReq := datasource.MetadataRequest{
ProviderTypeName: s.providerTypeName,
}
dataSourceTypeNameResp := datasource.MetadataResponse{}

dataSourceWithMetadata.Metadata(ctx, dataSourceTypeNameReq, &dataSourceTypeNameResp)
dataSource.Metadata(ctx, dataSourceTypeNameReq, &dataSourceTypeNameResp)

if dataSourceTypeNameResp.TypeName == "" {
s.dataSourceTypesDiags.AddError(
Expand Down Expand Up @@ -271,19 +230,8 @@ func (s *Server) DataSourceSchemas(ctx context.Context) (map[string]fwschema.Sch
for dataSourceTypeName, dataSourceFunc := range dataSourceFuncs {
dataSource := dataSourceFunc()

dataSourceWithGetSchema, ok := dataSource.(datasource.DataSourceWithGetSchema)

if !ok {
s.dataSourceSchemasDiags.AddError(
"Data Source Schema Missing",
fmt.Sprintf("The %T DataSource in the provider is missing the GetSchema method. ", dataSource)+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}

logging.FrameworkDebug(ctx, "Calling provider defined DataSource GetSchema", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeName})
schema, diags := dataSourceWithGetSchema.GetSchema(ctx)
schema, diags := dataSource.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined DataSource GetSchema", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeName})

s.dataSourceSchemasDiags.Append(diags...)
Expand All @@ -295,65 +243,9 @@ func (s *Server) DataSourceSchemas(ctx context.Context) (map[string]fwschema.Sch
s.dataSourceSchemas[dataSourceTypeName] = schema
}

if len(s.dataSourceSchemas) > 0 || s.dataSourceSchemasDiags.HasError() {
return s.dataSourceSchemas, s.dataSourceSchemasDiags
}

dataSourceTypes, diags := s.DataSourceTypes(ctx)

s.dataSourceSchemasDiags = diags

if s.dataSourceSchemasDiags.HasError() {
return s.dataSourceSchemas, s.dataSourceSchemasDiags
}

for dataSourceTypeName, dataSourceType := range dataSourceTypes {
logging.FrameworkTrace(ctx, "Found data source type", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeName})

logging.FrameworkDebug(ctx, "Calling provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeName})
schema, diags := dataSourceType.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined DataSourceType GetSchema", map[string]interface{}{logging.KeyDataSourceType: dataSourceTypeName})

s.dataSourceSchemasDiags.Append(diags...)

if s.dataSourceSchemasDiags.HasError() {
return s.dataSourceSchemas, s.dataSourceSchemasDiags
}

s.dataSourceSchemas[dataSourceTypeName] = &schema
}

return s.dataSourceSchemas, s.dataSourceSchemasDiags
}

// DataSourceTypes returns the map of DataSourceTypes. The results are cached
// on first use.
//
//nolint:staticcheck // Internal implementation
func (s *Server) DataSourceTypes(ctx context.Context) (map[string]provider.DataSourceType, diag.Diagnostics) {
logging.FrameworkTrace(ctx, "Checking DataSourceTypes lock")
s.dataSourceTypesMutex.Lock()
defer s.dataSourceTypesMutex.Unlock()

if s.dataSourceTypes != nil {
return s.dataSourceTypes, s.dataSourceTypesDiags
}

s.dataSourceTypes = make(map[string]provider.DataSourceType)

providerWithGetDataSources, ok := s.Provider.(provider.ProviderWithGetDataSources) //nolint:staticcheck // Internal usage

if !ok {
return s.dataSourceTypes, nil
}

logging.FrameworkDebug(ctx, "Calling provider defined Provider GetDataSources")
s.dataSourceTypes, s.dataSourceTypesDiags = providerWithGetDataSources.GetDataSources(ctx) //nolint:staticcheck // Internal usage
logging.FrameworkDebug(ctx, "Called provider defined Provider GetDataSources")

return s.dataSourceTypes, s.dataSourceTypesDiags
}

// ProviderSchema returns the Schema associated with the Provider. The Schema
// and Diagnostics are cached on first use.
func (s *Server) ProviderSchema(ctx context.Context) (fwschema.Schema, diag.Diagnostics) {
Expand Down Expand Up @@ -410,14 +302,6 @@ func (s *Server) Resource(ctx context.Context, typeName string) (resource.Resour

resourceFunc, ok := resourceFuncs[typeName]

if ok {
return resourceFunc(), diags
}

resourceTypes, diags := s.ResourceTypes(ctx)

resourceType, ok := resourceTypes[typeName]

if !ok {
diags.AddError(
"Resource Type Not Found",
Expand All @@ -427,11 +311,7 @@ func (s *Server) Resource(ctx context.Context, typeName string) (resource.Resour
return nil, diags
}

logging.FrameworkDebug(ctx, "Calling provider defined ResourceType NewResource")
resource, diags := resourceType.NewResource(ctx, s.Provider)
logging.FrameworkDebug(ctx, "Called provider defined ResourceType NewResource")

return resource, diags
return resourceFunc(), diags
}

// ResourceFuncs returns a map of Resource functions. The results are cached
Expand All @@ -447,36 +327,19 @@ func (s *Server) ResourceFuncs(ctx context.Context) (map[string]func() resource.

s.resourceFuncs = make(map[string]func() resource.Resource)

providerWithResources, ok := s.Provider.(provider.ProviderWithResources)

if !ok {
return s.resourceFuncs, nil
}

logging.FrameworkDebug(ctx, "Calling provider defined Provider Resources")
resourceFuncsSlice := providerWithResources.Resources(ctx)
resourceFuncsSlice := s.Provider.Resources(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Provider Resources")

for _, resourceFunc := range resourceFuncsSlice {
res := resourceFunc()

resourceWithMetadata, ok := res.(resource.ResourceWithMetadata)

if !ok {
s.resourceTypesDiags.AddError(
"Resource Type Name Missing",
fmt.Sprintf("The %T Resource in the provider Resources method results is missing the Metadata method. ", res)+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}

resourceTypeNameReq := resource.MetadataRequest{
ProviderTypeName: s.providerTypeName,
}
resourceTypeNameResp := resource.MetadataResponse{}

resourceWithMetadata.Metadata(ctx, resourceTypeNameReq, &resourceTypeNameResp)
res.Metadata(ctx, resourceTypeNameReq, &resourceTypeNameResp)

if resourceTypeNameResp.TypeName == "" {
s.resourceTypesDiags.AddError(
Expand Down Expand Up @@ -545,19 +408,8 @@ func (s *Server) ResourceSchemas(ctx context.Context) (map[string]fwschema.Schem
for resourceTypeName, resourceFunc := range resourceFuncs {
res := resourceFunc()

resourceWithGetSchema, ok := res.(resource.ResourceWithGetSchema)

if !ok {
s.resourceSchemasDiags.AddError(
"Resource Schema Missing",
fmt.Sprintf("The %T Resource in the provider is missing the GetSchema method. ", res)+
"This is always an issue with the provider and should be reported to the provider developers.",
)
continue
}

logging.FrameworkDebug(ctx, "Calling provider defined Resource GetSchema", map[string]interface{}{logging.KeyResourceType: resourceTypeName})
schema, diags := resourceWithGetSchema.GetSchema(ctx)
schema, diags := res.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined Resource GetSchema", map[string]interface{}{logging.KeyResourceType: resourceTypeName})

s.resourceSchemasDiags.Append(diags...)
Expand All @@ -569,61 +421,5 @@ func (s *Server) ResourceSchemas(ctx context.Context) (map[string]fwschema.Schem
s.resourceSchemas[resourceTypeName] = schema
}

if len(s.resourceSchemas) > 0 || s.resourceSchemasDiags.HasError() {
return s.resourceSchemas, s.resourceSchemasDiags
}

resourceTypes, diags := s.ResourceTypes(ctx)

s.resourceSchemasDiags = diags

if s.resourceSchemasDiags.HasError() {
return s.resourceSchemas, s.resourceSchemasDiags
}

for resourceTypeName, resourceType := range resourceTypes {
logging.FrameworkTrace(ctx, "Found resource type", map[string]interface{}{logging.KeyResourceType: resourceTypeName})

logging.FrameworkDebug(ctx, "Calling provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: resourceTypeName})
schema, diags := resourceType.GetSchema(ctx)
logging.FrameworkDebug(ctx, "Called provider defined ResourceType GetSchema", map[string]interface{}{logging.KeyResourceType: resourceTypeName})

s.resourceSchemasDiags.Append(diags...)

if s.resourceSchemasDiags.HasError() {
return s.resourceSchemas, s.resourceSchemasDiags
}

s.resourceSchemas[resourceTypeName] = &schema
}

return s.resourceSchemas, s.resourceSchemasDiags
}

// ResourceTypes returns the map of ResourceTypes. The results are cached
// on first use.
//
//nolint:staticcheck // Internal implementation
func (s *Server) ResourceTypes(ctx context.Context) (map[string]provider.ResourceType, diag.Diagnostics) {
logging.FrameworkTrace(ctx, "Checking ResourceTypes lock")
s.resourceTypesMutex.Lock()
defer s.resourceTypesMutex.Unlock()

if s.resourceTypes != nil {
return s.resourceTypes, s.resourceTypesDiags
}

s.resourceTypes = make(map[string]provider.ResourceType)

providerWithGetResources, ok := s.Provider.(provider.ProviderWithGetResources) //nolint:staticcheck // Internal usage

if !ok {
return s.resourceTypes, nil
}

logging.FrameworkDebug(ctx, "Calling provider defined Provider GetResources")
s.resourceTypes, s.resourceTypesDiags = providerWithGetResources.GetResources(ctx) //nolint:staticcheck // Internal usage
logging.FrameworkDebug(ctx, "Called provider defined Provider GetResources")

return s.resourceTypes, s.resourceTypesDiags
}

0 comments on commit 46b5573

Please sign in to comment.