providers: A type for all schemas for a particular provider

Previously the "providers" package contained only a type for representing
the schema of a particular object within a provider, and the terraform
package had the responsibility of aggregating many of those together to
describe the entire surface area of a provider.

Here we move what was previously terraform.ProviderSchema to instead be
providers.Schemas, retaining its existing API otherwise, and leave behind
a type alias to allow us to gradually update other references over time.

We've gradually been shrinking down the responsibilities of the
"terraform" package to just representing the graph components and
behaviors anyway, but the specific motivation for doing this _now_ is to
allow for other packages to both be called by the terraform package _and_
work with provider schemas at the same time, without creating a package
dependency cycle: instead, these other packages can just import the
"providers" package and not need to import the "terraform" package at all.

For now this does still leave the responsibility for _building_ a
providers.Schemas object over in the "terraform" package, because it's
currently doing that as part of some larger work that isn't easily
separable, and so reorganizing that would be a more involved and riskier
change than just moving the existing type elsewhere.
This commit is contained in:
Martin Atkins 2021-06-11 11:59:49 -07:00 committed by James Bardin
parent 2453025a1a
commit 1425374371
3 changed files with 73 additions and 49 deletions

View File

@ -3,7 +3,6 @@ package providers
import ( import (
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/internal/tfdiags"
) )
@ -88,13 +87,6 @@ type GetProviderSchemaResponse struct {
Diagnostics tfdiags.Diagnostics Diagnostics tfdiags.Diagnostics
} }
// Schema pairs a provider or resource schema with that schema's version.
// This is used to be able to upgrade the schema in UpgradeResourceState.
type Schema struct {
Version int64
Block *configschema.Block
}
type ValidateProviderConfigRequest struct { type ValidateProviderConfigRequest struct {
// Config is the raw configuration value for the provider. // Config is the raw configuration value for the provider.
Config cty.Value Config cty.Value

View File

@ -0,0 +1,62 @@
package providers
import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
)
// Schemas is an overall container for all of the schemas for all configurable
// objects defined within a particular provider.
//
// The schema for each individual configurable object is represented by nested
// instances of type Schema (singular) within this data structure.
//
// This type used to be known as terraform.ProviderSchema, but moved out here
// as part of our ongoing efforts to shrink down the "terraform" package.
// There's still a type alias at the old name, but we should prefer using
// providers.Schema in new code. However, a consequence of this transitional
// situation is that the "terraform" package still has the responsibility for
// constructing a providers.Schemas object based on responses from the provider
// API; hopefully we'll continue this refactor later so that functions in this
// package totally encapsulate the unmarshalling and include this as part of
// providers.GetProviderSchemaResponse.
type Schemas struct {
Provider *configschema.Block
ProviderMeta *configschema.Block
ResourceTypes map[string]*configschema.Block
DataSources map[string]*configschema.Block
ResourceTypeSchemaVersions map[string]uint64
}
// SchemaForResourceType attempts to find a schema for the given mode and type.
// Returns nil if no such schema is available.
func (ss *Schemas) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema *configschema.Block, version uint64) {
switch mode {
case addrs.ManagedResourceMode:
return ss.ResourceTypes[typeName], ss.ResourceTypeSchemaVersions[typeName]
case addrs.DataResourceMode:
// Data resources don't have schema versions right now, since state is discarded for each refresh
return ss.DataSources[typeName], 0
default:
// Shouldn't happen, because the above cases are comprehensive.
return nil, 0
}
}
// SchemaForResourceAddr attempts to find a schema for the mode and type from
// the given resource address. Returns nil if no such schema is available.
func (ss *Schemas) SchemaForResourceAddr(addr addrs.Resource) (schema *configschema.Block, version uint64) {
return ss.SchemaForResourceType(addr.Mode, addr.Type)
}
// Schema pairs a provider or resource schema with that schema's version.
// This is used to be able to upgrade the schema in UpgradeResourceState.
//
// This describes the schema for a single object within a provider. Type
// "Schemas" (plural) instead represents the overall collection of schemas
// for everything within a particular provider.
type Schema struct {
Version int64
Block *configschema.Block
}

View File

@ -12,10 +12,16 @@ import (
"github.com/hashicorp/terraform/internal/tfdiags" "github.com/hashicorp/terraform/internal/tfdiags"
) )
// ProviderSchema is an alias for providers.Schemas, which is the new location
// for what we originally called terraform.ProviderSchema but which has
// moved out as part of ongoing refactoring to shrink down the main "terraform"
// package.
type ProviderSchema = providers.Schemas
// Schemas is a container for various kinds of schema that Terraform needs // Schemas is a container for various kinds of schema that Terraform needs
// during processing. // during processing.
type Schemas struct { type Schemas struct {
Providers map[addrs.Provider]*ProviderSchema Providers map[addrs.Provider]*providers.Schemas
Provisioners map[string]*configschema.Block Provisioners map[string]*configschema.Block
} }
@ -24,7 +30,7 @@ type Schemas struct {
// //
// It's usually better to go use the more precise methods offered by type // It's usually better to go use the more precise methods offered by type
// Schemas to handle this detail automatically. // Schemas to handle this detail automatically.
func (ss *Schemas) ProviderSchema(provider addrs.Provider) *ProviderSchema { func (ss *Schemas) ProviderSchema(provider addrs.Provider) *providers.Schemas {
if ss.Providers == nil { if ss.Providers == nil {
return nil return nil
} }
@ -76,7 +82,7 @@ func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
// still valid but may be incomplete. // still valid but may be incomplete.
func loadSchemas(config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) { func loadSchemas(config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) {
schemas := &Schemas{ schemas := &Schemas{
Providers: map[addrs.Provider]*ProviderSchema{}, Providers: map[addrs.Provider]*providers.Schemas{},
Provisioners: map[string]*configschema.Block{}, Provisioners: map[string]*configschema.Block{},
} }
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
@ -89,7 +95,7 @@ func loadSchemas(config *configs.Config, state *states.State, plugins *contextPl
return schemas, diags.Err() return schemas, diags.Err()
} }
func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *configs.Config, state *states.State, plugins *contextPlugins) tfdiags.Diagnostics { func loadProviderSchemas(schemas map[addrs.Provider]*providers.Schemas, config *configs.Config, state *states.State, plugins *contextPlugins) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
ensure := func(fqn addrs.Provider) { ensure := func(fqn addrs.Provider) {
@ -105,7 +111,7 @@ func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *con
// We'll put a stub in the map so we won't re-attempt this on // We'll put a stub in the map so we won't re-attempt this on
// future calls, which would then repeat the same error message // future calls, which would then repeat the same error message
// multiple times. // multiple times.
schemas[fqn] = &ProviderSchema{} schemas[fqn] = &providers.Schemas{}
diags = diags.Append( diags = diags.Append(
tfdiags.Sourceless( tfdiags.Sourceless(
tfdiags.Error, tfdiags.Error,
@ -179,39 +185,3 @@ func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *conf
return diags return diags
} }
// ProviderSchema represents the schema for a provider's own configuration
// and the configuration for some or all of its resources and data sources.
//
// The completeness of this structure depends on how it was constructed.
// When constructed for a configuration, it will generally include only
// resource types and data sources used by that configuration.
type ProviderSchema struct {
Provider *configschema.Block
ProviderMeta *configschema.Block
ResourceTypes map[string]*configschema.Block
DataSources map[string]*configschema.Block
ResourceTypeSchemaVersions map[string]uint64
}
// SchemaForResourceType attempts to find a schema for the given mode and type.
// Returns nil if no such schema is available.
func (ps *ProviderSchema) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema *configschema.Block, version uint64) {
switch mode {
case addrs.ManagedResourceMode:
return ps.ResourceTypes[typeName], ps.ResourceTypeSchemaVersions[typeName]
case addrs.DataResourceMode:
// Data resources don't have schema versions right now, since state is discarded for each refresh
return ps.DataSources[typeName], 0
default:
// Shouldn't happen, because the above cases are comprehensive.
return nil, 0
}
}
// SchemaForResourceAddr attempts to find a schema for the mode and type from
// the given resource address. Returns nil if no such schema is available.
func (ps *ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema *configschema.Block, version uint64) {
return ps.SchemaForResourceType(addr.Mode, addr.Type)
}