From 417508abde6ee6846aa0ad7c0171d060cf4812c2 Mon Sep 17 00:00:00 2001 From: Deepak Kinni Date: Fri, 1 Mar 2024 13:00:02 -0800 Subject: [PATCH] Fail the Restore if the StorageClass is associated with WaitForFirstConsumer volumeBindingMode (#568) Signed-off-by: Deepak Kinni Co-authored-by: Deepak Kinni --- pkg/plugin/restore_pvc_action_plugin.go | 6 ++++ pkg/plugin/util/util.go | 44 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/pkg/plugin/restore_pvc_action_plugin.go b/pkg/plugin/restore_pvc_action_plugin.go index 5ff0fc7a..6766b12f 100644 --- a/pkg/plugin/restore_pvc_action_plugin.go +++ b/pkg/plugin/restore_pvc_action_plugin.go @@ -204,6 +204,12 @@ func (p *NewPVCRestoreItemAction) Execute(input *velero.RestoreItemActionExecute } } + // Check the StorageClass to validate that volumeBindingMode is not WaitForFirstConsumer + if err = pluginItem.ValidateRestoreStorageClass(restConfig, &itemSnapshot, p.Log); err != nil { + p.Log.Errorf("Failed to validate restore StorageClass VolumeBindingMode") + return nil, errors.WithStack(err) + } + snapshotID := itemSnapshot.Status.SnapshotID snapshotMetadata := itemSnapshot.Status.Metadata apiGroup := itemSnapshot.Spec.APIGroup diff --git a/pkg/plugin/util/util.go b/pkg/plugin/util/util.go index 796e411b..acdd4d04 100644 --- a/pkg/plugin/util/util.go +++ b/pkg/plugin/util/util.go @@ -16,6 +16,7 @@ import ( "github.com/vmware-tanzu/velero-plugin-for-vsphere/pkg/utils" "github.com/vmware-tanzu/velero/pkg/podvolume" corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -227,6 +228,49 @@ func RetrieveStorageClassMapping(config *rest.Config, veleroNs string, logger lo return configMaps.Items[0].Data, nil } +func ValidateRestoreStorageClass(config *rest.Config, itemSnapshot *backupdriverv1.Snapshot, logger logrus.FieldLogger) error { + if itemSnapshot == nil { + return errors.New("itemSnapshot is nil, unable to retrieve the StorageClass for validation") + } + + var err error + snapshotMetadata := itemSnapshot.Status.Metadata + + pvc := &corev1.PersistentVolumeClaim{} + if err = pvc.Unmarshal(snapshotMetadata); err != nil { + logger.WithError(err).Error("Failed to unmarshal snapshotMetadata when validating StorageClass") + return err + } + + if pvc.Spec.StorageClassName == nil || *pvc.Spec.StorageClassName == "" { + errMsg := fmt.Sprintf("PVC %s/%s has no storage class, unable to validate StorageClass", + pvc.Namespace, pvc.Name) + logger.Error(errMsg) + return errors.New(errMsg) + } + + restoreStorageClassName := *pvc.Spec.StorageClassName + // validate that new storage class exists + clientset, err := GetKubeClient(config, logger) + if err != nil { + logger.Error("Failed to get core v1 client from given config") + return err + } + restoreStorageClass, err := clientset.StorageV1().StorageClasses().Get(context.TODO(), restoreStorageClassName, metav1.GetOptions{}) + if err != nil { + return errors.Wrapf(err, "error getting storage class %s for validation", + restoreStorageClassName) + } + if restoreStorageClass.VolumeBindingMode != nil && *restoreStorageClass.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer { + errMsg := fmt.Sprintf("The PVC to be restored is associated with StorageClass with " + + "WaitForFirstConsumer VolumeBindingMode, this is currently not supported. " + + "Only StorageClass with Immediate VolumeBindingMode is supported.") + logger.Error(errMsg) + return errors.New(errMsg) + } + return nil +} + func UpdateSnapshotWithNewStorageClass(config *rest.Config, itemSnapshot *backupdriverv1.Snapshot, storageClassMapping map[string]string, logger logrus.FieldLogger) (backupdriverv1.Snapshot, error) { if itemSnapshot == nil { return backupdriverv1.Snapshot{}, errors.New("itemSnapshot is nil, unable to update")