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
24 changed files with 1173 additions and 120 deletions

View File

@@ -206,6 +206,24 @@ type ValidatingAdmissionPolicySpec struct {
// +optional
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
// occur from CEL expression parse errors, type check errors, runtime errors and invalid
// or mis-configured policy definitions or bindings.

View File

@@ -59,6 +59,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
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 {
return Convert_v1alpha1_MatchResources_To_admissionregistration_MatchResources(a.(*v1alpha1.MatchResources), b.(*admissionregistration.MatchResources), scope)
}); err != nil {
@@ -236,6 +246,28 @@ func Convert_admissionregistration_ExpressionWarning_To_v1alpha1_ExpressionWarni
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 {
out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector))
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.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy))
out.AuditAnnotations = *(*[]admissionregistration.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations))
out.MatchConditions = *(*[]admissionregistration.MatchCondition)(unsafe.Pointer(&in.MatchConditions))
return nil
}
@@ -612,6 +645,7 @@ func autoConvert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1
out.MatchConstraints = nil
}
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.AuditAnnotations = *(*[]v1alpha1.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations))
return nil

View File

@@ -213,6 +213,7 @@ func validateAdmissionReviewVersions(versions []string, requireRecognizedAdmissi
func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList {
return validateValidatingWebhookConfiguration(e, validationOptions{
ignoreMatchConditions: false,
allowParamsInMatchConditions: false,
requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: true,
@@ -241,6 +242,7 @@ func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW
func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration) field.ErrorList {
return validateMutatingWebhookConfiguration(e, validationOptions{
ignoreMatchConditions: false,
allowParamsInMatchConditions: false,
requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: true,
@@ -250,6 +252,7 @@ func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebho
type validationOptions struct {
ignoreMatchConditions bool
allowParamsInMatchConditions bool
requireNoSideEffects bool
requireRecognizedAdmissionReviewVersion bool
requireUniqueWebhookNames bool
@@ -324,7 +327,7 @@ func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, op
}
if !opts.ignoreMatchConditions {
allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, fldPath.Child("matchConditions"))...)
allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, opts, fldPath.Child("matchConditions"))...)
}
return allErrors
@@ -382,7 +385,7 @@ func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, opts v
}
if !opts.ignoreMatchConditions {
allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, fldPath.Child("matchConditions"))...)
allErrors = append(allErrors, validateMatchConditions(hook.MatchConditions, opts, fldPath.Child("matchConditions"))...)
}
return allErrors
@@ -520,6 +523,17 @@ func ignoreValidatingWebhookMatchConditions(new, old []admissionregistration.Val
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
func mutatingHasUniqueWebhookNames(webhooks []admissionregistration.MutatingWebhook) bool {
names := sets.NewString()
@@ -610,6 +624,7 @@ func mutatingWebhookHasInvalidLabelValueInSelector(webhooks []admissionregistrat
func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList {
return validateValidatingWebhookConfiguration(newC, validationOptions{
ignoreMatchConditions: ignoreValidatingWebhookMatchConditions(newC.Webhooks, oldC.Webhooks),
allowParamsInMatchConditions: false,
requireNoSideEffects: validatingHasNoSideEffects(oldC.Webhooks),
requireRecognizedAdmissionReviewVersion: validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks),
requireUniqueWebhookNames: validatingHasUniqueWebhookNames(oldC.Webhooks),
@@ -621,6 +636,7 @@ func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistrat
func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration) field.ErrorList {
return validateMutatingWebhookConfiguration(newC, validationOptions{
ignoreMatchConditions: ignoreMutatingWebhookMatchConditions(newC.Webhooks, oldC.Webhooks),
allowParamsInMatchConditions: false,
requireNoSideEffects: mutatingHasNoSideEffects(oldC.Webhooks),
requireRecognizedAdmissionReviewVersion: mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks),
requireUniqueWebhookNames: mutatingHasUniqueWebhookNames(oldC.Webhooks),
@@ -638,16 +654,16 @@ const (
// ValidateValidatingAdmissionPolicy validates a ValidatingAdmissionPolicy before creation.
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 = append(allErrors, validateValidatingAdmissionPolicySpec(p.ObjectMeta, &p.Spec, field.NewPath("spec"))...)
allErrors = append(allErrors, validateValidatingAdmissionPolicySpec(p.ObjectMeta, &p.Spec, opts, field.NewPath("spec"))...)
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
if spec.FailurePolicy == nil {
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()))
}
if spec.ParamKind != nil {
opts.allowParamsInMatchConditions = true
allErrors = append(allErrors, validateParamKind(*spec.ParamKind, fldPath.Child("paramKind"))...)
}
if spec.MatchConstraints == nil {
@@ -666,6 +683,9 @@ func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissi
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 {
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"))
@@ -822,14 +842,14 @@ func validateNamedRuleWithOperations(n *admissionregistration.NamedRuleWithOpera
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
conditionNames := sets.NewString()
if len(m) > 64 {
allErrors = append(allErrors, field.TooMany(fldPath, len(m), 64))
}
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 conditionNames.Has(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
}
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
trimmedExpression := strings.TrimSpace(v.Expression)
if len(trimmedExpression) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("expression"), ""))
} else {
allErrors = append(allErrors, validateCELExpression(trimmedExpression, plugincel.OptionalVariableDeclarations{
HasParams: false,
HasParams: opts.allowParamsInMatchConditions,
HasAuthorizer: true,
}, fldPath.Child("expression"))...)
}
@@ -995,7 +1015,7 @@ func validateParamRef(pr *admissionregistration.ParamRef, fldPath *field.Path) f
// ValidateValidatingAdmissionPolicyUpdate validates update of validating admission policy
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

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>`,
},
{
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 {
t.Run(test.name, func(t *testing.T) {
@@ -3293,6 +3418,202 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) {
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
}
for _, test := range tests {

View File

@@ -580,6 +580,11 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopyInto(out *ValidatingAdmissionPo
(*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 {
in, out := &in.FailurePolicy, &out.FailurePolicy
*out = new(FailurePolicyType)