don't panic with a null list block value in config

Using ignore_changes with a list block, where the provider returned an
invalid null value for that block, can result in a panic when validating
the plan.

Future releases may prevent providers from storing a null block in
state, however we can avoid the panic for now. Only the NestingList case
needs to be handled, because legacy providers only have list and set
blocks, and the set case does not use the config value.
This commit is contained in:
James Bardin 2022-12-21 14:53:14 -05:00
parent 0e46d0ad25
commit ea193d5ce6
2 changed files with 44 additions and 1 deletions

View File

@ -101,6 +101,14 @@ func assertPlanValid(schema *configschema.Block, priorState, config, plannedStat
continue
}
if configV.IsNull() {
// Configuration cannot decode a block into a null value, but
// we could be dealing with a null returned by a legacy
// provider and inserted via ignore_changes. Fix the value in
// place so the length can still be compared.
configV = cty.ListValEmpty(configV.Type().ElementType())
}
plannedL := plannedV.LengthInt()
configL := configV.LengthInt()
if plannedL != configL {

View File

@ -389,6 +389,41 @@ func TestAssertPlanValid(t *testing.T) {
},
},
// but don't panic on a null list just in case
"nested list, null in config": {
&configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"b": {
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"c": {
Type: cty.String,
Optional: true,
},
},
},
},
},
},
cty.ObjectVal(map[string]cty.Value{
"b": cty.ListValEmpty(cty.Object(map[string]cty.Type{
"c": cty.String,
})),
}),
cty.ObjectVal(map[string]cty.Value{
"b": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{
"c": cty.String,
}))),
}),
cty.ObjectVal(map[string]cty.Value{
"b": cty.ListValEmpty(cty.Object(map[string]cty.Type{
"c": cty.String,
})),
}),
nil,
},
// blocks can be unknown when using dynamic
"nested list, unknown nested dynamic": {
&configschema.Block{
@ -1671,7 +1706,7 @@ func TestAssertPlanValid(t *testing.T) {
t.Logf(
"\nprior: %sconfig: %splanned: %s",
dump.Value(test.Planned),
dump.Value(test.Prior),
dump.Value(test.Config),
dump.Value(test.Planned),
)