@@ -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,59 @@ 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 PVC if it exists with no DataSourceRef or it exists with a supported DataSourceRef.
760
+ // If PVC exists but has an unsupported DataSourceRef, return (nil, error).
761
+ // If PVC does not exist, return (nil, nil) so that it will be created later.
762
+ func checkExistingPVCDataSourceRef (ctx context.Context , k8sclient clientset.Interface ,
763
+ pvcName , namespace string ) (* v1.PersistentVolumeClaim , error ) {
764
+ log := logger .GetLogger (ctx )
765
+
766
+ // Try to get the existing PVC
767
+ existingPVC , err := k8sclient .CoreV1 ().PersistentVolumeClaims (namespace ).Get (ctx , pvcName , metav1.GetOptions {})
768
+ if err != nil {
769
+ if apierrors .IsNotFound (err ) {
770
+ // PVC doesn't exist, return nil (we'll create a new one)
771
+ return nil , nil
772
+ }
773
+ // Some other error occurred
774
+ return nil , fmt .Errorf ("failed to check existing PVC %s in namespace %s: %+v" , pvcName , namespace , err )
775
+ }
776
+
777
+ // PVC exists, check if it has DataSourceRef
778
+ if existingPVC .Spec .DataSourceRef == nil {
779
+ log .Infof ("Existing PVC %s in namespace %s has no DataSourceRef, can reuse" , pvcName , namespace )
780
+ return existingPVC , nil
781
+ }
782
+
783
+ // Check if DataSourceRef matches supported types
784
+ apiGroup := ""
785
+ if existingPVC .Spec .DataSourceRef .APIGroup != nil {
786
+ apiGroup = * existingPVC .Spec .DataSourceRef .APIGroup
787
+ }
788
+
789
+ for _ , supportedType := range supportedDataSourceTypes {
790
+ if supportedType .apiGroup == apiGroup && supportedType .kind == existingPVC .Spec .DataSourceRef .Kind {
791
+ log .Infof ("Existing PVC %s in namespace %s has valid DataSourceRef (apiGroup: %s, kind: %s), can reuse" ,
792
+ pvcName , namespace , apiGroup , existingPVC .Spec .DataSourceRef .Kind )
793
+ return existingPVC , nil
794
+ }
795
+ }
796
+
797
+ // Check if DataSourceRef is VolumeSnapshot
798
+ if existingPVC .Spec .DataSourceRef .Kind == "VolumeSnapshot" &&
799
+ existingPVC .Spec .DataSourceRef .APIGroup != nil &&
800
+ * existingPVC .Spec .DataSourceRef .APIGroup == "snapshot.storage.k8s.io" {
801
+ log .Infof ("WARNING: Existing PVC %s in namespace %s has valid VolumeSnapshot DataSourceRef, " +
802
+ "however, it is not supported for CNSRegisterVolume" , pvcName , namespace )
803
+ }
804
+
805
+ // DataSourceRef is not supported
806
+ return nil , fmt .Errorf ("existing PVC %s in namespace %s has unsupported DataSourceRef for CNSRegisterVolume. " +
807
+ "APIGroup: %s, Kind: %s is not supported. Supported types: %+v" ,
808
+ pvcName , namespace , apiGroup , existingPVC .Spec .DataSourceRef .Kind , supportedDataSourceTypes )
809
+ }
810
+
745
811
// validateCnsRegisterVolumeSpec validates the input params of
746
812
// CnsRegisterVolume instance.
747
813
func validateCnsRegisterVolumeSpec (ctx context.Context , instance * cnsregistervolumev1alpha1.CnsRegisterVolume ) error {
0 commit comments