diff --git a/lang/funcs/defaults.go b/lang/funcs/defaults.go index c5acf26e8..0e4fd41ff 100644 --- a/lang/funcs/defaults.go +++ b/lang/funcs/defaults.go @@ -97,7 +97,11 @@ func defaultsApply(input, fallback cty.Value) cty.Value { // For structural types, a null input value must be passed through. We // do not apply default values for missing optional structural values, // only their contents. - if input.IsNull() { + // + // We also pass through the input if the fallback value is null. This + // can happen if the given defaults do not include a value for this + // attribute. + if input.IsNull() || fallback.IsNull() { return input } atys := wantTy.AttributeTypes() @@ -116,7 +120,11 @@ func defaultsApply(input, fallback cty.Value) cty.Value { // For structural types, a null input value must be passed through. We // do not apply default values for missing optional structural values, // only their contents. - if input.IsNull() { + // + // We also pass through the input if the fallback value is null. This + // can happen if the given defaults do not include a value for this + // attribute. + if input.IsNull() || fallback.IsNull() { return input } diff --git a/lang/funcs/defaults_test.go b/lang/funcs/defaults_test.go index a77ff316a..3d36d75cc 100644 --- a/lang/funcs/defaults_test.go +++ b/lang/funcs/defaults_test.go @@ -398,6 +398,51 @@ func TestDefaults(t *testing.T) { "b": cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})), }), }, + // When applying default values to structural types, we permit null + // values in the defaults, and just pass through the input value. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "p": cty.StringVal("xyz"), + "q": cty.StringVal("xyz"), + }), + }), + "b": cty.SetVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(0), + cty.NumberIntVal(2), + }), + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(3), + }), + }), + "c": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "c": cty.StringVal("tada"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "p": cty.StringVal("xyz"), + "q": cty.StringVal("xyz"), + }), + }), + "b": cty.SetVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(0), + cty.NumberIntVal(2), + }), + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(3), + }), + }), + "c": cty.StringVal("tada"), + }), + }, // When applying default values to collection types, null collections in the // input should result in empty collections in the output. {