From c96437e79a4c257b47c5caa06eafae8152fb1214 Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Mon, 17 Oct 2022 08:30:57 +0000 Subject: [PATCH 1/2] backport of commit d7cb69e96d6e9a8d82fa473d17c6a25dc1d697f3 --- internal/terraform/context_apply_test.go | 2 +- internal/terraform/eval_variable.go | 16 +++--- internal/terraform/eval_variable_test.go | 62 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/internal/terraform/context_apply_test.go b/internal/terraform/context_apply_test.go index 4f16453310ee..150141df5fe4 100644 --- a/internal/terraform/context_apply_test.go +++ b/internal/terraform/context_apply_test.go @@ -12027,7 +12027,7 @@ output "out" { Mode: plans.NormalMode, SetVariables: InputValues{ "in": &InputValue{ - Value: cty.MapVal(map[string]cty.Value{ + Value: cty.ObjectVal(map[string]cty.Value{ "required": cty.StringVal("boop"), }), SourceType: ValueFromCaller, diff --git a/internal/terraform/eval_variable.go b/internal/terraform/eval_variable.go index 167840e7c110..c355204b1046 100644 --- a/internal/terraform/eval_variable.go +++ b/internal/terraform/eval_variable.go @@ -90,14 +90,6 @@ func prepareFinalInputVariableValue(addr addrs.AbsInputVariableInstance, raw *In given = defaultVal // must be set, because we checked above that the variable isn't required } - // Apply defaults from the variable's type constraint to the given value, - // unless the given value is null. We do not apply defaults to top-level - // null values, as doing so could prevent assigning null to a nullable - // variable. - if cfg.TypeDefaults != nil && !given.IsNull() { - given = cfg.TypeDefaults.Apply(given) - } - val, err := convert.Convert(given, convertTy) if err != nil { log.Printf("[ERROR] prepareFinalInputVariableValue: %s has unsuitable type\n got: %s\n want: %s", addr, given.Type(), convertTy) @@ -140,6 +132,14 @@ func prepareFinalInputVariableValue(addr addrs.AbsInputVariableInstance, raw *In return cty.UnknownVal(cfg.Type), diags } + // Apply defaults from the variable's type constraint to the given value, + // unless the given value is null. We do not apply defaults to top-level + // null values, as doing so could prevent assigning null to a nullable + // variable. + if cfg.TypeDefaults != nil && !val.IsNull() { + val = cfg.TypeDefaults.Apply(val) + } + // By the time we get here, we know: // - val matches the variable's type constraint // - val is definitely not cty.NilVal, but might be a null value if the given was already null. diff --git a/internal/terraform/eval_variable_test.go b/internal/terraform/eval_variable_test.go index cb6c1bb2b816..7974203f2d07 100644 --- a/internal/terraform/eval_variable_test.go +++ b/internal/terraform/eval_variable_test.go @@ -68,6 +68,15 @@ func TestPrepareFinalInputVariableValue(t *testing.T) { nullable = false type = string } + variable "complex_type_with_nested_default_optional" { + type = set(object({ + name = string + schedules = set(object({ + name = string + cold_storage_after = optional(number, 10) + })) + })) + } ` cfg := testModuleInline(t, map[string]string{ "main.tf": cfgSrc, @@ -399,6 +408,59 @@ func TestPrepareFinalInputVariableValue(t *testing.T) { ``, }, + // complex types + + { + "complex_type_with_nested_default_optional", + cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("test1"), + "schedules": cty.SetVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "name": cty.StringVal("daily"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("test2"), + "schedules": cty.SetVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "name": cty.StringVal("daily"), + }), + cty.MapVal(map[string]cty.Value{ + "name": cty.StringVal("weekly"), + "cold_storage_after": cty.StringVal("0"), + }), + }), + }), + }), + cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("test1"), + "schedules": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("daily"), + "cold_storage_after": cty.NumberIntVal(10), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("test2"), + "schedules": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("daily"), + "cold_storage_after": cty.NumberIntVal(10), + }), + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("weekly"), + "cold_storage_after": cty.NumberIntVal(0), + }), + }), + }), + }), + ``, + }, + // sensitive { "constrained_string_sensitive_required", From 982ccbb16a8adf33ab613ae449a9d46e32b72e8f Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Wed, 2 Nov 2022 09:45:15 +0100 Subject: [PATCH 2/2] fix backport --- go.mod | 2 +- go.sum | 4 +-- internal/terraform/context_apply_test.go | 2 +- internal/terraform/eval_variable_test.go | 40 ++++++++++++++++++++++-- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9974dab733a6..08c414e1f55e 100644 --- a/go.mod +++ b/go.mod @@ -74,7 +74,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.11.1 + github.com/zclconf/go-cty v1.12.0 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 b64a94d27e3c..51f1d2a2ef69 100644 --- a/go.sum +++ b/go.sum @@ -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.11.1 h1:UMMYDL4riBFaPdzjEWcDdWG7x/Adz8E8f9OX/MGR7V4= -github.com/zclconf/go-cty v1.11.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= +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-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/context_apply_test.go b/internal/terraform/context_apply_test.go index 150141df5fe4..4f16453310ee 100644 --- a/internal/terraform/context_apply_test.go +++ b/internal/terraform/context_apply_test.go @@ -12027,7 +12027,7 @@ output "out" { Mode: plans.NormalMode, SetVariables: InputValues{ "in": &InputValue{ - Value: cty.ObjectVal(map[string]cty.Value{ + Value: cty.MapVal(map[string]cty.Value{ "required": cty.StringVal("boop"), }), SourceType: ValueFromCaller, diff --git a/internal/terraform/eval_variable_test.go b/internal/terraform/eval_variable_test.go index 7974203f2d07..15d250ceb193 100644 --- a/internal/terraform/eval_variable_test.go +++ b/internal/terraform/eval_variable_test.go @@ -70,13 +70,28 @@ func TestPrepareFinalInputVariableValue(t *testing.T) { } variable "complex_type_with_nested_default_optional" { type = set(object({ - name = string + name = string schedules = set(object({ - name = string + name = string cold_storage_after = optional(number, 10) })) })) } + variable "complex_type_with_nested_complex_types" { + type = object({ + name = string + nested_object = object({ + name = string + value = optional(string, "foo") + }) + nested_object_with_default = optional(object({ + name = string + value = optional(string, "bar") + }), { + name = "nested_object_with_default" + }) + }) + } ` cfg := testModuleInline(t, map[string]string{ "main.tf": cfgSrc, @@ -460,6 +475,27 @@ func TestPrepareFinalInputVariableValue(t *testing.T) { }), ``, }, + { + "complex_type_with_nested_complex_types", + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("object"), + "nested_object": cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("nested_object"), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("object"), + "nested_object": cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("nested_object"), + "value": cty.StringVal("foo"), + }), + "nested_object_with_default": cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("nested_object_with_default"), + "value": cty.StringVal("bar"), + }), + }), + ``, + }, // sensitive {