From 7af10adcbe484f176ea479c86691e7a2801b891e Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 27 Jul 2016 17:14:47 -0500 Subject: [PATCH] core: Do not assume HCL parser has touched vars This PR fixes #7824, which crashed when applying a plan file. The bug is that while a map which has come from the HCL parser reifies as a []map[string]interface{}, the variable saved in the plan file was not. We now cover both cases. Fixes #7824. --- terraform/context.go | 22 +++++++--- terraform/context_apply_test.go | 49 ++++++++++++++++++++++ terraform/test-fixtures/issue-7824/main.tf | 6 +++ 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 terraform/test-fixtures/issue-7824/main.tf diff --git a/terraform/context.go b/terraform/context.go index 86d7e58ce..f43e48dfe 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -181,11 +181,16 @@ func NewContext(opts *ContextOpts) (*Context, error) { if existingMap, ok := existing.(map[string]interface{}); !ok { panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) } else { - if newMap, ok := varVal.(map[string]interface{}); ok { - for newKey, newVal := range newMap { + switch typedV := varVal.(type) { + case []map[string]interface{}: + for newKey, newVal := range typedV[0] { existingMap[newKey] = newVal } - } else { + case map[string]interface{}: + for newKey, newVal := range typedV { + existingMap[newKey] = newVal + } + default: panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) } } @@ -208,11 +213,16 @@ func NewContext(opts *ContextOpts) (*Context, error) { if existingMap, ok := existing.(map[string]interface{}); !ok { panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) } else { - if newMap, ok := v.([]map[string]interface{}); ok { - for newKey, newVal := range newMap[0] { + switch typedV := v.(type) { + case []map[string]interface{}: + for newKey, newVal := range typedV[0] { existingMap[newKey] = newVal } - } else { + case map[string]interface{}: + for newKey, newVal := range typedV { + existingMap[newKey] = newVal + } + default: panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) } } diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index ce64ddfca..6c2a493c5 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -4523,6 +4523,55 @@ func TestContext2Apply_singleDestroy(t *testing.T) { } } +// GH-7824 +func TestContext2Apply_issue7824(t *testing.T) { + p := testProvider("template") + p.ResourcesReturn = append(p.ResourcesReturn, ResourceType{ + Name: "template_file", + }) + + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + + // Apply cleanly step 0 + ctx := testContext2(t, &ContextOpts{ + Module: testModule(t, "issue-7824"), + Providers: map[string]ResourceProviderFactory{ + "template": testProviderFuncFixed(p), + }, + }) + + plan, err := ctx.Plan() + if err != nil { + t.Fatalf("err: %s", err) + } + + // 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) + } + + ctx, err = planFromFile.Context(&ContextOpts{ + Providers: map[string]ResourceProviderFactory{ + "template": testProviderFuncFixed(p), + }, + }) + if err != nil { + t.Fatalf("err: %s", err) + } + + _, err = ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } +} + // GH-5254 func TestContext2Apply_issue5254(t *testing.T) { // Create a provider. We use "template" here just to match the repro diff --git a/terraform/test-fixtures/issue-7824/main.tf b/terraform/test-fixtures/issue-7824/main.tf new file mode 100644 index 000000000..835254b65 --- /dev/null +++ b/terraform/test-fixtures/issue-7824/main.tf @@ -0,0 +1,6 @@ +variable "test" { + type = "map" + default = { + "test" = "1" + } +} \ No newline at end of file