From 6ee3a08799dc4d13174bde099797122d57d06e33 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 28 Nov 2016 13:35:29 -0800 Subject: [PATCH] terraform: deposed shows up in plan, tests --- terraform/context_plan_test.go | 54 ++++++++++++++++++++++++ terraform/diff.go | 28 +++++++++--- terraform/eval_diff_deposed.go | 37 ++++++++++++++++ terraform/node_resource_plan_instance.go | 4 ++ terraform/test-fixtures/plan-cbd/main.tf | 3 ++ 5 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 terraform/eval_diff_deposed.go create mode 100644 terraform/test-fixtures/plan-cbd/main.tf diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index 9c92b2f43e..aafb74b076 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -38,6 +38,60 @@ func TestContext2Plan_basic(t *testing.T) { } } +func TestContext2Plan_createBefore_deposed(t *testing.T) { + m := testModule(t, "plan-cbd") + p := testProvider("aws") + p.DiffFn = testDiffFn + + s := &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: []string{"root"}, + Resources: map[string]*ResourceState{ + "aws_instance.foo": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "baz", + }, + Deposed: []*InstanceState{ + &InstanceState{ID: "foo"}, + }, + }, + }, + }, + }, + } + + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: s, + }) + + plan, err := ctx.Plan() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(plan.String()) + expected := strings.TrimSpace(` +DIFF: + +DESTROY: aws_instance.foo (deposed only) + +STATE: + +aws_instance.foo: (1 deposed) + ID = baz + Deposed ID 1 = foo + `) + if actual != expected { + t.Fatalf("expected:\n%s, got:\n%s", expected, actual) + } +} + func TestContext2Plan_createBefore_maintainRoot(t *testing.T) { m := testModule(t, "plan-cbd-maintain-root") p := testProvider("aws") diff --git a/terraform/diff.go b/terraform/diff.go index 002f729215..75fd1778af 100644 --- a/terraform/diff.go +++ b/terraform/diff.go @@ -291,16 +291,22 @@ func (d *ModuleDiff) String() string { switch { case rdiff.RequiresNew() && (rdiff.GetDestroy() || rdiff.GetDestroyTainted()): crud = "DESTROY/CREATE" - case rdiff.GetDestroy(): + case rdiff.GetDestroy() || rdiff.GetDestroyDeposed(): crud = "DESTROY" case rdiff.RequiresNew(): crud = "CREATE" } + extra := "" + if !rdiff.GetDestroy() && rdiff.GetDestroyDeposed() { + extra = " (deposed only)" + } + buf.WriteString(fmt.Sprintf( - "%s: %s\n", + "%s: %s%s\n", crud, - name)) + name, + extra)) keyLen := 0 rdiffAttrs := rdiff.CopyAttributes() @@ -356,6 +362,7 @@ type InstanceDiff struct { mu sync.Mutex Attributes map[string]*ResourceAttrDiff Destroy bool + DestroyDeposed bool DestroyTainted bool } @@ -430,7 +437,7 @@ func (d *InstanceDiff) ChangeType() DiffChangeType { return DiffDestroyCreate } - if d.GetDestroy() { + if d.GetDestroy() || d.GetDestroyDeposed() { return DiffDestroy } @@ -449,7 +456,10 @@ func (d *InstanceDiff) Empty() bool { d.mu.Lock() defer d.mu.Unlock() - return !d.Destroy && !d.DestroyTainted && len(d.Attributes) == 0 + return !d.Destroy && + !d.DestroyTainted && + !d.DestroyDeposed && + len(d.Attributes) == 0 } // Equal compares two diffs for exact equality. @@ -482,6 +492,7 @@ func (d *InstanceDiff) GoString() string { Attributes: d.Attributes, Destroy: d.Destroy, DestroyTainted: d.DestroyTainted, + DestroyDeposed: d.DestroyDeposed, }) } @@ -516,6 +527,13 @@ func (d *InstanceDiff) requiresNew() bool { return false } +func (d *InstanceDiff) GetDestroyDeposed() bool { + d.mu.Lock() + defer d.mu.Unlock() + + return d.DestroyDeposed +} + // These methods are properly locked, for use outside other InstanceDiff // methods but everywhere else within in the terraform package. // TODO refactor the locking scheme diff --git a/terraform/eval_diff_deposed.go b/terraform/eval_diff_deposed.go new file mode 100644 index 0000000000..c873690905 --- /dev/null +++ b/terraform/eval_diff_deposed.go @@ -0,0 +1,37 @@ +package terraform + +// EvalDiffDeposed is an EvalNode implementation that marks DestroyDeposed +// in a diff if a resource has deposed instances that need destruction. +type EvalDiffDeposed struct { + Name string + Diff **InstanceDiff +} + +// TODO: test +func (n *EvalDiffDeposed) Eval(ctx EvalContext) (interface{}, error) { + // Check if there are any deposed items in the state + deposed := false + _, err := readInstanceFromState(ctx, n.Name, nil, func(rs *ResourceState) (*InstanceState, error) { + if len(rs.Deposed) > 0 { + deposed = true + } + + return nil, nil + }) + if err != nil { + return nil, err + } + + // If no deposed items, just return + if !deposed { + return nil, nil + } + + // Set the flag to true + if *n.Diff == nil { + *n.Diff = new(InstanceDiff) + } + (*n.Diff).DestroyDeposed = true + + return nil, nil +} diff --git a/terraform/node_resource_plan_instance.go b/terraform/node_resource_plan_instance.go index 9dafd22bad..b220e69e7d 100644 --- a/terraform/node_resource_plan_instance.go +++ b/terraform/node_resource_plan_instance.go @@ -177,6 +177,10 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource( OutputDiff: &diff, OutputState: &state, }, + &EvalDiffDeposed{ + Name: stateId, + Diff: &diff, + }, &EvalCheckPreventDestroy{ Resource: n.Config, Diff: &diff, diff --git a/terraform/test-fixtures/plan-cbd/main.tf b/terraform/test-fixtures/plan-cbd/main.tf new file mode 100644 index 0000000000..6edd78aef6 --- /dev/null +++ b/terraform/test-fixtures/plan-cbd/main.tf @@ -0,0 +1,3 @@ +resource "aws_instance" "foo" { + lifecycle { create_before_destroy = true } +}