plans/objchange: Fix panic in AssertObjectCompatible with set blocks
Our usual "ground rules" for mapping configschema to cty call for the collection values representing nested block types to always be known and non-null, using an empty collection to represent the absense of any blocks of that type so that users can always safely use length(...) etc on them without worrying about them sometimes being null. However, due to some different behaviors in the legacy SDK we've allowed it an exception to this rule which means that we can see unknown and null collections in these positions in object values returned from provider operations like PlanResourceChange and ApplyResourceChange when the legacy SDK opt-out is activated. As a consequence of this, we need to be mindful in our safety check functions, like AssertObjectCompatible here, of tolerating these non-ideal situations to allow the safety checks to complete. We run these checks even when the provider requests an opt-out, because we want to note any inconsistencies as WARNING level log lines to aid in debugging.
This commit is contained in:
parent
58eba8fe9a
commit
0b2cc6298b
|
@ -157,7 +157,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case configschema.NestingSet:
|
case configschema.NestingSet:
|
||||||
if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1073,6 +1073,43 @@ func TestAssertObjectCompatible(t *testing.T) {
|
||||||
`.block: planned set element cty.Value{ty: cty.Object(map[string]cty.Type{"foo":cty.String}), v: map[string]interface {}{"foo":"hello"}} does not correlate with any element in actual`,
|
`.block: planned set element cty.Value{ty: cty.Object(map[string]cty.Type{"foo":cty.String}), v: map[string]interface {}{"foo":"hello"}} does not correlate with any element in actual`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// This one is an odd situation where the value representing the
|
||||||
|
// block itself is unknown. This is never supposed to be true,
|
||||||
|
// but in legacy SDK mode we allow such things to pass through as
|
||||||
|
// a warning, and so we must tolerate them for matching purposes.
|
||||||
|
&configschema.Block{
|
||||||
|
BlockTypes: map[string]*configschema.NestedBlock{
|
||||||
|
"block": {
|
||||||
|
Nesting: configschema.NestingSet,
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"foo": {
|
||||||
|
Type: cty.String,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"block": cty.SetVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.UnknownVal(cty.String),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.UnknownVal(cty.String),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"block": cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.String,
|
||||||
|
}))),
|
||||||
|
}),
|
||||||
|
nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|
Loading…
Reference in New Issue