Merge pull request #115668 from jiahuif-forks/feature/validating-admission-policy/type-system

Type System for ValidatingAdmissionPolicy
This commit is contained in:
Kubernetes Prow Robot
2023-03-13 23:27:09 -07:00
committed by GitHub
58 changed files with 3893 additions and 122 deletions

View File

@@ -123,6 +123,52 @@ type ValidatingAdmissionPolicy struct {
metav1.ObjectMeta
// Specification of the desired behavior of the ValidatingAdmissionPolicy.
Spec ValidatingAdmissionPolicySpec
// The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy
// behaves in the expected way.
// Populated by the system.
// Read-only.
// +optional
Status ValidatingAdmissionPolicyStatus
}
// ValidatingAdmissionPolicyStatus represents the status of an admission validation policy.
type ValidatingAdmissionPolicyStatus struct {
// The generation observed by the controller.
// +optional
ObservedGeneration int64
// The results of type checking for each expression.
// Presence of this field indicates the completion of the type checking.
// +optional
TypeChecking *TypeChecking
// The conditions represent the latest available observations of a policy's current state.
// +optional
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition
}
// ValidatingAdmissionPolicyConditionType is the condition type of admission validation policy.
type ValidatingAdmissionPolicyConditionType string
// TypeChecking contains results of type checking the expressions in the
// ValidatingAdmissionPolicy
type TypeChecking struct {
// The type checking warnings for each expression.
// +optional
// +listType=atomic
ExpressionWarnings []ExpressionWarning
}
// ExpressionWarning is a warning information that targets a specific expression.
type ExpressionWarning struct {
// The path to the field that refers the expression.
// For example, the reference to the expression of the first item of
// validations is "spec.validations[0].expression"
FieldRef string
// The content of type checking information in a human-readable form.
// Each line of the warning contains the type that the expression is checked
// against, followed by the type check error from the compiler.
Warning string
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -49,6 +49,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ExpressionWarning)(nil), (*admissionregistration.ExpressionWarning)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(a.(*v1alpha1.ExpressionWarning), b.(*admissionregistration.ExpressionWarning), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*admissionregistration.ExpressionWarning)(nil), (*v1alpha1.ExpressionWarning)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(a.(*admissionregistration.ExpressionWarning), b.(*v1alpha1.ExpressionWarning), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.MatchResources)(nil), (*admissionregistration.MatchResources)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_MatchResources_To_admissionregistration_MatchResources(a.(*v1alpha1.MatchResources), b.(*admissionregistration.MatchResources), scope)
}); err != nil {
@@ -89,6 +99,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.TypeChecking)(nil), (*admissionregistration.TypeChecking)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(a.(*v1alpha1.TypeChecking), b.(*admissionregistration.TypeChecking), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*admissionregistration.TypeChecking)(nil), (*v1alpha1.TypeChecking)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(a.(*admissionregistration.TypeChecking), b.(*v1alpha1.TypeChecking), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ValidatingAdmissionPolicy)(nil), (*admissionregistration.ValidatingAdmissionPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ValidatingAdmissionPolicy_To_admissionregistration_ValidatingAdmissionPolicy(a.(*v1alpha1.ValidatingAdmissionPolicy), b.(*admissionregistration.ValidatingAdmissionPolicy), scope)
}); err != nil {
@@ -149,6 +169,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ValidatingAdmissionPolicyStatus)(nil), (*admissionregistration.ValidatingAdmissionPolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(a.(*v1alpha1.ValidatingAdmissionPolicyStatus), b.(*admissionregistration.ValidatingAdmissionPolicyStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*admissionregistration.ValidatingAdmissionPolicyStatus)(nil), (*v1alpha1.ValidatingAdmissionPolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(a.(*admissionregistration.ValidatingAdmissionPolicyStatus), b.(*v1alpha1.ValidatingAdmissionPolicyStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.Validation)(nil), (*admissionregistration.Validation)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_Validation_To_admissionregistration_Validation(a.(*v1alpha1.Validation), b.(*admissionregistration.Validation), scope)
}); err != nil {
@@ -184,6 +214,28 @@ func Convert_admissionregistration_AuditAnnotation_To_v1alpha1_AuditAnnotation(i
return autoConvert_admissionregistration_AuditAnnotation_To_v1alpha1_AuditAnnotation(in, out, s)
}
func autoConvert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(in *v1alpha1.ExpressionWarning, out *admissionregistration.ExpressionWarning, s conversion.Scope) error {
out.FieldRef = in.FieldRef
out.Warning = in.Warning
return nil
}
// Convert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning is an autogenerated conversion function.
func Convert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(in *v1alpha1.ExpressionWarning, out *admissionregistration.ExpressionWarning, s conversion.Scope) error {
return autoConvert_v1alpha1_ExpressionWarning_To_admissionregistration_ExpressionWarning(in, out, s)
}
func autoConvert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in *admissionregistration.ExpressionWarning, out *v1alpha1.ExpressionWarning, s conversion.Scope) error {
out.FieldRef = in.FieldRef
out.Warning = in.Warning
return nil
}
// Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning is an autogenerated conversion function.
func Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in *admissionregistration.ExpressionWarning, out *v1alpha1.ExpressionWarning, s conversion.Scope) error {
return autoConvert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in, out, s)
}
func autoConvert_v1alpha1_MatchResources_To_admissionregistration_MatchResources(in *v1alpha1.MatchResources, out *admissionregistration.MatchResources, s conversion.Scope) error {
out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector))
out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector))
@@ -322,11 +374,34 @@ func Convert_admissionregistration_ParamRef_To_v1alpha1_ParamRef(in *admissionre
return autoConvert_admissionregistration_ParamRef_To_v1alpha1_ParamRef(in, out, s)
}
func autoConvert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(in *v1alpha1.TypeChecking, out *admissionregistration.TypeChecking, s conversion.Scope) error {
out.ExpressionWarnings = *(*[]admissionregistration.ExpressionWarning)(unsafe.Pointer(&in.ExpressionWarnings))
return nil
}
// Convert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking is an autogenerated conversion function.
func Convert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(in *v1alpha1.TypeChecking, out *admissionregistration.TypeChecking, s conversion.Scope) error {
return autoConvert_v1alpha1_TypeChecking_To_admissionregistration_TypeChecking(in, out, s)
}
func autoConvert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(in *admissionregistration.TypeChecking, out *v1alpha1.TypeChecking, s conversion.Scope) error {
out.ExpressionWarnings = *(*[]v1alpha1.ExpressionWarning)(unsafe.Pointer(&in.ExpressionWarnings))
return nil
}
// Convert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking is an autogenerated conversion function.
func Convert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(in *admissionregistration.TypeChecking, out *v1alpha1.TypeChecking, s conversion.Scope) error {
return autoConvert_admissionregistration_TypeChecking_To_v1alpha1_TypeChecking(in, out, s)
}
func autoConvert_v1alpha1_ValidatingAdmissionPolicy_To_admissionregistration_ValidatingAdmissionPolicy(in *v1alpha1.ValidatingAdmissionPolicy, out *admissionregistration.ValidatingAdmissionPolicy, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1alpha1_ValidatingAdmissionPolicySpec_To_admissionregistration_ValidatingAdmissionPolicySpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
@@ -340,6 +415,9 @@ func autoConvert_admissionregistration_ValidatingAdmissionPolicy_To_v1alpha1_Val
if err := Convert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1_ValidatingAdmissionPolicySpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
@@ -544,6 +622,30 @@ func Convert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1_Val
return autoConvert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1_ValidatingAdmissionPolicySpec(in, out, s)
}
func autoConvert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(in *v1alpha1.ValidatingAdmissionPolicyStatus, out *admissionregistration.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
out.ObservedGeneration = in.ObservedGeneration
out.TypeChecking = (*admissionregistration.TypeChecking)(unsafe.Pointer(in.TypeChecking))
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}
// Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus is an autogenerated conversion function.
func Convert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(in *v1alpha1.ValidatingAdmissionPolicyStatus, out *admissionregistration.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
return autoConvert_v1alpha1_ValidatingAdmissionPolicyStatus_To_admissionregistration_ValidatingAdmissionPolicyStatus(in, out, s)
}
func autoConvert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(in *admissionregistration.ValidatingAdmissionPolicyStatus, out *v1alpha1.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
out.ObservedGeneration = in.ObservedGeneration
out.TypeChecking = (*v1alpha1.TypeChecking)(unsafe.Pointer(in.TypeChecking))
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}
// Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus is an autogenerated conversion function.
func Convert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(in *admissionregistration.ValidatingAdmissionPolicyStatus, out *v1alpha1.ValidatingAdmissionPolicyStatus, s conversion.Scope) error {
return autoConvert_admissionregistration_ValidatingAdmissionPolicyStatus_To_v1alpha1_ValidatingAdmissionPolicyStatus(in, out, s)
}
func autoConvert_v1alpha1_Validation_To_admissionregistration_Validation(in *v1alpha1.Validation, out *admissionregistration.Validation, s conversion.Scope) error {
out.Expression = in.Expression
out.Message = in.Message

View File

@@ -33,6 +33,7 @@ import (
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/util/jsonpath"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
admissionregistrationv1 "k8s.io/kubernetes/pkg/apis/admissionregistration/v1"
@@ -916,7 +917,56 @@ func ValidateValidatingAdmissionPolicyUpdate(newC, oldC *admissionregistration.V
return validateValidatingAdmissionPolicy(newC)
}
// ValidateValidatingAdmissionPolicyStatusUpdate validates update of status of validating admission policy
func ValidateValidatingAdmissionPolicyStatusUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList {
return validateValidatingAdmissionPolicyStatus(&newC.Status, field.NewPath("status"))
}
// ValidateValidatingAdmissionPolicyBindingUpdate validates update of validating admission policy
func ValidateValidatingAdmissionPolicyBindingUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicyBinding) field.ErrorList {
return validateValidatingAdmissionPolicyBinding(newC)
}
func validateValidatingAdmissionPolicyStatus(status *admissionregistration.ValidatingAdmissionPolicyStatus, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
allErrors = append(allErrors, validateTypeChecking(status.TypeChecking, fldPath.Child("typeChecking"))...)
allErrors = append(allErrors, metav1validation.ValidateConditions(status.Conditions, fldPath.Child("conditions"))...)
return allErrors
}
func validateTypeChecking(typeChecking *admissionregistration.TypeChecking, fldPath *field.Path) field.ErrorList {
if typeChecking == nil {
return nil
}
return validateExpressionWarnings(typeChecking.ExpressionWarnings, fldPath.Child("expressionWarnings"))
}
func validateExpressionWarnings(expressionWarnings []admissionregistration.ExpressionWarning, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
for i, warning := range expressionWarnings {
allErrors = append(allErrors, validateExpressionWarning(&warning, fldPath.Index(i))...)
}
return allErrors
}
func validateExpressionWarning(expressionWarning *admissionregistration.ExpressionWarning, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if expressionWarning.Warning == "" {
allErrors = append(allErrors, field.Required(fldPath.Child("warning"), ""))
}
allErrors = append(allErrors, validateFieldRef(expressionWarning.FieldRef, fldPath.Child("fieldRef"))...)
return allErrors
}
func validateFieldRef(fieldRef string, fldPath *field.Path) field.ErrorList {
fieldRef = strings.TrimSpace(fieldRef)
if fieldRef == "" {
return field.ErrorList{field.Required(fldPath, "")}
}
jsonPath := jsonpath.New("spec")
if err := jsonPath.Parse(fmt.Sprintf("{%s}", fieldRef)); err != nil {
return field.ErrorList{field.Invalid(fldPath, fieldRef, fmt.Sprintf("invalid JSONPath: %v", err))}
}
// no further checks, for an easier upgrade/rollback
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
)
@@ -3453,3 +3454,83 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
}
}
func TestValidateValidatingAdmissionPolicyStatus(t *testing.T) {
for _, tc := range []struct {
name string
status *admissionregistration.ValidatingAdmissionPolicyStatus
expectedError string
}{
{
name: "empty",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{},
},
{
name: "type checking",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
FieldRef: "spec.validations[0].expression",
Warning: "message",
},
},
},
},
},
{
name: "type checking bad json path",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
FieldRef: "spec[foo]",
Warning: "message",
},
},
},
},
expectedError: "invalid JSONPath: invalid array index foo",
},
{
name: "type checking missing warning",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
FieldRef: "spec.validations[0].expression",
},
},
},
},
expectedError: "Required value",
},
{
name: "type checking missing fieldRef",
status: &admissionregistration.ValidatingAdmissionPolicyStatus{
TypeChecking: &admissionregistration.TypeChecking{
ExpressionWarnings: []admissionregistration.ExpressionWarning{
{
Warning: "message",
},
},
},
},
expectedError: "Required value",
},
} {
t.Run(tc.name, func(t *testing.T) {
errs := validateValidatingAdmissionPolicyStatus(tc.status, field.NewPath("status"))
err := errs.ToAggregate()
if err != nil {
if e, a := tc.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
t.Errorf("expected to contain %s, got %s", e, a)
}
} else {
if tc.expectedError != "" {
t.Errorf("unexpected no error, expected to contain %s", tc.expectedError)
}
}
})
}
}

View File

@@ -42,6 +42,22 @@ func (in *AuditAnnotation) DeepCopy() *AuditAnnotation {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExpressionWarning) DeepCopyInto(out *ExpressionWarning) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExpressionWarning.
func (in *ExpressionWarning) DeepCopy() *ExpressionWarning {
if in == nil {
return nil
}
out := new(ExpressionWarning)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchResources) DeepCopyInto(out *MatchResources) {
*out = *in
@@ -350,12 +366,34 @@ func (in *ServiceReference) DeepCopy() *ServiceReference {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TypeChecking) DeepCopyInto(out *TypeChecking) {
*out = *in
if in.ExpressionWarnings != nil {
in, out := &in.ExpressionWarnings, &out.ExpressionWarnings
*out = make([]ExpressionWarning, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypeChecking.
func (in *TypeChecking) DeepCopy() *TypeChecking {
if in == nil {
return nil
}
out := new(TypeChecking)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingAdmissionPolicy) DeepCopyInto(out *ValidatingAdmissionPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
@@ -544,6 +582,34 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopy() *ValidatingAdmissionPolicySp
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingAdmissionPolicyStatus) DeepCopyInto(out *ValidatingAdmissionPolicyStatus) {
*out = *in
if in.TypeChecking != nil {
in, out := &in.TypeChecking, &out.TypeChecking
*out = new(TypeChecking)
(*in).DeepCopyInto(*out)
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidatingAdmissionPolicyStatus.
func (in *ValidatingAdmissionPolicyStatus) DeepCopy() *ValidatingAdmissionPolicyStatus {
if in == nil {
return nil
}
out := new(ValidatingAdmissionPolicyStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ValidatingWebhook) DeepCopyInto(out *ValidatingWebhook) {
*out = *in

View File

@@ -46,16 +46,19 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/admissionregistration/v1.ValidatingWebhookConfigurationList": schema_k8sio_api_admissionregistration_v1_ValidatingWebhookConfigurationList(ref),
"k8s.io/api/admissionregistration/v1.WebhookClientConfig": schema_k8sio_api_admissionregistration_v1_WebhookClientConfig(ref),
"k8s.io/api/admissionregistration/v1alpha1.AuditAnnotation": schema_k8sio_api_admissionregistration_v1alpha1_AuditAnnotation(ref),
"k8s.io/api/admissionregistration/v1alpha1.ExpressionWarning": schema_k8sio_api_admissionregistration_v1alpha1_ExpressionWarning(ref),
"k8s.io/api/admissionregistration/v1alpha1.MatchResources": schema_k8sio_api_admissionregistration_v1alpha1_MatchResources(ref),
"k8s.io/api/admissionregistration/v1alpha1.NamedRuleWithOperations": schema_k8sio_api_admissionregistration_v1alpha1_NamedRuleWithOperations(ref),
"k8s.io/api/admissionregistration/v1alpha1.ParamKind": schema_k8sio_api_admissionregistration_v1alpha1_ParamKind(ref),
"k8s.io/api/admissionregistration/v1alpha1.ParamRef": schema_k8sio_api_admissionregistration_v1alpha1_ParamRef(ref),
"k8s.io/api/admissionregistration/v1alpha1.TypeChecking": schema_k8sio_api_admissionregistration_v1alpha1_TypeChecking(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicy": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicy(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyBinding": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBinding(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyBindingList": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBindingList(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyBindingSpec": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBindingSpec(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyList": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyList(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicySpec(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyStatus": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyStatus(ref),
"k8s.io/api/admissionregistration/v1alpha1.Validation": schema_k8sio_api_admissionregistration_v1alpha1_Validation(ref),
"k8s.io/api/admissionregistration/v1beta1.MutatingWebhook": schema_k8sio_api_admissionregistration_v1beta1_MutatingWebhook(ref),
"k8s.io/api/admissionregistration/v1beta1.MutatingWebhookConfiguration": schema_k8sio_api_admissionregistration_v1beta1_MutatingWebhookConfiguration(ref),
@@ -1889,6 +1892,36 @@ func schema_k8sio_api_admissionregistration_v1alpha1_AuditAnnotation(ref common.
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_ExpressionWarning(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ExpressionWarning is a warning information that targets a specific expression.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"fieldRef": {
SchemaProps: spec.SchemaProps{
Description: "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"warning": {
SchemaProps: spec.SchemaProps{
Description: "The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"fieldRef", "warning"},
},
},
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_MatchResources(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -2156,6 +2189,40 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ParamRef(ref common.Referen
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_TypeChecking(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "TypeChecking contains results of type checking the expressions in the ValidatingAdmissionPolicy",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"expressionWarnings": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "atomic",
},
},
SchemaProps: spec.SchemaProps{
Description: "The type checking warnings for each expression.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.ExpressionWarning"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/admissionregistration/v1alpha1.ExpressionWarning"},
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -2191,11 +2258,18 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicy(r
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Description: "The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way. Populated by the system. Read-only.",
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyStatus"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec", "k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
@@ -2469,6 +2543,56 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicySp
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"observedGeneration": {
SchemaProps: spec.SchemaProps{
Description: "The generation observed by the controller.",
Type: []string{"integer"},
Format: "int64",
},
},
"typeChecking": {
SchemaProps: spec.SchemaProps{
Description: "The results of type checking for each expression. Presence of this field indicates the completion of the type checking.",
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.TypeChecking"),
},
},
"conditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"type",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "The conditions represent the latest available observations of a policy's current state.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/admissionregistration/v1alpha1.TypeChecking", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"},
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_Validation(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{

View File

@@ -28,6 +28,7 @@ import (
utilwait "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
webhookinit "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
genericapiserver "k8s.io/apiserver/pkg/server"
egressselector "k8s.io/apiserver/pkg/server/egressselector"
"k8s.io/apiserver/pkg/util/webhook"
@@ -47,7 +48,7 @@ type Config struct {
}
// New sets up the plugins and admission start hooks needed for admission
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider) ([]admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider, schemaResolver resolver.SchemaResolver) ([]admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
webhookAuthResolverWrapper := webhook.NewDefaultAuthenticationInfoResolverWrapper(proxyTransport, egressSelector, c.LoopbackClientConfig, tp)
webhookPluginInitializer := webhookinit.NewPluginInitializer(webhookAuthResolverWrapper, serviceResolver)
@@ -63,13 +64,13 @@ func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselec
if err != nil {
return nil, nil, err
}
discoveryClient := cacheddiscovery.NewMemCacheClient(clientset.Discovery())
discoveryRESTMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
kubePluginInitializer := NewPluginInitializer(
cloudConfig,
discoveryRESTMapper,
quotainstall.NewQuotaConfigurationForAdmission(),
schemaResolver,
)
admissionPostStartHook := func(context genericapiserver.PostStartHookContext) error {

View File

@@ -20,6 +20,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
quota "k8s.io/apiserver/pkg/quota/v1"
)
@@ -35,6 +36,7 @@ type PluginInitializer struct {
cloudConfig []byte
restMapper meta.RESTMapper
quotaConfiguration quota.Configuration
schemaResolver resolver.SchemaResolver
}
var _ admission.PluginInitializer = &PluginInitializer{}
@@ -46,11 +48,13 @@ func NewPluginInitializer(
cloudConfig []byte,
restMapper meta.RESTMapper,
quotaConfiguration quota.Configuration,
schemaResolver resolver.SchemaResolver,
) *PluginInitializer {
return &PluginInitializer{
cloudConfig: cloudConfig,
restMapper: restMapper,
quotaConfiguration: quotaConfiguration,
schemaResolver: schemaResolver,
}
}
@@ -68,4 +72,8 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
if wants, ok := plugin.(initializer.WantsQuotaConfiguration); ok {
wants.SetQuotaConfiguration(i.quotaConfiguration)
}
if wants, ok := plugin.(initializer.WantsSchemaResolver); ok {
wants.SetSchemaResolver(i.schemaResolver)
}
}

View File

@@ -49,7 +49,7 @@ func (p *WantsCloudConfigAdmissionPlugin) SetCloudConfig(cloudConfig []byte) {
func TestCloudConfigAdmissionPlugin(t *testing.T) {
cloudConfig := []byte("cloud-configuration")
initializer := NewPluginInitializer(cloudConfig, nil, nil)
initializer := NewPluginInitializer(cloudConfig, nil, nil, nil)
wantsCloudConfigAdmission := &WantsCloudConfigAdmissionPlugin{}
initializer.Initialize(wantsCloudConfigAdmission)
@@ -94,7 +94,7 @@ func (p *WantsRESTMapperAdmissionPlugin) SetRESTMapper(mapper meta.RESTMapper) {
func TestRESTMapperAdmissionPlugin(t *testing.T) {
mapper := doNothingRESTMapper{}
initializer := NewPluginInitializer(nil, mapper, nil)
initializer := NewPluginInitializer(nil, mapper, nil, nil)
wantsRESTMapperAdmission := &WantsRESTMapperAdmissionPlugin{}
initializer.Initialize(wantsRESTMapperAdmission)
@@ -121,7 +121,7 @@ func (p *WantsQuotaConfigurationAdmissionPlugin) SetQuotaConfiguration(config qu
func TestQuotaConfigurationAdmissionPlugin(t *testing.T) {
config := doNothingQuotaConfiguration{}
initializer := NewPluginInitializer(nil, nil, config)
initializer := NewPluginInitializer(nil, nil, config, nil)
wantsQuotaConfigurationAdmission := &WantsQuotaConfigurationAdmissionPlugin{}
initializer.Initialize(wantsQuotaConfigurationAdmission)

View File

@@ -95,12 +95,13 @@ func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstora
// validatingadmissionpolicies
if resource := "validatingadmissionpolicies"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1alpha1.SchemeGroupVersion.WithResource(resource)) {
policyStorage, err := validatingadmissionpolicystorage.NewREST(restOptionsGetter, p.Authorizer, r)
policyStorage, policyStatusStorage, err := validatingadmissionpolicystorage.NewREST(restOptionsGetter, p.Authorizer, r)
if err != nil {
return storage, err
}
policyGetter = policyStorage
storage[resource] = policyStorage
storage[resource+"/status"] = policyStatusStorage
}
// validatingadmissionpolicybindings

View File

@@ -17,6 +17,9 @@ limitations under the License.
package storage
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic"
@@ -28,6 +31,7 @@ import (
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/admissionregistration/resolver"
"k8s.io/kubernetes/pkg/registry/admissionregistration/validatingadmissionpolicy"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
)
// REST implements a RESTStorage for validatingAdmissionPolicy against etcd
@@ -35,10 +39,16 @@ type REST struct {
*genericregistry.Store
}
// StatusREST implements a RESTStorage for ValidatingAdmissionPolicyStatus
type StatusREST struct {
// DO NOT embed Store, manually select function to export.
store *genericregistry.Store
}
var groupResource = admissionregistration.Resource("validatingadmissionpolicies")
// NewREST returns a RESTStorage object that will work against validatingAdmissionPolicy.
func NewREST(optsGetter generic.RESTOptionsGetter, authorizer authorizer.Authorizer, resourceResolver resolver.ResourceResolver) (*REST, error) {
// NewREST returns two RESTStorage objects that will work against validatingAdmissionPolicy and its status.
func NewREST(optsGetter generic.RESTOptionsGetter, authorizer authorizer.Authorizer, resourceResolver resolver.ResourceResolver) (*REST, *StatusREST, error) {
r := &REST{}
strategy := validatingadmissionpolicy.NewStrategy(authorizer, resourceResolver)
store := &genericregistry.Store{
@@ -50,18 +60,24 @@ func NewREST(optsGetter generic.RESTOptionsGetter, authorizer authorizer.Authori
DefaultQualifiedResource: groupResource,
SingularQualifiedResource: admissionregistration.Resource("validatingadmissionpolicy"),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
ResetFieldsStrategy: strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
return nil, err
return nil, nil, err
}
r.Store = store
return r, nil
statusStrategy := validatingadmissionpolicy.NewStatusStrategy(strategy)
statusStore := *store
statusStore.UpdateStrategy = statusStrategy
statusStore.ResetFieldsStrategy = statusStrategy
sr := &StatusREST{store: &statusStore}
return r, sr, nil
}
// Implement CategoriesProvider
@@ -71,3 +87,34 @@ var _ rest.CategoriesProvider = &REST{}
func (r *REST) Categories() []string {
return []string{"api-extensions"}
}
// New generates a new ValidatingAdmissionPolicy object
func (r *StatusREST) New() runtime.Object {
return &admissionregistration.ValidatingAdmissionPolicy{}
}
// Destroy disposes the store object. For the StatusREST, this is a no-op.
func (r *StatusREST) Destroy() {
// Given that underlying store is shared with REST,
// we don't destroy it here explicitly.
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// GetResetFields returns the fields that got reset by the REST endpoint
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// ConvertToTable delegates to the store, implements TableConverter
func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return r.store.ConvertToTable(ctx, object, tableOptions)
}
// Update alters the status subset of an object. Delegates to the store
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options)
}

View File

@@ -211,7 +211,7 @@ func newStorage(t *testing.T, authorizer authorizer.Authorizer, resourceResolver
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "validatingadmissionpolicies"}
storage, err := NewREST(restOptions, authorizer, resourceResolver)
storage, _, err := NewREST(restOptions, authorizer, resourceResolver)
if err != nil {
t.Fatalf("unexpected error from REST storage: %v", err)
}

View File

@@ -19,7 +19,10 @@ package validatingadmissionpolicy
import (
"context"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/authorization/authorizer"
@@ -56,6 +59,7 @@ func (v *validatingAdmissionPolicyStrategy) NamespaceScoped() bool {
// PrepareForCreate clears the status of an validatingAdmissionPolicy before creation.
func (v *validatingAdmissionPolicyStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
ic := obj.(*admissionregistration.ValidatingAdmissionPolicy)
ic.Status = admissionregistration.ValidatingAdmissionPolicyStatus{}
ic.Generation = 1
}
@@ -64,6 +68,9 @@ func (v *validatingAdmissionPolicyStrategy) PrepareForUpdate(ctx context.Context
newIC := obj.(*admissionregistration.ValidatingAdmissionPolicy)
oldIC := old.(*admissionregistration.ValidatingAdmissionPolicy)
// Prevent any update on the Status object
newIC.Status = oldIC.Status
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See metav1.ObjectMeta description for more information on Generation.
@@ -120,3 +127,53 @@ func (v *validatingAdmissionPolicyStrategy) WarningsOnUpdate(ctx context.Context
func (v *validatingAdmissionPolicyStrategy) AllowUnconditionalUpdate() bool {
return false
}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (v *validatingAdmissionPolicyStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"admissionregistration.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
type validatingAdmissionPolicyStatusStrategy struct {
*validatingAdmissionPolicyStrategy
}
// ValidateUpdate is the default update validation of an update to the status object.
func (s *validatingAdmissionPolicyStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateValidatingAdmissionPolicyStatusUpdate(obj.(*admissionregistration.ValidatingAdmissionPolicy), old.(*admissionregistration.ValidatingAdmissionPolicy))
}
// PrepareForUpdate differs from the main strategy where setting the spec is not
// allowed, but setting status is OK.
func (s *validatingAdmissionPolicyStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIC := obj.(*admissionregistration.ValidatingAdmissionPolicy)
oldIC := old.(*admissionregistration.ValidatingAdmissionPolicy)
// Prevent any update on the Spec object from Status Strategy
newIC.Spec = oldIC.Spec
metav1.ResetObjectMetaForStatus(&newIC.ObjectMeta, &oldIC.ObjectMeta)
// No change in the generation.
}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (s *validatingAdmissionPolicyStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"admissionregistration.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata"),
),
}
}
// NewStatusStrategy creates a strategy for operating the status object.
func NewStatusStrategy(policyStrategy *validatingAdmissionPolicyStrategy) *validatingAdmissionPolicyStatusStrategy {
return &validatingAdmissionPolicyStatusStrategy{validatingAdmissionPolicyStrategy: policyStrategy}
}