Merge pull request #3393 from TimeIncOSS/b-destroy-cross-providers

Ignore missing variables during destroy phase
This commit is contained in:
Radek Simko 2015-10-03 14:24:35 -07:00
commit 2106ccf68f
15 changed files with 117 additions and 17 deletions

View File

@ -292,7 +292,11 @@ func (c *Context) Apply() (*State, error) {
} }
// Do the walk // Do the walk
_, err = c.walk(graph, walkApply) if c.destroy {
_, err = c.walk(graph, walkDestroy)
} else {
_, err = c.walk(graph, walkApply)
}
// Clean out any unused things // Clean out any unused things
c.state.prune() c.state.prune()

View File

@ -10,6 +10,8 @@ import (
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
"github.com/hashicorp/terraform/config/module"
) )
func TestContext2Apply(t *testing.T) { func TestContext2Apply(t *testing.T) {
@ -298,6 +300,88 @@ func TestContext2Apply_destroyComputed(t *testing.T) {
} }
} }
// https://github.com/hashicorp/terraform/issues/2892
func TestContext2Apply_destroyCrossProviders(t *testing.T) {
m := testModule(t, "apply-destroy-cross-providers")
p_aws := testProvider("aws")
p_aws.ApplyFn = testApplyFn
p_aws.DiffFn = testDiffFn
p_tf := testProvider("terraform")
p_tf.ApplyFn = testApplyFn
p_tf.DiffFn = testDiffFn
providers := map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p_aws),
"terraform": testProviderFuncFixed(p_tf),
}
// Bug only appears from time to time,
// so we run this test multiple times
// to check for the race-condition
for i := 0; i <= 10; i++ {
ctx := getContextForApply_destroyCrossProviders(
t, m, providers)
if p, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
} else {
t.Logf(p.String())
}
if _, err := ctx.Apply(); err != nil {
t.Fatalf("err: %s", err)
}
}
}
func getContextForApply_destroyCrossProviders(
t *testing.T,
m *module.Tree,
providers map[string]ResourceProviderFactory) *Context {
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"terraform_remote_state.shared": &ResourceState{
Type: "terraform_remote_state",
Primary: &InstanceState{
ID: "remote-2652591293",
Attributes: map[string]string{
"output.env_name": "test",
},
},
},
},
},
&ModuleState{
Path: []string{"root", "example"},
Resources: map[string]*ResourceState{
"aws_vpc.bar": &ResourceState{
Type: "aws_vpc",
Primary: &InstanceState{
ID: "vpc-aaabbb12",
Attributes: map[string]string{
"value": "test",
},
},
},
},
},
},
}
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: providers,
State: state,
Destroy: true,
})
return ctx
}
func TestContext2Apply_minimal(t *testing.T) { func TestContext2Apply_minimal(t *testing.T) {
m := testModule(t, "apply-minimal") m := testModule(t, "apply-minimal")
p := testProvider("aws") p := testProvider("aws")

View File

@ -71,7 +71,7 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
// Apply stuff // Apply stuff
seq = append(seq, &EvalOpFilter{ seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkRefresh, walkPlan, walkApply}, Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalGetProvider{ &EvalGetProvider{
@ -98,7 +98,7 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
// We configure on everything but validate, since validate may // We configure on everything but validate, since validate may
// not have access to all the variables. // not have access to all the variables.
seq = append(seq, &EvalOpFilter{ seq = append(seq, &EvalOpFilter{
Ops: []walkOperation{walkRefresh, walkPlan, walkApply}, Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalConfigProvider{ &EvalConfigProvider{

View File

@ -44,7 +44,7 @@ func (n *GraphNodeConfigOutput) DependentOn() []string {
// GraphNodeEvalable impl. // GraphNodeEvalable impl.
func (n *GraphNodeConfigOutput) EvalTree() EvalNode { func (n *GraphNodeConfigOutput) EvalTree() EvalNode {
return &EvalOpFilter{ return &EvalOpFilter{
Ops: []walkOperation{walkRefresh, walkPlan, walkApply}, Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalWriteOutput{ &EvalWriteOutput{

View File

@ -13,4 +13,5 @@ const (
walkPlanDestroy walkPlanDestroy
walkRefresh walkRefresh
walkValidate walkValidate
walkDestroy
) )

View File

@ -342,7 +342,7 @@ func (i *Interpolater) computeResourceVariable(
// TODO: test by creating a state and configuration that is referencing // TODO: test by creating a state and configuration that is referencing
// a non-existent variable "foo.bar" where the state only has "foo" // a non-existent variable "foo.bar" where the state only has "foo"
// and verify plan works, but apply doesn't. // and verify plan works, but apply doesn't.
if i.Operation == walkApply { if i.Operation == walkApply || i.Operation == walkDestroy {
goto MISSING goto MISSING
} }
@ -384,7 +384,7 @@ MISSING:
// //
// For an input walk, computed values are okay to return because we're only // For an input walk, computed values are okay to return because we're only
// looking for missing variables to prompt the user for. // looking for missing variables to prompt the user for.
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkInput { if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput {
return config.UnknownVariableValue, nil return config.UnknownVariableValue, nil
} }
@ -481,7 +481,7 @@ func (i *Interpolater) computeResourceMultiVariable(
// //
// For an input walk, computed values are okay to return because we're only // For an input walk, computed values are okay to return because we're only
// looking for missing variables to prompt the user for. // looking for missing variables to prompt the user for.
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkInput { if i.Operation == walkRefresh || i.Operation == walkPlanDestroy || i.Operation == walkDestroy || i.Operation == walkInput {
return config.UnknownVariableValue, nil return config.UnknownVariableValue, nil
} }

View File

@ -0,0 +1,5 @@
variable "value" {}
resource "aws_vpc" "bar" {
value = "${var.value}"
}

View File

@ -0,0 +1,6 @@
resource "terraform_remote_state" "shared" {}
module "child" {
source = "./child"
value = "${terraform_remote_state.shared.output.env_name}"
}

View File

@ -110,7 +110,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
var diff *InstanceDiff var diff *InstanceDiff
var err error var err error
seq.Nodes = append(seq.Nodes, &EvalOpFilter{ seq.Nodes = append(seq.Nodes, &EvalOpFilter{
Ops: []walkOperation{walkApply}, Ops: []walkOperation{walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalGetProvider{ &EvalGetProvider{

View File

@ -263,7 +263,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
// Apply // Apply
var err error var err error
seq.Nodes = append(seq.Nodes, &EvalOpFilter{ seq.Nodes = append(seq.Nodes, &EvalOpFilter{
Ops: []walkOperation{walkApply}, Ops: []walkOperation{walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalReadDiff{ &EvalReadDiff{

View File

@ -62,7 +62,7 @@ func (n *graphNodeOrphanOutput) Name() string {
func (n *graphNodeOrphanOutput) EvalTree() EvalNode { func (n *graphNodeOrphanOutput) EvalTree() EvalNode {
return &EvalOpFilter{ return &EvalOpFilter{
Ops: []walkOperation{walkApply, walkRefresh}, Ops: []walkOperation{walkApply, walkDestroy, walkRefresh},
Node: &EvalDeleteOutput{ Node: &EvalDeleteOutput{
Name: n.OutputName, Name: n.OutputName,
}, },
@ -90,7 +90,7 @@ func (n *graphNodeOrphanOutputFlat) Name() string {
func (n *graphNodeOrphanOutputFlat) EvalTree() EvalNode { func (n *graphNodeOrphanOutputFlat) EvalTree() EvalNode {
return &EvalOpFilter{ return &EvalOpFilter{
Ops: []walkOperation{walkApply, walkRefresh}, Ops: []walkOperation{walkApply, walkDestroy, walkRefresh},
Node: &EvalDeleteOutput{ Node: &EvalDeleteOutput{
Name: n.OutputName, Name: n.OutputName,
}, },

View File

@ -255,7 +255,7 @@ func (n *graphNodeDisabledProvider) EvalTree() EvalNode {
var resourceConfig *ResourceConfig var resourceConfig *ResourceConfig
return &EvalOpFilter{ return &EvalOpFilter{
Ops: []walkOperation{walkInput, walkValidate, walkRefresh, walkPlan, walkApply}, Ops: []walkOperation{walkInput, walkValidate, walkRefresh, walkPlan, walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalInterpolate{ &EvalInterpolate{

View File

@ -369,7 +369,7 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
var createNew, tainted bool var createNew, tainted bool
var createBeforeDestroyEnabled bool var createBeforeDestroyEnabled bool
seq.Nodes = append(seq.Nodes, &EvalOpFilter{ seq.Nodes = append(seq.Nodes, &EvalOpFilter{
Ops: []walkOperation{walkApply}, Ops: []walkOperation{walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
// Get the saved diff for apply // Get the saved diff for apply
@ -591,7 +591,7 @@ func (n *graphNodeExpandedResourceDestroy) EvalTree() EvalNode {
var state *InstanceState var state *InstanceState
var err error var err error
return &EvalOpFilter{ return &EvalOpFilter{
Ops: []walkOperation{walkApply}, Ops: []walkOperation{walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
// Get the saved diff for apply // Get the saved diff for apply

View File

@ -114,7 +114,7 @@ func (n *graphNodeTaintedResource) EvalTree() EvalNode {
// Apply // Apply
var diff *InstanceDiff var diff *InstanceDiff
seq.Nodes = append(seq.Nodes, &EvalOpFilter{ seq.Nodes = append(seq.Nodes, &EvalOpFilter{
Ops: []walkOperation{walkApply}, Ops: []walkOperation{walkApply, walkDestroy},
Node: &EvalSequence{ Node: &EvalSequence{
Nodes: []EvalNode{ Nodes: []EvalNode{
&EvalGetProvider{ &EvalGetProvider{

View File

@ -4,9 +4,9 @@ package terraform
import "fmt" import "fmt"
const _walkOperation_name = "walkInvalidwalkInputwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidate" const _walkOperation_name = "walkInvalidwalkInputwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroy"
var _walkOperation_index = [...]uint8{0, 11, 20, 29, 37, 52, 63, 75} var _walkOperation_index = [...]uint8{0, 11, 20, 29, 37, 52, 63, 75, 86}
func (i walkOperation) String() string { func (i walkOperation) String() string {
if i >= walkOperation(len(_walkOperation_index)-1) { if i >= walkOperation(len(_walkOperation_index)-1) {