From 4eed21f04c44672d3fd9a41933c82833a2e47315 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 24 Feb 2016 10:55:55 -0500 Subject: [PATCH 1/4] terraform: issue 5254 test case (not yet working) --- terraform/context_apply_test.go | 71 +++++++++++++++++++ terraform/context_test.go | 8 +++ .../test-fixtures/issue-5254/step-0/main.tf | 10 +++ .../test-fixtures/issue-5254/step-1/main.tf | 11 +++ 4 files changed, 100 insertions(+) create mode 100644 terraform/test-fixtures/issue-5254/step-0/main.tf create mode 100644 terraform/test-fixtures/issue-5254/step-1/main.tf diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 91bf663b6..74a30815c 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -3929,3 +3929,74 @@ func TestContext2Apply_singleDestroy(t *testing.T) { t.Fatalf("bad: %d", invokeCount) } } + +// GH-5254 +func TestContext2Apply_issue5254(t *testing.T) { + // Create a provider. We use "template" here just to match the repro + // we got from the issue itself. + p := testProvider("template") + p.ResourcesReturn = append(p.ResourcesReturn, ResourceType{ + Name: "template_file", + }) + + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + + // Apply cleanly step 0 + t.Log("Applying Step 0") + ctx := testContext2(t, &ContextOpts{ + Module: testModule(t, "issue-5254/step-0"), + Providers: map[string]ResourceProviderFactory{ + "template": testProviderFuncFixed(p), + }, + }) + + plan, err := ctx.Plan() + if err != nil { + t.Fatalf("err: %s", err) + } + t.Logf("Plan for Step 0: %s", plan) + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + // Application success. Now make the modification and store a plan + t.Log("Planning Step 1") + ctx = testContext2(t, &ContextOpts{ + Module: testModule(t, "issue-5254/step-1"), + State: state, + Providers: map[string]ResourceProviderFactory{ + "template": testProviderFuncFixed(p), + }, + }) + + plan, err = ctx.Plan() + if err != nil { + t.Fatalf("err: %s", err) + } + + t.Logf("Plan for Step 1: %s", plan) + + // Apply the plan + t.Log("Applying Step 1 (from plan)") + ctx = plan.Context(&ContextOpts{ + Providers: map[string]ResourceProviderFactory{ + "template": testProviderFuncFixed(p), + }, + }) + + state, err = ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + /* + actual := strings.TrimSpace(state.String()) + expected := strings.TrimSpace(testTerraformApplyProviderAliasStr) + if actual != expected { + t.Fatalf("bad: \n%s", actual) + } + */ +} diff --git a/terraform/context_test.go b/terraform/context_test.go index 65f840505..015ae1921 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -52,6 +52,11 @@ func testDiffFn( continue } + // Ignore __-prefixed keys since they're used for magic + if k[0] == '_' && k[1] == '_' { + continue + } + if k == "nil" { return nil, nil } @@ -100,6 +105,9 @@ func testDiffFn( if k == "require_new" { attrDiff.RequiresNew = true } + if _, ok := c.Raw["__"+k+"_requires_new"]; ok { + attrDiff.RequiresNew = true + } diff.Attributes[k] = attrDiff } diff --git a/terraform/test-fixtures/issue-5254/step-0/main.tf b/terraform/test-fixtures/issue-5254/step-0/main.tf new file mode 100644 index 000000000..14b39194e --- /dev/null +++ b/terraform/test-fixtures/issue-5254/step-0/main.tf @@ -0,0 +1,10 @@ +variable "c" { default = 1 } + +resource "template_file" "parent" { + count = "${var.c}" + template = "Hi" +} + +resource "template_file" "child" { + template = "${join(",", template_file.parent.*.template)} ok" +} diff --git a/terraform/test-fixtures/issue-5254/step-1/main.tf b/terraform/test-fixtures/issue-5254/step-1/main.tf new file mode 100644 index 000000000..e1d3bbac9 --- /dev/null +++ b/terraform/test-fixtures/issue-5254/step-1/main.tf @@ -0,0 +1,11 @@ +variable "c" { default = 1 } + +resource "template_file" "parent" { + count = "${var.c}" + template = "Hi" +} + +resource "template_file" "child" { + template = "${join(",", template_file.parent.*.template)}" + __template_requires_new = 1 +} From b7554ced1d11187a75bf23d32e08bc9f88e31a17 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 24 Feb 2016 10:04:57 -0600 Subject: [PATCH 2/4] terraform: repro for issue 5254 test --- terraform/context_apply_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 74a30815c..da1e63ff1 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -1,6 +1,7 @@ package terraform import ( + "bytes" "fmt" "os" "reflect" @@ -3977,11 +3978,22 @@ func TestContext2Apply_issue5254(t *testing.T) { t.Fatalf("err: %s", err) } - t.Logf("Plan for Step 1: %s", plan) + // Write / Read plan to simulate running it through a Plan file + var buf bytes.Buffer + if err := WritePlan(plan, &buf); err != nil { + t.Fatalf("err: %s", err) + } + + planFromFile, err := ReadPlan(&buf) + if err != nil { + t.Fatalf("err: %s", err) + } + + t.Logf("Plan for Step 1: %s", planFromFile) // Apply the plan t.Log("Applying Step 1 (from plan)") - ctx = plan.Context(&ContextOpts{ + ctx = planFromFile.Context(&ContextOpts{ Providers: map[string]ResourceProviderFactory{ "template": testProviderFuncFixed(p), }, From 54411301c410cc3c3789667b1d039bc732860ca9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 24 Feb 2016 11:57:27 -0500 Subject: [PATCH 3/4] terraform: println so we show up in logs --- terraform/context_apply_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index da1e63ff1..7c2769d29 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -3964,6 +3964,7 @@ func TestContext2Apply_issue5254(t *testing.T) { } // Application success. Now make the modification and store a plan + println("Planning Step 1") t.Log("Planning Step 1") ctx = testContext2(t, &ContextOpts{ Module: testModule(t, "issue-5254/step-1"), @@ -3992,6 +3993,7 @@ func TestContext2Apply_issue5254(t *testing.T) { t.Logf("Plan for Step 1: %s", planFromFile) // Apply the plan + println("Applying Step 1 (from Plan)") t.Log("Applying Step 1 (from plan)") ctx = planFromFile.Context(&ContextOpts{ Providers: map[string]ResourceProviderFactory{ From 12b6776675f7376792899f0e4a63990b39c1ba64 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 24 Feb 2016 12:04:42 -0500 Subject: [PATCH 4/4] terraform: don't prune resource if count contains interpolations --- terraform/graph_config_node_resource.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/terraform/graph_config_node_resource.go b/terraform/graph_config_node_resource.go index e010991ba..8abcd9156 100644 --- a/terraform/graph_config_node_resource.go +++ b/terraform/graph_config_node_resource.go @@ -283,6 +283,13 @@ func (n *GraphNodeConfigResource) Noop(opts *NoopOpts) bool { return false } + // If the count has any interpolations, we can't prune this node since + // we need to be sure to evaluate the count so that splat variables work + // later (which need to know the full count). + if len(n.Resource.RawCount.Interpolations) > 0 { + return false + } + // If we have no module diff, we're certainly a noop. This is because // it means there is a diff, and that the module we're in just isn't // in it, meaning we're not doing anything.