2017-10-13 20:43:08 -05:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
2018-05-31 14:39:45 -05:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
|
2021-05-17 14:00:50 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
2021-05-17 14:17:09 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/configs"
|
|
|
|
"github.com/hashicorp/terraform/internal/configs/configschema"
|
2021-05-17 12:40:40 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/providers"
|
2021-05-17 14:43:35 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/states"
|
2021-05-17 12:11:06 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
2017-10-13 20:43:08 -05:00
|
|
|
)
|
|
|
|
|
2018-05-31 14:39:45 -05:00
|
|
|
// Schemas is a container for various kinds of schema that Terraform needs
|
|
|
|
// during processing.
|
2017-10-13 20:43:08 -05:00
|
|
|
type Schemas struct {
|
2020-02-03 07:18:04 -06:00
|
|
|
Providers map[addrs.Provider]*ProviderSchema
|
2018-09-25 12:12:56 -05:00
|
|
|
Provisioners map[string]*configschema.Block
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
|
2018-06-01 14:36:55 -05:00
|
|
|
// 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.
|
2020-02-03 07:18:04 -06:00
|
|
|
func (ss *Schemas) ProviderSchema(provider addrs.Provider) *ProviderSchema {
|
2018-09-25 12:12:56 -05:00
|
|
|
if ss.Providers == nil {
|
2018-06-01 14:36:55 -05:00
|
|
|
return nil
|
|
|
|
}
|
2020-02-03 07:18:04 -06:00
|
|
|
return ss.Providers[provider]
|
2018-06-01 14:36:55 -05:00
|
|
|
}
|
|
|
|
|
2018-05-31 14:39:45 -05:00
|
|
|
// ProviderConfig returns the schema for the provider configuration of the
|
|
|
|
// given provider type, or nil if no such schema is available.
|
2020-02-03 07:18:04 -06:00
|
|
|
func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block {
|
|
|
|
ps := ss.ProviderSchema(provider)
|
2018-06-01 14:36:55 -05:00
|
|
|
if ps == nil {
|
2018-05-31 14:39:45 -05:00
|
|
|
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.
|
2020-02-03 07:18:04 -06:00
|
|
|
func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) {
|
|
|
|
ps := ss.ProviderSchema(provider)
|
2018-06-01 14:36:55 -05:00
|
|
|
if ps == nil || ps.ResourceTypes == nil {
|
2018-11-27 17:30:18 -06:00
|
|
|
return nil, 0
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
2018-11-27 17:30:18 -06:00
|
|
|
return ps.SchemaForResourceType(resourceMode, resourceType)
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2018-09-25 12:12:56 -05:00
|
|
|
return ss.Provisioners[name]
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
|
2021-08-31 11:37:29 -05:00
|
|
|
// loadSchemas searches the given configuration, state and plan (any of which
|
various: helpers for collecting necessary provider types
Since schemas are required to interpret provider, resource, and
provisioner attributes in configs, states, and plans, these helpers intend
to make it easier to gather up the the necessary provider types in order
to preload all of the needed schemas before beginning further processing.
Config.ProviderTypes returns directly the list of provider types, since
at this level further detail is not useful: we've not yet run the
provider allocation algorithm, and so the only thing we can reliably
extract here is provider types themselves.
State.ProviderAddrs and Plan.ProviderAddrs each return a list of
absolute provider addresses, which can then be turned into a list of
provider types using the new helper providers.AddressedTypesAbs.
Since we're already using configs.Config throughout core, this also
updates the terraform.LoadSchemas helper to use Config.ProviderTypes
to find the necessary providers, rather than implementing its own
discovery logic. states.State is not yet plumbed in, so we cannot yet
use State.ProviderAddrs to deal with the state but there's a TODO comment
to remind us to update that in a later commit when we swap out
terraform.State for states.State.
A later commit will probably refactor this further so that we can easily
obtain schema for the providers needed to interpret a plan too, but that
is deferred here because further work is required to make core work with
the new plan types first. At that point, terraform.LoadSchemas may become
providers.LoadSchemas with a different interface that just accepts lists
of provider and provisioner names that have been gathered by the caller
using these new helpers.
2018-06-21 19:39:27 -05:00
|
|
|
// may be nil) for constructs that have an associated schema, requests the
|
|
|
|
// necessary schemas from the given component factory (which must _not_ be nil),
|
2018-05-31 14:39:45 -05:00
|
|
|
// 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.
|
2021-08-31 12:58:05 -05:00
|
|
|
func loadSchemas(config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) {
|
2018-05-31 14:39:45 -05:00
|
|
|
schemas := &Schemas{
|
2020-02-03 07:18:04 -06:00
|
|
|
Providers: map[addrs.Provider]*ProviderSchema{},
|
2018-09-25 12:12:56 -05:00
|
|
|
Provisioners: map[string]*configschema.Block{},
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
var diags tfdiags.Diagnostics
|
|
|
|
|
2021-08-31 12:58:05 -05:00
|
|
|
newDiags := loadProviderSchemas(schemas.Providers, config, state, plugins)
|
2018-05-31 14:39:45 -05:00
|
|
|
diags = diags.Append(newDiags)
|
2021-08-31 12:58:05 -05:00
|
|
|
newDiags = loadProvisionerSchemas(schemas.Provisioners, config, plugins)
|
2018-05-31 14:39:45 -05:00
|
|
|
diags = diags.Append(newDiags)
|
|
|
|
|
|
|
|
return schemas, diags.Err()
|
|
|
|
}
|
|
|
|
|
2021-08-31 12:58:05 -05:00
|
|
|
func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *configs.Config, state *states.State, plugins *contextPlugins) tfdiags.Diagnostics {
|
2018-05-31 14:39:45 -05:00
|
|
|
var diags tfdiags.Diagnostics
|
|
|
|
|
2020-02-03 07:18:04 -06:00
|
|
|
ensure := func(fqn addrs.Provider) {
|
2020-03-30 17:32:31 -05:00
|
|
|
name := fqn.String()
|
Initial steps towards AbsProviderConfig/LocalProviderConfig separation (#23978)
* Introduce "Local" terminology for non-absolute provider config addresses
In a future change AbsProviderConfig and LocalProviderConfig are going to
become two entirely distinct types, rather than Abs embedding Local as
written here. This naming change is in preparation for that subsequent
work, which will also include introducing a new "ProviderConfig" type
that is an interface that AbsProviderConfig and LocalProviderConfig both
implement.
This is intended to be largely just a naming change to get started, so
we can deal with all of the messy renaming. However, this did also require
a slight change in modeling where the Resource.DefaultProviderConfig
method has become Resource.DefaultProvider returning a Provider address
directly, because this method doesn't have enough information to construct
a true and accurate LocalProviderConfig -- it would need to refer to the
configuration to know what this module is calling the provider it has
selected.
In order to leave a trail to follow for subsequent work, all of the
changes here are intended to ensure that remaining work will become
obvious via compile-time errors when all of the following changes happen:
- The concept of "legacy" provider addresses is removed from the addrs
package, including removing addrs.NewLegacyProvider and
addrs.Provider.LegacyString.
- addrs.AbsProviderConfig stops having addrs.LocalProviderConfig embedded
in it and has an addrs.Provider and a string alias directly instead.
- The provider-schema-handling parts of Terraform core are updated to
work with addrs.Provider to identify providers, rather than legacy
strings.
In particular, there are still several codepaths here making legacy
provider address assumptions (in order to limit the scope of this change)
but I've made sure each one is doing something that relies on at least
one of the above changes not having been made yet.
* addrs: ProviderConfig interface
In a (very) few special situations in the main "terraform" package we need
to make runtime decisions about whether a provider config is absolute
or local.
We currently do that by exploiting the fact that AbsProviderConfig has
LocalProviderConfig nested inside of it and so in the local case we can
just ignore the wrapping AbsProviderConfig and use the embedded value.
In a future change we'll be moving away from that embedding and making
these two types distinct in order to represent that mapping between them
requires consulting a lookup table in the configuration, and so here we
introduce a new interface type ProviderConfig that can represent either
AbsProviderConfig or LocalProviderConfig decided dynamically at runtime.
This also includes the Config.ResolveAbsProviderAddr method that will
eventually be responsible for that local-to-absolute translation, so
that callers with access to the configuration can normalize to an
addrs.AbsProviderConfig given a non-nil addrs.ProviderConfig. That's
currently unused because existing callers are still relying on the
simplistic structural transform, but we'll switch them over in a later
commit.
* rename LocalType to LocalName
Co-authored-by: Kristin Laemmert <mildwonkey@users.noreply.github.com>
2020-01-31 07:23:07 -06:00
|
|
|
|
2020-02-03 07:18:04 -06:00
|
|
|
if _, exists := schemas[fqn]; exists {
|
2018-05-31 14:39:45 -05:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-30 17:32:31 -05:00
|
|
|
log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", name)
|
2021-08-31 13:27:07 -05:00
|
|
|
schema, err := plugins.ProviderSchema(fqn)
|
2018-05-31 14:39:45 -05:00
|
|
|
if err != nil {
|
|
|
|
// We'll put a stub in the map so we won't re-attempt this on
|
2021-08-31 13:27:07 -05:00
|
|
|
// future calls, which would then repeat the same error message
|
|
|
|
// multiple times.
|
2020-02-03 07:18:04 -06:00
|
|
|
schemas[fqn] = &ProviderSchema{}
|
2018-05-31 14:39:45 -05:00
|
|
|
diags = diags.Append(
|
2021-08-31 13:27:07 -05:00
|
|
|
tfdiags.Sourceless(
|
|
|
|
tfdiags.Error,
|
|
|
|
"Failed to obtain provider schema",
|
|
|
|
fmt.Sprintf("Could not load the schema for provider %s: %s.", fqn, err),
|
|
|
|
),
|
2018-05-31 14:39:45 -05:00
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-31 13:27:07 -05:00
|
|
|
schemas[fqn] = schema
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if config != nil {
|
2020-02-03 07:18:04 -06:00
|
|
|
for _, fqn := range config.ProviderTypes() {
|
|
|
|
ensure(fqn)
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if state != nil {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
needed := providers.AddressedTypesAbs(state.ProviderAddrs())
|
Initial steps towards AbsProviderConfig/LocalProviderConfig separation (#23978)
* Introduce "Local" terminology for non-absolute provider config addresses
In a future change AbsProviderConfig and LocalProviderConfig are going to
become two entirely distinct types, rather than Abs embedding Local as
written here. This naming change is in preparation for that subsequent
work, which will also include introducing a new "ProviderConfig" type
that is an interface that AbsProviderConfig and LocalProviderConfig both
implement.
This is intended to be largely just a naming change to get started, so
we can deal with all of the messy renaming. However, this did also require
a slight change in modeling where the Resource.DefaultProviderConfig
method has become Resource.DefaultProvider returning a Provider address
directly, because this method doesn't have enough information to construct
a true and accurate LocalProviderConfig -- it would need to refer to the
configuration to know what this module is calling the provider it has
selected.
In order to leave a trail to follow for subsequent work, all of the
changes here are intended to ensure that remaining work will become
obvious via compile-time errors when all of the following changes happen:
- The concept of "legacy" provider addresses is removed from the addrs
package, including removing addrs.NewLegacyProvider and
addrs.Provider.LegacyString.
- addrs.AbsProviderConfig stops having addrs.LocalProviderConfig embedded
in it and has an addrs.Provider and a string alias directly instead.
- The provider-schema-handling parts of Terraform core are updated to
work with addrs.Provider to identify providers, rather than legacy
strings.
In particular, there are still several codepaths here making legacy
provider address assumptions (in order to limit the scope of this change)
but I've made sure each one is doing something that relies on at least
one of the above changes not having been made yet.
* addrs: ProviderConfig interface
In a (very) few special situations in the main "terraform" package we need
to make runtime decisions about whether a provider config is absolute
or local.
We currently do that by exploiting the fact that AbsProviderConfig has
LocalProviderConfig nested inside of it and so in the local case we can
just ignore the wrapping AbsProviderConfig and use the embedded value.
In a future change we'll be moving away from that embedding and making
these two types distinct in order to represent that mapping between them
requires consulting a lookup table in the configuration, and so here we
introduce a new interface type ProviderConfig that can represent either
AbsProviderConfig or LocalProviderConfig decided dynamically at runtime.
This also includes the Config.ResolveAbsProviderAddr method that will
eventually be responsible for that local-to-absolute translation, so
that callers with access to the configuration can normalize to an
addrs.AbsProviderConfig given a non-nil addrs.ProviderConfig. That's
currently unused because existing callers are still relying on the
simplistic structural transform, but we'll switch them over in a later
commit.
* rename LocalType to LocalName
Co-authored-by: Kristin Laemmert <mildwonkey@users.noreply.github.com>
2020-01-31 07:23:07 -06:00
|
|
|
for _, typeAddr := range needed {
|
|
|
|
ensure(typeAddr)
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return diags
|
|
|
|
}
|
|
|
|
|
2021-08-31 12:58:05 -05:00
|
|
|
func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, plugins *contextPlugins) tfdiags.Diagnostics {
|
2018-05-31 14:39:45 -05:00
|
|
|
var diags tfdiags.Diagnostics
|
|
|
|
|
|
|
|
ensure := func(name string) {
|
|
|
|
if _, exists := schemas[name]; exists {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-31 18:55:39 -05:00
|
|
|
log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name)
|
2021-08-31 13:27:07 -05:00
|
|
|
schema, err := plugins.ProvisionerSchema(name)
|
2018-05-31 14:39:45 -05:00
|
|
|
if err != nil {
|
|
|
|
// We'll put a stub in the map so we won't re-attempt this on
|
2021-08-31 13:27:07 -05:00
|
|
|
// future calls, which would then repeat the same error message
|
|
|
|
// multiple times.
|
2018-05-31 14:39:45 -05:00
|
|
|
schemas[name] = &configschema.Block{}
|
|
|
|
diags = diags.Append(
|
2021-08-31 13:27:07 -05:00
|
|
|
tfdiags.Sourceless(
|
|
|
|
tfdiags.Error,
|
|
|
|
"Failed to obtain provisioner schema",
|
|
|
|
fmt.Sprintf("Could not load the schema for provisioner %q: %s.", name, err),
|
|
|
|
),
|
2018-05-31 14:39:45 -05:00
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-31 13:27:07 -05:00
|
|
|
schemas[name] = schema
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if config != nil {
|
|
|
|
for _, rc := range config.Module.ManagedResources {
|
|
|
|
for _, pc := range rc.Managed.Provisioners {
|
|
|
|
ensure(pc.Type)
|
|
|
|
}
|
|
|
|
}
|
2018-06-01 14:50:44 -05:00
|
|
|
|
|
|
|
// Must also visit our child modules, recursively.
|
|
|
|
for _, cc := range config.Children {
|
2021-08-31 12:58:05 -05:00
|
|
|
childDiags := loadProvisionerSchemas(schemas, cc, plugins)
|
2018-06-01 14:50:44 -05:00
|
|
|
diags = diags.Append(childDiags)
|
|
|
|
}
|
2018-05-31 14:39:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return diags
|
|
|
|
}
|
2017-10-13 20:43:08 -05:00
|
|
|
|
|
|
|
// 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
|
2020-03-05 18:53:24 -06:00
|
|
|
ProviderMeta *configschema.Block
|
2017-10-13 20:43:08 -05:00
|
|
|
ResourceTypes map[string]*configschema.Block
|
|
|
|
DataSources map[string]*configschema.Block
|
2018-11-27 17:30:18 -06:00
|
|
|
|
|
|
|
ResourceTypeSchemaVersions map[string]uint64
|
2017-10-13 20:43:08 -05:00
|
|
|
}
|
|
|
|
|
2018-11-27 17:30:18 -06:00
|
|
|
// 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 {
|
2018-08-28 16:55:28 -05:00
|
|
|
case addrs.ManagedResourceMode:
|
2018-11-27 17:30:18 -06:00
|
|
|
return ps.ResourceTypes[typeName], ps.ResourceTypeSchemaVersions[typeName]
|
2018-08-28 16:55:28 -05:00
|
|
|
case addrs.DataResourceMode:
|
2018-11-27 17:30:18 -06:00
|
|
|
// Data resources don't have schema versions right now, since state is discarded for each refresh
|
|
|
|
return ps.DataSources[typeName], 0
|
2018-08-28 16:55:28 -05:00
|
|
|
default:
|
|
|
|
// Shouldn't happen, because the above cases are comprehensive.
|
2018-11-27 17:30:18 -06:00
|
|
|
return nil, 0
|
2018-08-28 16:55:28 -05:00
|
|
|
}
|
2018-11-27 17:30:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2018-08-28 16:55:28 -05:00
|
|
|
}
|