Skip to content

Commit 9ed8fe0

Browse files
committed
Validate volumeMode in CnsRegisterVolume
1 parent 2e7db95 commit 9ed8fe0

File tree

2 files changed

+569
-0
lines changed

2 files changed

+569
-0
lines changed

pkg/syncer/cnsoperator/controller/cnsregistervolume/cnsregistervolume_controller.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,25 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
534534
return reconcile.Result{RequeueAfter: timeout}, nil
535535
}
536536

537+
// If existing PVC has DataSourceRef and volumeMode set, handle volumeMode validation and inheritance
538+
if pvc != nil && pvc.Spec.DataSourceRef != nil && pvc.Spec.VolumeMode != nil {
539+
if instance.Spec.VolumeMode == "" {
540+
// Inherit the volumeMode from the existing PVC
541+
log.Infof("Existing PVC %s in namespace %s has DataSourceRef and volumeMode set to %s. "+
542+
"CnsRegisterVolume does not have volumeMode set. Inheriting volumeMode from existing PVC.",
543+
pvc.Name, pvc.Namespace, *pvc.Spec.VolumeMode)
544+
instance.Spec.VolumeMode = *pvc.Spec.VolumeMode
545+
} else if instance.Spec.VolumeMode != *pvc.Spec.VolumeMode {
546+
// Both are set but don't match - this is an error
547+
msg := fmt.Sprintf("VolumeMode mismatch: existing PVC %s in namespace %s has volumeMode %s, "+
548+
"but CnsRegisterVolume specifies volumeMode %s",
549+
pvc.Name, pvc.Namespace, *pvc.Spec.VolumeMode, instance.Spec.VolumeMode)
550+
log.Error(msg)
551+
setInstanceError(ctx, r, instance, msg)
552+
return reconcile.Result{RequeueAfter: timeout}, nil
553+
}
554+
}
555+
537556
// Do this check before creating a PV. Otherwise, PVC will be bound to PV after PV
538557
// is created even if validation fails
539558
if pvc != nil {
@@ -618,6 +637,13 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
618637
setInstanceError(ctx, r, instance, msg)
619638
return reconcile.Result{RequeueAfter: timeout}, nil
620639
}
640+
} else {
641+
// PV exists - check if volumeMode needs correction
642+
pv, err = validateAndFixPVVolumeMode(ctx, k8sclient, r, instance, pv, pvName, volumeID,
643+
capacityInMb, accessMode, storageClassName, pvNodeAffinity, timeout)
644+
if err != nil {
645+
return reconcile.Result{RequeueAfter: timeout}, nil
646+
}
621647
}
622648
// If PV is already bound to a different PVC at this point, then its a
623649
// duplicate request.
@@ -1029,6 +1055,79 @@ func isBlockVolumeRegisterRequest(ctx context.Context, instance *cnsregistervolu
10291055
return false
10301056
}
10311057

1058+
// validateAndFixPVVolumeMode checks if an existing PV has the correct volumeMode.
1059+
// If the volumeMode doesn't match what's expected, it untags the CNS volume, deletes
1060+
// and recreates the PV with the correct volumeMode since volumeMode is immutable on PVs.
1061+
func validateAndFixPVVolumeMode(ctx context.Context, k8sclient clientset.Interface,
1062+
r *ReconcileCnsRegisterVolume, instance *cnsregistervolumev1alpha1.CnsRegisterVolume,
1063+
pv *v1.PersistentVolume, pvName, volumeID string, capacityInMb int64,
1064+
accessMode v1.PersistentVolumeAccessMode, storageClassName string,
1065+
pvNodeAffinity *v1.VolumeNodeAffinity, timeout time.Duration) (*v1.PersistentVolume, error) {
1066+
log := logger.GetLogger(ctx)
1067+
1068+
// Determine expected volumeMode
1069+
expectedVolumeMode := instance.Spec.VolumeMode
1070+
if expectedVolumeMode == "" {
1071+
expectedVolumeMode = v1.PersistentVolumeFilesystem
1072+
}
1073+
1074+
// Get actual volumeMode from PV
1075+
pvVolumeMode := v1.PersistentVolumeFilesystem
1076+
if pv.Spec.VolumeMode != nil {
1077+
pvVolumeMode = *pv.Spec.VolumeMode
1078+
}
1079+
1080+
// Check if volumeMode matches
1081+
if expectedVolumeMode != pvVolumeMode {
1082+
log.Warnf("PV %s exists but has incorrect volumeMode. Expected: %s, Actual: %s. "+
1083+
"Untagging CNS volume and recreating PV with correct volumeMode.", pvName, expectedVolumeMode, pvVolumeMode)
1084+
1085+
// Untag the CNS volume before deleting PV to prevent underlying volume deletion.
1086+
// deleteDisk=false ensures the underlying vSphere disk is preserved.
1087+
log.Infof("Untagging CNS volume %s to preserve underlying disk before PV deletion", volumeID)
1088+
_, err := common.DeleteVolumeUtil(ctx, r.volumeManager, volumeID, false)
1089+
if err != nil {
1090+
msg := fmt.Sprintf("Failed to untag CNS volume %s. Error: %+v", volumeID, err)
1091+
log.Error(msg)
1092+
setInstanceError(ctx, r, instance, msg)
1093+
return nil, err
1094+
}
1095+
log.Infof("Successfully untagged CNS volume %s", volumeID)
1096+
1097+
// Delete the existing PV (underlying volume is safe due to CNS untag with deleteDisk=false)
1098+
err = k8sclient.CoreV1().PersistentVolumes().Delete(ctx, pvName, *metav1.NewDeleteOptions(0))
1099+
if err != nil {
1100+
msg := fmt.Sprintf("Failed to delete PV %s with incorrect volumeMode. Error: %+v", pvName, err)
1101+
log.Error(msg)
1102+
setInstanceError(ctx, r, instance, msg)
1103+
return nil, err
1104+
}
1105+
log.Infof("Successfully deleted PV %s with incorrect volumeMode", pvName)
1106+
1107+
// Recreate PV with correct volumeMode
1108+
claimRef := &v1.ObjectReference{
1109+
Kind: "PersistentVolumeClaim",
1110+
APIVersion: "v1",
1111+
Namespace: instance.Namespace,
1112+
Name: instance.Spec.PvcName,
1113+
}
1114+
pvSpec := getPersistentVolumeSpec(pvName, volumeID, capacityInMb,
1115+
accessMode, instance.Spec.VolumeMode, storageClassName, claimRef)
1116+
pvSpec.Spec.NodeAffinity = pvNodeAffinity
1117+
log.Debugf("Recreating PV with spec: %+v", pvSpec)
1118+
pv, err = k8sclient.CoreV1().PersistentVolumes().Create(ctx, pvSpec, metav1.CreateOptions{})
1119+
if err != nil {
1120+
log.Errorf("Failed to recreate PV with spec: %+v. Error: %+v", pvSpec, err)
1121+
setInstanceError(ctx, r, instance,
1122+
fmt.Sprintf("Failed to recreate PV: %s with correct volumeMode. Error: %+v", pvName, err))
1123+
return nil, err
1124+
}
1125+
log.Infof("Successfully recreated PV %s with correct volumeMode: %s", pvName, expectedVolumeMode)
1126+
}
1127+
1128+
return pv, nil
1129+
}
1130+
10321131
// setInstanceError sets error and records an event on the CnsRegisterVolume
10331132
// instance.
10341133
func setInstanceError(ctx context.Context, r *ReconcileCnsRegisterVolume,

0 commit comments

Comments
 (0)