mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 01:40:07 +00:00
Merge pull request #123001 from tkashem/apf-allow-zero-concurrency
Allow zero value for the 'nominalConcurrencyShares' field
This commit is contained in:
commit
862ff187ba
@ -1211,7 +1211,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
genericfeatures.WatchList: {Default: false, PreRelease: featuregate.Alpha},
|
genericfeatures.WatchList: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
genericfeatures.ZeroLimitedNominalConcurrencyShares: {Default: false, PreRelease: featuregate.Beta},
|
genericfeatures.ZeroLimitedNominalConcurrencyShares: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
|
||||||
|
|
||||||
// inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed
|
// inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed
|
||||||
// unintentionally on either side:
|
// unintentionally on either side:
|
||||||
|
@ -24,9 +24,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
"k8s.io/apiserver/pkg/storage/names"
|
"k8s.io/apiserver/pkg/storage/names"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||||
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
|
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
|
||||||
@ -102,7 +100,7 @@ func (priorityLevelConfigurationStrategy) Validate(ctx context.Context, obj runt
|
|||||||
// 'nominalConcurrencyShares' field of 'limited' for CREATE operation.
|
// 'nominalConcurrencyShares' field of 'limited' for CREATE operation.
|
||||||
// 1:30: lift this restriction, allow zero value via v1 or v1beta3
|
// 1:30: lift this restriction, allow zero value via v1 or v1beta3
|
||||||
opts := validation.PriorityLevelValidationOptions{
|
opts := validation.PriorityLevelValidationOptions{
|
||||||
AllowZeroLimitedNominalConcurrencyShares: utilfeature.DefaultFeatureGate.Enabled(features.ZeroLimitedNominalConcurrencyShares),
|
AllowZeroLimitedNominalConcurrencyShares: true,
|
||||||
}
|
}
|
||||||
return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration), getRequestGroupVersion(ctx), opts)
|
return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration), getRequestGroupVersion(ctx), opts)
|
||||||
}
|
}
|
||||||
@ -128,7 +126,6 @@ func (priorityLevelConfigurationStrategy) AllowCreateOnUpdate() bool {
|
|||||||
// ValidateUpdate is the default update validation for an end user.
|
// ValidateUpdate is the default update validation for an end user.
|
||||||
func (priorityLevelConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
func (priorityLevelConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
|
||||||
newPL := obj.(*flowcontrol.PriorityLevelConfiguration)
|
newPL := obj.(*flowcontrol.PriorityLevelConfiguration)
|
||||||
oldPL := old.(*flowcontrol.PriorityLevelConfiguration)
|
|
||||||
|
|
||||||
// 1.28 server is not aware of the roundtrip annotation, and will
|
// 1.28 server is not aware of the roundtrip annotation, and will
|
||||||
// default any 0 value persisted (for the NominalConcurrencyShares
|
// default any 0 value persisted (for the NominalConcurrencyShares
|
||||||
@ -144,8 +141,7 @@ func (priorityLevelConfigurationStrategy) ValidateUpdate(ctx context.Context, ob
|
|||||||
// only if the existing object already contains a zero value.
|
// only if the existing object already contains a zero value.
|
||||||
// 1:30: lift this restriction, allow zero value via v1 or v1beta3
|
// 1:30: lift this restriction, allow zero value via v1 or v1beta3
|
||||||
opts := validation.PriorityLevelValidationOptions{
|
opts := validation.PriorityLevelValidationOptions{
|
||||||
AllowZeroLimitedNominalConcurrencyShares: utilfeature.DefaultFeatureGate.Enabled(features.ZeroLimitedNominalConcurrencyShares) ||
|
AllowZeroLimitedNominalConcurrencyShares: true,
|
||||||
hasZeroLimitedNominalConcurrencyShares(oldPL),
|
|
||||||
}
|
}
|
||||||
return validation.ValidatePriorityLevelConfiguration(newPL, getRequestGroupVersion(ctx), opts)
|
return validation.ValidatePriorityLevelConfiguration(newPL, getRequestGroupVersion(ctx), opts)
|
||||||
}
|
}
|
||||||
@ -214,7 +210,3 @@ func getRequestGroupVersion(ctx context.Context) schema.GroupVersion {
|
|||||||
}
|
}
|
||||||
return schema.GroupVersion{}
|
return schema.GroupVersion{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasZeroLimitedNominalConcurrencyShares(obj *flowcontrol.PriorityLevelConfiguration) bool {
|
|
||||||
return obj != nil && obj.Spec.Limited != nil && obj.Spec.Limited.NominalConcurrencyShares == 0
|
|
||||||
}
|
|
||||||
|
@ -25,9 +25,6 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apiserver/pkg/features"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||||
"k8s.io/utils/ptr"
|
"k8s.io/utils/ptr"
|
||||||
|
|
||||||
@ -99,208 +96,98 @@ func TestPriorityLevelConfigurationValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return scheme
|
return scheme
|
||||||
}
|
}
|
||||||
errExpectedFn := func(v int32, msg string) field.ErrorList {
|
|
||||||
return field.ErrorList{
|
|
||||||
field.Invalid(field.NewPath("spec").Child("limited").Child("nominalConcurrencyShares"), int32(v), msg),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
obj runtime.Object
|
obj runtime.Object
|
||||||
old *flowcontrol.PriorityLevelConfiguration // for UPDATE only
|
old *flowcontrol.PriorityLevelConfiguration // for UPDATE only
|
||||||
zeroFeatureEnabled bool
|
scheme *runtime.Scheme
|
||||||
scheme *runtime.Scheme
|
errExpected field.ErrorList
|
||||||
errExpected field.ErrorList
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "v1, feature disabled, create, zero value, error expected",
|
name: "v1, create, zero value, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(0))),
|
obj: v1ObjFn(ptr.To(int32(0))),
|
||||||
zeroFeatureEnabled: false,
|
scheme: v1SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: errExpectedFn(0, "must be positive"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature disabled, create, unset, no error expected",
|
name: "v1, create, unset, no error expected",
|
||||||
obj: v1ObjFn(nil),
|
obj: v1ObjFn(nil),
|
||||||
zeroFeatureEnabled: false,
|
scheme: v1SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature disabled, create, non-zero, no error expected",
|
name: "v1, create, non-zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(1))),
|
obj: v1ObjFn(ptr.To(int32(1))),
|
||||||
zeroFeatureEnabled: false,
|
scheme: v1SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature enabled, create, zero value, no error expected",
|
name: "v1beta3, create, zero value, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(0))),
|
obj: v1beta3ObjFn(0, true),
|
||||||
zeroFeatureEnabled: true,
|
scheme: v1beta3SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature enabled, create, unset, no error expected",
|
name: "v1beta3, create, zero value without annotation, no error expected",
|
||||||
obj: v1ObjFn(nil),
|
obj: v1beta3ObjFn(0, false),
|
||||||
zeroFeatureEnabled: true,
|
scheme: v1beta3SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature enabled, create, non-zero, no error expected",
|
name: "v1beta3, create, non-zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(1))),
|
obj: v1beta3ObjFn(1, false),
|
||||||
zeroFeatureEnabled: true,
|
scheme: v1beta3SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature disabled, create, zero value, error expected",
|
|
||||||
obj: v1beta3ObjFn(0, true),
|
|
||||||
zeroFeatureEnabled: false,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: errExpectedFn(0, "must be positive"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature disabled, create, zero value without annotation, no error expected",
|
|
||||||
obj: v1beta3ObjFn(0, false),
|
|
||||||
zeroFeatureEnabled: false,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature disabled, create, non-zero, no error expected",
|
|
||||||
obj: v1beta3ObjFn(1, false),
|
|
||||||
zeroFeatureEnabled: false,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature enabled, create, zero value, no error expected",
|
|
||||||
obj: v1beta3ObjFn(0, true),
|
|
||||||
zeroFeatureEnabled: true,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature enabled, create, zero value without annotation, no error expected",
|
|
||||||
obj: v1beta3ObjFn(0, false),
|
|
||||||
zeroFeatureEnabled: true,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature enabled, create, non-zero, no error expected",
|
|
||||||
obj: v1beta3ObjFn(1, false),
|
|
||||||
zeroFeatureEnabled: true,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// the following use cases cover UPDATE
|
// the following use cases cover UPDATE
|
||||||
{
|
{
|
||||||
name: "v1, feature disabled, update, zero value, existing has non-zero, error expected",
|
name: "v1, update, zero value, existing has non-zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(0))),
|
obj: v1ObjFn(ptr.To(int32(0))),
|
||||||
old: internalObjFn(1),
|
old: internalObjFn(1),
|
||||||
zeroFeatureEnabled: false,
|
scheme: v1SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: errExpectedFn(0, "must be positive"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature disabled, update, zero value, existing has zero, no error expected",
|
name: "v1, update, zero value, existing has zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(0))),
|
obj: v1ObjFn(ptr.To(int32(0))),
|
||||||
old: internalObjFn(0),
|
old: internalObjFn(0),
|
||||||
zeroFeatureEnabled: false,
|
scheme: v1SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature disabled, update, non-zero value, existing has zero, no error expected",
|
name: "v1, update, non-zero value, existing has zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(1))),
|
obj: v1ObjFn(ptr.To(int32(1))),
|
||||||
old: internalObjFn(0),
|
old: internalObjFn(0),
|
||||||
zeroFeatureEnabled: false,
|
scheme: v1SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature enabled, update, zero value, existing has non-zero, no error expected",
|
name: "v1beta3, update, zero value, existing has non-zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(0))),
|
obj: v1beta3ObjFn(0, true),
|
||||||
old: internalObjFn(1),
|
old: internalObjFn(1),
|
||||||
zeroFeatureEnabled: true,
|
scheme: v1beta3SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature enabled, update, zero value, existing has zero, no error expected",
|
name: "v1beta3, update, zero value, existing has zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(0))),
|
obj: v1beta3ObjFn(0, true),
|
||||||
old: internalObjFn(0),
|
old: internalObjFn(0),
|
||||||
zeroFeatureEnabled: true,
|
scheme: v1beta3SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1, feature enabled, update, non-zero value, existing has zero, no error expected",
|
name: "v1beta3, update, non-zero value, existing has zero, no error expected",
|
||||||
obj: v1ObjFn(ptr.To(int32(1))),
|
obj: v1beta3ObjFn(1, false),
|
||||||
old: internalObjFn(0),
|
old: internalObjFn(0),
|
||||||
zeroFeatureEnabled: true,
|
scheme: v1beta3SchemeFn(t),
|
||||||
scheme: v1SchemeFn(t),
|
errExpected: nil,
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature disabled, update, zero value, existing has non-zero, error expected",
|
|
||||||
obj: v1beta3ObjFn(0, true),
|
|
||||||
old: internalObjFn(1),
|
|
||||||
zeroFeatureEnabled: false,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: errExpectedFn(0, "must be positive"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature disabled, update, zero value, existing has zero, no error expected",
|
|
||||||
obj: v1beta3ObjFn(0, true),
|
|
||||||
old: internalObjFn(0),
|
|
||||||
zeroFeatureEnabled: false,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature disabled, update, non-zero value, existing has zero, no error expected",
|
|
||||||
obj: v1beta3ObjFn(1, false),
|
|
||||||
old: internalObjFn(0),
|
|
||||||
zeroFeatureEnabled: false,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature enabled, update, zero value, existing has non-zero, no error expected",
|
|
||||||
obj: v1beta3ObjFn(0, true),
|
|
||||||
old: internalObjFn(1),
|
|
||||||
zeroFeatureEnabled: true,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature enabled, update, zero value, existing has zero, no error expected",
|
|
||||||
obj: v1beta3ObjFn(0, true),
|
|
||||||
old: internalObjFn(0),
|
|
||||||
zeroFeatureEnabled: true,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3, feature enabled, update, non-zero value, existing has zero, no error expected",
|
|
||||||
obj: v1beta3ObjFn(1, false),
|
|
||||||
old: internalObjFn(0),
|
|
||||||
zeroFeatureEnabled: true,
|
|
||||||
scheme: v1beta3SchemeFn(t),
|
|
||||||
errExpected: nil,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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.ZeroLimitedNominalConcurrencyShares, test.zeroFeatureEnabled)()
|
|
||||||
|
|
||||||
scheme := test.scheme
|
scheme := test.scheme
|
||||||
scheme.Default(test.obj)
|
scheme.Default(test.obj)
|
||||||
|
|
||||||
|
@ -247,6 +247,7 @@ const (
|
|||||||
|
|
||||||
// owner: @tkashem
|
// owner: @tkashem
|
||||||
// beta: v1.29
|
// beta: v1.29
|
||||||
|
// GA: v1.30
|
||||||
//
|
//
|
||||||
// Allow Priority & Fairness in the API server to use a zero value for
|
// Allow Priority & Fairness in the API server to use a zero value for
|
||||||
// the 'nominalConcurrencyShares' field of the 'limited' section of a
|
// the 'nominalConcurrencyShares' field of the 'limited' section of a
|
||||||
@ -315,5 +316,5 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
ConsistentListFromCache: {Default: false, PreRelease: featuregate.Alpha},
|
ConsistentListFromCache: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
ZeroLimitedNominalConcurrencyShares: {Default: false, PreRelease: featuregate.Beta},
|
ZeroLimitedNominalConcurrencyShares: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user