Skip to content

Commit 2562420

Browse files
committed
prevent panics with null objects in nested attrs
When descending into nested structural attributes, don't try to extract attributes from null objects. Unlike with blocks, nested attributes allow the possibility of assigning null values. While these technically aren't allowed to be altered, we need to accept these for compatibility.
1 parent c77898c commit 2562420

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

internal/plans/objchange/plan_valid.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,31 @@ func assertPlannedAttrsValid(schema map[string]*configschema.Attribute, priorSta
256256
}
257257

258258
func assertPlannedAttrValid(name string, attrS *configschema.Attribute, priorState, config, plannedState cty.Value, path cty.Path) []error {
259-
plannedV := plannedState.GetAttr(name)
260-
configV := config.GetAttr(name)
261-
priorV := cty.NullVal(attrS.Type)
259+
// any of the config, prior or planned values may be null at this point if
260+
// we are in nested structural attributes.
261+
var plannedV, configV, priorV cty.Value
262+
if attrS.NestedType != nil {
263+
configV = cty.NullVal(attrS.NestedType.ImpliedType())
264+
priorV = cty.NullVal(attrS.NestedType.ImpliedType())
265+
plannedV = cty.NullVal(attrS.NestedType.ImpliedType())
266+
} else {
267+
configV = cty.NullVal(attrS.Type)
268+
priorV = cty.NullVal(attrS.Type)
269+
plannedV = cty.NullVal(attrS.Type)
270+
}
271+
272+
if !config.IsNull() {
273+
configV = config.GetAttr(name)
274+
}
275+
262276
if !priorState.IsNull() {
263277
priorV = priorState.GetAttr(name)
264278
}
279+
280+
if !plannedState.IsNull() {
281+
plannedV = plannedState.GetAttr(name)
282+
}
283+
265284
path = append(path, cty.GetAttrStep{Name: name})
266285

267286
return assertPlannedValueValid(attrS, priorV, configV, plannedV, path)

internal/plans/objchange/plan_valid_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,6 +1510,7 @@ func TestAssertPlanValid(t *testing.T) {
15101510
// When an object has dynamic attrs, the map may be
15111511
// handled as an object.
15121512
"map_as_obj": {
1513+
Optional: true,
15131514
NestedType: &configschema.Object{
15141515
Nesting: configschema.NestingMap,
15151516
Attributes: map[string]*configschema.Attribute{
@@ -1522,6 +1523,7 @@ func TestAssertPlanValid(t *testing.T) {
15221523
},
15231524
},
15241525
"list": {
1526+
Optional: true,
15251527
NestedType: &configschema.Object{
15261528
Nesting: configschema.NestingList,
15271529
Attributes: map[string]*configschema.Attribute{
@@ -1586,11 +1588,23 @@ func TestAssertPlanValid(t *testing.T) {
15861588
"one": cty.ObjectVal(map[string]cty.Value{
15871589
"name": cty.NullVal(cty.DynamicPseudoType),
15881590
}),
1591+
"two": cty.NullVal(cty.Object(map[string]cty.Type{
1592+
"name": cty.DynamicPseudoType,
1593+
})),
1594+
"three": cty.NullVal(cty.Object(map[string]cty.Type{
1595+
"name": cty.DynamicPseudoType,
1596+
})),
15891597
}),
15901598
"list": cty.ListVal([]cty.Value{
15911599
cty.ObjectVal(map[string]cty.Value{
15921600
"name": cty.NullVal(cty.String),
15931601
}),
1602+
cty.NullVal(cty.Object(map[string]cty.Type{
1603+
"name": cty.String,
1604+
})),
1605+
cty.NullVal(cty.Object(map[string]cty.Type{
1606+
"name": cty.String,
1607+
})),
15941608
}),
15951609
"set": cty.SetVal([]cty.Value{
15961610
cty.ObjectVal(map[string]cty.Value{
@@ -1611,11 +1625,26 @@ func TestAssertPlanValid(t *testing.T) {
16111625
"one": cty.ObjectVal(map[string]cty.Value{
16121626
"name": cty.StringVal("computed"),
16131627
}),
1628+
// The config was null, but some providers may return a
1629+
// non-null object here, so we need to accept this for
1630+
// compatibility.
1631+
"two": cty.ObjectVal(map[string]cty.Value{
1632+
"name": cty.NullVal(cty.String),
1633+
}),
1634+
"three": cty.NullVal(cty.Object(map[string]cty.Type{
1635+
"name": cty.DynamicPseudoType,
1636+
})),
16141637
}),
16151638
"list": cty.ListVal([]cty.Value{
16161639
cty.ObjectVal(map[string]cty.Value{
16171640
"name": cty.StringVal("computed"),
16181641
}),
1642+
cty.ObjectVal(map[string]cty.Value{
1643+
"name": cty.NullVal(cty.String),
1644+
}),
1645+
cty.NullVal(cty.Object(map[string]cty.Type{
1646+
"name": cty.String,
1647+
})),
16191648
}),
16201649
"set": cty.SetVal([]cty.Value{
16211650
cty.ObjectVal(map[string]cty.Value{

0 commit comments

Comments
 (0)