mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
typeexpr: Replace null attr values with defaults
Previously, when applying defaults to an input variable's given value before type conversion, we would permit `null` attribute values to override a specified default. This behaviour is inconsistent with the intent of the type system underlying Terraform, and represented a divergence from the treatment of `null` as equivalent to unset which exists in resources. The same behaviour exists in top-level variable definitions with `nullable = false`, and we consider this to be the preferred behaviour here too. This commit slightly changes default value application such that an explicit `null` attribute value is treated as equivalent to the attribute being missing. Default values for attributes will now replace explicit nulls.
This commit is contained in:
parent
2aff67857f
commit
c85ae29419
@ -91,7 +91,7 @@ func (t *defaultsTransformer) Enter(p cty.Path, v cty.Value) (cty.Value, error)
|
||||
// Apply defaults where attributes are missing, constructing a new
|
||||
// value with the same marks.
|
||||
for attr, defaultValue := range defaults {
|
||||
if _, ok := attrs[attr]; !ok {
|
||||
if attrValue, ok := attrs[attr]; !ok || attrValue.IsNull() {
|
||||
attrs[attr] = defaultValue
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,23 @@ func TestDefaults_Apply(t *testing.T) {
|
||||
"b": cty.StringVal("false"),
|
||||
}),
|
||||
},
|
||||
// Defaults will replace explicit nulls.
|
||||
"object with explicit null for attribute with default": {
|
||||
defaults: &Defaults{
|
||||
Type: simpleObject,
|
||||
DefaultValues: map[string]cty.Value{
|
||||
"b": cty.True,
|
||||
},
|
||||
},
|
||||
value: cty.MapVal(map[string]cty.Value{
|
||||
"a": cty.StringVal("foo"),
|
||||
"b": cty.NullVal(cty.String),
|
||||
}),
|
||||
want: cty.ObjectVal(map[string]cty.Value{
|
||||
"a": cty.StringVal("foo"),
|
||||
"b": cty.True,
|
||||
}),
|
||||
},
|
||||
// Defaults can be specified at any level of depth and will be applied
|
||||
// so long as there is a parent value to populate.
|
||||
"nested object with defaults applied": {
|
||||
@ -371,7 +388,7 @@ func TestDefaults_Apply(t *testing.T) {
|
||||
// the child type.
|
||||
"c": cty.ObjectVal(map[string]cty.Value{
|
||||
"a": cty.StringVal("fallback"),
|
||||
"b": cty.NullVal(cty.Bool),
|
||||
"b": cty.False,
|
||||
}),
|
||||
},
|
||||
Children: map[string]*Defaults{
|
||||
@ -410,7 +427,65 @@ func TestDefaults_Apply(t *testing.T) {
|
||||
// default value for "c" includes a non-default value
|
||||
// already.
|
||||
"a": cty.StringVal("fallback"),
|
||||
"b": cty.NullVal(cty.Bool),
|
||||
"b": cty.False,
|
||||
}),
|
||||
"d": cty.NumberIntVal(7),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
"set of nested objects, nulls in default sub-object overridden": {
|
||||
defaults: &Defaults{
|
||||
Type: cty.Set(nestedObject),
|
||||
Children: map[string]*Defaults{
|
||||
"": {
|
||||
Type: nestedObject,
|
||||
DefaultValues: map[string]cty.Value{
|
||||
// The default value for "c" is used to prepopulate
|
||||
// the nested object's value if not specified, but
|
||||
// the null default for its "b" attribute will be
|
||||
// overridden by the default specified in the child
|
||||
// type.
|
||||
"c": cty.ObjectVal(map[string]cty.Value{
|
||||
"a": cty.StringVal("fallback"),
|
||||
"b": cty.NullVal(cty.Bool),
|
||||
}),
|
||||
},
|
||||
Children: map[string]*Defaults{
|
||||
"c": {
|
||||
Type: simpleObject,
|
||||
DefaultValues: map[string]cty.Value{
|
||||
"b": cty.True,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
value: cty.TupleVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"c": cty.ObjectVal(map[string]cty.Value{
|
||||
"a": cty.StringVal("foo"),
|
||||
}),
|
||||
"d": cty.NumberIntVal(5),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"d": cty.NumberIntVal(7),
|
||||
}),
|
||||
}),
|
||||
want: cty.TupleVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"c": cty.ObjectVal(map[string]cty.Value{
|
||||
"a": cty.StringVal("foo"),
|
||||
"b": cty.True,
|
||||
}),
|
||||
"d": cty.NumberIntVal(5),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"c": cty.ObjectVal(map[string]cty.Value{
|
||||
// The default value for "b" overrides the explicit
|
||||
// null in the default value for "c".
|
||||
"a": cty.StringVal("fallback"),
|
||||
"b": cty.True,
|
||||
}),
|
||||
"d": cty.NumberIntVal(7),
|
||||
}),
|
||||
|
Loading…
Reference in New Issue
Block a user