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()) actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr) expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr)
if actual != expected { 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{ 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", "child\nchild\n",
"root\n",
} }
sort.Strings(calls)
if !reflect.DeepEqual(calls, expected) { if !reflect.DeepEqual(calls, expected) {
t.Fatalf("expected:\n%#v\ngot:\n%#v\n", expected, calls) 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 is the UIInput object for interacting with the UI.
Input() UIInput 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. // returns the implementation of the resource provider or an error.
// //
// It is an error to initialize the same provider more than once. // 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 // Provider gets the provider instance with the given name (already
// initialized) or returns nil if the provider isn't initialized. // initialized) or returns nil if the provider isn't initialized.

View File

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

View File

@ -107,7 +107,7 @@ func (c *MockEvalContext) Input() UIInput {
return c.InputInput return c.InputInput
} }
func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) { func (c *MockEvalContext) InitProvider(t, n string) (ResourceProvider, error) {
c.InitProviderCalled = true c.InitProviderCalled = true
c.InitProviderName = n c.InitProviderName = n
return c.InitProviderProvider, c.InitProviderError 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 // and returns nothing. The provider can be retrieved again with the
// EvalGetProvider node. // EvalGetProvider node.
type EvalInitProvider struct { type EvalInitProvider struct {
Name string TypeName string
Name string
} }
func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) { 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 // 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) ctx.SetProviderInput(n.Name, confMap)
return nil, nil return nil, nil

View File

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

View File

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

View File

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

View File

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

View File

@ -7,5 +7,5 @@ type NodeApplyableProvider struct {
// GraphNodeEvalable // GraphNodeEvalable
func (n *NodeApplyableProvider) EvalTree() EvalNode { 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 Config *config.ProviderConfig
} }
func (n *NodeAbstractProvider) Name() string { func ResolveProviderName(name string, path []string) string {
result := fmt.Sprintf("provider.%s", n.NameValue) name = fmt.Sprintf("provider.%s", name)
if len(n.PathValue) > 1 { if len(path) > 1 {
result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) name = fmt.Sprintf("%s.%s", modulePrefixStr(path), name)
} }
return result return name
}
func (n *NodeAbstractProvider) Name() string {
return ResolveProviderName(n.NameValue, n.PathValue)
} }
// GraphNodeSubPath // GraphNodeSubPath

View File

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

View File

@ -135,7 +135,7 @@ func (n *NodeApplyableResource) evalTreeDataResource(
}, },
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
@ -242,7 +242,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
Output: &resourceConfig, Output: &resourceConfig,
}, },
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
&EvalReadState{ &EvalReadState{
@ -283,7 +283,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
}, },
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
&EvalReadState{ &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 // We want deposed resources in the state to be destroyed
steps = append(steps, &DeposedTransformer{ steps = append(steps, &DeposedTransformer{
State: state, State: state,
View: n.Addr.stateId(), View: n.Addr.stateId(),
ResolvedProvider: n.ResolvedProvider,
}) })
// Target // Target
@ -188,7 +189,7 @@ func (n *NodeDestroyResource) EvalTree() EvalNode {
&EvalInstanceInfo{Info: info}, &EvalInstanceInfo{Info: info},
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
&EvalReadState{ &EvalReadState{

View File

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

View File

@ -97,7 +97,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(
}, },
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
@ -143,7 +143,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(
Output: &resourceConfig, Output: &resourceConfig,
}, },
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
// Re-run validation to catch any errors we missed, e.g. type // 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 { concreteResource := func(a *NodeAbstractResource) dag.Vertex {
// Add the config and state since we don't do that via transforms // Add the config and state since we don't do that via transforms
a.Config = n.Config a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodeRefreshableManagedResourceInstance{ return &NodeRefreshableManagedResourceInstance{
NodeAbstractResource: a, NodeAbstractResource: a,
@ -149,7 +150,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
return &EvalSequence{ return &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
&EvalReadState{ &EvalReadState{
@ -220,7 +221,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState(
Output: &resourceConfig, Output: &resourceConfig,
}, },
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
// Re-run validation to catch any errors we missed, e.g. type // 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 { concreteResource := func(a *NodeAbstractResource) dag.Vertex {
// Add the config and state since we don't do that via transforms // Add the config and state since we don't do that via transforms
a.Config = n.Config a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
return &NodeValidatableResourceInstance{ return &NodeValidatableResourceInstance{
NodeAbstractResource: a, NodeAbstractResource: a,
@ -108,7 +109,7 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode {
Config: &n.Config.RawConfig, Config: &n.Config.RawConfig,
}, },
&EvalGetProvider{ &EvalGetProvider{
Name: n.ProvidedBy()[0], Name: n.ResolvedProvider,
Output: &provider, Output: &provider,
}, },
&EvalInterpolate{ &EvalInterpolate{

View File

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

View File

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

View File

@ -10,10 +10,12 @@ import (
) )
// GraphNodeProvider is an interface that nodes that can be a provider // GraphNodeProvider is an interface that nodes that can be a provider
// must implement. The ProviderName returned is the name of the provider // must implement.
// they satisfy. // ProviderName returns the name of the provider this satisfies.
// Name returns the full name of the provider in the config.
type GraphNodeProvider interface { type GraphNodeProvider interface {
ProviderName() string ProviderName() string
Name() string
} }
// GraphNodeCloseProvider is an interface that nodes that can be a close // GraphNodeCloseProvider is an interface that nodes that can be a close
@ -29,6 +31,8 @@ type GraphNodeCloseProvider interface {
type GraphNodeProviderConsumer interface { type GraphNodeProviderConsumer interface {
// TODO: make this return s string instead of a []string // TODO: make this return s string instead of a []string
ProvidedBy() []string ProvidedBy() []string
// Set the resolved provider address for this resource.
SetProvider(string)
} }
// ProviderTransformer is a GraphTransformer that maps resources to // 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 // if we don't have a provider at this level, walk up the path looking for one
for i := 1; target == nil; i++ { for i := 1; target == nil; i++ {
pathPrefix := "" path := normalizeModulePath(sp.Path())
raw := normalizeModulePath(sp.Path()) if len(path) < i {
if len(raw) < i {
break break
} }
raw = raw[:len(raw)-i] key = ResolveProviderName(p, path[:len(path)-i])
if len(raw) > len(rootModulePath) {
pathPrefix = modulePrefixStr(raw) + "."
}
key = pathPrefix + p
target = m[key] target = m[key]
if target != nil {
break
}
} }
if target == nil { if target == nil {
@ -80,6 +81,7 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
break break
} }
pv.SetProvider(key)
g.Connect(dag.BasicEdge(v, target)) g.Connect(dag.BasicEdge(v, target))
} }
} }
@ -95,36 +97,32 @@ type CloseProviderTransformer struct{}
func (t *CloseProviderTransformer) Transform(g *Graph) error { func (t *CloseProviderTransformer) Transform(g *Graph) error {
pm := providerVertexMap(g) pm := providerVertexMap(g)
cpm := closeProviderVertexMap(g) cpm := make(map[string]*graphNodeCloseProvider)
var err error 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 { for _, v := range pm {
// Create a new graphNodeCloseProvider and add it to the graph p := v.(GraphNodeProvider)
source = &graphNodeCloseProvider{ProviderNameValue: p}
g.Add(source)
// Close node needs to depend on provider // get the close provider of this type if we alread created it
provider, ok := pm[key] closer := cpm[p.ProviderName()]
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 if closer == nil {
// so we don't create and add any duplicate graphNodeCloseProviders. // create a closer for this provider type
cpm[key] = source closer = &graphNodeCloseProvider{ProviderNameValue: p.ProviderName()}
} g.Add(closer)
cpm[p.ProviderName()] = closer
}
// Close node depends on all nodes provided by the provider // Close node depends on the provider itself
g.Connect(dag.BasicEdge(source, v)) // 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() { p := pv.ProvidedBy()[0]
// always add the parent nodes to check, since configured providers // always add the parent nodes to check, since configured providers
// may have already been added for modules. // may have already been added for modules.
if len(path) > 0 { if len(path) > 0 {
// We'll need the parent provider as well, so let's // We'll need the parent provider as well, so let's
// add a dummy node to check to make sure that we add // add a dummy node to check to make sure that we add
// that parent provider. // that parent provider.
check = append(check, &graphNodeProviderConsumerDummy{ check = append(check, &graphNodeProviderConsumerDummy{
ProviderValue: p, ProviderValue: p,
PathValue: path[:len(path)-1], 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 // If the provider has an alias in it, we just want the type
// only adding root level missing providers // TODO: jbardin -- stop adding aliased providers altogether
key = p ptype := p
if _, ok := m[key]; ok { if idx := strings.IndexRune(p, '.'); idx != -1 {
// This provider already exists as a configure node 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 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 return nil
@ -274,15 +265,14 @@ func (t *ParentProviderTransformer) Transform(g *Graph) error {
} }
path = normalizeModulePath(path) path = normalizeModulePath(path)
// Build the key with path.name i.e. "child.subchild.aws" key := ResolveProviderName(pn.ProviderName(), path)
key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName())
m[key] = raw m[key] = raw
// Determine the parent if we're non-root. This is length 1 since // Determine the parent if we're non-root. This is length 1 since
// the 0 index should be "root" since we normalize above. // the 0 index should be "root" since we normalize above.
if len(path) > 1 { if len(path) > 1 {
path = path[: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 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 // providerMapKey is a helper that gives us the key to use for the
// maps returned by things such as providerVertexMap. // maps returned by things such as providerVertexMap.
func providerMapKey(k string, v dag.Vertex) string { func providerMapKey(k string, v dag.Vertex) string {
pathPrefix := "" // we create a dummy provider to
var path []string
if sp, ok := v.(GraphNodeSubPath); ok { if sp, ok := v.(GraphNodeSubPath); ok {
raw := normalizeModulePath(sp.Path()) path = normalizeModulePath(sp.Path())
if len(raw) > len(rootModulePath) {
pathPrefix = modulePrefixStr(raw) + "."
}
} }
return ResolveProviderName(k, path)
return pathPrefix + k
} }
func providerVertexMap(g *Graph) map[string]dag.Vertex { func providerVertexMap(g *Graph) map[string]dag.Vertex {
m := make(map[string]dag.Vertex) m := make(map[string]dag.Vertex)
for _, v := range g.Vertices() { for _, v := range g.Vertices() {
if pv, ok := v.(GraphNodeProvider); ok { if pv, ok := v.(GraphNodeProvider); ok {
key := providerMapKey(pv.ProviderName(), v) m[pv.Name()] = v
m[key] = v
} }
} }
@ -416,3 +402,5 @@ func (n *graphNodeProviderConsumerDummy) Path() []string {
func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string { func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string {
return []string{n.ProviderValue} 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()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformMissingProviderModuleGrandchildStr) expected := strings.TrimSpace(testTransformMissingProviderModuleGrandchildStr)
if actual != expected { 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 { 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) parts := make([]string, 0, len(p)*2)
for _, p := range p[1:] { for _, p := range p {
parts = append(parts, "module", p) parts = append(parts, "module", p)
} }

View File

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