Skip to content

bugfix: ensure collision detection runs before revision linearity check#501

Merged
thetechnick merged 1 commit intopackage-operator:mainfrom
perdasilva:boxcutter-fix
Mar 31, 2026
Merged

bugfix: ensure collision detection runs before revision linearity check#501
thetechnick merged 1 commit intopackage-operator:mainfrom
perdasilva:boxcutter-fix

Conversation

@perdasilva
Copy link
Copy Markdown
Contributor

Summary

  • Fixes a bug where a resource owned by a different parent object at a higher revision was reported as ActionProgressed instead of ActionCollision
  • Moves the revision linearity check from a top-level early return into the ctrlSituationIsController and ctrlSituationPreviousIsController switch cases, ensuring ctrlSituationUnknownController and ctrlSituationNoController always evaluate collision protection regardless of revision
  • Adds test coverage for cross-owner collision scenarios with higher revisions

Details

The revision linearity check in objectUpdateHandling() (actualObjectRevision > revision) was evaluated before the ownership-based collision protection switch statement. This meant that if Revision 2 (Parent-B) created a CRD and Revision 1 (Parent-A) later reconciled the same CRD, the check would short-circuit and return ActionProgressed before collision detection could run.

The fix restricts the linearity check to the two cases where it is safe to skip reconciliation:

  • ctrlSituationIsController: we already own the object
  • ctrlSituationPreviousIsController: a known previous owner has it

Test plan

  • Existing "Progressed" test scoped to withNativeOwnerMode only
  • Added "Progressed - previousOwner higher revision" test — verifies ActionProgressed for known lineage
  • Added "Collision - unknown controller higher revision" test — verifies ActionCollision for cross-owner conflict
  • Added "Collision - no controller higher revision boxcutter managed" test — verifies ActionCollision for orphaned boxcutter-managed objects
  • All existing tests continue to pass

🤖 Generated with Claude Code

@perdasilva perdasilva requested a review from a team as a code owner March 27, 2026 13:51
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c06b0ef4-8ca8-4105-b9c5-7a390a605338

📥 Commits

Reviewing files that changed from the base of the PR and between ef8929e and cb43b20.

📒 Files selected for processing (2)
  • machinery/objects.go
  • machinery/objects_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • machinery/objects_test.go
  • machinery/objects.go

Walkthrough

Removed the top-level early-return for revision linearity in objectUpdateHandling; revision checks are now applied inside specific ctrlSituation branches and a second post-switch linearity check was added. Tests updated to refine the "Progressed" case and add three collision/progression scenarios.

Changes

Cohort / File(s) Summary
Revision Linearity Logic
machinery/objects.go
Removed the global early actualObjectRevision > revision return; moved revision-linearity handling into ctrlSituation branches (controller / previous-controller / unknown / no-controller) and added a second post-switch check before setObjectRevision and owner/controller handoff. Comments updated to match new flow.
Test Case Coverage
machinery/objects_test.go
Adjusted TestObjectEngine cases: removed //nolint:dupl on some shared cases, restricted "Progressed" to native owner mode, and added three new cases covering: previous-owner higher revision (progressed), unknown-controller higher revision (collision), and no-controller higher revision with boxcutter-managed label (collision). Mocks extended to return provided actualObject and empty ddm comparison.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main bug fix: ensuring collision detection runs before the revision linearity check in the control flow.
Description check ✅ Passed The description provides a clear summary, details the root cause and fix, and documents the test plan. However, it lacks the formal 'Change Type' section and checklist from the required template.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.26%. Comparing base (2c0e56b) to head (cb43b20).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #501      +/-   ##
==========================================
+ Coverage   72.22%   72.26%   +0.03%     
==========================================
  Files          35       35              
  Lines        3017     3021       +4     
==========================================
+ Hits         2179     2183       +4     
  Misses        712      712              
  Partials      126      126              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The revision linearity check in objectUpdateHandling() was evaluated
before collision protection, causing a resource owned by a different
parent object at a higher revision to be reported as ActionProgressed
instead of ActionCollision.

For example, if Revision 2 (Parent-B) created a CRD and Revision 1
(Parent-A) later reconciled the same CRD, the check
`actualObjectRevision > revision` would short-circuit and return
ActionProgressed before the ownership-based collision detection in the
switch statement could run.

The fix moves the revision linearity check from the top-level early
return into the two switch cases where skipping reconciliation is safe:
- ctrlSituationIsController: we already own the object
- ctrlSituationPreviousIsController: a known previous owner has it

The ctrlSituationUnknownController and ctrlSituationNoController cases
now always evaluate their collision protection logic regardless of the
object's revision, ensuring that cross-owner conflicts are properly
detected as ActionCollision.

Test updates:
- Scoped existing "Progressed" test to withNativeOwnerMode (the
  withoutOwnerMode case now correctly evaluates collision protection)
- Added "Progressed - previousOwner higher revision" test
- Added "Collision - unknown controller higher revision" test
- Added "Collision - no controller higher revision boxcutter managed"
  test

Signed-off-by: Per G. da Silva <pegoncal@redhat.com>
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@machinery/objects.go`:
- Around line 325-329: The current eager call to
e.getObjectRevision(actualObject) makes all paths depend on revision parsing and
causes unrelated malformed revisions to short-circuit objectUpdateHandling;
remove the early call and the actualObjectRevision variable, and instead call
getActualObjectRevision (or e.getObjectRevision) lazily only inside the branches
ctrlSituationIsController and ctrlSituationPreviousIsController where the
revision is actually needed; update those two handlers to perform the revision
parsing and handle/return errors locally so that ctrlSituationUnknownController
and ctrlSituationNoController can run and report ActionCollision without being
blocked by revision parse errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6fbf07da-cce1-4d06-93c4-3cac3047a407

📥 Commits

Reviewing files that changed from the base of the PR and between 2c0e56b and ef8929e.

📒 Files selected for processing (2)
  • machinery/objects.go
  • machinery/objects_test.go

Comment thread machinery/objects.go
Comment on lines +325 to 329
// Get actual revision to ensure revision linearity
actualObjectRevision, err := e.getObjectRevision(actualObject)
if err != nil {
return nil, fmt.Errorf("getting revision of object: %w", err)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Defer revision parsing until a branch actually needs it.

Line 325 still makes cross-owner paths depend on parsing the revision annotation. If an unrelated object carries a malformed boxcutter revision value, objectUpdateHandling() returns "getting revision of object" before ctrlSituationUnknownController / ctrlSituationNoController can report ActionCollision.

💡 Proposed fix
-	// Get actual revision to ensure revision linearity
-	actualObjectRevision, err := e.getObjectRevision(actualObject)
-	if err != nil {
-		return nil, fmt.Errorf("getting revision of object: %w", err)
-	}
+	getActualObjectRevision := func() (int64, error) {
+		actualObjectRevision, err := e.getObjectRevision(actualObject)
+		if err != nil {
+			return 0, fmt.Errorf("getting revision of object: %w", err)
+		}
+		return actualObjectRevision, nil
+	}

Then call getActualObjectRevision() only inside ctrlSituationIsController and ctrlSituationPreviousIsController.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@machinery/objects.go` around lines 325 - 329, The current eager call to
e.getObjectRevision(actualObject) makes all paths depend on revision parsing and
causes unrelated malformed revisions to short-circuit objectUpdateHandling;
remove the early call and the actualObjectRevision variable, and instead call
getActualObjectRevision (or e.getObjectRevision) lazily only inside the branches
ctrlSituationIsController and ctrlSituationPreviousIsController where the
revision is actually needed; update those two handlers to perform the revision
parsing and handle/return errors locally so that ctrlSituationUnknownController
and ctrlSituationNoController can run and report ActionCollision without being
blocked by revision parse errors.

Copy link
Copy Markdown
Contributor

@camilamacedo86 camilamacedo86 left a comment

Choose a reason for hiding this comment

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

/lgtm

@thetechnick thetechnick merged commit 42098ac into package-operator:main Mar 31, 2026
4 checks passed
camilamacedo86 added a commit to camilamacedo86/operator-controller that referenced this pull request Apr 9, 2026
Boxcutter v0.13.1 includes the fix from package-operator/boxcutter#501 which ensures collision detection runs before revision linearity checks.

This allows us to remove the foreignRevisionController workaround that was manually detecting ActionProgressed objects owned by foreign ClusterExtensions.

Assisted-by: Claude
openshift-merge-bot Bot pushed a commit to operator-framework/operator-controller that referenced this pull request Apr 9, 2026
#2637)

Boxcutter v0.13.1 includes the fix from package-operator/boxcutter#501 which ensures collision detection runs before revision linearity checks.

This allows us to remove the foreignRevisionController workaround that was manually detecting ActionProgressed objects owned by foreign ClusterExtensions.

Assisted-by: Claude
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants