diff --git a/terraform/node_resource_abstract.go b/terraform/node_resource_abstract.go index 957c1fc6b6..05d4b519da 100644 --- a/terraform/node_resource_abstract.go +++ b/terraform/node_resource_abstract.go @@ -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, diff --git a/terraform/transform_attach_schema.go b/terraform/transform_attach_schema.go index 10d1e4641f..6aae68f97a 100644 --- a/terraform/transform_attach_schema.go +++ b/terraform/transform_attach_schema.go @@ -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 + + 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(providerAddr.Provider, mode, typeName) + schema, version := t.Schemas.ResourceTypeConfig(providerFqn, mode, typeName) if schema == nil { log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr) continue diff --git a/terraform/transform_import_state.go b/terraform/transform_import_state.go index 5743c06456..3f0bc87903 100644 --- a/terraform/transform_import_state.go +++ b/terraform/transform_import_state.go @@ -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 diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index ed9ccb63aa..02a31f163e 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -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)) + } + + if !exact { + log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), absPc) } - requested[v][p.String()] = ProviderRequest{ - Addr: p, + 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]