Skip to content

Commit 639ea5d

Browse files
committed
Consider volumes in status also while removing finalizers from PVC
1 parent 69a18ea commit 639ea5d

File tree

4 files changed

+223
-41
lines changed

4 files changed

+223
-41
lines changed

pkg/common/unittestcommon/utils.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,10 @@ func (c *FakeK8SOrchestrator) GetPVCNameFromCSIVolumeID(volumeID string) (string
469469
return "with-used-by-annotation", "test-ns", true
470470
}
471471

472+
if strings.Contains(volumeID, "vol-1") {
473+
return "mock-pvc-1", "mock-namespace", true
474+
}
475+
472476
// Simulate a case where the volumeID corresponds to a PVC.
473477
return "mock-pvc", "mock-namespace", true
474478
}

pkg/syncer/cnsoperator/controller/cnsnodevmbatchattachment/cnsnodevmbatchattachment_controller.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,16 @@ func (r *Reconciler) Reconcile(ctx context.Context,
293293
if instance.DeletionTimestamp != nil && vm == nil {
294294
log.Infof("Instance %s is being deleted and VM object is also deleted from VC", request.NamespacedName.String())
295295

296-
// For every PVC mentioned in instance.Spec, remove finalizer from its PVC.
297-
for _, volume := range instance.Spec.Volumes {
298-
err := removePvcFinalizer(ctx, r.client, k8sClient, volume.PersistentVolumeClaim.ClaimName, instance.Namespace,
296+
pvcsInSpecAndStatus := getPvcsFromSpecAndStatus(ctx, instance)
297+
298+
// For every PVC mentioned in instance.Spec and in instance.Status, remove finalizer from it.
299+
// It is important to remove the finalizer from PVCs in instance.Status also as it is possible
300+
// that someone removes the PVC from spec after trriggering deletion of VM.
301+
for pvcName := range pvcsInSpecAndStatus {
302+
err := removePvcFinalizer(ctx, r.client, k8sClient, pvcName, instance.Namespace,
299303
instance.Spec.InstanceUUID)
300304
if err != nil {
301-
log.Errorf("failed to remove finalizer from PVC %s. Err: %s", volume.PersistentVolumeClaim.ClaimName,
305+
log.Errorf("failed to remove finalizer from PVC %s. Err: %s", pvcName,
302306
err)
303307
return r.completeReconciliationWithError(batchAttachCtx, instance, request.NamespacedName, timeout, err)
304308
}
@@ -307,7 +311,7 @@ func (r *Reconciler) Reconcile(ctx context.Context,
307311
patchErr := removeFinalizerFromCRDInstance(batchAttachCtx, instance, r.client)
308312
if patchErr != nil {
309313
log.Errorf("failed to update CnsNodeVMBatchAttachment %s. Err: +%v", instance.Name, patchErr)
310-
return r.completeReconciliationWithError(batchAttachCtx, instance, request.NamespacedName, timeout, err)
314+
return r.completeReconciliationWithError(batchAttachCtx, instance, request.NamespacedName, timeout, patchErr)
311315
}
312316
log.Infof("Successfully removed finalizer %s from instance %s",
313317
cnsoperatortypes.CNSFinalizer, request.NamespacedName.String())

pkg/syncer/cnsoperator/controller/cnsnodevmbatchattachment/cnsnodevmbatchattachment_helper.go

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -454,33 +454,23 @@ func getVolumeMetadataMaps(ctx context.Context,
454454
return
455455
}
456456

457-
// getPvcsInSpec returns map of PVCs and their volumeIDs.
458-
func getPvcsInSpec(ctx context.Context, instance *v1alpha1.CnsNodeVMBatchAttachment,
459-
k8sClient kubernetes.Interface) (map[string]string, error) {
457+
// getAllPvcsAttachedToVM returns PVC name to volumeID map for all the FCDs which are attached to the VM.
458+
func getAllPvcsAttachedToVM(ctx context.Context,
459+
attachedFCDList map[string]FCDBackingDetails) map[string]string {
460460
log := logger.GetLogger(ctx)
461+
pvcsAttachedToVM := make(map[string]string)
461462

462-
pvcsInSpec := make(map[string]string)
463-
for _, volume := range instance.Spec.Volumes {
464-
volumeId, ok := commonco.ContainerOrchestratorUtility.GetVolumeIDFromPVCName(
465-
instance.Namespace, volume.PersistentVolumeClaim.ClaimName)
466-
if !ok {
467-
pvcName := volume.PersistentVolumeClaim.ClaimName
468-
_, err := k8sClient.CoreV1().PersistentVolumeClaims(instance.Namespace).Get(ctx,
469-
pvcName, metav1.GetOptions{})
470-
if err != nil {
471-
if apierrors.IsNotFound(err) {
472-
log.Infof("PVC %s has already been deleted. No action to be taken", pvcName)
473-
continue
474-
}
475-
return pvcsInSpec, fmt.Errorf("failed to find volumeID for PVC %s", volume.PersistentVolumeClaim.ClaimName)
476-
}
477-
return pvcsInSpec, fmt.Errorf("failed to find volumeID for PVC %s", volume.PersistentVolumeClaim.ClaimName)
463+
for volumeID := range attachedFCDList {
464+
pvcName, _, found := commonco.ContainerOrchestratorUtility.GetPVCNameFromCSIVolumeID(volumeID)
465+
if !found {
466+
// Do not fail if volumeID is not found to avoid cases where cache is not refreshed.
467+
// Clean up routine in syncer will take care of the cases where finalizer was not removed from such PVCs.
468+
log.Warnf("failed to find PVC name for volumeID %s", volumeID)
469+
continue
478470
}
479-
pvcsInSpec[volume.PersistentVolumeClaim.ClaimName] = volumeId
471+
pvcsAttachedToVM[pvcName] = volumeID
480472
}
481-
482-
return pvcsInSpec, nil
483-
473+
return pvcsAttachedToVM
484474
}
485475

486476
// listAttachedFcdsForVM returns list of FCDs (present in the K8s cluster)
@@ -534,7 +524,7 @@ func listAttachedFcdsForVM(ctx context.Context,
534524
log.Debugf("failed to get diskMode and sharingMode for virtual disk")
535525
}
536526

537-
log.Infof("Adding volume with ID %s to attachedFCDs list", virtualDisk.VDiskId.Id)
527+
log.Debugf("Adding volume with ID %s to attachedFCDs list", virtualDisk.VDiskId.Id)
538528
attachedFCDs[virtualDisk.VDiskId.Id] = FCDBackingDetails{
539529
ControllerKey: controllerKey,
540530
UnitNumber: unitNumber,
@@ -602,6 +592,21 @@ func constructBatchAttachRequest(ctx context.Context,
602592
return pvcsInSpec, volumeIdsInSpec, batchAttachRequest, nil
603593
}
604594

595+
// getPvcsFromSpecAndStatus returns all the PVCs in spec as well as in status of the given instance.
596+
func getPvcsFromSpecAndStatus(ctx context.Context,
597+
instance *v1alpha1.CnsNodeVMBatchAttachment) map[string]bool {
598+
599+
listOfPvcsToRemoveFinalzer := make(map[string]bool, 0)
600+
601+
for _, volume := range instance.Spec.Volumes {
602+
listOfPvcsToRemoveFinalzer[volume.PersistentVolumeClaim.ClaimName] = true
603+
}
604+
for _, volume := range instance.Status.VolumeStatus {
605+
listOfPvcsToRemoveFinalzer[volume.PersistentVolumeClaim.ClaimName] = true
606+
}
607+
return listOfPvcsToRemoveFinalzer
608+
}
609+
605610
// isPvcEncrypted returns true if annotation csi.vsphere.encryption-class
606611
// is present on the PVC.
607612
func isPvcEncrypted(pvcAnnotations map[string]string) bool {
@@ -645,17 +650,6 @@ func getVolumesToAttachAndDetach(ctx context.Context, instance *v1alpha1.CnsNode
645650
pvcsToAttach := make(map[string]string, 0)
646651
pvcsToDetach := make(map[string]string, 0)
647652

648-
if instance.DeletionTimestamp != nil {
649-
log.Debugf("Instance %s is being deleted, adding all volumes in spec to volumesToDetach list.", instance.Name)
650-
volumesToDetach, err := getPvcsInSpec(ctx, instance, k8sClient)
651-
if err != nil {
652-
log.Errorf("failed to get volumes to detach from instance spec. Err: %s", err)
653-
return pvcsToAttach, volumesToDetach, err
654-
}
655-
log.Debugf("Volumes to detach list %+v for instance %s", volumesToDetach, instance.Name)
656-
return pvcsToAttach, volumesToDetach, nil
657-
}
658-
659653
// Query vCenter to find the list of FCDs which are attached to the VM.
660654
attachedFcdList, err := listAttachedFcdsForVM(ctx, vm)
661655
if err != nil {
@@ -664,6 +658,14 @@ func getVolumesToAttachAndDetach(ctx context.Context, instance *v1alpha1.CnsNode
664658
}
665659
log.Infof("List of attached FCDs %+v to VM %s", attachedFcdList, instance.Spec.InstanceUUID)
666660

661+
if instance.DeletionTimestamp != nil {
662+
log.Debugf("Instance %s is being deleted, adding all volumes attached to the VM to volumesToDetach list.",
663+
instance.Name)
664+
volumesToDetach := getAllPvcsAttachedToVM(ctx, attachedFcdList)
665+
log.Debugf("Volumes to detach list %+v for instance %s", volumesToDetach, instance.Name)
666+
return pvcsToAttach, volumesToDetach, nil
667+
}
668+
667669
// Get all PVCs and their corresponding volumeID mapping from instance spec.
668670
volumeIdsInSpec, volumeNamesInSpec, pvcNameToVolumeIDInSpec, err := getVolumeMetadataMaps(ctx, instance)
669671
if err != nil {
@@ -923,7 +925,7 @@ func removePvcFinalizer(ctx context.Context, client client.Client,
923925
return nil
924926
}
925927

926-
log.Infof("VM %s was the last attached VM for the PVC %s. Finalizer %s can be safely removed fromt the PVC",
928+
log.Infof("VM %s was the last attached VM for the PVC %s. Finalizer %s can be safely removed from the PVC",
927929
vmInstanceUUID, pvcName, cnsoperatortypes.CNSPvcFinalizer)
928930

929931
if !controllerutil.ContainsFinalizer(pvc, cnsoperatortypes.CNSPvcFinalizer) {

pkg/syncer/cnsoperator/controller/cnsnodevmbatchattachment/cnsnodevmbatchattachment_test.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,178 @@ func TestIsPvcEncrypted(t *testing.T) {
12541254
}
12551255
}
12561256

1257+
func TestGetPvcsFromSpecAndStatus(t *testing.T) {
1258+
tests := []struct {
1259+
name string
1260+
instance *v1alpha1.CnsNodeVMBatchAttachment
1261+
want map[string]bool
1262+
}{
1263+
{
1264+
name: "PVCs only in spec",
1265+
instance: &v1alpha1.CnsNodeVMBatchAttachment{
1266+
Spec: v1alpha1.CnsNodeVMBatchAttachmentSpec{
1267+
Volumes: []v1alpha1.VolumeSpec{
1268+
{
1269+
Name: "vol-1",
1270+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimSpec{
1271+
ClaimName: "pvc-a",
1272+
},
1273+
},
1274+
{
1275+
Name: "vol-2",
1276+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimSpec{
1277+
ClaimName: "pvc-b",
1278+
},
1279+
},
1280+
},
1281+
},
1282+
},
1283+
want: map[string]bool{
1284+
"pvc-a": true,
1285+
"pvc-b": true,
1286+
},
1287+
},
1288+
{
1289+
name: "PVCs only in status",
1290+
instance: &v1alpha1.CnsNodeVMBatchAttachment{
1291+
Status: v1alpha1.CnsNodeVMBatchAttachmentStatus{
1292+
VolumeStatus: []v1alpha1.VolumeStatus{
1293+
{
1294+
Name: "vol-1",
1295+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimStatus{
1296+
ClaimName: "pvc-x",
1297+
},
1298+
},
1299+
{
1300+
Name: "vol-2",
1301+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimStatus{
1302+
ClaimName: "pvc-y",
1303+
},
1304+
},
1305+
},
1306+
},
1307+
},
1308+
want: map[string]bool{
1309+
"pvc-x": true,
1310+
"pvc-y": true,
1311+
},
1312+
},
1313+
{
1314+
name: "PVCs in both spec and status",
1315+
instance: &v1alpha1.CnsNodeVMBatchAttachment{
1316+
Spec: v1alpha1.CnsNodeVMBatchAttachmentSpec{
1317+
Volumes: []v1alpha1.VolumeSpec{
1318+
{
1319+
Name: "vol-1",
1320+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimSpec{
1321+
ClaimName: "pvc-a",
1322+
},
1323+
},
1324+
},
1325+
},
1326+
Status: v1alpha1.CnsNodeVMBatchAttachmentStatus{
1327+
VolumeStatus: []v1alpha1.VolumeStatus{
1328+
{
1329+
Name: "vol-2",
1330+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimStatus{
1331+
ClaimName: "pvc-b",
1332+
},
1333+
},
1334+
},
1335+
},
1336+
},
1337+
want: map[string]bool{
1338+
"pvc-a": true,
1339+
"pvc-b": true,
1340+
},
1341+
},
1342+
{
1343+
name: "Duplicate PVCs across spec and status",
1344+
instance: &v1alpha1.CnsNodeVMBatchAttachment{
1345+
Spec: v1alpha1.CnsNodeVMBatchAttachmentSpec{
1346+
Volumes: []v1alpha1.VolumeSpec{
1347+
{
1348+
Name: "vol-1",
1349+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimSpec{
1350+
ClaimName: "pvc-dup",
1351+
},
1352+
},
1353+
},
1354+
},
1355+
Status: v1alpha1.CnsNodeVMBatchAttachmentStatus{
1356+
VolumeStatus: []v1alpha1.VolumeStatus{
1357+
{
1358+
Name: "vol-1",
1359+
PersistentVolumeClaim: v1alpha1.PersistentVolumeClaimStatus{
1360+
ClaimName: "pvc-dup",
1361+
},
1362+
},
1363+
},
1364+
},
1365+
},
1366+
want: map[string]bool{
1367+
"pvc-dup": true,
1368+
},
1369+
},
1370+
{
1371+
name: "Empty spec and status",
1372+
instance: &v1alpha1.CnsNodeVMBatchAttachment{},
1373+
want: map[string]bool{},
1374+
},
1375+
}
1376+
1377+
for _, tt := range tests {
1378+
t.Run(tt.name, func(t *testing.T) {
1379+
got := getPvcsFromSpecAndStatus(context.Background(), tt.instance)
1380+
if !reflect.DeepEqual(got, tt.want) {
1381+
t.Errorf("getPvcsFromSpecAndStatus() = %v, want %v", got, tt.want)
1382+
}
1383+
})
1384+
}
1385+
}
1386+
1387+
func TestGetAllPvcsAttachedToVMSuccess(t *testing.T) {
1388+
commonco.ContainerOrchestratorUtility = &unittestcommon.FakeK8SOrchestrator{}
1389+
1390+
attachedFCD := map[string]FCDBackingDetails{
1391+
"vol-1": {},
1392+
"vol-2": {},
1393+
}
1394+
1395+
got := getAllPvcsAttachedToVM(context.Background(), attachedFCD)
1396+
want := map[string]string{
1397+
"mock-pvc-1": "vol-1",
1398+
"mock-pvc": "vol-2",
1399+
}
1400+
1401+
assert.Equal(t, want, got)
1402+
}
1403+
1404+
func TestGetAllPvcsAttachedToVM_EmptyInput(t *testing.T) {
1405+
1406+
commonco.ContainerOrchestratorUtility = &unittestcommon.FakeK8SOrchestrator{}
1407+
1408+
attachedFCD := map[string]FCDBackingDetails{}
1409+
1410+
got := getAllPvcsAttachedToVM(context.Background(), attachedFCD)
1411+
want := map[string]string{}
1412+
1413+
assert.Equal(t, want, got)
1414+
}
1415+
1416+
func TestGetAllPvcsAttachedToVM_PVCNotFound(t *testing.T) {
1417+
1418+
commonco.ContainerOrchestratorUtility = &unittestcommon.FakeK8SOrchestrator{}
1419+
1420+
attachedFCD := map[string]FCDBackingDetails{
1421+
"invalid": {},
1422+
}
1423+
got := getAllPvcsAttachedToVM(context.Background(), attachedFCD)
1424+
want := map[string]string{}
1425+
1426+
assert.Equal(t, want, got)
1427+
}
1428+
12571429
func MockGetVMFromVcenter(ctx context.Context, nodeUUID string,
12581430
configInfo config.ConfigurationInfo) (*cnsvsphere.VirtualMachine, error) {
12591431
var vm *cnsvsphere.VirtualMachine

0 commit comments

Comments
 (0)