plans/objchange: Don't panic when dynamic-typed attrs are present

When dynamically-typed attributes are in the schema, we use different
conventions for representing nested blocks containing them (using tuples
and objects instead of lists and maps).

The normalization code here doesn't deal with those because the legacy
SDK never generates them, but we must still pass them through properly or
else other SDKs will be blocked from using dynamic attributes.

Previously this function would panic in that situation. Now it will just
pass through nested blocks containing dynamic attribute values entirely
as-is, with no normalization whatsoever. That's okay, because the scope
of this function is only to normalize inconsistencies that the legacy
SDK is known to produce, and the legacy SDK never produces dynamic-typed
attributes.
This commit is contained in:
Martin Atkins 2019-03-09 11:51:33 -08:00
parent 30672faebe
commit c5aa5c68bc
2 changed files with 78 additions and 0 deletions

View File

@ -34,6 +34,16 @@ func NormalizeObjectFromLegacySDK(val cty.Value, schema *configschema.Block) cty
} }
for name, blockS := range schema.BlockTypes { for name, blockS := range schema.BlockTypes {
lv := val.GetAttr(name) lv := val.GetAttr(name)
// Legacy SDK never generates dynamically-typed attributes and so our
// normalization code doesn't deal with them, but we need to make sure
// we still pass them through properly so that we don't interfere with
// objects generated by other SDKs.
if ty := blockS.Block.ImpliedType(); ty.HasDynamicTypes() {
vals[name] = lv
continue
}
switch blockS.Nesting { switch blockS.Nesting {
case configschema.NestingSingle: case configschema.NestingSingle:
if lv.IsKnown() { if lv.IsKnown() {

View File

@ -224,6 +224,74 @@ func TestNormalizeObjectFromLegacySDK(t *testing.T) {
}), }),
}), }),
}, },
"block list with dynamic type": {
&configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"a": {
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"b": {Type: cty.DynamicPseudoType, Optional: true},
},
},
},
},
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.TupleVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"b": cty.StringVal("hello"),
}),
cty.ObjectVal(map[string]cty.Value{
"b": cty.True,
}),
}),
}),
cty.ObjectVal(map[string]cty.Value{
"a": cty.TupleVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"b": cty.StringVal("hello"),
}),
cty.ObjectVal(map[string]cty.Value{
"b": cty.True,
}),
}),
}),
},
"block map with dynamic type": {
&configschema.Block{
BlockTypes: map[string]*configschema.NestedBlock{
"a": {
Nesting: configschema.NestingMap,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"b": {Type: cty.DynamicPseudoType, Optional: true},
},
},
},
},
},
cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"one": cty.ObjectVal(map[string]cty.Value{
"b": cty.StringVal("hello"),
}),
"another": cty.ObjectVal(map[string]cty.Value{
"b": cty.True,
}),
}),
}),
cty.ObjectVal(map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"one": cty.ObjectVal(map[string]cty.Value{
"b": cty.StringVal("hello"),
}),
"another": cty.ObjectVal(map[string]cty.Value{
"b": cty.True,
}),
}),
}),
},
} }
for name, test := range tests { for name, test := range tests {