From a4c3c1d389cdee790d50c434c4c14f648ea2efae Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Mon, 18 May 2020 14:10:19 -0400 Subject: [PATCH] vendor: upgrade go-cty dependency to 1.4.1 (#24983) * vendor: upgrade go-cty dependency to 1.4.1 This upgrade fixes a panic with inconsistent object element types. --- go.mod | 2 +- go.sum | 2 + terraform/testdata/input-var-default/main.tf | 7 - .../child/main.tf | 5 - .../input-var-partially-computed/main.tf | 7 - .../testdata/input-variables-invalid/main.tf | 30 ++++ .../input-variables-invalid/terraform.tfvars | 13 ++ terraform/testdata/input-variables/main.tf | 30 ++++ terraform/testdata/input-vars-unset/main.tf | 7 - terraform/testdata/input-vars/main.tf | 21 --- terraform/testdata/smc-uservars/main.tf | 15 -- terraform/variables_test.go | 147 +++++++++++++----- .../cty/convert/conversion_collection.go | 41 +++++ .../zclconf/go-cty/cty/function/stdlib/set.go | 17 +- .../github.com/zclconf/go-cty/cty/path_set.go | 6 + .../zclconf/go-cty/cty/set/rules.go | 4 + .../github.com/zclconf/go-cty/cty/set/set.go | 4 +- .../zclconf/go-cty/cty/set_internals.go | 11 ++ vendor/modules.txt | 2 +- 19 files changed, 268 insertions(+), 103 deletions(-) delete mode 100644 terraform/testdata/input-var-default/main.tf delete mode 100644 terraform/testdata/input-var-partially-computed/child/main.tf delete mode 100644 terraform/testdata/input-var-partially-computed/main.tf create mode 100644 terraform/testdata/input-variables-invalid/main.tf create mode 100644 terraform/testdata/input-variables-invalid/terraform.tfvars create mode 100644 terraform/testdata/input-variables/main.tf delete mode 100644 terraform/testdata/input-vars-unset/main.tf delete mode 100644 terraform/testdata/input-vars/main.tf delete mode 100644 terraform/testdata/smc-uservars/main.tf diff --git a/go.mod b/go.mod index afb2bb9dd..0cb411c40 100644 --- a/go.mod +++ b/go.mod @@ -121,7 +121,7 @@ require ( github.com/xanzy/ssh-agent v0.2.1 github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 - github.com/zclconf/go-cty v1.4.0 + github.com/zclconf/go-cty v1.4.1 github.com/zclconf/go-cty-yaml v1.0.1 go.uber.org/atomic v1.3.2 // indirect go.uber.org/multierr v1.1.0 // indirect diff --git a/go.sum b/go.sum index 7ef24462b..ab9ed0c07 100644 --- a/go.sum +++ b/go.sum @@ -443,6 +443,8 @@ github.com/zclconf/go-cty v1.2.0 h1:sPHsy7ADcIZQP3vILvTjrh74ZA175TFP5vqiNK1UmlI= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.4.0 h1:+q+tmgyUB94HIdH/uVTIi/+kt3pt4sHwEZAcTyLoGsQ= github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ= +github.com/zclconf/go-cty v1.4.1 h1:Xzr4m4utRDhHDifag1onwwUSq32HLoLBsp+w6tD0880= +github.com/zclconf/go-cty v1.4.1/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ= github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8= github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/terraform/testdata/input-var-default/main.tf b/terraform/testdata/input-var-default/main.tf deleted file mode 100644 index 59dc34e58..000000000 --- a/terraform/testdata/input-var-default/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "foo" { - default = 123 -} - -resource "aws_instance" "foo" { - foo = "${var.foo}" -} diff --git a/terraform/testdata/input-var-partially-computed/child/main.tf b/terraform/testdata/input-var-partially-computed/child/main.tf deleted file mode 100644 index a11cc5e83..000000000 --- a/terraform/testdata/input-var-partially-computed/child/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "in" {} - -resource "aws_instance" "mod" { - value = "${var.in}" -} diff --git a/terraform/testdata/input-var-partially-computed/main.tf b/terraform/testdata/input-var-partially-computed/main.tf deleted file mode 100644 index ada6f0cea..000000000 --- a/terraform/testdata/input-var-partially-computed/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "foo" { } -resource "aws_instance" "bar" { } - -module "child" { - source = "./child" - in = "one,${aws_instance.foo.id},${aws_instance.bar.id}" -} diff --git a/terraform/testdata/input-variables-invalid/main.tf b/terraform/testdata/input-variables-invalid/main.tf new file mode 100644 index 000000000..9d6d49aa3 --- /dev/null +++ b/terraform/testdata/input-variables-invalid/main.tf @@ -0,0 +1,30 @@ +# Required +variable "foo" { +} + +# Optional +variable "bar" { + default = "baz" +} + +# Mapping +variable "map" { + default = { + foo = "bar" + } +} + +# Complex Object Types +variable "object_map" { + type = map(object({ + foo = string, + bar = any + })) +} + +variable "object_list" { + type = list(object({ + foo = string, + bar = any + })) +} diff --git a/terraform/testdata/input-variables-invalid/terraform.tfvars b/terraform/testdata/input-variables-invalid/terraform.tfvars new file mode 100644 index 000000000..4ebc83913 --- /dev/null +++ b/terraform/testdata/input-variables-invalid/terraform.tfvars @@ -0,0 +1,13 @@ +test = [ + { + foo = "blah1" + bar = {} + }, + { + foo = "blah2" + bar = { + var1 = "val1", + var2 = "var2" + } + } +] diff --git a/terraform/testdata/input-variables/main.tf b/terraform/testdata/input-variables/main.tf new file mode 100644 index 000000000..9d6d49aa3 --- /dev/null +++ b/terraform/testdata/input-variables/main.tf @@ -0,0 +1,30 @@ +# Required +variable "foo" { +} + +# Optional +variable "bar" { + default = "baz" +} + +# Mapping +variable "map" { + default = { + foo = "bar" + } +} + +# Complex Object Types +variable "object_map" { + type = map(object({ + foo = string, + bar = any + })) +} + +variable "object_list" { + type = list(object({ + foo = string, + bar = any + })) +} diff --git a/terraform/testdata/input-vars-unset/main.tf b/terraform/testdata/input-vars-unset/main.tf deleted file mode 100644 index 28cf230e6..000000000 --- a/terraform/testdata/input-vars-unset/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "foo" {} -variable "bar" {} - -resource "aws_instance" "foo" { - foo = "${var.foo}" - bar = "${var.bar}" -} diff --git a/terraform/testdata/input-vars/main.tf b/terraform/testdata/input-vars/main.tf deleted file mode 100644 index 905f5d75c..000000000 --- a/terraform/testdata/input-vars/main.tf +++ /dev/null @@ -1,21 +0,0 @@ -variable "amis" { - default = { - us-west-2 = "bar" - } -} - -variable "bar" { - default = "baz" -} - -variable "foo" {} - -resource "aws_instance" "foo" { - num = "2" - bar = var.bar -} - -resource "aws_instance" "bar" { - foo = var.foo - bar = var.amis[var.foo] -} diff --git a/terraform/testdata/smc-uservars/main.tf b/terraform/testdata/smc-uservars/main.tf deleted file mode 100644 index 7240900be..000000000 --- a/terraform/testdata/smc-uservars/main.tf +++ /dev/null @@ -1,15 +0,0 @@ -# Required -variable "foo" { -} - -# Optional -variable "bar" { - default = "baz" -} - -# Mapping -variable "map" { - default = { - foo = "bar" - } -} diff --git a/terraform/variables_test.go b/terraform/variables_test.go index 0a3d5f402..3ac112ebe 100644 --- a/terraform/variables_test.go +++ b/terraform/variables_test.go @@ -163,45 +163,120 @@ func TestVariables(t *testing.T) { } } -func TestSMCUserVariables(t *testing.T) { - c := testModule(t, "smc-uservars") +func TestCheckInputVariables(t *testing.T) { + c := testModule(t, "input-variables") - // No variables set - diags := checkInputVariables(c.Module.Variables, nil) - if !diags.HasErrors() { - t.Fatal("check succeeded, but want errors") - } + t.Run("No variables set", func(t *testing.T) { + // No variables set + diags := checkInputVariables(c.Module.Variables, nil) + if !diags.HasErrors() { + t.Fatal("check succeeded, but want errors") + } - // Required variables set, optional variables unset - // This is still an error at this layer, since it's the caller's - // responsibility to have already merged in any default values. - diags = checkInputVariables(c.Module.Variables, InputValues{ - "foo": &InputValue{ - Value: cty.StringVal("bar"), - SourceType: ValueFromCLIArg, - }, + // Required variables set, optional variables unset + // This is still an error at this layer, since it's the caller's + // responsibility to have already merged in any default values. + diags = checkInputVariables(c.Module.Variables, InputValues{ + "foo": &InputValue{ + Value: cty.StringVal("bar"), + SourceType: ValueFromCLIArg, + }, + }) + if !diags.HasErrors() { + t.Fatal("check succeeded, but want errors") + } }) - if !diags.HasErrors() { - t.Fatal("check succeeded, but want errors") - } - // All variables set - diags = checkInputVariables(c.Module.Variables, InputValues{ - "foo": &InputValue{ - Value: cty.StringVal("bar"), - SourceType: ValueFromCLIArg, - }, - "bar": &InputValue{ - Value: cty.StringVal("baz"), - SourceType: ValueFromCLIArg, - }, - "map": &InputValue{ - Value: cty.StringVal("baz"), // okay because config has no type constraint - SourceType: ValueFromCLIArg, - }, + t.Run("All variables set", func(t *testing.T) { + diags := checkInputVariables(c.Module.Variables, InputValues{ + "foo": &InputValue{ + Value: cty.StringVal("bar"), + SourceType: ValueFromCLIArg, + }, + "bar": &InputValue{ + Value: cty.StringVal("baz"), + SourceType: ValueFromCLIArg, + }, + "map": &InputValue{ + Value: cty.StringVal("baz"), // okay because config has no type constraint + SourceType: ValueFromCLIArg, + }, + "object_map": &InputValue{ + Value: cty.MapVal(map[string]cty.Value{ + "uno": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + "bar": cty.NumberIntVal(2), // type = any + }), + "dos": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bat"), + "bar": cty.NumberIntVal(99), // type = any + }), + }), + SourceType: ValueFromCLIArg, + }, + "object_list": &InputValue{ + Value: cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + "bar": cty.NumberIntVal(2), // type = any + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bang"), + "bar": cty.NumberIntVal(42), // type = any + }), + }), + SourceType: ValueFromCLIArg, + }, + }) + if diags.HasErrors() { + t.Fatalf("unexpected errors: %s", diags.Err()) + } + }) + + t.Run("Invalid Complex Types", func(t *testing.T) { + diags := checkInputVariables(c.Module.Variables, InputValues{ + "foo": &InputValue{ + Value: cty.StringVal("bar"), + SourceType: ValueFromCLIArg, + }, + "bar": &InputValue{ + Value: cty.StringVal("baz"), + SourceType: ValueFromCLIArg, + }, + "map": &InputValue{ + Value: cty.StringVal("baz"), // okay because config has no type constraint + SourceType: ValueFromCLIArg, + }, + "object_map": &InputValue{ + Value: cty.MapVal(map[string]cty.Value{ + "uno": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + "bar": cty.NumberIntVal(2), // type = any + }), + "dos": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bat"), + "bar": cty.NumberIntVal(99), // type = any + }), + }), + SourceType: ValueFromCLIArg, + }, + "object_list": &InputValue{ + Value: cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + "bar": cty.NumberIntVal(2), // type = any + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bang"), + "bar": cty.StringVal("42"), // type = any, but mismatch with the first list item + }), + }), + SourceType: ValueFromCLIArg, + }, + }) + + if diags.HasErrors() { + t.Fatalf("unexpected errors: %s", diags.Err()) + } }) - if diags.HasErrors() { - //t.Fatal("check succeeded, but want errors") - t.Fatalf("unexpected errors: %s", diags.Err()) - } } diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go index ea23bf618..dc8dcb92c 100644 --- a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go +++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_collection.go @@ -265,6 +265,7 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co // element conversions in elemConvs return func(val cty.Value, path cty.Path) (cty.Value, error) { elems := make([]cty.Value, 0, len(elemConvs)) + elemTys := make([]cty.Type, 0, len(elems)) elemPath := append(path.Copy(), nil) i := int64(0) it := val.ElementIterator() @@ -284,10 +285,15 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co } } elems = append(elems, val) + elemTys = append(elemTys, val.Type()) i++ } + elems, err := conversionUnifyListElements(elems, elemPath, unsafe) + if err != nil { + return cty.NilVal, err + } return cty.ListVal(elems), nil } } @@ -441,6 +447,7 @@ func conversionUnifyCollectionElements(elems map[string]cty.Value, path cty.Path } unifiedType, _ := unify(elemTypes, unsafe) if unifiedType == cty.NilType { + return nil, path.NewErrorf("collection elements cannot be unified") } unifiedElems := make(map[string]cty.Value) @@ -486,3 +493,37 @@ func conversionCheckMapElementTypes(elems map[string]cty.Value, path cty.Path) e return nil } + +func conversionUnifyListElements(elems []cty.Value, path cty.Path, unsafe bool) ([]cty.Value, error) { + elemTypes := make([]cty.Type, len(elems)) + for i, elem := range elems { + elemTypes[i] = elem.Type() + } + unifiedType, _ := unify(elemTypes, unsafe) + if unifiedType == cty.NilType { + return nil, path.NewErrorf("collection elements cannot be unified") + } + + ret := make([]cty.Value, len(elems)) + elemPath := append(path.Copy(), nil) + + for i, elem := range elems { + if elem.Type().Equals(unifiedType) { + ret[i] = elem + continue + } + conv := getConversion(elem.Type(), unifiedType, unsafe) + if conv == nil { + } + elemPath[len(elemPath)-1] = cty.IndexStep{ + Key: cty.NumberIntVal(int64(i)), + } + val, err := conv(elem, elemPath) + if err != nil { + return nil, err + } + ret[i] = val + } + + return ret, nil +} diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go index 100078fdc..ce1a52ee2 100644 --- a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/set.go @@ -163,8 +163,23 @@ func SetSymmetricDifference(sets ...cty.Value) (cty.Value, error) { func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) { var etys []cty.Type for _, arg := range args { - etys = append(etys, arg.Type().ElementType()) + ty := arg.Type().ElementType() + + // Do not unify types for empty dynamic pseudo typed collections. These + // will always convert to any other concrete type. + if arg.LengthInt() == 0 && ty.Equals(cty.DynamicPseudoType) { + continue + } + + etys = append(etys, ty) } + + // If all element types were skipped (due to being empty dynamic collections), + // the return type should also be a set of dynamic pseudo type. + if len(etys) == 0 { + return cty.Set(cty.DynamicPseudoType), nil + } + newEty, _ := convert.UnifyUnsafe(etys) if newEty == cty.NilType { return cty.NilType, fmt.Errorf("given sets must all have compatible element types") diff --git a/vendor/github.com/zclconf/go-cty/cty/path_set.go b/vendor/github.com/zclconf/go-cty/cty/path_set.go index f1c892b9d..1960c01e9 100644 --- a/vendor/github.com/zclconf/go-cty/cty/path_set.go +++ b/vendor/github.com/zclconf/go-cty/cty/path_set.go @@ -196,3 +196,9 @@ func (r pathSetRules) Equivalent(a, b interface{}) bool { return true } + +// SameRules is true if both Rules instances are pathSetRules structs. +func (r pathSetRules) SameRules(other set.Rules) bool { + _, ok := other.(pathSetRules) + return ok +} diff --git a/vendor/github.com/zclconf/go-cty/cty/set/rules.go b/vendor/github.com/zclconf/go-cty/cty/set/rules.go index 51f744b5e..03ecd25b9 100644 --- a/vendor/github.com/zclconf/go-cty/cty/set/rules.go +++ b/vendor/github.com/zclconf/go-cty/cty/set/rules.go @@ -22,6 +22,10 @@ type Rules interface { // though it is *not* required that two values with the same hash value // be equivalent. Equivalent(interface{}, interface{}) bool + + // SameRules returns true if the instance is equivalent to another Rules + // instance. + SameRules(Rules) bool } // OrderedRules is an extension of Rules that can apply a partial order to diff --git a/vendor/github.com/zclconf/go-cty/cty/set/set.go b/vendor/github.com/zclconf/go-cty/cty/set/set.go index b4fb316f1..15a76638f 100644 --- a/vendor/github.com/zclconf/go-cty/cty/set/set.go +++ b/vendor/github.com/zclconf/go-cty/cty/set/set.go @@ -41,7 +41,7 @@ func NewSetFromSlice(rules Rules, vals []interface{}) Set { } func sameRules(s1 Set, s2 Set) bool { - return s1.rules == s2.rules + return s1.rules.SameRules(s2.rules) } func mustHaveSameRules(s1 Set, s2 Set) { @@ -53,7 +53,7 @@ func mustHaveSameRules(s1 Set, s2 Set) { // HasRules returns true if and only if the receiving set has the given rules // instance as its rules. func (s Set) HasRules(rules Rules) bool { - return s.rules == rules + return s.rules.SameRules(rules) } // Rules returns the receiving set's rules instance. diff --git a/vendor/github.com/zclconf/go-cty/cty/set_internals.go b/vendor/github.com/zclconf/go-cty/cty/set_internals.go index e7e1d3337..2b8af1e21 100644 --- a/vendor/github.com/zclconf/go-cty/cty/set_internals.go +++ b/vendor/github.com/zclconf/go-cty/cty/set_internals.go @@ -65,6 +65,17 @@ func (r setRules) Equivalent(v1 interface{}, v2 interface{}) bool { return eqv.v == true } +// SameRules is only true if the other Rules instance is also a setRules struct, +// and the types are considered equal. +func (r setRules) SameRules(other set.Rules) bool { + rules, ok := other.(setRules) + if !ok { + return false + } + + return r.Type.Equals(rules.Type) +} + // Less is an implementation of set.OrderedRules so that we can iterate over // set elements in a consistent order, where such an order is possible. func (r setRules) Less(v1, v2 interface{}) bool { diff --git a/vendor/modules.txt b/vendor/modules.txt index e3441005b..74e0ac417 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -582,7 +582,7 @@ github.com/xanzy/ssh-agent # github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 ## explicit github.com/xlab/treeprint -# github.com/zclconf/go-cty v1.4.0 +# github.com/zclconf/go-cty v1.4.1 ## explicit github.com/zclconf/go-cty/cty github.com/zclconf/go-cty/cty/convert