Skip to content

Preserve PVCs during disable DR - #2638

Open
raaizik wants to merge 2 commits into
RamenDR:mainfrom
raaizik:fix-disable-dr-pvc-cleanup
Open

Preserve PVCs during disable DR#2638
raaizik wants to merge 2 commits into
RamenDR:mainfrom
raaizik:fix-disable-dr-pvc-cleanup

Conversation

@raaizik

@raaizik raaizik commented Jul 1, 2026

Copy link
Copy Markdown
Member

PR #2602 skipped PVC disownership whenever the VRG was deleting, which is correct for workload teardown but breaks disable DR: with do-not-delete-pvc set, PVCs must survive while VRG/RS/RD/RGS/RGD are removed.

Fixes disable DR regression where PVCs entered Terminating because RS ownership was retained and deleted with the ReplicationSource.

Test results

E2E

--- PASS: TestDR (6.14s)
    --- PASS: TestDR/appset-deploy-cephfs (49.37s)
        --- PASS: TestDR/appset-deploy-cephfs/Disable (49.37s)
PASS
--- PASS: TestDR/disapp-deploy-cephfs/Disable
PASS

Manual checks shown for managed apps (same results for discovered apps)

kubectl --context dr1 get pvc -n $APP_NS \
  -o custom-columns=NAME:.metadata.name,PHASE:.status.phase,DELETING:.metadata.deletionTimestamp
kubectl --context dr1 get deploy,pod,vrg -n $APP_NS
NAME          PHASE   DELETING
busybox-pvc   Bound   <none>
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/busybox   1/1     1            1           6m51s
NAME                           READY   STATUS    RESTARTS   AGE
pod/busybox-54d5b75d6c-jkbql   1/1     Running   0          6m51s
kubectl --context dr1 logs -n ramen-system deploy/ramen-dr-cluster-operator | \
  grep -E 'skipPVCDisownership|Unprotecting VolSync PVC'
2026-07-02T10:33:15.585Z	INFO	vrg	controller/vrg_volsync.go:902	Unprotecting VolSync PVC	{"vrg": {"name":"appset-deploy-cephfs","namespace":"test-appset-deploy-cephfs"}, "rid": "03516936", "State": "primary", "Finalize": true, "PVC": "busybox-pvc", "skipPVCDisownership": false}
2026-07-02T10:33:15.585Z	INFO	vrg	volsync/vshandler.go:3355	Unprotecting VolSync PVC	{"vrg": {"name":"appset-deploy-cephfs","namespace":"test-appset-deploy-cephfs"}, "rid": "03516936", "pvcName": "busybox-pvc", "pvcNamespace": "test-appset-deploy-cephfs", "skipPVCDisownership": false}
kubectl --context dr1 get pvc -n $APP_NS busybox-pvc -o json | jq '{
  phase: .status.phase,
  deletionTimestamp: .metadata.deletionTimestamp,
  ownerReferences: .metadata.ownerReferences
}'
{
  "phase": "Bound",
  "deletionTimestamp": null,
  "ownerReferences": null
}
kubectl --context dr2 get pvc -n $APP_NS \
  -o custom-columns=NAME:.metadata.name,PHASE:.status.phase,DELETING:.metadata.deletionTimestamp
kubectl --context dr2 get deploy,pod,vrg -n $APP_NS
NAME          PHASE   DELETING
busybox-pvc   Bound   <none>
No resources found in test-appset-deploy-cephfs namespace.
for ctx in dr1 dr2; do
  echo "=== $ctx: PVCs with deletionTimestamp ==="
  kubectl --context $ctx get pvc -A -o json | \
    jq -r '.items[] | select(.metadata.deletionTimestamp != null) |
      "\(.metadata.namespace)/\(.metadata.name) phase=\(.status.phase) deleting=\(.metadata.deletionTimestamp)"'
done
=== dr1: PVCs with deletionTimestamp ===
=== dr2: PVCs with deletionTimestamp ===

Result

  • PVC busybox-pvc stays Bound with no deletionTimestamp on dr1 and dr2
  • PVC disowned (ownerReferences: null)
  • Operator logs show skipPVCDisownership=false during disable. QE's must gather logs pre-fix ignored annotation, skipped PVC disownership:
2026-06-30T13:30:19.637Z  INFO  vrg  Unprotecting VolSync PVC  {"PVC": "busybox-pvc-1", "vrgBeingDeleted": true}
2026-06-30T13:30:19.637Z  INFO  vrg  Unprotecting VolSync PVC  {"pvcName": "busybox-pvc-1", "skipPVCDisownership": true}
2026-06-30T13:30:19.656Z  INFO  vrg  Unprotecting VolSync PVC  {"pvcName": "busybox-pvc-2", "skipPVCDisownership": true}
2026-06-30T13:30:19.742Z  INFO  vrg  Unprotecting VolSync PVC  {"pvcName": "busybox-pvc-3", "skipPVCDisownership": true}
2026-06-30T13:30:19.582Z  INFO  pvcmap  Skipping PVC, as it is marked for deletion and not yet protected  {"pvc": "test-cephfs/busybox-pvc-2"}
  • Workload Running on dr1; VRG/DRPC removed after disable

TODO

  • Verify for non-CG path
  • Verify for CG path - stuck due to issues with ELENAGER:drenv_cg_new

@raaizik
raaizik requested a review from ELENAGER July 1, 2026 10:48
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a3f6160a-a23d-462b-8e5d-eb8f7a64f9f6

📥 Commits

Reviewing files that changed from the base of the PR and between 51522a8 and f451ca0.

📒 Files selected for processing (1)
  • internal/controller/vrg_volsync.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/vrg_volsync.go
📜 Recent review details
⏰ Context from checks skipped due to timeout. (12)
  • GitHub Check: test
  • GitHub Check: Golangci Lint (e2e)
  • GitHub Check: Golangci Lint (.)
  • GitHub Check: Build image
  • GitHub Check: Go compatibility
  • GitHub Check: drenv (ubuntu-24.04, 3.13)
  • GitHub Check: Unit tests
  • GitHub Check: drenv (macos-latest, 3.13)
  • GitHub Check: drenv (ubuntu-24.04, 3.10)
  • GitHub Check: drenv (ubuntu-24.04, 3.12)
  • GitHub Check: drenv (ubuntu-24.04, 3.14-dev)
  • GitHub Check: drenv (ubuntu-24.04, 3.11)

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved cleanup of replication-related storage by more reliably removing PVC ownership before deleting replication group resources.
    • Consistently honors the “do not delete PVC” setting during cleanup, reducing unintended PVC ownership removal.
    • Updated group deletion flows to better tolerate missing resources.
  • Tests
    • Updated deletion-related test expectations to reflect the new cleanup behavior and arguments.

Walkthrough

This PR extracts CephFS CG PVC disownership logic into package-level helpers, adds optional disownership control to DeleteRGS and DeleteRGD, updates the related tests, and threads a centralized skipPVCDisownership decision through VolSync cleanup and unprotect paths.

Changes

PVC disownership control

Layer / File(s) Summary
Extract disown helpers
internal/controller/cephfscg/cghandler.go
cgHandler.disownRDManagedPVCs and cgHandler.disownRSManagedPVCs now delegate to package-level helpers that fetch the related RG objects, skip NotFound errors, and call RemoveOwnerFromPVC.
DeleteRGS and DeleteRGD disown PVCs before delete
internal/controller/cephfscg/cghandler.go, internal/controller/cephfscg/cghandler_test.go
DeleteRGS and DeleteRGD now accept vsHandler and skipPVCDisownership, conditionally remove PVC ownership, then delete the object lists; tests pass the expanded arguments.
Thread skipPVCDisownership through VolSync cleanup
internal/controller/vrg_volsync.go
pvcUnprotectVolSync and doCleanupResources now use shouldSkipPVCDisownership(), which checks the annotation and deletion state before routing the flag onward.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

  • RamenDR/ramen#2602: Shares the same skipPVCDisownership flow through UnprotectVolSyncPVC and VolSync deletion paths.

Suggested reviewers: ShyamsundarR, BenamarMk, ELENAGER

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: preserving PVCs during disable DR.
Description check ✅ Passed The description is directly about the PVC disownership fix and disable DR regression.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/controller/cephfscg/cghandler_test.go (1)

91-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the non-skip disownership path.

These updated call sites only exercise skipPVCDisownership=true. Please add a focused test with a VSHandler, an RS/RD-owned PVC, and skipPVCDisownership=false to assert the PVC owner reference is removed before RGS/RGD deletion.

Also applies to: 207-207

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/cephfscg/cghandler_test.go` at line 91, The current tests
only cover the skipPVCDisownership=true path in the DeleteRGS/DeleteRGD call
sites. Add a focused test around the existing cghandler_test coverage that uses
a VSHandler with an RS/RD-owned PVC and passes skipPVCDisownership=false, then
assert the PVC owner reference is cleared before the RGS/RGD deletion proceeds.
Use the existing DeleteRGD (and matching DeleteRGS if applicable) test setup and
verify the disownership behavior explicitly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/controller/cephfscg/cghandler.go`:
- Around line 781-790: The disownership flow in the cleanup path is being
silently skipped when skipPVCDisownership is false but vsHandler is nil, which
lets the delete continue without the requested owner-reference cleanup. Update
the logic in the cghandler cleanup routine (the block that iterates
rgsList.Items and calls disownRSManagedPVCs) to fail fast with an error when
disownership is requested but no VSHandler is available, and apply the same
guard to the related delete path noted in the companion block.

---

Nitpick comments:
In `@internal/controller/cephfscg/cghandler_test.go`:
- Line 91: The current tests only cover the skipPVCDisownership=true path in the
DeleteRGS/DeleteRGD call sites. Add a focused test around the existing
cghandler_test coverage that uses a VSHandler with an RS/RD-owned PVC and passes
skipPVCDisownership=false, then assert the PVC owner reference is cleared before
the RGS/RGD deletion proceeds. Use the existing DeleteRGD (and matching
DeleteRGS if applicable) test setup and verify the disownership behavior
explicitly.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 706f9001-be27-4952-b6f4-5c3da231d4c6

📥 Commits

Reviewing files that changed from the base of the PR and between 2fa150b and d041954.

📒 Files selected for processing (3)
  • internal/controller/cephfscg/cghandler.go
  • internal/controller/cephfscg/cghandler_test.go
  • internal/controller/vrg_volsync.go
📜 Review details
⏰ Context from checks skipped due to timeout. (10)
  • GitHub Check: test
  • GitHub Check: drenv (ubuntu-24.04, 3.10)
  • GitHub Check: drenv (ubuntu-24.04, 3.12)
  • GitHub Check: drenv (ubuntu-24.04, 3.11)
  • GitHub Check: drenv (ubuntu-24.04, 3.13)
  • GitHub Check: drenv (ubuntu-24.04, 3.14-dev)
  • GitHub Check: Unit tests
  • GitHub Check: Build image
  • GitHub Check: Golangci Lint (.)
  • GitHub Check: Go compatibility
🔇 Additional comments (2)
internal/controller/cephfscg/cghandler.go (1)

268-268: LGTM!

Also applies to: 583-583, 819-885

internal/controller/vrg_volsync.go (1)

900-904: LGTM!

Also applies to: 962-1000

Comment on lines +781 to +790
if !skipPVCDisownership && vsHandler != nil {
for i := range rgsList.Items {
rgs := &rgsList.Items[i]
if len(rgs.Spec.RSSpec) > 0 {
if err := disownRSManagedPVCs(ctx, k8sClient, vsHandler, rgs.Spec.RSSpec, logger); err != nil {
return 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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fail fast when disownership is requested without a VSHandler.

When skipPVCDisownership is false, the caller is explicitly requesting owner-reference cleanup before deleting RGS/RGD. The current && vsHandler != nil branch silently skips that cleanup and still deletes the group resources, which can preserve the same PVC GC race this PR is trying to fix.

Proposed fix
-	if !skipPVCDisownership && vsHandler != nil {
+	if !skipPVCDisownership {
 		for i := range rgsList.Items {
 			rgs := &rgsList.Items[i]
 			if len(rgs.Spec.RSSpec) > 0 {
+				if vsHandler == nil {
+					return fmt.Errorf("VSHandler is required to disown RS-managed PVCs before deleting RGS")
+				}
 				if err := disownRSManagedPVCs(ctx, k8sClient, vsHandler, rgs.Spec.RSSpec, logger); err != nil {
 					return err
 				}
 			}
 		}
 	}
-	if !skipPVCDisownership && vsHandler != nil {
+	if !skipPVCDisownership {
 		for i := range rgdList.Items {
 			rgd := &rgdList.Items[i]
 			if len(rgd.Spec.RDSpecs) > 0 {
+				if vsHandler == nil {
+					return fmt.Errorf("VSHandler is required to disown RD-managed PVCs before deleting RGD")
+				}
 				if err := disownRDManagedPVCs(ctx, k8sClient, vsHandler, rgd.Spec.RDSpecs, logger); err != nil {
 					return err
 				}
 			}
 		}
 	}

Also applies to: 805-814

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/cephfscg/cghandler.go` around lines 781 - 790, The
disownership flow in the cleanup path is being silently skipped when
skipPVCDisownership is false but vsHandler is nil, which lets the delete
continue without the requested owner-reference cleanup. Update the logic in the
cghandler cleanup routine (the block that iterates rgsList.Items and calls
disownRSManagedPVCs) to fail fast with an error when disownership is requested
but no VSHandler is available, and apply the same guard to the related delete
path noted in the companion block.

@nirs

nirs commented Jul 2, 2026

Copy link
Copy Markdown
Member

Manual checks shown for managed apps (same results for discovered apps)

kubectl --context dr1 get pvc -n $APP_NS \
  -o custom-columns=NAME:.metadata.name,PHASE:.status.phase,DELETING:.metadata.deletionTimestamp
kubectl --context dr1 get deploy,pod,vrg -n $APP_NS
NAME          PHASE   DELETING
busybox-pvc   Bound   <none>
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/busybox   1/1     1            1           6m51s
NAME                           READY   STATUS    RESTARTS   AGE
pod/busybox-54d5b75d6c-jkbql   1/1     Running   0          6m51s

We don't check that the PVC is not deleted in e2e? I remember that we added a check for application health - after disable dr we wait until the workload is healthy, and this should fail quickly if the pvc is deleted.

If this is not the case, please open an issue for e2e. We must verify this in the tests to avoid regressions.

@raaizik
raaizik force-pushed the fix-disable-dr-pvc-cleanup branch 2 times, most recently from eebf653 to 0f4c3e8 Compare July 6, 2026 11:15
PR RamenDR#2602 skipped PVC disownership whenever the VRG was deleting,
which is correct for workload teardown but breaks disable DR: with
do-not-delete-pvc set, PVCs must survive while VRG/RS/RD/RGS/RGD
are removed.

Fixes disable DR regression where PVCs entered Terminating because RS
ownership was retained and deleted with the ReplicationSource.

Signed-off-by: raaizik <132667934+raaizik@users.noreply.github.com>
@raaizik
raaizik force-pushed the fix-disable-dr-pvc-cleanup branch from 0f4c3e8 to 51522a8 Compare July 7, 2026 10:06
Align VolSync unprotect error handling with VolRep so VRG finalize retries disown before ReplicationSource deletion.

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

Copy link
Copy Markdown
Member

@raaizik Do you think the fix could simply be to remove this block that was added some time ago? It seems like that will fix the problem.

for _, ref := range pvc.OwnerReferences {
if ref.Kind != "VolumeReplicationGroup" {
newRefs = append(newRefs, ref)
}
}

@raaizik

raaizik commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

@raaizik Do you think the fix could simply be to remove this block that was added some time ago? It seems like that will fix the problem.

for _, ref := range pvc.OwnerReferences {
if ref.Kind != "VolumeReplicationGroup" {
newRefs = append(newRefs, ref)
}
}

@BenamarMk I added that logic in #2504 (428539a) because clearing all owners caused PVC leftovers when RS was deleted with skip=true. So just removing the block might fix disable DR but re-trigger that.

This PR still matters for cleanup paths that don’t go through disownPVCs first.

@BenamarMk

BenamarMk commented Jul 13, 2026

Copy link
Copy Markdown
Member

@BenamarMk I added that logic in #2504 (428539a) because clearing all owners caused PVC leftovers when RS was deleted with skip=true. So just removing the block might fix disable DR but re-trigger that.

Thanks @raaizik for the explanation. However, the function DisownVolSyncManagedPVC is only called once and only if DoNotDeletePVCAnnotation is true.

The PVC ownerReferences should not contain any DR related resources when we are disabling DR. Therefore, we can leave it unowned (brute force), the controller using it will add its own ownerReference. Otherwise, selectively remove our (DR) resources from the ownerreferences list. We know that the PVC should be owned by RD, RS, or VRG. In that case, we can keep that block, but modify it like this:

        for _, ref := range pvc.OwnerReferences {
		if ref.Kind != "VolumeReplicationGroup" ||
			ref.Kind != "RepicationSource" ||
			ref.Kind != "ReplicationDestination" {
			newRefs = append(newRefs, ref)
		}
	}

This PR still matters for cleanup paths that don’t go through disownPVCs first.

If you believe that this PR still matters, then it should be opened against the right bug.

@raaizik

raaizik commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

If you believe that this PR still matters, then it should be opened against the right bug.

@BenamarMk, @ELENAGER hypothesized that the root cause for the bug in question is that #2602 introduced a regression where skipPVCDisownership was effectively always true during VRG delete, so cleanupRS/DeleteRGS skipped disown even with the annotation. You have a different take - that's fine. But it should be discussed with her as well as she's the one who identified it.

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