From 9cc5e81a6c0a0f074472234ff8ea57a031dd80db Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 8 Nov 2016 18:01:06 -0800 Subject: [PATCH] terraform: add tests for IsComputed that pass on master --- terraform/interpolate.go | 4 ++ terraform/resource.go | 15 +++++ terraform/resource_test.go | 122 +++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) diff --git a/terraform/interpolate.go b/terraform/interpolate.go index 77d200e9ce..2bc74994fe 100644 --- a/terraform/interpolate.go +++ b/terraform/interpolate.go @@ -126,6 +126,10 @@ func unknownVariable() ast.Variable { } } +func unknownValue() string { + return hil.UnknownValue +} + func (i *Interpolater) valueModuleVar( scope *InterpolationScope, n string, diff --git a/terraform/resource.go b/terraform/resource.go index bbc6fa9b87..a0863dbaa8 100644 --- a/terraform/resource.go +++ b/terraform/resource.go @@ -276,6 +276,15 @@ func (c *ResourceConfig) get( current = v.Interface() case reflect.Slice: previous = current + + // If any value in a list is computed, this whole thing + // is computed and we can't read any part of it. + for i := 0; i < cv.Len(); i++ { + if v := cv.Index(i).Interface(); v == unknownValue() { + return v, false + } + } + if part == "#" { current = cv.Len() } else { @@ -289,6 +298,12 @@ func (c *ResourceConfig) get( current = cv.Index(int(i)).Interface() } case reflect.String: + // If the value is just the unknown value, then we don't + // know anything beyond here. + if current == unknownValue() { + return current, false + } + // This happens when map keys contain "." and have a common // prefix so were split as path components above. actualKey := strings.Join(parts[i-1:], ".") diff --git a/terraform/resource_test.go b/terraform/resource_test.go index e226ee9c3f..593ba02d78 100644 --- a/terraform/resource_test.go +++ b/terraform/resource_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/hashicorp/hil" "github.com/hashicorp/hil/ast" "github.com/hashicorp/terraform/config" ) @@ -239,6 +240,127 @@ func TestResourceConfigGet(t *testing.T) { } } +func TestResourceConfigIsComputed(t *testing.T) { + cases := []struct { + Name string + Config map[string]interface{} + Vars map[string]interface{} + Key string + Result bool + }{ + { + Name: "basic value", + Config: map[string]interface{}{ + "foo": "${var.foo}", + }, + Vars: map[string]interface{}{ + "foo": unknownValue(), + }, + Key: "foo", + Result: true, + }, + + { + Name: "set with a computed element", + Config: map[string]interface{}{ + "foo": "${var.foo}", + }, + Vars: map[string]interface{}{ + "foo": []string{ + "a", + unknownValue(), + }, + }, + Key: "foo", + Result: true, + }, + + { + Name: "set with no computed elements", + Config: map[string]interface{}{ + "foo": "${var.foo}", + }, + Vars: map[string]interface{}{ + "foo": []string{ + "a", + "b", + }, + }, + Key: "foo", + Result: false, + }, + + { + Name: "set count with computed elements", + Config: map[string]interface{}{ + "foo": "${var.foo}", + }, + Vars: map[string]interface{}{ + "foo": []string{ + "a", + unknownValue(), + }, + }, + Key: "foo.#", + Result: true, + }, + + { + Name: "set count with computed elements", + Config: map[string]interface{}{ + "foo": []interface{}{"${var.foo}"}, + }, + Vars: map[string]interface{}{ + "foo": []string{ + "a", + unknownValue(), + }, + }, + Key: "foo.#", + Result: true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + 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() + + t.Logf("Config: %#v", rc) + + actual := rc.IsComputed(tc.Key) + if actual != tc.Result { + t.Fatalf("bad: %#v", actual) + } + }) + } +} + func TestResourceConfigDeepCopy_nil(t *testing.T) { var nilRc *ResourceConfig actual := nilRc.DeepCopy()