Skip to content

Commit f47f0fc

Browse files
xing-yangnikolay-andreev
authored andcommitted
Add support for PVCs in captured blueprints (kubernetes-sigs#3449)
1 parent 3f192af commit f47f0fc

File tree

12 files changed

+283
-53
lines changed

12 files changed

+283
-53
lines changed

cmd/syncer/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ func initSyncerComponents(ctx context.Context, clusterFlavor cnstypes.CnsCluster
379379
}()
380380

381381
if clusterFlavor == cnstypes.CnsClusterFlavorWorkload &&
382-
commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.WCP_VMService_BYOK) {
382+
commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.BYOKEncryptionCapability) {
383383
// Start BYOK Operator for Supervisor clusters.
384384
go func() {
385385
defer func() {

manifests/supervisorcluster/1.28/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,6 @@ data:
505505
"vdpp-on-stretched-supervisor": "true"
506506
"cns-unregister-volume": "false"
507507
"workload-domain-isolation": "false"
508-
"WCP_VMService_BYOK": "true"
509508
kind: ConfigMap
510509
metadata:
511510
name: csi-feature-states

manifests/supervisorcluster/1.29/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@ data:
569569
"vdpp-on-stretched-supervisor": "true"
570570
"cns-unregister-volume": "false"
571571
"workload-domain-isolation": "false"
572-
"WCP_VMService_BYOK": "true"
573572
"sv-pvc-snapshot-protection-finalizer": "false"
574573
"file-volume-with-vm-service": "false"
575574
"csi-transaction-support": "false"

manifests/supervisorcluster/1.30/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@ data:
569569
"vdpp-on-stretched-supervisor": "true"
570570
"cns-unregister-volume": "false"
571571
"workload-domain-isolation": "false"
572-
"WCP_VMService_BYOK": "true"
573572
"sv-pvc-snapshot-protection-finalizer": "false"
574573
"file-volume-with-vm-service": "false"
575574
"csi-transaction-support": "false"

manifests/supervisorcluster/1.31/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@ data:
569569
"vdpp-on-stretched-supervisor": "true"
570570
"cns-unregister-volume": "false"
571571
"workload-domain-isolation": "false"
572-
"WCP_VMService_BYOK": "true"
573572
"sv-pvc-snapshot-protection-finalizer": "false"
574573
"file-volume-with-vm-service": "false"
575574
"csi-transaction-support": "false"

manifests/supervisorcluster/1.32/cns-csi.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@ data:
569569
"vdpp-on-stretched-supervisor": "true"
570570
"cns-unregister-volume": "false"
571571
"workload-domain-isolation": "false"
572-
"WCP_VMService_BYOK": "true"
573572
"sv-pvc-snapshot-protection-finalizer": "false"
574573
"file-volume-with-vm-service": "false"
575574
"csi-transaction-support": "false"

pkg/csi/service/common/constants.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,8 @@ const (
437437
MultipleClustersPerVsphereZone = "supports_multiple_clusters_per_zone"
438438
// VPCCapabilitySupervisor is a supervisor capability indicating if VPC FSS is enabled
439439
VPCCapabilitySupervisor = "VPC_Supported"
440-
// WCP_VMService_BYOK_FSS enables Bring Your Own Key (BYOK) capabilities.
441-
WCP_VMService_BYOK = "WCP_VMService_BYOK"
440+
// supports_BYOK_encryption enables Bring Your Own Key (BYOK) encryption capabilities.
441+
BYOKEncryptionCapability = "supports_BYOK_encryption"
442442
// SVPVCSnapshotProtectionFinalizer is FSS that controls add/remove
443443
// CNS finalizer on supervisor PVC/Snapshots from PVCSI
444444
SVPVCSnapshotProtectionFinalizer = "sv-pvc-snapshot-protection-finalizer"

pkg/csi/service/wcp/controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func (c *controller) Init(config *cnsconfig.Config, version string) error {
189189

190190
var cryptoClient crypto.Client
191191

192-
if commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.WCP_VMService_BYOK) {
192+
if commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.BYOKEncryptionCapability) {
193193
var err error
194194
if cryptoClient, err = crypto.NewClientWithDefaultConfig(ctx); err != nil {
195195
return logger.LogNewErrorf(log, "failed to create an instance of crypto client. err=%v", err)
@@ -811,7 +811,7 @@ func (c *controller) createBlockVolume(ctx context.Context, req *csi.CreateVolum
811811
}
812812

813813
var cryptoKeyID *common.CryptoKeyID
814-
isByokEnabled := commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.WCP_VMService_BYOK)
814+
isByokEnabled := commonco.ContainerOrchestratorUtility.IsFSSEnabled(ctx, common.BYOKEncryptionCapability)
815815
if isByokEnabled {
816816
if encClass, err := c.manager.CryptoClient.GetEncryptionClassForPVC(
817817
ctx,

pkg/syncer/admissionhandler/admissionhandler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func StartWebhookServer(ctx context.Context, enableWebhookClientCertVerification
148148
if clusterFlavor == cnstypes.CnsClusterFlavorWorkload {
149149
featureGateTKGSHaEnabled = containerOrchestratorUtility.IsFSSEnabled(ctx, common.TKGsHA)
150150
featureGateBlockVolumeSnapshotEnabled = containerOrchestratorUtility.IsFSSEnabled(ctx, common.BlockVolumeSnapshot)
151-
featureGateByokEnabled = containerOrchestratorUtility.IsFSSEnabled(ctx, common.WCP_VMService_BYOK)
151+
featureGateByokEnabled = containerOrchestratorUtility.IsFSSEnabled(ctx, common.BYOKEncryptionCapability)
152152
featureIsSharedDiskEnabled = containerOrchestratorUtility.IsFSSEnabled(ctx, common.SharedDiskFss)
153153
featureFileVolumesWithVmServiceEnabled = containerOrchestratorUtility.IsFSSEnabled(ctx,
154154
common.FileVolumesWithVmService)

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

Lines changed: 108 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"
@@ -557,53 +558,65 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
557558
setInstanceError(ctx, r, instance, "Duplicate Request")
558559
return reconcile.Result{RequeueAfter: timeout}, nil
559560
}
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)
564564
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))
568569
return reconcile.Result{RequeueAfter: timeout}, nil
569570
}
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)
578585
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))
595590
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)
603592
}
604-
return reconcile.Result{RequeueAfter: timeout}, nil
605593
}
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 {
607620
log.Errorf("Failed to create PVC with spec: %+v. Error: %+v", pvcSpec, err)
608621
setInstanceError(ctx, r, instance,
609622
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,
615628
setInstanceError(ctx, r, instance,
616629
fmt.Sprintf("Delete PV %s failed with error: %+v", pvName, err))
617630
return reconcile.Result{RequeueAfter: timeout}, nil
631+
} else {
632+
log.Infof("PVC: %s is created successfully", instance.Spec.PvcName)
618633
}
619-
} else {
620-
log.Infof("PVC: %s is created successfully", instance.Spec.PvcName)
621634
}
622635
// Watch for PVC to be bound.
623636
isBound, err := isPVCBound(ctx, k8sclient, pvc, time.Duration(1*time.Minute))
@@ -742,6 +755,59 @@ func (r *ReconcileCnsRegisterVolume) Reconcile(ctx context.Context,
742755
return reconcile.Result{}, nil
743756
}
744757

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

0 commit comments

Comments
 (0)