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