Skip to content

Commit

Permalink
Convert variable types before applying defaults (#32027)
Browse files Browse the repository at this point in the history
* Convert variable types before applying defaults

* revert change to unrelated test

* Add another test case to verify behaviour

* update go-cty

* Update internal/terraform/eval_variable.go

Co-authored-by: alisdair <alisdair@users.noreply.github.com>

Co-authored-by: alisdair <alisdair@users.noreply.github.com>
  • Loading branch information
liamcervante and alisdair committed Nov 2, 2022
1 parent 6663cde commit 6521355
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 11 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -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.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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -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=
Expand Down
16 changes: 8 additions & 8 deletions internal/terraform/eval_variable.go
Expand Up @@ -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)
Expand Down Expand Up @@ -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 converted value,
// unless the converted 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.
Expand Down
98 changes: 98 additions & 0 deletions internal/terraform/eval_variable_test.go
Expand Up @@ -68,6 +68,30 @@ 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)
}))
}))
}
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,
Expand Down Expand Up @@ -399,6 +423,80 @@ 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),
}),
}),
}),
}),
``,
},
{
"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
{
"constrained_string_sensitive_required",
Expand Down

0 comments on commit 6521355

Please sign in to comment.