mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
CSIStorageCapacity: CSIDriver.Spec.StorageCapacity field
This is needed to inform the Kubernetes pod scheduler whether it has to check CSIStorageCapacity objects for available capacity.
This commit is contained in:
parent
22aeb81e84
commit
1089954fa6
@ -78,6 +78,10 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||||||
obj.Spec.PodInfoOnMount = new(bool)
|
obj.Spec.PodInfoOnMount = new(bool)
|
||||||
*(obj.Spec.PodInfoOnMount) = false
|
*(obj.Spec.PodInfoOnMount) = false
|
||||||
}
|
}
|
||||||
|
if obj.Spec.StorageCapacity == nil {
|
||||||
|
obj.Spec.StorageCapacity = new(bool)
|
||||||
|
*(obj.Spec.StorageCapacity) = false
|
||||||
|
}
|
||||||
if len(obj.Spec.VolumeLifecycleModes) == 0 {
|
if len(obj.Spec.VolumeLifecycleModes) == 0 {
|
||||||
obj.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
|
obj.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
|
||||||
storage.VolumeLifecyclePersistent,
|
storage.VolumeLifecyclePersistent,
|
||||||
|
@ -309,6 +309,26 @@ type CSIDriverSpec struct {
|
|||||||
// more modes may be added in the future.
|
// more modes may be added in the future.
|
||||||
// +optional
|
// +optional
|
||||||
VolumeLifecycleModes []VolumeLifecycleMode
|
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.
|
// VolumeLifecycleMode specifies how a CSI volume is used in Kubernetes.
|
||||||
|
@ -49,6 +49,10 @@ func SetDefaults_CSIDriver(obj *storagev1.CSIDriver) {
|
|||||||
obj.Spec.PodInfoOnMount = new(bool)
|
obj.Spec.PodInfoOnMount = new(bool)
|
||||||
*(obj.Spec.PodInfoOnMount) = false
|
*(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) {
|
if len(obj.Spec.VolumeLifecycleModes) == 0 && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
|
||||||
obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1.VolumeLifecyclePersistent)
|
obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1.VolumeLifecyclePersistent)
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,11 @@ import (
|
|||||||
|
|
||||||
storagev1 "k8s.io/api/storage/v1"
|
storagev1 "k8s.io/api/storage/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"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/api/legacyscheme"
|
||||||
_ "k8s.io/kubernetes/pkg/apis/storage/install"
|
_ "k8s.io/kubernetes/pkg/apis/storage/install"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
|
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
|
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) {
|
func TestSetDefaultVolumeBindingMode(t *testing.T) {
|
||||||
class := &storagev1.StorageClass{}
|
class := &storagev1.StorageClass{}
|
||||||
|
|
||||||
|
@ -49,6 +49,10 @@ func SetDefaults_CSIDriver(obj *storagev1beta1.CSIDriver) {
|
|||||||
obj.Spec.PodInfoOnMount = new(bool)
|
obj.Spec.PodInfoOnMount = new(bool)
|
||||||
*(obj.Spec.PodInfoOnMount) = false
|
*(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) {
|
if len(obj.Spec.VolumeLifecycleModes) == 0 && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
|
||||||
obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1beta1.VolumeLifecyclePersistent)
|
obj.Spec.VolumeLifecycleModes = append(obj.Spec.VolumeLifecycleModes, storagev1beta1.VolumeLifecyclePersistent)
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
func TestSetDefaultVolumeLifecycleModesEnabled(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
|
||||||
driver := &storagev1beta1.CSIDriver{}
|
driver := &storagev1beta1.CSIDriver{}
|
||||||
|
@ -419,6 +419,7 @@ func validateCSIDriverSpec(
|
|||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
allErrs = append(allErrs, validateAttachRequired(spec.AttachRequired, fldPath.Child("attachedRequired"))...)
|
allErrs = append(allErrs, validateAttachRequired(spec.AttachRequired, fldPath.Child("attachedRequired"))...)
|
||||||
allErrs = append(allErrs, validatePodInfoOnMount(spec.PodInfoOnMount, fldPath.Child("podInfoOnMount"))...)
|
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"))...)
|
allErrs = append(allErrs, validateVolumeLifecycleModes(spec.VolumeLifecycleModes, fldPath.Child("volumeLifecycleModes"))...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -443,6 +444,16 @@ func validatePodInfoOnMount(podInfoOnMount *bool, fldPath *field.Path) field.Err
|
|||||||
return allErrs
|
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.
|
// validateVolumeLifecycleModes tests if mode has one of the allowed values.
|
||||||
func validateVolumeLifecycleModes(modes []storage.VolumeLifecycleMode, fldPath *field.Path) field.ErrorList {
|
func validateVolumeLifecycleModes(modes []storage.VolumeLifecycleMode, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
@ -46,6 +46,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/duration"
|
"k8s.io/apimachinery/pkg/util/duration"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||||
@ -64,6 +65,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/scheduling"
|
"k8s.io/kubernetes/pkg/apis/scheduling"
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
"k8s.io/kubernetes/pkg/apis/storage"
|
||||||
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
|
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
"k8s.io/kubernetes/pkg/util/node"
|
"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: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||||
{Name: "AttachRequired", Type: "boolean", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["attachRequired"]},
|
{Name: "AttachRequired", Type: "boolean", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["attachRequired"]},
|
||||||
{Name: "PodInfoOnMount", Type: "boolean", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["podInfoOnMount"]},
|
{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: "Modes", Type: "string", Description: storagev1.CSIDriverSpec{}.SwaggerDoc()["volumeLifecycleModes"]},
|
||||||
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
|
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
|
||||||
}
|
}...)
|
||||||
h.TableHandler(csiDriverColumnDefinitions, printCSIDriver)
|
h.TableHandler(csiDriverColumnDefinitions, printCSIDriver)
|
||||||
h.TableHandler(csiDriverColumnDefinitions, printCSIDriverList)
|
h.TableHandler(csiDriverColumnDefinitions, printCSIDriverList)
|
||||||
|
|
||||||
@ -1366,7 +1375,15 @@ func printCSIDriver(obj *storage.CSIDriver, options printers.GenerateOptions) ([
|
|||||||
modes = "<none>"
|
modes = "<none>"
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
return []metav1.TableRow{row}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +43,12 @@ func (csiDriverStrategy) NamespaceScoped() bool {
|
|||||||
return false
|
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) {
|
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) {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
|
||||||
csiDriver := obj.(*storage.CSIDriver)
|
csiDriver := obj.(*storage.CSIDriver)
|
||||||
csiDriver.Spec.VolumeLifecycleModes = nil
|
csiDriver.Spec.VolumeLifecycleModes = nil
|
||||||
@ -68,10 +72,15 @@ func (csiDriverStrategy) AllowCreateOnUpdate() bool {
|
|||||||
return false
|
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
|
// 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.
|
// downgrading to a version that has the feature disabled.
|
||||||
func (csiDriverStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
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 &&
|
if old.(*storage.CSIDriver).Spec.VolumeLifecycleModes == nil &&
|
||||||
!utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
|
!utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
|
||||||
newCSIDriver := obj.(*storage.CSIDriver)
|
newCSIDriver := obj.(*storage.CSIDriver)
|
||||||
|
@ -30,15 +30,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getValidCSIDriver(name string) *storage.CSIDriver {
|
func getValidCSIDriver(name string) *storage.CSIDriver {
|
||||||
attachRequired := true
|
enabled := true
|
||||||
podInfoOnMount := true
|
|
||||||
return &storage.CSIDriver{
|
return &storage.CSIDriver{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: &attachRequired,
|
AttachRequired: &enabled,
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
PodInfoOnMount: &enabled,
|
||||||
|
StorageCapacity: &enabled,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,22 +87,12 @@ func TestCSIDriverPrepareForCreate(t *testing.T) {
|
|||||||
|
|
||||||
attachRequired := true
|
attachRequired := true
|
||||||
podInfoOnMount := true
|
podInfoOnMount := true
|
||||||
csiDriver := &storage.CSIDriver{
|
storageCapacity := true
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
Spec: storage.CSIDriverSpec{
|
|
||||||
AttachRequired: &attachRequired,
|
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
|
||||||
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
|
||||||
storage.VolumeLifecyclePersistent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
withInline bool
|
withCapacity bool
|
||||||
|
withInline bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "inline enabled",
|
name: "inline enabled",
|
||||||
@ -112,17 +102,48 @@ func TestCSIDriverPrepareForCreate(t *testing.T) {
|
|||||||
name: "inline disabled",
|
name: "inline disabled",
|
||||||
withInline: false,
|
withInline: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "capacity enabled",
|
||||||
|
withCapacity: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "capacity disabled",
|
||||||
|
withCapacity: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
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)()
|
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)
|
Strategy.PrepareForCreate(ctx, csiDriver)
|
||||||
errs := Strategy.Validate(ctx, csiDriver)
|
errs := Strategy.Validate(ctx, csiDriver)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("unexpected validating errors: %v", errs)
|
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 test.withInline {
|
||||||
if len(csiDriver.Spec.VolumeLifecycleModes) != 1 {
|
if len(csiDriver.Spec.VolumeLifecycleModes) != 1 {
|
||||||
t.Errorf("VolumeLifecycleModes modified: %v", csiDriver.Spec)
|
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
|
var resultEmpty []storage.VolumeLifecycleMode
|
||||||
resultPersistent := []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent}
|
resultPersistent := []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent}
|
||||||
resultEphemeral := []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral}
|
resultEphemeral := []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
old, update *storage.CSIDriver
|
old, update *storage.CSIDriver
|
||||||
withInline, withoutInline []storage.VolumeLifecycleMode
|
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",
|
name: "before: no mode, update: no mode",
|
||||||
old: driverWithoutModes,
|
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 {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
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)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, withInline)()
|
||||||
|
|
||||||
csiDriver := test.update.DeepCopy()
|
csiDriver := test.update.DeepCopy()
|
||||||
Strategy.PrepareForUpdate(ctx, csiDriver, test.old)
|
Strategy.PrepareForUpdate(ctx, csiDriver, test.old)
|
||||||
if withInline {
|
if withCapacity {
|
||||||
require.Equal(t, csiDriver.Spec.VolumeLifecycleModes, test.withInline)
|
require.Equal(t, test.withCapacity, csiDriver.Spec.StorageCapacity)
|
||||||
} else {
|
} 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) {
|
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) {
|
t.Run("without inline volumes", func(t *testing.T) {
|
||||||
runAll(t, false)
|
runAll(t, false, false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCSIDriverValidation(t *testing.T) {
|
func TestCSIDriverValidation(t *testing.T) {
|
||||||
attachRequired := true
|
enabled := true
|
||||||
notAttachRequired := false
|
disabled := true
|
||||||
podInfoOnMount := true
|
|
||||||
notPodInfoOnMount := false
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -258,27 +344,29 @@ func TestCSIDriverValidation(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"true PodInfoOnMount and AttachRequired",
|
"true for all flags",
|
||||||
&storage.CSIDriver{
|
&storage.CSIDriver{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: &attachRequired,
|
AttachRequired: &enabled,
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
PodInfoOnMount: &enabled,
|
||||||
|
StorageCapacity: &enabled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"false PodInfoOnMount and AttachRequired",
|
"false for all flags",
|
||||||
&storage.CSIDriver{
|
&storage.CSIDriver{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: ¬AttachRequired,
|
AttachRequired: &disabled,
|
||||||
PodInfoOnMount: ¬PodInfoOnMount,
|
PodInfoOnMount: &disabled,
|
||||||
|
StorageCapacity: &disabled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@ -290,8 +378,9 @@ func TestCSIDriverValidation(t *testing.T) {
|
|||||||
Name: "*foo#",
|
Name: "*foo#",
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: &attachRequired,
|
AttachRequired: &enabled,
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
PodInfoOnMount: &enabled,
|
||||||
|
StorageCapacity: &enabled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
@ -303,8 +392,9 @@ func TestCSIDriverValidation(t *testing.T) {
|
|||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: &attachRequired,
|
AttachRequired: &enabled,
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
PodInfoOnMount: &enabled,
|
||||||
|
StorageCapacity: &enabled,
|
||||||
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
||||||
storage.VolumeLifecycleMode("no-such-mode"),
|
storage.VolumeLifecycleMode("no-such-mode"),
|
||||||
},
|
},
|
||||||
@ -319,8 +409,9 @@ func TestCSIDriverValidation(t *testing.T) {
|
|||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: &attachRequired,
|
AttachRequired: &enabled,
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
PodInfoOnMount: &enabled,
|
||||||
|
StorageCapacity: &enabled,
|
||||||
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
||||||
storage.VolumeLifecyclePersistent,
|
storage.VolumeLifecyclePersistent,
|
||||||
},
|
},
|
||||||
@ -335,8 +426,9 @@ func TestCSIDriverValidation(t *testing.T) {
|
|||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: &attachRequired,
|
AttachRequired: &enabled,
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
PodInfoOnMount: &enabled,
|
||||||
|
StorageCapacity: &enabled,
|
||||||
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
||||||
storage.VolumeLifecycleEphemeral,
|
storage.VolumeLifecycleEphemeral,
|
||||||
},
|
},
|
||||||
@ -351,8 +443,9 @@ func TestCSIDriverValidation(t *testing.T) {
|
|||||||
Name: "foo",
|
Name: "foo",
|
||||||
},
|
},
|
||||||
Spec: storage.CSIDriverSpec{
|
Spec: storage.CSIDriverSpec{
|
||||||
AttachRequired: &attachRequired,
|
AttachRequired: &enabled,
|
||||||
PodInfoOnMount: &podInfoOnMount,
|
PodInfoOnMount: &enabled,
|
||||||
|
StorageCapacity: &enabled,
|
||||||
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
|
||||||
storage.VolumeLifecyclePersistent,
|
storage.VolumeLifecyclePersistent,
|
||||||
storage.VolumeLifecycleEphemeral,
|
storage.VolumeLifecycleEphemeral,
|
||||||
|
@ -316,6 +316,26 @@ type CSIDriverSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
// +listType=set
|
// +listType=set
|
||||||
VolumeLifecycleModes []VolumeLifecycleMode `json:"volumeLifecycleModes,omitempty" protobuf:"bytes,3,opt,name=volumeLifecycleModes"`
|
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
|
// VolumeLifecycleMode is an enumeration of possible usage modes for a volume
|
||||||
|
@ -335,6 +335,27 @@ type CSIDriverSpec struct {
|
|||||||
// more modes may be added in the future.
|
// more modes may be added in the future.
|
||||||
// +optional
|
// +optional
|
||||||
VolumeLifecycleModes []VolumeLifecycleMode `json:"volumeLifecycleModes,omitempty" protobuf:"bytes,3,opt,name=volumeLifecycleModes"`
|
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
|
// VolumeLifecycleMode is an enumeration of possible usage modes for a volume
|
||||||
|
Loading…
Reference in New Issue
Block a user