mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
1425374371
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.
188 lines
6.1 KiB
Go
188 lines
6.1 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/configs"
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
|
"github.com/hashicorp/terraform/internal/providers"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
"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
|
|
// during processing.
|
|
type Schemas struct {
|
|
Providers map[addrs.Provider]*providers.Schemas
|
|
Provisioners map[string]*configschema.Block
|
|
}
|
|
|
|
// ProviderSchema returns the entire ProviderSchema object that was produced
|
|
// by the plugin for the given provider, or nil if no such schema is available.
|
|
//
|
|
// It's usually better to go use the more precise methods offered by type
|
|
// Schemas to handle this detail automatically.
|
|
func (ss *Schemas) ProviderSchema(provider addrs.Provider) *providers.Schemas {
|
|
if ss.Providers == nil {
|
|
return nil
|
|
}
|
|
return ss.Providers[provider]
|
|
}
|
|
|
|
// ProviderConfig returns the schema for the provider configuration of the
|
|
// given provider type, or nil if no such schema is available.
|
|
func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
|
|
ps := ss.ProviderSchema(provider)
|
|
if ps == nil {
|
|
return nil
|
|
}
|
|
return ps.Provider
|
|
}
|
|
|
|
// ResourceTypeConfig returns the schema for the configuration of a given
|
|
// resource type belonging to a given provider type, or nil of no such
|
|
// schema is available.
|
|
//
|
|
// In many cases the provider type is inferrable from the resource type name,
|
|
// but this is not always true because users can override the provider for
|
|
// a resource using the "provider" meta-argument. Therefore it's important to
|
|
// always pass the correct provider name, even though it many cases it feels
|
|
// redundant.
|
|
func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) {
|
|
ps := ss.ProviderSchema(provider)
|
|
if ps == nil || ps.ResourceTypes == nil {
|
|
return nil, 0
|
|
}
|
|
return ps.SchemaForResourceType(resourceMode, resourceType)
|
|
}
|
|
|
|
// ProvisionerConfig returns the schema for the configuration of a given
|
|
// provisioner, or nil of no such schema is available.
|
|
func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
|
|
return ss.Provisioners[name]
|
|
}
|
|
|
|
// loadSchemas searches the given configuration, state and plan (any of which
|
|
// may be nil) for constructs that have an associated schema, requests the
|
|
// necessary schemas from the given component factory (which must _not_ be nil),
|
|
// and returns a single object representing all of the necessary schemas.
|
|
//
|
|
// If an error is returned, it may be a wrapped tfdiags.Diagnostics describing
|
|
// errors across multiple separate objects. Errors here will usually indicate
|
|
// either misbehavior on the part of one of the providers or of the provider
|
|
// protocol itself. When returned with errors, the returned schemas object is
|
|
// still valid but may be incomplete.
|
|
func loadSchemas(config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) {
|
|
schemas := &Schemas{
|
|
Providers: map[addrs.Provider]*providers.Schemas{},
|
|
Provisioners: map[string]*configschema.Block{},
|
|
}
|
|
var diags tfdiags.Diagnostics
|
|
|
|
newDiags := loadProviderSchemas(schemas.Providers, config, state, plugins)
|
|
diags = diags.Append(newDiags)
|
|
newDiags = loadProvisionerSchemas(schemas.Provisioners, config, plugins)
|
|
diags = diags.Append(newDiags)
|
|
|
|
return schemas, diags.Err()
|
|
}
|
|
|
|
func loadProviderSchemas(schemas map[addrs.Provider]*providers.Schemas, config *configs.Config, state *states.State, plugins *contextPlugins) tfdiags.Diagnostics {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
ensure := func(fqn addrs.Provider) {
|
|
name := fqn.String()
|
|
|
|
if _, exists := schemas[fqn]; exists {
|
|
return
|
|
}
|
|
|
|
log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", name)
|
|
schema, err := plugins.ProviderSchema(fqn)
|
|
if err != nil {
|
|
// 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
|
|
// multiple times.
|
|
schemas[fqn] = &providers.Schemas{}
|
|
diags = diags.Append(
|
|
tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Failed to obtain provider schema",
|
|
fmt.Sprintf("Could not load the schema for provider %s: %s.", fqn, err),
|
|
),
|
|
)
|
|
return
|
|
}
|
|
|
|
schemas[fqn] = schema
|
|
}
|
|
|
|
if config != nil {
|
|
for _, fqn := range config.ProviderTypes() {
|
|
ensure(fqn)
|
|
}
|
|
}
|
|
|
|
if state != nil {
|
|
needed := providers.AddressedTypesAbs(state.ProviderAddrs())
|
|
for _, typeAddr := range needed {
|
|
ensure(typeAddr)
|
|
}
|
|
}
|
|
|
|
return diags
|
|
}
|
|
|
|
func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, plugins *contextPlugins) tfdiags.Diagnostics {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
ensure := func(name string) {
|
|
if _, exists := schemas[name]; exists {
|
|
return
|
|
}
|
|
|
|
log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name)
|
|
schema, err := plugins.ProvisionerSchema(name)
|
|
if err != nil {
|
|
// 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
|
|
// multiple times.
|
|
schemas[name] = &configschema.Block{}
|
|
diags = diags.Append(
|
|
tfdiags.Sourceless(
|
|
tfdiags.Error,
|
|
"Failed to obtain provisioner schema",
|
|
fmt.Sprintf("Could not load the schema for provisioner %q: %s.", name, err),
|
|
),
|
|
)
|
|
return
|
|
}
|
|
|
|
schemas[name] = schema
|
|
}
|
|
|
|
if config != nil {
|
|
for _, rc := range config.Module.ManagedResources {
|
|
for _, pc := range rc.Managed.Provisioners {
|
|
ensure(pc.Type)
|
|
}
|
|
}
|
|
|
|
// Must also visit our child modules, recursively.
|
|
for _, cc := range config.Children {
|
|
childDiags := loadProvisionerSchemas(schemas, cc, plugins)
|
|
diags = diags.Append(childDiags)
|
|
}
|
|
}
|
|
|
|
return diags
|
|
}
|