@@ -43,6 +43,7 @@ import (
43
43
44
44
clientConfig "sigs.k8s.io/controller-runtime/pkg/client/config"
45
45
46
+ clientset "k8s.io/client-go/kubernetes"
46
47
apis "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator"
47
48
cnsregistervolumev1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/cnsregistervolume/v1alpha1"
48
49
storagepolicyusagev1alpha2 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/storagepolicy/v1alpha2"
@@ -551,53 +552,63 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
551
552
setInstanceError (ctx , r , instance , "Duplicate Request" )
552
553
return reconcile.Result {RequeueAfter : timeout }, nil
553
554
}
554
- // Create PVC mapping to above created PV.
555
- log .Infof ("Creating PVC: %s" , instance .Spec .PvcName )
556
- pvcSpec , err := getPersistentVolumeClaimSpec (ctx , instance .Spec .PvcName , instance .Namespace , capacityInMb ,
557
- storageClassName , accessMode , pvName , datastoreAccessibleTopology )
555
+
556
+ // Check if PVC already exists and has valid DataSourceRef
557
+ pvc , err := checkExistingPVCDataSourceRef (ctx , k8sclient , instance .Spec .PvcName , instance .Namespace )
558
558
if err != nil {
559
- msg := fmt .Sprintf ("Failed to create spec for PVC: %q. Error: %v" , instance .Spec .PvcName , err )
560
- log .Errorf (msg )
561
- setInstanceError (ctx , r , instance , msg )
559
+ log .Errorf ("Failed to check existing PVC %s/%s with DataSourceRef: %+v" , instance .Namespace , instance .Spec .PvcName , err )
560
+ setInstanceError (ctx , r , instance , fmt .Sprintf ("Failed to check existing PVC %s/%s with DataSourceRef: %+v" , instance .Namespace , instance .Spec .PvcName , err ))
562
561
return reconcile.Result {RequeueAfter : timeout }, nil
563
562
}
564
- log .Debugf ("PVC spec is: %+v" , pvcSpec )
565
- pvc , err := k8sclient .CoreV1 ().PersistentVolumeClaims (instance .Namespace ).Create (ctx ,
566
- pvcSpec , metav1.CreateOptions {})
567
- if err != nil {
568
- if apierrors .IsAlreadyExists (err ) {
569
- log .Infof ("PVC: %s already exists" , instance .Spec .PvcName )
570
- pvc , err = k8sclient .CoreV1 ().PersistentVolumeClaims (instance .Namespace ).Get (ctx ,
571
- instance .Spec .PvcName , metav1.GetOptions {})
563
+
564
+ if pvc != nil {
565
+ log .Infof ("PVC: %s already exists" , instance .Spec .PvcName )
566
+ if pvc .Status .Phase == v1 .ClaimBound && pvc .Spec .VolumeName != pvName {
567
+ // This is handle cases where PVC with this name already exists and
568
+ // is bound. This happens when a new CnsRegisterVolume instance is
569
+ // created to import a new volume with PVC name which is already
570
+ // created and is bound.
571
+ msg := fmt .Sprintf ("Another PVC: %s already exists in namespace: %s which is Bound to a different PV" ,
572
+ instance .Spec .PvcName , instance .Namespace )
573
+ log .Errorf (msg )
574
+ setInstanceError (ctx , r , instance , msg )
575
+ // Untag the CNS volume which was created previously.
576
+ _ , err = common .DeleteVolumeUtil (ctx , r .volumeManager , volumeID , false )
572
577
if err != nil {
573
- msg := fmt .Sprintf ("Failed to get PVC: %s on namespace: %s" , instance .Spec .PvcName , instance .Namespace )
574
- log .Errorf (msg )
575
- setInstanceError (ctx , r , instance , msg )
576
- return reconcile.Result {RequeueAfter : timeout }, nil
577
- }
578
- if pvc .Status .Phase == v1 .ClaimBound && pvc .Spec .VolumeName != pvName {
579
- // This is handle cases where PVC with this name already exists and
580
- // is bound. This happens when a new CnsRegisterVolume instance is
581
- // created to import a new volume with PVC name which is already
582
- // created and is bound.
583
- msg := fmt .Sprintf ("Another PVC: %s already exists in namespace: %s which is Bound to a different PV" ,
584
- instance .Spec .PvcName , instance .Namespace )
585
- log .Errorf (msg )
586
- setInstanceError (ctx , r , instance , msg )
587
- // Untag the CNS volume which was created previously.
588
- _ , err = common .DeleteVolumeUtil (ctx , r .volumeManager , volumeID , false )
578
+ log .Errorf ("Failed to untag CNS volume: %s with error: %+v" , volumeID , err )
579
+ } else {
580
+ // Delete PV created above.
581
+ err = k8sclient .CoreV1 ().PersistentVolumes ().Delete (ctx , pvName , * metav1 .NewDeleteOptions (0 ))
589
582
if err != nil {
590
- log .Errorf ("Failed to untag CNS volume: %s with error: %+v" , volumeID , err )
591
- } else {
592
- // Delete PV created above.
593
- err = k8sclient .CoreV1 ().PersistentVolumes ().Delete (ctx , pvName , * metav1 .NewDeleteOptions (0 ))
594
- if err != nil {
595
- log .Errorf ("Failed to delete PV: %s with error: %+v" , pvName , err )
596
- }
583
+ log .Errorf ("Failed to delete PV: %s with error: %+v" , pvName , err )
597
584
}
598
- return reconcile.Result {RequeueAfter : timeout }, nil
599
585
}
600
- } else {
586
+ return reconcile.Result {RequeueAfter : timeout }, nil
587
+ }
588
+
589
+ if pvc .Spec .DataSourceRef != nil {
590
+ apiGroup := ""
591
+ if pvc .Spec .DataSourceRef .APIGroup != nil {
592
+ apiGroup = * pvc .Spec .DataSourceRef .APIGroup
593
+ }
594
+ log .Infof ("PVC %s in namespace %s has valid DataSourceRef with apiGroup: %s, kind: %s, name: %s" ,
595
+ pvc .Name , pvc .Namespace , apiGroup , pvc .Spec .DataSourceRef .Kind , pvc .Spec .DataSourceRef .Name )
596
+ }
597
+ } else {
598
+ // Create PVC mapping to above created PV.
599
+ log .Infof ("Creating PVC: %s" , instance .Spec .PvcName )
600
+ pvcSpec , err := getPersistentVolumeClaimSpec (ctx , instance .Spec .PvcName , instance .Namespace , capacityInMb ,
601
+ storageClassName , accessMode , pvName , datastoreAccessibleTopology )
602
+ if err != nil {
603
+ msg := fmt .Sprintf ("Failed to create spec for PVC: %q. Error: %v" , instance .Spec .PvcName , err )
604
+ log .Errorf (msg )
605
+ setInstanceError (ctx , r , instance , msg )
606
+ return reconcile.Result {RequeueAfter : timeout }, nil
607
+ }
608
+ log .Debugf ("PVC spec is: %+v" , pvcSpec )
609
+ pvc , err = k8sclient .CoreV1 ().PersistentVolumeClaims (instance .Namespace ).Create (ctx ,
610
+ pvcSpec , metav1.CreateOptions {})
611
+ if err != nil {
601
612
log .Errorf ("Failed to create PVC with spec: %+v. Error: %+v" , pvcSpec , err )
602
613
setInstanceError (ctx , r , instance ,
603
614
fmt .Sprintf ("Failed to create PVC: %s for volume with err: %+v" , instance .Spec .PvcName , err ))
@@ -609,9 +620,9 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
609
620
setInstanceError (ctx , r , instance ,
610
621
fmt .Sprintf ("Delete PV %s failed with error: %+v" , pvName , err ))
611
622
return reconcile.Result {RequeueAfter : timeout }, nil
623
+ } else {
624
+ log .Infof ("PVC: %s is created successfully" , instance .Spec .PvcName )
612
625
}
613
- } else {
614
- log .Infof ("PVC: %s is created successfully" , instance .Spec .PvcName )
615
626
}
616
627
// Watch for PVC to be bound.
617
628
isBound , err := isPVCBound (ctx , k8sclient , pvc , time .Duration (1 * time .Minute ))
@@ -736,6 +747,56 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
736
747
return reconcile.Result {}, nil
737
748
}
738
749
750
+ // checkExistingPVCDataSourceRef checks if a PVC already exists and validates its DataSourceRef.
751
+ // Returns the existing PVC if it exists regardless if it has a supported DataSourceRef, otherwise returns nil.
752
+ func checkExistingPVCDataSourceRef (ctx context.Context , k8sclient clientset.Interface ,
753
+ pvcName , namespace string ) (* v1.PersistentVolumeClaim , error ) {
754
+ log := logger .GetLogger (ctx )
755
+
756
+ // Try to get the existing PVC
757
+ existingPVC , err := k8sclient .CoreV1 ().PersistentVolumeClaims (namespace ).Get (ctx , pvcName , metav1.GetOptions {})
758
+ if err != nil {
759
+ if apierrors .IsNotFound (err ) {
760
+ // PVC doesn't exist, return nil (we'll create a new one)
761
+ return nil , nil
762
+ }
763
+ // Some other error occurred
764
+ return nil , fmt .Errorf ("failed to check existing PVC %s in namespace %s: %+v" , pvcName , namespace , err )
765
+ }
766
+
767
+ // PVC exists, check if it has DataSourceRef
768
+ if existingPVC .Spec .DataSourceRef == nil {
769
+ log .Infof ("Existing PVC %s in namespace %s has no DataSourceRef, can reuse" , pvcName , namespace )
770
+ return existingPVC , nil
771
+ }
772
+
773
+ // Check if DataSourceRef matches supported types
774
+ apiGroup := ""
775
+ if existingPVC .Spec .DataSourceRef .APIGroup != nil {
776
+ apiGroup = * existingPVC .Spec .DataSourceRef .APIGroup
777
+ }
778
+
779
+ for _ , supportedType := range supportedDataSourceTypes {
780
+ if supportedType .apiGroup == apiGroup && supportedType .kind == existingPVC .Spec .DataSourceRef .Kind {
781
+ log .Infof ("Existing PVC %s in namespace %s has valid DataSourceRef (apiGroup: %s, kind: %s), can reuse" ,
782
+ pvcName , namespace , apiGroup , existingPVC .Spec .DataSourceRef .Kind )
783
+ return existingPVC , nil
784
+ }
785
+ }
786
+
787
+ // Check if DataSourceRef is VolumeSnapshot
788
+ if existingPVC .Spec .DataSourceRef .Kind == "VolumeSnapshot" &&
789
+ existingPVC .Spec .DataSourceRef .APIGroup != nil &&
790
+ * existingPVC .Spec .DataSourceRef .APIGroup == "snapshot.storage.k8s.io" {
791
+ log .Infof ("WARNING: Existing PVC %s in namespace %s has valid VolumeSnapshot DataSourceRef, however, it is not supported for CNSRegisterVolume" , pvcName , namespace )
792
+ }
793
+
794
+ // DataSourceRef is not supported
795
+ return nil , fmt .Errorf ("existing PVC %s in namespace %s has unsupported DataSourceRef for CNSRegisterVolume. " +
796
+ "APIGroup: %s, Kind: %s is not supported. Supported types: %+v" ,
797
+ pvcName , namespace , apiGroup , existingPVC .Spec .DataSourceRef .Kind , supportedDataSourceTypes )
798
+ }
799
+
739
800
// validateCnsRegisterVolumeSpec validates the input params of
740
801
// CnsRegisterVolume instance.
741
802
func validateCnsRegisterVolumeSpec (ctx context.Context , instance * cnsregistervolumev1alpha1.CnsRegisterVolume ) error {
0 commit comments