Change:
In 1.30, Priority and Fairness should allow a zero value, either via v1 or v1beta3 (with the roundtrip annotation) in create and update requests See:
/sig api-machinery /cc /assign
The label(s) cannot be applied, because the repository doesn't have them. <detailsIn response to : /triage accpted Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/triage accepted

Discussion:
genericfeatures.WatchList: {Default: false, PreRelease: featuregate.Alpha}, <del> genericfeatures.ZeroLimitedNominalConcurrencyShares: {Default: false, PreRelease: featuregate.Beta}, </del> <ins> genericfeatures.ZeroLimitedNominalConcurrencyShares: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32 </ins> // inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed // unintentionally on either side:

"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" <del> "k8s.io/apiserver/pkg/features" </del> "k8s.io/apiserver/pkg/storage/names" <del> utilfeature "k8s.io/apiserver/pkg/util/feature" </del> "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/flowcontrol" "k8s.io/kubernetes/pkg/apis/flowcontrol/validation"

//  'nominalConcurrencyShares' field of 'limited' for CREATE operation. //  1:30: lift this restriction, allow zero value via v1 or v1beta3 opts := validation.PriorityLevelValidationOptions{ <del> AllowZeroLimitedNominalConcurrencyShares: utilfeature.DefaultFeatureGate.Enabled(features.ZeroLimitedNominalConcurrencyShares), </del> <ins> AllowZeroLimitedNominalConcurrencyShares: true, </ins> } return validation.ValidatePriorityLevelConfiguration(obj.(*flowcontrol.PriorityLevelConfiguration), getRequestGroupVersion(ctx), opts) }

// ValidateUpdate is the default update validation for an end user. func (priorityLevelConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { newPL := obj.(*flowcontrol.PriorityLevelConfiguration) <del> oldPL := old.(*flowcontrol.PriorityLevelConfiguration) </del> // 1.28 server is not aware of the roundtrip annotation, and will // default any 0 value persisted (for the NominalConcurrencyShares

//  only if the existing object already contains a zero value. //  1:30: lift this restriction, allow zero value via v1 or v1beta3 opts := validation.PriorityLevelValidationOptions{ <del> AllowZeroLimitedNominalConcurrencyShares: utilfeature.DefaultFeatureGate.Enabled(features.ZeroLimitedNominalConcurrencyShares) || hasZeroLimitedNominalConcurrencyShares(oldPL), </del> <ins> AllowZeroLimitedNominalConcurrencyShares: true, </ins> } return validation.ValidatePriorityLevelConfiguration(newPL, getRequestGroupVersion(ctx), opts) }

} return schema.GroupVersion{} } <del> func hasZeroLimitedNominalConcurrencyShares(obj *flowcontrol.PriorityLevelConfiguration) bool { return obj != nil && obj.Spec.Limited != nil && obj.Spec.Limited.NominalConcurrencyShares == 0 } </del>

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" <del> "k8s.io/apiserver/pkg/features" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" </del> "k8s.io/kubernetes/pkg/apis/flowcontrol" "k8s.io/utils/ptr"

} return scheme } <del> errExpectedFn := func(v int32, msg string) field.ErrorList { return field.ErrorList{ field.Invalid(field.NewPath("spec").Child("limited").Child("nominalConcurrencyShares"), int32(v), msg), } } </del> tests := []struct { <del> name               string obj                runtime.Object old                *flowcontrol.PriorityLevelConfiguration // for UPDATE only zeroFeatureEnabled bool scheme             *runtime.Scheme errExpected        field.ErrorList </del> <ins> name        string obj         runtime.Object old         *flowcontrol.PriorityLevelConfiguration // for UPDATE only scheme      *runtime.Scheme errExpected field.ErrorList </ins> }{ { <del> name:               "v1, feature disabled, create, zero value, error expected", obj:                v1ObjFn(ptr.To(int32(0))), zeroFeatureEnabled: false, scheme:             v1SchemeFn(t), errExpected:        errExpectedFn(0, "must be positive"), }, { name:               "v1, feature disabled, create, unset, no error expected", obj:                v1ObjFn(nil), zeroFeatureEnabled: false, scheme:             v1SchemeFn(t), errExpected:        nil, }, { name:               "v1, feature disabled, create, non-zero, no error expected", obj:                v1ObjFn(ptr.To(int32(1))), zeroFeatureEnabled: false, scheme:             v1SchemeFn(t), errExpected:        nil, }, { name:               "v1, feature enabled, create, zero value, no error expected", obj:                v1ObjFn(ptr.To(int32(0))), zeroFeatureEnabled: true, scheme:             v1SchemeFn(t), errExpected:        nil, }, { name:               "v1, feature enabled, create, unset, no error expected", obj:                v1ObjFn(nil), zeroFeatureEnabled: true, scheme:             v1SchemeFn(t), errExpected:        nil, }, { name:               "v1, feature enabled, create, non-zero, no error expected", obj:                v1ObjFn(ptr.To(int32(1))), zeroFeatureEnabled: true, scheme:             v1SchemeFn(t), 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"), </del> <ins> name:        "v1, create, zero value, no error expected", obj:         v1ObjFn(ptr.To(int32(0))), scheme:      v1SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1beta3, feature disabled, create, zero value without annotation, no error expected", obj:                v1beta3ObjFn(0, false), zeroFeatureEnabled: false, scheme:             v1beta3SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1, create, unset, no error expected", obj:         v1ObjFn(nil), scheme:      v1SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1beta3, feature disabled, create, non-zero, no error expected", obj:                v1beta3ObjFn(1, false), zeroFeatureEnabled: false, scheme:             v1beta3SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1, create, non-zero, no error expected", obj:         v1ObjFn(ptr.To(int32(1))), scheme:      v1SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1beta3, feature enabled, create, zero value, no error expected", obj:                v1beta3ObjFn(0, true), zeroFeatureEnabled: true, scheme:             v1beta3SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1beta3, create, zero value, no error expected", obj:         v1beta3ObjFn(0, true), scheme:      v1beta3SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1beta3, feature enabled, create, zero value without annotation, no error expected", obj:                v1beta3ObjFn(0, false), zeroFeatureEnabled: true, scheme:             v1beta3SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1beta3, create, zero value without annotation, no error expected", obj:         v1beta3ObjFn(0, false), scheme:      v1beta3SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1beta3, feature enabled, create, non-zero, no error expected", obj:                v1beta3ObjFn(1, false), zeroFeatureEnabled: true, scheme:             v1beta3SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1beta3, create, non-zero, no error expected", obj:         v1beta3ObjFn(1, false), scheme:      v1beta3SchemeFn(t), errExpected: nil, </ins> }, // the following use cases cover UPDATE { <del> name:               "v1, feature disabled, update, zero value, existing has non-zero, error expected", obj:                v1ObjFn(ptr.To(int32(0))), old:                internalObjFn(1), zeroFeatureEnabled: false, scheme:             v1SchemeFn(t), errExpected:        errExpectedFn(0, "must be positive"), </del> <ins> name:        "v1, update, zero value, existing has non-zero, no error expected", obj:         v1ObjFn(ptr.To(int32(0))), old:         internalObjFn(1), scheme:      v1SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1, feature disabled, update, zero value, existing has zero, no error expected", obj:                v1ObjFn(ptr.To(int32(0))), old:                internalObjFn(0), zeroFeatureEnabled: false, scheme:             v1SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1, update, zero value, existing has zero, no error expected", obj:         v1ObjFn(ptr.To(int32(0))), old:         internalObjFn(0), scheme:      v1SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1, feature disabled, update, non-zero value, existing has zero, no error expected", obj:                v1ObjFn(ptr.To(int32(1))), old:                internalObjFn(0), zeroFeatureEnabled: false, scheme:             v1SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1, update, non-zero value, existing has zero, no error expected", obj:         v1ObjFn(ptr.To(int32(1))), old:         internalObjFn(0), scheme:      v1SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1, feature enabled, update, zero value, existing has non-zero, no error expected", obj:                v1ObjFn(ptr.To(int32(0))), old:                internalObjFn(1), zeroFeatureEnabled: true, scheme:             v1SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1beta3, update, zero value, existing has non-zero, no error expected", obj:         v1beta3ObjFn(0, true), old:         internalObjFn(1), scheme:      v1beta3SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1, feature enabled, update, zero value, existing has zero, no error expected", obj:                v1ObjFn(ptr.To(int32(0))), old:                internalObjFn(0), zeroFeatureEnabled: true, scheme:             v1SchemeFn(t), errExpected:        nil, </del> <ins> name:        "v1beta3, update, zero value, existing has zero, no error expected", obj:         v1beta3ObjFn(0, true), old:         internalObjFn(0), scheme:      v1beta3SchemeFn(t), errExpected: nil, </ins> }, { <del> name:               "v1, feature enabled, update, non-zero value, existing has zero, no error expected", obj:                v1ObjFn(ptr.To(int32(1))), old:                internalObjFn(0), zeroFeatureEnabled: true, scheme:             v1SchemeFn(t), 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, </del> <ins> name:        "v1beta3, update, non-zero value, existing has zero, no error expected", obj:         v1beta3ObjFn(1, false), old:         internalObjFn(0), scheme:      v1beta3SchemeFn(t), errExpected: nil, </ins> }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { <del> defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ZeroLimitedNominalConcurrencyShares, test.zeroFeatureEnabled)() </del> scheme := test.scheme scheme.Default(test.obj)

// owner: @tkashem // beta: v1.29 <ins> // GA: v1.30 </ins> // // Allow Priority & Fairness in the API server to use a zero value for // the 'nominalConcurrencyShares' field of the 'limited' section of a

ConsistentListFromCache: {Default: false, PreRelease: featuregate.Alpha}, <del> ZeroLimitedNominalConcurrencyShares: {Default: false, PreRelease: featuregate.Beta}, </del> <ins> ZeroLimitedNominalConcurrencyShares: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32 </ins> }