From 2feaebdca598370ce5646cfc6c3a2ffa02ad1dc3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 27 Feb 2015 21:51:14 -0800 Subject: [PATCH] config: substring containing computed value replaces element --- config/interpolate_walk.go | 4 +- config/interpolate_walk_test.go | 34 +++++- helper/schema/field_reader_config_test.go | 125 ++++++++++++++++++++-- helper/schema/schema_test.go | 36 +++++++ 4 files changed, 184 insertions(+), 15 deletions(-) diff --git a/config/interpolate_walk.go b/config/interpolate_walk.go index 17329e5a8..3cbb3e26f 100644 --- a/config/interpolate_walk.go +++ b/config/interpolate_walk.go @@ -152,12 +152,12 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error { if w.loc == reflectwalk.SliceElem { parts := strings.Split(replaceVal, InterpSplitDelim) for _, p := range parts { - if p == UnknownVariableValue { + if strings.Contains(p, UnknownVariableValue) { remove = true break } } - } else if replaceVal == UnknownVariableValue { + } else if strings.Contains(replaceVal, UnknownVariableValue) { remove = true } if remove { diff --git a/config/interpolate_walk_test.go b/config/interpolate_walk_test.go index 9b2c34133..ce0671e0a 100644 --- a/config/interpolate_walk_test.go +++ b/config/interpolate_walk_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/hashicorp/terraform/config/lang" "github.com/hashicorp/terraform/config/lang/ast" "github.com/mitchellh/reflectwalk" ) @@ -124,7 +125,7 @@ func TestInterpolationWalker_replace(t *testing.T) { "foo": "hello, ${var.foo}", }, Output: map[string]interface{}{ - "foo": "bar", + "foo": "hello, bar", }, Value: "bar", }, @@ -170,11 +171,38 @@ func TestInterpolationWalker_replace(t *testing.T) { Output: map[string]interface{}{}, Value: UnknownVariableValue + InterpSplitDelim + "baz", }, + + { + Input: map[string]interface{}{ + "foo": []interface{}{ + "${var.foo}/32", + "bing", + }, + }, + Output: map[string]interface{}{}, + Value: UnknownVariableValue, + }, } for i, tc := range cases { - fn := func(ast.Node) (string, error) { - return tc.Value, nil + config := &lang.EvalConfig{ + GlobalScope: &ast.BasicScope{ + VarMap: map[string]ast.Variable{ + "var.foo": ast.Variable{ + Value: tc.Value, + Type: ast.TypeString, + }, + }, + }, + } + + fn := func(root ast.Node) (string, error) { + value, _, err := lang.Eval(root, config) + if err != nil { + return "", err + } + + return value.(string), nil } w := &interpolationWalker{F: fn, Replace: true} diff --git a/helper/schema/field_reader_config_test.go b/helper/schema/field_reader_config_test.go index e0a88e737..33672eff2 100644 --- a/helper/schema/field_reader_config_test.go +++ b/helper/schema/field_reader_config_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/terraform" ) @@ -48,16 +50,6 @@ func TestConfigFieldReader(t *testing.T) { }) } -func testConfig( - t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig { - rc, err := config.NewRawConfig(raw) - if err != nil { - t.Fatalf("err: %s", err) - } - - return terraform.NewResourceConfig(rc) -} - func TestConfigFieldReader_DefaultHandling(t *testing.T) { schema := map[string]*Schema{ "strWithDefault": &Schema{ @@ -142,3 +134,116 @@ func TestConfigFieldReader_DefaultHandling(t *testing.T) { } } } + +func TestConfigFieldReader_ComputedSet(t *testing.T) { + schema := map[string]*Schema{ + "strSet": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "set, normal": { + []string{"strSet"}, + FieldReadResult{ + Value: map[int]interface{}{ + 2356372769: "foo", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "strSet": []interface{}{"foo"}, + }), + false, + }, + + "set, computed element": { + []string{"strSet"}, + FieldReadResult{ + Value: nil, + Exists: true, + Computed: true, + }, + testConfigInterpolate(t, map[string]interface{}{ + "strSet": []interface{}{"${var.foo}"}, + }, map[string]ast.Variable{ + "var.foo": ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeString, + }, + }), + false, + }, + + "set, computed element substring": { + []string{"strSet"}, + FieldReadResult{ + Value: nil, + Exists: true, + Computed: true, + }, + testConfigInterpolate(t, map[string]interface{}{ + "strSet": []interface{}{"${var.foo}/32"}, + }, map[string]ast.Variable{ + "var.foo": ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeString, + }, + }), + false, + }, + } + + for name, tc := range cases { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if (err != nil) != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to the raw map + out.Value = s.m + if len(s.m) == 0 { + out.Value = nil + } + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func testConfig( + t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig { + return testConfigInterpolate(t, raw, nil) +} + +func testConfigInterpolate( + t *testing.T, + raw map[string]interface{}, + vs map[string]ast.Variable) *terraform.ResourceConfig { + rc, err := config.NewRawConfig(raw) + if err != nil { + t.Fatalf("err: %s", err) + } + if len(vs) > 0 { + if err := rc.Interpolate(vs); err != nil { + t.Fatalf("err: %s", err) + } + } + + return terraform.NewResourceConfig(rc) +} diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 1e9616552..81f38aa76 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -2165,6 +2165,42 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, + + // #56 - Set element computed substring + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, "${var.foo}32"}, + }, + + ConfigVariables: map[string]string{ + "var.foo": config.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, } for i, tc := range cases {