objects vs maps in nested object types

When using NestedMap objects, unify the codepath for both maps and
objects as they may be interchangeable.
This commit is contained in:
James Bardin 2021-08-30 13:53:49 -04:00
parent 7ca6be8285
commit 903797084a
2 changed files with 37 additions and 62 deletions

View File

@ -368,73 +368,48 @@ func assertPlannedObjectValid(schema *configschema.Object, prior, config, planne
case configschema.NestingMap:
// A NestingMap might either be a map or an object, depending on
// whether there are dynamically-typed attributes inside, but
// that's decided statically and so all values will have the same
// kind.
if planned.Type().IsObjectType() {
plannedAtys := planned.Type().AttributeTypes()
configAtys := config.Type().AttributeTypes()
for k := range plannedAtys {
if _, ok := configAtys[k]; !ok {
// whether there are dynamically-typed attributes inside, so we will
// break these down to maps to handle them both in the same manner.
plannedVals := map[string]cty.Value{}
configVals := map[string]cty.Value{}
priorVals := map[string]cty.Value{}
if !planned.IsNull() {
plannedVals = planned.AsValueMap()
}
if !config.IsNull() {
configVals = config.AsValueMap()
}
if !prior.IsNull() {
priorVals = prior.AsValueMap()
}
for k, plannedEV := range plannedVals {
configEV, ok := configVals[k]
if !ok {
errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k))
continue
}
path := append(path, cty.GetAttrStep{Name: k})
plannedEV := planned.GetAttr(k)
if !plannedEV.IsKnown() {
errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
continue
}
configEV := config.GetAttr(k)
priorEV := cty.NullVal(schema.ImpliedType())
if !prior.IsNull() && prior.Type().HasAttribute(k) {
priorEV = prior.GetAttr(k)
priorEV, ok := priorVals[k]
if !ok {
priorEV = cty.NullVal(schema.ImpliedType())
}
moreErrs := assertPlannedAttrsValid(schema.Attributes, priorEV, configEV, plannedEV, path)
errs = append(errs, moreErrs...)
}
for k := range configAtys {
if _, ok := plannedAtys[k]; !ok {
for k := range configVals {
if _, ok := plannedVals[k]; !ok {
errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", k))
continue
}
}
} else {
plannedL := planned.LengthInt()
configL := config.LengthInt()
if plannedL != configL {
errs = append(errs, path.NewErrorf("block count in plan (%d) disagrees with count in config (%d)", plannedL, configL))
return errs
}
for it := planned.ElementIterator(); it.Next(); {
idx, plannedEV := it.Element()
path := append(path, cty.IndexStep{Key: idx})
if !plannedEV.IsKnown() {
errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
continue
}
k := idx.AsString()
if !config.HasIndex(idx).True() {
errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k))
continue
}
configEV := config.Index(idx)
priorEV := cty.NullVal(schema.ImpliedType())
if !prior.IsNull() && prior.HasIndex(idx).True() {
priorEV = prior.Index(idx)
}
moreErrs := assertPlannedAttrsValid(schema.Attributes, priorEV, configEV, plannedEV, path)
errs = append(errs, moreErrs...)
}
for it := config.ElementIterator(); it.Next(); {
idx, _ := it.Element()
if !planned.HasIndex(idx).True() {
errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", idx.AsString()))
continue
}
}
}
case configschema.NestingSet:
// Because set elements have no identifier with which to correlate

View File

@ -1307,7 +1307,7 @@ func TestAssertPlanValid(t *testing.T) {
"name": cty.NullVal(cty.String),
}),
}),
"map_as_obj": cty.ObjectVal(map[string]cty.Value{
"map_as_obj": cty.MapVal(map[string]cty.Value{
"one": cty.ObjectVal(map[string]cty.Value{
"name": cty.NullVal(cty.DynamicPseudoType),
}),