Merge pull request #16619 from hashicorp/jbardin/implicit-providers
Allow overriding an implicitly used provider
This commit is contained in:
commit
fc2913d610
|
@ -0,0 +1,10 @@
|
|||
provider "aws" {
|
||||
alias = "foo"
|
||||
}
|
||||
|
||||
module "mod" {
|
||||
source = "./mod"
|
||||
providers = {
|
||||
"aws" = "aws.foo"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
resource "aws_instance" "bar" {
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
provider "aws" {
|
||||
}
|
||||
|
||||
module "mod" {
|
||||
source = "./mod"
|
||||
|
||||
# aws.foo doesn't exist, and should report an error
|
||||
providers = {
|
||||
"aws" = "aws.foo"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
resource "aws_resource" "foo" {
|
||||
}
|
|
@ -412,6 +412,8 @@ type ProviderConfigTransformer struct {
|
|||
// each provider node is stored here so that the proxy nodes can look up
|
||||
// their targets by name.
|
||||
providers map[string]GraphNodeProvider
|
||||
// record providers that can be overriden with a proxy
|
||||
proxiable map[string]bool
|
||||
|
||||
// Module is the module to add resources from.
|
||||
Module *module.Tree
|
||||
|
@ -429,10 +431,11 @@ func (t *ProviderConfigTransformer) Transform(g *Graph) error {
|
|||
}
|
||||
|
||||
t.providers = make(map[string]GraphNodeProvider)
|
||||
t.proxiable = make(map[string]bool)
|
||||
|
||||
// Start the transformation process
|
||||
if err := t.transform(g, t.Module); err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// finally attach the configs to the new nodes
|
||||
|
@ -471,19 +474,13 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, m *module.Tree) er
|
|||
path = append([]string{RootModuleName}, path...)
|
||||
}
|
||||
|
||||
// add all provider configs
|
||||
// add all providers from the configuration
|
||||
for _, p := range conf.ProviderConfigs {
|
||||
name := p.Name
|
||||
if p.Alias != "" {
|
||||
name += "." + p.Alias
|
||||
}
|
||||
|
||||
// if this is an empty config placeholder to accept a provier from a
|
||||
// parent module, add a proxy and continue.
|
||||
if t.addProxyProvider(g, m, p, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
v := t.Concrete(&NodeAbstractProvider{
|
||||
NameValue: name,
|
||||
PathValue: path,
|
||||
|
@ -491,26 +488,29 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, m *module.Tree) er
|
|||
|
||||
// Add it to the graph
|
||||
g.Add(v)
|
||||
t.providers[ResolveProviderName(name, path)] = v.(GraphNodeProvider)
|
||||
fullName := ResolveProviderName(name, path)
|
||||
t.providers[fullName] = v.(GraphNodeProvider)
|
||||
t.proxiable[fullName] = len(p.RawConfig.RawMap()) == 0
|
||||
}
|
||||
|
||||
return nil
|
||||
// Now replace the provider nodes with proxy nodes if a provider was being
|
||||
// passed in, and create implicit proxies if there was no config. Any extra
|
||||
// proxies will be removed in the prune step.
|
||||
return t.addProxyProviders(g, m)
|
||||
}
|
||||
|
||||
// add a ProxyProviderConfig if this was inherited from a parent module. Return
|
||||
// whether the proxy was added to the graph or not.
|
||||
func (t *ProviderConfigTransformer) addProxyProvider(g *Graph, m *module.Tree, pc *config.ProviderConfig, name string) bool {
|
||||
func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, m *module.Tree) error {
|
||||
path := m.Path()
|
||||
|
||||
// This isn't a proxy if there's a config, or we're at the root
|
||||
if len(pc.RawConfig.RawMap()) > 0 || len(path) == 0 {
|
||||
return false
|
||||
// can't add proxies at the root
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
parentPath := path[:len(path)-1]
|
||||
parent := t.Module.Child(parentPath)
|
||||
if parent == nil {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
var parentCfg *config.Module
|
||||
|
@ -522,35 +522,48 @@ func (t *ProviderConfigTransformer) addProxyProvider(g *Graph, m *module.Tree, p
|
|||
}
|
||||
|
||||
if parentCfg == nil {
|
||||
panic("immaculately conceived module " + m.Name())
|
||||
// this can't really happen during normal execution.
|
||||
return fmt.Errorf("parent module config not found for %s", m.Name())
|
||||
}
|
||||
|
||||
parentProviderName, ok := parentCfg.Providers[name]
|
||||
if !ok {
|
||||
// this provider isn't listed in a parent module block, so we just have
|
||||
// an empty config
|
||||
return false
|
||||
}
|
||||
// Go through all the providers the parent is passing in, and add proxies to
|
||||
// the parent provider nodes.
|
||||
for name, parentName := range parentCfg.Providers {
|
||||
fullName := ResolveProviderName(name, path)
|
||||
fullParentName := ResolveProviderName(parentName, parentPath)
|
||||
|
||||
// the parent module is passing in a provider
|
||||
fullParentName := ResolveProviderName(parentProviderName, parentPath)
|
||||
parentProvider := t.providers[fullParentName]
|
||||
|
||||
if parentProvider == nil {
|
||||
log.Printf("[ERROR] missing provider %s in module %s", parentProviderName, m.Name())
|
||||
return false
|
||||
return fmt.Errorf("missing provider %s", fullParentName)
|
||||
}
|
||||
|
||||
v := &graphNodeProxyProvider{
|
||||
proxy := &graphNodeProxyProvider{
|
||||
nameValue: name,
|
||||
path: path,
|
||||
target: parentProvider,
|
||||
}
|
||||
|
||||
// Add it to the graph
|
||||
g.Add(v)
|
||||
t.providers[ResolveProviderName(name, path)] = v
|
||||
return true
|
||||
concreteProvider := t.providers[fullName]
|
||||
|
||||
// replace the concrete node with the provider passed in
|
||||
if concreteProvider != nil && t.proxiable[fullName] {
|
||||
g.Replace(concreteProvider, proxy)
|
||||
t.providers[fullName] = proxy
|
||||
continue
|
||||
}
|
||||
|
||||
// aliased providers can't be implicitly passed in
|
||||
if strings.Contains(name, ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
// There was no concrete provider, so add this as an implicit provider.
|
||||
// The extra proxy will be pruned later if it's unused.
|
||||
g.Add(proxy)
|
||||
t.providers[fullName] = proxy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error {
|
||||
|
|
|
@ -511,6 +511,69 @@ func TestProviderConfigTransformer_grandparentProviders(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// pass a specific provider into a module using it implicitly
|
||||
func TestProviderConfigTransformer_implicitModule(t *testing.T) {
|
||||
mod := testModule(t, "transform-provider-implicit-module")
|
||||
concrete := func(a *NodeAbstractProvider) dag.Vertex { return a }
|
||||
|
||||
g := Graph{Path: RootModulePath}
|
||||
{
|
||||
tf := &ConfigTransformer{Module: mod}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
tf := &AttachResourceConfigTransformer{Module: mod}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
tf := TransformProviders([]string{"aws"}, concrete, mod)
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(g.String())
|
||||
expected := strings.TrimSpace(`module.mod.aws_instance.bar
|
||||
provider.aws.foo
|
||||
provider.aws.foo`)
|
||||
if actual != expected {
|
||||
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// error out when a non-existent provider is named in a module providers map
|
||||
func TestProviderConfigTransformer_invalidProvider(t *testing.T) {
|
||||
mod := testModule(t, "transform-provider-invalid")
|
||||
concrete := func(a *NodeAbstractProvider) dag.Vertex { return a }
|
||||
|
||||
g := Graph{Path: RootModulePath}
|
||||
{
|
||||
tf := &ConfigTransformer{Module: mod}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
tf := &AttachResourceConfigTransformer{Module: mod}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
tf := TransformProviders([]string{"aws"}, concrete, mod)
|
||||
err := tf.Transform(&g)
|
||||
if err == nil {
|
||||
t.Fatal("expected missing provider error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "provider.aws.foo") {
|
||||
t.Fatalf("error should reference missing provider, got: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
const testTransformProviderBasicStr = `
|
||||
aws_instance.web
|
||||
provider.aws
|
||||
|
|
Loading…
Reference in New Issue