Skip to content

Adds selective owner reference removal - #2601

Draft
raaizik wants to merge 2 commits into
RamenDR:mainfrom
raaizik:selective-ownerref-removal
Draft

Adds selective owner reference removal#2601
raaizik wants to merge 2 commits into
RamenDR:mainfrom
raaizik:selective-ownerref-removal

Conversation

@raaizik

@raaizik raaizik commented Jun 11, 2026

Copy link
Copy Markdown
Member

Resolves #2600

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where removing a specific owner reference could inadvertently clear all owner references. Now only the targeted owner reference is removed; other references are preserved. The operation is a no-op and returns false when no matching reference exists, improving reliability and preventing unintended loss of related ownership metadata.

Signed-off-by: raaizik <132667934+raaizik@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c1535fd0-0f1f-4634-beaa-38e7a545ef15

📥 Commits

Reviewing files that changed from the base of the PR and between 81a2b59 and 4de9c63.

📒 Files selected for processing (1)
  • internal/controller/util/misc.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/util/misc.go
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: e2e-rdr
  • GitHub Check: drenv (ubuntu-24.04, 3.14-dev)
  • GitHub Check: drenv (ubuntu-24.04, 3.10)
  • GitHub Check: drenv (ubuntu-24.04, 3.13)
  • GitHub Check: drenv (ubuntu-24.04, 3.11)
  • GitHub Check: drenv (ubuntu-24.04, 3.12)
  • GitHub Check: Unit tests
  • GitHub Check: Go compatibility
  • GitHub Check: Golangci Lint (.)
  • GitHub Check: Build image

📝 Walkthrough

Walkthrough

The RemoveOwnerReference function in internal/controller/util/misc.go was changed to short-circuit when no ownerReferences exist, and otherwise filter the existing slice to remove only entries whose UID matches the provided owner, returning true only if at least one matching reference was removed.

Changes

Selective Owner Reference Removal

Layer / File(s) Summary
RemoveOwnerReference filtering implementation
internal/controller/util/misc.go
Function now filters currentOwnerRefs by owner UID, rebuilds the ownerReferences slice to exclude only the matching owner, sets the filtered slice via SetOwnerReferences, and returns whether any reference was actually removed instead of clearing all references unconditionally.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested reviewers

  • ShyamsundarR
  • BenamarMk
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Adds selective owner reference removal' directly summarizes the main change in the pull request, which modifies RemoveOwnerReference to selectively remove matching owner references instead of clearing all of them.
Linked Issues check ✅ Passed The pull request implements all coding requirements from issue #2600: selective removal by UID matching, preservation of non-matching references, proper return value logic, and removal of the TODO comment.
Out of Scope Changes check ✅ Passed All changes are focused on the RemoveOwnerReference function in internal/controller/util/misc.go, directly addressing the requirements specified in issue #2600 with no unrelated modifications.

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

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@raaizik
raaizik force-pushed the selective-ownerref-removal branch from 37f71be to 81a2b59 Compare June 11, 2026 14:18
Remove only the specified owner's reference by matching Name and UID.
Follows Kubernetes standard approach while working within metav1.Object
interface constraints. Preserves other ownerReferences for proper
garbage collection.

Resolves RamenDR#2600

Signed-off-by: raaizik <132667934+raaizik@users.noreply.github.com>
@raaizik
raaizik force-pushed the selective-ownerref-removal branch from 81a2b59 to 4de9c63 Compare June 11, 2026 14:59

@nirs nirs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use Fixes #2600 - why introduce a new way?

}

newOwnerRefs = append(newOwnerRefs, ref)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We can replace the loop and variables with:

newOwnerRefs := slices.DeleteFunc(currentOwnerRefs, func(ref metav1.OwnerReference) bool {
    return ref.UID == ownerUID
})

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

slices.DeleteFunc reuses the underlying array and only adjusts the slice length. This can be problematic when modifying ownerReferences that may be referenced elsewhere in the code potentially leading to unexpected behavior. The current approach creates a new slice with its own backing array. Since we typically have O(1) ownerReferences per resource the performance difference is negligible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No code should reference the slice returned by obj.GetOwnerReferences() - if we have such code it should be fixed. If code need to keep this is must copy the slice to other code cannot modify the slice. So modifying the slice here is safe - it will not lead to unexpected behavior.

The main motivation for using slices.DeleteFunc() is to avoid the manual code creating a new slices. We should use go standard library for common operations instead of reinvent them everywhere.

This will minimize the code we need to maintain and minimize the executable size and memory usage.

In case when we must clone the slice because it is references elsewhere we can use slices.Clone().


return true, nil
newOwnerRefs := []metav1.OwnerReference{}
removed := false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The flag is not needed - we can return:

return len(currentOwnerRefs) != len(newOwnerRef), nil

@nirs

nirs commented Jun 11, 2026

Copy link
Copy Markdown
Member

A trivial unit tests can be useful, checking that we handle all cases:

  • nil owner refs
  • empty slice
  • owner ref in slice
  • owner ref appear multiple times in slice (should not happen but all entries should be removed)
  • owner ref not in slice

@raaizik

raaizik commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

We use Fixes #2600 - why introduce a new way?

@nirs
Both "resolves" and "fixes" work...

Development

Successfully merging this pull request may close these issues.

@nirs

nirs commented Jun 14, 2026

Copy link
Copy Markdown
Member

We use Fixes #2600 - why introduce a new way?

@nirs Both "resolves" and "fixes" work...

Yes, we have 9 options: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue

The reason to use Fixes is that this is the convention in the current code, and it makes it easier to inspect git log.

% git log | grep Fixes | wc -l
     127
% git log | grep Resolves | wc -l
       4

My preferences is:

Fixes: #NNN

This way we can use tools that extract trailers from git messages to get metadata.

@nirs

nirs commented Jun 16, 2026

Copy link
Copy Markdown
Member

@raaizik Fixing removal of owner references is nice - but all 3 builds failed with this change int the same way:

https://github.com/RamenDR/ramen/actions/runs/27356176128/job/80830943357

--- FAIL: TestDR (6.14s)
    --- PASS: TestDR/subscr-deploy-rbd (467.78s)
        --- PASS: TestDR/subscr-deploy-rbd/Deploy (10.20s)
        --- PASS: TestDR/subscr-deploy-rbd/Enable (95.56s)
        --- PASS: TestDR/subscr-deploy-rbd/Failover (180.32s)
        --- PASS: TestDR/subscr-deploy-rbd/Relocate (130.24s)
        --- PASS: TestDR/subscr-deploy-rbd/Disable (45.32s)
        --- PASS: TestDR/subscr-deploy-rbd/Undeploy (6.15s)
    --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd (496.41s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Deploy (6.81s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Enable (95.20s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Failover (205.87s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Relocate (123.31s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Disable (56.48s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Undeploy (8.74s)
    --- PASS: TestDR/disapp-deploy-rbd (501.43s)
        --- PASS: TestDR/disapp-deploy-rbd/Deploy (11.70s)
        --- PASS: TestDR/disapp-deploy-rbd/Enable (95.19s)
        --- PASS: TestDR/disapp-deploy-rbd/Failover (179.64s)
        --- PASS: TestDR/disapp-deploy-rbd/Relocate (156.15s)
        --- PASS: TestDR/disapp-deploy-rbd/Disable (50.40s)
        --- PASS: TestDR/disapp-deploy-rbd/Undeploy (8.35s)
    --- PASS: TestDR/disapp-deploy-cephfs (677.62s)
        --- PASS: TestDR/disapp-deploy-cephfs/Deploy (6.79s)
        --- PASS: TestDR/disapp-deploy-cephfs/Enable (120.26s)
        --- PASS: TestDR/disapp-deploy-cephfs/Failover (210.13s)
        --- PASS: TestDR/disapp-deploy-cephfs/Relocate (273.10s)
        --- PASS: TestDR/disapp-deploy-cephfs/Disable (57.48s)
        --- PASS: TestDR/disapp-deploy-cephfs/Undeploy (9.86s)
    --- PASS: TestDR/appset-deploy-rbd (706.79s)
        --- PASS: TestDR/appset-deploy-rbd/Deploy (10.13s)
        --- PASS: TestDR/appset-deploy-rbd/Enable (90.21s)
        --- PASS: TestDR/appset-deploy-rbd/Failover (360.71s)
        --- PASS: TestDR/appset-deploy-rbd/Relocate (200.33s)
        --- PASS: TestDR/appset-deploy-rbd/Disable (39.31s)
        --- PASS: TestDR/appset-deploy-rbd/Undeploy (6.10s)
    --- PASS: TestDR/subscr-deploy-cephfs (769.16s)
        --- PASS: TestDR/subscr-deploy-cephfs/Deploy (10.20s)
        --- PASS: TestDR/subscr-deploy-cephfs/Enable (125.39s)
        --- PASS: TestDR/subscr-deploy-cephfs/Failover (235.38s)
        --- PASS: TestDR/subscr-deploy-cephfs/Relocate (330.55s)
        --- PASS: TestDR/subscr-deploy-cephfs/Disable (60.50s)
        --- PASS: TestDR/subscr-deploy-cephfs/Undeploy (7.15s)
    --- FAIL: TestDR/appset-deploy-cephfs (1396.10s)
        --- PASS: TestDR/appset-deploy-cephfs/Deploy (10.13s)
        --- PASS: TestDR/appset-deploy-cephfs/Enable (65.17s)
        --- PASS: TestDR/appset-deploy-cephfs/Failover (420.79s)
        --- FAIL: TestDR/appset-deploy-cephfs/Relocate (900.00s)

https://github.com/RamenDR/ramen/actions/runs/27356176128/attempts/2?pr=2601

--- FAIL: TestDR (6.15s)
    --- PASS: TestDR/disapp-deploy-rbd (467.50s)
        --- PASS: TestDR/disapp-deploy-rbd/Deploy (6.93s)
        --- PASS: TestDR/disapp-deploy-rbd/Enable (90.24s)
        --- PASS: TestDR/disapp-deploy-rbd/Failover (184.46s)
        --- PASS: TestDR/disapp-deploy-rbd/Relocate (119.14s)
        --- PASS: TestDR/disapp-deploy-rbd/Disable (56.50s)
        --- PASS: TestDR/disapp-deploy-rbd/Undeploy (10.22s)
    --- PASS: TestDR/subscr-deploy-rbd (510.83s)
        --- PASS: TestDR/subscr-deploy-rbd/Deploy (20.22s)
        --- PASS: TestDR/subscr-deploy-rbd/Enable (95.28s)
        --- PASS: TestDR/subscr-deploy-rbd/Failover (180.45s)
        --- PASS: TestDR/subscr-deploy-rbd/Relocate (160.31s)
        --- PASS: TestDR/subscr-deploy-rbd/Disable (47.40s)
        --- PASS: TestDR/subscr-deploy-rbd/Undeploy (7.18s)
    --- PASS: TestDR/disapp-deploy-cephfs (617.29s)
        --- PASS: TestDR/disapp-deploy-cephfs/Deploy (6.95s)
        --- PASS: TestDR/disapp-deploy-cephfs/Enable (60.16s)
        --- PASS: TestDR/disapp-deploy-cephfs/Failover (210.34s)
        --- PASS: TestDR/disapp-deploy-cephfs/Relocate (271.85s)
        --- PASS: TestDR/disapp-deploy-cephfs/Disable (58.51s)
        --- PASS: TestDR/disapp-deploy-cephfs/Undeploy (9.48s)
    --- PASS: TestDR/appset-deploy-rbd (739.19s)
        --- PASS: TestDR/appset-deploy-rbd/Deploy (10.12s)
        --- PASS: TestDR/appset-deploy-rbd/Enable (95.30s)
        --- PASS: TestDR/appset-deploy-rbd/Failover (355.72s)
        --- PASS: TestDR/appset-deploy-rbd/Relocate (230.43s)
        --- PASS: TestDR/appset-deploy-rbd/Disable (40.38s)
        --- PASS: TestDR/appset-deploy-rbd/Undeploy (7.22s)
    --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd (742.71s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Deploy (12.06s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Enable (95.21s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Failover (386.70s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Relocate (174.57s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Disable (65.60s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Undeploy (8.57s)
    --- PASS: TestDR/subscr-deploy-cephfs (798.64s)
        --- PASS: TestDR/subscr-deploy-cephfs/Deploy (10.21s)
        --- PASS: TestDR/subscr-deploy-cephfs/Enable (95.24s)
        --- PASS: TestDR/subscr-deploy-cephfs/Failover (300.89s)
        --- PASS: TestDR/subscr-deploy-cephfs/Relocate (325.59s)
        --- PASS: TestDR/subscr-deploy-cephfs/Disable (59.57s)
        --- PASS: TestDR/subscr-deploy-cephfs/Undeploy (7.15s)
    --- FAIL: TestDR/appset-deploy-cephfs (1406.22s)
        --- PASS: TestDR/appset-deploy-cephfs/Deploy (20.16s)
        --- PASS: TestDR/appset-deploy-cephfs/Enable (95.27s)
        --- PASS: TestDR/appset-deploy-cephfs/Failover (390.78s)
        --- FAIL: TestDR/appset-deploy-cephfs/Relocate (900.00s)

https://github.com/RamenDR/ramen/actions/runs/27356176128/attempts/3?pr=2601

--- FAIL: TestDR (6.14s)
    --- PASS: TestDR/subscr-deploy-rbd (467.59s)
        --- PASS: TestDR/subscr-deploy-rbd/Deploy (10.20s)
        --- PASS: TestDR/subscr-deploy-rbd/Enable (90.21s)
        --- PASS: TestDR/subscr-deploy-rbd/Failover (180.35s)
        --- PASS: TestDR/subscr-deploy-rbd/Relocate (130.24s)
        --- PASS: TestDR/subscr-deploy-rbd/Disable (50.44s)
        --- PASS: TestDR/subscr-deploy-rbd/Undeploy (6.15s)
    --- PASS: TestDR/disapp-deploy-rbd (505.59s)
        --- PASS: TestDR/disapp-deploy-rbd/Deploy (12.82s)
        --- PASS: TestDR/disapp-deploy-rbd/Enable (95.19s)
        --- PASS: TestDR/disapp-deploy-rbd/Failover (209.41s)
        --- PASS: TestDR/disapp-deploy-rbd/Relocate (120.42s)
        --- PASS: TestDR/disapp-deploy-rbd/Disable (56.53s)
        --- PASS: TestDR/disapp-deploy-rbd/Undeploy (11.22s)
    --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd (686.43s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Deploy (12.94s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Enable (95.39s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Failover (385.95s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Relocate (124.58s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Disable (56.46s)
        --- PASS: TestDR/disapp-recipe-check-exec-deploy-rbd/Undeploy (11.12s)
    --- PASS: TestDR/disapp-deploy-cephfs (777.27s)
        --- PASS: TestDR/disapp-deploy-cephfs/Deploy (12.72s)
        --- PASS: TestDR/disapp-deploy-cephfs/Enable (95.21s)
        --- PASS: TestDR/disapp-deploy-cephfs/Failover (296.74s)
        --- PASS: TestDR/disapp-deploy-cephfs/Relocate (303.96s)
        --- PASS: TestDR/disapp-deploy-cephfs/Disable (56.40s)
        --- PASS: TestDR/disapp-deploy-cephfs/Undeploy (12.23s)
    --- PASS: TestDR/appset-deploy-rbd (799.19s)
        --- PASS: TestDR/appset-deploy-rbd/Deploy (10.12s)
        --- PASS: TestDR/appset-deploy-rbd/Enable (95.42s)
        --- PASS: TestDR/appset-deploy-rbd/Failover (360.59s)
        --- PASS: TestDR/appset-deploy-rbd/Relocate (270.43s)
        --- PASS: TestDR/appset-deploy-rbd/Disable (55.46s)
        --- PASS: TestDR/appset-deploy-rbd/Undeploy (7.17s)
    --- PASS: TestDR/subscr-deploy-cephfs (799.20s)
        --- PASS: TestDR/subscr-deploy-cephfs/Deploy (10.16s)
        --- PASS: TestDR/subscr-deploy-cephfs/Enable (95.55s)
        --- PASS: TestDR/subscr-deploy-cephfs/Failover (296.24s)
        --- PASS: TestDR/subscr-deploy-cephfs/Relocate (330.49s)
        --- PASS: TestDR/subscr-deploy-cephfs/Disable (59.57s)
        --- PASS: TestDR/subscr-deploy-cephfs/Undeploy (7.17s)
    --- FAIL: TestDR/appset-deploy-cephfs (1426.11s)
        --- PASS: TestDR/appset-deploy-cephfs/Deploy (10.10s)
        --- PASS: TestDR/appset-deploy-cephfs/Enable (95.22s)
        --- PASS: TestDR/appset-deploy-cephfs/Failover (420.79s)
        --- FAIL: TestDR/appset-deploy-cephfs/Relocate (900.00s)

This is not a random failure (happens from time to time). The old behavior of removing all owner references was correct, and the new behavior is not.

You can download the logs from the builds and inspect them. You should be able to reproduce the same in your local env.

@nirs nirs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All e2e builds failed - the change is wrong.

@raaizik

raaizik commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

Actually @BenamarMk that call site where the TODO is was meant to remove all OwnerRefs, not selectively. The VolSync cleanup functions (ReleasePVCOwnership and UnprotectVolSyncPVC) need to completely disown PVCs by removing all owner references. What can be done for clarity is the code could be refactored with two distinct utility functions:

RemoveAllOwnerReferences() - for complete removal (what those two sites need)
RemoveOwnerReference() - for selective removal

The selective removal logic already exists in vshandler's pruneOnerReferences() and could be consolidated into the utility function to eliminate duplication and make the intent explicit at each call site.

@nirs

nirs commented Jun 16, 2026

Copy link
Copy Markdown
Member

Actually @BenamarMk that call site where the TODO is was meant to remove all OwnerRefs, not selectively. The VolSync cleanup functions (ReleasePVCOwnership and UnprotectVolSyncPVC) need to completely disown PVCs by removing all owner references.

Is it possible that there are other refs that we don't control and we need to remove only our refs?

This can be solved by supporting multiple refs and remove all refs we manage:

RemoveOwnerReferences(uid string...) bool

If no other ref is expected - nobody should own our resources - using RemoveAll and RemoveOne sounds good.

If the current code need to remove all and we don't need yet selective remove we can just rename the current function which is wrong - it claims to remove one ref but it removes all.

@raaizik

raaizik commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

If the current code need to remove all and we don't need yet selective remove we can just rename the current function which is wrong - it claims to remove one ref but it removes all.

@nirs I just wrote that we do need selective removal, to quote myself:

The selective removal logic already exists in vshandler's pruneOnerReferences() and could be consolidated into the utility function to eliminate duplication and make the intent explicit at each call site.


// Always update ownerReferences to maintain consistency
// Return true only if we actually removed an owner
obj.SetOwnerReferences(newOwnerRefs)

@ELENAGER ELENAGER Jun 21, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need to update ownerReferences if no owner reference was removed? If no matching owner reference was found, then newOwnerRefs should be identical to currentOwnerRefs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We need to set only if we changed the value.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, but the comment says: "Always update ownerReferences to maintain consistency" and we do it always indeed.

@raaizik
raaizik marked this pull request as draft June 21, 2026 15:05
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.

Implement selective owner reference removal in RemoveOwnerReference()

4 participants