import (
"fmt"
"log"
- "strings"
- "github.com/hashicorp/go-multierror"
+ "github.com/hashicorp/hcl2/hcl"
+ "github.com/hashicorp/terraform/addrs"
+ "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/dag"
+ "github.com/hashicorp/terraform/tfdiags"
)
+func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, config *configs.Config) GraphTransformer {
+ return GraphTransformMulti(
+ // Add providers from the config
+ &ProviderConfigTransformer{
+ Config: config,
+ Providers: providers,
+ Concrete: concrete,
+ },
+ // Add any remaining missing providers
+ &MissingProviderTransformer{
+ Providers: providers,
+ Concrete: concrete,
+ },
+ // Connect the providers
+ &ProviderTransformer{
+ Config: config,
+ },
+ // Remove unused providers and proxies
+ &PruneProviderTransformer{},
+ // Connect provider to their parent provider nodes
+ &ParentProviderTransformer{},
+ )
+}
+
// GraphNodeProvider is an interface that nodes that can be a provider
-// must implement. The ProviderName returned is the name of the provider
-// they satisfy.
+// must implement.
+//
+// ProviderAddr returns the address of the provider configuration this
+// satisfies, which is relative to the path returned by method Path().
+//
+// Name returns the full name of the provider in the config.
type GraphNodeProvider interface {
- ProviderName() string
+ GraphNodeSubPath
+ ProviderAddr() addrs.AbsProviderConfig
+ Name() string
}
// GraphNodeCloseProvider is an interface that nodes that can be a close
// provider must implement. The CloseProviderName returned is the name of
// the provider they satisfy.
type GraphNodeCloseProvider interface {
- CloseProviderName() string
+ GraphNodeSubPath
+ CloseProviderAddr() addrs.AbsProviderConfig
}
// GraphNodeProviderConsumer is an interface that nodes that require
-// a provider must implement. ProvidedBy must return the name of the provider
-// to use.
+// a provider must implement. ProvidedBy must return the address of the provider
+// to use, which will be resolved to a configuration either in the same module
+// or in an ancestor module, with the resulting absolute address passed to
+// SetProvider.
type GraphNodeProviderConsumer interface {
- ProvidedBy() []string
+ // 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)
+ // Set the resolved provider address for this resource.
+ SetProvider(addrs.AbsProviderConfig)
}
// ProviderTransformer is a GraphTransformer that maps resources to
// providers within the graph. This will error if there are any resources
// that don't map to proper resources.
-type ProviderTransformer struct{}
+type ProviderTransformer struct {
+ Config *configs.Config
+}
func (t *ProviderTransformer) Transform(g *Graph) error {
- // Go through the other nodes and match them to providers they need
- var err error
- m := providerVertexMap(g)
+ // We need to find a provider configuration address for each resource
+ // either directly represented by a node or referenced by a node in
+ // the graph, and then create graph edges from provider to provider user
+ // so that the providers will get initialized first.
+
+ var diags tfdiags.Diagnostics
+
+ // To start, we'll collect the _requested_ provider addresses for each
+ // node, which we'll then resolve (handling provider inheritence, etc) in
+ // the next step.
+ // Our "requested" map is from graph vertices to string representations of
+ // provider config addresses (for deduping) to requests.
+ type ProviderRequest struct {
+ Addr addrs.AbsProviderConfig
+ Exact bool // If true, inheritence from parent modules is not attempted
+ }
+ requested := map[dag.Vertex]map[string]ProviderRequest{}
+ needConfigured := map[string]addrs.AbsProviderConfig{}
for _, v := range g.Vertices() {
+
+ // Does the vertex _directly_ use a provider?
if pv, ok := v.(GraphNodeProviderConsumer); ok {
- for _, p := range pv.ProvidedBy() {
- target := m[providerMapKey(p, pv)]
- if target == nil {
- println(fmt.Sprintf("%#v\n\n%#v", m, providerMapKey(p, pv)))
- err = multierror.Append(err, fmt.Errorf(
- "%s: provider %s couldn't be found",
- dag.VertexName(v), p))
- continue
+ 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)
+ }
+
+ requested[v][p.String()] = ProviderRequest{
+ Addr: p,
+ Exact: exact,
+ }
+
+ // Direct references need the provider configured as well as initialized
+ needConfigured[p.String()] = p
+ }
+ }
+
+ // Now we'll go through all the requested addresses we just collected and
+ // figure out which _actual_ config address each belongs to, after resolving
+ // for provider inheritance and passing.
+ m := providerVertexMap(g)
+ for v, reqs := range requested {
+ for key, req := range reqs {
+ p := req.Addr
+ target := m[key]
+
+ _, ok := v.(GraphNodeSubPath)
+ if !ok && target == nil {
+ // No target and no path to traverse up from
+ diags = diags.Append(fmt.Errorf("%s: provider %s couldn't be found", dag.VertexName(v), p))
+ continue
+ }
+
+ if target != nil {
+ log.Printf("[TRACE] ProviderTransformer: exact match for %s serving %s", p, dag.VertexName(v))
+ }
+
+ // if we don't have a provider at this level, walk up the path looking for one,
+ // unless we were told to be exact.
+ if target == nil && !req.Exact {
+ for pp, ok := p.Inherited(); ok; pp, ok = pp.Inherited() {
+ key := pp.String()
+ target = m[key]
+ if target != nil {
+ log.Printf("[TRACE] ProviderTransformer: %s uses inherited configuration %s", dag.VertexName(v), pp)
+ break
+ }
+ log.Printf("[TRACE] ProviderTransformer: looking for %s to serve %s", pp, dag.VertexName(v))
}
+ }
+
+ // If this provider doesn't need to be configured then we can just
+ // stub it out with an init-only provider node, which will just
+ // start up the provider and fetch its schema.
+ if _, exists := needConfigured[key]; target == nil && !exists {
+ stubAddr := p.ProviderConfig.Absolute(addrs.RootModuleInstance)
+ stub := &NodeEvalableProvider{
+ &NodeAbstractProvider{
+ Addr: stubAddr,
+ },
+ }
+ m[stubAddr.String()] = stub
+ log.Printf("[TRACE] ProviderTransformer: creating init-only node for %s", stubAddr)
+ target = stub
+ g.Add(target)
+ }
+
+ if target == nil {
+ diags = diags.Append(tfdiags.Sourceless(
+ tfdiags.Error,
+ "Provider configuration not present",
+ fmt.Sprintf(
+ "To work with %s its original provider configuration at %s is required, but it has been removed. This occurs when a provider configuration is removed while objects created by that provider still exist in the state. Re-add the provider configuration to destroy %s, after which you can remove the provider configuration again.",
+ dag.VertexName(v), p, dag.VertexName(v),
+ ),
+ ))
+ break
+ }
+
+ // see if this in an inherited provider
+ if p, ok := target.(*graphNodeProxyProvider); ok {
+ g.Remove(p)
+ target = p.Target()
+ key = target.(GraphNodeProvider).ProviderAddr().String()
+ }
- g.Connect(dag.BasicEdge(v, target))
+ log.Printf("[DEBUG] ProviderTransformer: %q (%T) needs %s", dag.VertexName(v), v, dag.VertexName(target))
+ if pv, ok := v.(GraphNodeProviderConsumer); ok {
+ pv.SetProvider(target.ProviderAddr())
}
+ g.Connect(dag.BasicEdge(v, target))
}
}
- return err
+ return diags.Err()
}
// CloseProviderTransformer is a GraphTransformer that adds nodes to the
func (t *CloseProviderTransformer) Transform(g *Graph) error {
pm := providerVertexMap(g)
- cpm := closeProviderVertexMap(g)
+ cpm := make(map[string]*graphNodeCloseProvider)
var err error
- for _, v := range g.Vertices() {
- if pv, ok := v.(GraphNodeProviderConsumer); ok {
- for _, p := range pv.ProvidedBy() {
- key := p
- source := cpm[key]
-
- if source == nil {
- // Create a new graphNodeCloseProvider and add it to the graph
- source = &graphNodeCloseProvider{ProviderNameValue: p}
- g.Add(source)
-
- // Close node needs to depend on provider
- provider, ok := pm[key]
- if !ok {
- err = multierror.Append(err, fmt.Errorf(
- "%s: provider %s couldn't be found for closing",
- dag.VertexName(v), p))
- continue
- }
- g.Connect(dag.BasicEdge(source, provider))
- // Make sure we also add the new graphNodeCloseProvider to the map
- // so we don't create and add any duplicate graphNodeCloseProviders.
- cpm[key] = source
- }
+ for _, v := range pm {
+ p := v.(GraphNodeProvider)
+ key := p.ProviderAddr().String()
- // Close node depends on all nodes provided by the provider
- g.Connect(dag.BasicEdge(source, v))
+ // get the close provider of this type if we alread created it
+ closer := cpm[key]
+
+ if closer == nil {
+ // create a closer for this provider type
+ closer = &graphNodeCloseProvider{Addr: p.ProviderAddr()}
+ g.Add(closer)
+ cpm[key] = closer
+ }
+
+ // Close node depends on the provider itself
+ // this is added unconditionally, so it will connect to all instances
+ // of the provider. Extra edges will be removed by transitive
+ // reduction.
+ g.Connect(dag.BasicEdge(closer, p))
+
+ // connect all the provider's resources to the close node
+ for _, s := range g.UpEdges(p).List() {
+ if _, ok := s.(GraphNodeProviderConsumer); ok {
+ g.Connect(dag.BasicEdge(closer, s))
}
}
}
return err
}
-// MissingProviderTransformer is a GraphTransformer that adds nodes
-// for missing providers into the graph. Specifically, it creates provider
-// configuration nodes for all the providers that we support. These are
-// pruned later during an optimization pass.
+// MissingProviderTransformer is a GraphTransformer that adds to the graph
+// a node for each default provider configuration that is referenced by another
+// node but not already present in the graph.
+//
+// These "default" nodes are always added to the root module, regardless of
+// where they are requested. This is important because our inheritance
+// resolution behavior in ProviderTransformer will then treat these as a
+// last-ditch fallback after walking up the tree, rather than preferring them
+// as it would if they were placed in the same module as the requester.
+//
+// This transformer may create extra nodes that are not needed in practice,
+// due to overriding provider configurations in child modules.
+// PruneProviderTransformer can then remove these once ProviderTransformer
+// has resolved all of the inheritence, etc.
type MissingProviderTransformer struct {
// Providers is the list of providers we support.
Providers []string
- // AllowAny will not check that a provider is supported before adding
- // it to the graph.
- AllowAny bool
-
// Concrete, if set, overrides how the providers are made.
Concrete ConcreteProviderNodeFunc
}
}
}
- // Create a set of our supported providers
- supported := make(map[string]struct{}, len(t.Providers))
- for _, v := range t.Providers {
- supported[v] = struct{}{}
- }
-
- // Get the map of providers we already have in our graph
+ var err error
m := providerVertexMap(g)
-
- // Go through all the provider consumers and make sure we add
- // that provider if it is missing. We use a for loop here instead
- // of "range" since we'll modify check as we go to add more to check.
- check := g.Vertices()
- for i := 0; i < len(check); i++ {
- v := check[i]
-
+ for _, v := range g.Vertices() {
pv, ok := v.(GraphNodeProviderConsumer)
if !ok {
continue
}
- // If this node has a subpath, then we use that as a prefix
- // into our map to check for an existing provider.
- var path []string
- if sp, ok := pv.(GraphNodeSubPath); ok {
- raw := normalizeModulePath(sp.Path())
- if len(raw) > len(rootModulePath) {
- path = raw
- }
+ // For our work here we actually care only about the provider type and
+ // we plan to place all default providers in the root module, and so
+ // it's safe for us to rely on ProvidedBy here rather than waiting for
+ // the later proper resolution of provider inheritance done by
+ // ProviderTransformer.
+ p, _ := pv.ProvidedBy()
+ if p.ProviderConfig.Alias != "" {
+ // We do not create default aliased configurations.
+ log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p)
+ continue
}
- for _, p := range pv.ProvidedBy() {
- key := providerMapKey(p, pv)
- if _, ok := m[key]; ok {
- // This provider already exists as a configure node
- continue
- }
+ // 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.ProviderConfig.Type)
+ key := defaultAddr.String()
+ provider := m[key]
- // If the provider has an alias in it, we just want the type
- ptype := p
- if idx := strings.IndexRune(p, '.'); idx != -1 {
- ptype = p[:idx]
- }
+ if provider != nil {
+ // There's already an explicit default configuration for this
+ // provider type in the root module, so we have nothing to do.
+ continue
+ }
- if !t.AllowAny {
- if _, ok := supported[ptype]; !ok {
- // If we don't support the provider type, skip it.
- // Validation later will catch this as an error.
- continue
- }
- }
+ log.Printf("[DEBUG] adding implicit provider configuration %s, implied first by %s", defaultAddr, dag.VertexName(v))
- // Add the missing provider node to the graph
- v := t.Concrete(&NodeAbstractProvider{
- NameValue: p,
- PathValue: path,
- }).(dag.Vertex)
- if len(path) > 0 {
- // We'll need the parent provider as well, so let's
- // add a dummy node to check to make sure that we add
- // that parent provider.
- check = append(check, &graphNodeProviderConsumerDummy{
- ProviderValue: p,
- PathValue: path[:len(path)-1],
- })
- }
+ // create the missing top-level provider
+ provider = t.Concrete(&NodeAbstractProvider{
+ Addr: defaultAddr,
+ }).(GraphNodeProvider)
- m[key] = g.Add(v)
- }
+ g.Add(provider)
+ m[key] = provider
}
- return nil
+ return err
}
// ParentProviderTransformer connects provider nodes to their parents.
//
// This works by finding nodes that are both GraphNodeProviders and
// GraphNodeSubPath. It then connects the providers to their parent
-// path.
+// path. The parent provider is always at the root level.
type ParentProviderTransformer struct{}
func (t *ParentProviderTransformer) Transform(g *Graph) error {
- // Make a mapping of path to dag.Vertex, where path is: "path.name"
- m := make(map[string]dag.Vertex)
-
- // Also create a map that maps a provider to its parent
- parentMap := make(map[dag.Vertex]string)
- for _, raw := range g.Vertices() {
- // If it is the flat version, then make it the non-flat version.
- // We eventually want to get rid of the flat version entirely so
- // this is a stop-gap while it still exists.
- var v dag.Vertex = raw
-
+ pm := providerVertexMap(g)
+ for _, v := range g.Vertices() {
// Only care about providers
pn, ok := v.(GraphNodeProvider)
- if !ok || pn.ProviderName() == "" {
+ if !ok {
continue
}
- // Also require a subpath, if there is no subpath then we
- // just totally ignore it. The expectation of this transform is
- // that it is used with a graph builder that is already flattened.
- var path []string
- if pn, ok := raw.(GraphNodeSubPath); ok {
- path = pn.Path()
- }
- path = normalizeModulePath(path)
-
- // Build the key with path.name i.e. "child.subchild.aws"
- key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
- m[key] = raw
-
- // Determine the parent if we're non-root. This is length 1 since
- // the 0 index should be "root" since we normalize above.
- if len(path) > 1 {
- path = path[:len(path)-1]
- key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
- parentMap[raw] = key
+ // Also require non-empty path, since otherwise we're in the root
+ // module and so cannot have a parent.
+ if len(pn.Path()) <= 1 {
+ continue
}
- }
- // Connect!
- for v, key := range parentMap {
- if parent, ok := m[key]; ok {
- g.Connect(dag.BasicEdge(v, parent))
+ // this provider may be disabled, but we can only get it's name from
+ // the ProviderName string
+ addr := pn.ProviderAddr()
+ parentAddr, ok := addr.Inherited()
+ if ok {
+ parent := pm[parentAddr.String()]
+ if parent != nil {
+ g.Connect(dag.BasicEdge(v, parent))
+ }
}
}
-
return nil
}
-// PruneProviderTransformer is a GraphTransformer that prunes all the
-// providers that aren't needed from the graph. A provider is unneeded if
-// no resource or module is using that provider.
+// PruneProviderTransformer removes any providers that are not actually used by
+// anything, and provider proxies. This avoids the provider being initialized
+// and configured. This both saves resources but also avoids errors since
+// configuration may imply initialization which may require auth.
type PruneProviderTransformer struct{}
func (t *PruneProviderTransformer) Transform(g *Graph) error {
for _, v := range g.Vertices() {
- // We only care about the providers
- if pn, ok := v.(GraphNodeProvider); !ok || pn.ProviderName() == "" {
+ // We only care about providers
+ _, ok := v.(GraphNodeProvider)
+ if !ok {
continue
}
- // Does anything depend on this? If not, then prune it.
- if s := g.UpEdges(v); s.Len() == 0 {
- if nv, ok := v.(dag.NamedVertex); ok {
- log.Printf("[DEBUG] Pruning provider with no dependencies: %s", nv.Name())
- }
+
+ // ProxyProviders will have up edges, but we're now done with them in the graph
+ if _, ok := v.(*graphNodeProxyProvider); ok {
+ log.Printf("[DEBUG] pruning proxy %s", dag.VertexName(v))
g.Remove(v)
}
- }
-
- return nil
-}
-// providerMapKey is a helper that gives us the key to use for the
-// maps returned by things such as providerVertexMap.
-func providerMapKey(k string, v dag.Vertex) string {
- pathPrefix := ""
- if sp, ok := v.(GraphNodeSubPath); ok {
- raw := normalizeModulePath(sp.Path())
- if len(raw) > len(rootModulePath) {
- pathPrefix = modulePrefixStr(raw) + "."
+ // Remove providers with no dependencies.
+ if g.UpEdges(v).Len() == 0 {
+ log.Printf("[DEBUG] pruning unused %s", dag.VertexName(v))
+ g.Remove(v)
}
}
- return pathPrefix + k
+ return nil
}
-func providerVertexMap(g *Graph) map[string]dag.Vertex {
- m := make(map[string]dag.Vertex)
+func providerVertexMap(g *Graph) map[string]GraphNodeProvider {
+ m := make(map[string]GraphNodeProvider)
for _, v := range g.Vertices() {
if pv, ok := v.(GraphNodeProvider); ok {
- key := providerMapKey(pv.ProviderName(), v)
- m[key] = v
+ addr := pv.ProviderAddr()
+ m[addr.String()] = pv
}
}
return m
}
-func closeProviderVertexMap(g *Graph) map[string]dag.Vertex {
- m := make(map[string]dag.Vertex)
+func closeProviderVertexMap(g *Graph) map[string]GraphNodeCloseProvider {
+ m := make(map[string]GraphNodeCloseProvider)
for _, v := range g.Vertices() {
if pv, ok := v.(GraphNodeCloseProvider); ok {
- m[pv.CloseProviderName()] = v
+ addr := pv.CloseProviderAddr()
+ m[addr.String()] = pv
}
}
}
type graphNodeCloseProvider struct {
- ProviderNameValue string
+ Addr addrs.AbsProviderConfig
}
+var (
+ _ GraphNodeCloseProvider = (*graphNodeCloseProvider)(nil)
+)
+
func (n *graphNodeCloseProvider) Name() string {
- return fmt.Sprintf("provider.%s (close)", n.ProviderNameValue)
+ return n.Addr.String() + " (close)"
+}
+
+// GraphNodeSubPath impl.
+func (n *graphNodeCloseProvider) Path() addrs.ModuleInstance {
+ return n.Addr.Module
}
// GraphNodeEvalable impl.
func (n *graphNodeCloseProvider) EvalTree() EvalNode {
- return CloseProviderEvalTree(n.ProviderNameValue)
+ return CloseProviderEvalTree(n.Addr)
}
// GraphNodeDependable impl.
return []string{n.Name()}
}
-func (n *graphNodeCloseProvider) CloseProviderName() string {
- return n.ProviderNameValue
+func (n *graphNodeCloseProvider) CloseProviderAddr() addrs.AbsProviderConfig {
+ return n.Addr
}
// GraphNodeDotter impl.
return true
}
-// graphNodeProviderConsumerDummy is a struct that never enters the real
-// graph (though it could to no ill effect). It implements
-// GraphNodeProviderConsumer and GraphNodeSubpath as a way to force
-// certain transformations.
-type graphNodeProviderConsumerDummy struct {
- ProviderValue string
- PathValue []string
+// graphNodeProxyProvider is a GraphNodeProvider implementation that is used to
+// store the name and value of a provider node for inheritance between modules.
+// These nodes are only used to store the data while loading the provider
+// configurations, and are removed after all the resources have been connected
+// to their providers.
+type graphNodeProxyProvider struct {
+ addr addrs.AbsProviderConfig
+ target GraphNodeProvider
}
-func (n *graphNodeProviderConsumerDummy) Path() []string {
- return n.PathValue
+var (
+ _ GraphNodeProvider = (*graphNodeProxyProvider)(nil)
+)
+
+func (n *graphNodeProxyProvider) ProviderAddr() addrs.AbsProviderConfig {
+ return n.addr
+}
+
+func (n *graphNodeProxyProvider) Path() addrs.ModuleInstance {
+ return n.addr.Module
+}
+
+func (n *graphNodeProxyProvider) Name() string {
+ return n.addr.String() + " (proxy)"
}
-func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string {
- return []string{n.ProviderValue}
+// find the concrete provider instance
+func (n *graphNodeProxyProvider) Target() GraphNodeProvider {
+ switch t := n.target.(type) {
+ case *graphNodeProxyProvider:
+ return t.Target()
+ default:
+ return n.target
+ }
+}
+
+// ProviderConfigTransformer adds all provider nodes from the configuration and
+// attaches the configs.
+type ProviderConfigTransformer struct {
+ Providers []string
+ Concrete ConcreteProviderNodeFunc
+
+ // 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
+
+ // Config is the root node of the configuration tree to add providers from.
+ Config *configs.Config
+}
+
+func (t *ProviderConfigTransformer) Transform(g *Graph) error {
+ // If no configuration is given, we don't do anything
+ if t.Config == nil {
+ return nil
+ }
+
+ t.providers = make(map[string]GraphNodeProvider)
+ t.proxiable = make(map[string]bool)
+
+ // Start the transformation process
+ if err := t.transform(g, t.Config); err != nil {
+ return err
+ }
+
+ // finally attach the configs to the new nodes
+ return t.attachProviderConfigs(g)
+}
+
+func (t *ProviderConfigTransformer) transform(g *Graph, c *configs.Config) error {
+ // If no config, do nothing
+ if c == nil {
+ return nil
+ }
+
+ // Add our resources
+ if err := t.transformSingle(g, c); err != nil {
+ return err
+ }
+
+ // Transform all the children.
+ for _, cc := range c.Children {
+ if err := t.transform(g, cc); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config) error {
+ // Get the module associated with this configuration tree node
+ mod := c.Module
+ staticPath := c.Path
+
+ // We actually need a dynamic module path here, but we've not yet updated
+ // our graph builders enough to support expansion of module calls with
+ // "count" and "for_each" set, so for now we'll shim this by converting to
+ // a dynamic path with no keys. At the time of writing this is the only
+ // possible kind of dynamic path anyway.
+ path := make(addrs.ModuleInstance, len(staticPath))
+ for i, name := range staticPath {
+ path[i] = addrs.ModuleInstanceStep{
+ Name: name,
+ }
+ }
+
+ // add all providers from the configuration
+ for _, p := range mod.ProviderConfigs {
+ relAddr := p.Addr()
+ addr := relAddr.Absolute(path)
+
+ abstract := &NodeAbstractProvider{
+ Addr: addr,
+ }
+ var v dag.Vertex
+ if t.Concrete != nil {
+ v = t.Concrete(abstract)
+ } else {
+ v = abstract
+ }
+
+ // Add it to the graph
+ g.Add(v)
+ 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()
+ }
+
+ // 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, c)
+}
+
+func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Config) error {
+ path := c.Path
+
+ // can't add proxies at the root
+ if len(path) == 0 {
+ return nil
+ }
+
+ parentPath, callAddr := path.Call()
+ parent := c.Parent
+ if parent == nil {
+ return nil
+ }
+
+ callName := callAddr.Name
+ var parentCfg *configs.ModuleCall
+ for name, mod := range parent.Module.ModuleCalls {
+ if name == callName {
+ parentCfg = mod
+ break
+ }
+ }
+
+ // We currently don't support count/for_each for modules and so we must
+ // shim our path and parentPath into module instances here so that the
+ // rest of Terraform can behave as if we do. This shimming should be
+ // removed later as part of implementing count/for_each for modules.
+ instPath := make(addrs.ModuleInstance, len(path))
+ for i, name := range path {
+ instPath[i] = addrs.ModuleInstanceStep{Name: name}
+ }
+ parentInstPath := make(addrs.ModuleInstance, len(parentPath))
+ for i, name := range parentPath {
+ parentInstPath[i] = addrs.ModuleInstanceStep{Name: name}
+ }
+
+ if parentCfg == nil {
+ // this can't really happen during normal execution.
+ return fmt.Errorf("parent module config not found for %s", c.Path.String())
+ }
+
+ // Go through all the providers the parent is passing in, and add proxies to
+ // the parent provider nodes.
+ for _, pair := range parentCfg.Providers {
+ fullAddr := pair.InChild.Addr().Absolute(instPath)
+ fullParentAddr := pair.InParent.Addr().Absolute(parentInstPath)
+ fullName := fullAddr.String()
+ fullParentName := fullParentAddr.String()
+
+ parentProvider := t.providers[fullParentName]
+
+ if parentProvider == nil {
+ return fmt.Errorf("missing provider %s", fullParentName)
+ }
+
+ proxy := &graphNodeProxyProvider{
+ addr: fullAddr,
+ 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 configurations can't be implicitly passed in
+ if fullAddr.ProviderConfig.Alias != "" {
+ 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 {
+ for _, v := range g.Vertices() {
+ // Only care about GraphNodeAttachProvider implementations
+ apn, ok := v.(GraphNodeAttachProvider)
+ if !ok {
+ continue
+ }
+
+ // Determine what we're looking for
+ addr := apn.ProviderAddr()
+
+ // Get the configuration.
+ mc := t.Config.DescendentForInstance(addr.Module)
+ if mc == nil {
+ log.Printf("[TRACE] ProviderConfigTransformer: no configuration available for %s", addr.String())
+ continue
+ }
+
+ // Go through the provider configs to find the matching config
+ for _, p := range mc.Module.ProviderConfigs {
+ if p.Name == addr.ProviderConfig.Type && p.Alias == addr.ProviderConfig.Alias {
+ log.Printf("[TRACE] ProviderConfigTransformer: attaching to %q provider configuration from %s", dag.VertexName(v), p.DeclRange)
+ apn.AttachProvider(p)
+ break
+ }
+ }
+ }
+
+ return nil
}