From 14f7067b0c59ce18d2115e68abd00687a113344b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 22 Jul 2014 10:30:42 -0700 Subject: [PATCH] terraform: taint plan requires destroy/create --- terraform/context.go | 13 +++++++- terraform/context_test.go | 38 ++++++++++++++++++++++ terraform/graph.go | 10 ++++-- terraform/resource.go | 1 + terraform/terraform_test.go | 16 +++++++++ terraform/test-fixtures/plan-taint/main.tf | 7 ++++ 6 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 terraform/test-fixtures/plan-taint/main.tf diff --git a/terraform/context.go b/terraform/context.go index 25be6c047..4411dea56 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -681,7 +681,13 @@ func (c *Context) planWalkFn(result *Plan) depgraph.WalkFunc { // Get a diff from the newest state log.Printf("[DEBUG] %s: Executing diff", r.Id) var err error - diff, err = r.Provider.Diff(r.State, r.Config) + state := r.State + if r.Tainted { + // If we're tainted, we pretend to create a new thing. + state = new(ResourceState) + state.Type = r.State.Type + } + diff, err = r.Provider.Diff(state, r.Config) if err != nil { return err } @@ -691,6 +697,11 @@ func (c *Context) planWalkFn(result *Plan) depgraph.WalkFunc { diff = new(ResourceDiff) } + if r.Tainted { + // Tainted resources must also be destroyed + diff.Destroy = true + } + if diff.RequiresNew() && r.State.ID != "" { // This will also require a destroy diff.Destroy = true diff --git a/terraform/context_test.go b/terraform/context_test.go index d412dac06..9a79feed0 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -1457,6 +1457,44 @@ func TestContextPlan_state(t *testing.T) { } } +func TestContextPlan_taint(t *testing.T) { + c := testConfig(t, "plan-taint") + p := testProvider("aws") + p.DiffFn = testDiffFn + s := &State{ + Resources: map[string]*ResourceState{ + "aws_instance.foo": &ResourceState{ + ID: "bar", + Type: "aws_instance", + Attributes: map[string]string{"num": "2"}, + }, + "aws_instance.bar": &ResourceState{ + ID: "baz", + Type: "aws_instance", + }, + }, + Tainted: map[string]struct{}{"aws_instance.bar": struct{}{}}, + } + ctx := testContext(t, &ContextOpts{ + Config: c, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: s, + }) + + plan, err := ctx.Plan(nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(plan.String()) + expected := strings.TrimSpace(testTerraformPlanTaintStr) + if actual != expected { + t.Fatalf("bad:\n%s", actual) + } +} + func TestContextRefresh(t *testing.T) { p := testProvider("aws") c := testConfig(t, "refresh-basic") diff --git a/terraform/graph.go b/terraform/graph.go index 4b993d5a3..f0952b976 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -178,6 +178,9 @@ func graphAddConfigResources( index = i } + // Determine if this resource is tainted + _, tainted := s.Tainted[r.Id()] + var state *ResourceState if s != nil { state = s.Resources[name] @@ -209,9 +212,10 @@ func graphAddConfigResources( Type: r.Type, Config: r, Resource: &Resource{ - Id: name, - State: state, - Config: NewResourceConfig(r.RawConfig), + Id: name, + State: state, + Config: NewResourceConfig(r.RawConfig), + Tainted: tainted, }, }, } diff --git a/terraform/resource.go b/terraform/resource.go index d19949840..8eba49db4 100644 --- a/terraform/resource.go +++ b/terraform/resource.go @@ -31,6 +31,7 @@ type Resource struct { Provider ResourceProvider State *ResourceState Provisioners []*ResourceProvisionerConfig + Tainted bool } // Vars returns the mapping of variables that should be replaced in diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index 04885cb73..d9f9027fd 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -411,3 +411,19 @@ STATE: aws_instance.foo: ID = bar ` + +const testTerraformPlanTaintStr = ` +DIFF: + +DESTROY: aws_instance.bar + foo: "" => "2" + type: "" => "aws_instance" + +STATE: + +aws_instance.bar: (tainted) + ID = baz +aws_instance.foo: + ID = bar + num = 2 +` diff --git a/terraform/test-fixtures/plan-taint/main.tf b/terraform/test-fixtures/plan-taint/main.tf new file mode 100644 index 000000000..94ed55478 --- /dev/null +++ b/terraform/test-fixtures/plan-taint/main.tf @@ -0,0 +1,7 @@ +resource "aws_instance" "foo" { + num = "2" +} + +resource "aws_instance" "bar" { + foo = "${aws_instance.foo.num}" +}