WIP reference providers by full name

This turned out to be a big messy commit, since the way providers are
referenced is tightly coupled throughout the code. That starts to unify
how providers are referenced, using the format output node Name method.

Add a new field to the internal resource data types called
ResolvedProvider. This is set by a new setter method SetProvider when a
resource is connected to a provider during graph creation. This allows
us to later lookup the provider instance a resource is connected to,
without requiring it to have the same module path.

The InitProvider context method now takes 2 arguments, one if the
provider type and the second is the full name of the provider. While the
provider type could still be parsed from the full name, this makes it
more explicit and, and changes to the name format won't effect this
code.
This commit is contained in:
James Bardin 2017-11-01 18:34:18 -04:00
parent 1d54d4b10d
commit a14fd0344c
25 changed files with 218 additions and 171 deletions

View File

@ -861,7 +861,7 @@ func TestContext2Apply_createBeforeDestroy(t *testing.T) {
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr)
if actual != expected {
t.Fatalf("bad: \n%s", actual)
t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual)
}
}

View File

@ -687,13 +687,10 @@ func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) {
}
expected := []string{
"root\n",
// this test originally verified that a parent provider config can
// partially override a child. That's no longer the case, so the child
// config is used in its entirety here.
//"root\nchild\n",
"child\nchild\n",
"root\n",
}
sort.Strings(calls)
if !reflect.DeepEqual(calls, expected) {
t.Fatalf("expected:\n%#v\ngot:\n%#v\n", expected, calls)
}

View File

@ -22,11 +22,11 @@ type EvalContext interface {
// Input is the UIInput object for interacting with the UI.
Input() UIInput
// InitProvider initializes the provider with the given name and
// InitProvider initializes the provider with the given type and name, and
// returns the implementation of the resource provider or an error.
//
// It is an error to initialize the same provider more than once.
InitProvider(string) (ResourceProvider, error)
InitProvider(typ string, name string) (ResourceProvider, error)
// Provider gets the provider instance with the given name (already
// initialized) or returns nil if the provider isn't initialized.

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"log"
"strings"
"sync"
"github.com/hashicorp/terraform/config"
@ -79,12 +78,12 @@ func (ctx *BuiltinEvalContext) Input() UIInput {
return ctx.InputValue
}
func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
func (ctx *BuiltinEvalContext) InitProvider(typeName, name string) (ResourceProvider, error) {
ctx.once.Do(ctx.init)
// If we already initialized, it is an error
if p := ctx.Provider(n); p != nil {
return nil, fmt.Errorf("Provider '%s' already initialized", n)
if p := ctx.Provider(name); p != nil {
return nil, fmt.Errorf("Provider '%s' already initialized", name)
}
// Warning: make sure to acquire these locks AFTER the call to Provider
@ -92,13 +91,12 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
ctx.ProviderLock.Lock()
defer ctx.ProviderLock.Unlock()
typeName := strings.SplitN(n, ".", 2)[0]
p, err := ctx.Components.ResourceProvider(typeName, n)
p, err := ctx.Components.ResourceProvider(typeName, name)
if err != nil {
return nil, err
}
ctx.ProviderCache[n] = p
ctx.ProviderCache[name] = p
return p, nil
}

View File

@ -107,7 +107,7 @@ func (c *MockEvalContext) Input() UIInput {
return c.InputInput
}
func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) {
func (c *MockEvalContext) InitProvider(t, n string) (ResourceProvider, error) {
c.InitProviderCalled = true
c.InitProviderName = n
return c.InitProviderProvider, c.InitProviderError

View File

@ -52,11 +52,12 @@ func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
// and returns nothing. The provider can be retrieved again with the
// EvalGetProvider node.
type EvalInitProvider struct {
Name string
TypeName string
Name string
}
func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) {
return ctx.InitProvider(n.Name)
return ctx.InitProvider(n.TypeName, n.Name)
}
// EvalCloseProvider is an EvalNode implementation that closes provider
@ -129,6 +130,7 @@ func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) {
}
}
}
ctx.SetProviderInput(n.Name, confMap)
return nil, nil

View File

@ -1,17 +1,24 @@
package terraform
import (
"strings"
"github.com/hashicorp/terraform/config"
)
// ProviderEvalTree returns the evaluation tree for initializing and
// configuring providers.
func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
func ProviderEvalTree(n *NodeApplyableProvider, config *config.ProviderConfig) EvalNode {
var provider ResourceProvider
var resourceConfig *ResourceConfig
typeName := strings.SplitN(n.NameValue, ".", 2)[0]
seq := make([]EvalNode, 0, 5)
seq = append(seq, &EvalInitProvider{Name: n})
seq = append(seq, &EvalInitProvider{
TypeName: typeName,
Name: n.Name(),
})
// Input stuff
seq = append(seq, &EvalOpFilter{
@ -19,7 +26,7 @@ func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n,
Name: n.Name(),
Output: &provider,
},
&EvalInterpolateProvider{
@ -27,12 +34,12 @@ func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
Output: &resourceConfig,
},
&EvalBuildProviderConfig{
Provider: n,
Provider: n.NameValue,
Config: &resourceConfig,
Output: &resourceConfig,
},
&EvalInputProvider{
Name: n,
Name: n.NameValue,
Provider: &provider,
Config: &resourceConfig,
},
@ -45,7 +52,7 @@ func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n,
Name: n.Name(),
Output: &provider,
},
&EvalInterpolateProvider{
@ -53,7 +60,7 @@ func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
Output: &resourceConfig,
},
&EvalBuildProviderConfig{
Provider: n,
Provider: n.NameValue,
Config: &resourceConfig,
Output: &resourceConfig,
},
@ -71,7 +78,7 @@ func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n,
Name: n.Name(),
Output: &provider,
},
&EvalInterpolateProvider{
@ -79,7 +86,7 @@ func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
Output: &resourceConfig,
},
&EvalBuildProviderConfig{
Provider: n,
Provider: n.NameValue,
Config: &resourceConfig,
Output: &resourceConfig,
},
@ -94,7 +101,7 @@ func ProviderEvalTree(n string, config *config.ProviderConfig) EvalNode {
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalConfigProvider{
Provider: n,
Provider: n.Name(),
Config: &resourceConfig,
},
},

View File

@ -52,6 +52,13 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer {
// Add the import steps
&ImportStateTransformer{Targets: b.ImportTargets},
// add configured providers
&ProviderConfigTransformer{
Module: b.Module,
Providers: b.Providers,
Concrete: concreteProvider,
},
// Provider-related transformations
&MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider},
&ProviderTransformer{},

View File

@ -126,6 +126,13 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
// Add root variables
&RootVariableTransformer{Module: b.Module},
// add configured providers
&ProviderConfigTransformer{
Module: b.Module,
Providers: b.Providers,
Concrete: concreteProvider,
},
// Create all the providers
&MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider},
&ProviderTransformer{},

View File

@ -27,6 +27,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodeRefreshableDataResourceInstance{
NodeAbstractResource: a,
@ -185,7 +186,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
// provider configurations that need this data during
// refresh/plan.
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},

View File

@ -7,5 +7,5 @@ type NodeApplyableProvider struct {
// GraphNodeEvalable
func (n *NodeApplyableProvider) EvalTree() EvalNode {
return ProviderEvalTree(n.NameValue, n.ProviderConfig())
return ProviderEvalTree(n, n.ProviderConfig())
}

View File

@ -24,13 +24,17 @@ type NodeAbstractProvider struct {
Config *config.ProviderConfig
}
func (n *NodeAbstractProvider) Name() string {
result := fmt.Sprintf("provider.%s", n.NameValue)
if len(n.PathValue) > 1 {
result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result)
func ResolveProviderName(name string, path []string) string {
name = fmt.Sprintf("provider.%s", name)
if len(path) > 1 {
name = fmt.Sprintf("%s.%s", modulePrefixStr(path), name)
}
return result
return name
}
func (n *NodeAbstractProvider) Name() string {
return ResolveProviderName(n.NameValue, n.PathValue)
}
// GraphNodeSubPath

View File

@ -33,6 +33,9 @@ type NodeAbstractResource struct {
ResourceState *ResourceState // ResourceState is the ResourceState for this
Targets []ResourceAddress // Set from GraphNodeTargetable
// The address of the provider this resource will use
ResolvedProvider string
}
func (n *NodeAbstractResource) Name() string {
@ -170,6 +173,10 @@ func (n *NodeAbstractResource) StateReferences() []string {
return deps
}
func (n *NodeAbstractResource) SetProvider(p string) {
n.ResolvedProvider = p
}
// GraphNodeProviderConsumer
func (n *NodeAbstractResource) ProvidedBy() []string {
// If we have a config we prefer that above all else

View File

@ -135,7 +135,7 @@ func (n *NodeApplyableResource) evalTreeDataResource(
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
@ -242,7 +242,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
Output: &resourceConfig,
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadState{
@ -283,7 +283,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadState{

View File

@ -102,8 +102,9 @@ func (n *NodeDestroyResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
// We want deposed resources in the state to be destroyed
steps = append(steps, &DeposedTransformer{
State: state,
View: n.Addr.stateId(),
State: state,
View: n.Addr.stateId(),
ResolvedProvider: n.ResolvedProvider,
})
// Target
@ -188,7 +189,7 @@ func (n *NodeDestroyResource) EvalTree() EvalNode {
&EvalInstanceInfo{Info: info},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadState{

View File

@ -27,6 +27,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodePlannableResourceInstance{
NodeAbstractResource: a,
@ -37,6 +38,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
concreteResourceOrphan := func(a *NodeAbstractResource) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodePlannableResourceOrphan{
NodeAbstractResource: a,

View File

@ -97,7 +97,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
@ -143,7 +143,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(
Output: &resourceConfig,
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
// Re-run validation to catch any errors we missed, e.g. type

View File

@ -30,6 +30,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodeRefreshableManagedResourceInstance{
NodeAbstractResource: a,
@ -149,7 +150,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
return &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadState{
@ -220,7 +221,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState(
Output: &resourceConfig,
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
// Re-run validation to catch any errors we missed, e.g. type

View File

@ -39,6 +39,7 @@ func (n *NodeValidatableResource) DynamicExpand(ctx EvalContext) (*Graph, error)
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodeValidatableResourceInstance{
NodeAbstractResource: a,
@ -108,7 +109,7 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
Config: &n.Config.RawConfig,
},
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalInterpolate{

View File

@ -12,6 +12,9 @@ type DeposedTransformer struct {
// View, if non-empty, is the ModuleState.View used around the state
// to find deposed resources.
View string
// The provider used by the resourced which were deposed
ResolvedProvider string
}
func (t *DeposedTransformer) Transform(g *Graph) error {
@ -33,14 +36,16 @@ func (t *DeposedTransformer) Transform(g *Graph) error {
if len(rs.Deposed) == 0 {
continue
}
deposed := rs.Deposed
for i, _ := range deposed {
g.Add(&graphNodeDeposedResource{
Index: i,
ResourceName: k,
ResourceType: rs.Type,
Provider: rs.Provider,
Index: i,
ResourceName: k,
ResourceType: rs.Type,
ProviderName: rs.Provider,
ResolvedProvider: t.ResolvedProvider,
})
}
}
@ -50,10 +55,11 @@ func (t *DeposedTransformer) Transform(g *Graph) error {
// graphNodeDeposedResource is the graph vertex representing a deposed resource.
type graphNodeDeposedResource struct {
Index int
ResourceName string
ResourceType string
Provider string
Index int
ResourceName string
ResourceType string
ProviderName string
ResolvedProvider string
}
func (n *graphNodeDeposedResource) Name() string {
@ -61,7 +67,11 @@ func (n *graphNodeDeposedResource) Name() string {
}
func (n *graphNodeDeposedResource) ProvidedBy() []string {
return []string{resourceProvider(n.ResourceName, n.Provider)}
return []string{resourceProvider(n.ResourceName, n.ProviderName)}
}
func (n *graphNodeDeposedResource) SetProvider(p string) {
n.ResolvedProvider = p
}
// GraphNodeEvalable impl.
@ -81,7 +91,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadStateDeposed{
@ -98,7 +108,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
&EvalWriteStateDeposed{
Name: n.ResourceName,
ResourceType: n.ResourceType,
Provider: n.Provider,
Provider: n.ProviderName,
State: &state,
Index: n.Index,
},
@ -114,7 +124,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalReadStateDeposed{
@ -147,7 +157,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
&EvalWriteStateDeposed{
Name: n.ResourceName,
ResourceType: n.ResourceType,
Provider: n.Provider,
Provider: n.ProviderName,
State: &state,
Index: n.Index,
},

View File

@ -21,9 +21,9 @@ func (t *ImportStateTransformer) Transform(g *Graph) error {
}
nodes = append(nodes, &graphNodeImportState{
Addr: addr,
ID: target.ID,
Provider: target.Provider,
Addr: addr,
ID: target.ID,
ProviderName: target.Provider,
})
}
@ -36,9 +36,10 @@ func (t *ImportStateTransformer) Transform(g *Graph) error {
}
type graphNodeImportState struct {
Addr *ResourceAddress // Addr is the resource address to import to
ID string // ID is the ID to import as
Provider string // Provider string
Addr *ResourceAddress // Addr is the resource address to import to
ID string // ID is the ID to import as
ProviderName string // Provider string
ResolvedProvider string // provider node address
states []*InstanceState
}
@ -48,7 +49,11 @@ func (n *graphNodeImportState) Name() string {
}
func (n *graphNodeImportState) ProvidedBy() []string {
return []string{resourceProvider(n.Addr.Type, n.Provider)}
return []string{resourceProvider(n.Addr.Type, n.ProviderName)}
}
func (n *graphNodeImportState) SetProvider(p string) {
n.ResolvedProvider = p
}
// GraphNodeSubPath
@ -72,7 +77,7 @@ func (n *graphNodeImportState) EvalTree() EvalNode {
return &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: n.ProvidedBy()[0],
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalImportState{
@ -149,10 +154,11 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) {
// is safe.
for i, state := range n.states {
g.Add(&graphNodeImportStateSub{
Target: addrs[i],
Path_: n.Path(),
State: state,
Provider: n.Provider,
Target: addrs[i],
Path_: n.Path(),
State: state,
ProviderName: n.ProviderName,
ResolvedProvider: n.ResolvedProvider,
})
}
@ -170,10 +176,11 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) {
// and is part of the subgraph. This node is responsible for refreshing
// and adding a resource to the state once it is imported.
type graphNodeImportStateSub struct {
Target *ResourceAddress
State *InstanceState
Path_ []string
Provider string
Target *ResourceAddress
State *InstanceState
Path_ []string
ProviderName string
ResolvedProvider string
}
func (n *graphNodeImportStateSub) Name() string {
@ -216,7 +223,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode {
return &EvalSequence{
Nodes: []EvalNode{
&EvalGetProvider{
Name: resourceProvider(info.Type, n.Provider),
Name: n.ResolvedProvider,
Output: &provider,
},
&EvalRefresh{
@ -233,7 +240,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode {
&EvalWriteState{
Name: key.String(),
ResourceType: info.Type,
Provider: resourceProvider(info.Type, n.Provider),
Provider: resourceProvider(info.Type, n.ProviderName),
State: &state,
},
},

View File

@ -10,10 +10,12 @@ import (
)
// 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.
// ProviderName returns the name of the provider this satisfies.
// Name returns the full name of the provider in the config.
type GraphNodeProvider interface {
ProviderName() string
Name() string
}
// GraphNodeCloseProvider is an interface that nodes that can be a close
@ -29,6 +31,8 @@ type GraphNodeCloseProvider interface {
type GraphNodeProviderConsumer interface {
// TODO: make this return s string instead of a []string
ProvidedBy() []string
// Set the resolved provider address for this resource.
SetProvider(string)
}
// ProviderTransformer is a GraphTransformer that maps resources to
@ -58,19 +62,16 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
// if we don't have a provider at this level, walk up the path looking for one
for i := 1; target == nil; i++ {
pathPrefix := ""
raw := normalizeModulePath(sp.Path())
if len(raw) < i {
path := normalizeModulePath(sp.Path())
if len(path) < i {
break
}
raw = raw[:len(raw)-i]
if len(raw) > len(rootModulePath) {
pathPrefix = modulePrefixStr(raw) + "."
}
key = pathPrefix + p
key = ResolveProviderName(p, path[:len(path)-i])
target = m[key]
if target != nil {
break
}
}
if target == nil {
@ -80,6 +81,7 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
break
}
pv.SetProvider(key)
g.Connect(dag.BasicEdge(v, target))
}
}
@ -95,36 +97,32 @@ type CloseProviderTransformer struct{}
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)
for _, v := range pm {
p := v.(GraphNodeProvider)
// 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))
// get the close provider of this type if we alread created it
closer := cpm[p.ProviderName()]
// Make sure we also add the new graphNodeCloseProvider to the map
// so we don't create and add any duplicate graphNodeCloseProviders.
cpm[key] = source
}
if closer == nil {
// create a closer for this provider type
closer = &graphNodeCloseProvider{ProviderNameValue: p.ProviderName()}
g.Add(closer)
cpm[p.ProviderName()] = closer
}
// Close node depends on all nodes provided by the provider
g.Connect(dag.BasicEdge(source, v))
// 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.(GraphNodeResource); ok {
g.Connect(dag.BasicEdge(closer, s))
}
}
}
@ -187,54 +185,47 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error {
}
}
for _, p := range pv.ProvidedBy() {
// always add the parent nodes to check, since configured providers
// may have already been added for modules.
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],
})
}
p := pv.ProvidedBy()[0]
// always add the parent nodes to check, since configured providers
// may have already been added for modules.
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],
})
}
key := providerMapKey(p, pv)
key := providerMapKey(p, pv)
if _, ok := m[key]; ok {
// This provider already exists as a configure node
continue
}
// TODO: jbardin come back to this
// only adding root level missing providers
key = p
if _, ok := m[key]; ok {
// This provider already exists as a configure node
// If the provider has an alias in it, we just want the type
// TODO: jbardin -- stop adding aliased providers altogether
ptype := p
if idx := strings.IndexRune(p, '.'); idx != -1 {
ptype = p[:idx]
}
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
}
// If the provider has an alias in it, we just want the type
// TODO: jbardin -- stop adding aliased providers altogether
ptype := p
if idx := strings.IndexRune(p, '.'); idx != -1 {
ptype = p[:idx]
}
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
}
}
// Add the missing provider node to the graph
v := t.Concrete(&NodeAbstractProvider{
NameValue: p,
// TODO: jbardin come back to this
// only adding root level missing providers
//PathValue: path,
}).(dag.Vertex)
m[key] = g.Add(v)
}
// Add the missing provider node to the graph
provider := t.Concrete(&NodeAbstractProvider{
NameValue: p,
PathValue: path,
}).(dag.Vertex)
m[key] = g.Add(provider)
}
return nil
@ -274,15 +265,14 @@ func (t *ParentProviderTransformer) Transform(g *Graph) error {
}
path = normalizeModulePath(path)
// Build the key with path.name i.e. "child.subchild.aws"
key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
key := ResolveProviderName(pn.ProviderName(), path)
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())
key := ResolveProviderName(pn.ProviderName(), path)
parentMap[raw] = key
}
}
@ -323,23 +313,19 @@ func (t *PruneProviderTransformer) Transform(g *Graph) error {
// 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 := ""
// we create a dummy provider to
var path []string
if sp, ok := v.(GraphNodeSubPath); ok {
raw := normalizeModulePath(sp.Path())
if len(raw) > len(rootModulePath) {
pathPrefix = modulePrefixStr(raw) + "."
}
path = normalizeModulePath(sp.Path())
}
return pathPrefix + k
return ResolveProviderName(k, path)
}
func providerVertexMap(g *Graph) map[string]dag.Vertex {
m := make(map[string]dag.Vertex)
for _, v := range g.Vertices() {
if pv, ok := v.(GraphNodeProvider); ok {
key := providerMapKey(pv.ProviderName(), v)
m[key] = v
m[pv.Name()] = v
}
}
@ -416,3 +402,5 @@ func (n *graphNodeProviderConsumerDummy) Path() []string {
func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string {
return []string{n.ProviderValue}
}
func (n *graphNodeProviderConsumerDummy) SetProvider(string) {}

View File

@ -256,7 +256,7 @@ func TestMissingProviderTransformer_moduleGrandchild(t *testing.T) {
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformMissingProviderModuleGrandchildStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
}
}

View File

@ -335,8 +335,13 @@ func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string {
}
func modulePrefixStr(p []string) string {
// strip "root"
if len(p) > 0 && p[0] == rootModulePath[0] {
p = p[1:]
}
parts := make([]string, 0, len(p)*2)
for _, p := range p[1:] {
for _, p := range p {
parts = append(parts, "module", p)
}

View File

@ -37,7 +37,9 @@ func (t *ResourceCountTransformer) Transform(g *Graph) error {
addr.Index = index
// Build the abstract node and the concrete one
abstract := &NodeAbstractResource{Addr: addr}
abstract := &NodeAbstractResource{
Addr: addr,
}
var node dag.Vertex = abstract
if f := t.Concrete; f != nil {
node = f(abstract)