From 16597d4a556afd313e221565f12de0ea52237e69 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 16 Sep 2016 07:26:39 +1200 Subject: [PATCH] Nested lists and maps fail in GetRaw When referencing a list of maps variable from within a resource, only the first list element is included the plan. This is because GetRaw can't access the interpolated values. Add some tests to document this behavior for both Get and GetRaw. --- terraform/resource_test.go | 122 +++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/terraform/resource_test.go b/terraform/resource_test.go index dde22b1ff..750e7060d 100644 --- a/terraform/resource_test.go +++ b/terraform/resource_test.go @@ -172,6 +172,42 @@ func TestResourceConfigGet(t *testing.T) { Value: nil, }, + // Reference list of maps variable. + // This does not work from GetRaw. + { + Vars: map[string]interface{}{ + "maplist": []interface{}{ + map[string]interface{}{ + "key": "a", + }, + map[string]interface{}{ + "key": "b", + }, + }, + }, + Config: map[string]interface{}{ + "maplist": "${var.maplist}", + }, + Key: "maplist.0", + Value: map[string]interface{}{"key": "a"}, + }, + + // Reference a map-of-lists variable. + // This does not work from GetRaw. + { + Vars: map[string]interface{}{ + "listmap": map[string]interface{}{ + "key1": []interface{}{"a", "b"}, + "key2": []interface{}{"c", "d"}, + }, + }, + Config: map[string]interface{}{ + "listmap": "${var.listmap}", + }, + Key: "listmap.key1", + Value: []interface{}{"a", "b"}, + }, + // FIXME: this is ambiguous, and matches the nested map // leaving here to catch this behaviour if it changes. { @@ -270,6 +306,92 @@ func TestResourceConfigGet(t *testing.T) { } } +func TestResourceConfigGetRaw(t *testing.T) { + cases := []struct { + Config map[string]interface{} + Vars map[string]interface{} + Key string + Value interface{} + }{ + // Referencing a list-of-maps variable doesn't work from GetRaw. + // The ConfigFieldReader currently catches this case and looks up the + // variable in the config. + { + Vars: map[string]interface{}{ + "maplist": []interface{}{ + map[string]interface{}{ + "key": "a", + }, + map[string]interface{}{ + "key": "b", + }, + }, + }, + Config: map[string]interface{}{ + "maplist": "${var.maplist}", + }, + Key: "maplist.0", + Value: nil, + }, + // Reference a map-of-lists variable. + // The ConfigFieldReader currently catches this case and looks up the + // variable in the config. + { + Vars: map[string]interface{}{ + "listmap": map[string]interface{}{ + "key1": []interface{}{"a", "b"}, + "key2": []interface{}{"c", "d"}, + }, + }, + Config: map[string]interface{}{ + "listmap": "${var.listmap}", + }, + Key: "listmap.key1", + Value: nil, + }, + } + + for i, tc := range cases { + var rawC *config.RawConfig + if tc.Config != nil { + var err error + rawC, err = config.NewRawConfig(tc.Config) + if err != nil { + t.Fatalf("err: %s", err) + } + } + + if tc.Vars != nil { + vs := make(map[string]ast.Variable) + for k, v := range tc.Vars { + hilVar, err := hil.InterfaceToVariable(v) + if err != nil { + t.Fatalf("%#v to var: %s", v, err) + } + vs["var."+k] = hilVar + } + if err := rawC.Interpolate(vs); err != nil { + t.Fatalf("err: %s", err) + } + } + + rc := NewResourceConfig(rawC) + rc.interpolateForce() + + // Test getting a key + t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) { + v, ok := rc.GetRaw(tc.Key) + if ok && v == nil { + t.Fatal("(nil, true) returned from GetRaw") + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("%d bad: %#v", i, v) + } + }) + } +} + func TestResourceConfigIsComputed(t *testing.T) { cases := []struct { Name string