Skip to content

Commit

Permalink
don't panic with a null list block value in config
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jbardin committed Dec 21, 2022
1 parent 0e46d0a commit ea193d5
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 1 deletion.
8 changes: 8 additions & 0 deletions internal/plans/objchange/plan_valid.go
Expand Up @@ -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 {
Expand Down
37 changes: 36 additions & 1 deletion internal/plans/objchange/plan_valid_test.go
Expand Up @@ -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{
Expand Down Expand Up @@ -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),
)
Expand Down

0 comments on commit ea193d5

Please sign in to comment.