From 10c638bd8a39f602a32391d1c19e80e83babfa2e Mon Sep 17 00:00:00 2001 From: j-griffith Date: Fri, 21 Jun 2019 09:34:52 -0600 Subject: [PATCH] UPSTREAM: : PVC DataSource support This pulls in support for specifying object of kind `PersistentVolumeclaim` as a DataSource in pvc create manifests, enabling clone operations for CSI plugins that support it. --- .../pkg/api/persistentvolumeclaim/util.go | 31 ++++++- .../api/persistentvolumeclaim/util_test.go | 89 ++++++++++++++++++- .../k8s.io/kubernetes/pkg/apis/core/types.go | 13 +-- .../pkg/apis/core/validation/validation.go | 1 + .../apis/core/validation/validation_test.go | 69 ++++++++++++++ .../kubernetes/pkg/features/kube_features.go | 7 ++ 6 files changed, 202 insertions(+), 8 deletions(-) diff --git a/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util.go b/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util.go index 0c7116de5ec4..1ede41d0b817 100644 --- a/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util.go +++ b/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util.go @@ -22,13 +22,18 @@ import ( "k8s.io/kubernetes/pkg/features" ) +const ( + pvc string = "PersistentVolumeClaim" + volumeSnapshot string = "VolumeSnapshot" +) + // DropDisabledFields removes disabled fields from the pvc spec. // This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pvc spec. func DropDisabledFields(pvcSpec, oldPVCSpec *core.PersistentVolumeClaimSpec) { if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && !volumeModeInUse(oldPVCSpec) { pvcSpec.VolumeMode = nil } - if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeSnapshotDataSource) && !volumeSnapshotDataSourceInUse(oldPVCSpec) { + if !dataSourceIsEnabled(pvcSpec) && !dataSourceInUse(oldPVCSpec) { pvcSpec.DataSource = nil } } @@ -43,7 +48,7 @@ func volumeModeInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) bool { return false } -func volumeSnapshotDataSourceInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) bool { +func dataSourceInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) bool { if oldPVCSpec == nil { return false } @@ -52,3 +57,25 @@ func volumeSnapshotDataSourceInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) b } return false } + +func dataSourceIsEnabled(pvcSpec *core.PersistentVolumeClaimSpec) bool { + if pvcSpec.DataSource != nil { + apiGroup := "" + if pvcSpec.DataSource.APIGroup != nil { + apiGroup = *pvcSpec.DataSource.APIGroup + } + if utilfeature.DefaultFeatureGate.Enabled(features.VolumePVCDataSource) && + pvcSpec.DataSource.Kind == pvc && + apiGroup == "" { + return true + + } + + if utilfeature.DefaultFeatureGate.Enabled(features.VolumeSnapshotDataSource) && + pvcSpec.DataSource.Kind == volumeSnapshot && + apiGroup == "snapshot.storage.k8s.io" { + return true + } + } + return false +} diff --git a/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util_test.go b/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util_test.go index 5f8ec27014c2..333a169eada2 100644 --- a/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util_test.go +++ b/vendor/k8s.io/kubernetes/pkg/api/persistentvolumeclaim/util_test.go @@ -118,7 +118,7 @@ func TestDropAlphaPVCVolumeMode(t *testing.T) { } } -func TestDropDisabledDataSource(t *testing.T) { +func TestDropDisabledSnapshotDataSource(t *testing.T) { pvcWithoutDataSource := func() *core.PersistentVolumeClaim { return &core.PersistentVolumeClaim{ Spec: core.PersistentVolumeClaimSpec{ @@ -210,3 +210,90 @@ func TestDropDisabledDataSource(t *testing.T) { } } } + +// TestPVCDataSourceSpecFilter checks to ensure the DropDisabledFields function behaves correctly for PVCDataSource featuregate +func TestPVCDataSourceSpecFilter(t *testing.T) { + apiGroup := "" + validSpec := core.PersistentVolumeClaimSpec{ + DataSource: &core.TypedLocalObjectReference{ + APIGroup: &apiGroup, + Kind: "PersistentVolumeClaim", + Name: "test_clone", + }, + } + validSpecNilAPIGroup := core.PersistentVolumeClaimSpec{ + DataSource: &core.TypedLocalObjectReference{ + Kind: "PersistentVolumeClaim", + Name: "test_clone", + }, + } + + invalidAPIGroup := "invalid.pvc.api.group" + invalidSpec := core.PersistentVolumeClaimSpec{ + DataSource: &core.TypedLocalObjectReference{ + APIGroup: &invalidAPIGroup, + Kind: "PersistentVolumeClaim", + Name: "test_clone_invalid", + }, + } + + var tests = map[string]struct { + spec core.PersistentVolumeClaimSpec + gateEnabled bool + want *core.TypedLocalObjectReference + }{ + "enabled with empty ds": { + spec: core.PersistentVolumeClaimSpec{}, + gateEnabled: true, + want: nil, + }, + "enabled with invalid spec": { + spec: invalidSpec, + gateEnabled: true, + want: nil, + }, + "enabled with valid spec": { + spec: validSpec, + gateEnabled: true, + want: validSpec.DataSource, + }, + "disabled with invalid spec": { + spec: invalidSpec, + gateEnabled: false, + want: nil, + }, + "disabled with valid spec": { + spec: validSpec, + gateEnabled: false, + want: nil, + }, + "diabled with empty ds": { + spec: core.PersistentVolumeClaimSpec{}, + gateEnabled: false, + want: nil, + }, + "enabled with valid spec but nil APIGroup": { + spec: validSpecNilAPIGroup, + gateEnabled: true, + want: validSpecNilAPIGroup.DataSource, + }, + "disabled with valid spec but nil APIGroup": { + spec: validSpecNilAPIGroup, + gateEnabled: false, + want: nil, + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumePVCDataSource, test.gateEnabled)() + DropDisabledFields(&test.spec, nil) + if test.spec.DataSource != test.want { + t.Errorf("expected drop datasource condition was not met, test: %s, gateEnabled: %v, spec: %v, expected: %v", testName, test.gateEnabled, test.spec, test.want) + } + + }) + + } + +} diff --git a/vendor/k8s.io/kubernetes/pkg/apis/core/types.go b/vendor/k8s.io/kubernetes/pkg/apis/core/types.go index 157d38ced5f5..6e5c582179d5 100644 --- a/vendor/k8s.io/kubernetes/pkg/apis/core/types.go +++ b/vendor/k8s.io/kubernetes/pkg/apis/core/types.go @@ -416,11 +416,14 @@ type PersistentVolumeClaimSpec struct { // This is a beta feature. // +optional VolumeMode *PersistentVolumeMode - // This field requires the VolumeSnapshotDataSource alpha feature gate to be - // enabled and currently VolumeSnapshot is the only supported data source. - // If the provisioner can support VolumeSnapshot data source, it will create - // a new volume and data will be restored to the volume at the same time. - // If the provisioner does not support VolumeSnapshot data source, volume will + // This field can be used to specify either: + // * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + // * An existing PVC (PersistentVolumeClaim) + // In order to use either of these DataSource types, the appropriate feature gate + // must be enabled (VolumeSnapshotDataSource, VolumePVCDataSource) + // If the provisioner can support the specified data source, it will create + // a new volume based on the contents of the specified PVC or Snapshot. + // If the provisioner does not support the specified data source, the volume will // not be created and the failure will be reported as an event. // In the future, we plan to support more data source types and the behavior // of the provisioner may change. diff --git a/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation.go b/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation.go index 84b8a2ed9ec5..ea8233393c75 100644 --- a/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation.go +++ b/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation.go @@ -1524,6 +1524,7 @@ var supportedVolumeModes = sets.NewString(string(core.PersistentVolumeBlock), st var supportedDataSourceAPIGroupKinds = map[schema.GroupKind]bool{ {Group: "snapshot.storage.k8s.io", Kind: "VolumeSnapshot"}: true, + {Group: "", Kind: "PersistentVolumeClaim"}: true, } func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList { diff --git a/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation_test.go b/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation_test.go index ddb21670b9c5..6e7ae2e6d1e5 100644 --- a/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation_test.go +++ b/vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation_test.go @@ -13142,3 +13142,72 @@ func TestValidateOrSetClientIPAffinityConfig(t *testing.T) { func boolPtr(b bool) *bool { return &b } + +func testDataSourceInSpec(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 TestAlphaVolumePVCDataSource(t *testing.T) { + + testCases := []struct { + testName string + claimSpec core.PersistentVolumeClaimSpec + expectedFail bool + }{ + { + testName: "test create from valid snapshot source", + claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"), + }, + { + testName: "test create from valid pvc source", + claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""), + }, + { + testName: "test missing name in snapshot datasource should fail", + claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"), + expectedFail: true, + }, + { + testName: "test specifying pvc with snapshot api group should fail", + claimSpec: *testDataSourceInSpec("test_snapshot", "PersistentVolumeClaim", "snapshot.storage.k8s.io"), + expectedFail: true, + }, + { + testName: "test invalid group name in snapshot datasource should fail", + claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "storage.k8s.io"), + expectedFail: true, + }, + } + + for _, tc := range testCases { + if tc.expectedFail { + if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) == 0 { + t.Errorf("expected failure: %v", errs) + } + + } else { + if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + + } + } +} diff --git a/vendor/k8s.io/kubernetes/pkg/features/kube_features.go b/vendor/k8s.io/kubernetes/pkg/features/kube_features.go index 443876c16c5e..0d91eac92da6 100644 --- a/vendor/k8s.io/kubernetes/pkg/features/kube_features.go +++ b/vendor/k8s.io/kubernetes/pkg/features/kube_features.go @@ -433,6 +433,12 @@ const ( // // Enables the OpenStack Cinder in-tree driver to OpenStack Cinder CSI Driver migration feature. CSIMigrationOpenStack utilfeature.Feature = "CSIMigrationOpenStack" + + // owner: @j-griffith + // alpha: v1.15 + // + // Enable support for specifying an existing PVC as a DataSource + VolumePVCDataSource utilfeature.Feature = "VolumePVCDataSource" ) func init() { @@ -507,6 +513,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS TTLAfterFinished: {Default: false, PreRelease: utilfeature.Alpha}, KubeletPodResources: {Default: false, PreRelease: utilfeature.Alpha}, WindowsGMSA: {Default: false, PreRelease: utilfeature.Alpha}, + VolumePVCDataSource: {Default: false, PreRelease: utilfeature.Alpha}, // inherited features from generic apiserver, relisted here to get a conflict if it is changed // unintentionally on either side: