Skip to content

Commit 6546bbf

Browse files
committed
Add support for PVCs in captured blueprints
1 parent cae0b46 commit 6546bbf

File tree

2 files changed

+113
-42
lines changed

2 files changed

+113
-42
lines changed

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

Lines changed: 103 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343

4444
clientConfig "sigs.k8s.io/controller-runtime/pkg/client/config"
4545

46+
clientset "k8s.io/client-go/kubernetes"
4647
apis "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator"
4748
cnsregistervolumev1alpha1 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/cnsregistervolume/v1alpha1"
4849
storagepolicyusagev1alpha2 "sigs.k8s.io/vsphere-csi-driver/v3/pkg/apis/cnsoperator/storagepolicy/v1alpha2"
@@ -551,53 +552,63 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
551552
setInstanceError(ctx, r, instance, "Duplicate Request")
552553
return reconcile.Result{RequeueAfter: timeout}, nil
553554
}
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)
558558
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))
562561
return reconcile.Result{RequeueAfter: timeout}, nil
563562
}
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)
572577
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))
589582
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)
597584
}
598-
return reconcile.Result{RequeueAfter: timeout}, nil
599585
}
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 {
601612
log.Errorf("Failed to create PVC with spec: %+v. Error: %+v", pvcSpec, err)
602613
setInstanceError(ctx, r, instance,
603614
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,
609620
setInstanceError(ctx, r, instance,
610621
fmt.Sprintf("Delete PV %s failed with error: %+v", pvName, err))
611622
return reconcile.Result{RequeueAfter: timeout}, nil
623+
} else {
624+
log.Infof("PVC: %s is created successfully", instance.Spec.PvcName)
612625
}
613-
} else {
614-
log.Infof("PVC: %s is created successfully", instance.Spec.PvcName)
615626
}
616627
// Watch for PVC to be bound.
617628
isBound, err := isPVCBound(ctx, k8sclient, pvc, time.Duration(1*time.Minute))
@@ -736,6 +747,56 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
736747
return reconcile.Result{}, nil
737748
}
738749

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+
739800
// validateCnsRegisterVolumeSpec validates the input params of
740801
// CnsRegisterVolume instance.
741802
func validateCnsRegisterVolumeSpec(ctx context.Context, instance *cnsregistervolumev1alpha1.CnsRegisterVolume) error {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ const (
4848
scResourceNameSuffix = ".storageclass.storage.k8s.io/requests.storage"
4949
)
5050

51+
var (
52+
// Supported data source types for PVCs
53+
supportedDataSourceTypes = []struct {
54+
apiGroup string
55+
kind string
56+
}{
57+
{"vmoperator.vmware.com", "VirtualMachine"},
58+
}
59+
)
60+
5161
// isDatastoreAccessibleToCluster verifies if the datastoreUrl is accessible to
5262
// cluster with clusterID.
5363
func isDatastoreAccessibleToCluster(ctx context.Context, vc *vsphere.VirtualCenter,

0 commit comments

Comments
 (0)