Skip to content

Conversation

@chrischdi
Copy link
Contributor

@chrischdi chrischdi commented Oct 10, 2025

Summary by CodeRabbit

  • Refactor
    • Consolidated diffing into a unified, platform-aware differ with per-block change checks and improved error propagation; removed legacy map-based diffs.
  • New Features
    • Added a reusable DiffResult interface (metadata/spec/providerSpec/status checks), platform-aware providerSpec handling, and deterministic AWS provider tag ordering.
  • Tests
    • Added unit tests covering diff behaviors, list/nested changes, and condition timestamp handling.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Oct 10, 2025
@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 10, 2025

@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 10, 2025
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Oct 10, 2025

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Oct 10, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign racheljpg for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai
Copy link

coderabbitai bot commented Oct 10, 2025

Walkthrough

Replaces ad-hoc map-based diffs with a structured pkg/util DiffResult across machine and machineset controllers, adds a platform-aware differ with providerSpec handling and deterministic output, removes go-test/deep, updates signatures and error propagation across comparison/update paths, and adds unit tests for the differ and status diffs.

Changes

Cohort / File(s) Summary
Diff Utility Implementation
pkg/util/diff.go
Adds a differ and DiffResult interface with HasChanges/HasMetadataChanges/HasSpecChanges/HasProviderSpecChanges/HasStatusChanges, deterministic deep diffing, ignore-options (fields, LastTransitionTime), and providerSpec extraction options (WithProviderSpec).
Diff Utility Tests
pkg/util/diff_test.go
Adds unit tests validating differ behavior for additions, removals, modifications, list mutations, HasChanges, and exact diff strings.
MachineSet Sync Controller
pkg/controllers/machinesetsync/machineset_sync_controller.go
Replaces map-based diffs with util.DiffResult for CAPI/MAPI machine sets and infra templates, introduces platform-aware compare signatures, refactors ensure* methods to use DiffResult Has* checks, and improves error wrapping/propagation.
Machine Sync Controller
pkg/controllers/machinesync/machine_sync_controller.go
compareCAPIMachines/compareMAPIMachines now return (util.DiffResult, error); MAPIs are platform-aware; spec/status ensure methods accept DiffResult and use Has* checks; legacy map helpers removed.
Machine Infra Sync (mapi→capi)
pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go
Replaces map diffs with util.DiffResult, updates compare/ensure signatures, delegates field diffing to the new differ, and gates status updates on diff.HasStatusChanges().
Sync Helpers Removed
pkg/util/sync.go
Removes previous specialized comparison helpers (ObjectMetaEqual, CAPIMachineSetStatusEqual, CAPIMachineStatusEqual, MAPIMachineStatusEqual) and drops go-test/deep usage.
Provider Spec Conversion
pkg/conversion/mapi2capi/util.go
Adds ProviderSpecFromRawExtension(platform, rawExtension) with platform dispatch (AWS implemented) and new sentinel errUnsupportedPlatform.
AWS Provider Determinism
pkg/conversion/mapi2capi/aws.go
Sorts AWS provider Tags deterministically during conversion to ensure stable providerSpec ordering.
Controller Tests Updated
pkg/controllers/machinesetsync/machineset_sync_controller_test.go
Tests updated to pass platform into compareMAPIMachineSets and assert on DiffResult methods (HasProviderSpecChanges, HasChanges) instead of map key access.
MachineSet Controller Unit Tests
pkg/controllers/machinesetsync/machineset_sync_controller_unit_test.go
Adds unit tests covering CAPIMachineSet status diffs across v1beta1/v1beta2 conditions, LastTransitionTime ignoring, nil vs present status, and diff string assertions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Reconciler
  participant Converter
  participant Differ as "Differ (pkg/util)"
  participant API as "K8s API"

  Reconciler->>Converter: build converted object (CAPI/MAPI)
  Reconciler->>Differ: diffResult, err = Diff(existing, converted)
  alt Diff error
    Differ-->>Reconciler: error
    Reconciler->>API: record/return error
  else Diff computed
    Differ-->>Reconciler: DiffResult (HasChanges?, HasSpec?, HasStatus?, HasProviderSpec?)
    alt HasSpecChanges or HasMetadataChanges or HasProviderSpecChanges
      Reconciler->>API: patch/update spec/metadata
    end
    alt HasStatusChanges
      Reconciler->>API: patch/update status (observe generation guards)
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Multiple controllers and call sites updated with new diff type and platform parameter changes.
  • New differ contains non-trivial logic (providerSpec extraction/marshalling, LastTransitionTime removal, deterministic key-wise diff).
  • Tests added/updated to reflect new API.

Files/areas needing extra attention:

  • removeConditionsLastTransitionTime handling across v1beta1/v1beta2 condition shapes.
  • WithProviderSpec / ProviderSpecFromRawExtension correctness per-platform (marshalling and promoted field path).
  • All updated function signatures and call sites to ensure no residual map[string]any assumptions.
  • Determinism of diff strings for nested lists and tag sorting in AWS conversion.

Poem

🐰 I hopped through fields both old and new,
I turned loose maps into a structured view.
I silenced times that softly sway,
Sorted tags so diffs would stay,
A rabbit's patch — tidy, true, and few.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "OCPCLOUD-3172: machinesetsync: refactor to a generalized differ which can work independent of types" directly and clearly summarizes the primary objective of the changeset. The raw summary confirms that the main changes involve replacing map-based diff representations with a unified util.DiffResult-based differ mechanism across multiple machine synchronization controllers, removing external deep-equal dependencies, and introducing platform-aware comparison pathways. The title accurately captures this core refactoring effort by highlighting the shift from type-specific comparison logic to a generalized, type-independent differ, and the Jira reference has been validated as valid by CI checks.
Docstring Coverage ✅ Passed Docstring coverage is 92.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Comment @coderabbitai help to get the list of available commands and usage tips.

@chrischdi
Copy link
Contributor Author

/test unit
/test lint
/test build

Copy link
Contributor

@mdbooth mdbooth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is great and a clear improvement. I have some follow-on ideas, but I would personally gladly merge this and iterate on it in the unlikely event it ever became worth it.

I shared a thought inline based on something similar we did in ORC with field indexers.

Not at all important, but I also feel that Unstructured is an internal implementation detail: e.g. the Diff method could be implemented with reflection and the signature would not change.

pkg/util/sync.go Outdated
Comment on lines 93 to 100
differ := UnstructuredDiffer[clusterv1.MachineSetStatus]{
customDiff: []func(a clusterv1.MachineSetStatus, b clusterv1.MachineSetStatus) ([]string, error){
func(a, b clusterv1.MachineSetStatus) ([]string, error) {
return compareCAPIMachineSetConditions(a.Conditions, b.Conditions), nil
},
},
ignoreFields: [][]string{{"conditions"}},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

differ := UnstructuredDiffer[clusterv1.MachineSetStatus]()

differ = WithCustomDiff(differ,
    "conditions",
    func(x *clusterv1.MachineSetStatus) []clusterv1.Condition {return x.conditions},
    compareCAPIConditions)

func compareCAPIConditions(a, b []clusterv1.Conditions) []string {
    ...
}

WithCustomDiff unfortunately can't be a method on UnstructuredDiffer as it has an additional type argument([]clusterv1.Condition).

Advantages of this approach:

  • ignoreField is a required argument of CustomDiff, removing a footgun
  • compareCAPIConditions is now reusable by all types with CAPI conditions

Disadvantages:

  • The argument to pull a field out is a bit ugly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ignoreField is a required argument of CustomDiff, removing a footgun

Are there cases when we want to ignore fields without custom diffs no? Maybe around deprecated fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there are (e.g. CAPI's conversion-data annotation).

However I'd combine it, but keeping the option to ignore paths completely:

	differ := NewUnstructuredDiffer(
		WithCustomDiff([]string{"conditions"}, func(a, b clusterv1.MachineSetStatus) ([]string, error) {
			return compareCAPIV1Beta1Conditions(a.Conditions, b.Conditions), nil
		}),
		WithCustomDiff([]string{"v1beta2", "conditions"}, func(a, b clusterv1.MachineSetStatus) ([]string, error) {
			return compareCAPIV1Beta2Conditions(
					ptr.Deref(a.V1Beta2, clusterv1.MachineSetV1Beta2Status{}).Conditions,
					ptr.Deref(b.V1Beta2, clusterv1.MachineSetV1Beta2Status{}).Conditions),
				nil
		}),
		WithIgnoreField[clusterv1.MachineSetStatus]("conditions", "bar"),
	)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored, don't have the "WithCustomDiff" anymore.
I now have an option for the conditions part instead.

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 10, 2025
pkg/util/diff.go Outdated
"k8s.io/apimachinery/pkg/runtime"
)

type UnstructuredDiffer[T any] struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is unstructured, why is it also generic? Does it make this simpler than using runtime.Object for the a, b arguments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed just to differ. Should work for any struct.

pkg/util/diff.go Outdated

diff := deep.Equal(unstructuredA, unstructuredB)
if len(diff) > 0 {
diffs["deep.Equal"] = diff
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the deep.Equal key? Does this get exposed to end users? Is this a magic word?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not there anymore :-)

pkg/util/sync.go Outdated
Comment on lines 85 to 91
// Maybe we can also have it this way:
// differ := NewUnstructuredDiffer(
// WithIgnoreField[clusterv1.MachineSetStatus]("conditions"),
// WithCustomDiff(func(a, b clusterv1.MachineSetStatus) ([]string, error) {
// return compareCAPIMachineSetConditions(a.Conditions, b.Conditions), nil
// }),
// )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored :-)

pkg/util/sync.go Outdated
Comment on lines 93 to 100
differ := UnstructuredDiffer[clusterv1.MachineSetStatus]{
customDiff: []func(a clusterv1.MachineSetStatus, b clusterv1.MachineSetStatus) ([]string, error){
func(a, b clusterv1.MachineSetStatus) ([]string, error) {
return compareCAPIMachineSetConditions(a.Conditions, b.Conditions), nil
},
},
ignoreFields: [][]string{{"conditions"}},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ignoreField is a required argument of CustomDiff, removing a footgun

Are there cases when we want to ignore fields without custom diffs no? Maybe around deprecated fields?

@chrischdi chrischdi force-pushed the pr-sync-refactor-how-to-diff branch from ac815fd to f494b0c Compare October 30, 2025 17:34
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 30, 2025
@chrischdi chrischdi marked this pull request as ready for review October 30, 2025 17:34
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 30, 2025
@openshift-ci openshift-ci bot requested review from damdo and nrb October 30, 2025 17:34
@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 30, 2025

@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Summary by CodeRabbit

Release Notes

  • Refactor

  • Improved resource comparison logic across machine synchronization controllers with structured, field-by-field analysis

  • Enhanced platform-aware handling for infrastructure resources across supported cloud platforms

  • Bug Fixes

  • Added proper error handling and reporting in resource synchronization comparison operations to surface issues previously undetected

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Comment on lines 1381 to 1400
// Compare metadata
if diffMetadata, err := util.ObjectMetaEqual(metadata1, metadata2); err != nil {
return nil, fmt.Errorf("failed to compare Cluster API Infrastructure machine metadata: %w", err)
} else if diffMetadata.Changed() {
diff[".metadata"] = diffMetadata.String()
}

// TODO: Evaluate if we want to add status comparison if needed in the future (e.g. for scale from zero capacity).
// Compare spec
if diffSpec, err := util.NewDiffer().Diff(spec1, spec2); err != nil {
return nil, fmt.Errorf("failed to compare Cluster API Infrastructure machine spec: %w", err)
} else if diffSpec.Changed() {
diff[".spec"] = diffSpec.String()
}

return diff, nil
default:
return nil, fmt.Errorf("%w: %s", errPlatformNotSupported, platform)
// Compare status
if diffStatus, err := util.NewDiffer(util.WithIgnoreConditionsLastTransitionTime()).Diff(status1, status2); err != nil {
return nil, fmt.Errorf("failed to compare Cluster API Infrastructure machine status: %w", err)
} else if diffStatus.Changed() {
diff[".status"] = diffStatus.String()
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about doing this even more generic, as this is the same across the codebase.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More generic as in abstracting each of the diff fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, updated the PR. We now don't have to distinct and manually do the diff for metadata, spec, status.

We always compare the whole object and can have all results:

...Diff(clusterv1.Machine, clusterv1.Machine)

:-)

Comment on lines 1343 to 1344
status1 = typedInfraMachineTemplate1.Status
status2 = typedinfraMachineTemplate2.Status
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: added status conversion here.

Let's see if we break something or not :-)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/controllers/machinesetsync/machineset_sync_controller.go (1)

1346-1354: Return the correct OpenStack assertion error.

In the OpenStack branch we still return errAssertingCAPIIBMPowerVSMachineTemplate on a type mismatch. Callers that rely on errors.Is(..., errAssertingCAPIOpenStackMachineTemplate) will now miss this case, breaking platform-specific handling. Please swap the constant to the OpenStack one for both checks in this branch.

-		typedInfraMachineTemplate1, ok := infraMachineTemplate1.(*openstackv1.OpenStackMachineTemplate)
-		if !ok {
-			return nil, errAssertingCAPIIBMPowerVSMachineTemplate
-		}
+		typedInfraMachineTemplate1, ok := infraMachineTemplate1.(*openstackv1.OpenStackMachineTemplate)
+		if !ok {
+			return nil, errAssertingCAPIOpenStackMachineTemplate
+		}

 		typedinfraMachineTemplate2, ok := infraMachineTemplate2.(*openstackv1.OpenStackMachineTemplate)
 		if !ok {
 			return nil, errAssertingCAPIOpenStackMachineTemplate
 		}
🧹 Nitpick comments (2)
pkg/util/sync_test.go (1)

332-341: Assert the string output even for unchanged diffs
Right now we skip got.String() comparisons when tt.wantChanges is false, so a future regression where Changed() returns false but String() leaks content would slide through. Consider moving the g.Expect(got.String()).To(Equal(tt.want)) assertion outside the conditional (the happy-path cases already set want to ""), or duplicating it in the else. That keeps the test guarding both facets of the contract.

pkg/controllers/machinesync/machine_sync_controller.go (1)

1259-1277: Tighten the error message wording
These error strings still say “Cluster API Infrastructure machine …” even though this helper compares the top-level CAPI Machine. Tweaking the wording here (and in the similar spec/status branches) would make logs clearer when we do hit this path.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between a395cf1 and f494b0c.

📒 Files selected for processing (7)
  • pkg/controllers/machinesetsync/machineset_sync_controller.go (6 hunks)
  • pkg/controllers/machinesync/machine_sync_controller.go (3 hunks)
  • pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go (5 hunks)
  • pkg/util/diff.go (1 hunks)
  • pkg/util/diff_test.go (1 hunks)
  • pkg/util/sync.go (1 hunks)
  • pkg/util/sync_test.go (1 hunks)
🔇 Additional comments (1)
pkg/util/diff_test.go (1)

78-115: Nice coverage for slice diffs
Exercising add/remove/change scenarios on list fields gives strong confidence that the new differ is handling positional data correctly. Thanks for including these cases.

Comment on lines +63 to +134
unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&a)
if err != nil {
return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
}

unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&b)
if err != nil {
return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the unstructured conversion and error message.

runtime.DefaultUnstructuredConverter.ToUnstructured expects the concrete object, not a pointer to the interface wrapper. By passing &a / &b, we hand it values of type *interface{}, which the converter cannot handle, so every diff involving typed structs will error out. While you are there, the first error message also says “convert b”, making diagnostics misleading. Update both calls to pass a / b directly and correct the message. (reddit.com)

-	unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&a)
+	unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(a)
 	if err != nil {
-		return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
+		return nil, fmt.Errorf("failed to convert a to unstructured: %w", err)
 	}

-	unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&b)
+	unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(b)
 	if err != nil {
 		return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
 	}
🤖 Prompt for AI Agents
In pkg/util/diff.go around lines 63 to 71, the calls pass &a and &b to
runtime.DefaultUnstructuredConverter.ToUnstructured and the first error message
incorrectly references "b"; change the calls to pass a and b (the concrete
values, not pointers to interface wrappers) and update the error messages to
"failed to convert a to unstructured" and "failed to convert b to unstructured"
respectively so conversion works and diagnostics are accurate.

Comment on lines 1381 to 1400
// Compare metadata
if diffMetadata, err := util.ObjectMetaEqual(metadata1, metadata2); err != nil {
return nil, fmt.Errorf("failed to compare Cluster API Infrastructure machine metadata: %w", err)
} else if diffMetadata.Changed() {
diff[".metadata"] = diffMetadata.String()
}

// TODO: Evaluate if we want to add status comparison if needed in the future (e.g. for scale from zero capacity).
// Compare spec
if diffSpec, err := util.NewDiffer().Diff(spec1, spec2); err != nil {
return nil, fmt.Errorf("failed to compare Cluster API Infrastructure machine spec: %w", err)
} else if diffSpec.Changed() {
diff[".spec"] = diffSpec.String()
}

return diff, nil
default:
return nil, fmt.Errorf("%w: %s", errPlatformNotSupported, platform)
// Compare status
if diffStatus, err := util.NewDiffer(util.WithIgnoreConditionsLastTransitionTime()).Diff(status1, status2); err != nil {
return nil, fmt.Errorf("failed to compare Cluster API Infrastructure machine status: %w", err)
} else if diffStatus.Changed() {
diff[".status"] = diffStatus.String()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More generic as in abstracting each of the diff fields?


out := "." + strings.Join(d.diff, ", .")
out = strings.ReplaceAll(out, ".slice[", "[")
out = strings.ReplaceAll(out, "map[", "[")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a nit, but could we distinguish maps from slices with {}? Might be too much work, since we'd have to find and replace the closing characters, but it could be helpful in interpreting output.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pure cosmetics / output (I guess mostly for us when debugging).

I'm even okay with keeping the more verbose mode.

@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 31, 2025

@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Summary by CodeRabbit

  • Refactor

  • Improved machine synchronization comparison logic with enhanced error handling and deterministic diff detection across controllers

  • Replaced legacy comparison mechanisms with a unified, structured approach for more reliable change detection

  • Tests

  • Added comprehensive unit tests for differential comparison functionality

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 31, 2025

@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Summary by CodeRabbit

Release Notes

  • Refactor
  • Improved machine and machineset synchronization with structured diff handling, enabling more granular change detection and better error propagation across comparison operations.
  • Removed external dependency (go-test/deep) and consolidated diffing utilities for cleaner internal architecture.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
pkg/util/diff.go (1)

126-134: Don't wrap the operands in & before converting to unstructured.

runtime.DefaultUnstructuredConverter.ToUnstructured expects the actual object. Passing &a / &b hands it *client.Object, so every diff on typed resources fails. While fixing that, please correct the first error message so it references “a”.

-	unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&a)
-	if err != nil {
-		return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
+	unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(a)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert a to unstructured: %w", err)
 	}

-	unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&b)
+	unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(b)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between f494b0c and e0114e3.

📒 Files selected for processing (9)
  • pkg/controllers/machinesetsync/machineset_sync_controller.go (11 hunks)
  • pkg/controllers/machinesetsync/machineset_sync_controller_test.go (1 hunks)
  • pkg/controllers/machinesetsync/machineset_sync_controller_unit_test.go (1 hunks)
  • pkg/controllers/machinesync/machine_sync_controller.go (6 hunks)
  • pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go (7 hunks)
  • pkg/conversion/mapi2capi/util.go (1 hunks)
  • pkg/util/diff.go (1 hunks)
  • pkg/util/diff_test.go (1 hunks)
  • pkg/util/sync.go (0 hunks)
💤 Files with no reviewable changes (1)
  • pkg/util/sync.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/util/diff_test.go
🔇 Additional comments (13)
pkg/conversion/mapi2capi/util.go (1)

44-49: Deterministic tag ordering looks great.

Sorting the AWS tags up front eliminates noisy diffs when we convert back and forth between map and slice representations.

pkg/controllers/machinesetsync/machineset_sync_controller_test.go (1)

1177-1188: Thanks for exercising the new diff surface.

Checking HasProviderSpecChanges() and HasChanges() here makes sure the platform-aware comparer behaves correctly.

pkg/controllers/machinesetsync/machineset_sync_controller_unit_test.go (1)

31-351: Great coverage for DiffResult behaviour.

These table-driven cases hit all the tricky status paths—including the v1beta1/v1beta2 condition handling and LastTransitionTime suppression—so we can trust the refactored comparer.

pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go (1)

63-82: DiffResult integration reads clean.

Branching on HasSpecChanges, HasMetadataChanges, and HasStatusChanges keeps the reconciliation flow explicit while reusing the shared differ.

pkg/controllers/machinesync/machine_sync_controller.go (5)

649-674: LGTM! Signature and logic updated correctly.

The function signature has been appropriately updated to accept util.DiffResult, and the conditional logic correctly uses the new Has*Changes() methods to determine if updates are needed.


697-700: LGTM! Error handling added correctly.

The comparison function now properly returns and propagates errors, improving robustness of the diff operation.


1254-1279: LGTM! Refactored comparison functions.

Both compareCAPIMachines and compareMAPIMachines have been properly refactored to:

  • Use the new util.NewDefaultDiffer() interface
  • Return structured util.DiffResult instead of maps
  • Propagate errors from diff computation
  • Support platform-aware provider spec comparison (MAPI machines)

The platform-aware diffing with WithProviderSpec and field ignore options in compareMAPIMachines is a solid improvement for handling provider-specific differences.


1282-1331: LGTM! Status update functions refactored correctly.

Both ensureCAPIMachineStatusUpdated and ensureMAPIMachineStatusUpdated have been consistently updated to accept util.DiffResult and use the HasStatusChanges() method for determining if status updates are needed.

Also applies to: 1363-1408


1411-1434: LGTM! Comprehensive change detection for MAPI machines.

The function correctly checks for metadata, spec, and provider spec changes, which is appropriate given that MAPI machines have provider-specific specifications that need to be tracked separately.

pkg/controllers/machinesetsync/machineset_sync_controller.go (4)

787-814: LGTM! Proper error handling for comparison.

The comparison function call correctly handles errors and propagates them with appropriate wrapping.


843-866: LGTM! All ensure functions refactored correctly.

All four ensure*Updated functions have been consistently updated to:

  • Accept util.DiffResult instead of map-based diffs
  • Use the appropriate Has*Changes() methods
  • Apply correct conditional logic with proper negation for early returns

Also applies to: 869-917, 920-944, 947-996


1021-1024: LGTM! Platform parameter added correctly.

The comparison function correctly receives the platform parameter to enable platform-aware provider spec diffing.


1317-1399: LGTM! Comparison functions properly refactored.

All three comparison functions have been successfully refactored to:

  • Return (util.DiffResult, error) instead of map-based diffs
  • Use util.NewDefaultDiffer() with appropriate configuration options
  • Handle platform-specific differences where needed (provider specs)
  • Properly wrap and propagate errors

The platform-aware diffing in compareCAPIInfraMachineTemplates and compareMAPIMachineSets with support for provider specs and field filtering is a significant improvement.

Comment on lines +742 to 745
if capiInfraMachineTemplatesDiff.HasChanges() {
logger.Info("No changes detected for CAPI infra machine template")
return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical logic error: Inverted condition.

The condition if capiInfraMachineTemplatesDiff.HasChanges() is inverted. When HasChanges() returns true, it means there ARE changes detected, but the code logs "No changes detected" and returns early, skipping the update. This will prevent legitimate updates from being applied.

Apply this fix:

-	if capiInfraMachineTemplatesDiff.HasChanges() {
+	if !capiInfraMachineTemplatesDiff.HasChanges() {
 		logger.Info("No changes detected for CAPI infra machine template")
 		return nil
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if capiInfraMachineTemplatesDiff.HasChanges() {
logger.Info("No changes detected for CAPI infra machine template")
return nil
}
if !capiInfraMachineTemplatesDiff.HasChanges() {
logger.Info("No changes detected for CAPI infra machine template")
return nil
}
🤖 Prompt for AI Agents
In pkg/controllers/machinesetsync/machineset_sync_controller.go around lines 742
to 745, the if-condition is inverted: when
capiInfraMachineTemplatesDiff.HasChanges() is true the code incorrectly logs "No
changes detected" and returns early; change the condition to if
!capiInfraMachineTemplatesDiff.HasChanges() to return early with the "No changes
detected" log, allowing the update path to execute when HasChanges() is true.

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 31, 2025
@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 31, 2025

@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Summary by CodeRabbit

  • Refactor
  • Consolidated diffing into a unified, platform-aware differ for more reliable, deterministic change detection and improved error handling.
  • New Features
  • Added a reusable diff result interface with per-block change checks (metadata/spec/providerSpec/status).
  • Added providerSpec conversion support for platform-aware provider handling (AWS).
  • Tests
  • Added comprehensive unit tests covering diff behaviors and edge cases.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@chrischdi chrischdi force-pushed the pr-sync-refactor-how-to-diff branch from a9987c6 to dbbfcd3 Compare October 31, 2025 10:54
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 31, 2025
@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 31, 2025

@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Summary by CodeRabbit

  • Refactor
  • Consolidated diffing into a unified, platform-aware differ with per-block change checks and improved error propagation; removed legacy map-based diffs and external deep-equal dependency.
  • New Features
  • Introduced a reusable DiffResult interface (HasMetadata/Spec/ProviderSpec/Status) and platform-aware providerSpec handling.
  • Added ProviderSpec conversion for AWS.
  • Tests
  • Added unit tests covering diff behaviors, list/nested changes, and condition timestamp handling.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
pkg/controllers/machinesetsync/machineset_sync_controller.go (1)

742-745: Critical: Inverted condition prevents infrastructure template updates.

The condition is inverted: when HasChanges() returns true (changes exist), the code incorrectly logs "No changes detected" and returns early, preventing legitimate updates from being applied.

Apply this fix:

-	if capiInfraMachineTemplatesDiff.HasChanges() {
+	if !capiInfraMachineTemplatesDiff.HasChanges() {
 		logger.Info("No changes detected for CAPI infra machine template")
 		return nil
 	}
🧹 Nitpick comments (2)
pkg/conversion/mapi2capi/util.go (1)

33-53: Export the sentinel error so callers can detect unsupported platforms.

Because ProviderSpecFromRawExtension is exported, downstream callers will likely need to distinguish an unsupported platform from other parse failures. Keeping the sentinel unexported forces string matching; exporting it (e.g., ErrUnsupportedPlatform) lets clients rely on errors.Is. Please export the sentinel and update the reference accordingly.

-var errUnsupportedPlatform = errors.New("unsupported platform")
+var ErrUnsupportedPlatform = errors.New("unsupported platform")
...
-		return nil, fmt.Errorf("%w: %s", errUnsupportedPlatform, platform)
+		return nil, fmt.Errorf("%w: %s", ErrUnsupportedPlatform, platform)
pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go (1)

248-301: LGTM: Platform-aware comparison successfully generalized.

The refactored comparison function correctly handles platform-specific type assertions before delegating to the type-agnostic differ. The error handling is comprehensive, and the default case ensures unsupported platforms are caught.

Optional: Consider renaming obj1 and obj2 to typedInfraMachine1 and typedInfraMachine2 for consistency with the parameter names and to make the type assertion flow clearer.

 func compareCAPIInfraMachines(platform configv1.PlatformType, infraMachine1, infraMachine2 client.Object) (util.DiffResult, error) {
-	var obj1, obj2 client.Object
+	var typedInfraMachine1, typedInfraMachine2 client.Object

 	switch platform {
 	case configv1.AWSPlatformType:
-		typedInfraMachine1, ok := infraMachine1.(*awsv1.AWSMachine)
+		awsMachine1, ok := infraMachine1.(*awsv1.AWSMachine)
 		if !ok {
 			return nil, errAssertingCAPIAWSMachine
 		}

-		typedinfraMachine2, ok := infraMachine2.(*awsv1.AWSMachine)
+		awsMachine2, ok := infraMachine2.(*awsv1.AWSMachine)
 		if !ok {
 			return nil, errAssertingCAPIAWSMachine
 		}

-		obj1 = typedInfraMachine1
-		obj2 = typedinfraMachine2
+		typedInfraMachine1 = awsMachine1
+		typedInfraMachine2 = awsMachine2
 	// ... similar for other platforms
 	}

-	diff, err := util.NewDefaultDiffer().Diff(obj1, obj2)
+	diff, err := util.NewDefaultDiffer().Diff(typedInfraMachine1, typedInfraMachine2)
 	if err != nil {
 		return nil, fmt.Errorf("failed to compare Cluster API infrastructure machines: %w", err)
 	}

 	return diff, nil
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between a9987c6 and dbbfcd3.

📒 Files selected for processing (9)
  • pkg/controllers/machinesetsync/machineset_sync_controller.go (11 hunks)
  • pkg/controllers/machinesetsync/machineset_sync_controller_test.go (1 hunks)
  • pkg/controllers/machinesetsync/machineset_sync_controller_unit_test.go (1 hunks)
  • pkg/controllers/machinesync/machine_sync_controller.go (6 hunks)
  • pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go (7 hunks)
  • pkg/conversion/mapi2capi/util.go (1 hunks)
  • pkg/util/diff.go (1 hunks)
  • pkg/util/diff_test.go (1 hunks)
  • pkg/util/sync.go (0 hunks)
💤 Files with no reviewable changes (1)
  • pkg/util/sync.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/util/diff_test.go
  • pkg/util/diff.go
🔇 Additional comments (20)
pkg/controllers/machinesetsync/machineset_sync_controller_unit_test.go (1)

30-351: Excellent test coverage for status diffing!

The comprehensive test suite effectively validates the new DiffResult-based diff approach across multiple scenarios:

  • Individual and combined field differences
  • v1beta1 and v1beta2 conditions handling
  • LastTransitionTime correctly ignored in comparisons
  • nil vs non-nil status scenarios

The test structure is clear and the assertions properly validate both HasChanges() and the diff string format.

pkg/controllers/machinesetsync/machineset_sync_controller_test.go (1)

1175-1189: Test updates correctly reflect new diff API.

The changes properly adapt to the platform-aware compareMAPIMachineSets signature and use the new DiffResult methods (HasProviderSpecChanges(), HasChanges()) instead of map-based checks.

pkg/controllers/machinesetsync/machineset_sync_controller.go (9)

787-790: Proper error handling for diff operation.

The updated signature correctly propagates errors from compareCAPIMachineSets, with appropriate error wrapping for context.


843-849: Clean integration with DiffResult API.

The function signature and logic correctly use the new DiffResult methods (HasMetadataChanges(), HasSpecChanges(), HasProviderSpecChanges()) to determine when spec updates are needed.


869-875: Correct status update gating logic.

The function properly uses HasStatusChanges() and the specUpdated flag to determine when status updates are necessary, maintaining the synchronization semantics.


920-926: Consistent diff checking for MAPI spec updates.

The implementation mirrors the CAPI version with appropriate use of HasMetadataChanges(), HasSpecChanges(), and HasProviderSpecChanges() to detect when updates are needed.


947-953: Status update logic correctly adapted.

The function properly integrates with the new DiffResult API, using HasStatusChanges() and the specUpdated flag to gate status updates appropriately.


1021-1024: Platform-aware diff comparison properly integrated.

The call to compareMAPIMachineSets correctly includes the platform parameter to enable platform-specific providerSpec handling, with appropriate error propagation.


1317-1370: Well-structured platform-aware infrastructure template comparison.

The refactored function cleanly handles platform-specific types (AWS, OpenStack, PowerVS) and delegates to the new util.NewDefaultDiffer() for consistent, generalized diffing with proper error handling.


1373-1380: Simplified and consistent CAPI MachineSet comparison.

The refactored function correctly delegates to util.NewDefaultDiffer() for consistent comparison logic, with proper error handling.


1383-1399: Comprehensive platform-aware MAPI MachineSet comparison.

The refactored function properly configures the differ with:

  • Platform-specific providerSpec handling via WithProviderSpec
  • Appropriate status field exclusions (replicas, observedGeneration, etc.) that are managed separately by sync logic
  • Clean error handling and propagation
pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go (3)

66-92: LGTM: Clean migration to DiffResult-based spec change detection.

The refactored logic correctly uses diff.HasSpecChanges() to detect when the infrastructure machine needs recreation due to immutable spec changes. The error handling and logging properly capture the diff details for debugging.


124-147: LGTM: Signature update aligns with the new differ approach.

The function signature now accepts util.DiffResult, and the logic correctly uses HasMetadataChanges() for early-exit optimization. This is a clean improvement over the previous map-based approach.


149-204: LGTM: Status update logic correctly uses DiffResult.

The refactored signature and logic properly use diff.HasStatusChanges() to determine when status updates are needed. The generation synchronization check (line 160) ensures consistency between MAPI and CAPI resources.

pkg/controllers/machinesync/machine_sync_controller.go (6)

649-674: LGTM: Spec update logic cleanly migrated to DiffResult.

The refactored function correctly uses capiMachinesDiff.HasMetadataChanges() and capiMachinesDiff.HasSpecChanges() to determine when updates are needed. The early-return optimization on line 653 is logically sound.


697-700: LGTM: Call site properly handles new comparison API.

The error handling and return type match the updated compareCAPIMachines signature, with proper error wrapping.


751-754: LGTM: Platform parameter enables platform-aware diffing.

The addition of the r.Platform parameter is essential for the new platform-aware providerSpec comparison logic. This change aligns with the PR's refactoring objectives.


1254-1261: LGTM: Simplified and type-safe comparison for CAPI machines.

The refactored implementation is clean and concise, delegating to the generalized differ. The error handling properly wraps errors for context. This is a significant improvement over the previous approach.


1264-1279: Excellent: Platform-aware MAPI comparison with providerSpec handling.

This is a key improvement in the refactoring. The differ configuration correctly:

  • Uses platform-specific providerSpec conversion via WithProviderSpec (line 1266)
  • Ignores controller-managed status fields that should not affect sync decisions (lines 1268-1271)
  • Enables deterministic, structured field-by-field comparison

The mapi2capi.ProviderSpecFromRawExtension converter ensures platform-specific providerSpec schemas are handled correctly.


1411-1434: LGTM: MAPI spec update correctly includes providerSpec changes.

The refactored logic properly checks for HasProviderSpecChanges() in addition to metadata and spec changes (line 1415). This is essential for MAPI machines since their providerSpec field contains platform-specific configuration that requires special handling during comparison.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Oct 31, 2025

@chrischdi: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/okd-scos-e2e-aws-ovn dbbfcd3 link false /test okd-scos-e2e-aws-ovn

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 31, 2025

@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Summary by CodeRabbit

  • Refactor
  • Consolidated diffing into a unified, platform-aware differ with per-block change checks and improved error propagation; removed legacy map-based diffs.
  • New Features
  • Added a reusable DiffResult interface (metadata/spec/providerSpec/status checks), platform-aware providerSpec handling, and deterministic AWS provider tag ordering.
  • Tests
  • Added unit tests covering diff behaviors, list/nested changes, and condition timestamp handling.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
pkg/util/diff.go (1)

130-138: Fix the unstructured conversion and error message.

runtime.DefaultUnstructuredConverter.ToUnstructured expects the concrete object, not a pointer to the interface wrapper. By passing &a / &b, you're handing it values of type *interface{}, which the converter cannot handle, so every diff involving typed structs will error out. Additionally, the first error message incorrectly says "convert b" when it should say "convert a".

Apply this diff:

-	unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&a)
+	unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(a)
 	if err != nil {
-		return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
+		return nil, fmt.Errorf("failed to convert a to unstructured: %w", err)
 	}

-	unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&b)
+	unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(b)
 	if err != nil {
 		return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
 	}
pkg/controllers/machinesetsync/machineset_sync_controller.go (1)

742-745: Critical logic error: Inverted condition.

The condition if capiInfraMachineTemplatesDiff.HasChanges() is inverted. When HasChanges() returns true, it means there ARE changes detected, but the code logs "No changes detected" and returns early, skipping the update. This will prevent legitimate updates from being applied.

Apply this fix:

-	if capiInfraMachineTemplatesDiff.HasChanges() {
+	if !capiInfraMachineTemplatesDiff.HasChanges() {
 		logger.Info("No changes detected for CAPI infra machine template")
 		return nil
 	}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between dbbfcd3 and b2008f4.

📒 Files selected for processing (4)
  • pkg/controllers/machinesetsync/machineset_sync_controller.go (11 hunks)
  • pkg/conversion/mapi2capi/aws.go (2 hunks)
  • pkg/conversion/mapi2capi/util.go (1 hunks)
  • pkg/util/diff.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/conversion/mapi2capi/util.go
🔇 Additional comments (3)
pkg/conversion/mapi2capi/aws.go (1)

440-445: LGTM! Deterministic tag sorting.

Sorting tags by name after unmarshalling ensures consistent ordering when converting between MAPI slice and CAPI map representations, reducing non-deterministic diffs. This pairs well with the new platform-aware differ framework.

pkg/controllers/machinesetsync/machineset_sync_controller.go (2)

787-790: LGTM! Improved error handling.

Now properly capturing and propagating errors from compareCAPIMachineSets, preventing silent comparison failures. The error wrapping provides clear context for debugging.


1383-1399: LGTM! Platform-aware MAPI diff implementation.

The implementation correctly:

  • Accepts platform for platform-specific providerSpec handling
  • Uses WithProviderSpec to extract and diff providerSpec separately (enabling HasProviderSpecChanges() checks)
  • Ignores status fields that are managed independently (observedGeneration, authoritativeAPI, synchronizedGeneration)
  • Properly wraps and returns errors

This aligns well with the broader refactoring to generalized, platform-aware diffing.

"sigs.k8s.io/controller-runtime/pkg/client"
)

var errTimedOutWaitingForFeatureGates = errors.New("objects to diff cannot be nil")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the mismatched error variable name.

The variable name errTimedOutWaitingForFeatureGates doesn't match its message "objects to diff cannot be nil". This appears to be a copy-paste error.

Apply this diff:

-var errTimedOutWaitingForFeatureGates = errors.New("objects to diff cannot be nil")
+var errObjectsToCompareCannotBeNil = errors.New("objects to diff cannot be nil")

Then update line 126 accordingly:

 	if a == nil || b == nil {
-		return nil, errTimedOutWaitingForFeatureGates
+		return nil, errObjectsToCompareCannotBeNil
 	}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In pkg/util/diff.go around line 33, the error variable name
errTimedOutWaitingForFeatureGates is mismatched with its message "objects to
diff cannot be nil"; rename the variable to a matching identifier (for example
errObjectsToDiffCannotBeNil) and update any usages (including the reference at
line 126) to use the new variable name so the identifier and message are
consistent.

}

if err := modifyFunc(unstructuredB); err != nil {
return nil, fmt.Errorf("failed to run modify function %son b: %w", funcName, err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add missing space in error message format string.

The format string is missing a space between %s and on b, which will produce error messages like "ProviderSpecon b" instead of "ProviderSpec on b".

Apply this diff:

-		if err := modifyFunc(unstructuredB); err != nil {
-			return nil, fmt.Errorf("failed to run modify function %son b: %w", funcName, err)
+		if err := modifyFunc(unstructuredB); err != nil {
+			return nil, fmt.Errorf("failed to run modify function %s on b: %w", funcName, err)
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return nil, fmt.Errorf("failed to run modify function %son b: %w", funcName, err)
if err := modifyFunc(unstructuredB); err != nil {
return nil, fmt.Errorf("failed to run modify function %s on b: %w", funcName, err)
}
🤖 Prompt for AI Agents
In pkg/util/diff.go around line 151, the error format string is missing a space
between the %s and "on b"; update the format string from "%son b: %w" to "%s on
b: %w" so the error message prints "ProviderSpec on b" instead of
"ProviderSpecon b".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants