Merge pull request #3393 from TimeIncOSS/b-destroy-cross-providers
Ignore missing variables during destroy phase
This commit is contained in:
commit
2106ccf68f
|
@ -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()
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -13,4 +13,5 @@ const (
|
||||||
walkPlanDestroy
|
walkPlanDestroy
|
||||||
walkRefresh
|
walkRefresh
|
||||||
walkValidate
|
walkValidate
|
||||||
|
walkDestroy
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
variable "value" {}
|
||||||
|
|
||||||
|
resource "aws_vpc" "bar" {
|
||||||
|
value = "${var.value}"
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
resource "terraform_remote_state" "shared" {}
|
||||||
|
|
||||||
|
module "child" {
|
||||||
|
source = "./child"
|
||||||
|
value = "${terraform_remote_state.shared.output.env_name}"
|
||||||
|
}
|
|
@ -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{
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue