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