diff --git a/go.mod b/go.mod index 26bec9a4db62..0dd496cdb969 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f - github.com/hashicorp/hcl/v2 v2.14.1 + github.com/hashicorp/hcl/v2 v2.15.0 github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 @@ -75,7 +75,7 @@ require ( github.com/tombuildsstuff/giovanni v0.15.1 github.com/xanzy/ssh-agent v0.3.1 github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 - github.com/zclconf/go-cty v1.12.0 + github.com/zclconf/go-cty v1.12.1 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b github.com/zclconf/go-cty-yaml v1.0.2 golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 diff --git a/go.sum b/go.sum index 611b8429fbac..6dde32328e44 100644 --- a/go.sum +++ b/go.sum @@ -387,8 +387,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws= github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= -github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= -github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hashicorp/hcl/v2 v2.15.0 h1:CPDXO6+uORPjKflkWCCwoWc9uRp+zSIPcCQ+BrxV7m8= +github.com/hashicorp/hcl/v2 v2.15.0/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d h1:9ARUJJ1VVynB176G1HCwleORqCaXm/Vx0uUi0dL26I0= github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -619,8 +619,8 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.12.0 h1:F5E/vbilcrCtat9sYcEjlwwg1mDqbRTjyXR57nnx5sc= -github.com/zclconf/go-cty v1.12.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= +github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= +github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0= diff --git a/internal/terraform/eval_variable_test.go b/internal/terraform/eval_variable_test.go index 15d250ceb193..702c7a8a31b0 100644 --- a/internal/terraform/eval_variable_test.go +++ b/internal/terraform/eval_variable_test.go @@ -92,6 +92,94 @@ func TestPrepareFinalInputVariableValue(t *testing.T) { }) }) } + // https://github.com/hashicorp/terraform/issues/32152 + // This variable was originally added to test that optional attribute + // metadata is stripped from empty default collections. Essentially, you + // should be able to mix and match custom and default values for the + // optional_list attribute. + variable "complex_type_with_empty_default_and_nested_optional" { + type = list(object({ + name = string + optional_list = optional(list(object({ + string = string + optional_string = optional(string) + })), []) + })) + } + // https://github.com/hashicorp/terraform/issues/32160#issuecomment-1302783910 + // These variables were added to test the specific use case from this + // GitHub comment. + variable "empty_object_with_optional_nested_object_with_optional_bool" { + type = object({ + thing = optional(object({ + flag = optional(bool, false) + })) + }) + default = {} + } + variable "populated_object_with_optional_nested_object_with_optional_bool" { + type = object({ + thing = optional(object({ + flag = optional(bool, false) + })) + }) + default = { + thing = {} + } + } + variable "empty_object_with_default_nested_object_with_optional_bool" { + type = object({ + thing = optional(object({ + flag = optional(bool, false) + }), {}) + }) + default = {} + } + // https://github.com/hashicorp/terraform/issues/32160 + // This variable was originally added to test that optional objects do + // get created containing only their defaults. Instead they should be + // left empty. We do not expect nested_object to be created just because + // optional_string has a default value. + variable "object_with_nested_object_with_required_and_optional_attributes" { + type = object({ + nested_object = optional(object({ + string = string + optional_string = optional(string, "optional") + })) + }) + } + // https://github.com/hashicorp/terraform/issues/32157 + // Similar to above, we want to see that merging combinations of the + // nested_object into a single collection doesn't crash because of + // inconsistent elements. + variable "list_with_nested_object_with_required_and_optional_attributes" { + type = list(object({ + nested_object = optional(object({ + string = string + optional_string = optional(string, "optional") + })) + })) + } + // https://github.com/hashicorp/terraform/issues/32109 + // This variable was originally introduced to test the behaviour of + // the dynamic type constraint. You should be able to use the 'any' + // constraint and introduce empty, null, and populated values into the + // list. + variable "list_with_nested_list_of_any" { + type = list(object({ + a = string + b = optional(list(any)) + })) + default = [ + { + a = "a" + }, + { + a = "b" + b = [1] + } + ] + } ` cfg := testModuleInline(t, map[string]string{ "main.tf": cfgSrc, @@ -496,6 +584,136 @@ func TestPrepareFinalInputVariableValue(t *testing.T) { }), ``, }, + { + "complex_type_with_empty_default_and_nested_optional", + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("abc"), + "optional_list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal("child"), + "optional_string": cty.NullVal(cty.String), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("def"), + "optional_list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "string": cty.String, + "optional_string": cty.String, + }))), + }), + }), + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("abc"), + "optional_list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal("child"), + "optional_string": cty.NullVal(cty.String), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("def"), + "optional_list": cty.ListValEmpty(cty.Object(map[string]cty.Type{ + "string": cty.String, + "optional_string": cty.String, + })), + }), + }), + ``, + }, + { + "object_with_nested_object_with_required_and_optional_attributes", + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "nested_object": cty.NullVal(cty.Object(map[string]cty.Type{ + "string": cty.String, + "optional_string": cty.String, + })), + }), + ``, + }, + { + "empty_object_with_optional_nested_object_with_optional_bool", + cty.NilVal, + cty.ObjectVal(map[string]cty.Value{ + "thing": cty.NullVal(cty.Object(map[string]cty.Type{ + "flag": cty.Bool, + })), + }), + ``, + }, + { + "populated_object_with_optional_nested_object_with_optional_bool", + cty.NilVal, + cty.ObjectVal(map[string]cty.Value{ + "thing": cty.ObjectVal(map[string]cty.Value{ + "flag": cty.False, + }), + }), + ``, + }, + { + "empty_object_with_default_nested_object_with_optional_bool", + cty.NilVal, + cty.ObjectVal(map[string]cty.Value{ + "thing": cty.ObjectVal(map[string]cty.Value{ + "flag": cty.False, + }), + }), + ``, + }, + { + "list_with_nested_object_with_required_and_optional_attributes", + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "nested_object": cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal("string"), + "optional_string": cty.NullVal(cty.String), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "nested_object": cty.NullVal(cty.Object(map[string]cty.Type{ + "string": cty.String, + "optional_string": cty.String, + })), + }), + }), + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "nested_object": cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal("string"), + "optional_string": cty.StringVal("optional"), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "nested_object": cty.NullVal(cty.Object(map[string]cty.Type{ + "string": cty.String, + "optional_string": cty.String, + })), + }), + }), + ``, + }, + { + "list_with_nested_list_of_any", + cty.NilVal, + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + "b": cty.NullVal(cty.List(cty.Number)), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("b"), + "b": cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + }), + }), + }), + ``, + }, // sensitive {