add default provider nodes to root modules

If a root modules declares a required_provider but has no configuration,
add a graph node for the provider as if there were an empty
configuration. This will allow the provider to be referenced by name in
module call provider maps, so that a module can pass a default provider
by name to a submodule.

Normally these nodes are added by the MissingProviderTransformer, but
they need to be in place earlier to resolve any possible "proxy provider
nodes" within modules.
This commit is contained in:
James Bardin 2021-05-03 16:17:41 -04:00
parent 95b86bf7ad
commit f738246a03
2 changed files with 112 additions and 8 deletions

View File

@ -2093,3 +2093,71 @@ resource "test_instance" "c" {
t.Fatal(diags.ErrWithWarnings())
}
}
func TestContext2Validate_passInheritedProvider(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
terraform {
required_providers {
test = {
source = "hashicorp/test"
}
}
}
module "first" {
source = "./first"
providers = {
test = test
}
}
`,
// This module does not define a config for the test provider, but we
// should be able to pass whatever the implied config is to a child
// module.
"first/main.tf": `
terraform {
required_providers {
test = {
source = "hashicorp/test"
}
}
}
module "second" {
source = "./second"
providers = {
test.alias = test
}
}`,
"first/second/main.tf": `
terraform {
required_providers {
test = {
source = "hashicorp/test"
configuration_aliases = [test.alias]
}
}
}
resource "test_object" "t" {
provider = test.alias
}
`,
})
p := simpleMockProvider()
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
diags := ctx.Validate()
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
}

View File

@ -534,6 +534,37 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config)
mod := c.Module
path := c.Path
// If this is the root module, we can add nodes for required providers that
// have no configuration, equivalent to having an empty configuration
// block. This will ensure that a provider node exists for modules to
// access when passing around configuration and inheritance.
if path.IsRoot() && c.Module.ProviderRequirements != nil {
for name, p := range c.Module.ProviderRequirements.RequiredProviders {
if _, configured := mod.ProviderConfigs[name]; configured {
continue
}
addr := addrs.AbsProviderConfig{
Provider: p.Type,
Module: path,
}
abstract := &NodeAbstractProvider{
Addr: addr,
}
var v dag.Vertex
if t.Concrete != nil {
v = t.Concrete(abstract)
} else {
v = abstract
}
g.Add(v)
t.providers[addr.String()] = v.(GraphNodeProvider)
}
}
// add all providers from the configuration
for _, p := range mod.ProviderConfigs {
fqn := mod.ProviderForLocalConfig(p.Addr())
@ -558,13 +589,17 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config)
key := addr.String()
t.providers[key] = v.(GraphNodeProvider)
// A provider configuration is "proxyable" if its configuration is
// entirely empty. This means it's standing in for a provider
// configuration that must be passed in from the parent module.
// We decide this by evaluating the config with an empty schema;
// if this succeeds, then we know there's nothing in the body.
_, diags := p.Config.Content(&hcl.BodySchema{})
t.proxiable[key] = !diags.HasErrors()
// While deprecated, we still accept empty configuration blocks within
// modules as being a possible proxy for passed configuration.
if !path.IsRoot() {
// A provider configuration is "proxyable" if its configuration is
// entirely empty. This means it's standing in for a provider
// configuration that must be passed in from the parent module.
// We decide this by evaluating the config with an empty schema;
// if this succeeds, then we know there's nothing in the body.
_, diags := p.Config.Content(&hcl.BodySchema{})
t.proxiable[key] = !diags.HasErrors()
}
}
// Now replace the provider nodes with proxy nodes if a provider was being
@ -577,7 +612,7 @@ func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Confi
path := c.Path
// can't add proxies at the root
if len(path) == 0 {
if path.IsRoot() {
return nil
}
@ -648,6 +683,7 @@ func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Confi
g.Add(proxy)
t.providers[fullName] = proxy
}
return nil
}