Merge pull request #16213 from hashicorp/jbardin/local-destroy

Don't evaluate locals during destroy
This commit is contained in:
James Bardin 2017-09-28 16:13:31 -04:00 committed by GitHub
commit feb3883568
5 changed files with 184 additions and 14 deletions

View File

@ -8809,3 +8809,110 @@ module.child:
t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
}
}
func TestContext2Apply_destroyWithLocals(t *testing.T) {
m := testModule(t, "apply-destroy-with-locals")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) {
d, err := testDiffFn(info, s, c)
fmt.Println("DIFF:", d)
return d, err
}
s := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Outputs: map[string]*OutputState{
"name": &OutputState{
Type: "string",
Value: "test-bar",
},
},
Resources: map[string]*ResourceState{
"aws_instance.foo": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "foo",
},
},
},
},
},
}
ctx := testContext2(t, &ContextOpts{
Module: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
State: s,
Destroy: true,
})
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("error during apply: %s", err)
}
got := strings.TrimSpace(state.String())
want := strings.TrimSpace(`<no state>`)
if got != want {
t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
}
}
func TestContext2Apply_providerWithLocals(t *testing.T) {
m := testModule(t, "provider-with-locals")
p := testProvider("aws")
p.DiffFn = testDiffFn
p.ApplyFn = testApplyFn
ctx := testContext2(t, &ContextOpts{
Module: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
})
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
ctx = testContext2(t, &ContextOpts{
Module: m,
ProviderResolver: ResourceProviderResolverFixed(
map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
),
State: state,
Destroy: true,
})
if _, err = ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err = ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
if state.HasResources() {
t.Fatal("expected no state, got:", state)
}
}

View File

@ -56,3 +56,31 @@ func (n *EvalLocal) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil
}
// EvalDeleteLocal is an EvalNode implementation that deletes a Local value
// from the state. Locals aren't persisted, but we don't need to evaluate them
// during destroy.
type EvalDeleteLocal struct {
Name string
}
func (n *EvalDeleteLocal) Eval(ctx EvalContext) (interface{}, error) {
state, lock := ctx.State()
if state == nil {
return nil, nil
}
// Get a write lock so we can access this instance
lock.Lock()
defer lock.Unlock()
// Look for the module state. If we don't have one, create it.
mod := state.ModuleByPath(ctx.Path())
if mod == nil {
return nil, nil
}
delete(mod.Locals, n.Name)
return nil, nil
}

View File

@ -59,14 +59,15 @@ func (n *NodeLocal) References() []string {
// GraphNodeEvalable
func (n *NodeLocal) EvalTree() EvalNode {
return &EvalOpFilter{
return &EvalSequence{
Nodes: []EvalNode{
&EvalOpFilter{
Ops: []walkOperation{
walkInput,
walkValidate,
walkRefresh,
walkPlan,
walkApply,
walkDestroy,
},
Node: &EvalSequence{
Nodes: []EvalNode{
@ -76,5 +77,20 @@ func (n *NodeLocal) EvalTree() EvalNode {
},
},
},
},
&EvalOpFilter{
Ops: []walkOperation{
walkPlanDestroy,
walkDestroy,
},
Node: &EvalSequence{
Nodes: []EvalNode{
&EvalDeleteLocal{
Name: n.Config.Name,
},
},
},
},
},
}
}

View File

@ -0,0 +1,8 @@
locals {
name = "test-${aws_instance.foo.id}"
}
resource "aws_instance" "foo" {}
output "name" {
value = "${local.name}"
}

View File

@ -0,0 +1,11 @@
provider "aws" {
alias = "${local.foo}"
}
locals {
foo = "bar"
}
resource "aws_instance" "foo" {
value = "${local.foo}"
}