diff --git a/pkg/apis/rbac/v1/zz_generated.validations.go b/pkg/apis/rbac/v1/zz_generated.validations.go index 73e28035dba..44b6e35c742 100644 --- a/pkg/apis/rbac/v1/zz_generated.validations.go +++ b/pkg/apis/rbac/v1/zz_generated.validations.go @@ -22,7 +22,16 @@ limitations under the License. package v1 import ( + context "context" + fmt "fmt" + + rbacv1 "k8s.io/api/rbac/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" ) func init() { localSchemeBuilder.Register(RegisterValidations) } @@ -30,5 +39,151 @@ 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 ClusterRole + scheme.AddValidationFunc((*rbacv1.ClusterRole)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRole(ctx, op, nil /* fldPath */, obj.(*rbacv1.ClusterRole), safe.Cast[*rbacv1.ClusterRole](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() { + case "/": + return Validate_ClusterRoleList(ctx, op, nil /* fldPath */, obj.(*rbacv1.ClusterRoleList), safe.Cast[*rbacv1.ClusterRoleList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type Role + scheme.AddValidationFunc((*rbacv1.Role)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_Role(ctx, op, nil /* fldPath */, obj.(*rbacv1.Role), safe.Cast[*rbacv1.Role](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() { + case "/": + return Validate_RoleList(ctx, op, nil /* fldPath */, obj.(*rbacv1.RoleList), safe.Cast[*rbacv1.RoleList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) return nil } + +// Validate_ClusterRole validates an instance of ClusterRole according +// to declarative validation rules in the API schema. +func Validate_ClusterRole(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.ClusterRole) (errs field.ErrorList) { + // field rbacv1.ClusterRole.TypeMeta has no validation + // field rbacv1.ClusterRole.ObjectMeta has no validation + + // field rbacv1.ClusterRole.Rules + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.PolicyRule) (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_PolicyRule)...) + return + }(fldPath.Child("rules"), obj.Rules, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRole) []rbacv1.PolicyRule { return oldObj.Rules }))...) + + // field rbacv1.ClusterRole.AggregationRule has no validation + 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) { + // field rbacv1.ClusterRoleList.TypeMeta has no validation + // field rbacv1.ClusterRoleList.ListMeta has no validation + + // field rbacv1.ClusterRoleList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.ClusterRole) (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_ClusterRole)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRoleList) []rbacv1.ClusterRole { return oldObj.Items }))...) + + return errs +} + +// Validate_PolicyRule validates an instance of PolicyRule according +// to declarative validation rules in the API schema. +func Validate_PolicyRule(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.PolicyRule) (errs field.ErrorList) { + // field rbacv1.PolicyRule.Verbs + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + // don't revalidate unchanged data + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("verbs"), obj.Verbs, safe.Field(oldObj, func(oldObj *rbacv1.PolicyRule) []string { return oldObj.Verbs }))...) + + // field rbacv1.PolicyRule.APIGroups has no validation + // field rbacv1.PolicyRule.Resources has no validation + // field rbacv1.PolicyRule.ResourceNames has no validation + // field rbacv1.PolicyRule.NonResourceURLs has no validation + return errs +} + +// Validate_Role validates an instance of Role according +// to declarative validation rules in the API schema. +func Validate_Role(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.Role) (errs field.ErrorList) { + // field rbacv1.Role.TypeMeta has no validation + // field rbacv1.Role.ObjectMeta has no validation + + // field rbacv1.Role.Rules + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.PolicyRule) (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_PolicyRule)...) + return + }(fldPath.Child("rules"), obj.Rules, safe.Field(oldObj, func(oldObj *rbacv1.Role) []rbacv1.PolicyRule { return oldObj.Rules }))...) + + 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) { + // field rbacv1.RoleList.TypeMeta has no validation + // field rbacv1.RoleList.ListMeta has no validation + + // field rbacv1.RoleList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.Role) (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_Role)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1.RoleList) []rbacv1.Role { return oldObj.Items }))...) + + return errs +} diff --git a/pkg/apis/rbac/v1alpha1/zz_generated.validations.go b/pkg/apis/rbac/v1alpha1/zz_generated.validations.go index 483365f8c94..90c331d3ba3 100644 --- a/pkg/apis/rbac/v1alpha1/zz_generated.validations.go +++ b/pkg/apis/rbac/v1alpha1/zz_generated.validations.go @@ -22,7 +22,16 @@ limitations under the License. package v1alpha1 import ( + context "context" + fmt "fmt" + + rbacv1alpha1 "k8s.io/api/rbac/v1alpha1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" ) func init() { localSchemeBuilder.Register(RegisterValidations) } @@ -30,5 +39,151 @@ 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 ClusterRole + scheme.AddValidationFunc((*rbacv1alpha1.ClusterRole)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRole(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.ClusterRole), safe.Cast[*rbacv1alpha1.ClusterRole](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() { + case "/": + return Validate_ClusterRoleList(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.ClusterRoleList), safe.Cast[*rbacv1alpha1.ClusterRoleList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type Role + scheme.AddValidationFunc((*rbacv1alpha1.Role)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_Role(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.Role), safe.Cast[*rbacv1alpha1.Role](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() { + case "/": + return Validate_RoleList(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.RoleList), safe.Cast[*rbacv1alpha1.RoleList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) return nil } + +// Validate_ClusterRole validates an instance of ClusterRole according +// to declarative validation rules in the API schema. +func Validate_ClusterRole(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.ClusterRole) (errs field.ErrorList) { + // field rbacv1alpha1.ClusterRole.TypeMeta has no validation + // field rbacv1alpha1.ClusterRole.ObjectMeta has no validation + + // field rbacv1alpha1.ClusterRole.Rules + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.PolicyRule) (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_PolicyRule)...) + return + }(fldPath.Child("rules"), obj.Rules, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRole) []rbacv1alpha1.PolicyRule { return oldObj.Rules }))...) + + // field rbacv1alpha1.ClusterRole.AggregationRule has no validation + 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) { + // field rbacv1alpha1.ClusterRoleList.TypeMeta has no validation + // field rbacv1alpha1.ClusterRoleList.ListMeta has no validation + + // field rbacv1alpha1.ClusterRoleList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.ClusterRole) (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_ClusterRole)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRoleList) []rbacv1alpha1.ClusterRole { return oldObj.Items }))...) + + return errs +} + +// Validate_PolicyRule validates an instance of PolicyRule according +// to declarative validation rules in the API schema. +func Validate_PolicyRule(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.PolicyRule) (errs field.ErrorList) { + // field rbacv1alpha1.PolicyRule.Verbs + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + // don't revalidate unchanged data + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("verbs"), obj.Verbs, safe.Field(oldObj, func(oldObj *rbacv1alpha1.PolicyRule) []string { return oldObj.Verbs }))...) + + // field rbacv1alpha1.PolicyRule.APIGroups has no validation + // field rbacv1alpha1.PolicyRule.Resources has no validation + // field rbacv1alpha1.PolicyRule.ResourceNames has no validation + // field rbacv1alpha1.PolicyRule.NonResourceURLs has no validation + return errs +} + +// Validate_Role validates an instance of Role according +// to declarative validation rules in the API schema. +func Validate_Role(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.Role) (errs field.ErrorList) { + // field rbacv1alpha1.Role.TypeMeta has no validation + // field rbacv1alpha1.Role.ObjectMeta has no validation + + // field rbacv1alpha1.Role.Rules + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.PolicyRule) (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_PolicyRule)...) + return + }(fldPath.Child("rules"), obj.Rules, safe.Field(oldObj, func(oldObj *rbacv1alpha1.Role) []rbacv1alpha1.PolicyRule { return oldObj.Rules }))...) + + 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) { + // field rbacv1alpha1.RoleList.TypeMeta has no validation + // field rbacv1alpha1.RoleList.ListMeta has no validation + + // field rbacv1alpha1.RoleList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.Role) (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_Role)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleList) []rbacv1alpha1.Role { return oldObj.Items }))...) + + return errs +} diff --git a/pkg/apis/rbac/v1beta1/zz_generated.validations.go b/pkg/apis/rbac/v1beta1/zz_generated.validations.go index 0b9e874b635..a405bb6de7d 100644 --- a/pkg/apis/rbac/v1beta1/zz_generated.validations.go +++ b/pkg/apis/rbac/v1beta1/zz_generated.validations.go @@ -22,7 +22,16 @@ limitations under the License. package v1beta1 import ( + context "context" + fmt "fmt" + + rbacv1beta1 "k8s.io/api/rbac/v1beta1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" ) func init() { localSchemeBuilder.Register(RegisterValidations) } @@ -30,5 +39,151 @@ 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 ClusterRole + scheme.AddValidationFunc((*rbacv1beta1.ClusterRole)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRole(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.ClusterRole), safe.Cast[*rbacv1beta1.ClusterRole](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() { + case "/": + return Validate_ClusterRoleList(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.ClusterRoleList), safe.Cast[*rbacv1beta1.ClusterRoleList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type Role + scheme.AddValidationFunc((*rbacv1beta1.Role)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_Role(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.Role), safe.Cast[*rbacv1beta1.Role](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() { + case "/": + return Validate_RoleList(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.RoleList), safe.Cast[*rbacv1beta1.RoleList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) return nil } + +// Validate_ClusterRole validates an instance of ClusterRole according +// to declarative validation rules in the API schema. +func Validate_ClusterRole(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.ClusterRole) (errs field.ErrorList) { + // field rbacv1beta1.ClusterRole.TypeMeta has no validation + // field rbacv1beta1.ClusterRole.ObjectMeta has no validation + + // field rbacv1beta1.ClusterRole.Rules + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.PolicyRule) (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_PolicyRule)...) + return + }(fldPath.Child("rules"), obj.Rules, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRole) []rbacv1beta1.PolicyRule { return oldObj.Rules }))...) + + // field rbacv1beta1.ClusterRole.AggregationRule has no validation + 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) { + // field rbacv1beta1.ClusterRoleList.TypeMeta has no validation + // field rbacv1beta1.ClusterRoleList.ListMeta has no validation + + // field rbacv1beta1.ClusterRoleList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.ClusterRole) (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_ClusterRole)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRoleList) []rbacv1beta1.ClusterRole { return oldObj.Items }))...) + + return errs +} + +// Validate_PolicyRule validates an instance of PolicyRule according +// to declarative validation rules in the API schema. +func Validate_PolicyRule(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.PolicyRule) (errs field.ErrorList) { + // field rbacv1beta1.PolicyRule.Verbs + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + // don't revalidate unchanged data + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("verbs"), obj.Verbs, safe.Field(oldObj, func(oldObj *rbacv1beta1.PolicyRule) []string { return oldObj.Verbs }))...) + + // field rbacv1beta1.PolicyRule.APIGroups has no validation + // field rbacv1beta1.PolicyRule.Resources has no validation + // field rbacv1beta1.PolicyRule.ResourceNames has no validation + // field rbacv1beta1.PolicyRule.NonResourceURLs has no validation + return errs +} + +// Validate_Role validates an instance of Role according +// to declarative validation rules in the API schema. +func Validate_Role(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.Role) (errs field.ErrorList) { + // field rbacv1beta1.Role.TypeMeta has no validation + // field rbacv1beta1.Role.ObjectMeta has no validation + + // field rbacv1beta1.Role.Rules + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.PolicyRule) (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_PolicyRule)...) + return + }(fldPath.Child("rules"), obj.Rules, safe.Field(oldObj, func(oldObj *rbacv1beta1.Role) []rbacv1beta1.PolicyRule { return oldObj.Rules }))...) + + 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) { + // field rbacv1beta1.RoleList.TypeMeta has no validation + // field rbacv1beta1.RoleList.ListMeta has no validation + + // field rbacv1beta1.RoleList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.Role) (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_Role)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleList) []rbacv1beta1.Role { return oldObj.Items }))...) + + return errs +} diff --git a/pkg/apis/rbac/validation/validation.go b/pkg/apis/rbac/validation/validation.go index b65ac19f72e..511159404db 100644 --- a/pkg/apis/rbac/validation/validation.go +++ b/pkg/apis/rbac/validation/validation.go @@ -103,7 +103,7 @@ func ValidateClusterRoleUpdate(role *rbac.ClusterRole, oldRole *rbac.ClusterRole func ValidatePolicyRule(rule rbac.PolicyRule, isNamespaced bool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(rule.Verbs) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value")) + allErrs = append(allErrs, field.Required(fldPath.Child("verbs"), "verbs must contain at least one value").MarkCoveredByDeclarative()) } if len(rule.NonResourceURLs) > 0 { diff --git a/pkg/registry/rbac/clusterrole/declarative_validation_test.go b/pkg/registry/rbac/clusterrole/declarative_validation_test.go new file mode 100644 index 00000000000..6daa5041b01 --- /dev/null +++ b/pkg/registry/rbac/clusterrole/declarative_validation_test.go @@ -0,0 +1,150 @@ +/* +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 clusterrole + +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.ClusterRole + expectedErrs field.ErrorList + }{ + "missing verbs": { + input: rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: "test-role"}, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + }, + }, + }, + expectedErrs: field.ErrorList{ + field.Required(field.NewPath("rules").Index(0).Child("verbs"), "must have at least one verb"), + }, + }, + "valid rule": { + input: rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: "valid-role"}, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, + }, + 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.ClusterRole + update rbac.ClusterRole + expectedErrs field.ErrorList + }{ + "update missing verbs": { + old: rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: "role"}, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get"}, + }, + }, + }, + update: rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: "role", ResourceVersion: "1"}, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + }, + }, + }, + expectedErrs: field.ErrorList{ + field.Required(field.NewPath("rules").Index(0).Child("verbs"), "must have at least one verb"), + }, + }, + "valid update": { + old: rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: "role"}, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get"}, + }, + }, + }, + update: rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: "role", ResourceVersion: "1"}, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list"}, + }, + }, + }, + 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) + }) + } +} diff --git a/pkg/registry/rbac/clusterrole/strategy.go b/pkg/registry/rbac/clusterrole/strategy.go index 4b7a27a3579..e5d63cbc8bb 100644 --- a/pkg/registry/rbac/clusterrole/strategy.go +++ b/pkg/registry/rbac/clusterrole/strategy.go @@ -19,6 +19,7 @@ package clusterrole import ( "context" + "k8s.io/apimachinery/pkg/api/operation" metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" @@ -75,7 +76,16 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis opts := validation.ClusterRoleValidationOptions{ AllowInvalidLabelValueInSelector: false, } - return validation.ValidateClusterRole(clusterRole, opts) + allErrs := validation.ValidateClusterRole(clusterRole, opts) + + return rest.ValidateDeclarativelyWithMigrationChecks( + ctx, + legacyscheme.Scheme, + clusterRole, + nil, + allErrs, + operation.Create, + ) } // WarningsOnCreate returns warnings for the creation of the given object. @@ -93,7 +103,16 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie opts := validation.ClusterRoleValidationOptions{ AllowInvalidLabelValueInSelector: hasInvalidLabelValueInLabelSelector(oldObj), } - return validation.ValidateClusterRoleUpdate(newObj, old.(*rbac.ClusterRole), opts) + errs := validation.ValidateClusterRoleUpdate(newObj, oldObj, opts) + + return rest.ValidateDeclarativelyWithMigrationChecks( + ctx, + legacyscheme.Scheme, + newObj, + oldObj, + errs, + operation.Update, + ) } // WarningsOnUpdate returns warnings for the given update. diff --git a/staging/src/k8s.io/api/rbac/v1/types.go b/staging/src/k8s.io/api/rbac/v1/types.go index f9628b8536a..97ccc2a581d 100644 --- a/staging/src/k8s.io/api/rbac/v1/types.go +++ b/staging/src/k8s.io/api/rbac/v1/types.go @@ -49,6 +49,7 @@ const ( type PolicyRule struct { // Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. // +listType=atomic + // +k8s:required Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"` // APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of diff --git a/staging/src/k8s.io/api/rbac/v1alpha1/types.go b/staging/src/k8s.io/api/rbac/v1alpha1/types.go index 2146b4ce39f..081a8055bd9 100644 --- a/staging/src/k8s.io/api/rbac/v1alpha1/types.go +++ b/staging/src/k8s.io/api/rbac/v1alpha1/types.go @@ -49,6 +49,7 @@ const ( type PolicyRule struct { // Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. // +listType=atomic + // +k8s:required Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"` // APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of diff --git a/staging/src/k8s.io/api/rbac/v1beta1/types.go b/staging/src/k8s.io/api/rbac/v1beta1/types.go index 9cfaaceb92c..35f319f4164 100644 --- a/staging/src/k8s.io/api/rbac/v1beta1/types.go +++ b/staging/src/k8s.io/api/rbac/v1beta1/types.go @@ -49,6 +49,7 @@ const ( type PolicyRule struct { // Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. // +listType=atomic + // +k8s:required Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"` // APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of