-
Notifications
You must be signed in to change notification settings - Fork 49
OCPCLOUD-3172: machinesetsync: refactor to a generalized differ which can work independent of types #382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@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. |
|
Skipping CI for Draft Pull Request. |
|
Note
|
| Cohort / File(s) | Summary |
|---|---|
Diff Utility Implementation pkg/util/diff.go |
Adds a pluggable differ and public DiffResult interface with HasChanges/HasMetadataChanges/HasSpecChanges/HasProviderSpecChanges/HasStatusChanges; supports ignore paths, LastTransitionTime ignoring, providerSpec extraction, and deterministic per-top-level-key diffs. |
Diff Tests & Suite pkg/util/diff_test.go, pkg/util/suite_test.go |
Adds Ginkgo/Gomega tests and a test suite entry validating differ behavior, HasChanges, and string output. |
MachineSet Sync Controller pkg/controllers/machinesetsync/machineset_sync_controller.go, pkg/controllers/machinesetsync/machineset_sync_controller_test.go, pkg/controllers/machinesetsync/machineset_sync_controller_unit_test.go |
Replaces map-based diffs with util.DiffResult for CAPI/MAPI MachineSets and infra templates; compare functions become platform-aware; ensure*/status methods accept DiffResult and use Has* checks; tests updated and new unit tests added. |
Machine Sync Controller pkg/controllers/machinesync/machine_sync_controller.go, pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go |
compareCAPIMachines/compareMAPIMachines and related ensure/update functions now use (util.DiffResult, error) and platform-aware providerSpec diffing; call sites updated; deep-based helpers and imports removed; minor rename fix for validateMAPIToCAPIPlatformSpecifics. |
Sync Helpers Removed pkg/util/sync.go |
Removes legacy specialized comparison helpers and condition comparators; drops dependency on github.com/go-test/deep. |
ProviderSpec Conversion pkg/conversion/mapi2capi/util.go |
Adds ProviderSpecFromRawExtension(platform, rawExtension) with platform dispatch (AWS implemented) and a sentinel error errUnsupportedPlatform. |
Provider Determinism Fixes pkg/conversion/mapi2capi/aws.go, pkg/conversion/mapi2capi/openstack.go |
Adds deterministic sorting (AWS Tags, OpenStack metadata) after unmarshalling to ensure stable conversions. |
Controller Tests & Contexts pkg/controllers/machinesync/machine_sync_controller_test.go, pkg/controllers/machinesetsync/*_test.go |
Tests updated to new platform-aware signatures and DiffResult API; new MAPI sync test contexts and machineset unit tests added/adjusted. |
Sequence Diagram(s)
sequenceDiagram
autonumber
participant Reconciler
participant Converter
participant Differ as "pkg/util differ"
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/surface error
else Diff computed
Differ-->>Reconciler: DiffResult (HasChanges, HasSpec/Meta/Status/ProviderSpec)
alt Spec/Metadata/ProviderSpec changes
Reconciler->>API: patch/update spec/metadata
end
alt Status changes (observedGeneration guard)
Reconciler->>API: patch/update status
end
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
- Review focus:
- ProviderSpec extraction plumbing and platform dispatch (WithProviderSpec + ProviderSpecFromRawExtension).
- removeConditionsLastTransitionTime and ignore rules across API versions.
- All updated function signatures and call sites to ensure no remaining
map[string]anyassumptions. - Deterministic diff string formatting and AWS/OpenStack sorting changes.
- New/updated tests for correctness and flakiness.
Poem
🐰 I hopped through fields both old and new,
I turned loose maps into a structured view.
I hushed the times that softly sway,
I 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 title clearly and specifically describes the main refactoring effort: replacing legacy map-based diffs with a generalized, type-independent differ system, which is the primary focus of all changes across the codebase. |
| Docstring Coverage | ✅ Passed | Docstring coverage is 81.82% 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
📜 Recent 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
📒 Files selected for processing (3)
pkg/controllers/machinesync/machine_sync_controller.go(9 hunks)pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go(9 hunks)pkg/util/diff.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/util/diff.go
🔇 Additional comments (12)
pkg/controllers/machinesync/machine_sync_mapi2capi_infrastructure.go (4)
32-32: LGTM: Import addition is necessary.The
clusterv1import is correctly added to support theclusterv1.PausedV1Beta2Conditionconstant used in the differ configuration.
67-67: LGTM: Function signature updates are consistent.The migration from map-based diffs to
util.DiffResultwith per-block checking methods (HasSpecChanges(),HasMetadataChanges(),HasStatusChanges()) is implemented correctly across all call sites.Also applies to: 76-76, 125-125, 129-129, 150-150, 154-154
88-90: LGTM: Log message clarification.The log messages now consistently refer to "Cluster API Infrastructure machine" which improves clarity and distinguishes from CAPI Machine objects.
249-305: LGTM: Refactored differ implementation is clean and consistent.The migration to
util.NewDefaultDiffer()simplifies the comparison logic while maintaining platform-specific type handling. The configuration correctly ignoresPausedV1Beta2Conditionsince it's managed by CAPI controllers.pkg/controllers/machinesync/machine_sync_controller.go (8)
484-484: LGTM: Typo correction in function name.The function name has been corrected from
validateMAPIToCAPIPlatfromSpecificstovalidateMAPIToCAPIPlatformSpecifics. Both the definition and call site are properly updated.Also applies to: 600-608
679-705: LGTM: CAPI machine spec update logic is correct.The function correctly uses
HasMetadataChanges()andHasSpecChanges()for CAPI machines. Note thatHasProviderSpecChanges()is not checked here, which is appropriate since CAPI machines useInfrastructureRefrather than an embeddedproviderSpec.
728-731: LGTM: Error handling added for compare operation.The error handling for
compareCAPIMachinesis correctly implemented with proper error wrapping and propagation.
782-785: LGTM: Platform parameter correctly added.The
compareMAPIMachinescall now correctly passes the platform parameter to enable platform-aware providerSpec diffing.
1295-1305: LGTM: Simplified CAPI machine comparison.The refactored
compareCAPIMachinesfunction cleanly leverages the unified differ and correctly ignores thePausedV1Beta2Conditionwhich is managed by CAPI controllers.
1308-1325: LGTM: Platform-aware MAPI machine comparison is well-configured.The differ configuration correctly:
- Extracts and compares platform-specific providerSpecs using the path
["spec", "providerSpec", "value"]- Ignores MAPI-specific status fields managed by other controllers (
providerStatus,lastOperation,authoritativeAPI,synchronizedGeneration)- Ignores the
Synchronizedcondition managed by the migration controller
1328-1377: LGTM: CAPI machine status update logic is sound.The function correctly uses
HasStatusChanges()and preserves the generation observation guard to ensure the MAPI machine status has caught up before updating CAPI status.
1409-1454: LGTM: MAPI machine update logic correctly handles providerSpec.Both functions properly use the per-block diff methods:
ensureMAPIMachineStatusUpdatedchecksHasStatusChanges()ensureMAPIMachineSpecUpdatedchecksHasMetadataChanges(),HasSpecChanges(), andHasProviderSpecChanges()Note that
HasProviderSpecChanges()is correctly checked for MAPI machines (which have embedded providerSpecs) but not for CAPI machines (which use InfrastructureRef).Also applies to: 1457-1480
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
Tip
📝 Customizable high-level summaries are now available in beta!
You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
- Provide your own instructions using the
high_level_summary_instructionssetting. - Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
- Use
high_level_summary_in_walkthroughto move the summary from the description to the walkthrough section.
Example instruction:
"Divide the high-level summary into five sections:
- 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
- 📓 References — List relevant issues, discussions, documentation, or related PRs.
- 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
- 📊 Contributor Summary — Include a Markdown table showing contributions:
| Contributor | Lines Added | Lines Removed | Files Changed |- ✔️ Additional Notes — Add any extra reviewer context.
Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.
Comment @coderabbitai help to get the list of available commands and usage tips.
|
/test unit |
mdbooth
left a comment
There was a problem hiding this 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
| 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"}}, | ||
| } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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"),
)There was a problem hiding this comment.
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.
pkg/util/diff.go
Outdated
| "k8s.io/apimachinery/pkg/runtime" | ||
| ) | ||
|
|
||
| type UnstructuredDiffer[T any] struct { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
| // 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 | ||
| // }), | ||
| // ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer this
There was a problem hiding this comment.
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
| 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"}}, | ||
| } |
There was a problem hiding this comment.
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?
ac815fd to
f494b0c
Compare
|
@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. |
| // 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() | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
:-)
| status1 = typedInfraMachineTemplate1.Status | ||
| status2 = typedinfraMachineTemplate2.Status |
There was a problem hiding this comment.
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 :-)
There was a problem hiding this 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
errAssertingCAPIIBMPowerVSMachineTemplateon a type mismatch. Callers that rely onerrors.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 skipgot.String()comparisons whentt.wantChangesis false, so a future regression whereChanged()returns false butString()leaks content would slide through. Consider moving theg.Expect(got.String()).To(Equal(tt.want))assertion outside the conditional (the happy-path cases already setwantto""), or duplicating it in theelse. 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
📒 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.
| // 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() | ||
| } |
There was a problem hiding this comment.
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[", "[") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Examples:
Input:
a := &awsv1.AWSMachine{
Spec: awsv1.AWSMachineSpec{
ProviderID: ptr.To("before-change"),
InstanceID: ptr.To("removed"),
// ImageLookupOrg: "added",
},
}
b := &awsv1.AWSMachine{
Spec: awsv1.AWSMachineSpec{
ProviderID: ptr.To("after-change"),
// InstanceID: ptr.To("removed"),
ImageLookupOrg: "added",
},
}Before:
[Spec.ProviderID: after-change != before-change Spec.InstanceID: <nil pointer> != string Spec.ImageLookupOrg: added != ]
After:
.[spec].[imageLookupOrg]: added != <does not have key>, .[spec].[instanceID]: <does not have key> != removed, .[spec].[providerID]: after-change != before-change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Main reason why I did the [] thing was e.g. label keys, because they have dots.
|
@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. |
|
@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. |
There was a problem hiding this 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.ToUnstructuredexpects the actual object. Passing&a/&bhands 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
📒 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()andHasChanges()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, andHasStatusChangeskeeps 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 newHas*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
compareCAPIMachinesandcompareMAPIMachineshave been properly refactored to:
- Use the new
util.NewDefaultDiffer()interface- Return structured
util.DiffResultinstead of maps- Propagate errors from diff computation
- Support platform-aware provider spec comparison (MAPI machines)
The platform-aware diffing with
WithProviderSpecand field ignore options incompareMAPIMachinesis a solid improvement for handling provider-specific differences.
1282-1331: LGTM! Status update functions refactored correctly.Both
ensureCAPIMachineStatusUpdatedandensureMAPIMachineStatusUpdatedhave been consistently updated to acceptutil.DiffResultand use theHasStatusChanges()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*Updatedfunctions have been consistently updated to:
- Accept
util.DiffResultinstead 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
compareCAPIInfraMachineTemplatesandcompareMAPIMachineSetswith support for provider specs and field filtering is a significant improvement.
|
@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. |
a9987c6 to
dbbfcd3
Compare
|
@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. |
There was a problem hiding this 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()returnstrue(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
ProviderSpecFromRawExtensionis 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 onerrors.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
obj1andobj2totypedInfraMachine1andtypedInfraMachine2for 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
📒 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
compareMAPIMachineSetssignature and use the newDiffResultmethods (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
DiffResultmethods (HasMetadataChanges(),HasSpecChanges(),HasProviderSpecChanges()) to determine when spec updates are needed.
869-875: Correct status update gating logic.The function properly uses
HasStatusChanges()and thespecUpdatedflag 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(), andHasProviderSpecChanges()to detect when updates are needed.
947-953: Status update logic correctly adapted.The function properly integrates with the new
DiffResultAPI, usingHasStatusChanges()and thespecUpdatedflag to gate status updates appropriately.
1021-1024: Platform-aware diff comparison properly integrated.The call to
compareMAPIMachineSetscorrectly 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 usesHasMetadataChanges()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()andcapiMachinesDiff.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
compareCAPIMachinessignature, with proper error wrapping.
751-754: LGTM: Platform parameter enables platform-aware diffing.The addition of the
r.Platformparameter 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.ProviderSpecFromRawExtensionconverter 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.
|
@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. |
|
/retest |
|
@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue. 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. |
|
/pipeline required |
|
Scheduling tests matching the |
|
/retest |
2 similar comments
|
/retest |
|
/retest |
|
Seems like a permafailing job, also for other PRs. /retest |
|
/retest |
|
@chrischdi |
32ead67 to
53d4007
Compare
There was a problem hiding this 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
🧹 Nitpick comments (2)
pkg/util/diff.go (2)
128-200: Diff pipeline is correct; consider future-proofing modifyFuncs orderingThe overall Diff flow (nil/type checks → unstructured conversion → apply
modifyFuncs→ applyignoredPath→ per-top-level-keydeep.Equalwith sorted entries) looks correct and should be deterministic at the result level.One thing to keep in mind:
modifyFuncsis a map, so the order in which modifiers run is unspecified. Today the modifiers (RemoveConditionsLastTransitionTime,ProviderSpec) are independent, so this is fine, but if future options introduce dependencies between modifiers, the order could start to matter. If that ever happens, consider switchingmodifyFuncsto a slice (or a slice-backed registry) to make the execution order explicit.
275-305: Clarify/confirm semantics when providerSpec is missing
WithProviderSpeccurrently treats a missing providerSpec (!found) as an error (errProviderSpecNotFound), which will causeDiffto fail rather than just report a diff when one side has a providerSpec and the other does not.That’s reasonable if all objects passed to a
differconfigured withWithProviderSpecare expected to always have a providerSpec (and missing it is a programming/config bug). If there are legitimate cases where one or both sides may not have a providerSpec, you might want to instead:
- treat “both missing” as a no-op, and/or
- treat “present vs missing” as a regular diff (e.g., by promoting only the present one to
providerSpecPathand lettingdeep.Equalcomparenilvs value).If the “must-exist” assumption is intentional for all current call sites, a short comment on
WithProviderSpecto document that contract would help future readers.
📜 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
📒 Files selected for processing (2)
pkg/util/diff.go(1 hunks)pkg/util/diff_test.go(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/util/diff_test.go
🔇 Additional comments (1)
pkg/util/diff.go (1)
40-116: DiffResult representation and String() output look solidThe split of
Has*Changesby top-level key plus a deterministicString()(sorting and normalizingmap[/slice[prefixes) gives a clear, stable surface for controllers and tests. I don't see correctness issues here, and the behavior matches the examples in the PR discussion.
|
/lgtm Readding to trigger E2Es. Let's see their output |
|
Scheduling tests matching the |
…and Synchronized for MAPI
|
/pipeline required |
|
Scheduling tests matching the |
|
@chrischdi: This pull request references OCPCLOUD-3172 which is a valid jira issue. 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. |
|
/lgtm The openshift e2es are painfully flakey. If you've had them green previously, and the only changes are rebase + adding tests (or they're failing on different flakes each time) I'd override them. |
|
Tests from second stage were triggered manually. Pipeline can be controlled only manually, until HEAD changes. Use command to trigger second stage. |
|
@chrischdi: The following tests failed, say
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. |
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.