Skip to content

Commit

Permalink
use walkTwoPropertyValues recursion
Browse files Browse the repository at this point in the history
  • Loading branch information
VenelinMartinov committed Dec 19, 2024
1 parent 9de798d commit d71edbb
Showing 1 changed file with 21 additions and 133 deletions.
154 changes: 21 additions & 133 deletions pkg/tfbridge/property_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,11 @@ func propertyValueTriggersReplacement(
return replacement
}

// isPlanCompatibleWithInputs returns true if all values in the walkedValue are also in the comparedValue,
// bar any computed properties.
func isPlanCompatibleWithInputs(
// validInputsFromPlan returns true if the given plan property value could originate from the given inputs.
// Under the hood, it walks the plan and the inputs and checks that all differences stem from computed properties.
// Any differences coming from properties which are not computed will be rejected.
// Note that we are relying on the fact that the inputs will have defaults already applied.
func validInputsFromPlan(
path propertyPath,
inputs resource.PropertyValue,
plan resource.PropertyValue,
Expand All @@ -255,166 +257,52 @@ func isPlanCompatibleWithInputs(
) bool {
abortErr := errors.New("abort")
visitor := func(
subpath resource.PropertyPath, planSubVal resource.PropertyValue, entering bool,
) (resource.PropertyValue, error) {
if !entering {
return planSubVal, nil
}

subpath propertyPath, inputsSubVal, planSubVal resource.PropertyValue,
) error {
// Do not compare and do not descend into internal properties.
{
lastPathStep := subpath[len(subpath)-1]
if stepStr, ok := lastPathStep.(string); ok {
if resource.IsInternalPropertyKey(resource.PropertyKey(stepStr)) {
return planSubVal, propertyvalue.SkipChildrenError{}
}
}
if subpath.IsReservedKey() {
return SkipChildrenError{}
}

tfs, _, err := lookupSchemas(propertyPath(subpath), tfs, ps)
tfs, _, err := lookupSchemas(subpath, tfs, ps)
if err != nil {
// TODO log
return resource.NewNullProperty(), abortErr
}

relativePath, err := propertyPath(subpath).GetPathRelativeTo(path)
if err != nil {
return resource.NewNullProperty(), abortErr
}

inputsSubVal, ok := relativePath.Get(inputs)
if !ok || inputsSubVal.IsNull() {
if tfs.Computed() {
return planSubVal, nil
}
return resource.NewNullProperty(), abortErr
}

if tfs.Type() == shim.TypeList || tfs.Type() == shim.TypeMap {
// We only need to check the leaf values, so we skip any collection types.
return planSubVal, nil
}

// We can not descend into nested sets as planning re-orders the elements
if tfs.Type() == shim.TypeSet {
// TODO: more sophisticated comparison of nested sets
if planSubVal.DeepEquals(inputsSubVal) {
return planSubVal, propertyvalue.SkipChildrenError{}
}
return resource.NewNullProperty(), abortErr
}

if planSubVal.DeepEquals(inputsSubVal) {
return planSubVal, nil
}

return resource.NewNullProperty(), abortErr
}
_, err := propertyvalue.TransformPropertyValueDirectional(
resource.PropertyPath(path),
visitor,
plan,
)
if err == abortErr {
return false
}
contract.AssertNoErrorf(err, "TransformPropertyValue should only return an abort error")
return true
}

func isInputCompatibleWithPlan(
path propertyPath,
plan resource.PropertyValue,
inputs resource.PropertyValue,
tfs shim.SchemaMap,
ps map[string]*info.Schema,
) bool {
abortErr := errors.New("abort")
visitor := func(
subpath resource.PropertyPath, inputsSubVal resource.PropertyValue, entering bool,
) (resource.PropertyValue, error) {
if !entering {
return inputsSubVal, nil
}
// Do not compare and do not descend into internal properties.
{
lastPathStep := subpath[len(subpath)-1]
if stepStr, ok := lastPathStep.(string); ok {
if resource.IsInternalPropertyKey(resource.PropertyKey(stepStr)) {
return inputsSubVal, propertyvalue.SkipChildrenError{}
}
}
}

tfs, _, err := lookupSchemas(propertyPath(subpath), tfs, ps)
if err != nil {
// TODO log
return resource.NewNullProperty(), abortErr
}

relativePath, err := propertyPath(subpath).GetPathRelativeTo(path)
if err != nil {
return resource.NewNullProperty(), abortErr
}

planSubVal, ok := relativePath.Get(plan)
if !ok || planSubVal.IsNull() {
return resource.NewNullProperty(), abortErr
return abortErr
}

if tfs.Computed() && inputsSubVal.IsNull() {
return planSubVal, nil
return nil
}

if tfs.Type() == shim.TypeList || tfs.Type() == shim.TypeMap {
// We only need to check the leaf values, so we skip any collection types.
return inputsSubVal, nil
return nil
}

// We can not descend into nested sets as planning re-orders the elements
if tfs.Type() == shim.TypeSet {
// TODO: more sophisticated comparison of nested sets
if inputsSubVal.DeepEquals(planSubVal) {
return inputsSubVal, propertyvalue.SkipChildrenError{}
return SkipChildrenError{}
}
return resource.NewNullProperty(), abortErr
return abortErr
}

if inputsSubVal.DeepEquals(planSubVal) {
return inputsSubVal, nil
return nil
}

return resource.NewNullProperty(), abortErr
return abortErr
}
_, err := propertyvalue.TransformPropertyValueDirectional(
resource.PropertyPath(path),
visitor,
err := walkTwoPropertyValues(
path,
inputs,
plan,
visitor,
)
if err == abortErr {
return false
}
contract.AssertNoErrorf(err, "TransformPropertyValue should only return an abort error")
return true
}

// validInputsFromPlan returns true if the given plan property value could originate from the given inputs.
// Under the hood, it walks the plan and the inputs and checks that all differences stem from computed properties.
// Any differences coming from properties which are not computed will be rejected.
// Note that we are relying on the fact that the inputs will have defaults already applied.
func validInputsFromPlan(
path propertyPath,
inputs resource.PropertyValue,
plan resource.PropertyValue,
tfs shim.SchemaMap,
ps map[string]*info.Schema,
) bool {
// We walk both the plan and the inputs and check that all differences stem from computed properties.
// We first walk over the plan.
if !isPlanCompatibleWithInputs(path, inputs, plan, tfs, ps) {
return false
}

// We then walk over the inputs.
return isInputCompatibleWithPlan(path, plan, inputs, tfs, ps)
}

0 comments on commit d71edbb

Please sign in to comment.