rewrite the ProviderConfigTransformer

It's become apparent that passing in a provider config for an implicitly
used provider would be very useful. While the ProviderConfigTransformer
efficiently added providers to the graph, the algorithm was reversed
from what would be needed to allow overriding implicit providers.

Change the ProviderConfigTransformer to fist add all configured
provider, even if they are empty stubs. Then run through all providers
being passed in from the parent, and replace the provider nodes we
created with proxies, and add implicit proxies where none existed. The
extra nodes will then be pruned later.
This commit is contained in:
James Bardin 2017-11-09 23:22:40 -05:00
parent aef082d1ec
commit 8bf270daa9
1 changed files with 54 additions and 42 deletions

View File

@ -405,6 +405,8 @@ type ProviderConfigTransformer struct {
// each provider node is stored here so that the proxy nodes can look up // each provider node is stored here so that the proxy nodes can look up
// their targets by name. // their targets by name.
providers map[string]GraphNodeProvider 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 is the module to add resources from.
Module *module.Tree Module *module.Tree
@ -422,6 +424,7 @@ func (t *ProviderConfigTransformer) Transform(g *Graph) error {
} }
t.providers = make(map[string]GraphNodeProvider) t.providers = make(map[string]GraphNodeProvider)
t.proxiable = make(map[string]bool)
// Start the transformation process // Start the transformation process
if err := t.transform(g, t.Module); err != nil { if err := t.transform(g, t.Module); err != nil {
@ -464,19 +467,13 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, m *module.Tree) er
path = append([]string{RootModuleName}, path...) path = append([]string{RootModuleName}, path...)
} }
// add all provider configs // add all providers from the configuration
for _, p := range conf.ProviderConfigs { for _, p := range conf.ProviderConfigs {
name := p.Name name := p.Name
if p.Alias != "" { if p.Alias != "" {
name += "." + 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{ v := t.Concrete(&NodeAbstractProvider{
NameValue: name, NameValue: name,
PathValue: path, PathValue: path,
@ -484,26 +481,29 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, m *module.Tree) er
// Add it to the graph // Add it to the graph
g.Add(v) 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 func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, m *module.Tree) error {
// 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 {
path := m.Path() path := m.Path()
// This isn't a proxy if there's a config, or we're at the root // can't add proxies at the root
if len(pc.RawConfig.RawMap()) > 0 || len(path) == 0 { if len(path) == 0 {
return false return nil
} }
parentPath := path[:len(path)-1] parentPath := path[:len(path)-1]
parent := t.Module.Child(parentPath) parent := t.Module.Child(parentPath)
if parent == nil { if parent == nil {
return false return nil
} }
var parentCfg *config.Module var parentCfg *config.Module
@ -515,35 +515,47 @@ func (t *ProviderConfigTransformer) addProxyProvider(g *Graph, m *module.Tree, p
} }
if parentCfg == nil { 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] // Go through all the providers the parent is passing in, and add proxies to
if !ok { // the parent provider nodes.
// this provider isn't listed in a parent module block, so we just have for name, parentName := range parentCfg.Providers {
// an empty config fullName := ResolveProviderName(name, path)
return false fullParentName := ResolveProviderName(parentName, parentPath)
parentProvider := t.providers[fullParentName]
if parentProvider == nil {
return fmt.Errorf("missing provider %s", fullParentName)
}
proxy := &graphNodeProxyProvider{
nameValue: name,
path: path,
target: parentProvider,
}
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
// 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
}
v := &graphNodeProxyProvider{
nameValue: name,
path: path,
target: parentProvider,
}
// Add it to the graph
g.Add(v)
t.providers[ResolveProviderName(name, path)] = v
return true
} }
func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error { func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error {