diff --git a/builtin/providers/test/resource_test.go b/builtin/providers/test/resource_test.go index 198101363..ee46f0b51 100644 --- a/builtin/providers/test/resource_test.go +++ b/builtin/providers/test/resource_test.go @@ -393,6 +393,40 @@ resource "test_resource" "foo" { }) } +// Reproduces plan-time panic when the wrong type is interpolated in a list of +// maps. +// TODO: this should return a type error, rather than silently setting an empty +// list +func TestResource_dataSourceListMapPanic(t *testing.T) { + resource.UnitTest(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckResourceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: strings.TrimSpace(` +resource "test_resource" "foo" { + required = "val" + required_map = {x = "y"} + list_of_map = "${var.maplist}" +} + +variable "maplist" { + type = "list" + + default = [ + {a = "b"} + ] +} + `), + ExpectError: nil, + Check: func(s *terraform.State) error { + return nil + }, + }, + }, + }) +} + func testAccCheckResourceDestroy(s *terraform.State) error { return nil } diff --git a/terraform/resource.go b/terraform/resource.go index 9a8305536..0acf0beb2 100644 --- a/terraform/resource.go +++ b/terraform/resource.go @@ -316,7 +316,8 @@ func (c *ResourceConfig) get( // prefix so were split as path components above. actualKey := strings.Join(parts[i-1:], ".") if prevMap, ok := previous.(map[string]interface{}); ok { - return prevMap[actualKey], true + v, ok := prevMap[actualKey] + return v, ok } return nil, false diff --git a/terraform/resource_test.go b/terraform/resource_test.go index 7e116b550..dde22b1ff 100644 --- a/terraform/resource_test.go +++ b/terraform/resource_test.go @@ -160,6 +160,18 @@ func TestResourceConfigGet(t *testing.T) { Key: "mapname.0.listkey.0.key", Value: 3, }, + + // A map assigned to a list via interpolation should Get a non-existent + // value. The test code now also checks that Get doesn't return (nil, + // true), which it previously did for this configuration. + { + Config: map[string]interface{}{ + "maplist": "${var.maplist}", + }, + Key: "maplist.0", + Value: nil, + }, + // FIXME: this is ambiguous, and matches the nested map // leaving here to catch this behaviour if it changes. { @@ -226,7 +238,11 @@ func TestResourceConfigGet(t *testing.T) { // Test getting a key t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) { - v, _ := rc.Get(tc.Key) + v, ok := rc.Get(tc.Key) + if ok && v == nil { + t.Fatal("(nil, true) returned from Get") + } + if !reflect.DeepEqual(v, tc.Value) { t.Fatalf("%d bad: %#v", i, v) }