terraform: refactor ProvidedBy() to return an addrs.ProviderConfig interface (#24295)

* terraform: refactor ProvidedBy() to return an addrs.ProviderConfig
interface

This refactor allows terraform to indicate whether a specific provider
configuration was found for the resource or if it is instead returning
the assumed default.

With that additional information the provider transformer can check if
there is a specific (non-default) provider FQN.
This commit is contained in:
Kristin Laemmert 2020-03-06 08:33:44 -05:00 committed by GitHub
parent e6592dc710
commit 6118d22c1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 102 additions and 42 deletions

View File

@ -285,25 +285,17 @@ func (n *NodeAbstractResource) SetProvider(p addrs.AbsProviderConfig) {
} }
// GraphNodeProviderConsumer // GraphNodeProviderConsumer
func (n *NodeAbstractResource) ProvidedBy() (addrs.AbsProviderConfig, bool) { func (n *NodeAbstractResource) ProvidedBy() (addrs.ProviderConfig, bool) {
// If we have a config we prefer that above all else // If we have a config we prefer that above all else
if n.Config != nil { if n.Config != nil {
relAddr := n.Config.ProviderConfigAddr() relAddr := n.Config.ProviderConfigAddr()
// FIXME: this will need to lookup the provider and see if there's an return addrs.LocalProviderConfig{
// FQN associated with the local config LocalName: relAddr.LocalName,
fqn := addrs.NewLegacyProvider(relAddr.LocalName) Alias: relAddr.Alias,
return addrs.AbsProviderConfig{
Provider: fqn,
Module: n.Path(),
Alias: relAddr.Alias,
}, false }, false
} }
// Use our type and containing module path to guess a provider configuration address. // Use our type and containing module path to guess a provider configuration address.
// FIXME: This is relying on the FQN-to-local matching true only of legacy
// addresses, so this will need to switch to using an addrs.LocalProviderConfig
// with the local name here, once we've done the work elsewhere to make
// that possible.
defaultFQN := n.Addr.Resource.DefaultProvider() defaultFQN := n.Addr.Resource.DefaultProvider()
return addrs.AbsProviderConfig{ return addrs.AbsProviderConfig{
Provider: defaultFQN, Provider: defaultFQN,
@ -312,19 +304,13 @@ func (n *NodeAbstractResource) ProvidedBy() (addrs.AbsProviderConfig, bool) {
} }
// GraphNodeProviderConsumer // GraphNodeProviderConsumer
func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.AbsProviderConfig, bool) { func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.ProviderConfig, bool) {
// If we have a config we prefer that above all else // If we have a config we prefer that above all else
if n.Config != nil { if n.Config != nil {
relAddr := n.Config.ProviderConfigAddr() relAddr := n.Config.ProviderConfigAddr()
// Use our type and containing module path to guess a provider configuration address. return addrs.LocalProviderConfig{
// FIXME: This is relying on the FQN-to-local matching true only of legacy LocalName: relAddr.LocalName,
// addresses. Alias: relAddr.Alias,
fqn := addrs.NewLegacyProvider(relAddr.LocalName)
return addrs.AbsProviderConfig{
Provider: fqn,
Module: n.Path(),
Alias: relAddr.Alias,
}, false }, false
} }
@ -337,10 +323,6 @@ func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.AbsProviderConfig, bo
} }
// Use our type and containing module path to guess a provider configuration address // Use our type and containing module path to guess a provider configuration address
// FIXME: This is relying on the FQN-to-local matching true only of legacy
// addresses, so this will need to switch to using an addrs.LocalProviderConfig
// with the local name here, once we've done the work elsewhere to make
// that possible.
defaultFQN := n.Addr.Resource.DefaultProvider() defaultFQN := n.Addr.Resource.DefaultProvider()
return addrs.AbsProviderConfig{ return addrs.AbsProviderConfig{
Provider: defaultFQN, Provider: defaultFQN,

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/dag"
) )
@ -59,8 +60,17 @@ func (t *AttachSchemaTransformer) Transform(g *Graph) error {
mode := addr.Resource.Mode mode := addr.Resource.Mode
typeName := addr.Resource.Type typeName := addr.Resource.Type
providerAddr, _ := tv.ProvidedBy() providerAddr, _ := tv.ProvidedBy()
var providerFqn addrs.Provider
schema, version := t.Schemas.ResourceTypeConfig(providerAddr.Provider, mode, typeName) switch p := providerAddr.(type) {
case addrs.LocalProviderConfig:
// FIXME: need to look up the providerFQN in the config
providerFqn = addrs.NewLegacyProvider(p.LocalName)
case addrs.AbsProviderConfig:
providerFqn = p.Provider
}
schema, version := t.Schemas.ResourceTypeConfig(providerFqn, mode, typeName)
if schema == nil { if schema == nil {
log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr) log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr)
continue continue

View File

@ -59,7 +59,7 @@ func (n *graphNodeImportState) Name() string {
} }
// GraphNodeProviderConsumer // GraphNodeProviderConsumer
func (n *graphNodeImportState) ProvidedBy() (addrs.AbsProviderConfig, bool) { func (n *graphNodeImportState) ProvidedBy() (addrs.ProviderConfig, bool) {
// We assume that n.ProviderAddr has been properly populated here. // We assume that n.ProviderAddr has been properly populated here.
// It's the responsibility of the code creating a graphNodeImportState // It's the responsibility of the code creating a graphNodeImportState
// to populate this, possibly by calling DefaultProviderConfig() on the // to populate this, possibly by calling DefaultProviderConfig() on the

View File

@ -62,11 +62,12 @@ type GraphNodeCloseProvider interface {
// or in an ancestor module, with the resulting absolute address passed to // or in an ancestor module, with the resulting absolute address passed to
// SetProvider. // SetProvider.
type GraphNodeProviderConsumer interface { type GraphNodeProviderConsumer interface {
GraphNodeSubPath
// ProvidedBy returns the address of the provider configuration the node // ProvidedBy returns the address of the provider configuration the node
// refers to. If the returned "exact" value is true, this address will // refers to. If the returned "exact" value is true, this address will
// be taken exactly. If "exact" is false, a provider configuration from // be taken exactly. If "exact" is false, a provider configuration from
// an ancestor module may be selected instead. // an ancestor module may be selected instead.
ProvidedBy() (addr addrs.AbsProviderConfig, exact bool) ProvidedBy() (addr addrs.ProviderConfig, exact bool)
// Set the resolved provider address for this resource. // Set the resolved provider address for this resource.
SetProvider(addrs.AbsProviderConfig) SetProvider(addrs.AbsProviderConfig)
} }
@ -110,20 +111,77 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
if pv, ok := v.(GraphNodeProviderConsumer); ok { if pv, ok := v.(GraphNodeProviderConsumer); ok {
requested[v] = make(map[string]ProviderRequest) requested[v] = make(map[string]ProviderRequest)
p, exact := pv.ProvidedBy() providerAddr, exact := pv.ProvidedBy()
if exact { var absPc addrs.AbsProviderConfig
log.Printf("[TRACE] ProviderTransformer: %s is provided by %s exactly", dag.VertexName(v), p) var providerFqn addrs.Provider
} else {
log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), p) switch p := providerAddr.(type) {
case addrs.AbsProviderConfig:
absPc = p
// ProvidedBy() returns an AbsProviderConfig + exact == true
// when the provider configuration is set in state, so we do not
// need to verify the FQN matches.
if exact {
log.Printf("[TRACE] ProviderTransformer: %s is provided by %s exactly", dag.VertexName(v), absPc)
break
}
// if there is no config at all, the assumed default provider
// must be correct.
if t.Config == nil {
break
}
// If `exact` is false, an AbsProviderConfig indicates that
// ProvidedBy() returned an inferred default FQN. We must check
// if the inferred type name matches a non-default provider
// source in the config.
modConfig := t.Config.DescendentForInstance(pv.Path())
if modConfig != nil {
providerFqn = modConfig.Module.ProviderForLocalConfig(addrs.LocalProviderConfig{
LocalName: p.Provider.Type,
})
// This is only a change to the absPc if
// ProviderForLocalConfig returns a different Provider
absPc.Provider = providerFqn
}
case addrs.LocalProviderConfig:
// ProvidedBy() return a LocalProviderConfig when the resource
// contains a `provider` attribute
modPath := pv.Path()
if t.Config == nil {
absPc.Provider = addrs.NewLegacyProvider(p.LocalName)
absPc.Module = modPath
absPc.Alias = p.Alias
break
}
modConfig := t.Config.DescendentForInstance(modPath)
if modConfig == nil {
absPc.Provider = addrs.NewLegacyProvider(p.LocalName)
} else {
absPc.Provider = modConfig.Module.ProviderForLocalConfig(p)
}
absPc.Module = modPath
absPc.Alias = p.Alias
default:
// This should never happen, the case statements are exhaustive
panic(fmt.Sprintf("%s: provider for %s couldn't be determined", dag.VertexName(v), absPc))
} }
requested[v][p.String()] = ProviderRequest{ if !exact {
Addr: p, log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), absPc)
}
requested[v][absPc.String()] = ProviderRequest{
Addr: absPc,
Exact: exact, Exact: exact,
} }
// Direct references need the provider configured as well as initialized // Direct references need the provider configured as well as initialized
needConfigured[p.String()] = p needConfigured[absPc.String()] = absPc
} }
} }
@ -303,16 +361,26 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error {
// the later proper resolution of provider inheritance done by // the later proper resolution of provider inheritance done by
// ProviderTransformer. // ProviderTransformer.
p, _ := pv.ProvidedBy() p, _ := pv.ProvidedBy()
if p.Alias != "" { var providerFqn addrs.Provider
// We do not create default aliased configurations. switch p.(type) {
log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p) case addrs.LocalProviderConfig:
continue if p.(addrs.LocalProviderConfig).Alias != "" {
// We do not create default aliased configurations.
log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p)
continue
}
providerFqn = addrs.NewLegacyProvider(p.(addrs.LocalProviderConfig).LocalName)
case addrs.AbsProviderConfig:
providerFqn = p.(addrs.AbsProviderConfig).Provider
default:
// This should never happen, the case statements are exhaustive
panic(fmt.Sprintf("%s: provider for %s couldn't be determined", dag.VertexName(v), p))
} }
// We're going to create an implicit _default_ configuration for the // We're going to create an implicit _default_ configuration for the
// referenced provider type in the _root_ module, ignoring all other // referenced provider type in the _root_ module, ignoring all other
// aspects of the resource's declared provider address. // aspects of the resource's declared provider address.
defaultAddr := addrs.RootModuleInstance.ProviderConfigDefault(p.Provider) defaultAddr := addrs.RootModuleInstance.ProviderConfigDefault(providerFqn)
key := defaultAddr.String() key := defaultAddr.String()
provider := m[key] provider := m[key]