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
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 n.Config != nil {
relAddr := n.Config.ProviderConfigAddr()
// FIXME: this will need to lookup the provider and see if there's an
// FQN associated with the local config
fqn := addrs.NewLegacyProvider(relAddr.LocalName)
return addrs.AbsProviderConfig{
Provider: fqn,
Module: n.Path(),
Alias: relAddr.Alias,
return addrs.LocalProviderConfig{
LocalName: relAddr.LocalName,
Alias: relAddr.Alias,
}, false
}
// 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()
return addrs.AbsProviderConfig{
Provider: defaultFQN,
@ -312,19 +304,13 @@ func (n *NodeAbstractResource) ProvidedBy() (addrs.AbsProviderConfig, bool) {
}
// 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 n.Config != nil {
relAddr := n.Config.ProviderConfigAddr()
// 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.
fqn := addrs.NewLegacyProvider(relAddr.LocalName)
return addrs.AbsProviderConfig{
Provider: fqn,
Module: n.Path(),
Alias: relAddr.Alias,
return addrs.LocalProviderConfig{
LocalName: relAddr.LocalName,
Alias: relAddr.Alias,
}, 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
// 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()
return addrs.AbsProviderConfig{
Provider: defaultFQN,

View File

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/dag"
)
@ -59,8 +60,17 @@ func (t *AttachSchemaTransformer) Transform(g *Graph) error {
mode := addr.Resource.Mode
typeName := addr.Resource.Type
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 {
log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr)
continue

View File

@ -59,7 +59,7 @@ func (n *graphNodeImportState) Name() string {
}
// 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.
// It's the responsibility of the code creating a graphNodeImportState
// 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
// SetProvider.
type GraphNodeProviderConsumer interface {
GraphNodeSubPath
// ProvidedBy returns the address of the provider configuration the node
// refers to. If the returned "exact" value is true, this address will
// be taken exactly. If "exact" is false, a provider configuration from
// 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.
SetProvider(addrs.AbsProviderConfig)
}
@ -110,20 +111,77 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
if pv, ok := v.(GraphNodeProviderConsumer); ok {
requested[v] = make(map[string]ProviderRequest)
p, exact := pv.ProvidedBy()
if exact {
log.Printf("[TRACE] ProviderTransformer: %s is provided by %s exactly", dag.VertexName(v), p)
} else {
log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), p)
providerAddr, exact := pv.ProvidedBy()
var absPc addrs.AbsProviderConfig
var providerFqn addrs.Provider
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{
Addr: p,
if !exact {
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,
}
// 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
// ProviderTransformer.
p, _ := pv.ProvidedBy()
if p.Alias != "" {
// We do not create default aliased configurations.
log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p)
continue
var providerFqn addrs.Provider
switch p.(type) {
case addrs.LocalProviderConfig:
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
// referenced provider type in the _root_ module, ignoring all other
// aspects of the resource's declared provider address.
defaultAddr := addrs.RootModuleInstance.ProviderConfigDefault(p.Provider)
defaultAddr := addrs.RootModuleInstance.ProviderConfigDefault(providerFqn)
key := defaultAddr.String()
provider := m[key]