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.
This commit is contained in:
James Nugent 2016-07-27 17:14:47 -05:00
parent 4cdebd7a60
commit 7af10adcbe
3 changed files with 71 additions and 6 deletions

View File

@ -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))
}
}

View File

@ -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

View File

@ -0,0 +1,6 @@
variable "test" {
type = "map"
default = {
"test" = "1"
}
}