feat(validation-gen): Add declarative validation support for rolebinding(v1,v1alpha1,v1beta1)

This commit is contained in:
pranshul gupta 2025-10-17 20:45:36 +05:30
parent 4e660cc05b
commit 2ea3d3815c
8 changed files with 746 additions and 2 deletions

View file

@ -47,6 +47,22 @@ func RegisterValidations(scheme *runtime.Scheme) error {
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleBinding
scheme.AddValidationFunc((*rbacv1.ClusterRoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_ClusterRoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1.ClusterRoleBinding), safe.Cast[*rbacv1.ClusterRoleBinding](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleBindingList
scheme.AddValidationFunc((*rbacv1.ClusterRoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_ClusterRoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1.ClusterRoleBindingList), safe.Cast[*rbacv1.ClusterRoleBindingList](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleList
scheme.AddValidationFunc((*rbacv1.ClusterRoleList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
@ -63,6 +79,22 @@ func RegisterValidations(scheme *runtime.Scheme) error {
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleBinding
scheme.AddValidationFunc((*rbacv1.RoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_RoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1.RoleBinding), safe.Cast[*rbacv1.RoleBinding](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleBindingList
scheme.AddValidationFunc((*rbacv1.RoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_RoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1.RoleBindingList), safe.Cast[*rbacv1.RoleBindingList](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleList
scheme.AddValidationFunc((*rbacv1.RoleList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
@ -96,6 +128,60 @@ func Validate_ClusterRole(ctx context.Context, op operation.Operation, fldPath *
return errs
}
// Validate_ClusterRoleBinding validates an instance of ClusterRoleBinding according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.ClusterRoleBinding) (errs field.ErrorList) {
// field rbacv1.ClusterRoleBinding.TypeMeta has no validation
// field rbacv1.ClusterRoleBinding.ObjectMeta has no validation
// field rbacv1.ClusterRoleBinding.Subjects
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1.Subject) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...)
return
}(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRoleBinding) []rbacv1.Subject { return oldObj.Subjects }))...)
// field rbacv1.ClusterRoleBinding.RoleRef
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *rbacv1.RoleRef) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call the type's validation function
errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...)
return
}(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRoleBinding) *rbacv1.RoleRef { return &oldObj.RoleRef }))...)
return errs
}
// Validate_ClusterRoleBindingList validates an instance of ClusterRoleBindingList according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.ClusterRoleBindingList) (errs field.ErrorList) {
// field rbacv1.ClusterRoleBindingList.TypeMeta has no validation
// field rbacv1.ClusterRoleBindingList.ListMeta has no validation
// field rbacv1.ClusterRoleBindingList.Items
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1.ClusterRoleBinding) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ClusterRoleBinding)...)
return
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRoleBindingList) []rbacv1.ClusterRoleBinding { return oldObj.Items }))...)
return errs
}
// Validate_ClusterRoleList validates an instance of ClusterRoleList according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.ClusterRoleList) (errs field.ErrorList) {
@ -167,6 +253,60 @@ func Validate_Role(ctx context.Context, op operation.Operation, fldPath *field.P
return errs
}
// Validate_RoleBinding validates an instance of RoleBinding according
// to declarative validation rules in the API schema.
func Validate_RoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.RoleBinding) (errs field.ErrorList) {
// field rbacv1.RoleBinding.TypeMeta has no validation
// field rbacv1.RoleBinding.ObjectMeta has no validation
// field rbacv1.RoleBinding.Subjects
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1.Subject) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...)
return
}(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1.RoleBinding) []rbacv1.Subject { return oldObj.Subjects }))...)
// field rbacv1.RoleBinding.RoleRef
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *rbacv1.RoleRef) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call the type's validation function
errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...)
return
}(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1.RoleBinding) *rbacv1.RoleRef { return &oldObj.RoleRef }))...)
return errs
}
// Validate_RoleBindingList validates an instance of RoleBindingList according
// to declarative validation rules in the API schema.
func Validate_RoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.RoleBindingList) (errs field.ErrorList) {
// field rbacv1.RoleBindingList.TypeMeta has no validation
// field rbacv1.RoleBindingList.ListMeta has no validation
// field rbacv1.RoleBindingList.Items
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1.RoleBinding) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_RoleBinding)...)
return
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1.RoleBindingList) []rbacv1.RoleBinding { return oldObj.Items }))...)
return errs
}
// Validate_RoleList validates an instance of RoleList according
// to declarative validation rules in the API schema.
func Validate_RoleList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.RoleList) (errs field.ErrorList) {
@ -187,3 +327,59 @@ func Validate_RoleList(ctx context.Context, op operation.Operation, fldPath *fie
return errs
}
// Validate_RoleRef validates an instance of RoleRef according
// to declarative validation rules in the API schema.
func Validate_RoleRef(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.RoleRef) (errs field.ErrorList) {
// field rbacv1.RoleRef.APIGroup has no validation
// field rbacv1.RoleRef.Kind
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (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); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("kind"), &obj.Kind, safe.Field(oldObj, func(oldObj *rbacv1.RoleRef) *string { return &oldObj.Kind }))...)
// field rbacv1.RoleRef.Name has no validation
return errs
}
// 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 *rbacv1.Subject) (errs field.ErrorList) {
// field rbacv1.Subject.Kind
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (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); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("kind"), &obj.Kind, safe.Field(oldObj, func(oldObj *rbacv1.Subject) *string { return &oldObj.Kind }))...)
// field rbacv1.Subject.APIGroup has no validation
// field rbacv1.Subject.Name has no validation
// field rbacv1.Subject.Namespace has no validation
return errs
}

View file

@ -47,6 +47,22 @@ func RegisterValidations(scheme *runtime.Scheme) error {
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleBinding
scheme.AddValidationFunc((*rbacv1alpha1.ClusterRoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_ClusterRoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.ClusterRoleBinding), safe.Cast[*rbacv1alpha1.ClusterRoleBinding](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleBindingList
scheme.AddValidationFunc((*rbacv1alpha1.ClusterRoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_ClusterRoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.ClusterRoleBindingList), safe.Cast[*rbacv1alpha1.ClusterRoleBindingList](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleList
scheme.AddValidationFunc((*rbacv1alpha1.ClusterRoleList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
@ -63,6 +79,22 @@ func RegisterValidations(scheme *runtime.Scheme) error {
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleBinding
scheme.AddValidationFunc((*rbacv1alpha1.RoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_RoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.RoleBinding), safe.Cast[*rbacv1alpha1.RoleBinding](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleBindingList
scheme.AddValidationFunc((*rbacv1alpha1.RoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_RoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.RoleBindingList), safe.Cast[*rbacv1alpha1.RoleBindingList](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleList
scheme.AddValidationFunc((*rbacv1alpha1.RoleList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
@ -96,6 +128,62 @@ func Validate_ClusterRole(ctx context.Context, op operation.Operation, fldPath *
return errs
}
// Validate_ClusterRoleBinding validates an instance of ClusterRoleBinding according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.ClusterRoleBinding) (errs field.ErrorList) {
// field rbacv1alpha1.ClusterRoleBinding.TypeMeta has no validation
// field rbacv1alpha1.ClusterRoleBinding.ObjectMeta has no validation
// field rbacv1alpha1.ClusterRoleBinding.Subjects
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.Subject) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...)
return
}(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRoleBinding) []rbacv1alpha1.Subject { return oldObj.Subjects }))...)
// field rbacv1alpha1.ClusterRoleBinding.RoleRef
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleRef) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call the type's validation function
errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...)
return
}(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRoleBinding) *rbacv1alpha1.RoleRef { return &oldObj.RoleRef }))...)
return errs
}
// Validate_ClusterRoleBindingList validates an instance of ClusterRoleBindingList according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.ClusterRoleBindingList) (errs field.ErrorList) {
// field rbacv1alpha1.ClusterRoleBindingList.TypeMeta has no validation
// field rbacv1alpha1.ClusterRoleBindingList.ListMeta has no validation
// field rbacv1alpha1.ClusterRoleBindingList.Items
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.ClusterRoleBinding) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ClusterRoleBinding)...)
return
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRoleBindingList) []rbacv1alpha1.ClusterRoleBinding {
return oldObj.Items
}))...)
return errs
}
// Validate_ClusterRoleList validates an instance of ClusterRoleList according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.ClusterRoleList) (errs field.ErrorList) {
@ -167,6 +255,60 @@ func Validate_Role(ctx context.Context, op operation.Operation, fldPath *field.P
return errs
}
// Validate_RoleBinding validates an instance of RoleBinding according
// to declarative validation rules in the API schema.
func Validate_RoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleBinding) (errs field.ErrorList) {
// field rbacv1alpha1.RoleBinding.TypeMeta has no validation
// field rbacv1alpha1.RoleBinding.ObjectMeta has no validation
// field rbacv1alpha1.RoleBinding.Subjects
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.Subject) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...)
return
}(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleBinding) []rbacv1alpha1.Subject { return oldObj.Subjects }))...)
// field rbacv1alpha1.RoleBinding.RoleRef
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleRef) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call the type's validation function
errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...)
return
}(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleBinding) *rbacv1alpha1.RoleRef { return &oldObj.RoleRef }))...)
return errs
}
// Validate_RoleBindingList validates an instance of RoleBindingList according
// to declarative validation rules in the API schema.
func Validate_RoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleBindingList) (errs field.ErrorList) {
// field rbacv1alpha1.RoleBindingList.TypeMeta has no validation
// field rbacv1alpha1.RoleBindingList.ListMeta has no validation
// field rbacv1alpha1.RoleBindingList.Items
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.RoleBinding) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_RoleBinding)...)
return
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleBindingList) []rbacv1alpha1.RoleBinding { return oldObj.Items }))...)
return errs
}
// Validate_RoleList validates an instance of RoleList according
// to declarative validation rules in the API schema.
func Validate_RoleList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleList) (errs field.ErrorList) {
@ -187,3 +329,59 @@ func Validate_RoleList(ctx context.Context, op operation.Operation, fldPath *fie
return errs
}
// Validate_RoleRef validates an instance of RoleRef according
// to declarative validation rules in the API schema.
func Validate_RoleRef(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleRef) (errs field.ErrorList) {
// field rbacv1alpha1.RoleRef.APIGroup has no validation
// field rbacv1alpha1.RoleRef.Kind
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (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); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("kind"), &obj.Kind, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleRef) *string { return &oldObj.Kind }))...)
// field rbacv1alpha1.RoleRef.Name has no validation
return errs
}
// 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 *rbacv1alpha1.Subject) (errs field.ErrorList) {
// field rbacv1alpha1.Subject.Kind
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (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); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("kind"), &obj.Kind, safe.Field(oldObj, func(oldObj *rbacv1alpha1.Subject) *string { return &oldObj.Kind }))...)
// field rbacv1alpha1.Subject.APIVersion has no validation
// field rbacv1alpha1.Subject.Name has no validation
// field rbacv1alpha1.Subject.Namespace has no validation
return errs
}

View file

@ -47,6 +47,22 @@ func RegisterValidations(scheme *runtime.Scheme) error {
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleBinding
scheme.AddValidationFunc((*rbacv1beta1.ClusterRoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_ClusterRoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.ClusterRoleBinding), safe.Cast[*rbacv1beta1.ClusterRoleBinding](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleBindingList
scheme.AddValidationFunc((*rbacv1beta1.ClusterRoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_ClusterRoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.ClusterRoleBindingList), safe.Cast[*rbacv1beta1.ClusterRoleBindingList](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type ClusterRoleList
scheme.AddValidationFunc((*rbacv1beta1.ClusterRoleList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
@ -63,6 +79,22 @@ func RegisterValidations(scheme *runtime.Scheme) error {
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleBinding
scheme.AddValidationFunc((*rbacv1beta1.RoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_RoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.RoleBinding), safe.Cast[*rbacv1beta1.RoleBinding](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleBindingList
scheme.AddValidationFunc((*rbacv1beta1.RoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
case "/":
return Validate_RoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.RoleBindingList), safe.Cast[*rbacv1beta1.RoleBindingList](oldObj))
}
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
})
// type RoleList
scheme.AddValidationFunc((*rbacv1beta1.RoleList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
switch op.Request.SubresourcePath() {
@ -96,6 +128,60 @@ func Validate_ClusterRole(ctx context.Context, op operation.Operation, fldPath *
return errs
}
// Validate_ClusterRoleBinding validates an instance of ClusterRoleBinding according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.ClusterRoleBinding) (errs field.ErrorList) {
// field rbacv1beta1.ClusterRoleBinding.TypeMeta has no validation
// field rbacv1beta1.ClusterRoleBinding.ObjectMeta has no validation
// field rbacv1beta1.ClusterRoleBinding.Subjects
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1beta1.Subject) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...)
return
}(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRoleBinding) []rbacv1beta1.Subject { return oldObj.Subjects }))...)
// field rbacv1beta1.ClusterRoleBinding.RoleRef
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleRef) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call the type's validation function
errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...)
return
}(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRoleBinding) *rbacv1beta1.RoleRef { return &oldObj.RoleRef }))...)
return errs
}
// Validate_ClusterRoleBindingList validates an instance of ClusterRoleBindingList according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.ClusterRoleBindingList) (errs field.ErrorList) {
// field rbacv1beta1.ClusterRoleBindingList.TypeMeta has no validation
// field rbacv1beta1.ClusterRoleBindingList.ListMeta has no validation
// field rbacv1beta1.ClusterRoleBindingList.Items
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1beta1.ClusterRoleBinding) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ClusterRoleBinding)...)
return
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRoleBindingList) []rbacv1beta1.ClusterRoleBinding { return oldObj.Items }))...)
return errs
}
// Validate_ClusterRoleList validates an instance of ClusterRoleList according
// to declarative validation rules in the API schema.
func Validate_ClusterRoleList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.ClusterRoleList) (errs field.ErrorList) {
@ -167,6 +253,60 @@ func Validate_Role(ctx context.Context, op operation.Operation, fldPath *field.P
return errs
}
// Validate_RoleBinding validates an instance of RoleBinding according
// to declarative validation rules in the API schema.
func Validate_RoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleBinding) (errs field.ErrorList) {
// field rbacv1beta1.RoleBinding.TypeMeta has no validation
// field rbacv1beta1.RoleBinding.ObjectMeta has no validation
// field rbacv1beta1.RoleBinding.Subjects
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1beta1.Subject) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...)
return
}(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleBinding) []rbacv1beta1.Subject { return oldObj.Subjects }))...)
// field rbacv1beta1.RoleBinding.RoleRef
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleRef) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call the type's validation function
errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...)
return
}(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleBinding) *rbacv1beta1.RoleRef { return &oldObj.RoleRef }))...)
return errs
}
// Validate_RoleBindingList validates an instance of RoleBindingList according
// to declarative validation rules in the API schema.
func Validate_RoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleBindingList) (errs field.ErrorList) {
// field rbacv1beta1.RoleBindingList.TypeMeta has no validation
// field rbacv1beta1.RoleBindingList.ListMeta has no validation
// field rbacv1beta1.RoleBindingList.Items
errs = append(errs,
func(fldPath *field.Path, obj, oldObj []rbacv1beta1.RoleBinding) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
return nil
}
// iterate the list and call the type's validation function
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_RoleBinding)...)
return
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleBindingList) []rbacv1beta1.RoleBinding { return oldObj.Items }))...)
return errs
}
// Validate_RoleList validates an instance of RoleList according
// to declarative validation rules in the API schema.
func Validate_RoleList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleList) (errs field.ErrorList) {
@ -187,3 +327,59 @@ func Validate_RoleList(ctx context.Context, op operation.Operation, fldPath *fie
return errs
}
// Validate_RoleRef validates an instance of RoleRef according
// to declarative validation rules in the API schema.
func Validate_RoleRef(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleRef) (errs field.ErrorList) {
// field rbacv1beta1.RoleRef.APIGroup has no validation
// field rbacv1beta1.RoleRef.Kind
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (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); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("kind"), &obj.Kind, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleRef) *string { return &oldObj.Kind }))...)
// field rbacv1beta1.RoleRef.Name has no validation
return errs
}
// 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 *rbacv1beta1.Subject) (errs field.ErrorList) {
// field rbacv1beta1.Subject.Kind
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
// don't revalidate unchanged data
if op.Type == operation.Update && (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); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("kind"), &obj.Kind, safe.Field(oldObj, func(oldObj *rbacv1beta1.Subject) *string { return &oldObj.Kind }))...)
// field rbacv1beta1.Subject.APIGroup has no validation
// field rbacv1beta1.Subject.Name has no validation
// field rbacv1beta1.Subject.Namespace has no validation
return errs
}

View file

@ -0,0 +1,134 @@
/*
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 rolebinding
import (
"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"
rbac "k8s.io/kubernetes/pkg/apis/rbac"
)
var apiVersions = []string{"v1", "v1alpha1", "v1beta1"}
func TestDeclarativeValidateForDeclarative(t *testing.T) {
for _, apiVersion := range apiVersions {
testDeclarativeValidateForDeclarative(t, apiVersion)
}
}
func testDeclarativeValidateForDeclarative(t *testing.T, apiVersion string) {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
APIGroup: "rbac.authorization.k8s.io",
APIVersion: apiVersion,
})
testCases := map[string]struct {
input rbac.RoleBinding
expectedErrs field.ErrorList
}{
"missing roleref name": {
input: rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{Name: "test-binding", Namespace: "test-namespace"},
RoleRef: rbac.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
},
Subjects: []rbac.Subject{
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "user"},
},
},
expectedErrs: field.ErrorList{
field.Required(field.NewPath("roleRef", "name"), "name is required"),
},
},
"valid binding": {
input: rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{Name: "valid-binding", Namespace: "test-namespace"},
RoleRef: rbac.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: "admin",
},
Subjects: []rbac.Subject{
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "user"},
},
},
expectedErrs: field.ErrorList{},
},
}
for k, tc := range testCases {
t.Run(k, func(t *testing.T) {
apitesting.VerifyValidationEquivalence(t, ctx, &tc.input, Strategy.Validate, tc.expectedErrs)
})
}
}
func TestValidateUpdateForDeclarative(t *testing.T) {
for _, apiVersion := range apiVersions {
testValidateUpdateForDeclarative(t, apiVersion)
}
}
func testValidateUpdateForDeclarative(t *testing.T, apiVersion string) {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
APIGroup: "rbac.authorization.k8s.io",
APIVersion: apiVersion,
})
testCases := map[string]struct {
old rbac.RoleBinding
update rbac.RoleBinding
expectedErrs field.ErrorList
}{
"update immutable roleRef Kind": {
old: rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding", Namespace: "test-namespace"},
RoleRef: rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "Role", Name: "reader"},
},
update: rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding", ResourceVersion: "1", Namespace: "test-namespace"},
RoleRef: rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: "reader"},
},
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("roleRef"), rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: "reader"}, "cannot change roleRef"),
},
},
"valid update": {
old: rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding", Namespace: "test-namespace"},
RoleRef: rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "Role", Name: "reader"},
Subjects: []rbac.Subject{{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "user1"}},
},
update: rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding", ResourceVersion: "1", Namespace: "test-namespace"},
RoleRef: rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "Role", Name: "reader"},
Subjects: []rbac.Subject{
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "user1"},
{Kind: rbac.UserKind, APIGroup: rbac.GroupName, Name: "user2"},
},
},
expectedErrs: field.ErrorList{},
},
}
for k, tc := range testCases {
t.Run(k, func(t *testing.T) {
apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.update, &tc.old, Strategy.ValidateUpdate, tc.expectedErrs)
})
}
}

View file

@ -19,6 +19,7 @@ package rolebinding
import (
"context"
"k8s.io/apimachinery/pkg/api/operation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/registry/rest"
@ -71,7 +72,8 @@ func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
// Validate validates a new RoleBinding. Validation must check for a correct signature.
func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
roleBinding := obj.(*rbac.RoleBinding)
return validation.ValidateRoleBinding(roleBinding)
allErrs := validation.ValidateRoleBinding(roleBinding)
return rest.ValidateDeclarativelyWithMigrationChecks(ctx, legacyscheme.Scheme, roleBinding, nil, allErrs, operation.Create)
}
// WarningsOnCreate returns warnings for the creation of the given object.
@ -84,7 +86,13 @@ func (strategy) Canonicalize(obj runtime.Object) {
// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateRoleBindingUpdate(obj.(*rbac.RoleBinding), old.(*rbac.RoleBinding))
newRoleBinding := obj.(*rbac.RoleBinding)
oldRoleBinding := old.(*rbac.RoleBinding)
allErrs := validation.ValidateRoleBindingUpdate(newRoleBinding, oldRoleBinding)
return rest.ValidateDeclarativelyWithMigrationChecks(ctx, legacyscheme.Scheme, newRoleBinding, oldRoleBinding, allErrs, operation.Update)
}
// WarningsOnUpdate returns warnings for the given update.

View file

@ -80,6 +80,8 @@ type PolicyRule struct {
type Subject struct {
// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount".
// If the Authorizer does not recognized the kind value, the Authorizer should report an error.
// +required
// +k8s:required
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// APIGroup holds the API group of the referenced subject.
// Defaults to "" for ServiceAccount subjects.
@ -100,6 +102,8 @@ type RoleRef struct {
// APIGroup is the group for the resource being referenced
APIGroup string `json:"apiGroup" protobuf:"bytes,1,opt,name=apiGroup"`
// Kind is the type of resource being referenced
// +required
// +k8s:required
Kind string `json:"kind" protobuf:"bytes,2,opt,name=kind"`
// Name is the name of resource being referenced
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`

View file

@ -79,6 +79,8 @@ type PolicyRule struct {
type Subject struct {
// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount".
// If the Authorizer does not recognized the kind value, the Authorizer should report an error.
// +required
// +k8s:required
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// APIVersion holds the API group and version of the referenced subject.
// Defaults to "v1" for ServiceAccount subjects.
@ -99,6 +101,8 @@ type RoleRef struct {
// APIGroup is the group for the resource being referenced
APIGroup string `json:"apiGroup" protobuf:"bytes,1,opt,name=apiGroup"`
// Kind is the type of resource being referenced
// +required
// +k8s:required
Kind string `json:"kind" protobuf:"bytes,2,opt,name=kind"`
// Name is the name of resource being referenced
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`

View file

@ -80,6 +80,8 @@ type PolicyRule struct {
type Subject struct {
// Kind of object being referenced. Values defined by this API group are "User", "Group", and "ServiceAccount".
// If the Authorizer does not recognized the kind value, the Authorizer should report an error.
// +required
// +k8s:required
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// APIGroup holds the API group of the referenced subject.
// Defaults to "" for ServiceAccount subjects.
@ -99,6 +101,8 @@ type RoleRef struct {
// APIGroup is the group for the resource being referenced
APIGroup string `json:"apiGroup" protobuf:"bytes,1,opt,name=apiGroup"`
// Kind is the type of resource being referenced
// +required
// +k8s:required
Kind string `json:"kind" protobuf:"bytes,2,opt,name=kind"`
// Name is the name of resource being referenced
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`