From 15f50b86bff364e798fda34ddc98b64b8d1ab037 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 10 Nov 2016 08:25:48 -0800 Subject: [PATCH 1/3] helper/schema: passing tests for computed complex sets --- helper/schema/field_reader_config_test.go | 135 ++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/helper/schema/field_reader_config_test.go b/helper/schema/field_reader_config_test.go index 01cc1d703..8ca0a2fbe 100644 --- a/helper/schema/field_reader_config_test.go +++ b/helper/schema/field_reader_config_test.go @@ -1,11 +1,14 @@ package schema import ( + "bytes" + "fmt" "reflect" "testing" "github.com/hashicorp/hil/ast" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/terraform" ) @@ -391,6 +394,138 @@ func TestConfigFieldReader_ComputedSet(t *testing.T) { } } +func TestConfigFieldReader_computedComplexSet(t *testing.T) { + hashfunc := func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string))) + return hashcode.String(buf.String()) + } + + schema := map[string]*Schema{ + "set": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + + "vhd_uri": { + Type: TypeString, + Required: true, + }, + }, + }, + Set: hashfunc, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "set, normal": { + []string{"set"}, + FieldReadResult{ + Value: map[string]interface{}{ + "532860136": map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "bar", + }, + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "set": []interface{}{ + map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "bar", + }, + }, + }), + false, + }, + + "set, computed element": { + []string{"set"}, + FieldReadResult{ + Value: map[string]interface{}{ + "~3596295623": map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "${var.foo}/bar", + }, + }, + Exists: true, + Computed: false, + }, + testConfigInterpolate(t, map[string]interface{}{ + "set": []interface{}{ + map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "${var.foo}/bar", + }, + }, + }, map[string]ast.Variable{ + "var.foo": ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeUnknown, + }, + }), + false, + }, + + "set, computed element single": { + []string{"set", "~3596295623", "vhd_uri"}, + FieldReadResult{ + Value: "${var.foo}/bar", + Exists: true, + Computed: true, + }, + testConfigInterpolate(t, map[string]interface{}{ + "set": []interface{}{ + map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "${var.foo}/bar", + }, + }, + }, map[string]ast.Variable{ + "var.foo": ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeUnknown, + }, + }), + 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) From efd27e9e4e0a73a78ee66ccace8eee2d907b8746 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 10 Nov 2016 17:14:12 -0800 Subject: [PATCH 2/3] update HIL --- vendor/github.com/hashicorp/hil/convert.go | 2 +- vendor/vendor.json | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/vendor/github.com/hashicorp/hil/convert.go b/vendor/github.com/hashicorp/hil/convert.go index 983b66baf..f2024d01c 100644 --- a/vendor/github.com/hashicorp/hil/convert.go +++ b/vendor/github.com/hashicorp/hil/convert.go @@ -55,7 +55,7 @@ func InterfaceToVariable(input interface{}) (ast.Variable, error) { if err := hilMapstructureWeakDecode(input, &stringVal); err == nil { // Special case the unknown value to turn into "unknown" if stringVal == UnknownValue { - return ast.Variable{Type: ast.TypeUnknown}, nil + return ast.Variable{Value: UnknownValue, Type: ast.TypeUnknown}, nil } // Otherwise return the string value diff --git a/vendor/vendor.json b/vendor/vendor.json index 3d4d0f28a..3758fa418 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1412,10 +1412,10 @@ "revisionTime": "2016-11-09T22:51:35Z" }, { - "checksumSHA1": "mZhRldYjh9MAXzdi3zihMX0A/JU=", + "checksumSHA1": "PjLBj8sicHOz2ZzuaMTPZ09OuFs=", "path": "github.com/hashicorp/hil", - "revision": "ce4ab742a9dd2bb6e55050337333b2c56666e5a0", - "revisionTime": "2016-10-27T15:25:34Z" + "revision": "a69e0a85dd050184c00f6080fce138f2dadb1a4c", + "revisionTime": "2016-11-11T01:09:07Z" }, { "checksumSHA1": "FFroNUb6Nn6xUQJMsVDTb4Cqzo4=", @@ -1880,7 +1880,6 @@ }, { "checksumSHA1": "DVXnx4zyb0wkeIdIRjwjvR7Dslo=", - "origin": "github.com/hashicorp/terraform/vendor/github.com/mitchellh/go-ps", "path": "github.com/mitchellh/go-ps", "revision": "e2d21980687ce16e58469d98dcee92d27fbbd7fb", "revisionTime": "2016-08-22T16:54:47Z" From 5643a7c28b82947ca08393c7a3b591ed84f530f9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 10 Nov 2016 17:14:20 -0800 Subject: [PATCH 3/3] terraform: when returning a raw attribute value, use hil conversion Because we now rely on HIL to do the computed calculation, we must make sure the type is correct (TypeUnknown). Before, we'd just check for the UUID in the string. This changes all variable returns in the interpolater to run it through `hil.InterfaceToVariable` which handles this lookup for us. --- terraform/interpolate.go | 3 ++- terraform/interpolate_test.go | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/terraform/interpolate.go b/terraform/interpolate.go index 2bc74994f..3fb0b66a7 100644 --- a/terraform/interpolate.go +++ b/terraform/interpolate.go @@ -404,7 +404,8 @@ func (i *Interpolater) computeResourceVariable( } if attr, ok := r.Primary.Attributes[v.Field]; ok { - return &ast.Variable{Type: ast.TypeString, Value: attr}, nil + v, err := hil.InterfaceToVariable(attr) + return &v, err } // computed list or map attribute diff --git a/terraform/interpolate_test.go b/terraform/interpolate_test.go index 9b0925908..4eaf882bc 100644 --- a/terraform/interpolate_test.go +++ b/terraform/interpolate_test.go @@ -569,6 +569,44 @@ func TestInterpolator_resourceMultiAttributesComputed(t *testing.T) { }) } +func TestInterpolator_resourceAttributeComputed(t *testing.T) { + lock := new(sync.RWMutex) + // The state would never be written with an UnknownVariableValue in it, but + // it can/does exist that way in memory during the plan phase. + state := &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_route53_zone.yada": &ResourceState{ + Type: "aws_route53_zone", + Primary: &InstanceState{ + ID: "z-abc123", + Attributes: map[string]string{ + "id": config.UnknownVariableValue, + }, + }, + }, + }, + }, + }, + } + i := &Interpolater{ + Module: testModule(t, "interpolate-multi-vars"), + StateLock: lock, + State: state, + } + + scope := &InterpolationScope{ + Path: rootModulePath, + } + + testInterpolate(t, i, scope, "aws_route53_zone.yada.id", ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeUnknown, + }) +} + func TestInterpolater_selfVarWithoutResource(t *testing.T) { i := &Interpolater{}