diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index cf5584dd9db..eb22aee1899 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -1824,6 +1824,22 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld } else if spec.VolumeMode != nil && !supportedVolumeModes.Has(string(*spec.VolumeMode)) { allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumeMode"), *spec.VolumeMode, supportedVolumeModes.List())) } + + if spec.DataSource != nil && !utilfeature.DefaultFeatureGate.Enabled(features.VolumeSnapshotDataSource) { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("dataSource"), "VolumeSnapshotDataSource is disabled by feature-gate")) + spec.DataSource = nil + } else if spec.DataSource != nil { + if len(spec.DataSource.Name) == 0 { + allErrs = append(allErrs, field.Required(fldPath.Child("dataSource").Key(string("Name")), "VolumeSnapshotDataSource Name cannot be empty")) + } + if spec.DataSource.Kind != "VolumeSnapshot" { + allErrs = append(allErrs, field.Invalid(fldPath.Child("dataSource"), spec.DataSource.Kind, "expected DataSource Kind is VolumeSnapshot")) + } + if spec.DataSource.APIGroup != "snapshot.storage.k8s.io" { + allErrs = append(allErrs, field.Invalid(fldPath.Child("dataSource"), spec.DataSource.APIGroup, "expected DataSource APIGroup is snapshot.storage.k8s.io")) + } + } + return allErrs } diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index 44567293493..7a5a8b08a91 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -713,6 +713,67 @@ func testVolumeClaimStorageClassInSpec(name, namespace, scName string, spec core } } +func testVolumeSnapshotDataSourceInSpec(name string, kind string, apiGroup string) *core.PersistentVolumeClaimSpec { + scName := "csi-plugin" + dataSourceInSpec := core.PersistentVolumeClaimSpec{ + AccessModes: []core.PersistentVolumeAccessMode{ + core.ReadOnlyMany, + }, + Resources: core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceName(core.ResourceStorage): resource.MustParse("10G"), + }, + }, + StorageClassName: &scName, + DataSource: &core.TypedLocalObjectReference{ + APIGroup: apiGroup, + Kind: kind, + Name: name, + }, + } + + return &dataSourceInSpec +} + +func TestAlphaVolumeSnapshotDataSource(t *testing.T) { + successTestCases := []core.PersistentVolumeClaimSpec{ + *testVolumeSnapshotDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"), + } + failedTestCases := []core.PersistentVolumeClaimSpec{ + *testVolumeSnapshotDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"), + *testVolumeSnapshotDataSourceInSpec("test_snapshot", "PersistentVolumeClaim", "snapshot.storage.k8s.io"), + *testVolumeSnapshotDataSourceInSpec("test_snapshot", "VolumeSnapshot", "storage.k8s.io"), + } + + // Enable alpha feature VolumeSnapshotDataSource + err := utilfeature.DefaultFeatureGate.Set("VolumeSnapshotDataSource=true") + if err != nil { + t.Errorf("Failed to enable feature gate for VolumeSnapshotDataSource: %v", err) + return + } + for _, tc := range successTestCases { + if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec")); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + for _, tc := range failedTestCases { + if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec")); len(errs) == 0 { + t.Errorf("expected failure: %v", errs) + } + } + // Disable alpha feature VolumeSnapshotDataSource + err = utilfeature.DefaultFeatureGate.Set("VolumeSnapshotDataSource=false") + if err != nil { + t.Errorf("Failed to disable feature gate for VolumeSnapshotDataSource: %v", err) + return + } + for _, tc := range successTestCases { + if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec")); len(errs) == 0 { + t.Errorf("expected failure: %v", errs) + } + } +} + func testVolumeClaimStorageClassInAnnotationAndSpec(name, namespace, scNameInAnn, scName string, spec core.PersistentVolumeClaimSpec) *core.PersistentVolumeClaim { spec.StorageClassName = &scName return &core.PersistentVolumeClaim{