Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider: Remove DataSourceType and ResourceType types, Provider GetDataSources and GetResources methods #478

Merged
merged 4 commits into from Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}