Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pkg/controller/external_tfpluginfw.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,11 +357,21 @@ func (c *TerraformPluginFrameworkConnector) getResourceConfigTerraformValue(ctx
// when only computed attributes or not-specified argument diffs
// exist in the raw diff and no actual diff exists in the
// parametrizable attributes.
// Exception: a diff where the prior value (Value2) is non-null and the
// planned value (Value1) is null represents an explicit removal of a
// previously-set optional attribute and must not be filtered.
func (n *terraformPluginFrameworkExternalClient) filteredDiffExists(rawDiff []tftypes.ValueDiff) bool {
filteredDiff := make([]tftypes.ValueDiff, 0)
for _, diff := range rawDiff {
// Keep diffs where the planned value is non-null and known.
if diff.Value1 != nil && diff.Value1.IsKnown() && !diff.Value1.IsNull() {
filteredDiff = append(filteredDiff, diff)
continue
}
// Keep diffs where the prior value was non-null and the planned value
// is null — this is an explicit removal of a previously-set attribute.
if diff.Value1 != nil && diff.Value1.IsNull() && diff.Value2 != nil && !diff.Value2.IsNull() {
filteredDiff = append(filteredDiff, diff)
}
}
return len(filteredDiff) > 0
Expand Down
88 changes: 88 additions & 0 deletions pkg/controller/external_tfpluginfw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1272,3 +1272,91 @@ func newMockTPFResourceWithIdentity() *mockTPFResourceWithIdentity {
},
}
}

func TestFilteredDiffExists(t *testing.T) {
strVal := func(s string) *tftypes.Value {
v := tftypes.NewValue(tftypes.String, s)
return &v
}
nullVal := func() *tftypes.Value {
v := tftypes.NewValue(tftypes.String, nil)
return &v
}
unknownVal := func() *tftypes.Value {
v := tftypes.NewValue(tftypes.String, tftypes.UnknownValue)
return &v
}

cases := map[string]struct {
rawDiff []tftypes.ValueDiff
want bool
}{
"EmptyDiff": {
rawDiff: []tftypes.ValueDiff{},
want: false,
},
"PlannedNonNullPriorNull": {
rawDiff: []tftypes.ValueDiff{
{Value1: strVal("foo"), Value2: nullVal()},
},
want: true,
},
"PlannedNonNullPriorNonNull": {
rawDiff: []tftypes.ValueDiff{
{Value1: strVal("new"), Value2: strVal("old")},
},
want: true,
},
// Explicit removal: prior was set, planned is null. The fix ensures
// this is not filtered out.
"PlannedNullPriorNonNull": {
rawDiff: []tftypes.ValueDiff{
{Value1: nullVal(), Value2: strVal("foo")},
},
want: true,
},
// Field was never specified; both sides are null — no real diff.
"PlannedNullPriorNull": {
rawDiff: []tftypes.ValueDiff{
{Value1: nullVal(), Value2: nullVal()},
},
want: false,
},
// Value1 nil means the child attribute has no individual planned value
// (e.g. when its parent object is null). Should remain filtered.
"PlannedNilPriorNonNull": {
rawDiff: []tftypes.ValueDiff{
{Value1: nil, Value2: strVal("foo")},
},
want: false,
},
// Unknown planned value corresponds to a computed field — filtered.
"PlannedUnknownPriorNonNull": {
rawDiff: []tftypes.ValueDiff{
{Value1: unknownVal(), Value2: strVal("foo")},
},
want: false,
},
// Simulates optional nested object removal: child attribute diffs have
// nil Value1, but the parent-level diff has null Value1 / non-null
// Value2 and must be detected.
"NestedObjectRemoval": {
rawDiff: []tftypes.ValueDiff{
{Value1: nil, Value2: strVal("ClusterIP")}, // child attr, Value1 nil
{Value1: nil, Value2: strVal("Cluster")}, // child attr, Value1 nil
{Value1: nullVal(), Value2: strVal("3")}, // parent object null → removal
},
want: true,
},
}

client := &terraformPluginFrameworkExternalClient{}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := client.filteredDiffExists(tc.rawDiff)
if got != tc.want {
t.Errorf("filteredDiffExists() = %v, want %v", got, tc.want)
}
})
}
}
Loading