diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 22d7d6db3..0f1fd47a6 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -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) } } diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index 7dc860bb5..3eb213028 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -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) } diff --git a/terraform/eval_context.go b/terraform/eval_context.go index 655916fc3..86481dedb 100644 --- a/terraform/eval_context.go +++ b/terraform/eval_context.go @@ -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. diff --git a/terraform/eval_context_builtin.go b/terraform/eval_context_builtin.go index 3753beec7..193421b78 100644 --- a/terraform/eval_context_builtin.go +++ b/terraform/eval_context_builtin.go @@ -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 } diff --git a/terraform/eval_context_mock.go b/terraform/eval_context_mock.go index a148a54bf..646451795 100644 --- a/terraform/eval_context_mock.go +++ b/terraform/eval_context_mock.go @@ -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 diff --git a/terraform/eval_provider.go b/terraform/eval_provider.go index 328d72b5e..61f6ff941 100644 --- a/terraform/eval_provider.go +++ b/terraform/eval_provider.go @@ -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 diff --git a/terraform/evaltree_provider.go b/terraform/evaltree_provider.go index 55e5c5258..0c3da48f0 100644 --- a/terraform/evaltree_provider.go +++ b/terraform/evaltree_provider.go @@ -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, }, }, diff --git a/terraform/graph_builder_import.go b/terraform/graph_builder_import.go index 7070c59e4..e064ae629 100644 --- a/terraform/graph_builder_import.go +++ b/terraform/graph_builder_import.go @@ -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{}, diff --git a/terraform/graph_builder_refresh.go b/terraform/graph_builder_refresh.go index beb5a4a44..da506170b 100644 --- a/terraform/graph_builder_refresh.go +++ b/terraform/graph_builder_refresh.go @@ -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{}, diff --git a/terraform/node_data_refresh.go b/terraform/node_data_refresh.go index 45129b3cb..15d9b8f9c 100644 --- a/terraform/node_data_refresh.go +++ b/terraform/node_data_refresh.go @@ -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, }, diff --git a/terraform/node_provider.go b/terraform/node_provider.go index 8e2c176fa..2071ab168 100644 --- a/terraform/node_provider.go +++ b/terraform/node_provider.go @@ -7,5 +7,5 @@ type NodeApplyableProvider struct { // GraphNodeEvalable func (n *NodeApplyableProvider) EvalTree() EvalNode { - return ProviderEvalTree(n.NameValue, n.ProviderConfig()) + return ProviderEvalTree(n, n.ProviderConfig()) } diff --git a/terraform/node_provider_abstract.go b/terraform/node_provider_abstract.go index 4a2c00087..3230558e8 100644 --- a/terraform/node_provider_abstract.go +++ b/terraform/node_provider_abstract.go @@ -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 diff --git a/terraform/node_resource_abstract.go b/terraform/node_resource_abstract.go index 50bb70792..e46c32009 100644 --- a/terraform/node_resource_abstract.go +++ b/terraform/node_resource_abstract.go @@ -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 diff --git a/terraform/node_resource_apply.go b/terraform/node_resource_apply.go index 3599782b9..807b1f416 100644 --- a/terraform/node_resource_apply.go +++ b/terraform/node_resource_apply.go @@ -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{ diff --git a/terraform/node_resource_destroy.go b/terraform/node_resource_destroy.go index c2efd2c38..cffb9ae60 100644 --- a/terraform/node_resource_destroy.go +++ b/terraform/node_resource_destroy.go @@ -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{ diff --git a/terraform/node_resource_plan.go b/terraform/node_resource_plan.go index 52bbf88a1..1afae7a04 100644 --- a/terraform/node_resource_plan.go +++ b/terraform/node_resource_plan.go @@ -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, diff --git a/terraform/node_resource_plan_instance.go b/terraform/node_resource_plan_instance.go index b52956908..25a76a99f 100644 --- a/terraform/node_resource_plan_instance.go +++ b/terraform/node_resource_plan_instance.go @@ -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 diff --git a/terraform/node_resource_refresh.go b/terraform/node_resource_refresh.go index cd4fe9201..d504e7de4 100644 --- a/terraform/node_resource_refresh.go +++ b/terraform/node_resource_refresh.go @@ -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 diff --git a/terraform/node_resource_validate.go b/terraform/node_resource_validate.go index f528f24b1..0df223d9e 100644 --- a/terraform/node_resource_validate.go +++ b/terraform/node_resource_validate.go @@ -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{ diff --git a/terraform/transform_deposed.go b/terraform/transform_deposed.go index 2148cef47..fd920fbda 100644 --- a/terraform/transform_deposed.go +++ b/terraform/transform_deposed.go @@ -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, }, diff --git a/terraform/transform_import_state.go b/terraform/transform_import_state.go index 081df2f84..762bf1ded 100644 --- a/terraform/transform_import_state.go +++ b/terraform/transform_import_state.go @@ -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, }, }, diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index 0bbbd1d99..37bb6be6c 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -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) {} diff --git a/terraform/transform_provider_test.go b/terraform/transform_provider_test.go index 29561a246..a577d2c12 100644 --- a/terraform/transform_provider_test.go +++ b/terraform/transform_provider_test.go @@ -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) } } diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index 24ddc7a0a..2560e5ad6 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -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) } diff --git a/terraform/transform_resource_count.go b/terraform/transform_resource_count.go index cda35cb7b..e528b37b4 100644 --- a/terraform/transform_resource_count.go +++ b/terraform/transform_resource_count.go @@ -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)