From 1089954fa6cc3e20ce14fad3df010403808c08a0 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 21 Jan 2020 11:57:55 +0100 Subject: [PATCH] CSIStorageCapacity: CSIDriver.Spec.StorageCapacity field This is needed to inform the Kubernetes pod scheduler whether it has to check CSIStorageCapacity objects for available capacity. --- pkg/apis/storage/fuzzer/fuzzer.go | 4 + pkg/apis/storage/types.go | 20 ++ pkg/apis/storage/v1/defaults.go | 4 + pkg/apis/storage/v1/defaults_test.go | 30 +++ pkg/apis/storage/v1beta1/defaults.go | 4 + pkg/apis/storage/v1beta1/defaults_test.go | 27 +++ pkg/apis/storage/validation/validation.go | 11 ++ pkg/printers/internalversion/printers.go | 21 +- pkg/registry/storage/csidriver/strategy.go | 13 +- .../storage/csidriver/strategy_test.go | 187 +++++++++++++----- staging/src/k8s.io/api/storage/v1/types.go | 20 ++ .../src/k8s.io/api/storage/v1beta1/types.go | 21 ++ 12 files changed, 311 insertions(+), 51 deletions(-) diff --git a/pkg/apis/storage/fuzzer/fuzzer.go b/pkg/apis/storage/fuzzer/fuzzer.go index a059856cc7f..ed8621899ab 100644 --- a/pkg/apis/storage/fuzzer/fuzzer.go +++ b/pkg/apis/storage/fuzzer/fuzzer.go @@ -78,6 +78,10 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { obj.Spec.PodInfoOnMount = new(bool) *(obj.Spec.PodInfoOnMount) = false } + if obj.Spec.StorageCapacity == nil { + obj.Spec.StorageCapacity = new(bool) + *(obj.Spec.StorageCapacity) = false + } if len(obj.Spec.VolumeLifecycleModes) == 0 { obj.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{ storage.VolumeLifecyclePersistent, diff --git a/pkg/apis/storage/types.go b/pkg/apis/storage/types.go index 5c741a17405..4cb484aabfe 100644 --- a/pkg/apis/storage/types.go +++ b/pkg/apis/storage/types.go @@ -309,6 +309,26 @@ type CSIDriverSpec struct { // more modes may be added in the future. // +optional VolumeLifecycleModes []VolumeLifecycleMode + + // If set to true, storageCapacity indicates that the CSI + // volume driver wants pod scheduling to consider the storage + // capacity that the driver deployment will report by creating + // CSIStorageCapacity objects with capacity information. + // + // The check can be enabled immediately when deploying a driver. + // In that case, provisioning new volumes with late binding + // will pause until the driver deployment has published + // some suitable CSIStorageCapacity object. + // + // Alternatively, the driver can be deployed with the field + // unset or false and it can be flipped later when storage + // capacity information has been published. + // + // This is an alpha field and only available when the CSIStorageCapacity + // feature is enabled. The default is false. + // + // +optional + StorageCapacity *bool } // VolumeLifecycleMode specifies how a CSI volume is used in Kubernetes. diff --git a/pkg/apis/storage/v1/defaults.go b/pkg/apis/storage/v1/defaults.go index 856ed98e2d5..4d9145a8f49 100644 --- a/pkg/apis/storage/v1/defaults.go +++ b/pkg/apis/storage/v1/defaults.go @@ -49,6 +49,10 @@ func SetDefaults_CSIDriver(obj *storagev1.CSIDriver) { obj.Spec.PodInfoOnMount = new(bool) *(obj.Spec.PodInfoOnMount) = false } + if obj.Spec.StorageCapacity == nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIStorageCapacity) { + obj.Spec.StorageCapacity = new(bool) + *(obj.Spec.StorageCapacity) = false + } if len(obj.Spec.VolumeLifecycleModes) == 0 && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1.VolumeLifecyclePersistent) } diff --git a/pkg/apis/storage/v1/defaults_test.go b/pkg/apis/storage/v1/defaults_test.go index 48a45a27199..4b7fc118169 100644 --- a/pkg/apis/storage/v1/defaults_test.go +++ b/pkg/apis/storage/v1/defaults_test.go @@ -22,8 +22,11 @@ import ( storagev1 "k8s.io/api/storage/v1" "k8s.io/apimachinery/pkg/runtime" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" _ "k8s.io/kubernetes/pkg/apis/storage/install" + "k8s.io/kubernetes/pkg/features" ) func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { @@ -47,6 +50,33 @@ func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { return obj3 } +func TestSetDefaultStorageCapacityEnabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIStorageCapacity, true)() + driver := &storagev1.CSIDriver{} + + // field should be defaulted + defaultStorageCapacity := false + output := roundTrip(t, runtime.Object(driver)).(*storagev1.CSIDriver) + outStorageCapacity := output.Spec.StorageCapacity + if outStorageCapacity == nil { + t.Errorf("Expected StorageCapacity to be defaulted to: %+v, got: nil", defaultStorageCapacity) + } else if *outStorageCapacity != defaultStorageCapacity { + t.Errorf("Expected StorageCapacity to be defaulted to: %+v, got: %+v", defaultStorageCapacity, outStorageCapacity) + } +} + +func TestSetDefaultStorageCapacityDisabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIStorageCapacity, false)() + driver := &storagev1.CSIDriver{} + + // field should not be defaulted + output := roundTrip(t, runtime.Object(driver)).(*storagev1.CSIDriver) + outStorageCapacity := output.Spec.StorageCapacity + if outStorageCapacity != nil { + t.Errorf("Expected StorageCapacity to remain nil, got: %+v", outStorageCapacity) + } +} + func TestSetDefaultVolumeBindingMode(t *testing.T) { class := &storagev1.StorageClass{} diff --git a/pkg/apis/storage/v1beta1/defaults.go b/pkg/apis/storage/v1beta1/defaults.go index 50cc4fe15c0..fc6e346c2ec 100644 --- a/pkg/apis/storage/v1beta1/defaults.go +++ b/pkg/apis/storage/v1beta1/defaults.go @@ -49,6 +49,10 @@ func SetDefaults_CSIDriver(obj *storagev1beta1.CSIDriver) { obj.Spec.PodInfoOnMount = new(bool) *(obj.Spec.PodInfoOnMount) = false } + if obj.Spec.StorageCapacity == nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIStorageCapacity) { + obj.Spec.StorageCapacity = new(bool) + *(obj.Spec.StorageCapacity) = false + } if len(obj.Spec.VolumeLifecycleModes) == 0 && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1beta1.VolumeLifecyclePersistent) } diff --git a/pkg/apis/storage/v1beta1/defaults_test.go b/pkg/apis/storage/v1beta1/defaults_test.go index a76efa5de24..b60641b1d46 100644 --- a/pkg/apis/storage/v1beta1/defaults_test.go +++ b/pkg/apis/storage/v1beta1/defaults_test.go @@ -85,6 +85,33 @@ func TestSetDefaultAttachRequired(t *testing.T) { } } +func TestSetDefaultStorageCapacityEnabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIStorageCapacity, true)() + driver := &storagev1beta1.CSIDriver{} + + // field should be defaulted + defaultStorageCapacity := false + output := roundTrip(t, runtime.Object(driver)).(*storagev1beta1.CSIDriver) + outStorageCapacity := output.Spec.StorageCapacity + if outStorageCapacity == nil { + t.Errorf("Expected StorageCapacity to be defaulted to: %+v, got: nil", defaultStorageCapacity) + } else if *outStorageCapacity != defaultStorageCapacity { + t.Errorf("Expected StorageCapacity to be defaulted to: %+v, got: %+v", defaultStorageCapacity, outStorageCapacity) + } +} + +func TestSetDefaultStorageCapacityDisabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIStorageCapacity, false)() + driver := &storagev1beta1.CSIDriver{} + + // field should not be defaulted + output := roundTrip(t, runtime.Object(driver)).(*storagev1beta1.CSIDriver) + outStorageCapacity := output.Spec.StorageCapacity + if outStorageCapacity != nil { + t.Errorf("Expected StorageCapacity to remain nil, got: %+v", outStorageCapacity) + } +} + func TestSetDefaultVolumeLifecycleModesEnabled(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)() driver := &storagev1beta1.CSIDriver{} diff --git a/pkg/apis/storage/validation/validation.go b/pkg/apis/storage/validation/validation.go index b299f5a03ef..03541bf66f8 100644 --- a/pkg/apis/storage/validation/validation.go +++ b/pkg/apis/storage/validation/validation.go @@ -419,6 +419,7 @@ func validateCSIDriverSpec( allErrs := field.ErrorList{} allErrs = append(allErrs, validateAttachRequired(spec.AttachRequired, fldPath.Child("attachedRequired"))...) allErrs = append(allErrs, validatePodInfoOnMount(spec.PodInfoOnMount, fldPath.Child("podInfoOnMount"))...) + allErrs = append(allErrs, validateStorageCapacity(spec.StorageCapacity, fldPath.Child("storageCapacity"))...) allErrs = append(allErrs, validateVolumeLifecycleModes(spec.VolumeLifecycleModes, fldPath.Child("volumeLifecycleModes"))...) return allErrs } @@ -443,6 +444,16 @@ func validatePodInfoOnMount(podInfoOnMount *bool, fldPath *field.Path) field.Err return allErrs } +// validateStorageCapacity tests if storageCapacity is set for CSIDriver. +func validateStorageCapacity(storageCapacity *bool, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if storageCapacity == nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIStorageCapacity) { + allErrs = append(allErrs, field.Required(fldPath, "")) + } + + return allErrs +} + // validateVolumeLifecycleModes tests if mode has one of the allowed values. func validateVolumeLifecycleModes(modes []storage.VolumeLifecycleMode, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} diff --git a/pkg/printers/internalversion/printers.go b/pkg/printers/internalversion/printers.go index ff47448d2ba..f31be801dae 100644 --- a/pkg/printers/internalversion/printers.go +++ b/pkg/printers/internalversion/printers.go @@ -46,6 +46,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/duration" "k8s.io/apimachinery/pkg/util/sets" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/autoscaling" @@ -64,6 +65,7 @@ import ( "k8s.io/kubernetes/pkg/apis/scheduling" "k8s.io/kubernetes/pkg/apis/storage" storageutil "k8s.io/kubernetes/pkg/apis/storage/util" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/node" ) @@ -510,9 +512,16 @@ func AddHandlers(h printers.PrintHandler) { {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, {Name: "AttachRequired", Type: "boolean", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["attachRequired"]}, {Name: "PodInfoOnMount", Type: "boolean", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["podInfoOnMount"]}, + } + if utilfeature.DefaultFeatureGate.Enabled(features.CSIStorageCapacity) { + csiDriverColumnDefinitions = append(csiDriverColumnDefinitions, metav1.TableColumnDefinition{ + Name: "StorageCapacity", Type: "boolean", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["storageCapacity"], + }) + } + csiDriverColumnDefinitions = append(csiDriverColumnDefinitions, []metav1.TableColumnDefinition{ {Name: "Modes", Type: "string", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["volumeLifecycleModes"]}, {Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]}, - } + }...) h.TableHandler(csiDriverColumnDefinitions, printCSIDriver) h.TableHandler(csiDriverColumnDefinitions, printCSIDriverList) @@ -1366,7 +1375,15 @@ func printCSIDriver(obj *storage.CSIDriver, options printers.GenerateOptions) ([ modes = "" } - row.Cells = append(row.Cells, obj.Name, attachRequired, podInfoOnMount, modes, translateTimestampSince(obj.CreationTimestamp)) + row.Cells = append(row.Cells, obj.Name, attachRequired, podInfoOnMount) + if utilfeature.DefaultFeatureGate.Enabled(features.CSIStorageCapacity) { + storageCapacity := false + if obj.Spec.StorageCapacity != nil { + storageCapacity = *obj.Spec.StorageCapacity + } + row.Cells = append(row.Cells, storageCapacity) + } + row.Cells = append(row.Cells, modes, translateTimestampSince(obj.CreationTimestamp)) return []metav1.TableRow{row}, nil } diff --git a/pkg/registry/storage/csidriver/strategy.go b/pkg/registry/storage/csidriver/strategy.go index a968fcbaf17..f406ef55885 100644 --- a/pkg/registry/storage/csidriver/strategy.go +++ b/pkg/registry/storage/csidriver/strategy.go @@ -43,8 +43,12 @@ func (csiDriverStrategy) NamespaceScoped() bool { return false } -// PrepareForCreate clears the VolumeLifecycleModes field if the corresponding feature is disabled. +// PrepareForCreate clears the fields for which the corresponding feature is disabled. func (csiDriverStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { + if !utilfeature.DefaultFeatureGate.Enabled(features.CSIStorageCapacity) { + csiDriver := obj.(*storage.CSIDriver) + csiDriver.Spec.StorageCapacity = nil + } if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { csiDriver := obj.(*storage.CSIDriver) csiDriver.Spec.VolumeLifecycleModes = nil @@ -68,10 +72,15 @@ func (csiDriverStrategy) AllowCreateOnUpdate() bool { return false } -// PrepareForUpdate clears the VolumeLifecycleModes field if the corresponding feature is disabled and +// PrepareForUpdate clears the fields for which the corresponding feature is disabled and // existing object does not already have that field set. This allows the field to remain when // downgrading to a version that has the feature disabled. func (csiDriverStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { + if old.(*storage.CSIDriver).Spec.StorageCapacity == nil && + !utilfeature.DefaultFeatureGate.Enabled(features.CSIStorageCapacity) { + newCSIDriver := obj.(*storage.CSIDriver) + newCSIDriver.Spec.StorageCapacity = nil + } if old.(*storage.CSIDriver).Spec.VolumeLifecycleModes == nil && !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { newCSIDriver := obj.(*storage.CSIDriver) diff --git a/pkg/registry/storage/csidriver/strategy_test.go b/pkg/registry/storage/csidriver/strategy_test.go index 7da19925dde..3088fb85694 100644 --- a/pkg/registry/storage/csidriver/strategy_test.go +++ b/pkg/registry/storage/csidriver/strategy_test.go @@ -30,15 +30,15 @@ import ( ) func getValidCSIDriver(name string) *storage.CSIDriver { - attachRequired := true - podInfoOnMount := true + enabled := true return &storage.CSIDriver{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, + AttachRequired: &enabled, + PodInfoOnMount: &enabled, + StorageCapacity: &enabled, }, } } @@ -87,22 +87,12 @@ func TestCSIDriverPrepareForCreate(t *testing.T) { attachRequired := true podInfoOnMount := true - csiDriver := &storage.CSIDriver{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }, - Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, - VolumeLifecycleModes: []storage.VolumeLifecycleMode{ - storage.VolumeLifecyclePersistent, - }, - }, - } + storageCapacity := true tests := []struct { - name string - withInline bool + name string + withCapacity bool + withInline bool }{ { name: "inline enabled", @@ -112,17 +102,48 @@ func TestCSIDriverPrepareForCreate(t *testing.T) { name: "inline disabled", withInline: false, }, + { + name: "capacity enabled", + withCapacity: true, + }, + { + name: "capacity disabled", + withCapacity: false, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIStorageCapacity, test.withCapacity)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, test.withInline)() + csiDriver := &storage.CSIDriver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: storage.CSIDriverSpec{ + AttachRequired: &attachRequired, + PodInfoOnMount: &podInfoOnMount, + StorageCapacity: &storageCapacity, + VolumeLifecycleModes: []storage.VolumeLifecycleMode{ + storage.VolumeLifecyclePersistent, + }, + }, + } Strategy.PrepareForCreate(ctx, csiDriver) errs := Strategy.Validate(ctx, csiDriver) if len(errs) != 0 { t.Errorf("unexpected validating errors: %v", errs) } + if test.withCapacity { + if csiDriver.Spec.StorageCapacity == nil || *csiDriver.Spec.StorageCapacity != storageCapacity { + t.Errorf("StorageCapacity modified: %v", csiDriver.Spec.StorageCapacity) + } + } else { + if csiDriver.Spec.StorageCapacity != nil { + t.Errorf("StorageCapacity not stripped: %v", csiDriver.Spec.StorageCapacity) + } + } if test.withInline { if len(csiDriver.Spec.VolumeLifecycleModes) != 1 { t.Errorf("VolumeLifecycleModes modified: %v", csiDriver.Spec) @@ -178,15 +199,69 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) { }, }, } + enabled := true + disabled := false + driverWithoutCapacity := &storage.CSIDriver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + } + driverWithCapacityEnabled := &storage.CSIDriver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: storage.CSIDriverSpec{ + StorageCapacity: &enabled, + }, + } + driverWithCapacityDisabled := &storage.CSIDriver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: storage.CSIDriverSpec{ + StorageCapacity: &disabled, + }, + } + var resultEmpty []storage.VolumeLifecycleMode resultPersistent := []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent} resultEphemeral := []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral} tests := []struct { - name string - old, update *storage.CSIDriver - withInline, withoutInline []storage.VolumeLifecycleMode + name string + old, update *storage.CSIDriver + withCapacity, withoutCapacity *bool + withInline, withoutInline []storage.VolumeLifecycleMode }{ + { + name: "before: no capacity, update: no capacity", + old: driverWithoutCapacity, + update: driverWithoutCapacity, + withCapacity: nil, + withoutCapacity: nil, + }, + { + name: "before: no capacity, update: enabled", + old: driverWithoutCapacity, + update: driverWithCapacityEnabled, + withCapacity: &enabled, + withoutCapacity: nil, + }, + { + name: "before: capacity enabled, update: disabled", + old: driverWithCapacityEnabled, + update: driverWithCapacityDisabled, + withCapacity: &disabled, + withoutCapacity: &disabled, + }, + { + name: "before: capacity enabled, update: no capacity", + old: driverWithCapacityEnabled, + update: driverWithoutCapacity, + withCapacity: nil, + withoutCapacity: nil, + }, + { name: "before: no mode, update: no mode", old: driverWithoutModes, @@ -217,35 +292,46 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) { }, } - runAll := func(t *testing.T, withInline bool) { + runAll := func(t *testing.T, withCapacity, withInline bool) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIStorageCapacity, withCapacity)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, withInline)() csiDriver := test.update.DeepCopy() Strategy.PrepareForUpdate(ctx, csiDriver, test.old) - if withInline { - require.Equal(t, csiDriver.Spec.VolumeLifecycleModes, test.withInline) + if withCapacity { + require.Equal(t, test.withCapacity, csiDriver.Spec.StorageCapacity) } else { - require.Equal(t, csiDriver.Spec.VolumeLifecycleModes, test.withoutInline) + require.Equal(t, test.withoutCapacity, csiDriver.Spec.StorageCapacity) + } + if withInline { + require.Equal(t, test.withInline, csiDriver.Spec.VolumeLifecycleModes) + } else { + require.Equal(t, test.withoutInline, csiDriver.Spec.VolumeLifecycleModes) } }) } } + t.Run("with capacity", func(t *testing.T) { + runAll(t, true, false) + }) + t.Run("without capacity", func(t *testing.T) { + runAll(t, false, false) + }) + t.Run("with inline volumes", func(t *testing.T) { - runAll(t, true) + runAll(t, false, true) }) t.Run("without inline volumes", func(t *testing.T) { - runAll(t, false) + runAll(t, false, false) }) } func TestCSIDriverValidation(t *testing.T) { - attachRequired := true - notAttachRequired := false - podInfoOnMount := true - notPodInfoOnMount := false + enabled := true + disabled := true tests := []struct { name string @@ -258,27 +344,29 @@ func TestCSIDriverValidation(t *testing.T) { false, }, { - "true PodInfoOnMount and AttachRequired", + "true for all flags", &storage.CSIDriver{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, + AttachRequired: &enabled, + PodInfoOnMount: &enabled, + StorageCapacity: &enabled, }, }, false, }, { - "false PodInfoOnMount and AttachRequired", + "false for all flags", &storage.CSIDriver{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, Spec: storage.CSIDriverSpec{ - AttachRequired: ¬AttachRequired, - PodInfoOnMount: ¬PodInfoOnMount, + AttachRequired: &disabled, + PodInfoOnMount: &disabled, + StorageCapacity: &disabled, }, }, false, @@ -290,8 +378,9 @@ func TestCSIDriverValidation(t *testing.T) { Name: "*foo#", }, Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, + AttachRequired: &enabled, + PodInfoOnMount: &enabled, + StorageCapacity: &enabled, }, }, true, @@ -303,8 +392,9 @@ func TestCSIDriverValidation(t *testing.T) { Name: "foo", }, Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, + AttachRequired: &enabled, + PodInfoOnMount: &enabled, + StorageCapacity: &enabled, VolumeLifecycleModes: []storage.VolumeLifecycleMode{ storage.VolumeLifecycleMode("no-such-mode"), }, @@ -319,8 +409,9 @@ func TestCSIDriverValidation(t *testing.T) { Name: "foo", }, Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, + AttachRequired: &enabled, + PodInfoOnMount: &enabled, + StorageCapacity: &enabled, VolumeLifecycleModes: []storage.VolumeLifecycleMode{ storage.VolumeLifecyclePersistent, }, @@ -335,8 +426,9 @@ func TestCSIDriverValidation(t *testing.T) { Name: "foo", }, Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, + AttachRequired: &enabled, + PodInfoOnMount: &enabled, + StorageCapacity: &enabled, VolumeLifecycleModes: []storage.VolumeLifecycleMode{ storage.VolumeLifecycleEphemeral, }, @@ -351,8 +443,9 @@ func TestCSIDriverValidation(t *testing.T) { Name: "foo", }, Spec: storage.CSIDriverSpec{ - AttachRequired: &attachRequired, - PodInfoOnMount: &podInfoOnMount, + AttachRequired: &enabled, + PodInfoOnMount: &enabled, + StorageCapacity: &enabled, VolumeLifecycleModes: []storage.VolumeLifecycleMode{ storage.VolumeLifecyclePersistent, storage.VolumeLifecycleEphemeral, diff --git a/staging/src/k8s.io/api/storage/v1/types.go b/staging/src/k8s.io/api/storage/v1/types.go index 556427bb20e..6f0b3ee465b 100644 --- a/staging/src/k8s.io/api/storage/v1/types.go +++ b/staging/src/k8s.io/api/storage/v1/types.go @@ -316,6 +316,26 @@ type CSIDriverSpec struct { // +optional // +listType=set VolumeLifecycleModes []VolumeLifecycleMode `json:"volumeLifecycleModes,omitempty" protobuf:"bytes,3,opt,name=volumeLifecycleModes"` + + // If set to true, storageCapacity indicates that the CSI + // volume driver wants pod scheduling to consider the storage + // capacity that the driver deployment will report by creating + // CSIStorageCapacity objects with capacity information. + // + // The check can be enabled immediately when deploying a driver. + // In that case, provisioning new volumes with late binding + // will pause until the driver deployment has published + // some suitable CSIStorageCapacity object. + // + // Alternatively, the driver can be deployed with the field + // unset or false and it can be flipped later when storage + // capacity information has been published. + // + // This is an alpha field and only available when the CSIStorageCapacity + // feature is enabled. The default is false. + // + // +optional + StorageCapacity *bool `json:"storageCapacity,omitempty" protobuf:"bytes,4,opt,name=storageCapacity"` } // VolumeLifecycleMode is an enumeration of possible usage modes for a volume diff --git a/staging/src/k8s.io/api/storage/v1beta1/types.go b/staging/src/k8s.io/api/storage/v1beta1/types.go index 95f647ab2f3..40d8ba20334 100644 --- a/staging/src/k8s.io/api/storage/v1beta1/types.go +++ b/staging/src/k8s.io/api/storage/v1beta1/types.go @@ -335,6 +335,27 @@ type CSIDriverSpec struct { // more modes may be added in the future. // +optional VolumeLifecycleModes []VolumeLifecycleMode `json:"volumeLifecycleModes,omitempty" protobuf:"bytes,3,opt,name=volumeLifecycleModes"` + + // If set to true, storageCapacity indicates that the CSI + // volume driver wants pod scheduling to consider the storage + // capacity that the driver deployment will report by creating + // CSIStorageCapacity objects with capacity information. + // + // + // The check can be enabled immediately when deploying a driver. + // In that case, provisioning new volumes with late binding + // will pause until the driver deployment has published + // some suitable CSIStorageCapacity object. + // + // Alternatively, the driver can be deployed with the field + // unset or false and it can be flipped later when storage + // capacity information has been published. + // + // This is an alpha field and only available when the CSIStorageCapacity + // feature is enabled. The default is false. + // + // +optional + StorageCapacity *bool `json:"storageCapacity,omitempty" protobuf:"bytes,4,opt,name=storageCapacity"` } // VolumeLifecycleMode is an enumeration of possible usage modes for a volume