Unverified Commit 2e2a77a3 authored by Alisdair McDiarmid's avatar Alisdair McDiarmid Committed by GitHub
Browse files

Merge pull request #31805 from...

Merge pull request #31805 from hashicorp/backport/alisdair/skip-defaults-apply-for-null/evenly-vast-tahr

Backport of Do not apply type defaults to null values into v1.3
Showing with 94 additions and 4 deletions
+94 -4
......@@ -140,8 +140,11 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno
if v.ConstraintType != cty.NilType {
var err error
// If the type constraint has defaults, we must apply those
// defaults to the variable default value before type conversion.
if v.TypeDefaults != nil {
// defaults to the variable default value before type conversion,
// unless the default value is null. Null is excluded from the
// type default application process as a special case, to allow
// nullable variables to have a null default value.
if v.TypeDefaults != nil && !val.IsNull() {
val = v.TypeDefaults.Apply(val)
}
val, err = convert.Convert(val, v.ConstraintType)
......
variable "a" {
type = object({
foo = optional(string)
bar = optional(bool, true)
})
}
variable "b" {
type = list(
object({
foo = optional(string)
})
)
}
variable "c" {
type = set(
object({
foo = optional(string)
})
)
}
variable "d" {
type = map(
object({
foo = optional(string)
})
)
}
variable "e" {
type = object({
foo = string
bar = optional(bool, true)
})
default = null
}
......@@ -12119,6 +12119,52 @@ output "out" {
}
}
func TestContext2Apply_moduleVariableOptionalAttributesDefaultNull(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
variable "in" {
type = object({
required = string
optional = optional(string)
default = optional(bool, true)
})
default = null
}
# Wrap the input variable in a tuple because a null output value is elided from
# the plan, which prevents us from testing its type.
output "out" {
value = [var.in]
}
`})
ctx := testContext2(t, &ContextOpts{})
// We don't specify a value for the variable here, relying on its defined
// default.
plan, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
state, diags := ctx.Apply(plan, m)
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
got := state.RootModule().OutputValues["out"].Value
// The null default value should be bound, after type converting to the
// full object type
want := cty.TupleVal([]cty.Value{cty.NullVal(cty.Object(map[string]cty.Type{
"required": cty.String,
"optional": cty.String,
"default": cty.Bool,
}))})
if !want.RawEquals(got) {
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, want)
}
}
func TestContext2Apply_provisionerSensitive(t *testing.T) {
m := testModule(t, "apply-provisioner-sensitive")
p := testProvider("aws")
......
......@@ -90,8 +90,11 @@ 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
if cfg.TypeDefaults != nil {
// 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)
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment