save resolved providers for resources to state

Use the ResourceState.Provider field to store the full name of the
provider used during apply. This field is only used when a resource is
removed from the config, and will allow that resource to be removed by
the exact same provider with which it was created.

Modify the locations which might accept the alue of the
ResourceState.Provider field to detect that the name is resolved.
This commit is contained in:
James Bardin 2017-11-07 13:09:36 -05:00
parent 990acca758
commit b79adeae02
10 changed files with 31 additions and 13 deletions

View File

@ -261,7 +261,9 @@ func (r *Resource) ProviderFullName() string {
// the provider name is inferred from the resource type name. // the provider name is inferred from the resource type name.
func ResourceProviderFullName(resourceType, explicitProvider string) string { func ResourceProviderFullName(resourceType, explicitProvider string) string {
if explicitProvider != "" { if explicitProvider != "" {
return explicitProvider // check for an explicit provider name, or return the original
parts := strings.SplitAfter(explicitProvider, "provider.")
return parts[len(parts)-1]
} }
idx := strings.IndexRune(resourceType, '_') idx := strings.IndexRune(resourceType, '_')

View File

@ -147,6 +147,7 @@ func TestNewContextState(t *testing.T) {
} }
func testContext2(t *testing.T, opts *ContextOpts) *Context { func testContext2(t *testing.T, opts *ContextOpts) *Context {
t.Helper()
// Enable the shadow graph // Enable the shadow graph
opts.Shadow = true opts.Shadow = true

View File

@ -108,7 +108,9 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
// Get the state if we have it, if not we build it // Get the state if we have it, if not we build it
rs := n.ResourceState rs := n.ResourceState
if rs == nil { if rs == nil {
rs = &ResourceState{} rs = &ResourceState{
Provider: n.ResolvedProvider,
}
} }
// If the config isn't empty we update the state // If the config isn't empty we update the state

View File

@ -2,6 +2,7 @@ package terraform
import ( import (
"fmt" "fmt"
"strings"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/dag"
@ -25,6 +26,11 @@ type NodeAbstractProvider struct {
} }
func ResolveProviderName(name string, path []string) string { func ResolveProviderName(name string, path []string) string {
if strings.Contains(name, "provider.") {
// already resolved
return name
}
name = fmt.Sprintf("provider.%s", name) name = fmt.Sprintf("provider.%s", name)
if len(path) >= 1 { if len(path) >= 1 {
name = fmt.Sprintf("%s.%s", modulePrefixStr(path), name) name = fmt.Sprintf("%s.%s", modulePrefixStr(path), name)

View File

@ -158,7 +158,7 @@ func (n *NodeApplyableResource) evalTreeDataResource(
&EvalWriteState{ &EvalWriteState{
Name: stateId, Name: stateId,
ResourceType: n.Config.Type, ResourceType: n.Config.Type,
Provider: n.Config.Provider, Provider: n.ResolvedProvider,
Dependencies: stateDeps, Dependencies: stateDeps,
State: &state, State: &state,
}, },
@ -308,7 +308,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
&EvalWriteState{ &EvalWriteState{
Name: stateId, Name: stateId,
ResourceType: n.Config.Type, ResourceType: n.Config.Type,
Provider: n.Config.Provider, Provider: n.ResolvedProvider,
Dependencies: stateDeps, Dependencies: stateDeps,
State: &state, State: &state,
}, },
@ -332,7 +332,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource(
Else: &EvalWriteState{ Else: &EvalWriteState{
Name: stateId, Name: stateId,
ResourceType: n.Config.Type, ResourceType: n.Config.Type,
Provider: n.Config.Provider, Provider: n.ResolvedProvider,
Dependencies: stateDeps, Dependencies: stateDeps,
State: &state, State: &state,
}, },

View File

@ -149,7 +149,9 @@ func (n *NodeDestroyResource) EvalTree() EvalNode {
// Get our state // Get our state
rs := n.ResourceState rs := n.ResourceState
if rs == nil { if rs == nil {
rs = &ResourceState{} rs = &ResourceState{
Provider: n.ResolvedProvider,
}
} }
var diffApply *InstanceDiff var diffApply *InstanceDiff

View File

@ -112,7 +112,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(
&EvalWriteState{ &EvalWriteState{
Name: stateId, Name: stateId,
ResourceType: n.Config.Type, ResourceType: n.Config.Type,
Provider: n.Config.Provider, Provider: n.ResolvedProvider,
Dependencies: stateDeps, Dependencies: stateDeps,
State: &state, State: &state,
}, },
@ -177,7 +177,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(
&EvalWriteState{ &EvalWriteState{
Name: stateId, Name: stateId,
ResourceType: n.Config.Type, ResourceType: n.Config.Type,
Provider: n.Config.Provider, Provider: n.ResolvedProvider,
Dependencies: stateDeps, Dependencies: stateDeps,
State: &state, State: &state,
}, },

View File

@ -251,7 +251,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState(
&EvalWriteState{ &EvalWriteState{
Name: stateID, Name: stateID,
ResourceType: n.Config.Type, ResourceType: n.Config.Type,
Provider: n.Config.Provider, Provider: n.ResolvedProvider,
Dependencies: stateDeps, Dependencies: stateDeps,
State: &state, State: &state,
}, },

View File

@ -108,7 +108,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
&EvalWriteStateDeposed{ &EvalWriteStateDeposed{
Name: n.ResourceName, Name: n.ResourceName,
ResourceType: n.ResourceType, ResourceType: n.ResourceType,
Provider: n.ProviderName, Provider: n.ResolvedProvider,
State: &state, State: &state,
Index: n.Index, Index: n.Index,
}, },
@ -157,7 +157,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
&EvalWriteStateDeposed{ &EvalWriteStateDeposed{
Name: n.ResourceName, Name: n.ResourceName,
ResourceType: n.ResourceType, ResourceType: n.ResourceType,
Provider: n.ProviderName, Provider: n.ResolvedProvider,
State: &state, State: &state,
Index: n.Index, Index: n.Index,
}, },

View File

@ -52,7 +52,8 @@ type GraphNodeCloseProvider interface {
// GraphNodeProviderConsumer is an interface that nodes that require // GraphNodeProviderConsumer is an interface that nodes that require
// a provider must implement. ProvidedBy must return the name of the provider // a provider must implement. ProvidedBy must return the name of the provider
// to use. // to use. This may be a provider by type, type.alias or a fully resolved
// provider name
type GraphNodeProviderConsumer interface { type GraphNodeProviderConsumer interface {
ProvidedBy() string ProvidedBy() string
// Set the resolved provider address for this resource. // Set the resolved provider address for this resource.
@ -127,7 +128,6 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
// in the graph are evaluated. // in the graph are evaluated.
type CloseProviderTransformer struct{} type CloseProviderTransformer struct{}
// FIXME: this doesn't close providers if the root provider is disabled
func (t *CloseProviderTransformer) Transform(g *Graph) error { func (t *CloseProviderTransformer) Transform(g *Graph) error {
pm := providerVertexMap(g) pm := providerVertexMap(g)
cpm := make(map[string]*graphNodeCloseProvider) cpm := make(map[string]*graphNodeCloseProvider)
@ -286,6 +286,11 @@ 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 {
if strings.Contains(k, "provider.") {
// this is already resolved
return k
}
// we create a dummy provider to // we create a dummy provider to
var path []string var path []string
if sp, ok := v.(GraphNodeSubPath); ok { if sp, ok := v.(GraphNodeSubPath); ok {