Skip to content

Commit d82d155

Browse files
BenamarMkShyamsundarR
authored andcommitted
Disown volsync managed pvcs upon vrg deletion
This commit introduces changes to disown PVC resources when a VRG is deleted, particularly in scenarios where the DR is disabled. The motivation behind this adjustment is to give back the management of the PVC lifecycle to OCM. Upon disabling DR, the VRG now iterates through all volsync-managed PVCs. It removes VRG ownership from these PVCs and reinstates OCM annotations. This ensures that PVCs are not prematurely garbage collected upon VRG deletion. In order for that to work, a new annotation needs to be added to the VRG to indicate that the PVCs should be disowned upon DR disabling drplacementcontrol.ramendr.openshift.io/do-not-delete-pvc: "true" Signed-off-by: Benamar Mekhissi <[email protected]>
1 parent 7b51785 commit d82d155

6 files changed

+90
-43
lines changed

controllers/drplacementcontrol.go

+1
Original file line numberDiff line numberDiff line change
@@ -1524,6 +1524,7 @@ func (d *DRPCInstance) generateVRG(dstCluster string, repState rmn.ReplicationSt
15241524
Namespace: d.vrgNamespace,
15251525
Annotations: map[string]string{
15261526
DestinationClusterAnnotationKey: dstCluster,
1527+
DoNotDeletePVCAnnotation: d.instance.GetAnnotations()[DoNotDeletePVCAnnotation],
15271528
},
15281529
},
15291530
Spec: rmn.VolumeReplicationGroupSpec{

controllers/drplacementcontrol_controller.go

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ const (
5959
MaxPlacementDecisionConflictCount = 5
6060

6161
DestinationClusterAnnotationKey = "drplacementcontrol.ramendr.openshift.io/destination-cluster"
62+
63+
DoNotDeletePVCAnnotation = "drplacementcontrol.ramendr.openshift.io/do-not-delete-pvc"
64+
DoNotDeletePVCAnnotationVal = "true"
6265
)
6366

6467
var InitialWaitTimeForDRPCPlacementRule = errorswrapper.New("Waiting for DRPC Placement to produces placement decision")

controllers/volsync/vshandler.go

+62-24
Original file line numberDiff line numberDiff line change
@@ -521,22 +521,6 @@ func (v *VSHandler) TakePVCOwnership(pvcName string) (bool, error) {
521521
return false, err
522522
}
523523

524-
// Remove acm annotations from the PVC just as a precaution - ACM uses the annotations to track that the
525-
// pvc is owned by an application. Removing them should not be necessary now that we are adding
526-
// the do-not-delete annotation. With the do-not-delete annotation (see ACMAppSubDoNotDeleteAnnotation), ACM
527-
// should not delete the pvc when the application is removed.
528-
updatedAnnotations := map[string]string{}
529-
530-
for currAnnotationKey, currAnnotationValue := range pvc.Annotations {
531-
// We want to only preserve annotations not from ACM (i.e. remove all ACM annotations to break ownership)
532-
if !strings.HasPrefix(currAnnotationKey, "apps.open-cluster-management.io") ||
533-
currAnnotationKey == ACMAppSubDoNotDeleteAnnotation {
534-
updatedAnnotations[currAnnotationKey] = currAnnotationValue
535-
}
536-
}
537-
538-
pvc.Annotations = updatedAnnotations
539-
540524
err = v.client.Update(v.ctx, pvc)
541525
if err != nil {
542526
l.Error(err, "Error updating annotations on PVC to break appsub ownership")
@@ -578,21 +562,21 @@ func (v *VSHandler) pvcExistsAndInUse(pvcName string, inUsePodMustBeReady bool)
578562
return util.IsPVAttachedToNode(v.ctx, v.client, v.log, pvc)
579563
}
580564

581-
func (v *VSHandler) pvcExists(pvcName string) (bool, error) {
582-
_, err := v.getPVC(pvcName)
565+
func (v *VSHandler) pvcExists(pvcName string) (bool, *corev1.PersistentVolumeClaim, error) {
566+
pvc, err := v.getPVC(pvcName)
583567
if err != nil {
584568
if !kerrors.IsNotFound(err) {
585569
v.log.V(1).Info("failed to get PVC")
586570

587-
return false, err
571+
return false, pvc, err
588572
}
589573

590-
return false, nil
574+
return false, pvc, nil
591575
}
592576

593577
v.log.V(1).Info("PVC found")
594578

595-
return true, nil
579+
return true, pvc, nil
596580
}
597581

598582
func (v *VSHandler) getPVC(pvcName string) (*corev1.PersistentVolumeClaim, error) {
@@ -946,16 +930,16 @@ func (v *VSHandler) EnsurePVCforDirectCopy(ctx context.Context,
946930
return fmt.Errorf("capacity must be provided %v", rdSpec.ProtectedPVC)
947931
}
948932

949-
exists, err := v.pvcExists(rdSpec.ProtectedPVC.Name)
933+
exists, pvc, err := v.pvcExists(rdSpec.ProtectedPVC.Name)
950934
if err != nil {
951935
return err
952936
}
953937

954938
if exists {
955-
return nil
939+
return v.removeOCMAnnotationsAndUpdate(pvc)
956940
}
957941

958-
pvc := &corev1.PersistentVolumeClaim{
942+
pvc = &corev1.PersistentVolumeClaim{
959943
ObjectMeta: metav1.ObjectMeta{
960944
Name: rdSpec.ProtectedPVC.Name,
961945
Namespace: v.owner.GetNamespace(),
@@ -1043,6 +1027,17 @@ func (v *VSHandler) validateSnapshotAndEnsurePVC(rdSpec ramendrv1alpha1.VolSyncR
10431027
}
10441028
}
10451029

1030+
pvc, err := v.getPVC(rdSpec.ProtectedPVC.Name)
1031+
if err != nil {
1032+
return err
1033+
}
1034+
1035+
// Once the PVC is restored/rolled back, need to re-add the annotations from old Primary
1036+
err = v.addBackOCMAnnotationsAndUpdate(pvc, rdSpec.ProtectedPVC.Annotations)
1037+
if err != nil {
1038+
return err
1039+
}
1040+
10461041
// Add ownerRef on snapshot pointing to the vrg - if/when the VRG gets cleaned up, then GC can cleanup the snap
10471042
return v.addOwnerReferenceAndUpdate(snap, v.owner)
10481043
}
@@ -2110,3 +2105,46 @@ func (v *VSHandler) checkLastSnapshotSyncStatus(lrs *volsyncv1alpha1.Replication
21102105

21112106
return !completed
21122107
}
2108+
2109+
func (v *VSHandler) DisownVolSyncManagedPVC(pvc *corev1.PersistentVolumeClaim) error {
2110+
// TODO: Remove just the VRG ownerReference instead of blindly removing all ownerreferences.
2111+
// For now, this is fine, given that the VRG is the sole owner of the PVC after DR is enabled.
2112+
pvc.ObjectMeta.OwnerReferences = nil
2113+
v.deleteAnnotation(pvc, ACMAppSubDoNotDeleteAnnotation)
2114+
2115+
return v.client.Update(v.ctx, pvc)
2116+
}
2117+
2118+
func (v *VSHandler) deleteAnnotation(obj client.Object, annotationName string) {
2119+
annotations := obj.GetAnnotations()
2120+
delete(annotations, annotationName)
2121+
obj.SetAnnotations(annotations)
2122+
}
2123+
2124+
func (v *VSHandler) addBackOCMAnnotationsAndUpdate(obj client.Object, annotations map[string]string) error {
2125+
updatedAnnotations := obj.GetAnnotations()
2126+
2127+
for key, val := range annotations {
2128+
if strings.HasPrefix(key, "apps.open-cluster-management.io") {
2129+
updatedAnnotations[key] = val
2130+
}
2131+
}
2132+
2133+
obj.SetAnnotations(updatedAnnotations)
2134+
2135+
return v.client.Update(v.ctx, obj)
2136+
}
2137+
2138+
func (v *VSHandler) removeOCMAnnotationsAndUpdate(obj client.Object) error {
2139+
updatedAnnotations := map[string]string{}
2140+
2141+
for key, val := range obj.GetAnnotations() {
2142+
if !strings.HasPrefix(key, "apps.open-cluster-management.io") {
2143+
updatedAnnotations[key] = val
2144+
}
2145+
}
2146+
2147+
obj.SetAnnotations(updatedAnnotations)
2148+
2149+
return v.client.Update(v.ctx, obj)
2150+
}

controllers/volsync/vshandler_test.go

-19
Original file line numberDiff line numberDiff line change
@@ -1807,25 +1807,6 @@ var _ = Describe("VolSync_Handler", func() {
18071807
Expect(pvcPreparationErr).ToNot(HaveOccurred())
18081808
Expect(pvcPreparationComplete).To(BeTrue())
18091809

1810-
Eventually(func() int {
1811-
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(testPVC), testPVC)
1812-
if err != nil {
1813-
return 0
1814-
}
1815-
1816-
return len(testPVC.Annotations)
1817-
}, maxWait, interval).Should(Equal(len(initialAnnotations) - 2))
1818-
// We had 2 acm annotations in initialAnnotations
1819-
1820-
for key, val := range testPVC.Annotations {
1821-
if key != volsync.ACMAppSubDoNotDeleteAnnotation {
1822-
// Other ACM annotations should be deleted
1823-
Expect(strings.HasPrefix(key, "apps.open-cluster-management.io")).To(BeFalse())
1824-
1825-
Expect(initialAnnotations[key]).To(Equal(val)) // Other annotations should still be there
1826-
}
1827-
}
1828-
18291810
// ACM do-not-delete annotation should be added to the PVC
18301811
pvcAnnotations := testPVC.GetAnnotations()
18311812
val, ok := pvcAnnotations[volsync.ACMAppSubDoNotDeleteAnnotation]

controllers/volumereplicationgroup_controller.go

+6
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,12 @@ func (v *VRGInstance) processForDeletion() ctrl.Result {
774774

775775
defer v.log.Info("Exiting processing VolumeReplicationGroup")
776776

777+
if err := v.disownPVCs(); err != nil {
778+
v.log.Info("Disowning PVCs failed", "error", err)
779+
780+
return ctrl.Result{Requeue: true}
781+
}
782+
777783
if !containsString(v.instance.ObjectMeta.Finalizers, vrgFinalizerName) {
778784
v.log.Info("Finalizer missing from resource", "finalizer", vrgFinalizerName)
779785

controllers/vrg_volsync.go

+18
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,21 @@ func (v *VRGInstance) pvcUnprotectVolSync(pvc corev1.PersistentVolumeClaim, log
402402
// TODO Delete ReplicationSource, ReplicationDestination, etc.
403403
v.pvcStatusDeleteIfPresent(pvc.Namespace, pvc.Name, log)
404404
}
405+
406+
// disownPVCs this function is disassociating all PVCs (targeted for VolSync replication) from its owner (VRG)
407+
func (v *VRGInstance) disownPVCs() error {
408+
if v.instance.GetAnnotations()[DoNotDeletePVCAnnotation] != DoNotDeletePVCAnnotationVal {
409+
return nil
410+
}
411+
412+
for idx := range v.volSyncPVCs {
413+
pvc := &v.volSyncPVCs[idx]
414+
415+
err := v.volSyncHandler.DisownVolSyncManagedPVC(pvc)
416+
if err != nil {
417+
return err
418+
}
419+
}
420+
421+
return nil
422+
}

0 commit comments

Comments
 (0)