Skip to content

Commit e8b09d3

Browse files
committed
Add AnyVolumeDataSource feature gate
Allow any custom resource to be the data source of a PVC, if the AnyVolumeDataSource feature gate is enabled. This is an alpha feature.
1 parent e865c0b commit e8b09d3

File tree

11 files changed

+155
-36
lines changed

11 files changed

+155
-36
lines changed

api/openapi-spec/swagger.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/api/persistentvolumeclaim/util.go

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ func dataSourceInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) bool {
4747

4848
func dataSourceIsEnabled(pvcSpec *core.PersistentVolumeClaimSpec) bool {
4949
if pvcSpec.DataSource != nil {
50+
if utilfeature.DefaultFeatureGate.Enabled(features.AnyVolumeDataSource) {
51+
return true
52+
}
53+
5054
apiGroup := ""
5155
if pvcSpec.DataSource.APIGroup != nil {
5256
apiGroup = *pvcSpec.DataSource.APIGroup

pkg/api/persistentvolumeclaim/util_test.go

+111
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ func TestDropDisabledSnapshotDataSource(t *testing.T) {
7171
},
7272
}
7373

74+
// Ensure that any data sources aren't enabled for this test
75+
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, false)()
76+
7477
for _, enabled := range []bool{true, false} {
7578
for _, oldpvcInfo := range pvcInfo {
7679
for _, newpvcInfo := range pvcInfo {
@@ -169,6 +172,9 @@ func TestPVCDataSourceSpecFilter(t *testing.T) {
169172
},
170173
}
171174

175+
// Ensure that any data sources aren't enabled for this test
176+
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, false)()
177+
172178
for testName, test := range tests {
173179
t.Run(testName, func(t *testing.T) {
174180
DropDisabledFields(&test.spec, nil)
@@ -181,3 +187,108 @@ func TestPVCDataSourceSpecFilter(t *testing.T) {
181187
}
182188

183189
}
190+
191+
// TestAnyDataSourceFilter checks to ensure the AnyVolumeDataSource feature gate works
192+
func TestAnyDataSourceFilter(t *testing.T) {
193+
makeDataSource := func(apiGroup, kind, name string) *core.TypedLocalObjectReference {
194+
return &core.TypedLocalObjectReference{
195+
APIGroup: &apiGroup,
196+
Kind: kind,
197+
Name: name,
198+
}
199+
}
200+
201+
volumeDataSource := makeDataSource("", "PersistentVolumeClaim", "my-vol")
202+
snapshotDataSource := makeDataSource("snapshot.storage.k8s.io", "VolumeSnapshot", "my-snap")
203+
genericDataSource := makeDataSource("generic.storage.k8s.io", "Generic", "my-foo")
204+
205+
var tests = map[string]struct {
206+
spec core.PersistentVolumeClaimSpec
207+
snapshotEnabled bool
208+
anyEnabled bool
209+
want *core.TypedLocalObjectReference
210+
}{
211+
"both disabled with empty ds": {
212+
spec: core.PersistentVolumeClaimSpec{},
213+
want: nil,
214+
},
215+
"both disabled with volume ds": {
216+
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource},
217+
want: volumeDataSource,
218+
},
219+
"both disabled with snapshot ds": {
220+
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
221+
want: nil,
222+
},
223+
"both disabled with generic ds": {
224+
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
225+
want: nil,
226+
},
227+
"any enabled with empty ds": {
228+
spec: core.PersistentVolumeClaimSpec{},
229+
anyEnabled: true,
230+
want: nil,
231+
},
232+
"any enabled with volume ds": {
233+
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource},
234+
anyEnabled: true,
235+
want: volumeDataSource,
236+
},
237+
"any enabled with snapshot ds": {
238+
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
239+
anyEnabled: true,
240+
want: snapshotDataSource,
241+
},
242+
"any enabled with generic ds": {
243+
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
244+
anyEnabled: true,
245+
want: genericDataSource,
246+
},
247+
"snapshot enabled with snapshot ds": {
248+
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
249+
snapshotEnabled: true,
250+
want: snapshotDataSource,
251+
},
252+
"snapshot enabled with generic ds": {
253+
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
254+
snapshotEnabled: true,
255+
want: nil,
256+
},
257+
"both enabled with empty ds": {
258+
spec: core.PersistentVolumeClaimSpec{},
259+
snapshotEnabled: true,
260+
anyEnabled: true,
261+
want: nil,
262+
},
263+
"both enabled with volume ds": {
264+
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource},
265+
snapshotEnabled: true,
266+
anyEnabled: true,
267+
want: volumeDataSource,
268+
},
269+
"both enabled with snapshot ds": {
270+
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
271+
snapshotEnabled: true,
272+
anyEnabled: true,
273+
want: snapshotDataSource,
274+
},
275+
"both enabled with generic ds": {
276+
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
277+
snapshotEnabled: true,
278+
anyEnabled: true,
279+
want: genericDataSource,
280+
},
281+
}
282+
283+
for testName, test := range tests {
284+
t.Run(testName, func(t *testing.T) {
285+
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeSnapshotDataSource, test.snapshotEnabled)()
286+
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled)()
287+
DropDisabledFields(&test.spec, nil)
288+
if test.spec.DataSource != test.want {
289+
t.Errorf("expected condition was not met, test: %s, snapshotEnabled: %v, anyEnabled: %v, spec: %v, expected: %v",
290+
testName, test.snapshotEnabled, test.anyEnabled, test.spec, test.want)
291+
}
292+
})
293+
}
294+
}

pkg/apis/core/types.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,12 @@ type PersistentVolumeClaimSpec struct {
420420
// This field can be used to specify either:
421421
// * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot - Beta)
422422
// * An existing PVC (PersistentVolumeClaim)
423+
// * An existing custom resource/object that implements data population (Alpha)
423424
// In order to use VolumeSnapshot object types, the appropriate feature gate
424-
// must be enabled (VolumeSnapshotDataSource)
425-
// If the provisioner can support the specified data source, it will create
426-
// a new volume based on the contents of the specified PVC or Snapshot.
427-
// If the provisioner does not support the specified data source, the volume will
425+
// must be enabled (VolumeSnapshotDataSource or AnyVolumeDataSource)
426+
// If the provisioner or an external controller can support the specified data source,
427+
// it will create a new volume based on the contents of the specified data source.
428+
// If the specified data source is not supported, the volume will
428429
// not be created and the failure will be reported as an event.
429430
// In the future, we plan to support more data source types and the behavior
430431
// of the provisioner may change.

pkg/apis/core/validation/BUILD

-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ go_library(
3333
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
3434
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library",
3535
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
36-
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
3736
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
3837
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
3938
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",

pkg/apis/core/validation/validation.go

+7-15
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import (
3838
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3939
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
4040
"k8s.io/apimachinery/pkg/labels"
41-
"k8s.io/apimachinery/pkg/runtime/schema"
4241
"k8s.io/apimachinery/pkg/util/diff"
4342
"k8s.io/apimachinery/pkg/util/intstr"
4443
"k8s.io/apimachinery/pkg/util/sets"
@@ -1563,11 +1562,6 @@ var supportedReclaimPolicy = sets.NewString(string(core.PersistentVolumeReclaimD
15631562

15641563
var supportedVolumeModes = sets.NewString(string(core.PersistentVolumeBlock), string(core.PersistentVolumeFilesystem))
15651564

1566-
var supportedDataSourceAPIGroupKinds = map[schema.GroupKind]bool{
1567-
{Group: "snapshot.storage.k8s.io", Kind: "VolumeSnapshot"}: true,
1568-
{Group: "", Kind: "PersistentVolumeClaim"}: true,
1569-
}
1570-
15711565
func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName string, validateInlinePersistentVolumeSpec bool, fldPath *field.Path) field.ErrorList {
15721566
allErrs := field.ErrorList{}
15731567

@@ -1929,17 +1923,15 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
19291923
if len(spec.DataSource.Name) == 0 {
19301924
allErrs = append(allErrs, field.Required(fldPath.Child("dataSource", "name"), ""))
19311925
}
1932-
1933-
groupKind := schema.GroupKind{Group: "", Kind: spec.DataSource.Kind}
1934-
if spec.DataSource.APIGroup != nil {
1935-
groupKind.Group = string(*spec.DataSource.APIGroup)
1926+
if len(spec.DataSource.Kind) == 0 {
1927+
allErrs = append(allErrs, field.Required(fldPath.Child("dataSource", "kind"), ""))
19361928
}
1937-
groupKindList := make([]string, 0, len(supportedDataSourceAPIGroupKinds))
1938-
for grp := range supportedDataSourceAPIGroupKinds {
1939-
groupKindList = append(groupKindList, grp.String())
1929+
apiGroup := ""
1930+
if spec.DataSource.APIGroup != nil {
1931+
apiGroup = *spec.DataSource.APIGroup
19401932
}
1941-
if !supportedDataSourceAPIGroupKinds[groupKind] {
1942-
allErrs = append(allErrs, field.NotSupported(fldPath.Child("dataSource"), groupKind.String(), groupKindList))
1933+
if len(apiGroup) == 0 && spec.DataSource.Kind != "PersistentVolumeClaim" {
1934+
allErrs = append(allErrs, field.Invalid(fldPath.Child("dataSource"), spec.DataSource.Kind, ""))
19431935
}
19441936
}
19451937

pkg/apis/core/validation/validation_test.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -904,8 +904,7 @@ func TestAlphaVolumeSnapshotDataSource(t *testing.T) {
904904
}
905905
failedTestCases := []core.PersistentVolumeClaimSpec{
906906
*testVolumeSnapshotDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
907-
*testVolumeSnapshotDataSourceInSpec("test_snapshot", "PersistentVolumeClaim", "snapshot.storage.k8s.io"),
908-
*testVolumeSnapshotDataSourceInSpec("test_snapshot", "VolumeSnapshot", "storage.k8s.io"),
907+
*testVolumeSnapshotDataSourceInSpec("test_snapshot", "", "snapshot.storage.k8s.io"),
909908
}
910909

911910
for _, tc := range successTestCases {
@@ -14864,13 +14863,17 @@ func TestAlphaVolumePVCDataSource(t *testing.T) {
1486414863
expectedFail: true,
1486514864
},
1486614865
{
14867-
testName: "test specifying pvc with snapshot api group should fail",
14868-
claimSpec: *testDataSourceInSpec("test_snapshot", "PersistentVolumeClaim", "snapshot.storage.k8s.io"),
14866+
testName: "test missing kind in snapshot datasource should fail",
14867+
claimSpec: *testDataSourceInSpec("test_snapshot", "", "snapshot.storage.k8s.io"),
1486914868
expectedFail: true,
1487014869
},
1487114870
{
14872-
testName: "test invalid group name in snapshot datasource should fail",
14873-
claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "storage.k8s.io"),
14871+
testName: "test create from valid generic custom resource source",
14872+
claimSpec: *testDataSourceInSpec("test_generic", "Generic", "generic.storage.k8s.io"),
14873+
},
14874+
{
14875+
testName: "test invalid datasource should fail",
14876+
claimSpec: *testDataSourceInSpec("test_pod", "Pod", ""),
1487414877
expectedFail: true,
1487514878
},
1487614879
}

pkg/features/kube_features.go

+7
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,12 @@ const (
564564
// e.g. emptyDir:
565565
// medium: HugePages-1Gi
566566
HugePageStorageMediumSize featuregate.Feature = "HugePageStorageMediumSize"
567+
568+
// owner: @bswartz
569+
// alpha: v1.18
570+
//
571+
// Enables usage of any object for volume data source in PVCs
572+
AnyVolumeDataSource featuregate.Feature = "AnyVolumeDataSource"
567573
)
568574

569575
func init() {
@@ -652,6 +658,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
652658
ImmutableEphemeralVolumes: {Default: false, PreRelease: featuregate.Alpha},
653659
DefaultIngressClass: {Default: true, PreRelease: featuregate.Beta},
654660
HugePageStorageMediumSize: {Default: false, PreRelease: featuregate.Alpha},
661+
AnyVolumeDataSource: {Default: false, PreRelease: featuregate.Alpha},
655662

656663
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
657664
// unintentionally on either side:

staging/src/k8s.io/api/core/v1/generated.proto

+5-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/src/k8s.io/api/core/v1/types.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -464,11 +464,12 @@ type PersistentVolumeClaimSpec struct {
464464
// This field can be used to specify either:
465465
// * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot - Beta)
466466
// * An existing PVC (PersistentVolumeClaim)
467+
// * An existing custom resource/object that implements data population (Alpha)
467468
// In order to use VolumeSnapshot object types, the appropriate feature gate
468-
// must be enabled (VolumeSnapshotDataSource)
469-
// If the provisioner can support the specified data source, it will create
470-
// a new volume based on the contents of the specified PVC or Snapshot.
471-
// If the provisioner does not support the specified data source, the volume will
469+
// must be enabled (VolumeSnapshotDataSource or AnyVolumeDataSource)
470+
// If the provisioner or an external controller can support the specified data source,
471+
// it will create a new volume based on the contents of the specified data source.
472+
// If the specified data source is not supported, the volume will
472473
// not be created and the failure will be reported as an event.
473474
// In the future, we plan to support more data source types and the behavior
474475
// of the provisioner may change.

staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)