mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
plans/objchange: Decompose assertNestedBlockCompatible
The main function is now just a jump table into a separate function for each nesting mode. The observable behavior is unchanged. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
parent
55ff663aff
commit
d49f997b65
@ -95,31 +95,101 @@ func assertAttributeCompatible(plannedV, actualV cty.Value, attrS *configschema.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
|
func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
|
||||||
var errs []error
|
|
||||||
|
|
||||||
switch blockS.Nesting {
|
switch blockS.Nesting {
|
||||||
case configschema.NestingSingle, configschema.NestingGroup:
|
case configschema.NestingSingle, configschema.NestingGroup:
|
||||||
// If an unknown block placeholder was present then the placeholder
|
return assertNestedBlockCompatibleSingle(plannedV, actualV, blockS, path)
|
||||||
// may have expanded out into zero blocks, which is okay.
|
|
||||||
if !plannedV.IsKnown() && actualV.IsNull() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
moreErrs := assertObjectCompatible(&blockS.Block, plannedV, actualV, path)
|
|
||||||
errs = append(errs, moreErrs...)
|
|
||||||
case configschema.NestingList:
|
case configschema.NestingList:
|
||||||
// A NestingList might either be a list or a tuple, depending on
|
return assertNestedBlockCompatibleList(plannedV, actualV, blockS, path)
|
||||||
// whether there are dynamically-typed attributes inside. However,
|
case configschema.NestingMap:
|
||||||
// both support a similar-enough API that we can treat them the
|
return assertNestedBlockCompatibleMap(plannedV, actualV, blockS, path)
|
||||||
// same for our purposes here.
|
case configschema.NestingSet:
|
||||||
if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
return assertNestedBlockCompatibleSet(plannedV, actualV, blockS, path)
|
||||||
break
|
default:
|
||||||
}
|
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertNestedBlockCompatibleSingle(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
|
||||||
|
// If an unknown block placeholder was present then the placeholder
|
||||||
|
// may have expanded out into zero blocks, which is okay.
|
||||||
|
if !plannedV.IsKnown() && actualV.IsNull() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
moreErrs := assertObjectCompatible(&blockS.Block, plannedV, actualV, path)
|
||||||
|
errs = append(errs, moreErrs...)
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertNestedBlockCompatibleList(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
|
||||||
|
// A NestingList might either be a list or a tuple, depending on
|
||||||
|
// whether there are dynamically-typed attributes inside. However,
|
||||||
|
// both support a similar-enough API that we can treat them the
|
||||||
|
// same for our purposes here.
|
||||||
|
|
||||||
|
if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
plannedL := plannedV.LengthInt()
|
||||||
|
actualL := actualV.LengthInt()
|
||||||
|
if plannedL != actualL {
|
||||||
|
errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
for it := plannedV.ElementIterator(); it.Next(); {
|
||||||
|
idx, plannedEV := it.Element()
|
||||||
|
if !actualV.HasIndex(idx).True() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
actualEV := actualV.Index(idx)
|
||||||
|
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx}))
|
||||||
|
errs = append(errs, moreErrs...)
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertNestedBlockCompatibleMap(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
|
||||||
|
// 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 both values will have the same
|
||||||
|
// kind.
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if plannedV.Type().IsObjectType() {
|
||||||
|
plannedAtys := plannedV.Type().AttributeTypes()
|
||||||
|
actualAtys := actualV.Type().AttributeTypes()
|
||||||
|
for k := range plannedAtys {
|
||||||
|
if _, ok := actualAtys[k]; !ok {
|
||||||
|
errs = append(errs, path.NewErrorf("block key %q has vanished", k))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
plannedEV := plannedV.GetAttr(k)
|
||||||
|
actualEV := actualV.GetAttr(k)
|
||||||
|
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.GetAttrStep{Name: k}))
|
||||||
|
errs = append(errs, moreErrs...)
|
||||||
|
}
|
||||||
|
if plannedV.IsKnown() { // new blocks may appear if unknown blocks were present in the plan
|
||||||
|
for k := range actualAtys {
|
||||||
|
if _, ok := plannedAtys[k]; !ok {
|
||||||
|
errs = append(errs, path.NewErrorf("new block key %q has appeared", k))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
plannedL := plannedV.LengthInt()
|
plannedL := plannedV.LengthInt()
|
||||||
actualL := actualV.LengthInt()
|
actualL := actualV.LengthInt()
|
||||||
if plannedL != actualL {
|
if plannedL != actualL && plannedV.IsKnown() { // new blocks may appear if unknown blocks were present in the plan
|
||||||
errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
|
errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
|
||||||
break
|
return errs
|
||||||
}
|
}
|
||||||
for it := plannedV.ElementIterator(); it.Next(); {
|
for it := plannedV.ElementIterator(); it.Next(); {
|
||||||
idx, plannedEV := it.Element()
|
idx, plannedEV := it.Element()
|
||||||
@ -130,84 +200,41 @@ func assertNestedBlockCompatible(plannedV, actualV cty.Value, blockS *configsche
|
|||||||
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx}))
|
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx}))
|
||||||
errs = append(errs, moreErrs...)
|
errs = append(errs, moreErrs...)
|
||||||
}
|
}
|
||||||
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 both values will have the same
|
|
||||||
// kind.
|
|
||||||
if plannedV.Type().IsObjectType() {
|
|
||||||
plannedAtys := plannedV.Type().AttributeTypes()
|
|
||||||
actualAtys := actualV.Type().AttributeTypes()
|
|
||||||
for k := range plannedAtys {
|
|
||||||
if _, ok := actualAtys[k]; !ok {
|
|
||||||
errs = append(errs, path.NewErrorf("block key %q has vanished", k))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
plannedEV := plannedV.GetAttr(k)
|
return errs
|
||||||
actualEV := actualV.GetAttr(k)
|
}
|
||||||
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.GetAttrStep{Name: k}))
|
|
||||||
errs = append(errs, moreErrs...)
|
|
||||||
}
|
|
||||||
if plannedV.IsKnown() { // new blocks may appear if unknown blocks were present in the plan
|
|
||||||
for k := range actualAtys {
|
|
||||||
if _, ok := plannedAtys[k]; !ok {
|
|
||||||
errs = append(errs, path.NewErrorf("new block key %q has appeared", k))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
plannedL := plannedV.LengthInt()
|
|
||||||
actualL := actualV.LengthInt()
|
|
||||||
if plannedL != actualL && plannedV.IsKnown() { // new blocks may appear if unknown blocks were present in the plan
|
|
||||||
errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for it := plannedV.ElementIterator(); it.Next(); {
|
|
||||||
idx, plannedEV := it.Element()
|
|
||||||
if !actualV.HasIndex(idx).True() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
actualEV := actualV.Index(idx)
|
|
||||||
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx}))
|
|
||||||
errs = append(errs, moreErrs...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case configschema.NestingSet:
|
|
||||||
if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !plannedV.IsKnown() {
|
func assertNestedBlockCompatibleSet(plannedV, actualV cty.Value, blockS *configschema.NestedBlock, path cty.Path) []error {
|
||||||
// When unknown blocks are present the final number of blocks
|
if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
|
||||||
// may be different, either because the unknown set values
|
return nil
|
||||||
// become equal and are collapsed, or the count is unknown due
|
}
|
||||||
// a dynamic block. Unfortunately this means we can't do our
|
|
||||||
// usual checks in this case without generating false
|
|
||||||
// negatives.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
setErrs := assertSetValuesCompatible(plannedV, actualV, path, func(plannedEV, actualEV cty.Value) bool {
|
if !plannedV.IsKnown() {
|
||||||
errs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: actualEV}))
|
// When unknown blocks are present the final number of blocks
|
||||||
return len(errs) == 0
|
// may be different, either because the unknown set values
|
||||||
})
|
// become equal and are collapsed, or the count is unknown due
|
||||||
errs = append(errs, setErrs...)
|
// a dynamic block. Unfortunately this means we can't do our
|
||||||
|
// usual checks in this case without generating false
|
||||||
|
// negatives.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// There can be fewer elements in a set after its elements are all
|
var errs []error
|
||||||
// known (values that turn out to be equal will coalesce) but the
|
|
||||||
// number of elements must never get larger.
|
setErrs := assertSetValuesCompatible(plannedV, actualV, path, func(plannedEV, actualEV cty.Value) bool {
|
||||||
plannedL := plannedV.LengthInt()
|
moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: actualEV}))
|
||||||
actualL := actualV.LengthInt()
|
return len(moreErrs) == 0
|
||||||
if plannedL < actualL {
|
})
|
||||||
errs = append(errs, path.NewErrorf("block set length changed from %d to %d", plannedL, actualL))
|
errs = append(errs, setErrs...)
|
||||||
}
|
|
||||||
default:
|
// There can be fewer elements in a set after its elements are all
|
||||||
panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
|
// known (values that turn out to be equal will coalesce) but the
|
||||||
|
// number of elements must never get larger.
|
||||||
|
plannedL := plannedV.LengthInt()
|
||||||
|
actualL := actualV.LengthInt()
|
||||||
|
if plannedL < actualL {
|
||||||
|
errs = append(errs, path.NewErrorf("block set length changed from %d to %d", plannedL, actualL))
|
||||||
}
|
}
|
||||||
|
|
||||||
return errs
|
return errs
|
||||||
|
Loading…
Reference in New Issue
Block a user