Custom match criteria (#116350)

* Add custom match conditions for CEL admission

This PR is based off of, and dependent on the following PR:

https://github.com/kubernetes/kubernetes/pull/116261

Signed-off-by: Max Smythe <smythe@google.com>

* run `make update`

Signed-off-by: Max Smythe <smythe@google.com>

* Fix unit tests

Signed-off-by: Max Smythe <smythe@google.com>

* Fix unit tests

Signed-off-by: Max Smythe <smythe@google.com>

* Update compatibility test data

Signed-off-by: Max Smythe <smythe@google.com>

* Revert "Update compatibility test data"

This reverts commit 312ba7f9e74e0ec4a7ac1f07bf575479c608af28.

* Allow params during validation; make match conditions optional

Signed-off-by: Max Smythe <smythe@google.com>

* Add conditional ignoring of matcher CEL expression validation on update

Signed-off-by: Max Smythe <smythe@google.com>

* Run codegen

Signed-off-by: Max Smythe <smythe@google.com>

* Add more validation tests

Signed-off-by: Max Smythe <smythe@google.com>

* Short-circuit CEL matcher when no matchers specified

Signed-off-by: Max Smythe <smythe@google.com>

* Run codegen

Signed-off-by: Max Smythe <smythe@google.com>

* Address review comments

Signed-off-by: Max Smythe <smythe@google.com>

---------

Signed-off-by: Max Smythe <smythe@google.com>
This commit is contained in:
Max Smythe 2023-03-15 17:23:15 -07:00 committed by GitHub
parent 6711a81f02
commit e5fd204c33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1173 additions and 120 deletions

View File

@ -428,6 +428,23 @@
], ],
"type": "object" "type": "object"
}, },
"io.k8s.api.admissionregistration.v1alpha1.MatchCondition": {
"properties": {
"expression": {
"description": "Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables:\n\n'object' - The object from the incoming request. The value is null for DELETE requests. 'oldObject' - The existing object. The value is null for CREATE requests. 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\nDocumentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/\n\nRequired.",
"type": "string"
},
"name": {
"description": "Name is an identifier for this match condition, used for strategic merging of MatchConditions, as well as providing an identifier for logging purposes. A good name should be descriptive of the associated expression. Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')\n\nRequired.",
"type": "string"
}
},
"required": [
"name",
"expression"
],
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.MatchResources": { "io.k8s.api.admissionregistration.v1alpha1.MatchResources": {
"description": "MatchResources decides whether to run the admission control policy on an object based on whether it meets the match criteria. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)", "description": "MatchResources decides whether to run the admission control policy on an object based on whether it meets the match criteria. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)",
"properties": { "properties": {
@ -725,6 +742,19 @@
"description": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.", "description": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.",
"type": "string" "type": "string"
}, },
"matchConditions": {
"description": "MatchConditions is a list of conditions that must be met for a request to be validated. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nIf a parameter object is provided, it can be accessed via the `params` handle in the same manner as validation expressions.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the policy is skipped",
"items": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.MatchCondition"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"name"
],
"x-kubernetes-list-type": "map",
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge"
},
"matchConstraints": { "matchConstraints": {
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.MatchResources", "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.MatchResources",
"description": "MatchConstraints specifies what resources this policy is designed to validate. The AdmissionPolicy cares about a request if it matches _all_ Constraints. However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding. Required." "description": "MatchConstraints specifies what resources this policy is designed to validate. The AdmissionPolicy cares about a request if it matches _all_ Constraints. However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding. Required."

View File

@ -41,6 +41,25 @@
], ],
"type": "object" "type": "object"
}, },
"io.k8s.api.admissionregistration.v1alpha1.MatchCondition": {
"properties": {
"expression": {
"default": "",
"description": "Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables:\n\n'object' - The object from the incoming request. The value is null for DELETE requests. 'oldObject' - The existing object. The value is null for CREATE requests. 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\nDocumentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/\n\nRequired.",
"type": "string"
},
"name": {
"default": "",
"description": "Name is an identifier for this match condition, used for strategic merging of MatchConditions, as well as providing an identifier for logging purposes. A good name should be descriptive of the associated expression. Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')\n\nRequired.",
"type": "string"
}
},
"required": [
"name",
"expression"
],
"type": "object"
},
"io.k8s.api.admissionregistration.v1alpha1.MatchResources": { "io.k8s.api.admissionregistration.v1alpha1.MatchResources": {
"description": "MatchResources decides whether to run the admission control policy on an object based on whether it meets the match criteria. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)", "description": "MatchResources decides whether to run the admission control policy on an object based on whether it meets the match criteria. The exclude rules take precedence over include rules (if a resource matches both, it is excluded)",
"properties": { "properties": {
@ -425,6 +444,24 @@
"description": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.", "description": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.",
"type": "string" "type": "string"
}, },
"matchConditions": {
"description": "MatchConditions is a list of conditions that must be met for a request to be validated. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nIf a parameter object is provided, it can be accessed via the `params` handle in the same manner as validation expressions.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the policy is skipped",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.MatchCondition"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-map-keys": [
"name"
],
"x-kubernetes-list-type": "map",
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge"
},
"matchConstraints": { "matchConstraints": {
"allOf": [ "allOf": [
{ {

View File

@ -206,6 +206,24 @@ type ValidatingAdmissionPolicySpec struct {
// +optional // +optional
Validations []Validation Validations []Validation
// MatchConditions is a list of conditions that must be met for a request to be validated.
// Match conditions filter requests that have already been matched by the rules,
// namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests.
// There are a maximum of 64 match conditions allowed.
//
// If a parameter object is provided, it can be accessed via the `params` handle in the same
// manner as validation expressions.
//
// The exact matching logic is (in order):
// 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.
// 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.
// 3. If any matchCondition evaluates to an error (but none are FALSE):
// - If failurePolicy=Fail, reject the request
// - If failurePolicy=Ignore, the policy is skipped
//
// +optional
MatchConditions []MatchCondition
// failurePolicy defines how to handle failures for the admission policy. Failures can // failurePolicy defines how to handle failures for the admission policy. Failures can
// occur from CEL expression parse errors, type check errors, runtime errors and invalid // occur from CEL expression parse errors, type check errors, runtime errors and invalid
// or mis-configured policy definitions or bindings. // or mis-configured policy definitions or bindings.

View File

@ -59,6 +59,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err return err
} }
if err := s.AddGeneratedConversionFunc((*v1alpha1.MatchCondition)(nil), (*admissionregistration.MatchCondition)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_MatchCondition_To_admissionregistration_MatchCondition(a.(*v1alpha1.MatchCondition), b.(*admissionregistration.MatchCondition), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*admissionregistration.MatchCondition)(nil), (*v1alpha1.MatchCondition)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_admissionregistration_MatchCondition_To_v1alpha1_MatchCondition(a.(*admissionregistration.MatchCondition), b.(*v1alpha1.MatchCondition), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.MatchResources)(nil), (*admissionregistration.MatchResources)(nil), func(a, b interface{}, scope conversion.Scope) error { 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) return Convert_v1alpha1_MatchResources_To_admissionregistration_MatchResources(a.(*v1alpha1.MatchResources), b.(*admissionregistration.MatchResources), scope)
}); err != nil { }); err != nil {
@ -236,6 +246,28 @@ func Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarni
return autoConvert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in, out, s) return autoConvert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarning(in, out, s)
} }
func autoConvert_v1alpha1_MatchCondition_To_admissionregistration_MatchCondition(in *v1alpha1.MatchCondition, out *admissionregistration.MatchCondition, s conversion.Scope) error {
out.Name = in.Name
out.Expression = in.Expression
return nil
}
// Convert_v1alpha1_MatchCondition_To_admissionregistration_MatchCondition is an autogenerated conversion function.
func Convert_v1alpha1_MatchCondition_To_admissionregistration_MatchCondition(in *v1alpha1.MatchCondition, out *admissionregistration.MatchCondition, s conversion.Scope) error {
return autoConvert_v1alpha1_MatchCondition_To_admissionregistration_MatchCondition(in, out, s)
}
func autoConvert_admissionregistration_MatchCondition_To_v1alpha1_MatchCondition(in *admissionregistration.MatchCondition, out *v1alpha1.MatchCondition, s conversion.Scope) error {
out.Name = in.Name
out.Expression = in.Expression
return nil
}
// Convert_admissionregistration_MatchCondition_To_v1alpha1_MatchCondition is an autogenerated conversion function.
func Convert_admissionregistration_MatchCondition_To_v1alpha1_MatchCondition(in *admissionregistration.MatchCondition, out *v1alpha1.MatchCondition, s conversion.Scope) error {
return autoConvert_admissionregistration_MatchCondition_To_v1alpha1_MatchCondition(in, out, s)
}
func autoConvert_v1alpha1_MatchResources_To_admissionregistration_MatchResources(in *v1alpha1.MatchResources, out *admissionregistration.MatchResources, s conversion.Scope) error { 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.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector))
out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector)) out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector))
@ -592,6 +624,7 @@ func autoConvert_v1alpha1_ValidatingAdmissionPolicySpec_To_admissionregistration
out.Validations = *(*[]admissionregistration.Validation)(unsafe.Pointer(&in.Validations)) out.Validations = *(*[]admissionregistration.Validation)(unsafe.Pointer(&in.Validations))
out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy))
out.AuditAnnotations = *(*[]admissionregistration.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations)) out.AuditAnnotations = *(*[]admissionregistration.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations))
out.MatchConditions = *(*[]admissionregistration.MatchCondition)(unsafe.Pointer(&in.MatchConditions))
return nil return nil
} }
@ -612,6 +645,7 @@ func autoConvert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1
out.MatchConstraints = nil out.MatchConstraints = nil
} }
out.Validations = *(*[]v1alpha1.Validation)(unsafe.Pointer(&in.Validations)) out.Validations = *(*[]v1alpha1.Validation)(unsafe.Pointer(&in.Validations))
out.MatchConditions = *(*[]v1alpha1.MatchCondition)(unsafe.Pointer(&in.MatchConditions))
out.FailurePolicy = (*v1alpha1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.FailurePolicy = (*v1alpha1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy))
out.AuditAnnotations = *(*[]v1alpha1.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations)) out.AuditAnnotations = *(*[]v1alpha1.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations))
return nil return nil

View File

@ -213,6 +213,7 @@ func validateAdmissionReviewVersions(versions []string, requireRecognizedAdmissi
func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList {
return validateValidatingWebhookConfiguration(e, validationOptions{ return validateValidatingWebhookConfiguration(e, validationOptions{
ignoreMatchConditions: false, ignoreMatchConditions: false,
allowParamsInMatchConditions: false,
requireNoSideEffects: true, requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true, requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: true, requireUniqueWebhookNames: true,
@ -241,6 +242,7 @@ func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW
func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration) field.ErrorList { func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration) field.ErrorList {
return validateMutatingWebhookConfiguration(e, validationOptions{ return validateMutatingWebhookConfiguration(e, validationOptions{
ignoreMatchConditions: false, ignoreMatchConditions: false,
allowParamsInMatchConditions: false,
requireNoSideEffects: true, requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true, requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: true, requireUniqueWebhookNames: true,
@ -250,6 +252,7 @@ func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebho
type validationOptions struct { type validationOptions struct {
ignoreMatchConditions bool ignoreMatchConditions bool
allowParamsInMatchConditions bool
requireNoSideEffects bool requireNoSideEffects bool
requireRecognizedAdmissionReviewVersion bool requireRecognizedAdmissionReviewVersion bool
requireUniqueWebhookNames bool requireUniqueWebhookNames bool
@ -324,7 +327,7 @@ func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, op
} }
if !opts.ignoreMatchConditions { if !opts.ignoreMatchConditions {
allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, fldPath.Child("matchConditions"))...) allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, opts, fldPath.Child("matchConditions"))...)
} }
return allErrors return allErrors
@ -382,7 +385,7 @@ func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, opts v
} }
if !opts.ignoreMatchConditions { if !opts.ignoreMatchConditions {
allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, fldPath.Child("matchConditions"))...) allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, opts, fldPath.Child("matchConditions"))...)
} }
return allErrors return allErrors
@ -520,6 +523,17 @@ func ignoreValidatingWebhookMatchConditions(new, old []admissionregistration.Val
return true return true
} }
// ignoreValidatingAdmissionPolicyMatchConditions returns true if there have been no updates that could invalidate previously-valid match conditions
func ignoreValidatingAdmissionPolicyMatchConditions(new, old *admissionregistration.ValidatingAdmissionPolicy) bool {
if !reflect.DeepEqual(new.Spec.ParamKind, old.Spec.ParamKind) {
return false
}
if !reflect.DeepEqual(new.Spec.MatchConditions, old.Spec.MatchConditions) {
return false
}
return true
}
// mutatingHasUniqueWebhookNames returns true if all webhooks have unique names // mutatingHasUniqueWebhookNames returns true if all webhooks have unique names
func mutatingHasUniqueWebhookNames(webhooks []admissionregistration.MutatingWebhook) bool { func mutatingHasUniqueWebhookNames(webhooks []admissionregistration.MutatingWebhook) bool {
names := sets.NewString() names := sets.NewString()
@ -610,6 +624,7 @@ func mutatingWebhookHasInvalidLabelValueInSelector(webhooks []admissionregistrat
func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList {
return validateValidatingWebhookConfiguration(newC, validationOptions{ return validateValidatingWebhookConfiguration(newC, validationOptions{
ignoreMatchConditions: ignoreValidatingWebhookMatchConditions(newC.Webhooks, oldC.Webhooks), ignoreMatchConditions: ignoreValidatingWebhookMatchConditions(newC.Webhooks, oldC.Webhooks),
allowParamsInMatchConditions: false,
requireNoSideEffects: validatingHasNoSideEffects(oldC.Webhooks), requireNoSideEffects: validatingHasNoSideEffects(oldC.Webhooks),
requireRecognizedAdmissionReviewVersion: validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), requireRecognizedAdmissionReviewVersion: validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks),
requireUniqueWebhookNames: validatingHasUniqueWebhookNames(oldC.Webhooks), requireUniqueWebhookNames: validatingHasUniqueWebhookNames(oldC.Webhooks),
@ -621,6 +636,7 @@ func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistrat
func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration) field.ErrorList { func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration) field.ErrorList {
return validateMutatingWebhookConfiguration(newC, validationOptions{ return validateMutatingWebhookConfiguration(newC, validationOptions{
ignoreMatchConditions: ignoreMutatingWebhookMatchConditions(newC.Webhooks, oldC.Webhooks), ignoreMatchConditions: ignoreMutatingWebhookMatchConditions(newC.Webhooks, oldC.Webhooks),
allowParamsInMatchConditions: false,
requireNoSideEffects: mutatingHasNoSideEffects(oldC.Webhooks), requireNoSideEffects: mutatingHasNoSideEffects(oldC.Webhooks),
requireRecognizedAdmissionReviewVersion: mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), requireRecognizedAdmissionReviewVersion: mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks),
requireUniqueWebhookNames: mutatingHasUniqueWebhookNames(oldC.Webhooks), requireUniqueWebhookNames: mutatingHasUniqueWebhookNames(oldC.Webhooks),
@ -638,16 +654,16 @@ const (
// ValidateValidatingAdmissionPolicy validates a ValidatingAdmissionPolicy before creation. // ValidateValidatingAdmissionPolicy validates a ValidatingAdmissionPolicy before creation.
func ValidateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList { func ValidateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList {
return validateValidatingAdmissionPolicy(p) return validateValidatingAdmissionPolicy(p, validationOptions{ignoreMatchConditions: false})
} }
func validateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList { func validateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy, opts validationOptions) field.ErrorList {
allErrors := genericvalidation.ValidateObjectMeta(&p.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) allErrors := genericvalidation.ValidateObjectMeta(&p.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
allErrors = append(allErrors, validateValidatingAdmissionPolicySpec(p.ObjectMeta, &p.Spec, field.NewPath("spec"))...) allErrors = append(allErrors, validateValidatingAdmissionPolicySpec(p.ObjectMeta, &p.Spec, opts, field.NewPath("spec"))...)
return allErrors return allErrors
} }
func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissionregistration.ValidatingAdmissionPolicySpec, fldPath *field.Path) field.ErrorList { func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissionregistration.ValidatingAdmissionPolicySpec, opts validationOptions, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList var allErrors field.ErrorList
if spec.FailurePolicy == nil { if spec.FailurePolicy == nil {
allErrors = append(allErrors, field.Required(fldPath.Child("failurePolicy"), "")) allErrors = append(allErrors, field.Required(fldPath.Child("failurePolicy"), ""))
@ -655,6 +671,7 @@ func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissi
allErrors = append(allErrors, field.NotSupported(fldPath.Child("failurePolicy"), *spec.FailurePolicy, supportedFailurePolicies.List())) allErrors = append(allErrors, field.NotSupported(fldPath.Child("failurePolicy"), *spec.FailurePolicy, supportedFailurePolicies.List()))
} }
if spec.ParamKind != nil { if spec.ParamKind != nil {
opts.allowParamsInMatchConditions = true
allErrors = append(allErrors, validateParamKind(*spec.ParamKind, fldPath.Child("paramKind"))...) allErrors = append(allErrors, validateParamKind(*spec.ParamKind, fldPath.Child("paramKind"))...)
} }
if spec.MatchConstraints == nil { if spec.MatchConstraints == nil {
@ -666,6 +683,9 @@ func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissi
allErrors = append(allErrors, field.Required(fldPath.Child("matchConstraints", "resourceRules"), "")) allErrors = append(allErrors, field.Required(fldPath.Child("matchConstraints", "resourceRules"), ""))
} }
} }
if !opts.ignoreMatchConditions {
allErrors = append(allErrors, validateMatchConditions(spec.MatchConditions, opts, fldPath.Child("matchConditions"))...)
}
if len(spec.Validations) == 0 && len(spec.AuditAnnotations) == 0 { if len(spec.Validations) == 0 && len(spec.AuditAnnotations) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("validations"), "validations or auditAnnotations must contain at least one item")) allErrors = append(allErrors, field.Required(fldPath.Child("validations"), "validations or auditAnnotations must contain at least one item"))
allErrors = append(allErrors, field.Required(fldPath.Child("auditAnnotations"), "validations or auditAnnotations must contain at least one item")) allErrors = append(allErrors, field.Required(fldPath.Child("auditAnnotations"), "validations or auditAnnotations must contain at least one item"))
@ -822,14 +842,14 @@ func validateNamedRuleWithOperations(n *admissionregistration.NamedRuleWithOpera
return allErrors return allErrors
} }
func validateMatchConditions(m []admissionregistration.MatchCondition, fldPath *field.Path) field.ErrorList { func validateMatchConditions(m []admissionregistration.MatchCondition, opts validationOptions, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList var allErrors field.ErrorList
conditionNames := sets.NewString() conditionNames := sets.NewString()
if len(m) > 64 { if len(m) > 64 {
allErrors = append(allErrors, field.TooMany(fldPath, len(m), 64)) allErrors = append(allErrors, field.TooMany(fldPath, len(m), 64))
} }
for i, matchCondition := range m { for i, matchCondition := range m {
allErrors = append(allErrors, validateMatchCondition(&matchCondition, fldPath.Index(i))...) allErrors = append(allErrors, validateMatchCondition(&matchCondition, opts, fldPath.Index(i))...)
if len(matchCondition.Name) > 0 { if len(matchCondition.Name) > 0 {
if conditionNames.Has(matchCondition.Name) { if conditionNames.Has(matchCondition.Name) {
allErrors = append(allErrors, field.Duplicate(fldPath.Index(i).Child("name"), matchCondition.Name)) allErrors = append(allErrors, field.Duplicate(fldPath.Index(i).Child("name"), matchCondition.Name))
@ -841,14 +861,14 @@ func validateMatchConditions(m []admissionregistration.MatchCondition, fldPath *
return allErrors return allErrors
} }
func validateMatchCondition(v *admissionregistration.MatchCondition, fldPath *field.Path) field.ErrorList { func validateMatchCondition(v *admissionregistration.MatchCondition, opts validationOptions, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList var allErrors field.ErrorList
trimmedExpression := strings.TrimSpace(v.Expression) trimmedExpression := strings.TrimSpace(v.Expression)
if len(trimmedExpression) == 0 { if len(trimmedExpression) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("expression"), "")) allErrors = append(allErrors, field.Required(fldPath.Child("expression"), ""))
} else { } else {
allErrors = append(allErrors, validateCELExpression(trimmedExpression, plugincel.OptionalVariableDeclarations{ allErrors = append(allErrors, validateCELExpression(trimmedExpression, plugincel.OptionalVariableDeclarations{
HasParams: false, HasParams: opts.allowParamsInMatchConditions,
HasAuthorizer: true, HasAuthorizer: true,
}, fldPath.Child("expression"))...) }, fldPath.Child("expression"))...)
} }
@ -995,7 +1015,7 @@ func validateParamRef(pr *admissionregistration.ParamRef, fldPath *field.Path) f
// ValidateValidatingAdmissionPolicyUpdate validates update of validating admission policy // ValidateValidatingAdmissionPolicyUpdate validates update of validating admission policy
func ValidateValidatingAdmissionPolicyUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList { func ValidateValidatingAdmissionPolicyUpdate(newC, oldC *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList {
return validateValidatingAdmissionPolicy(newC) return validateValidatingAdmissionPolicy(newC, validationOptions{ignoreMatchConditions: ignoreValidatingAdmissionPolicyMatchConditions(newC, oldC)})
} }
// ValidateValidatingAdmissionPolicyStatusUpdate validates update of status of validating admission policy // ValidateValidatingAdmissionPolicyStatusUpdate validates update of status of validating admission policy

View File

@ -3150,6 +3150,131 @@ func TestValidateValidatingAdmissionPolicy(t *testing.T) {
}, },
expectedError: `spec.auditAnnotations[0].valueExpression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: <input>:1:19: Syntax error: missing ']' at '<EOF>`, expectedError: `spec.auditAnnotations[0].valueExpression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: <input>:1:19: Syntax error: missing ']' at '<EOF>`,
}, },
{
name: "single match condition must have a name",
config: &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
MatchConditions: []admissionregistration.MatchCondition{
{
Expression: "true",
},
},
Validations: []admissionregistration.Validation{
{
Expression: "object.x < 100",
},
},
},
},
expectedError: `spec.matchConditions[0].name: Required value`,
},
{
name: "match condition with parameters allowed",
config: &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
ParamKind: &admissionregistration.ParamKind{
Kind: "Foo",
APIVersion: "foobar/v1alpha1",
},
MatchConstraints: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"*"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
},
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.FailurePolicyType("Fail")
return &r
}(),
MatchConditions: []admissionregistration.MatchCondition{
{
Name: "hasParams",
Expression: `params.foo == "okay"`,
},
},
Validations: []admissionregistration.Validation{
{
Expression: "object.x < 100",
},
},
},
},
expectedError: "",
},
{
name: "match condition with parameters not allowed if no param kind",
config: &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
MatchConstraints: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"*"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
},
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.FailurePolicyType("Fail")
return &r
}(),
MatchConditions: []admissionregistration.MatchCondition{
{
Name: "hasParams",
Expression: `params.foo == "okay"`,
},
},
Validations: []admissionregistration.Validation{
{
Expression: "object.x < 100",
},
},
},
},
expectedError: `undeclared reference to 'params'`,
},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
@ -3293,6 +3418,202 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicySpec{}, Spec: admissionregistration.ValidatingAdmissionPolicySpec{},
}, },
}, },
{
name: "match conditions re-checked if paramKind changes",
oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
ParamKind: &admissionregistration.ParamKind{
Kind: "Foo",
APIVersion: "foobar/v1alpha1",
},
MatchConstraints: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"*"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
},
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.FailurePolicyType("Fail")
return &r
}(),
MatchConditions: []admissionregistration.MatchCondition{
{
Name: "hasParams",
Expression: `params.foo == "okay"`,
},
},
Validations: []admissionregistration.Validation{
{
Expression: "object.x < 100",
},
},
},
},
config: &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
MatchConstraints: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"*"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
},
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.FailurePolicyType("Fail")
return &r
}(),
MatchConditions: []admissionregistration.MatchCondition{
{
Name: "hasParams",
Expression: `params.foo == "okay"`,
},
},
Validations: []admissionregistration.Validation{
{
Expression: "object.x < 100",
},
},
},
},
expectedError: `undeclared reference to 'params'`,
},
{
name: "match conditions not re-checked if no change to paramKind or matchConditions",
oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
MatchConstraints: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"*"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
},
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.FailurePolicyType("Fail")
return &r
}(),
MatchConditions: []admissionregistration.MatchCondition{
{
Name: "hasParams",
Expression: `params.foo == "okay"`,
},
},
Validations: []admissionregistration.Validation{
{
Expression: "object.x < 100",
},
},
},
},
config: &admissionregistration.ValidatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicySpec{
MatchConstraints: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistration.RuleWithOperations{
Operations: []admissionregistration.OperationType{"*"},
Rule: admissionregistration.Rule{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
Resources: []string{"a"},
},
},
},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
MatchPolicy: func() *admissionregistration.MatchPolicyType {
r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
},
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.FailurePolicyType("Ignore")
return &r
}(),
MatchConditions: []admissionregistration.MatchCondition{
{
Name: "hasParams",
Expression: `params.foo == "okay"`,
},
},
Validations: []admissionregistration.Validation{
{
Expression: "object.x < 50",
},
},
},
},
expectedError: "",
},
// TODO: CustomAuditAnnotations: string valueExpression with {oldObject} is allowed // TODO: CustomAuditAnnotations: string valueExpression with {oldObject} is allowed
} }
for _, test := range tests { for _, test := range tests {

View File

@ -580,6 +580,11 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopyInto(out *ValidatingAdmissionPo
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.MatchConditions != nil {
in, out := &in.MatchConditions, &out.MatchConditions
*out = make([]MatchCondition, len(*in))
copy(*out, *in)
}
if in.FailurePolicy != nil { if in.FailurePolicy != nil {
in, out := &in.FailurePolicy, &out.FailurePolicy in, out := &in.FailurePolicy, &out.FailurePolicy
*out = new(FailurePolicyType) *out = new(FailurePolicyType)

View File

@ -48,6 +48,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/admissionregistration/v1.WebhookClientConfig": schema_k8sio_api_admissionregistration_v1_WebhookClientConfig(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.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.ExpressionWarning": schema_k8sio_api_admissionregistration_v1alpha1_ExpressionWarning(ref),
"k8s.io/api/admissionregistration/v1alpha1.MatchCondition": schema_k8sio_api_admissionregistration_v1alpha1_MatchCondition(ref),
"k8s.io/api/admissionregistration/v1alpha1.MatchResources": schema_k8sio_api_admissionregistration_v1alpha1_MatchResources(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.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.ParamKind": schema_k8sio_api_admissionregistration_v1alpha1_ParamKind(ref),
@ -2008,6 +2009,35 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ExpressionWarning(ref commo
} }
} }
func schema_k8sio_api_admissionregistration_v1alpha1_MatchCondition(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Description: "Name is an identifier for this match condition, used for strategic merging of MatchConditions, as well as providing an identifier for logging purposes. A good name should be descriptive of the associated expression. Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')\n\nRequired.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"expression": {
SchemaProps: spec.SchemaProps{
Description: "Expression represents the expression which will be evaluated by CEL. Must evaluate to bool. CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables:\n\n'object' - The object from the incoming request. The value is null for DELETE requests. 'oldObject' - The existing object. The value is null for CREATE requests. 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest). 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\nDocumentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/\n\nRequired.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"name", "expression"},
},
},
}
}
func schema_k8sio_api_admissionregistration_v1alpha1_MatchResources(ref common.ReferenceCallback) common.OpenAPIDefinition { func schema_k8sio_api_admissionregistration_v1alpha1_MatchResources(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{ return common.OpenAPIDefinition{
Schema: spec.Schema{ Schema: spec.Schema{
@ -2621,11 +2651,35 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicySp
}, },
}, },
}, },
"matchConditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"name",
},
"x-kubernetes-list-type": "map",
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge",
},
},
SchemaProps: spec.SchemaProps{
Description: "MatchConditions is a list of conditions that must be met for a request to be validated. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nIf a parameter object is provided, it can be accessed via the `params` handle in the same manner as validation expressions.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the policy is skipped",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.MatchCondition"),
},
},
},
},
},
}, },
}, },
}, },
Dependencies: []string{ Dependencies: []string{
"k8s.io/api/admissionregistration/v1alpha1.AuditAnnotation", "k8s.io/api/admissionregistration/v1alpha1.MatchResources", "k8s.io/api/admissionregistration/v1alpha1.ParamKind", "k8s.io/api/admissionregistration/v1alpha1.Validation"}, "k8s.io/api/admissionregistration/v1alpha1.AuditAnnotation", "k8s.io/api/admissionregistration/v1alpha1.MatchCondition", "k8s.io/api/admissionregistration/v1alpha1.MatchResources", "k8s.io/api/admissionregistration/v1alpha1.ParamKind", "k8s.io/api/admissionregistration/v1alpha1.Validation"},
} }
} }

View File

@ -101,10 +101,38 @@ func (m *ExpressionWarning) XXX_DiscardUnknown() {
var xxx_messageInfo_ExpressionWarning proto.InternalMessageInfo var xxx_messageInfo_ExpressionWarning proto.InternalMessageInfo
func (m *MatchCondition) Reset() { *m = MatchCondition{} }
func (*MatchCondition) ProtoMessage() {}
func (*MatchCondition) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{2}
}
func (m *MatchCondition) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *MatchCondition) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
func (m *MatchCondition) XXX_Merge(src proto.Message) {
xxx_messageInfo_MatchCondition.Merge(m, src)
}
func (m *MatchCondition) XXX_Size() int {
return m.Size()
}
func (m *MatchCondition) XXX_DiscardUnknown() {
xxx_messageInfo_MatchCondition.DiscardUnknown(m)
}
var xxx_messageInfo_MatchCondition proto.InternalMessageInfo
func (m *MatchResources) Reset() { *m = MatchResources{} } func (m *MatchResources) Reset() { *m = MatchResources{} }
func (*MatchResources) ProtoMessage() {} func (*MatchResources) ProtoMessage() {}
func (*MatchResources) Descriptor() ([]byte, []int) { func (*MatchResources) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{2} return fileDescriptor_c3be8d256e3ae3cf, []int{3}
} }
func (m *MatchResources) XXX_Unmarshal(b []byte) error { func (m *MatchResources) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -132,7 +160,7 @@ var xxx_messageInfo_MatchResources proto.InternalMessageInfo
func (m *NamedRuleWithOperations) Reset() { *m = NamedRuleWithOperations{} } func (m *NamedRuleWithOperations) Reset() { *m = NamedRuleWithOperations{} }
func (*NamedRuleWithOperations) ProtoMessage() {} func (*NamedRuleWithOperations) ProtoMessage() {}
func (*NamedRuleWithOperations) Descriptor() ([]byte, []int) { func (*NamedRuleWithOperations) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{3} return fileDescriptor_c3be8d256e3ae3cf, []int{4}
} }
func (m *NamedRuleWithOperations) XXX_Unmarshal(b []byte) error { func (m *NamedRuleWithOperations) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -160,7 +188,7 @@ var xxx_messageInfo_NamedRuleWithOperations proto.InternalMessageInfo
func (m *ParamKind) Reset() { *m = ParamKind{} } func (m *ParamKind) Reset() { *m = ParamKind{} }
func (*ParamKind) ProtoMessage() {} func (*ParamKind) ProtoMessage() {}
func (*ParamKind) Descriptor() ([]byte, []int) { func (*ParamKind) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{4} return fileDescriptor_c3be8d256e3ae3cf, []int{5}
} }
func (m *ParamKind) XXX_Unmarshal(b []byte) error { func (m *ParamKind) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -188,7 +216,7 @@ var xxx_messageInfo_ParamKind proto.InternalMessageInfo
func (m *ParamRef) Reset() { *m = ParamRef{} } func (m *ParamRef) Reset() { *m = ParamRef{} }
func (*ParamRef) ProtoMessage() {} func (*ParamRef) ProtoMessage() {}
func (*ParamRef) Descriptor() ([]byte, []int) { func (*ParamRef) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{5} return fileDescriptor_c3be8d256e3ae3cf, []int{6}
} }
func (m *ParamRef) XXX_Unmarshal(b []byte) error { func (m *ParamRef) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -216,7 +244,7 @@ var xxx_messageInfo_ParamRef proto.InternalMessageInfo
func (m *TypeChecking) Reset() { *m = TypeChecking{} } func (m *TypeChecking) Reset() { *m = TypeChecking{} }
func (*TypeChecking) ProtoMessage() {} func (*TypeChecking) ProtoMessage() {}
func (*TypeChecking) Descriptor() ([]byte, []int) { func (*TypeChecking) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{6} return fileDescriptor_c3be8d256e3ae3cf, []int{7}
} }
func (m *TypeChecking) XXX_Unmarshal(b []byte) error { func (m *TypeChecking) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -244,7 +272,7 @@ var xxx_messageInfo_TypeChecking proto.InternalMessageInfo
func (m *ValidatingAdmissionPolicy) Reset() { *m = ValidatingAdmissionPolicy{} } func (m *ValidatingAdmissionPolicy) Reset() { *m = ValidatingAdmissionPolicy{} }
func (*ValidatingAdmissionPolicy) ProtoMessage() {} func (*ValidatingAdmissionPolicy) ProtoMessage() {}
func (*ValidatingAdmissionPolicy) Descriptor() ([]byte, []int) { func (*ValidatingAdmissionPolicy) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{7} return fileDescriptor_c3be8d256e3ae3cf, []int{8}
} }
func (m *ValidatingAdmissionPolicy) XXX_Unmarshal(b []byte) error { func (m *ValidatingAdmissionPolicy) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -272,7 +300,7 @@ var xxx_messageInfo_ValidatingAdmissionPolicy proto.InternalMessageInfo
func (m *ValidatingAdmissionPolicyBinding) Reset() { *m = ValidatingAdmissionPolicyBinding{} } func (m *ValidatingAdmissionPolicyBinding) Reset() { *m = ValidatingAdmissionPolicyBinding{} }
func (*ValidatingAdmissionPolicyBinding) ProtoMessage() {} func (*ValidatingAdmissionPolicyBinding) ProtoMessage() {}
func (*ValidatingAdmissionPolicyBinding) Descriptor() ([]byte, []int) { func (*ValidatingAdmissionPolicyBinding) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{8} return fileDescriptor_c3be8d256e3ae3cf, []int{9}
} }
func (m *ValidatingAdmissionPolicyBinding) XXX_Unmarshal(b []byte) error { func (m *ValidatingAdmissionPolicyBinding) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -300,7 +328,7 @@ var xxx_messageInfo_ValidatingAdmissionPolicyBinding proto.InternalMessageInfo
func (m *ValidatingAdmissionPolicyBindingList) Reset() { *m = ValidatingAdmissionPolicyBindingList{} } func (m *ValidatingAdmissionPolicyBindingList) Reset() { *m = ValidatingAdmissionPolicyBindingList{} }
func (*ValidatingAdmissionPolicyBindingList) ProtoMessage() {} func (*ValidatingAdmissionPolicyBindingList) ProtoMessage() {}
func (*ValidatingAdmissionPolicyBindingList) Descriptor() ([]byte, []int) { func (*ValidatingAdmissionPolicyBindingList) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{9} return fileDescriptor_c3be8d256e3ae3cf, []int{10}
} }
func (m *ValidatingAdmissionPolicyBindingList) XXX_Unmarshal(b []byte) error { func (m *ValidatingAdmissionPolicyBindingList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -328,7 +356,7 @@ var xxx_messageInfo_ValidatingAdmissionPolicyBindingList proto.InternalMessageIn
func (m *ValidatingAdmissionPolicyBindingSpec) Reset() { *m = ValidatingAdmissionPolicyBindingSpec{} } func (m *ValidatingAdmissionPolicyBindingSpec) Reset() { *m = ValidatingAdmissionPolicyBindingSpec{} }
func (*ValidatingAdmissionPolicyBindingSpec) ProtoMessage() {} func (*ValidatingAdmissionPolicyBindingSpec) ProtoMessage() {}
func (*ValidatingAdmissionPolicyBindingSpec) Descriptor() ([]byte, []int) { func (*ValidatingAdmissionPolicyBindingSpec) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{10} return fileDescriptor_c3be8d256e3ae3cf, []int{11}
} }
func (m *ValidatingAdmissionPolicyBindingSpec) XXX_Unmarshal(b []byte) error { func (m *ValidatingAdmissionPolicyBindingSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -356,7 +384,7 @@ var xxx_messageInfo_ValidatingAdmissionPolicyBindingSpec proto.InternalMessageIn
func (m *ValidatingAdmissionPolicyList) Reset() { *m = ValidatingAdmissionPolicyList{} } func (m *ValidatingAdmissionPolicyList) Reset() { *m = ValidatingAdmissionPolicyList{} }
func (*ValidatingAdmissionPolicyList) ProtoMessage() {} func (*ValidatingAdmissionPolicyList) ProtoMessage() {}
func (*ValidatingAdmissionPolicyList) Descriptor() ([]byte, []int) { func (*ValidatingAdmissionPolicyList) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{11} return fileDescriptor_c3be8d256e3ae3cf, []int{12}
} }
func (m *ValidatingAdmissionPolicyList) XXX_Unmarshal(b []byte) error { func (m *ValidatingAdmissionPolicyList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -384,7 +412,7 @@ var xxx_messageInfo_ValidatingAdmissionPolicyList proto.InternalMessageInfo
func (m *ValidatingAdmissionPolicySpec) Reset() { *m = ValidatingAdmissionPolicySpec{} } func (m *ValidatingAdmissionPolicySpec) Reset() { *m = ValidatingAdmissionPolicySpec{} }
func (*ValidatingAdmissionPolicySpec) ProtoMessage() {} func (*ValidatingAdmissionPolicySpec) ProtoMessage() {}
func (*ValidatingAdmissionPolicySpec) Descriptor() ([]byte, []int) { func (*ValidatingAdmissionPolicySpec) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{12} return fileDescriptor_c3be8d256e3ae3cf, []int{13}
} }
func (m *ValidatingAdmissionPolicySpec) XXX_Unmarshal(b []byte) error { func (m *ValidatingAdmissionPolicySpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -412,7 +440,7 @@ var xxx_messageInfo_ValidatingAdmissionPolicySpec proto.InternalMessageInfo
func (m *ValidatingAdmissionPolicyStatus) Reset() { *m = ValidatingAdmissionPolicyStatus{} } func (m *ValidatingAdmissionPolicyStatus) Reset() { *m = ValidatingAdmissionPolicyStatus{} }
func (*ValidatingAdmissionPolicyStatus) ProtoMessage() {} func (*ValidatingAdmissionPolicyStatus) ProtoMessage() {}
func (*ValidatingAdmissionPolicyStatus) Descriptor() ([]byte, []int) { func (*ValidatingAdmissionPolicyStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{13} return fileDescriptor_c3be8d256e3ae3cf, []int{14}
} }
func (m *ValidatingAdmissionPolicyStatus) XXX_Unmarshal(b []byte) error { func (m *ValidatingAdmissionPolicyStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -440,7 +468,7 @@ var xxx_messageInfo_ValidatingAdmissionPolicyStatus proto.InternalMessageInfo
func (m *Validation) Reset() { *m = Validation{} } func (m *Validation) Reset() { *m = Validation{} }
func (*Validation) ProtoMessage() {} func (*Validation) ProtoMessage() {}
func (*Validation) Descriptor() ([]byte, []int) { func (*Validation) Descriptor() ([]byte, []int) {
return fileDescriptor_c3be8d256e3ae3cf, []int{14} return fileDescriptor_c3be8d256e3ae3cf, []int{15}
} }
func (m *Validation) XXX_Unmarshal(b []byte) error { func (m *Validation) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
@ -468,6 +496,7 @@ var xxx_messageInfo_Validation proto.InternalMessageInfo
func init() { func init() {
proto.RegisterType((*AuditAnnotation)(nil), "k8s.io.api.admissionregistration.v1alpha1.AuditAnnotation") proto.RegisterType((*AuditAnnotation)(nil), "k8s.io.api.admissionregistration.v1alpha1.AuditAnnotation")
proto.RegisterType((*ExpressionWarning)(nil), "k8s.io.api.admissionregistration.v1alpha1.ExpressionWarning") proto.RegisterType((*ExpressionWarning)(nil), "k8s.io.api.admissionregistration.v1alpha1.ExpressionWarning")
proto.RegisterType((*MatchCondition)(nil), "k8s.io.api.admissionregistration.v1alpha1.MatchCondition")
proto.RegisterType((*MatchResources)(nil), "k8s.io.api.admissionregistration.v1alpha1.MatchResources") proto.RegisterType((*MatchResources)(nil), "k8s.io.api.admissionregistration.v1alpha1.MatchResources")
proto.RegisterType((*NamedRuleWithOperations)(nil), "k8s.io.api.admissionregistration.v1alpha1.NamedRuleWithOperations") proto.RegisterType((*NamedRuleWithOperations)(nil), "k8s.io.api.admissionregistration.v1alpha1.NamedRuleWithOperations")
proto.RegisterType((*ParamKind)(nil), "k8s.io.api.admissionregistration.v1alpha1.ParamKind") proto.RegisterType((*ParamKind)(nil), "k8s.io.api.admissionregistration.v1alpha1.ParamKind")
@ -488,93 +517,95 @@ func init() {
} }
var fileDescriptor_c3be8d256e3ae3cf = []byte{ var fileDescriptor_c3be8d256e3ae3cf = []byte{
// 1367 bytes of a gzipped FileDescriptorProto // 1407 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4b, 0x6f, 0x5b, 0xc5, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcb, 0x6f, 0x1b, 0x45,
0x17, 0xcf, 0x8d, 0xdd, 0x36, 0x39, 0xce, 0xcb, 0xf3, 0x6f, 0x55, 0x37, 0xfa, 0xd7, 0x8e, 0xae, 0x18, 0xcf, 0xc6, 0x4e, 0x9a, 0x8c, 0xf3, 0xb0, 0x87, 0x56, 0x75, 0x23, 0x6a, 0x47, 0xab, 0x0a,
0x2a, 0xd4, 0x48, 0x70, 0x4d, 0xd2, 0x42, 0x01, 0x21, 0xa1, 0xdc, 0xbe, 0xe8, 0x23, 0x4d, 0x34, 0x35, 0x12, 0xec, 0x92, 0xb4, 0x50, 0x40, 0x48, 0x28, 0xdb, 0x17, 0x7d, 0xa4, 0x89, 0xa6, 0x28,
0x45, 0x89, 0x84, 0xa8, 0xc4, 0xe4, 0xde, 0x89, 0x3d, 0xb5, 0xef, 0x83, 0x3b, 0xd7, 0xa1, 0x11, 0x91, 0x10, 0x95, 0x98, 0xec, 0x4e, 0xec, 0xa9, 0xbd, 0x0f, 0x76, 0xd6, 0xa1, 0x11, 0x48, 0x54,
0x0b, 0x2a, 0xb1, 0x81, 0x1d, 0x0b, 0x36, 0x7c, 0x19, 0x24, 0x76, 0x5d, 0x76, 0x59, 0x16, 0x58, 0xe2, 0x02, 0x37, 0x0e, 0x5c, 0xf8, 0x5f, 0xb8, 0x70, 0xeb, 0xb1, 0xc7, 0x72, 0xc0, 0x22, 0xe6,
0xd4, 0x6c, 0xf8, 0x04, 0x20, 0x65, 0x03, 0x9a, 0xb9, 0x73, 0x9f, 0x76, 0x88, 0x53, 0x02, 0x3b, 0xc2, 0x5f, 0x00, 0x52, 0x2e, 0xa0, 0x99, 0x9d, 0x7d, 0x3b, 0xc4, 0x2e, 0x81, 0x9b, 0xf7, 0x7b,
0xdf, 0xf3, 0xf8, 0xfd, 0xe6, 0x9c, 0x39, 0x73, 0xe6, 0x8c, 0x01, 0x77, 0xde, 0xe1, 0x06, 0xf3, 0xfc, 0x7e, 0xf3, 0x7d, 0xf3, 0x7d, 0x33, 0xdf, 0x18, 0xa0, 0xce, 0x3b, 0x4c, 0xa3, 0xae, 0xde,
0x9a, 0x9d, 0xde, 0x0e, 0x0d, 0x5c, 0x1a, 0x52, 0xde, 0xdc, 0xa3, 0xae, 0xed, 0x05, 0x4d, 0xa5, 0xe9, 0xed, 0x12, 0xdf, 0x21, 0x01, 0x61, 0xfa, 0x3e, 0x71, 0x2c, 0xd7, 0xd7, 0xa5, 0x02, 0x7b,
0x20, 0x3e, 0x6b, 0x12, 0xdb, 0x61, 0x9c, 0x33, 0xcf, 0x0d, 0x68, 0x8b, 0xf1, 0x30, 0x20, 0x21, 0x54, 0xc7, 0x96, 0x4d, 0x19, 0xa3, 0xae, 0xe3, 0x93, 0x16, 0x65, 0x81, 0x8f, 0x03, 0xea, 0x3a,
0xf3, 0xdc, 0xe6, 0xde, 0x0a, 0xe9, 0xfa, 0x6d, 0xb2, 0xd2, 0x6c, 0x51, 0x97, 0x06, 0x24, 0xa4, 0xfa, 0xfe, 0x2a, 0xee, 0x7a, 0x6d, 0xbc, 0xaa, 0xb7, 0x88, 0x43, 0x7c, 0x1c, 0x10, 0x4b, 0xf3,
0xb6, 0xe1, 0x07, 0x5e, 0xe8, 0xa1, 0xe5, 0xc8, 0xd5, 0x20, 0x3e, 0x33, 0x46, 0xba, 0x1a, 0xb1, 0x7c, 0x37, 0x70, 0xe1, 0x4a, 0xe8, 0xaa, 0x61, 0x8f, 0x6a, 0x43, 0x5d, 0xb5, 0xc8, 0x75, 0xe9,
0xeb, 0xe2, 0x1b, 0x2d, 0x16, 0xb6, 0x7b, 0x3b, 0x86, 0xe5, 0x39, 0xcd, 0x96, 0xd7, 0xf2, 0x9a, 0x8d, 0x16, 0x0d, 0xda, 0xbd, 0x5d, 0xcd, 0x74, 0x6d, 0xbd, 0xe5, 0xb6, 0x5c, 0x5d, 0x20, 0xec,
0x12, 0x61, 0xa7, 0xb7, 0x2b, 0xbf, 0xe4, 0x87, 0xfc, 0x15, 0x21, 0x2f, 0x5e, 0x19, 0x63, 0x51, 0xf6, 0xf6, 0xc4, 0x97, 0xf8, 0x10, 0xbf, 0x42, 0xe4, 0xa5, 0x2b, 0x23, 0x2c, 0x2a, 0xbf, 0x9c,
0xc5, 0xe5, 0x2c, 0x5e, 0x4d, 0x9d, 0x1c, 0x62, 0xb5, 0x99, 0x4b, 0x83, 0xfd, 0xa6, 0xdf, 0x69, 0xa5, 0xab, 0x89, 0x93, 0x8d, 0xcd, 0x36, 0x75, 0x88, 0x7f, 0xa0, 0x7b, 0x9d, 0x16, 0x17, 0x30,
0x09, 0x01, 0x6f, 0x3a, 0x34, 0x24, 0xa3, 0xbc, 0x9a, 0x87, 0x79, 0x05, 0x3d, 0x37, 0x64, 0x0e, 0xdd, 0x26, 0x01, 0x1e, 0xe6, 0xa5, 0x1f, 0xe7, 0xe5, 0xf7, 0x9c, 0x80, 0xda, 0xa4, 0xe0, 0xf0,
0x1d, 0x72, 0x78, 0xfb, 0x28, 0x07, 0x6e, 0xb5, 0xa9, 0x43, 0x8a, 0x7e, 0x3a, 0x87, 0xf9, 0xb5, 0xf6, 0x49, 0x0e, 0xcc, 0x6c, 0x13, 0x1b, 0xe7, 0xfd, 0x54, 0x06, 0x16, 0xd7, 0x7b, 0x16, 0x0d,
0x9e, 0xcd, 0xc2, 0x35, 0xd7, 0xf5, 0x42, 0x19, 0x04, 0xba, 0x08, 0xa5, 0x0e, 0xdd, 0xaf, 0x69, 0xd6, 0x1d, 0xc7, 0x0d, 0x44, 0x10, 0xf0, 0x22, 0x28, 0x75, 0xc8, 0x41, 0x5d, 0x59, 0x56, 0x2e,
0x4b, 0xda, 0xe5, 0x69, 0xb3, 0xf2, 0xac, 0xdf, 0x98, 0x18, 0xf4, 0x1b, 0xa5, 0x7b, 0x74, 0x1f, 0xcf, 0x1a, 0x95, 0x67, 0xfd, 0xe6, 0xc4, 0xa0, 0xdf, 0x2c, 0xdd, 0x23, 0x07, 0x88, 0xcb, 0xe1,
0x0b, 0x39, 0x5a, 0x83, 0xf9, 0x3d, 0xd2, 0xed, 0xd1, 0x9b, 0x4f, 0xfc, 0x80, 0xca, 0x14, 0xd4, 0x3a, 0x58, 0xdc, 0xc7, 0xdd, 0x1e, 0xb9, 0xf9, 0xc4, 0xf3, 0x89, 0x48, 0x41, 0x7d, 0x52, 0x98,
0x26, 0xa5, 0xe9, 0x79, 0x65, 0x3a, 0xbf, 0x95, 0x57, 0xe3, 0xa2, 0xbd, 0xde, 0x85, 0x6a, 0xfa, 0x9e, 0x97, 0xa6, 0x8b, 0xdb, 0x59, 0x35, 0xca, 0xdb, 0xab, 0x5d, 0x50, 0x4b, 0xbe, 0x76, 0xb0,
0xb5, 0x4d, 0x02, 0x97, 0xb9, 0x2d, 0xf4, 0x3a, 0x4c, 0xed, 0x32, 0xda, 0xb5, 0x31, 0xdd, 0x55, 0xef, 0x50, 0xa7, 0x05, 0x5f, 0x07, 0x33, 0x7b, 0x94, 0x74, 0x2d, 0x44, 0xf6, 0x24, 0x60, 0x55,
0x80, 0x0b, 0x0a, 0x70, 0xea, 0x96, 0x92, 0xe3, 0xc4, 0x02, 0x2d, 0xc3, 0x99, 0xcf, 0x23, 0xc7, 0x02, 0xce, 0xdc, 0x92, 0x72, 0x14, 0x5b, 0xc0, 0x15, 0x70, 0xe6, 0xf3, 0xd0, 0xb1, 0x5e, 0x12,
0x5a, 0x49, 0x1a, 0xcf, 0x2b, 0xe3, 0x33, 0x0a, 0x0f, 0xc7, 0x7a, 0xfd, 0xa7, 0x32, 0xcc, 0xad, 0xc6, 0x8b, 0xd2, 0xf8, 0x8c, 0xc4, 0x43, 0x91, 0x5e, 0xdd, 0x03, 0x0b, 0x1b, 0x38, 0x30, 0xdb,
0x93, 0xd0, 0x6a, 0x63, 0xca, 0xbd, 0x5e, 0x60, 0x51, 0x8e, 0x9e, 0x40, 0xd5, 0x25, 0x0e, 0xe5, 0xd7, 0x5d, 0xc7, 0xa2, 0x22, 0xc2, 0x65, 0x50, 0x76, 0xb0, 0x4d, 0x64, 0x88, 0x73, 0xd2, 0xb3,
0x3e, 0xb1, 0xe8, 0x43, 0xda, 0xa5, 0x56, 0xe8, 0x05, 0x32, 0xe0, 0xca, 0xea, 0x15, 0x23, 0xad, 0xfc, 0x00, 0xdb, 0x04, 0x09, 0x0d, 0x5c, 0x03, 0x80, 0xe4, 0xe3, 0x83, 0xd2, 0x0e, 0xa4, 0x42,
0x9f, 0x24, 0x93, 0x86, 0xdf, 0x69, 0x09, 0x01, 0x37, 0xc4, 0x86, 0x19, 0x7b, 0x2b, 0xc6, 0x7d, 0x4b, 0x59, 0xa9, 0x3f, 0x97, 0x25, 0x11, 0x22, 0xcc, 0xed, 0xf9, 0x26, 0x61, 0xf0, 0x09, 0xa8,
0xb2, 0x43, 0xbb, 0xb1, 0xab, 0x79, 0x6e, 0xd0, 0x6f, 0x54, 0x1f, 0x14, 0x11, 0xf1, 0x30, 0x09, 0x71, 0x38, 0xe6, 0x61, 0x93, 0x3c, 0x24, 0x5d, 0x62, 0x06, 0xae, 0x2f, 0x58, 0x2b, 0x6b, 0x57,
0xf2, 0x60, 0xce, 0xdb, 0x79, 0x4c, 0xad, 0x30, 0xa1, 0x9d, 0x7c, 0x75, 0x5a, 0x34, 0xe8, 0x37, 0xb4, 0xa4, 0x4e, 0xe3, 0x1d, 0xd3, 0xbc, 0x4e, 0x8b, 0x0b, 0x98, 0xc6, 0x0b, 0x43, 0xdb, 0x5f,
0xe6, 0x36, 0x72, 0x70, 0xb8, 0x00, 0x8f, 0xbe, 0x84, 0xd9, 0x40, 0xc5, 0x8d, 0x7b, 0x5d, 0xca, 0xd5, 0xee, 0xe3, 0x5d, 0xd2, 0x8d, 0x5c, 0x8d, 0x73, 0x83, 0x7e, 0xb3, 0xf6, 0x20, 0x8f, 0x88,
0x6b, 0xa5, 0xa5, 0xd2, 0xe5, 0xca, 0xaa, 0x69, 0x8c, 0x7d, 0x4c, 0x0c, 0x11, 0x98, 0x2d, 0x9c, 0x8a, 0x24, 0xd0, 0x05, 0x0b, 0xee, 0xee, 0x63, 0x62, 0x06, 0x31, 0xed, 0xe4, 0xcb, 0xd3, 0xc2,
0xb7, 0x59, 0xd8, 0xde, 0xf0, 0x69, 0xa4, 0xe7, 0xe6, 0x39, 0x95, 0xf2, 0x59, 0x9c, 0x25, 0xc0, 0x41, 0xbf, 0xb9, 0xb0, 0x99, 0x81, 0x43, 0x39, 0x78, 0xf8, 0x15, 0x98, 0xf7, 0x65, 0xdc, 0xa8,
0x79, 0x3e, 0xf4, 0x9d, 0x06, 0x67, 0xe9, 0x13, 0xab, 0xdb, 0xb3, 0x69, 0xce, 0xae, 0x56, 0x3e, 0xd7, 0x25, 0xac, 0x5e, 0x5a, 0x2e, 0x5d, 0xae, 0xac, 0x19, 0xda, 0xc8, 0xed, 0xa8, 0xf1, 0xc0,
0xb1, 0x85, 0xfc, 0x5f, 0x2d, 0xe4, 0xec, 0xcd, 0x11, 0x3c, 0x78, 0x24, 0x3b, 0xba, 0x01, 0x15, 0x2c, 0xee, 0xbc, 0x43, 0x83, 0xf6, 0xa6, 0x47, 0x42, 0x3d, 0x33, 0xce, 0xc9, 0xc4, 0xcf, 0xa3,
0x47, 0x14, 0xc5, 0xa6, 0xd7, 0x65, 0xd6, 0x7e, 0xed, 0x8c, 0x2c, 0x22, 0x7d, 0xd0, 0x6f, 0x54, 0x34, 0x01, 0xca, 0xf2, 0xc1, 0xef, 0x15, 0x70, 0x96, 0x3c, 0x31, 0xbb, 0x3d, 0x8b, 0x64, 0xec,
0xd6, 0x53, 0xf1, 0x41, 0xbf, 0x31, 0x9f, 0xf9, 0xfc, 0x68, 0xdf, 0xa7, 0x38, 0xeb, 0xa6, 0xbf, 0xea, 0xe5, 0x53, 0x5b, 0xc8, 0xab, 0x72, 0x21, 0x67, 0x6f, 0x0e, 0xe1, 0x41, 0x43, 0xd9, 0xe1,
0xd0, 0xe0, 0xfc, 0x21, 0xab, 0x42, 0xd7, 0xd2, 0xcc, 0xcb, 0xd2, 0xa8, 0x69, 0x4b, 0xa5, 0xcb, 0x0d, 0x50, 0xb1, 0x79, 0x51, 0x6c, 0xb9, 0x5d, 0x6a, 0x1e, 0xd4, 0xcf, 0x88, 0x52, 0x52, 0x07,
0xd3, 0x66, 0x35, 0x9b, 0x31, 0xa9, 0xc0, 0x79, 0x3b, 0xf4, 0x95, 0x06, 0x28, 0x18, 0xc2, 0x53, 0xfd, 0x66, 0x65, 0x23, 0x11, 0x1f, 0xf5, 0x9b, 0x8b, 0xa9, 0xcf, 0x8f, 0x0e, 0x3c, 0x82, 0xd2,
0x85, 0x72, 0x6d, 0x9c, 0x7c, 0x19, 0x23, 0x92, 0xb4, 0xa8, 0x92, 0x84, 0x86, 0x75, 0x78, 0x04, 0x6e, 0xea, 0x0b, 0x05, 0x9c, 0x3f, 0x66, 0x55, 0xf0, 0x5a, 0x92, 0x79, 0x51, 0x1a, 0x75, 0x65,
0x9d, 0x4e, 0x60, 0x7a, 0x93, 0x04, 0xc4, 0xb9, 0xc7, 0x5c, 0x1b, 0xad, 0x02, 0x10, 0x9f, 0x6d, 0xb9, 0x74, 0x79, 0xd6, 0xa8, 0xa5, 0x33, 0x26, 0x14, 0x28, 0x6b, 0x07, 0xbf, 0x56, 0x00, 0xf4,
0xd1, 0x40, 0x9e, 0xf7, 0xa8, 0x35, 0x20, 0x05, 0x08, 0x6b, 0x9b, 0x77, 0x94, 0x06, 0x67, 0xac, 0x0b, 0x78, 0xb2, 0x50, 0xae, 0x8d, 0x92, 0x2f, 0x6d, 0x48, 0x92, 0x96, 0x64, 0x92, 0x60, 0x51,
0xd0, 0x12, 0x94, 0x3b, 0xcc, 0xb5, 0xd5, 0x61, 0x9e, 0x51, 0xd6, 0x65, 0x81, 0x87, 0xa5, 0x46, 0x87, 0x86, 0xd0, 0xa9, 0x18, 0xcc, 0x6e, 0x61, 0x1f, 0xdb, 0xf7, 0xa8, 0x63, 0xf1, 0xbe, 0xc3,
0x7f, 0x04, 0x53, 0x92, 0x42, 0x1c, 0xe8, 0x25, 0x28, 0x8b, 0xd3, 0xa2, 0xb0, 0x13, 0x6b, 0x91, 0x1e, 0xdd, 0x26, 0xbe, 0xe8, 0x3b, 0x25, 0xdb, 0x77, 0xeb, 0x5b, 0x77, 0xa4, 0x06, 0xa5, 0xac,
0x11, 0x2c, 0x35, 0xa8, 0x09, 0xd3, 0xc9, 0x79, 0x52, 0xa0, 0x55, 0x65, 0x36, 0x9d, 0x9c, 0x3d, 0x78, 0x37, 0x77, 0xa8, 0x63, 0xc9, 0x2e, 0x8d, 0xbb, 0x99, 0xe3, 0x21, 0xa1, 0x51, 0x1f, 0x81,
0x9c, 0xda, 0xe8, 0xdf, 0x6b, 0x30, 0x23, 0xb6, 0xec, 0x7a, 0x9b, 0x5a, 0x1d, 0xd1, 0x62, 0xbe, 0x19, 0x41, 0xc1, 0x0f, 0x8e, 0x93, 0x7b, 0x5f, 0x07, 0xb3, 0x71, 0x3f, 0x49, 0xd0, 0x9a, 0x34,
0xd6, 0x00, 0xd1, 0x62, 0xe3, 0x89, 0xf6, 0xa5, 0xb2, 0xfa, 0xfe, 0x31, 0x0a, 0x71, 0xa8, 0x7b, 0x9b, 0x8d, 0x7b, 0x0f, 0x25, 0x36, 0xea, 0x0f, 0x0a, 0x98, 0xe3, 0x5b, 0x76, 0xbd, 0x4d, 0xcc,
0xa5, 0xd9, 0x1d, 0x52, 0x71, 0x3c, 0x82, 0x53, 0xff, 0x79, 0x12, 0x2e, 0x6c, 0x91, 0x2e, 0xb3, 0x0e, 0x3f, 0xca, 0xbe, 0x51, 0x00, 0x24, 0xf9, 0x03, 0x2e, 0xdc, 0x97, 0xca, 0xda, 0xfb, 0x63,
0x49, 0xc8, 0xdc, 0xd6, 0x5a, 0x4c, 0x17, 0x95, 0x15, 0xfa, 0x14, 0xa6, 0xc4, 0x89, 0xb7, 0x49, 0x14, 0x62, 0xe1, 0x94, 0x4c, 0xb2, 0x5b, 0x50, 0x31, 0x34, 0x84, 0x53, 0xfd, 0x65, 0x12, 0x5c,
0x48, 0x54, 0x5b, 0x7a, 0x73, 0xbc, 0xfe, 0x10, 0x35, 0x83, 0x75, 0x1a, 0x92, 0x74, 0x7b, 0x52, 0xd8, 0xc6, 0x5d, 0x6a, 0xe1, 0x80, 0x3a, 0xad, 0xf5, 0x88, 0x2e, 0x2c, 0x2b, 0xf8, 0x29, 0x98,
0x19, 0x4e, 0x50, 0xd1, 0x63, 0x28, 0x73, 0x9f, 0x5a, 0xaa, 0xa8, 0x3e, 0x3c, 0x46, 0xec, 0x87, 0xe1, 0x1d, 0x6f, 0xe1, 0x00, 0xcb, 0x63, 0xe9, 0xcd, 0xd1, 0xce, 0x87, 0xf0, 0x30, 0xd8, 0x20,
0xae, 0xfa, 0xa1, 0x4f, 0xad, 0x74, 0xe3, 0xc4, 0x17, 0x96, 0x1c, 0x28, 0x80, 0xd3, 0x3c, 0x24, 0x01, 0x4e, 0xb6, 0x27, 0x91, 0xa1, 0x18, 0x15, 0x3e, 0x06, 0x65, 0xe6, 0x11, 0x53, 0x16, 0xd5,
0x61, 0x8f, 0xcb, 0x56, 0x5d, 0x59, 0xbd, 0x7b, 0x22, 0x6c, 0x12, 0xd1, 0x9c, 0x53, 0x7c, 0xa7, 0x87, 0x63, 0xc4, 0x7e, 0xec, 0xaa, 0x1f, 0x7a, 0xc4, 0x4c, 0x36, 0x8e, 0x7f, 0x21, 0xc1, 0x01,
0xa3, 0x6f, 0xac, 0x98, 0xf4, 0x3f, 0x34, 0x58, 0x3a, 0xd4, 0xd7, 0x64, 0xae, 0x2d, 0xea, 0xe1, 0x7d, 0x30, 0xcd, 0x02, 0x1c, 0xf4, 0x98, 0xb8, 0x12, 0x2a, 0x6b, 0x77, 0x4f, 0x85, 0x4d, 0x20,
0xdf, 0x4f, 0xf3, 0x67, 0xb9, 0x34, 0x6f, 0x9c, 0x44, 0xe0, 0x6a, 0xf1, 0x87, 0x65, 0x5b, 0xff, 0x1a, 0x0b, 0x92, 0x6f, 0x3a, 0xfc, 0x46, 0x92, 0x49, 0xfd, 0x53, 0x01, 0xcb, 0xc7, 0xfa, 0x1a,
0x5d, 0x83, 0x4b, 0x47, 0x39, 0xdf, 0x67, 0x3c, 0x44, 0x9f, 0x0c, 0x45, 0x6f, 0x8c, 0x79, 0x09, 0xd4, 0xb1, 0x78, 0x3d, 0xfc, 0xf7, 0x69, 0xfe, 0x2c, 0x93, 0xe6, 0xcd, 0xd3, 0x08, 0x5c, 0x2e,
0x31, 0x1e, 0xc5, 0x9e, 0x5c, 0xd0, 0xb1, 0x24, 0x13, 0xb9, 0x0f, 0xa7, 0x58, 0x48, 0x1d, 0xd1, 0xfe, 0xb8, 0x6c, 0xab, 0x7f, 0x28, 0xe0, 0xd2, 0x49, 0xce, 0xf7, 0x29, 0x0b, 0xe0, 0x27, 0x85,
0xb6, 0xc4, 0xe9, 0xba, 0x77, 0x82, 0xa1, 0x9b, 0xb3, 0x8a, 0xf7, 0xd4, 0x1d, 0xc1, 0x80, 0x23, 0xe8, 0xb5, 0x11, 0x2f, 0x21, 0xca, 0xc2, 0xd8, 0xe3, 0x41, 0x20, 0x92, 0xa4, 0x22, 0xf7, 0xc0,
0x22, 0xfd, 0x9b, 0xd2, 0xd1, 0x81, 0x8b, 0x3c, 0x89, 0x66, 0xe6, 0x4b, 0xe1, 0x83, 0xb4, 0xe1, 0x14, 0x0d, 0x88, 0xcd, 0x8f, 0x2d, 0xde, 0x5d, 0xf7, 0x4e, 0x31, 0x74, 0x63, 0x5e, 0xf2, 0x4e,
0x24, 0xdb, 0xb8, 0x99, 0x68, 0x70, 0xc6, 0x0a, 0x3d, 0x82, 0x29, 0x5f, 0xb5, 0xaa, 0x11, 0x37, 0xdd, 0xe1, 0x0c, 0x28, 0x24, 0x52, 0xbf, 0x2d, 0x9d, 0x1c, 0x38, 0xcf, 0x13, 0x3f, 0xcc, 0x3c,
0xf6, 0x51, 0x11, 0xc5, 0x5d, 0xce, 0x9c, 0x11, 0xd9, 0x8a, 0xbf, 0x70, 0x02, 0x89, 0x7a, 0x30, 0x21, 0x7c, 0x90, 0x1c, 0x38, 0xf1, 0x36, 0x6e, 0xc5, 0x1a, 0x94, 0xb2, 0x82, 0x8f, 0xc0, 0x8c,
0xe7, 0xe4, 0x46, 0x14, 0x75, 0x54, 0xde, 0x3d, 0x06, 0x49, 0x7e, 0xc6, 0x89, 0x86, 0x83, 0xbc, 0x27, 0x8f, 0xaa, 0x21, 0x37, 0xf6, 0x49, 0x11, 0x45, 0xa7, 0x9c, 0x31, 0xc7, 0xb3, 0x15, 0x7d,
0x0c, 0x17, 0x48, 0xd0, 0x36, 0x54, 0xf7, 0x54, 0xc6, 0x3c, 0x77, 0xcd, 0x8a, 0xee, 0x99, 0xb2, 0xa1, 0x18, 0x12, 0xf6, 0xc0, 0x82, 0x9d, 0x19, 0x51, 0x64, 0xab, 0xbc, 0x3b, 0x06, 0x49, 0x76,
0xbc, 0xa6, 0x96, 0xc5, 0x48, 0xb3, 0x55, 0x54, 0x1e, 0xf4, 0x1b, 0x0b, 0x45, 0x21, 0x1e, 0xc6, 0xc6, 0x09, 0x87, 0x83, 0xac, 0x0c, 0xe5, 0x48, 0xe0, 0x0e, 0xa8, 0xed, 0xcb, 0x8c, 0xb9, 0xce,
0xd0, 0x7f, 0xd3, 0xe0, 0xe2, 0xa1, 0x7b, 0xf1, 0x1f, 0x54, 0x1f, 0xcb, 0x57, 0xdf, 0x8d, 0x13, 0xba, 0x19, 0xde, 0x33, 0x65, 0x71, 0x4d, 0xad, 0xf0, 0x91, 0x66, 0x3b, 0xaf, 0x3c, 0xea, 0x37,
0xa9, 0xbe, 0xd1, 0x65, 0xf7, 0x43, 0xf9, 0x6f, 0x42, 0x95, 0xf5, 0x46, 0x60, 0xda, 0x8f, 0x6f, 0xab, 0x79, 0x21, 0x2a, 0x62, 0xa8, 0xbf, 0x2b, 0xe0, 0xe2, 0xb1, 0x7b, 0xf1, 0x3f, 0x54, 0x1f,
0x52, 0x15, 0xeb, 0xd5, 0xe3, 0x16, 0x8f, 0xf0, 0x35, 0x67, 0xc5, 0x55, 0x97, 0x7c, 0xe2, 0x14, 0xcd, 0x56, 0xdf, 0x8d, 0x53, 0xa9, 0xbe, 0xe1, 0x65, 0xf7, 0xe3, 0xd4, 0x3f, 0x84, 0x2a, 0xea,
0x15, 0x7d, 0x01, 0x0b, 0x72, 0x6b, 0xaf, 0x7b, 0xae, 0x00, 0x60, 0x6e, 0x18, 0xcf, 0x0b, 0xff, 0x0d, 0x83, 0x59, 0x2f, 0xba, 0x49, 0x65, 0xac, 0x57, 0xc7, 0x2d, 0x1e, 0xee, 0x6b, 0xcc, 0xf3,
0xa0, 0x82, 0xce, 0x0e, 0xfa, 0x8d, 0x85, 0xf5, 0x02, 0x2c, 0x1e, 0x22, 0x42, 0x5d, 0xa8, 0xa4, 0xab, 0x2e, 0xfe, 0x44, 0x09, 0x2a, 0xfc, 0x02, 0x54, 0x6d, 0x39, 0x4b, 0x73, 0x00, 0xea, 0x04,
0x15, 0x10, 0x0f, 0x98, 0x6f, 0xbd, 0x42, 0xca, 0x3d, 0xd7, 0xfc, 0x9f, 0xca, 0x71, 0x25, 0x95, 0xd1, 0xbc, 0xf0, 0x2f, 0x2a, 0xe8, 0xec, 0xa0, 0xdf, 0xac, 0x6e, 0xe4, 0x60, 0x51, 0x81, 0x08,
0x71, 0x9c, 0x85, 0x47, 0xf7, 0x61, 0x76, 0x97, 0xb0, 0x6e, 0x2f, 0xa0, 0x6a, 0x74, 0x2b, 0xcb, 0x76, 0x41, 0x25, 0xa9, 0x80, 0x68, 0xc0, 0x7c, 0xeb, 0x25, 0x52, 0xee, 0x3a, 0xc6, 0x2b, 0x32,
0x03, 0xfc, 0x9a, 0x18, 0xab, 0x6e, 0x65, 0x15, 0x07, 0xfd, 0x46, 0x35, 0x27, 0x90, 0xe3, 0x5b, 0xc7, 0x95, 0x44, 0xc6, 0x50, 0x1a, 0x1e, 0xde, 0x07, 0xf3, 0x7b, 0x98, 0x76, 0x7b, 0x3e, 0x91,
0xde, 0x19, 0x3d, 0xd5, 0x60, 0x81, 0xe4, 0x1f, 0x40, 0xbc, 0x76, 0x4a, 0x46, 0xf0, 0xde, 0x31, 0xa3, 0x5b, 0x59, 0x34, 0xf0, 0x6b, 0x7c, 0xac, 0xba, 0x95, 0x56, 0x1c, 0xf5, 0x9b, 0xb5, 0x8c,
0x22, 0x28, 0xbc, 0xa1, 0xcc, 0x9a, 0x0a, 0x63, 0xa1, 0xa0, 0xe0, 0x78, 0x88, 0x4d, 0xff, 0x71, 0x40, 0x8c, 0x6f, 0x59, 0x67, 0xf8, 0x54, 0x01, 0x55, 0x9c, 0x7d, 0x68, 0xb1, 0xfa, 0x94, 0x88,
0x12, 0x1a, 0x47, 0x5c, 0x73, 0xe8, 0x2e, 0x20, 0x6f, 0x87, 0xd3, 0x60, 0x8f, 0xda, 0xb7, 0xa3, 0xe0, 0xbd, 0x31, 0x22, 0xc8, 0xbd, 0xd5, 0x8c, 0xba, 0x0c, 0xa3, 0x9a, 0x53, 0x30, 0x54, 0x60,
0x17, 0x5c, 0x3c, 0x87, 0x95, 0xd2, 0xd1, 0x63, 0x63, 0xc8, 0x02, 0x8f, 0xf0, 0x42, 0x0e, 0xcc, 0x83, 0x5f, 0x82, 0x45, 0x3b, 0xf3, 0x0e, 0x62, 0xf5, 0x69, 0xb1, 0x80, 0xb1, 0xb7, 0x2e, 0x46,
0x84, 0x99, 0xa9, 0xe8, 0x38, 0x73, 0xa5, 0x8a, 0x36, 0x3b, 0x54, 0x99, 0x0b, 0x83, 0x7e, 0x23, 0x48, 0xde, 0x7c, 0x59, 0x39, 0x43, 0x79, 0x2a, 0xf5, 0xa7, 0x49, 0xd0, 0x3c, 0xe1, 0x92, 0x85,
0x37, 0x66, 0xe1, 0x1c, 0x3c, 0xb2, 0x00, 0x2c, 0xcf, 0xb5, 0x59, 0xb6, 0x38, 0x9a, 0xe3, 0x1d, 0x77, 0x01, 0x74, 0x77, 0x19, 0xf1, 0xf7, 0x89, 0x75, 0x3b, 0x7c, 0xa7, 0x46, 0x53, 0x60, 0x29,
0xf5, 0xeb, 0xb1, 0x5f, 0xda, 0x9e, 0x13, 0x11, 0xc7, 0x19, 0x58, 0xfd, 0x4f, 0x0d, 0x20, 0xad, 0x19, 0x7c, 0x36, 0x0b, 0x16, 0x68, 0x88, 0x17, 0xb4, 0xc1, 0x5c, 0x90, 0x9a, 0xc9, 0xc6, 0x99,
0x18, 0x74, 0x09, 0x20, 0xf3, 0x3c, 0x8d, 0x3a, 0x7c, 0x59, 0x40, 0xe0, 0x8c, 0x5c, 0xbc, 0x21, 0x6a, 0x65, 0xa8, 0xe9, 0x91, 0xce, 0xa8, 0x0e, 0xfa, 0xcd, 0xcc, 0x90, 0x87, 0x32, 0xf0, 0xd0,
0x1d, 0xca, 0x39, 0x69, 0xc5, 0xe3, 0x64, 0xf2, 0x86, 0x5c, 0x8f, 0xc4, 0x38, 0xd6, 0xa3, 0x6d, 0x04, 0xc0, 0x4c, 0xf2, 0x1a, 0x96, 0xa6, 0x3e, 0xda, 0x41, 0x93, 0x64, 0x33, 0xbe, 0x1c, 0x52,
0x38, 0x1d, 0x50, 0xc2, 0x3d, 0x57, 0xbd, 0x36, 0x3f, 0x10, 0x23, 0x07, 0x96, 0x92, 0x83, 0x7e, 0x89, 0x4c, 0xc1, 0xaa, 0x7f, 0x29, 0x00, 0x24, 0xf5, 0x0a, 0x2f, 0x81, 0xd4, 0x53, 0x54, 0xde,
0x63, 0x65, 0x9c, 0x37, 0xbe, 0xa1, 0x26, 0x14, 0xe9, 0x84, 0x15, 0x1c, 0xba, 0x0d, 0x55, 0xc5, 0x2f, 0x65, 0x0e, 0x81, 0x52, 0x72, 0xfe, 0x52, 0xb6, 0x09, 0x63, 0xb8, 0x15, 0x0d, 0xb3, 0xf1,
0x91, 0x59, 0x70, 0x54, 0xd1, 0x17, 0xd4, 0x6a, 0xaa, 0xeb, 0x45, 0x03, 0x3c, 0xec, 0x63, 0x6e, 0x4b, 0x79, 0x23, 0x14, 0xa3, 0x48, 0x0f, 0x77, 0xc0, 0xb4, 0x4f, 0x30, 0x73, 0x1d, 0xf9, 0xa6,
0x3c, 0x7b, 0x59, 0x9f, 0x78, 0xfe, 0xb2, 0x3e, 0xf1, 0xe2, 0x65, 0x7d, 0xe2, 0xe9, 0xa0, 0xae, 0xfe, 0x80, 0x0f, 0x3c, 0x48, 0x48, 0x8e, 0xfa, 0xcd, 0xd5, 0x51, 0xfe, 0xc9, 0xd0, 0xe4, 0x7c,
0x3d, 0x1b, 0xd4, 0xb5, 0xe7, 0x83, 0xba, 0xf6, 0x62, 0x50, 0xd7, 0x7e, 0x19, 0xd4, 0xb5, 0x6f, 0x24, 0x9c, 0x90, 0x84, 0x83, 0xb7, 0x41, 0x4d, 0x72, 0xa4, 0x16, 0x1c, 0xf6, 0xd3, 0x05, 0xb9,
0x7f, 0xad, 0x4f, 0x7c, 0xbc, 0x3c, 0xf6, 0xff, 0x2a, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x9a, 0xda, 0x46, 0xde, 0x00, 0x15, 0x7d, 0x8c, 0xcd, 0x67, 0x87, 0x8d, 0x89, 0xe7, 0x87, 0x8d,
0xd9, 0x66, 0xdd, 0x9c, 0x11, 0x00, 0x00, 0x89, 0x17, 0x87, 0x8d, 0x89, 0xa7, 0x83, 0x86, 0xf2, 0x6c, 0xd0, 0x50, 0x9e, 0x0f, 0x1a, 0xca,
0x8b, 0x41, 0x43, 0xf9, 0x75, 0xd0, 0x50, 0xbe, 0xfb, 0xad, 0x31, 0xf1, 0xf1, 0xca, 0xc8, 0xff,
0x1e, 0xfd, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x08, 0xaf, 0xaa, 0x52, 0x82, 0x12, 0x00, 0x00,
} }
func (m *AuditAnnotation) Marshal() (dAtA []byte, err error) { func (m *AuditAnnotation) Marshal() (dAtA []byte, err error) {
@ -643,6 +674,39 @@ func (m *ExpressionWarning) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *MatchCondition) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *MatchCondition) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *MatchCondition) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
i -= len(m.Expression)
copy(dAtA[i:], m.Expression)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Expression)))
i--
dAtA[i] = 0x12
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *MatchResources) Marshal() (dAtA []byte, err error) { func (m *MatchResources) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
@ -1141,6 +1205,20 @@ func (m *ValidatingAdmissionPolicySpec) MarshalToSizedBuffer(dAtA []byte) (int,
_ = i _ = i
var l int var l int
_ = l _ = l
if len(m.MatchConditions) > 0 {
for iNdEx := len(m.MatchConditions) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.MatchConditions[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x32
}
}
if len(m.AuditAnnotations) > 0 { if len(m.AuditAnnotations) > 0 {
for iNdEx := len(m.AuditAnnotations) - 1; iNdEx >= 0; iNdEx-- { for iNdEx := len(m.AuditAnnotations) - 1; iNdEx >= 0; iNdEx-- {
{ {
@ -1337,6 +1415,19 @@ func (m *ExpressionWarning) Size() (n int) {
return n return n
} }
func (m *MatchCondition) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
n += 1 + l + sovGenerated(uint64(l))
l = len(m.Expression)
n += 1 + l + sovGenerated(uint64(l))
return n
}
func (m *MatchResources) Size() (n int) { func (m *MatchResources) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
@ -1545,6 +1636,12 @@ func (m *ValidatingAdmissionPolicySpec) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l)) n += 1 + l + sovGenerated(uint64(l))
} }
} }
if len(m.MatchConditions) > 0 {
for _, e := range m.MatchConditions {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
}
return n return n
} }
@ -1615,6 +1712,17 @@ func (this *ExpressionWarning) String() string {
}, "") }, "")
return s return s
} }
func (this *MatchCondition) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&MatchCondition{`,
`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
`Expression:` + fmt.Sprintf("%v", this.Expression) + `,`,
`}`,
}, "")
return s
}
func (this *MatchResources) String() string { func (this *MatchResources) String() string {
if this == nil { if this == nil {
return "nil" return "nil"
@ -1769,12 +1877,18 @@ func (this *ValidatingAdmissionPolicySpec) String() string {
repeatedStringForAuditAnnotations += strings.Replace(strings.Replace(f.String(), "AuditAnnotation", "AuditAnnotation", 1), `&`, ``, 1) + "," repeatedStringForAuditAnnotations += strings.Replace(strings.Replace(f.String(), "AuditAnnotation", "AuditAnnotation", 1), `&`, ``, 1) + ","
} }
repeatedStringForAuditAnnotations += "}" repeatedStringForAuditAnnotations += "}"
repeatedStringForMatchConditions := "[]MatchCondition{"
for _, f := range this.MatchConditions {
repeatedStringForMatchConditions += strings.Replace(strings.Replace(f.String(), "MatchCondition", "MatchCondition", 1), `&`, ``, 1) + ","
}
repeatedStringForMatchConditions += "}"
s := strings.Join([]string{`&ValidatingAdmissionPolicySpec{`, s := strings.Join([]string{`&ValidatingAdmissionPolicySpec{`,
`ParamKind:` + strings.Replace(this.ParamKind.String(), "ParamKind", "ParamKind", 1) + `,`, `ParamKind:` + strings.Replace(this.ParamKind.String(), "ParamKind", "ParamKind", 1) + `,`,
`MatchConstraints:` + strings.Replace(this.MatchConstraints.String(), "MatchResources", "MatchResources", 1) + `,`, `MatchConstraints:` + strings.Replace(this.MatchConstraints.String(), "MatchResources", "MatchResources", 1) + `,`,
`Validations:` + repeatedStringForValidations + `,`, `Validations:` + repeatedStringForValidations + `,`,
`FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`, `FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`,
`AuditAnnotations:` + repeatedStringForAuditAnnotations + `,`, `AuditAnnotations:` + repeatedStringForAuditAnnotations + `,`,
`MatchConditions:` + repeatedStringForMatchConditions + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -2045,6 +2159,120 @@ func (m *ExpressionWarning) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *MatchCondition) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: MatchCondition: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: MatchCondition: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Expression", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Expression = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *MatchResources) Unmarshal(dAtA []byte) error { func (m *MatchResources) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
@ -3582,6 +3810,40 @@ func (m *ValidatingAdmissionPolicySpec) Unmarshal(dAtA []byte) error {
return err return err
} }
iNdEx = postIndex iNdEx = postIndex
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field MatchConditions", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.MatchConditions = append(m.MatchConditions, MatchCondition{})
if err := m.MatchConditions[len(m.MatchConditions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:]) skippy, err := skipGenerated(dAtA[iNdEx:])

View File

@ -79,6 +79,34 @@ message ExpressionWarning {
optional string warning = 3; optional string warning = 3;
} }
message MatchCondition {
// Name is an identifier for this match condition, used for strategic merging of MatchConditions,
// as well as providing an identifier for logging purposes. A good name should be descriptive of
// the associated expression.
// Name must be a qualified name consisting of alphanumeric characters, '-', '_' or '.', and
// must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or
// '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an
// optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')
//
// Required.
optional string name = 1;
// Expression represents the expression which will be evaluated by CEL. Must evaluate to bool.
// CEL expressions have access to the contents of the AdmissionRequest and Authorizer, organized into CEL variables:
//
// 'object' - The object from the incoming request. The value is null for DELETE requests.
// 'oldObject' - The existing object. The value is null for CREATE requests.
// 'request' - Attributes of the admission request(/pkg/apis/admission/types.go#AdmissionRequest).
// 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
// 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
// request resource.
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Required.
optional string expression = 2;
}
// MatchResources decides whether to run the admission control policy on an object based // MatchResources decides whether to run the admission control policy on an object based
// on whether it meets the match criteria. // on whether it meets the match criteria.
// The exclude rules take precedence over include rules (if a resource matches both, it is excluded) // The exclude rules take precedence over include rules (if a resource matches both, it is excluded)
@ -380,6 +408,28 @@ message ValidatingAdmissionPolicySpec {
// +listType=atomic // +listType=atomic
// +optional // +optional
repeated AuditAnnotation auditAnnotations = 5; repeated AuditAnnotation auditAnnotations = 5;
// MatchConditions is a list of conditions that must be met for a request to be validated.
// Match conditions filter requests that have already been matched by the rules,
// namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests.
// There are a maximum of 64 match conditions allowed.
//
// If a parameter object is provided, it can be accessed via the `params` handle in the same
// manner as validation expressions.
//
// The exact matching logic is (in order):
// 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.
// 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.
// 3. If any matchCondition evaluates to an error (but none are FALSE):
// - If failurePolicy=Fail, reject the request
// - If failurePolicy=Ignore, the policy is skipped
//
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
// +optional
repeated MatchCondition matchConditions = 6;
} }
// ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy. // ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.

View File

@ -179,8 +179,32 @@ type ValidatingAdmissionPolicySpec struct {
// +listType=atomic // +listType=atomic
// +optional // +optional
AuditAnnotations []AuditAnnotation `json:"auditAnnotations,omitempty" protobuf:"bytes,5,rep,name=auditAnnotations"` AuditAnnotations []AuditAnnotation `json:"auditAnnotations,omitempty" protobuf:"bytes,5,rep,name=auditAnnotations"`
// MatchConditions is a list of conditions that must be met for a request to be validated.
// Match conditions filter requests that have already been matched by the rules,
// namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests.
// There are a maximum of 64 match conditions allowed.
//
// If a parameter object is provided, it can be accessed via the `params` handle in the same
// manner as validation expressions.
//
// The exact matching logic is (in order):
// 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.
// 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.
// 3. If any matchCondition evaluates to an error (but none are FALSE):
// - If failurePolicy=Fail, reject the request
// - If failurePolicy=Ignore, the policy is skipped
//
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
// +optional
MatchConditions []MatchCondition `json:"matchConditions,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,6,rep,name=matchConditions"`
} }
type MatchCondition v1.MatchCondition
// ParamKind is a tuple of Group Kind and Version. // ParamKind is a tuple of Group Kind and Version.
// +structType=atomic // +structType=atomic
type ParamKind struct { type ParamKind struct {

View File

@ -158,6 +158,7 @@ var map_ValidatingAdmissionPolicySpec = map[string]string{
"validations": "Validations contain CEL expressions which is used to apply the validation. Validations and AuditAnnotations may not both be empty; a minimum of one Validations or AuditAnnotations is required.", "validations": "Validations contain CEL expressions which is used to apply the validation. Validations and AuditAnnotations may not both be empty; a minimum of one Validations or AuditAnnotations is required.",
"failurePolicy": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.", "failurePolicy": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.",
"auditAnnotations": "auditAnnotations contains CEL expressions which are used to produce audit annotations for the audit event of the API request. validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is required.", "auditAnnotations": "auditAnnotations contains CEL expressions which are used to produce audit annotations for the audit event of the API request. validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is required.",
"matchConditions": "MatchConditions is a list of conditions that must be met for a request to be validated. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nIf a parameter object is provided, it can be accessed via the `params` handle in the same manner as validation expressions.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the policy is skipped",
} }
func (ValidatingAdmissionPolicySpec) SwaggerDoc() map[string]string { func (ValidatingAdmissionPolicySpec) SwaggerDoc() map[string]string {

View File

@ -58,6 +58,22 @@ func (in *ExpressionWarning) DeepCopy() *ExpressionWarning {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchCondition) DeepCopyInto(out *MatchCondition) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchCondition.
func (in *MatchCondition) DeepCopy() *MatchCondition {
if in == nil {
return nil
}
out := new(MatchCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchResources) DeepCopyInto(out *MatchResources) { func (in *MatchResources) DeepCopyInto(out *MatchResources) {
*out = *in *out = *in
@ -360,6 +376,11 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopyInto(out *ValidatingAdmissionPo
*out = make([]AuditAnnotation, len(*in)) *out = make([]AuditAnnotation, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.MatchConditions != nil {
in, out := &in.MatchConditions, &out.MatchConditions
*out = make([]MatchCondition, len(*in))
copy(*out, *in)
}
return return
} }

View File

@ -133,6 +133,12 @@
"key": "keyValue", "key": "keyValue",
"valueExpression": "valueExpressionValue" "valueExpression": "valueExpressionValue"
} }
],
"matchConditions": [
{
"name": "nameValue",
"expression": "expressionValue"
}
] ]
}, },
"status": { "status": {

View File

@ -37,6 +37,9 @@ spec:
- key: keyValue - key: keyValue
valueExpression: valueExpressionValue valueExpression: valueExpressionValue
failurePolicy: failurePolicyValue failurePolicy: failurePolicyValue
matchConditions:
- expression: expressionValue
name: nameValue
matchConstraints: matchConstraints:
excludeResourceRules: excludeResourceRules:
- apiGroups: - apiGroups:

View File

@ -28,8 +28,6 @@ import (
celgo "github.com/google/cel-go/cel" celgo "github.com/google/cel-go/cel"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/klog/v2"
admissionv1 "k8s.io/api/admission/v1" admissionv1 "k8s.io/api/admission/v1"
admissionRegistrationv1 "k8s.io/api/admissionregistration/v1" admissionRegistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/api/admissionregistration/v1alpha1" "k8s.io/api/admissionregistration/v1alpha1"
@ -47,6 +45,7 @@ import (
"k8s.io/apiserver/pkg/admission/initializer" "k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/apiserver/pkg/admission/plugin/cel" "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/internal/generic" "k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/internal/generic"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
auditinternal "k8s.io/apiserver/pkg/apis/audit" auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/features"
@ -57,6 +56,7 @@ import (
clienttesting "k8s.io/client-go/testing" clienttesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/component-base/featuregate" "k8s.io/component-base/featuregate"
"k8s.io/klog/v2"
) )
var ( var (
@ -418,7 +418,7 @@ func setupTestCommon(t *testing.T, compiler cel.FilterCompiler, matcher Matcher,
// Override compiler used by controller for tests // Override compiler used by controller for tests
controller = handler.evaluator.(*celAdmissionController) controller = handler.evaluator.(*celAdmissionController)
controller.policyController.filterCompiler = compiler controller.policyController.filterCompiler = compiler
controller.policyController.newValidator = func(validationFilter, auditAnnotationFilter, messageFilter cel.Filter, fail *admissionRegistrationv1.FailurePolicyType, authorizer authorizer.Authorizer) Validator { controller.policyController.newValidator = func(validationFilter cel.Filter, celMatcher matchconditions.Matcher, auditAnnotationFilter, messageFilter cel.Filter, fail *admissionRegistrationv1.FailurePolicyType, authorizer authorizer.Authorizer) Validator {
f := validationFilter.(*fakeFilter) f := validationFilter.(*fakeFilter)
v := validatorMap[f.keyId] v := validatorMap[f.keyId]
v.validationFilter = f v.validationFilter = f

View File

@ -35,6 +35,7 @@ import (
celmetrics "k8s.io/apiserver/pkg/admission/cel" celmetrics "k8s.io/apiserver/pkg/admission/cel"
"k8s.io/apiserver/pkg/admission/plugin/cel" "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/internal/generic" "k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/internal/generic"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
celconfig "k8s.io/apiserver/pkg/apis/cel" celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
@ -99,7 +100,7 @@ type policyController struct {
authz authorizer.Authorizer authz authorizer.Authorizer
} }
type newValidator func(validationFilter cel.Filter, auditAnnotationFilter cel.Filter, messageFilter cel.Filter, failurePolicy *v1.FailurePolicyType, authorizer authorizer.Authorizer) Validator type newValidator func(validationFilter cel.Filter, celMatcher matchconditions.Matcher, auditAnnotationFilter, messageFilter cel.Filter, failurePolicy *v1.FailurePolicyType, authorizer authorizer.Authorizer) Validator
func newPolicyController( func newPolicyController(
restMapper meta.RESTMapper, restMapper meta.RESTMapper,
@ -503,11 +504,22 @@ func (c *policyController) latestPolicyData() []policyData {
} }
optionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: true} optionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: true}
expressionOptionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false} expressionOptionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}
failurePolicy := convertv1alpha1FailurePolicyTypeTov1FailurePolicyType(definitionInfo.lastReconciledValue.Spec.FailurePolicy)
var matcher matchconditions.Matcher = nil
matchConditions := definitionInfo.lastReconciledValue.Spec.MatchConditions
if len(matchConditions) > 0 {
matchExpressionAccessors := make([]cel.ExpressionAccessor, len(matchConditions))
for i := range matchConditions {
matchExpressionAccessors[i] = (*matchconditions.MatchCondition)(&matchConditions[i])
}
matcher = matchconditions.NewMatcher(c.filterCompiler.Compile(matchExpressionAccessors, optionalVars, celconfig.PerCallLimit), c.authz, failurePolicy, "validatingadmissionpolicy", definitionInfo.lastReconciledValue.Name)
}
bindingInfo.validator = c.newValidator( bindingInfo.validator = c.newValidator(
c.filterCompiler.Compile(convertv1alpha1Validations(definitionInfo.lastReconciledValue.Spec.Validations), optionalVars, celconfig.PerCallLimit), c.filterCompiler.Compile(convertv1alpha1Validations(definitionInfo.lastReconciledValue.Spec.Validations), optionalVars, celconfig.PerCallLimit),
matcher,
c.filterCompiler.Compile(convertv1alpha1AuditAnnotations(definitionInfo.lastReconciledValue.Spec.AuditAnnotations), optionalVars, celconfig.PerCallLimit), c.filterCompiler.Compile(convertv1alpha1AuditAnnotations(definitionInfo.lastReconciledValue.Spec.AuditAnnotations), optionalVars, celconfig.PerCallLimit),
c.filterCompiler.Compile(convertV1Alpha1MessageExpressions(definitionInfo.lastReconciledValue.Spec.Validations), expressionOptionalVars, celconfig.PerCallLimit), c.filterCompiler.Compile(convertV1Alpha1MessageExpressions(definitionInfo.lastReconciledValue.Spec.Validations), expressionOptionalVars, celconfig.PerCallLimit),
convertv1alpha1FailurePolicyTypeTov1FailurePolicyType(definitionInfo.lastReconciledValue.Spec.FailurePolicy), failurePolicy,
c.authz, c.authz,
) )
} }

View File

@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel" "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
celconfig "k8s.io/apiserver/pkg/apis/cel" celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
apiservercel "k8s.io/apiserver/pkg/cel" apiservercel "k8s.io/apiserver/pkg/cel"
@ -36,6 +37,7 @@ import (
// validator implements the Validator interface // validator implements the Validator interface
type validator struct { type validator struct {
celMatcher matchconditions.Matcher
validationFilter cel.Filter validationFilter cel.Filter
auditAnnotationFilter cel.Filter auditAnnotationFilter cel.Filter
messageFilter cel.Filter messageFilter cel.Filter
@ -43,8 +45,9 @@ type validator struct {
authorizer authorizer.Authorizer authorizer authorizer.Authorizer
} }
func NewValidator(validationFilter, auditAnnotationFilter, messageFilter cel.Filter, failPolicy *v1.FailurePolicyType, authorizer authorizer.Authorizer) Validator { func NewValidator(validationFilter cel.Filter, celMatcher matchconditions.Matcher, auditAnnotationFilter, messageFilter cel.Filter, failPolicy *v1.FailurePolicyType, authorizer authorizer.Authorizer) Validator {
return &validator{ return &validator{
celMatcher: celMatcher,
validationFilter: validationFilter, validationFilter: validationFilter,
auditAnnotationFilter: auditAnnotationFilter, auditAnnotationFilter: auditAnnotationFilter,
messageFilter: messageFilter, messageFilter: messageFilter,
@ -77,6 +80,26 @@ func (v *validator) Validate(ctx context.Context, versionedAttr *admission.Versi
f = *v.failPolicy f = *v.failPolicy
} }
if v.celMatcher != nil {
matchResults := v.celMatcher.Match(ctx, versionedAttr, versionedParams)
if matchResults.Error != nil {
return ValidateResult{
Decisions: []PolicyDecision{
{
Action: policyDecisionActionForError(f),
Evaluation: EvalError,
Message: matchResults.Error.Error(),
},
},
}
}
// if preconditions are not met, then do not return any validations
if !matchResults.Matches {
return ValidateResult{}
}
}
optionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams, Authorizer: v.authorizer} optionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams, Authorizer: v.authorizer}
expressionOptionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams} expressionOptionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams}
admissionRequest := cel.CreateAdmissionRequest(versionedAttr.Attributes) admissionRequest := cel.CreateAdmissionRequest(versionedAttr.Attributes)

View File

@ -23,16 +23,17 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/require"
celtypes "github.com/google/cel-go/common/types" celtypes "github.com/google/cel-go/common/types"
"github.com/stretchr/testify/require"
admissionv1 "k8s.io/api/admission/v1" admissionv1 "k8s.io/api/admission/v1"
v1 "k8s.io/api/admissionregistration/v1" v1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel" "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
celconfig "k8s.io/apiserver/pkg/apis/cel" celconfig "k8s.io/apiserver/pkg/apis/cel"
apiservercel "k8s.io/apiserver/pkg/cel" apiservercel "k8s.io/apiserver/pkg/cel"
) )
@ -61,6 +62,17 @@ func (f *fakeCelFilter) CompilationErrors() []error {
return []error{} return []error{}
} }
var _ matchconditions.Matcher = &fakeCELMatcher{}
type fakeCELMatcher struct {
error error
matches bool
}
func (f *fakeCELMatcher) Match(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object) matchconditions.MatchResult {
return matchconditions.MatchResult{Matches: f.matches, FailedConditionName: "placeholder", Error: f.error}
}
func TestValidate(t *testing.T) { func TestValidate(t *testing.T) {
ignore := v1.Ignore ignore := v1.Ignore
fail := v1.Fail fail := v1.Fail
@ -74,6 +86,7 @@ func TestValidate(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
failPolicy *v1.FailurePolicyType failPolicy *v1.FailurePolicyType
matcher matchconditions.Matcher
evaluations []cel.EvaluationResult evaluations []cel.EvaluationResult
messageEvaluations []cel.EvaluationResult messageEvaluations []cel.EvaluationResult
auditEvaluations []cel.EvaluationResult auditEvaluations []cel.EvaluationResult
@ -819,11 +832,46 @@ func TestValidate(t *testing.T) {
}, },
costBudget: 1, // shared between expression and messageExpression, needs 1 + 1 = 2 in total costBudget: 1, // shared between expression and messageExpression, needs 1 + 1 = 2 in total
}, },
{
name: "no match surpresses failure",
matcher: &fakeCELMatcher{matches: false},
evaluations: []cel.EvaluationResult{
{
Error: errors.New("expected"),
ExpressionAccessor: &ValidationCondition{},
},
},
policyDecision: []PolicyDecision{},
failPolicy: &fail,
},
{
name: "match error => presumed match",
matcher: &fakeCELMatcher{matches: true, error: fmt.Errorf("test error")},
evaluations: []cel.EvaluationResult{
{
Error: errors.New("expected"),
ExpressionAccessor: &ValidationCondition{},
},
},
policyDecision: []PolicyDecision{
{
Action: ActionDeny,
},
},
failPolicy: &fail,
},
} }
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
var matcher matchconditions.Matcher
if tc.matcher == nil {
matcher = &fakeCELMatcher{matches: true}
} else {
matcher = tc.matcher
}
v := validator{ v := validator{
failPolicy: tc.failPolicy, failPolicy: tc.failPolicy,
celMatcher: matcher,
validationFilter: &fakeCelFilter{ validationFilter: &fakeCelFilter{
evaluations: tc.evaluations, evaluations: tc.evaluations,
throwError: tc.throwError, throwError: tc.throwError,
@ -884,6 +932,7 @@ func TestContextCanceled(t *testing.T) {
f := fc.Compile([]cel.ExpressionAccessor{&ValidationCondition{Expression: "[1,2,3,4,5,6,7,8,9,10].map(x, [1,2,3,4,5,6,7,8,9,10].map(y, x*y)) == []"}}, cel.OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false}, celconfig.PerCallLimit) f := fc.Compile([]cel.ExpressionAccessor{&ValidationCondition{Expression: "[1,2,3,4,5,6,7,8,9,10].map(x, [1,2,3,4,5,6,7,8,9,10].map(y, x*y)) == []"}}, cel.OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false}, celconfig.PerCallLimit)
v := validator{ v := validator{
failPolicy: &fail, failPolicy: &fail,
celMatcher: &fakeCELMatcher{matches: true},
validationFilter: f, validationFilter: f,
messageFilter: f, messageFilter: f,
auditAnnotationFilter: &fakeCelFilter{ auditAnnotationFilter: &fakeCelFilter{

View File

@ -0,0 +1,48 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1alpha1
// MatchConditionApplyConfiguration represents an declarative configuration of the MatchCondition type for use
// with apply.
type MatchConditionApplyConfiguration struct {
Name *string `json:"name,omitempty"`
Expression *string `json:"expression,omitempty"`
}
// MatchConditionApplyConfiguration constructs an declarative configuration of the MatchCondition type for use with
// apply.
func MatchCondition() *MatchConditionApplyConfiguration {
return &MatchConditionApplyConfiguration{}
}
// WithName sets the Name field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Name field is set to the value of the last call.
func (b *MatchConditionApplyConfiguration) WithName(value string) *MatchConditionApplyConfiguration {
b.Name = &value
return b
}
// WithExpression sets the Expression field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Expression field is set to the value of the last call.
func (b *MatchConditionApplyConfiguration) WithExpression(value string) *MatchConditionApplyConfiguration {
b.Expression = &value
return b
}

View File

@ -30,6 +30,7 @@ type ValidatingAdmissionPolicySpecApplyConfiguration struct {
Validations []ValidationApplyConfiguration `json:"validations,omitempty"` Validations []ValidationApplyConfiguration `json:"validations,omitempty"`
FailurePolicy *admissionregistrationv1alpha1.FailurePolicyType `json:"failurePolicy,omitempty"` FailurePolicy *admissionregistrationv1alpha1.FailurePolicyType `json:"failurePolicy,omitempty"`
AuditAnnotations []AuditAnnotationApplyConfiguration `json:"auditAnnotations,omitempty"` AuditAnnotations []AuditAnnotationApplyConfiguration `json:"auditAnnotations,omitempty"`
MatchConditions []MatchConditionApplyConfiguration `json:"matchConditions,omitempty"`
} }
// ValidatingAdmissionPolicySpecApplyConfiguration constructs an declarative configuration of the ValidatingAdmissionPolicySpec type for use with // ValidatingAdmissionPolicySpecApplyConfiguration constructs an declarative configuration of the ValidatingAdmissionPolicySpec type for use with
@ -87,3 +88,16 @@ func (b *ValidatingAdmissionPolicySpecApplyConfiguration) WithAuditAnnotations(v
} }
return b return b
} }
// WithMatchConditions adds the given value to the MatchConditions field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the MatchConditions field.
func (b *ValidatingAdmissionPolicySpecApplyConfiguration) WithMatchConditions(values ...*MatchConditionApplyConfiguration) *ValidatingAdmissionPolicySpecApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithMatchConditions")
}
b.MatchConditions = append(b.MatchConditions, *values[i])
}
return b
}

View File

@ -274,6 +274,17 @@ var schemaYAML = typed.YAMLObject(`types:
type: type:
scalar: string scalar: string
default: "" default: ""
- name: io.k8s.api.admissionregistration.v1alpha1.MatchCondition
map:
fields:
- name: expression
type:
scalar: string
default: ""
- name: name
type:
scalar: string
default: ""
- name: io.k8s.api.admissionregistration.v1alpha1.MatchResources - name: io.k8s.api.admissionregistration.v1alpha1.MatchResources
map: map:
fields: fields:
@ -433,6 +444,14 @@ var schemaYAML = typed.YAMLObject(`types:
- name: failurePolicy - name: failurePolicy
type: type:
scalar: string scalar: string
- name: matchConditions
type:
list:
elementType:
namedType: io.k8s.api.admissionregistration.v1alpha1.MatchCondition
elementRelationship: associative
keys:
- name
- name: matchConstraints - name: matchConstraints
type: type:
namedType: io.k8s.api.admissionregistration.v1alpha1.MatchResources namedType: io.k8s.api.admissionregistration.v1alpha1.MatchResources

View File

@ -145,6 +145,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &admissionregistrationv1alpha1.AuditAnnotationApplyConfiguration{} return &admissionregistrationv1alpha1.AuditAnnotationApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ExpressionWarning"): case v1alpha1.SchemeGroupVersion.WithKind("ExpressionWarning"):
return &admissionregistrationv1alpha1.ExpressionWarningApplyConfiguration{} return &admissionregistrationv1alpha1.ExpressionWarningApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("MatchCondition"):
return &admissionregistrationv1alpha1.MatchConditionApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("MatchResources"): case v1alpha1.SchemeGroupVersion.WithKind("MatchResources"):
return &admissionregistrationv1alpha1.MatchResourcesApplyConfiguration{} return &admissionregistrationv1alpha1.MatchResourcesApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("NamedRuleWithOperations"): case v1alpha1.SchemeGroupVersion.WithKind("NamedRuleWithOperations"):