This commit is contained in:
Darshan Murthy 2026-05-11 17:45:37 +08:00 committed by GitHub
commit 41dcbf520b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 1515 additions and 4 deletions

View file

@ -64,6 +64,10 @@ func TestVersionedValidationByFuzzing(t *testing.T) {
{Group: "admissionregistration.k8s.io", Version: "v1"},
{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
{Group: "admissionregistration.k8s.io", Version: "v1alpha1"},
{Group: "flowcontrol.apiserver.k8s.io", Version: "v1"},
{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta3"},
{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2"},
{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta1"},
}
// subresourceOnly specifies the subresource path for types that can only be validated

View file

@ -39,6 +39,21 @@ func init() { localSchemeBuilder.Register(RegisterValidations) }
// RegisterValidations adds validation functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterValidations(scheme *runtime.Scheme) error {
// type FlowSchema
scheme.AddValidationFunc(
(*flowcontrolv1.FlowSchema)(nil),
func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/", "/status":
return Validate_FlowSchema(
ctx, op, nil, /* fldPath */
obj.(*flowcontrolv1.FlowSchema),
safe.Cast[*flowcontrolv1.FlowSchema](oldObj))
}
return field.ErrorList{
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath())),
}
})
// type PriorityLevelConfiguration
scheme.AddValidationFunc(
(*flowcontrolv1.PriorityLevelConfiguration)(nil),
@ -57,6 +72,86 @@ func RegisterValidations(scheme *runtime.Scheme) error {
return nil
}
// Validate_FlowSchema validates an instance of FlowSchema according
// to declarative validation rules in the API schema.
func Validate_FlowSchema(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1.FlowSchema) (errs field.ErrorList) {
// field flowcontrolv1.FlowSchema.TypeMeta has no validation
// field flowcontrolv1.FlowSchema.ObjectMeta has no validation
{ // field flowcontrolv1.FlowSchema.Spec
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1.FlowSchemaSpec,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call the type's validation function
errs = append(errs, Validate_FlowSchemaSpec(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1.FlowSchema) *flowcontrolv1.FlowSchemaSpec {
return &oldObj.Spec
})
errs = append(errs, fn(fldPath.Child("spec"), &obj.Spec, oldVal, oldObj != nil)...)
}
// field flowcontrolv1.FlowSchema.Status has no validation
return errs
}
// Validate_FlowSchemaSpec validates an instance of FlowSchemaSpec according
// to declarative validation rules in the API schema.
func Validate_FlowSchemaSpec(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1.FlowSchemaSpec) (errs field.ErrorList) {
// field flowcontrolv1.FlowSchemaSpec.PriorityLevelConfiguration has no validation
// field flowcontrolv1.FlowSchemaSpec.MatchingPrecedence has no validation
// field flowcontrolv1.FlowSchemaSpec.DistinguisherMethod has no validation
{ // field flowcontrolv1.FlowSchemaSpec.Rules
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1.PolicyRulesWithSubjects,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_PolicyRulesWithSubjects); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1.FlowSchemaSpec) []flowcontrolv1.PolicyRulesWithSubjects {
return oldObj.Rules
})
errs = append(errs, fn(fldPath.Child("rules"), obj.Rules, oldVal, oldObj != nil)...)
}
return errs
}
// Validate_LimitResponse validates an instance of LimitResponse according
// to declarative validation rules in the API schema.
func Validate_LimitResponse(
@ -187,6 +282,50 @@ func Validate_LimitedPriorityLevelConfiguration(
return errs
}
// Validate_PolicyRulesWithSubjects validates an instance of PolicyRulesWithSubjects according
// to declarative validation rules in the API schema.
func Validate_PolicyRulesWithSubjects(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1.PolicyRulesWithSubjects) (errs field.ErrorList) {
{ // field flowcontrolv1.PolicyRulesWithSubjects.Subjects
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1.Subject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1.PolicyRulesWithSubjects) []flowcontrolv1.Subject {
return oldObj.Subjects
})
errs = append(errs, fn(fldPath.Child("subjects"), obj.Subjects, oldVal, oldObj != nil)...)
}
// field flowcontrolv1.PolicyRulesWithSubjects.ResourceRules has no validation
// field flowcontrolv1.PolicyRulesWithSubjects.NonResourceRules has no validation
return errs
}
// Validate_PriorityLevelConfiguration validates an instance of PriorityLevelConfiguration according
// to declarative validation rules in the API schema.
func Validate_PriorityLevelConfiguration(
@ -381,3 +520,123 @@ func Validate_PriorityLevelConfigurationSpec(
return errs
}
var unionMembershipFor_k8s_io_api_flowcontrol_v1_Subject_ = validate.NewUnionMembership(validate.NewUnionMember("user"))
// Validate_Subject validates an instance of Subject according
// to declarative validation rules in the API schema.
func Validate_Subject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1.Subject) (errs field.ErrorList) {
if e := validate.Union(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_api_flowcontrol_v1_Subject_,
func(obj *flowcontrolv1.Subject) bool {
if obj == nil {
return false
}
return obj.User != nil
}); len(e) != 0 {
errs = append(errs, e...)
}
{ // field flowcontrolv1.Subject.Kind
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1.SubjectKind,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1.Subject) *flowcontrolv1.SubjectKind {
return &oldObj.Kind
})
errs = append(errs, fn(fldPath.Child("kind"), &obj.Kind, oldVal, oldObj != nil)...)
}
{ // field flowcontrolv1.Subject.User
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1.UserSubject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// call the type's validation function
errs = append(errs, Validate_UserSubject(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1.Subject) *flowcontrolv1.UserSubject {
return oldObj.User
})
errs = append(errs, fn(fldPath.Child("user"), obj.User, oldVal, oldObj != nil)...)
}
// field flowcontrolv1.Subject.Group has no validation
// field flowcontrolv1.Subject.ServiceAccount has no validation
return errs
}
// Validate_UserSubject validates an instance of UserSubject according
// to declarative validation rules in the API schema.
func Validate_UserSubject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1.UserSubject) (errs field.ErrorList) {
{ // field flowcontrolv1.UserSubject.Name
fn := func(
fldPath *field.Path,
obj, oldObj *string,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1.UserSubject) *string {
return &oldObj.Name
})
errs = append(errs, fn(fldPath.Child("name"), &obj.Name, oldVal, oldObj != nil)...)
}
return errs
}

View file

@ -39,6 +39,21 @@ func init() { localSchemeBuilder.Register(RegisterValidations) }
// RegisterValidations adds validation functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterValidations(scheme *runtime.Scheme) error {
// type FlowSchema
scheme.AddValidationFunc(
(*flowcontrolv1beta1.FlowSchema)(nil),
func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/", "/status":
return Validate_FlowSchema(
ctx, op, nil, /* fldPath */
obj.(*flowcontrolv1beta1.FlowSchema),
safe.Cast[*flowcontrolv1beta1.FlowSchema](oldObj))
}
return field.ErrorList{
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath())),
}
})
// type PriorityLevelConfiguration
scheme.AddValidationFunc(
(*flowcontrolv1beta1.PriorityLevelConfiguration)(nil),
@ -57,6 +72,86 @@ func RegisterValidations(scheme *runtime.Scheme) error {
return nil
}
// Validate_FlowSchema validates an instance of FlowSchema according
// to declarative validation rules in the API schema.
func Validate_FlowSchema(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.FlowSchema) (errs field.ErrorList) {
// field flowcontrolv1beta1.FlowSchema.TypeMeta has no validation
// field flowcontrolv1beta1.FlowSchema.ObjectMeta has no validation
{ // field flowcontrolv1beta1.FlowSchema.Spec
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.FlowSchemaSpec,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call the type's validation function
errs = append(errs, Validate_FlowSchemaSpec(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta1.FlowSchema) *flowcontrolv1beta1.FlowSchemaSpec {
return &oldObj.Spec
})
errs = append(errs, fn(fldPath.Child("spec"), &obj.Spec, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta1.FlowSchema.Status has no validation
return errs
}
// Validate_FlowSchemaSpec validates an instance of FlowSchemaSpec according
// to declarative validation rules in the API schema.
func Validate_FlowSchemaSpec(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.FlowSchemaSpec) (errs field.ErrorList) {
// field flowcontrolv1beta1.FlowSchemaSpec.PriorityLevelConfiguration has no validation
// field flowcontrolv1beta1.FlowSchemaSpec.MatchingPrecedence has no validation
// field flowcontrolv1beta1.FlowSchemaSpec.DistinguisherMethod has no validation
{ // field flowcontrolv1beta1.FlowSchemaSpec.Rules
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1beta1.PolicyRulesWithSubjects,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_PolicyRulesWithSubjects); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta1.FlowSchemaSpec) []flowcontrolv1beta1.PolicyRulesWithSubjects {
return oldObj.Rules
})
errs = append(errs, fn(fldPath.Child("rules"), obj.Rules, oldVal, oldObj != nil)...)
}
return errs
}
// Validate_LimitResponse validates an instance of LimitResponse according
// to declarative validation rules in the API schema.
func Validate_LimitResponse(
@ -189,6 +284,50 @@ func Validate_LimitedPriorityLevelConfiguration(
return errs
}
// Validate_PolicyRulesWithSubjects validates an instance of PolicyRulesWithSubjects according
// to declarative validation rules in the API schema.
func Validate_PolicyRulesWithSubjects(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.PolicyRulesWithSubjects) (errs field.ErrorList) {
{ // field flowcontrolv1beta1.PolicyRulesWithSubjects.Subjects
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1beta1.Subject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta1.PolicyRulesWithSubjects) []flowcontrolv1beta1.Subject {
return oldObj.Subjects
})
errs = append(errs, fn(fldPath.Child("subjects"), obj.Subjects, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta1.PolicyRulesWithSubjects.ResourceRules has no validation
// field flowcontrolv1beta1.PolicyRulesWithSubjects.NonResourceRules has no validation
return errs
}
// Validate_PriorityLevelConfiguration validates an instance of PriorityLevelConfiguration according
// to declarative validation rules in the API schema.
func Validate_PriorityLevelConfiguration(
@ -383,3 +522,129 @@ func Validate_PriorityLevelConfigurationSpec(
return errs
}
var unionMembershipFor_k8s_io_api_flowcontrol_v1beta1_Subject_ = validate.NewDiscriminatedUnionMembership("kind", validate.NewDiscriminatedUnionMember("user", "User"))
// Validate_Subject validates an instance of Subject according
// to declarative validation rules in the API schema.
func Validate_Subject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.Subject) (errs field.ErrorList) {
if e := validate.DiscriminatedUnion(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_api_flowcontrol_v1beta1_Subject_,
func(obj *flowcontrolv1beta1.Subject) string {
if obj == nil {
return ""
}
return string(obj.Kind)
},
func(obj *flowcontrolv1beta1.Subject) bool {
if obj == nil {
return false
}
return obj.User != nil
}); len(e) != 0 {
errs = append(errs, e...)
}
{ // field flowcontrolv1beta1.Subject.Kind
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.SubjectKind,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta1.Subject) *flowcontrolv1beta1.SubjectKind {
return &oldObj.Kind
})
errs = append(errs, fn(fldPath.Child("kind"), &obj.Kind, oldVal, oldObj != nil)...)
}
{ // field flowcontrolv1beta1.Subject.User
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.UserSubject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// call the type's validation function
errs = append(errs, Validate_UserSubject(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta1.Subject) *flowcontrolv1beta1.UserSubject {
return oldObj.User
})
errs = append(errs, fn(fldPath.Child("user"), obj.User, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta1.Subject.Group has no validation
// field flowcontrolv1beta1.Subject.ServiceAccount has no validation
return errs
}
// Validate_UserSubject validates an instance of UserSubject according
// to declarative validation rules in the API schema.
func Validate_UserSubject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta1.UserSubject) (errs field.ErrorList) {
{ // field flowcontrolv1beta1.UserSubject.Name
fn := func(
fldPath *field.Path,
obj, oldObj *string,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta1.UserSubject) *string {
return &oldObj.Name
})
errs = append(errs, fn(fldPath.Child("name"), &obj.Name, oldVal, oldObj != nil)...)
}
return errs
}

View file

@ -39,6 +39,21 @@ func init() { localSchemeBuilder.Register(RegisterValidations) }
// RegisterValidations adds validation functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterValidations(scheme *runtime.Scheme) error {
// type FlowSchema
scheme.AddValidationFunc(
(*flowcontrolv1beta2.FlowSchema)(nil),
func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/", "/status":
return Validate_FlowSchema(
ctx, op, nil, /* fldPath */
obj.(*flowcontrolv1beta2.FlowSchema),
safe.Cast[*flowcontrolv1beta2.FlowSchema](oldObj))
}
return field.ErrorList{
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath())),
}
})
// type PriorityLevelConfiguration
scheme.AddValidationFunc(
(*flowcontrolv1beta2.PriorityLevelConfiguration)(nil),
@ -57,6 +72,86 @@ func RegisterValidations(scheme *runtime.Scheme) error {
return nil
}
// Validate_FlowSchema validates an instance of FlowSchema according
// to declarative validation rules in the API schema.
func Validate_FlowSchema(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.FlowSchema) (errs field.ErrorList) {
// field flowcontrolv1beta2.FlowSchema.TypeMeta has no validation
// field flowcontrolv1beta2.FlowSchema.ObjectMeta has no validation
{ // field flowcontrolv1beta2.FlowSchema.Spec
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.FlowSchemaSpec,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call the type's validation function
errs = append(errs, Validate_FlowSchemaSpec(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta2.FlowSchema) *flowcontrolv1beta2.FlowSchemaSpec {
return &oldObj.Spec
})
errs = append(errs, fn(fldPath.Child("spec"), &obj.Spec, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta2.FlowSchema.Status has no validation
return errs
}
// Validate_FlowSchemaSpec validates an instance of FlowSchemaSpec according
// to declarative validation rules in the API schema.
func Validate_FlowSchemaSpec(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.FlowSchemaSpec) (errs field.ErrorList) {
// field flowcontrolv1beta2.FlowSchemaSpec.PriorityLevelConfiguration has no validation
// field flowcontrolv1beta2.FlowSchemaSpec.MatchingPrecedence has no validation
// field flowcontrolv1beta2.FlowSchemaSpec.DistinguisherMethod has no validation
{ // field flowcontrolv1beta2.FlowSchemaSpec.Rules
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1beta2.PolicyRulesWithSubjects,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_PolicyRulesWithSubjects); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta2.FlowSchemaSpec) []flowcontrolv1beta2.PolicyRulesWithSubjects {
return oldObj.Rules
})
errs = append(errs, fn(fldPath.Child("rules"), obj.Rules, oldVal, oldObj != nil)...)
}
return errs
}
// Validate_LimitResponse validates an instance of LimitResponse according
// to declarative validation rules in the API schema.
func Validate_LimitResponse(
@ -189,6 +284,50 @@ func Validate_LimitedPriorityLevelConfiguration(
return errs
}
// Validate_PolicyRulesWithSubjects validates an instance of PolicyRulesWithSubjects according
// to declarative validation rules in the API schema.
func Validate_PolicyRulesWithSubjects(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.PolicyRulesWithSubjects) (errs field.ErrorList) {
{ // field flowcontrolv1beta2.PolicyRulesWithSubjects.Subjects
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1beta2.Subject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta2.PolicyRulesWithSubjects) []flowcontrolv1beta2.Subject {
return oldObj.Subjects
})
errs = append(errs, fn(fldPath.Child("subjects"), obj.Subjects, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta2.PolicyRulesWithSubjects.ResourceRules has no validation
// field flowcontrolv1beta2.PolicyRulesWithSubjects.NonResourceRules has no validation
return errs
}
// Validate_PriorityLevelConfiguration validates an instance of PriorityLevelConfiguration according
// to declarative validation rules in the API schema.
func Validate_PriorityLevelConfiguration(
@ -383,3 +522,129 @@ func Validate_PriorityLevelConfigurationSpec(
return errs
}
var unionMembershipFor_k8s_io_api_flowcontrol_v1beta2_Subject_ = validate.NewDiscriminatedUnionMembership("kind", validate.NewDiscriminatedUnionMember("user", "User"))
// Validate_Subject validates an instance of Subject according
// to declarative validation rules in the API schema.
func Validate_Subject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.Subject) (errs field.ErrorList) {
if e := validate.DiscriminatedUnion(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_api_flowcontrol_v1beta2_Subject_,
func(obj *flowcontrolv1beta2.Subject) string {
if obj == nil {
return ""
}
return string(obj.Kind)
},
func(obj *flowcontrolv1beta2.Subject) bool {
if obj == nil {
return false
}
return obj.User != nil
}); len(e) != 0 {
errs = append(errs, e...)
}
{ // field flowcontrolv1beta2.Subject.Kind
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.SubjectKind,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta2.Subject) *flowcontrolv1beta2.SubjectKind {
return &oldObj.Kind
})
errs = append(errs, fn(fldPath.Child("kind"), &obj.Kind, oldVal, oldObj != nil)...)
}
{ // field flowcontrolv1beta2.Subject.User
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.UserSubject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// call the type's validation function
errs = append(errs, Validate_UserSubject(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta2.Subject) *flowcontrolv1beta2.UserSubject {
return oldObj.User
})
errs = append(errs, fn(fldPath.Child("user"), obj.User, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta2.Subject.Group has no validation
// field flowcontrolv1beta2.Subject.ServiceAccount has no validation
return errs
}
// Validate_UserSubject validates an instance of UserSubject according
// to declarative validation rules in the API schema.
func Validate_UserSubject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta2.UserSubject) (errs field.ErrorList) {
{ // field flowcontrolv1beta2.UserSubject.Name
fn := func(
fldPath *field.Path,
obj, oldObj *string,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta2.UserSubject) *string {
return &oldObj.Name
})
errs = append(errs, fn(fldPath.Child("name"), &obj.Name, oldVal, oldObj != nil)...)
}
return errs
}

View file

@ -39,6 +39,21 @@ func init() { localSchemeBuilder.Register(RegisterValidations) }
// RegisterValidations adds validation functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterValidations(scheme *runtime.Scheme) error {
// type FlowSchema
scheme.AddValidationFunc(
(*flowcontrolv1beta3.FlowSchema)(nil),
func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/", "/status":
return Validate_FlowSchema(
ctx, op, nil, /* fldPath */
obj.(*flowcontrolv1beta3.FlowSchema),
safe.Cast[*flowcontrolv1beta3.FlowSchema](oldObj))
}
return field.ErrorList{
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath())),
}
})
// type PriorityLevelConfiguration
scheme.AddValidationFunc(
(*flowcontrolv1beta3.PriorityLevelConfiguration)(nil),
@ -57,6 +72,86 @@ func RegisterValidations(scheme *runtime.Scheme) error {
return nil
}
// Validate_FlowSchema validates an instance of FlowSchema according
// to declarative validation rules in the API schema.
func Validate_FlowSchema(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.FlowSchema) (errs field.ErrorList) {
// field flowcontrolv1beta3.FlowSchema.TypeMeta has no validation
// field flowcontrolv1beta3.FlowSchema.ObjectMeta has no validation
{ // field flowcontrolv1beta3.FlowSchema.Spec
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.FlowSchemaSpec,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call the type's validation function
errs = append(errs, Validate_FlowSchemaSpec(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta3.FlowSchema) *flowcontrolv1beta3.FlowSchemaSpec {
return &oldObj.Spec
})
errs = append(errs, fn(fldPath.Child("spec"), &obj.Spec, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta3.FlowSchema.Status has no validation
return errs
}
// Validate_FlowSchemaSpec validates an instance of FlowSchemaSpec according
// to declarative validation rules in the API schema.
func Validate_FlowSchemaSpec(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.FlowSchemaSpec) (errs field.ErrorList) {
// field flowcontrolv1beta3.FlowSchemaSpec.PriorityLevelConfiguration has no validation
// field flowcontrolv1beta3.FlowSchemaSpec.MatchingPrecedence has no validation
// field flowcontrolv1beta3.FlowSchemaSpec.DistinguisherMethod has no validation
{ // field flowcontrolv1beta3.FlowSchemaSpec.Rules
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1beta3.PolicyRulesWithSubjects,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_PolicyRulesWithSubjects); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta3.FlowSchemaSpec) []flowcontrolv1beta3.PolicyRulesWithSubjects {
return oldObj.Rules
})
errs = append(errs, fn(fldPath.Child("rules"), obj.Rules, oldVal, oldObj != nil)...)
}
return errs
}
// Validate_LimitResponse validates an instance of LimitResponse according
// to declarative validation rules in the API schema.
func Validate_LimitResponse(
@ -189,6 +284,50 @@ func Validate_LimitedPriorityLevelConfiguration(
return errs
}
// Validate_PolicyRulesWithSubjects validates an instance of PolicyRulesWithSubjects according
// to declarative validation rules in the API schema.
func Validate_PolicyRulesWithSubjects(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.PolicyRulesWithSubjects) (errs field.ErrorList) {
{ // field flowcontrolv1beta3.PolicyRulesWithSubjects.Subjects
fn := func(
fldPath *field.Path,
obj, oldObj []flowcontrolv1beta3.Subject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// iterate the list and call the type's validation function
if e := validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject); len(e) != 0 {
errs = append(errs, e...)
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta3.PolicyRulesWithSubjects) []flowcontrolv1beta3.Subject {
return oldObj.Subjects
})
errs = append(errs, fn(fldPath.Child("subjects"), obj.Subjects, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta3.PolicyRulesWithSubjects.ResourceRules has no validation
// field flowcontrolv1beta3.PolicyRulesWithSubjects.NonResourceRules has no validation
return errs
}
// Validate_PriorityLevelConfiguration validates an instance of PriorityLevelConfiguration according
// to declarative validation rules in the API schema.
func Validate_PriorityLevelConfiguration(
@ -383,3 +522,129 @@ func Validate_PriorityLevelConfigurationSpec(
return errs
}
var unionMembershipFor_k8s_io_api_flowcontrol_v1beta3_Subject_ = validate.NewDiscriminatedUnionMembership("kind", validate.NewDiscriminatedUnionMember("user", "User"))
// Validate_Subject validates an instance of Subject according
// to declarative validation rules in the API schema.
func Validate_Subject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.Subject) (errs field.ErrorList) {
if e := validate.DiscriminatedUnion(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_api_flowcontrol_v1beta3_Subject_,
func(obj *flowcontrolv1beta3.Subject) string {
if obj == nil {
return ""
}
return string(obj.Kind)
},
func(obj *flowcontrolv1beta3.Subject) bool {
if obj == nil {
return false
}
return obj.User != nil
}); len(e) != 0 {
errs = append(errs, e...)
}
{ // field flowcontrolv1beta3.Subject.Kind
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.SubjectKind,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta3.Subject) *flowcontrolv1beta3.SubjectKind {
return &oldObj.Kind
})
errs = append(errs, fn(fldPath.Child("kind"), &obj.Kind, oldVal, oldObj != nil)...)
}
{ // field flowcontrolv1beta3.Subject.User
fn := func(
fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.UserSubject,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
// call the type's validation function
errs = append(errs, Validate_UserSubject(ctx, op, fldPath, obj, oldObj)...)
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta3.Subject) *flowcontrolv1beta3.UserSubject {
return oldObj.User
})
errs = append(errs, fn(fldPath.Child("user"), obj.User, oldVal, oldObj != nil)...)
}
// field flowcontrolv1beta3.Subject.Group has no validation
// field flowcontrolv1beta3.Subject.ServiceAccount has no validation
return errs
}
// Validate_UserSubject validates an instance of UserSubject according
// to declarative validation rules in the API schema.
func Validate_UserSubject(
ctx context.Context, op operation.Operation, fldPath *field.Path,
obj, oldObj *flowcontrolv1beta3.UserSubject) (errs field.ErrorList) {
{ // field flowcontrolv1beta3.UserSubject.Name
fn := func(
fldPath *field.Path,
obj, oldObj *string,
oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update {
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
return nil
}
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}
oldVal := safe.Field(oldObj,
func(oldObj *flowcontrolv1beta3.UserSubject) *string {
return &oldObj.Name
})
errs = append(errs, fn(fldPath.Child("name"), &obj.Name, oldVal, oldObj != nil)...)
}
return errs
}

View file

@ -217,10 +217,10 @@ func ValidateServiceAccountSubject(subject *flowcontrol.ServiceAccountSubject, f
func ValidateUserSubject(subject *flowcontrol.UserSubject, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if subject == nil {
return append(allErrs, field.Required(fldPath, "user is required when subject kind is 'User'"))
return append(allErrs, field.Required(fldPath, "user is required when subject kind is 'User'").MarkCoveredByDeclarative())
}
if len(subject.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "").MarkCoveredByDeclarative())
}
return allErrs
}

View file

@ -192,7 +192,7 @@ func TestFlowSchemaValidation(t *testing.T) {
},
expectedErrors: field.ErrorList{
field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"),
field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is required when subject kind is 'User'"),
field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is required when subject kind is 'User'").MarkCoveredByDeclarative(),
field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("group"), "group is forbidden when subject kind is not 'Group'"),
},
}, {

View file

@ -18,11 +18,11 @@ package flowschema
import (
"context"
"k8s.io/apiserver/pkg/registry/rest"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/flowcontrol"

View file

@ -146,6 +146,7 @@ message FlowSchemaSpec {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
repeated PolicyRulesWithSubjects rules = 4;
}
@ -286,6 +287,7 @@ message PolicyRulesWithSubjects {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
repeated Subject subjects = 1;
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
@ -508,10 +510,14 @@ message Subject {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:required
optional string kind = 1;
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
optional UserSubject user = 2;
// `group` matches based on user group name.
@ -527,6 +533,8 @@ message Subject {
message UserSubject {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
optional string name = 1;
}

View file

@ -163,6 +163,7 @@ type FlowSchemaSpec struct {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
Rules []PolicyRulesWithSubjects `json:"rules,omitempty" protobuf:"bytes,4,rep,name=rules"`
}
@ -208,6 +209,7 @@ type PolicyRulesWithSubjects struct {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
Subjects []Subject `json:"subjects" protobuf:"bytes,1,rep,name=subjects"`
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
// target resource.
@ -229,9 +231,13 @@ type Subject struct {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:required
Kind SubjectKind `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
User *UserSubject `json:"user,omitempty" protobuf:"bytes,2,opt,name=user"`
// `group` matches based on user group name.
// +optional
@ -255,6 +261,8 @@ const (
type UserSubject struct {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
}

View file

@ -146,6 +146,7 @@ message FlowSchemaSpec {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
repeated PolicyRulesWithSubjects rules = 4;
}
@ -279,6 +280,7 @@ message PolicyRulesWithSubjects {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
repeated Subject subjects = 1;
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
@ -499,10 +501,15 @@ message Subject {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:unionDiscriminator
// +k8s:required
optional string kind = 1;
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
optional UserSubject user = 2;
// `group` matches based on user group name.
@ -518,6 +525,8 @@ message Subject {
message UserSubject {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
optional string name = 1;
}

View file

@ -165,6 +165,7 @@ type FlowSchemaSpec struct {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
Rules []PolicyRulesWithSubjects `json:"rules,omitempty" protobuf:"bytes,4,rep,name=rules"`
}
@ -210,6 +211,7 @@ type PolicyRulesWithSubjects struct {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
Subjects []Subject `json:"subjects" protobuf:"bytes,1,rep,name=subjects"`
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
// target resource.
@ -231,9 +233,14 @@ type Subject struct {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:unionDiscriminator
// +k8s:required
Kind SubjectKind `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
User *UserSubject `json:"user,omitempty" protobuf:"bytes,2,opt,name=user"`
// `group` matches based on user group name.
// +optional
@ -257,6 +264,8 @@ const (
type UserSubject struct {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
}

View file

@ -146,6 +146,7 @@ message FlowSchemaSpec {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
repeated PolicyRulesWithSubjects rules = 4;
}
@ -279,6 +280,7 @@ message PolicyRulesWithSubjects {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
repeated Subject subjects = 1;
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
@ -499,10 +501,15 @@ message Subject {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:unionDiscriminator
// +k8s:required
optional string kind = 1;
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
optional UserSubject user = 2;
// `group` matches based on user group name.
@ -518,6 +525,8 @@ message Subject {
message UserSubject {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
optional string name = 1;
}

View file

@ -165,6 +165,7 @@ type FlowSchemaSpec struct {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
Rules []PolicyRulesWithSubjects `json:"rules,omitempty" protobuf:"bytes,4,rep,name=rules"`
}
@ -210,6 +211,7 @@ type PolicyRulesWithSubjects struct {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
Subjects []Subject `json:"subjects" protobuf:"bytes,1,rep,name=subjects"`
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
// target resource.
@ -231,9 +233,14 @@ type Subject struct {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:unionDiscriminator
// +k8s:required
Kind SubjectKind `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
User *UserSubject `json:"user,omitempty" protobuf:"bytes,2,opt,name=user"`
// `group` matches based on user group name.
// +optional
@ -257,6 +264,8 @@ const (
type UserSubject struct {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
}

View file

@ -146,6 +146,7 @@ message FlowSchemaSpec {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
repeated PolicyRulesWithSubjects rules = 4;
}
@ -281,6 +282,7 @@ message PolicyRulesWithSubjects {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
repeated Subject subjects = 1;
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
@ -503,10 +505,15 @@ message Subject {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:unionDiscriminator
// +k8s:required
optional string kind = 1;
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
optional UserSubject user = 2;
// `group` matches based on user group name.
@ -522,6 +529,8 @@ message Subject {
message UserSubject {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
optional string name = 1;
}

View file

@ -179,6 +179,7 @@ type FlowSchemaSpec struct {
// if it is an empty slice, there will be no requests matching the FlowSchema.
// +listType=atomic
// +optional
// +k8s:optional
Rules []PolicyRulesWithSubjects `json:"rules,omitempty" protobuf:"bytes,4,rep,name=rules"`
}
@ -224,6 +225,7 @@ type PolicyRulesWithSubjects struct {
// A slice that includes both the system:authenticated and system:unauthenticated user groups matches every request.
// +listType=atomic
// Required.
// +k8s:required
Subjects []Subject `json:"subjects" protobuf:"bytes,1,rep,name=subjects"`
// `resourceRules` is a slice of ResourcePolicyRules that identify matching requests according to their verb and the
// target resource.
@ -245,9 +247,14 @@ type Subject struct {
// `kind` indicates which one of the other fields is non-empty.
// Required
// +unionDiscriminator
// +k8s:unionDiscriminator
// +k8s:required
Kind SubjectKind `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// `user` matches based on username.
// +unionMember=User
// +k8s:unionMember(memberName: "User")
// +optional
// +k8s:optional
User *UserSubject `json:"user,omitempty" protobuf:"bytes,2,opt,name=user"`
// `group` matches based on user group name.
// +optional
@ -271,6 +278,8 @@ const (
type UserSubject struct {
// `name` is the username that matches, or "*" to match all usernames.
// Required.
// +required
// +k8s:required
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
}

View file

@ -0,0 +1,152 @@
/*
Copyright 2025 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flowschema
import (
flowschema2 "k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/flowcontrol"
)
func TestDeclarativeValidate(t *testing.T) {
for _, apiVersion := range apiVersions {
t.Run(apiVersion, func(t *testing.T) {
testDeclarativeValidate(t, apiVersion)
})
}
}
func testDeclarativeValidate(t *testing.T, apiVersion string) {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
APIGroup: "flowcontrol.apiserver.k8s.io",
APIVersion: apiVersion,
Resource: "flowschemas",
IsResourceRequest: true,
Verb: "create",
})
testCases := map[string]struct {
input flowcontrol.FlowSchema
expectedErrs field.ErrorList
}{
"valid": {
input: mkValidFlowSchema(),
},
"user subject name is required": {
input: mkValidFlowSchema(tweakUserSubjectName("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "rules").Index(0).Child("subjects").Index(0).Child("user.name"), "").MarkCoveredByDeclarative(),
},
},
}
for k, tc := range testCases {
t.Run(k, func(t *testing.T) {
apitesting.VerifyValidationEquivalence(t, ctx, &tc.input, flowschema2.Strategy, tc.expectedErrs)
})
}
}
func TestDeclarativeValidateUpdate(t *testing.T) {
for _, apiVersion := range apiVersions {
t.Run(apiVersion, func(t *testing.T) {
testDeclarativeValidateUpdate(t, apiVersion)
})
}
}
func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) {
testCases := map[string]struct {
oldObj flowcontrol.FlowSchema
updateObj flowcontrol.FlowSchema
expectedErrs field.ErrorList
}{
"valid update": {
oldObj: mkValidFlowSchema(),
updateObj: mkValidFlowSchema(),
},
"user subject name is required": {
updateObj: mkValidFlowSchema(tweakUserSubjectName("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "rules").Index(0).Child("subjects").Index(0).Child("user.name"), "").MarkCoveredByDeclarative(),
},
},
}
for k, tc := range testCases {
t.Run(k, func(t *testing.T) {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
APIPrefix: "apis",
APIGroup: "flowcontrol.apiserver.k8s.io",
APIVersion: apiVersion,
Resource: "flowschemas",
Name: "valid-flow-schema",
IsResourceRequest: true,
Verb: "update",
})
apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.updateObj, &tc.oldObj, flowschema2.Strategy, tc.expectedErrs)
})
}
}
func mkValidFlowSchema(tweaks ...func(obj *flowcontrol.FlowSchema)) flowcontrol.FlowSchema {
obj := flowcontrol.FlowSchema{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-flow-schema",
},
Spec: flowcontrol.FlowSchemaSpec{
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
Name: "system-leader-election",
},
MatchingPrecedence: 1000,
Rules: []flowcontrol.PolicyRulesWithSubjects{
{
Subjects: []flowcontrol.Subject{
{
Kind: flowcontrol.SubjectKindUser,
User: &flowcontrol.UserSubject{
Name: "system:kube-controller-manager",
},
},
},
ResourceRules: []flowcontrol.ResourcePolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{""},
Resources: []string{"pods"},
Namespaces: []string{"production", "staging"},
},
},
},
},
},
}
obj.ResourceVersion = "1"
for _, tweak := range tweaks {
tweak(&obj)
}
return obj
}
func tweakUserSubjectName(name string) func(*flowcontrol.FlowSchema) {
return func(fs *flowcontrol.FlowSchema) {
fs.Spec.Rules[0].Subjects[0].User.Name = name
}
}

View file

@ -0,0 +1,43 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by validation-gen. DO NOT EDIT.
package flowschema
import (
fmt "fmt"
os "os"
testing "testing"
coverage "k8s.io/apimachinery/pkg/test/coverage"
)
var apiVersions = []string{"v1", "v1beta1", "v1beta2", "v1beta3"}
func TestMain(m *testing.M) {
code := m.Run()
if err := coverage.AssertDeclarativeCoverage(); err != nil {
fmt.Fprintln(os.Stderr, err)
if code == 0 {
code = 1
}
}
os.Exit(code)
}

View file

@ -0,0 +1,47 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by validation-gen. DO NOT EDIT.
package flowschema
import (
schema "k8s.io/apimachinery/pkg/runtime/schema"
coverage "k8s.io/apimachinery/pkg/test/coverage"
)
func init() {
coverage.RegisterDeclaredRules(
schema.GroupVersionKind{Group: "flowcontrol.apiserver.k8s.io", Version: "v1", Kind: "FlowSchema"},
coverage.FieldRules{
"spec.rules[*].subjects": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*]": {
{ErrorType: "FieldValueInvalid", Origin: "union"},
},
"spec.rules[*].subjects[*].kind": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*].user.name": {
{ErrorType: "FieldValueRequired"},
},
},
)
}

View file

@ -0,0 +1,47 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by validation-gen. DO NOT EDIT.
package flowschema
import (
schema "k8s.io/apimachinery/pkg/runtime/schema"
coverage "k8s.io/apimachinery/pkg/test/coverage"
)
func init() {
coverage.RegisterDeclaredRules(
schema.GroupVersionKind{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta1", Kind: "FlowSchema"},
coverage.FieldRules{
"spec.rules[*].subjects": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*]": {
{ErrorType: "FieldValueInvalid", Origin: "union"},
},
"spec.rules[*].subjects[*].kind": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*].user.name": {
{ErrorType: "FieldValueRequired"},
},
},
)
}

View file

@ -0,0 +1,47 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by validation-gen. DO NOT EDIT.
package flowschema
import (
schema "k8s.io/apimachinery/pkg/runtime/schema"
coverage "k8s.io/apimachinery/pkg/test/coverage"
)
func init() {
coverage.RegisterDeclaredRules(
schema.GroupVersionKind{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2", Kind: "FlowSchema"},
coverage.FieldRules{
"spec.rules[*].subjects": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*]": {
{ErrorType: "FieldValueInvalid", Origin: "union"},
},
"spec.rules[*].subjects[*].kind": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*].user.name": {
{ErrorType: "FieldValueRequired"},
},
},
)
}

View file

@ -0,0 +1,47 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by validation-gen. DO NOT EDIT.
package flowschema
import (
schema "k8s.io/apimachinery/pkg/runtime/schema"
coverage "k8s.io/apimachinery/pkg/test/coverage"
)
func init() {
coverage.RegisterDeclaredRules(
schema.GroupVersionKind{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta3", Kind: "FlowSchema"},
coverage.FieldRules{
"spec.rules[*].subjects": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*]": {
{ErrorType: "FieldValueInvalid", Origin: "union"},
},
"spec.rules[*].subjects[*].kind": {
{ErrorType: "FieldValueRequired"},
},
"spec.rules[*].subjects[*].user.name": {
{ErrorType: "FieldValueRequired"},
},
},
)
}