Merge pull request #119409 from alexzielenski/apiserver/policy/vap-tests
Add test cases for ValidatingAdmissionPolicy
This commit is contained in:
		@@ -1206,7 +1206,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	genericfeatures.APIResponseCompression: {Default: true, PreRelease: featuregate.Beta},
 | 
						genericfeatures.APIResponseCompression: {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	genericfeatures.ValidatingAdmissionPolicy: {Default: false, PreRelease: featuregate.Alpha},
 | 
						genericfeatures.ValidatingAdmissionPolicy: {Default: false, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	genericfeatures.CustomResourceValidationExpressions: {Default: true, PreRelease: featuregate.Beta},
 | 
						genericfeatures.CustomResourceValidationExpressions: {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/google/cel-go/cel"
 | 
						"github.com/google/cel-go/cel"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/admission"
 | 
						"k8s.io/apiserver/pkg/admission"
 | 
				
			||||||
	celconfig "k8s.io/apiserver/pkg/apis/cel"
 | 
						celconfig "k8s.io/apiserver/pkg/apis/cel"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/cel/environment"
 | 
						"k8s.io/apiserver/pkg/cel/environment"
 | 
				
			||||||
@@ -141,7 +142,7 @@ func TestCompositedPolicies(t *testing.T) {
 | 
				
			|||||||
			if costBudget == 0 {
 | 
								if costBudget == 0 {
 | 
				
			||||||
				costBudget = celconfig.RuntimeCELCostBudget
 | 
									costBudget = celconfig.RuntimeCELCostBudget
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			result, _, err := f.ForInput(context.Background(), versionedAttr, CreateAdmissionRequest(versionedAttr.Attributes), optionalVars, nil, costBudget)
 | 
								result, _, err := f.ForInput(context.Background(), versionedAttr, CreateAdmissionRequest(versionedAttr.Attributes, v1.GroupVersionResource(tc.attributes.GetResource()), v1.GroupVersionKind(versionedAttr.VersionedKind)), optionalVars, nil, costBudget)
 | 
				
			||||||
			if !tc.expectErr && err != nil {
 | 
								if !tc.expectErr && err != nil {
 | 
				
			||||||
				t.Fatalf("failed evaluation: %v", err)
 | 
									t.Fatalf("failed evaluation: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -253,10 +253,13 @@ func (f *filter) ForInput(ctx context.Context, versionedAttr *admission.Versione
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: to reuse https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go#L154
 | 
					// TODO: to reuse https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request/admissionreview.go#L154
 | 
				
			||||||
func CreateAdmissionRequest(attr admission.Attributes) *admissionv1.AdmissionRequest {
 | 
					func CreateAdmissionRequest(attr admission.Attributes, equivalentGVR metav1.GroupVersionResource, equivalentKind metav1.GroupVersionKind) *admissionv1.AdmissionRequest {
 | 
				
			||||||
	// FIXME: how to get resource GVK, GVR and subresource?
 | 
						// Attempting to use same logic as webhook for constructing resource
 | 
				
			||||||
	gvk := attr.GetKind()
 | 
						// GVK, GVR, subresource
 | 
				
			||||||
	gvr := attr.GetResource()
 | 
						// Use the GVK, GVR that the matcher decided was equivalent to that of the request
 | 
				
			||||||
 | 
						// https://github.com/kubernetes/kubernetes/blob/90c362b3430bcbbf8f245fadbcd521dab39f1d7c/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go#L182-L210
 | 
				
			||||||
 | 
						gvk := equivalentKind
 | 
				
			||||||
 | 
						gvr := equivalentGVR
 | 
				
			||||||
	subresource := attr.GetSubresource()
 | 
						subresource := attr.GetSubresource()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	requestGVK := attr.GetKind()
 | 
						requestGVK := attr.GetKind()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -787,7 +787,7 @@ func TestFilter(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			optionalVars := OptionalVariableBindings{VersionedParams: tc.params, Authorizer: tc.authorizer}
 | 
								optionalVars := OptionalVariableBindings{VersionedParams: tc.params, Authorizer: tc.authorizer}
 | 
				
			||||||
			ctx := context.TODO()
 | 
								ctx := context.TODO()
 | 
				
			||||||
			evalResults, _, err := f.ForInput(ctx, versionedAttr, CreateAdmissionRequest(versionedAttr.Attributes), optionalVars, CreateNamespaceObject(tc.namespaceObject), celconfig.RuntimeCELCostBudget)
 | 
								evalResults, _, err := f.ForInput(ctx, versionedAttr, CreateAdmissionRequest(versionedAttr.Attributes, metav1.GroupVersionResource(versionedAttr.GetResource()), metav1.GroupVersionKind(versionedAttr.VersionedKind)), optionalVars, CreateNamespaceObject(tc.namespaceObject), celconfig.RuntimeCELCostBudget)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				t.Fatalf("unexpected error: %v", err)
 | 
									t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -933,7 +933,7 @@ func TestRuntimeCELCostBudget(t *testing.T) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			optionalVars := OptionalVariableBindings{VersionedParams: tc.params, Authorizer: tc.authorizer}
 | 
								optionalVars := OptionalVariableBindings{VersionedParams: tc.params, Authorizer: tc.authorizer}
 | 
				
			||||||
			ctx := context.TODO()
 | 
								ctx := context.TODO()
 | 
				
			||||||
			evalResults, remaining, err := f.ForInput(ctx, versionedAttr, CreateAdmissionRequest(versionedAttr.Attributes), optionalVars, nil, tc.testRuntimeCELCostBudget)
 | 
								evalResults, remaining, err := f.ForInput(ctx, versionedAttr, CreateAdmissionRequest(versionedAttr.Attributes, metav1.GroupVersionResource(versionedAttr.GetResource()), metav1.GroupVersionKind(versionedAttr.VersionedKind)), optionalVars, nil, tc.testRuntimeCELCostBudget)
 | 
				
			||||||
			if tc.exceedBudget && err == nil {
 | 
								if tc.exceedBudget && err == nil {
 | 
				
			||||||
				t.Errorf("Expected RuntimeCELCostBudge to be exceeded but got nil")
 | 
									t.Errorf("Expected RuntimeCELCostBudge to be exceeded but got nil")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -318,10 +318,10 @@ var _ Validator = &fakeValidator{}
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type fakeValidator struct {
 | 
					type fakeValidator struct {
 | 
				
			||||||
	validationFilter, auditAnnotationFilter, messageFilter *fakeFilter
 | 
						validationFilter, auditAnnotationFilter, messageFilter *fakeFilter
 | 
				
			||||||
	ValidateFunc                                           func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult
 | 
						ValidateFunc                                           func(ctx context.Context, matchResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *fakeValidator) RegisterDefinition(definition *v1beta1.ValidatingAdmissionPolicy, validateFunc func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult) {
 | 
					func (f *fakeValidator) RegisterDefinition(definition *v1beta1.ValidatingAdmissionPolicy, validateFunc func(ctx context.Context, matchResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult) {
 | 
				
			||||||
	//Key must be something that we can decipher from the inputs to Validate so using message which will be on the validationCondition object of evalResult
 | 
						//Key must be something that we can decipher from the inputs to Validate so using message which will be on the validationCondition object of evalResult
 | 
				
			||||||
	var key string
 | 
						var key string
 | 
				
			||||||
	if len(definition.Spec.Validations) > 0 {
 | 
						if len(definition.Spec.Validations) > 0 {
 | 
				
			||||||
@@ -338,8 +338,8 @@ func (f *fakeValidator) RegisterDefinition(definition *v1beta1.ValidatingAdmissi
 | 
				
			|||||||
	validatorMap[key] = f
 | 
						validatorMap[key] = f
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *fakeValidator) Validate(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
					func (f *fakeValidator) Validate(ctx context.Context, matchResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
	return f.ValidateFunc(ctx, versionedAttr, versionedParams, namespace, runtimeCELCostBudget, authz)
 | 
						return f.ValidateFunc(ctx, matchResource, versionedAttr, versionedParams, namespace, runtimeCELCostBudget, authz)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ Matcher = &fakeMatcher{}
 | 
					var _ Matcher = &fakeMatcher{}
 | 
				
			||||||
@@ -390,18 +390,18 @@ func (f *fakeMatcher) RegisterBinding(binding *v1beta1.ValidatingAdmissionPolicy
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Matches says whether this policy definition matches the provided admission
 | 
					// Matches says whether this policy definition matches the provided admission
 | 
				
			||||||
// resource request
 | 
					// resource request
 | 
				
			||||||
func (f *fakeMatcher) DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition *v1beta1.ValidatingAdmissionPolicy) (bool, schema.GroupVersionKind, error) {
 | 
					func (f *fakeMatcher) DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition *v1beta1.ValidatingAdmissionPolicy) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error) {
 | 
				
			||||||
	namespace, name := definition.Namespace, definition.Name
 | 
						namespace, name := definition.Namespace, definition.Name
 | 
				
			||||||
	key := namespacedName{
 | 
						key := namespacedName{
 | 
				
			||||||
		name:      name,
 | 
							name:      name,
 | 
				
			||||||
		namespace: namespace,
 | 
							namespace: namespace,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if fun, ok := f.DefinitionMatchFuncs[key]; ok {
 | 
						if fun, ok := f.DefinitionMatchFuncs[key]; ok {
 | 
				
			||||||
		return fun(definition, a), a.GetKind(), nil
 | 
							return fun(definition, a), a.GetResource(), a.GetKind(), nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Default is match everything
 | 
						// Default is match everything
 | 
				
			||||||
	return f.DefaultMatch, a.GetKind(), nil
 | 
						return f.DefaultMatch, a.GetResource(), a.GetKind(), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Matches says whether this policy definition matches the provided admission
 | 
					// Matches says whether this policy definition matches the provided admission
 | 
				
			||||||
@@ -834,7 +834,7 @@ func TestBasicPolicyDefinitionFailure(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -904,7 +904,7 @@ func TestDefinitionDoesntMatch(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1019,7 +1019,7 @@ func TestReconfigureBinding(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1130,7 +1130,7 @@ func TestRemoveDefinition(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1199,7 +1199,7 @@ func TestRemoveBinding(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1309,7 +1309,7 @@ func TestInvalidParamSourceInstanceName(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1370,7 +1370,7 @@ func TestEmptyParamRef(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		// Versioned params must be nil to pass the test
 | 
							// Versioned params must be nil to pass the test
 | 
				
			||||||
		if versionedParams != nil {
 | 
							if versionedParams != nil {
 | 
				
			||||||
			return ValidateResult{
 | 
								return ValidateResult{
 | 
				
			||||||
@@ -1447,7 +1447,7 @@ func TestEmptyParamSource(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1549,7 +1549,7 @@ func TestMultiplePoliciesSharedParamType(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator1.RegisterDefinition(&policy1, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator1.RegisterDefinition(&policy1, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		evaluations1.Add(1)
 | 
							evaluations1.Add(1)
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
@@ -1568,7 +1568,7 @@ func TestMultiplePoliciesSharedParamType(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator2.RegisterDefinition(&policy2, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator2.RegisterDefinition(&policy2, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		evaluations2.Add(1)
 | 
							evaluations2.Add(1)
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
@@ -1678,7 +1678,7 @@ func TestNativeTypeParam(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		evaluations.Add(1)
 | 
							evaluations.Add(1)
 | 
				
			||||||
		if _, ok := versionedParams.(*v1.ConfigMap); ok {
 | 
							if _, ok := versionedParams.(*v1.ConfigMap); ok {
 | 
				
			||||||
			return ValidateResult{
 | 
								return ValidateResult{
 | 
				
			||||||
@@ -1760,7 +1760,7 @@ func TestAuditValidationAction(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1831,7 +1831,7 @@ func TestWarnValidationAction(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1890,7 +1890,7 @@ func TestAllValidationActions(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -1977,7 +1977,7 @@ func TestNamespaceParamRefName(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	lock := sync.Mutex{}
 | 
						lock := sync.Mutex{}
 | 
				
			||||||
	observedParamNamespaces := []string{}
 | 
						observedParamNamespaces := []string{}
 | 
				
			||||||
	validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		lock.Lock()
 | 
							lock.Lock()
 | 
				
			||||||
		defer lock.Unlock()
 | 
							defer lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2264,7 +2264,7 @@ func testParamRefCase(t *testing.T, paramIsClusterScoped, nameIsSet, namespaceIs
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(&policy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(&policy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		observeParam(versionedParams)
 | 
							observeParam(versionedParams)
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
@@ -2505,7 +2505,7 @@ func TestNamespaceParamRefClusterScopedParamError(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		evaluations.Add(1)
 | 
							evaluations.Add(1)
 | 
				
			||||||
		if _, ok := versionedParams.(*v1beta1.ValidatingAdmissionPolicy); ok {
 | 
							if _, ok := versionedParams.(*v1beta1.ValidatingAdmissionPolicy); ok {
 | 
				
			||||||
			return ValidateResult{
 | 
								return ValidateResult{
 | 
				
			||||||
@@ -2570,7 +2570,7 @@ func TestAuditAnnotations(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
						validator.RegisterDefinition(denyPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
		o, err := meta.Accessor(versionedParams)
 | 
							o, err := meta.Accessor(versionedParams)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								t.Fatal(err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -244,7 +244,7 @@ func (c *celAdmissionController) Validate(
 | 
				
			|||||||
		var versionedAttr *admission.VersionedAttributes
 | 
							var versionedAttr *admission.VersionedAttributes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		definition := definitionInfo.lastReconciledValue
 | 
							definition := definitionInfo.lastReconciledValue
 | 
				
			||||||
		matches, matchKind, err := c.policyController.matcher.DefinitionMatches(a, o, definition)
 | 
							matches, matchResource, matchKind, err := c.policyController.matcher.DefinitionMatches(a, o, definition)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			// Configuration error.
 | 
								// Configuration error.
 | 
				
			||||||
			addConfigError(err, definition, nil)
 | 
								addConfigError(err, definition, nil)
 | 
				
			||||||
@@ -323,7 +323,7 @@ func (c *celAdmissionController) Validate(
 | 
				
			|||||||
						nested: param,
 | 
											nested: param,
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				validationResults = append(validationResults, bindingInfo.validator.Validate(ctx, versionedAttr, p, namespace, celconfig.RuntimeCELCostBudget, authz))
 | 
									validationResults = append(validationResults, bindingInfo.validator.Validate(ctx, matchResource, versionedAttr, p, namespace, celconfig.RuntimeCELCostBudget, authz))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for _, validationResult := range validationResults {
 | 
								for _, validationResult := range validationResults {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,7 +86,7 @@ type Matcher interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// DefinitionMatches says whether this policy definition matches the provided admission
 | 
						// DefinitionMatches says whether this policy definition matches the provided admission
 | 
				
			||||||
	// resource request
 | 
						// resource request
 | 
				
			||||||
	DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition *v1beta1.ValidatingAdmissionPolicy) (bool, schema.GroupVersionKind, error)
 | 
						DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition *v1beta1.ValidatingAdmissionPolicy) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// BindingMatches says whether this policy definition matches the provided admission
 | 
						// BindingMatches says whether this policy definition matches the provided admission
 | 
				
			||||||
	// resource request
 | 
						// resource request
 | 
				
			||||||
@@ -109,5 +109,5 @@ type ValidateResult struct {
 | 
				
			|||||||
type Validator interface {
 | 
					type Validator interface {
 | 
				
			||||||
	// Validate is used to take cel evaluations and convert into decisions
 | 
						// Validate is used to take cel evaluations and convert into decisions
 | 
				
			||||||
	// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
 | 
						// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
 | 
				
			||||||
	Validate(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *corev1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult
 | 
						Validate(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *corev1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,7 +63,7 @@ func (c *matcher) ValidateInitialization() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefinitionMatches returns whether this ValidatingAdmissionPolicy matches the provided admission resource request
 | 
					// DefinitionMatches returns whether this ValidatingAdmissionPolicy matches the provided admission resource request
 | 
				
			||||||
func (c *matcher) DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition *v1beta1.ValidatingAdmissionPolicy) (bool, schema.GroupVersionKind, error) {
 | 
					func (c *matcher) DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition *v1beta1.ValidatingAdmissionPolicy) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error) {
 | 
				
			||||||
	criteria := matchCriteria{constraints: definition.Spec.MatchConstraints}
 | 
						criteria := matchCriteria{constraints: definition.Spec.MatchConstraints}
 | 
				
			||||||
	return c.Matcher.Matches(a, o, &criteria)
 | 
						return c.Matcher.Matches(a, o, &criteria)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -74,7 +74,7 @@ func (c *matcher) BindingMatches(a admission.Attributes, o admission.ObjectInter
 | 
				
			|||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	criteria := matchCriteria{constraints: binding.Spec.MatchResources}
 | 
						criteria := matchCriteria{constraints: binding.Spec.MatchResources}
 | 
				
			||||||
	isMatch, _, err := c.Matcher.Matches(a, o, &criteria)
 | 
						isMatch, _, _, err := c.Matcher.Matches(a, o, &criteria)
 | 
				
			||||||
	return isMatch, err
 | 
						return isMatch, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,56 +71,60 @@ func (m *Matcher) ValidateInitialization() error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *Matcher) Matches(attr admission.Attributes, o admission.ObjectInterfaces, criteria MatchCriteria) (bool, schema.GroupVersionKind, error) {
 | 
					func (m *Matcher) Matches(attr admission.Attributes, o admission.ObjectInterfaces, criteria MatchCriteria) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error) {
 | 
				
			||||||
	matches, matchNsErr := m.namespaceMatcher.MatchNamespaceSelector(criteria, attr)
 | 
						matches, matchNsErr := m.namespaceMatcher.MatchNamespaceSelector(criteria, attr)
 | 
				
			||||||
	// Should not return an error here for policy which do not apply to the request, even if err is an unexpected scenario.
 | 
						// Should not return an error here for policy which do not apply to the request, even if err is an unexpected scenario.
 | 
				
			||||||
	if !matches && matchNsErr == nil {
 | 
						if !matches && matchNsErr == nil {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, nil
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	matches, matchObjErr := m.objectMatcher.MatchObjectSelector(criteria, attr)
 | 
						matches, matchObjErr := m.objectMatcher.MatchObjectSelector(criteria, attr)
 | 
				
			||||||
	// Should not return an error here for policy which do not apply to the request, even if err is an unexpected scenario.
 | 
						// Should not return an error here for policy which do not apply to the request, even if err is an unexpected scenario.
 | 
				
			||||||
	if !matches && matchObjErr == nil {
 | 
						if !matches && matchObjErr == nil {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, nil
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	matchResources := criteria.GetMatchResources()
 | 
						matchResources := criteria.GetMatchResources()
 | 
				
			||||||
	matchPolicy := matchResources.MatchPolicy
 | 
						matchPolicy := matchResources.MatchPolicy
 | 
				
			||||||
	if isExcluded, _, err := matchesResourceRules(matchResources.ExcludeResourceRules, matchPolicy, attr, o); isExcluded || err != nil {
 | 
						if isExcluded, _, _, err := matchesResourceRules(matchResources.ExcludeResourceRules, matchPolicy, attr, o); isExcluded || err != nil {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, err
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		isMatch   bool
 | 
							isMatch       bool
 | 
				
			||||||
		matchKind schema.GroupVersionKind
 | 
							matchResource schema.GroupVersionResource
 | 
				
			||||||
		matchErr  error
 | 
							matchKind     schema.GroupVersionKind
 | 
				
			||||||
 | 
							matchErr      error
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if len(matchResources.ResourceRules) == 0 {
 | 
						if len(matchResources.ResourceRules) == 0 {
 | 
				
			||||||
		isMatch = true
 | 
							isMatch = true
 | 
				
			||||||
		matchKind = attr.GetKind()
 | 
							matchKind = attr.GetKind()
 | 
				
			||||||
 | 
							matchResource = attr.GetResource()
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		isMatch, matchKind, matchErr = matchesResourceRules(matchResources.ResourceRules, matchPolicy, attr, o)
 | 
							isMatch, matchResource, matchKind, matchErr = matchesResourceRules(matchResources.ResourceRules, matchPolicy, attr, o)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if matchErr != nil {
 | 
						if matchErr != nil {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, matchErr
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, matchErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !isMatch {
 | 
						if !isMatch {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, nil
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// now that we know this applies to this request otherwise, if there were selector errors, return them
 | 
						// now that we know this applies to this request otherwise, if there were selector errors, return them
 | 
				
			||||||
	if matchNsErr != nil {
 | 
						if matchNsErr != nil {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, matchNsErr
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, matchNsErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if matchObjErr != nil {
 | 
						if matchObjErr != nil {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, matchObjErr
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, matchObjErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true, matchKind, nil
 | 
						return true, matchResource, matchKind, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPolicy *v1beta1.MatchPolicyType, attr admission.Attributes, o admission.ObjectInterfaces) (bool, schema.GroupVersionKind, error) {
 | 
					func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPolicy *v1beta1.MatchPolicyType, attr admission.Attributes, o admission.ObjectInterfaces) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error) {
 | 
				
			||||||
	matchKind := attr.GetKind()
 | 
						matchKind := attr.GetKind()
 | 
				
			||||||
 | 
						matchResource := attr.GetResource()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, namedRule := range namedRules {
 | 
						for _, namedRule := range namedRules {
 | 
				
			||||||
		rule := v1.RuleWithOperations(namedRule.RuleWithOperations)
 | 
							rule := v1.RuleWithOperations(namedRule.RuleWithOperations)
 | 
				
			||||||
		ruleMatcher := rules.Matcher{
 | 
							ruleMatcher := rules.Matcher{
 | 
				
			||||||
@@ -132,14 +136,14 @@ func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPol
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		// an empty name list always matches
 | 
							// an empty name list always matches
 | 
				
			||||||
		if len(namedRule.ResourceNames) == 0 {
 | 
							if len(namedRule.ResourceNames) == 0 {
 | 
				
			||||||
			return true, matchKind, nil
 | 
								return true, matchResource, matchKind, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// TODO: GetName() can return an empty string if the user is relying on
 | 
							// TODO: GetName() can return an empty string if the user is relying on
 | 
				
			||||||
		// the API server to generate the name... figure out what to do for this edge case
 | 
							// the API server to generate the name... figure out what to do for this edge case
 | 
				
			||||||
		name := attr.GetName()
 | 
							name := attr.GetName()
 | 
				
			||||||
		for _, matchedName := range namedRule.ResourceNames {
 | 
							for _, matchedName := range namedRule.ResourceNames {
 | 
				
			||||||
			if name == matchedName {
 | 
								if name == matchedName {
 | 
				
			||||||
				return true, matchKind, nil
 | 
									return true, matchResource, matchKind, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -147,7 +151,7 @@ func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPol
 | 
				
			|||||||
	// if match policy is undefined or exact, don't perform fuzzy matching
 | 
						// if match policy is undefined or exact, don't perform fuzzy matching
 | 
				
			||||||
	// note that defaulting to fuzzy matching is set by the API
 | 
						// note that defaulting to fuzzy matching is set by the API
 | 
				
			||||||
	if matchPolicy == nil || *matchPolicy == v1beta1.Exact {
 | 
						if matchPolicy == nil || *matchPolicy == v1beta1.Exact {
 | 
				
			||||||
		return false, schema.GroupVersionKind{}, nil
 | 
							return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	attrWithOverride := &attrWithResourceOverride{Attributes: attr}
 | 
						attrWithOverride := &attrWithResourceOverride{Attributes: attr}
 | 
				
			||||||
@@ -169,11 +173,11 @@ func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPol
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			matchKind = o.GetEquivalentResourceMapper().KindFor(equivalent, attr.GetSubresource())
 | 
								matchKind = o.GetEquivalentResourceMapper().KindFor(equivalent, attr.GetSubresource())
 | 
				
			||||||
			if matchKind.Empty() {
 | 
								if matchKind.Empty() {
 | 
				
			||||||
				return false, schema.GroupVersionKind{}, fmt.Errorf("unable to convert to %v: unknown kind", equivalent)
 | 
									return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, fmt.Errorf("unable to convert to %v: unknown kind", equivalent)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// an empty name list always matches
 | 
								// an empty name list always matches
 | 
				
			||||||
			if len(namedRule.ResourceNames) == 0 {
 | 
								if len(namedRule.ResourceNames) == 0 {
 | 
				
			||||||
				return true, matchKind, nil
 | 
									return true, equivalent, matchKind, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// TODO: GetName() can return an empty string if the user is relying on
 | 
								// TODO: GetName() can return an empty string if the user is relying on
 | 
				
			||||||
@@ -181,12 +185,12 @@ func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPol
 | 
				
			|||||||
			name := attr.GetName()
 | 
								name := attr.GetName()
 | 
				
			||||||
			for _, matchedName := range namedRule.ResourceNames {
 | 
								for _, matchedName := range namedRule.ResourceNames {
 | 
				
			||||||
				if name == matchedName {
 | 
									if name == matchedName {
 | 
				
			||||||
					return true, matchKind, nil
 | 
										return true, equivalent, matchKind, nil
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return false, schema.GroupVersionKind{}, nil
 | 
						return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type attrWithResourceOverride struct {
 | 
					type attrWithResourceOverride struct {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,9 +98,10 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
		criteria *v1beta1.MatchResources
 | 
							criteria *v1beta1.MatchResources
 | 
				
			||||||
		attrs    admission.Attributes
 | 
							attrs    admission.Attributes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		expectMatches   bool
 | 
							expectMatches       bool
 | 
				
			||||||
		expectMatchKind schema.GroupVersionKind
 | 
							expectMatchKind     schema.GroupVersionKind
 | 
				
			||||||
		expectErr       string
 | 
							expectMatchResource schema.GroupVersionResource
 | 
				
			||||||
 | 
							expectErr           string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name:          "no rules (just write)",
 | 
								name:          "no rules (just write)",
 | 
				
			||||||
@@ -204,9 +205,10 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
 | 
											Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				}}},
 | 
									}}},
 | 
				
			||||||
			attrs:           admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
								attrs:               admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
				
			||||||
			expectMatches:   true,
 | 
								expectMatches:       true,
 | 
				
			||||||
			expectMatchKind: gvk("extensions", "v1beta1", "Deployment"),
 | 
								expectMatchResource: gvr("extensions", "v1beta1", "deployments"),
 | 
				
			||||||
 | 
								expectMatchKind:     gvk("extensions", "v1beta1", "Deployment"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "specific rules, equivalent match, prefer apps",
 | 
								name: "specific rules, equivalent match, prefer apps",
 | 
				
			||||||
@@ -225,9 +227,10 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
 | 
											Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				}}},
 | 
									}}},
 | 
				
			||||||
			attrs:           admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
								attrs:               admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
				
			||||||
			expectMatches:   true,
 | 
								expectMatches:       true,
 | 
				
			||||||
			expectMatchKind: gvk("apps", "v1beta1", "Deployment"),
 | 
								expectMatchResource: gvr("apps", "v1beta1", "deployments"),
 | 
				
			||||||
 | 
								expectMatchKind:     gvk("apps", "v1beta1", "Deployment"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -311,9 +314,10 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
 | 
											Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				}}},
 | 
									}}},
 | 
				
			||||||
			attrs:           admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
								attrs:               admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
				
			||||||
			expectMatches:   true,
 | 
								expectMatches:       true,
 | 
				
			||||||
			expectMatchKind: gvk("extensions", "v1beta1", "Scale"),
 | 
								expectMatchResource: gvr("extensions", "v1beta1", "deployments"),
 | 
				
			||||||
 | 
								expectMatchKind:     gvk("extensions", "v1beta1", "Scale"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "specific rules, subresource equivalent match, prefer apps",
 | 
								name: "specific rules, subresource equivalent match, prefer apps",
 | 
				
			||||||
@@ -332,9 +336,10 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
 | 
											Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				}}},
 | 
									}}},
 | 
				
			||||||
			attrs:           admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
								attrs:               admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
				
			||||||
			expectMatches:   true,
 | 
								expectMatches:       true,
 | 
				
			||||||
			expectMatchKind: gvk("apps", "v1beta1", "Scale"),
 | 
								expectMatchResource: gvr("apps", "v1beta1", "deployments"),
 | 
				
			||||||
 | 
								expectMatchKind:     gvk("apps", "v1beta1", "Scale"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "specific rules, prefer exact match and name match",
 | 
								name: "specific rules, prefer exact match and name match",
 | 
				
			||||||
@@ -380,9 +385,10 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
 | 
											Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				}}},
 | 
									}}},
 | 
				
			||||||
			attrs:           admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("extensions", "v1beta1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
								attrs:               admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("extensions", "v1beta1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
 | 
				
			||||||
			expectMatches:   true,
 | 
								expectMatches:       true,
 | 
				
			||||||
			expectMatchKind: gvk("autoscaling", "v1", "Scale"),
 | 
								expectMatchResource: gvr("apps", "v1", "deployments"),
 | 
				
			||||||
 | 
								expectMatchKind:     gvk("autoscaling", "v1", "Scale"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "specific rules, subresource equivalent match, prefer extensions and name match miss",
 | 
								name: "specific rules, subresource equivalent match, prefer extensions and name match miss",
 | 
				
			||||||
@@ -536,7 +542,7 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, testcase := range testcases {
 | 
						for _, testcase := range testcases {
 | 
				
			||||||
		t.Run(testcase.name, func(t *testing.T) {
 | 
							t.Run(testcase.name, func(t *testing.T) {
 | 
				
			||||||
			matches, matchKind, err := a.Matches(testcase.attrs, interfaces, &fakeCriteria{matchResources: *testcase.criteria})
 | 
								matches, matchResource, matchKind, err := a.Matches(testcase.attrs, interfaces, &fakeCriteria{matchResources: *testcase.criteria})
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				if len(testcase.expectErr) == 0 {
 | 
									if len(testcase.expectErr) == 0 {
 | 
				
			||||||
					t.Fatal(err)
 | 
										t.Fatal(err)
 | 
				
			||||||
@@ -558,6 +564,22 @@ func TestMatcher(t *testing.T) {
 | 
				
			|||||||
			if matches != testcase.expectMatches {
 | 
								if matches != testcase.expectMatches {
 | 
				
			||||||
				t.Fatalf("expected matches = %v; got %v", testcase.expectMatches, matches)
 | 
									t.Fatalf("expected matches = %v; got %v", testcase.expectMatches, matches)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								expectResource := testcase.expectMatchResource
 | 
				
			||||||
 | 
								if !expectResource.Empty() && !matches {
 | 
				
			||||||
 | 
									t.Fatalf("expectResource is non-empty, but did not match")
 | 
				
			||||||
 | 
								} else if expectResource.Empty() {
 | 
				
			||||||
 | 
									// Test for exact match by default. Tests that expect an equivalent
 | 
				
			||||||
 | 
									// resource to match should explicitly state so by supplying
 | 
				
			||||||
 | 
									// expectMatchResource
 | 
				
			||||||
 | 
									expectResource = testcase.attrs.GetResource()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if matches {
 | 
				
			||||||
 | 
									if matchResource != expectResource {
 | 
				
			||||||
 | 
										t.Fatalf("expected matchResource %v, got %v", expectResource, matchResource)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import (
 | 
				
			|||||||
	corev1 "k8s.io/api/core/v1"
 | 
						corev1 "k8s.io/api/core/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"
 | 
				
			||||||
 | 
						"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"
 | 
						"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
 | 
				
			||||||
@@ -72,7 +73,7 @@ func auditAnnotationEvaluationForError(f v1.FailurePolicyType) PolicyAuditAnnota
 | 
				
			|||||||
// Validate takes a list of Evaluation and a failure policy and converts them into actionable PolicyDecisions
 | 
					// Validate takes a list of Evaluation and a failure policy and converts them into actionable PolicyDecisions
 | 
				
			||||||
// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
 | 
					// runtimeCELCostBudget was added for testing purpose only. Callers should always use const RuntimeCELCostBudget from k8s.io/apiserver/pkg/apis/cel/config.go as input.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (v *validator) Validate(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *corev1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
					func (v *validator) Validate(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *corev1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
 | 
				
			||||||
	var f v1.FailurePolicyType
 | 
						var f v1.FailurePolicyType
 | 
				
			||||||
	if v.failPolicy == nil {
 | 
						if v.failPolicy == nil {
 | 
				
			||||||
		f = v1.Fail
 | 
							f = v1.Fail
 | 
				
			||||||
@@ -102,7 +103,7 @@ func (v *validator) Validate(ctx context.Context, versionedAttr *admission.Versi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	optionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams, Authorizer: authz}
 | 
						optionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams, Authorizer: authz}
 | 
				
			||||||
	expressionOptionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams}
 | 
						expressionOptionalVars := cel.OptionalVariableBindings{VersionedParams: versionedParams}
 | 
				
			||||||
	admissionRequest := cel.CreateAdmissionRequest(versionedAttr.Attributes)
 | 
						admissionRequest := cel.CreateAdmissionRequest(versionedAttr.Attributes, metav1.GroupVersionResource(matchedResource), metav1.GroupVersionKind(versionedAttr.VersionedKind))
 | 
				
			||||||
	// Decide which fields are exposed
 | 
						// Decide which fields are exposed
 | 
				
			||||||
	ns := cel.CreateNamespaceObject(namespace)
 | 
						ns := cel.CreateNamespaceObject(namespace)
 | 
				
			||||||
	evalResults, remainingBudget, err := v.validationFilter.ForInput(ctx, versionedAttr, admissionRequest, optionalVars, ns, runtimeCELCostBudget)
 | 
						evalResults, remainingBudget, err := v.validationFilter.ForInput(ctx, versionedAttr, admissionRequest, optionalVars, ns, runtimeCELCostBudget)
 | 
				
			||||||
@@ -195,7 +196,7 @@ func (v *validator) Validate(ctx context.Context, versionedAttr *admission.Versi
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	options := cel.OptionalVariableBindings{VersionedParams: versionedParams}
 | 
						options := cel.OptionalVariableBindings{VersionedParams: versionedParams}
 | 
				
			||||||
	auditAnnotationEvalResults, _, err := v.auditAnnotationFilter.ForInput(ctx, versionedAttr, cel.CreateAdmissionRequest(versionedAttr.Attributes), options, namespace, runtimeCELCostBudget)
 | 
						auditAnnotationEvalResults, _, err := v.auditAnnotationFilter.ForInput(ctx, versionedAttr, admissionRequest, options, namespace, runtimeCELCostBudget)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return ValidateResult{
 | 
							return ValidateResult{
 | 
				
			||||||
			Decisions: []PolicyDecision{
 | 
								Decisions: []PolicyDecision{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -893,7 +893,7 @@ func TestValidate(t *testing.T) {
 | 
				
			|||||||
			if tc.costBudget != 0 {
 | 
								if tc.costBudget != 0 {
 | 
				
			||||||
				budget = tc.costBudget
 | 
									budget = tc.costBudget
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			validateResult := v.Validate(ctx, fakeVersionedAttr, nil, nil, budget, nil)
 | 
								validateResult := v.Validate(ctx, fakeVersionedAttr.GetResource(), fakeVersionedAttr, nil, nil, budget, nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			require.Equal(t, len(validateResult.Decisions), len(tc.policyDecision))
 | 
								require.Equal(t, len(validateResult.Decisions), len(tc.policyDecision))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -945,7 +945,7 @@ func TestContextCanceled(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	ctx, cancel := context.WithCancel(context.TODO())
 | 
						ctx, cancel := context.WithCancel(context.TODO())
 | 
				
			||||||
	cancel()
 | 
						cancel()
 | 
				
			||||||
	validationResult := v.Validate(ctx, fakeVersionedAttr, nil, nil, celconfig.RuntimeCELCostBudget, nil)
 | 
						validationResult := v.Validate(ctx, fakeVersionedAttr.GetResource(), fakeVersionedAttr, nil, nil, celconfig.RuntimeCELCostBudget, nil)
 | 
				
			||||||
	if len(validationResult.Decisions) != 1 || !strings.Contains(validationResult.Decisions[0].Message, "operation interrupted") {
 | 
						if len(validationResult.Decisions) != 1 || !strings.Contains(validationResult.Decisions[0].Message, "operation interrupted") {
 | 
				
			||||||
		t.Errorf("Expected 'operation interrupted' but got %v", validationResult.Decisions)
 | 
							t.Errorf("Expected 'operation interrupted' but got %v", validationResult.Decisions)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ import (
 | 
				
			|||||||
	celtypes "github.com/google/cel-go/common/types"
 | 
						celtypes "github.com/google/cel-go/common/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/admissionregistration/v1"
 | 
						v1 "k8s.io/api/admissionregistration/v1"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 | 
						utilerrors "k8s.io/apimachinery/pkg/util/errors"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/admission"
 | 
						"k8s.io/apiserver/pkg/admission"
 | 
				
			||||||
@@ -78,7 +79,7 @@ func NewMatcher(filter celplugin.Filter, failPolicy *v1.FailurePolicyType, match
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (m *matcher) Match(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, authz authorizer.Authorizer) MatchResult {
 | 
					func (m *matcher) Match(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, authz authorizer.Authorizer) MatchResult {
 | 
				
			||||||
	t := time.Now()
 | 
						t := time.Now()
 | 
				
			||||||
	evalResults, _, err := m.filter.ForInput(ctx, versionedAttr, celplugin.CreateAdmissionRequest(versionedAttr.Attributes), celplugin.OptionalVariableBindings{
 | 
						evalResults, _, err := m.filter.ForInput(ctx, versionedAttr, celplugin.CreateAdmissionRequest(versionedAttr.Attributes, metav1.GroupVersionResource(versionedAttr.GetResource()), metav1.GroupVersionKind(versionedAttr.VersionedKind)), celplugin.OptionalVariableBindings{
 | 
				
			||||||
		VersionedParams: versionedParams,
 | 
							VersionedParams: versionedParams,
 | 
				
			||||||
		Authorizer:      authz,
 | 
							Authorizer:      authz,
 | 
				
			||||||
	}, nil, celconfig.RuntimeCELCostBudgetMatchConditions)
 | 
						}, nil, celconfig.RuntimeCELCostBudgetMatchConditions)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -248,7 +248,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	APIServerTracing: {Default: true, PreRelease: featuregate.Beta},
 | 
						APIServerTracing: {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ValidatingAdmissionPolicy: {Default: false, PreRelease: featuregate.Alpha},
 | 
						ValidatingAdmissionPolicy: {Default: false, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CustomResourceValidationExpressions: {Default: true, PreRelease: featuregate.Beta},
 | 
						CustomResourceValidationExpressions: {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										714
									
								
								test/integration/apiserver/cel/admission_policy_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										714
									
								
								test/integration/apiserver/cel/admission_policy_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,714 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2023 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package cel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/csv"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/api/admission/v1beta1"
 | 
				
			||||||
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 | 
				
			||||||
 | 
						genericfeatures "k8s.io/apiserver/pkg/features"
 | 
				
			||||||
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
 | 
						featuregatetesting "k8s.io/component-base/featuregate/testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/apis/admissionregistration"
 | 
				
			||||||
 | 
						admissionregistrationv1alpha1apis "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"
 | 
				
			||||||
 | 
						admissionregistrationv1beta1apis "k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/test/integration/etcd"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/test/integration/framework"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/runtime/schema"
 | 
				
			||||||
 | 
						"k8s.io/client-go/dynamic"
 | 
				
			||||||
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
 | 
				
			||||||
 | 
						admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
 | 
				
			||||||
 | 
						admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						beginSentinel   = "###___BEGIN_SENTINEL___###"
 | 
				
			||||||
 | 
						recordSeparator = `###$$$###`
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Policy registration helpers
 | 
				
			||||||
 | 
					var testSpec admissionregistration.ValidatingAdmissionPolicy = admissionregistration.ValidatingAdmissionPolicy{
 | 
				
			||||||
 | 
						Spec: admissionregistration.ValidatingAdmissionPolicySpec{
 | 
				
			||||||
 | 
							ParamKind: &admissionregistration.ParamKind{
 | 
				
			||||||
 | 
								APIVersion: "v1",
 | 
				
			||||||
 | 
								Kind:       "ConfigMap",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Variables: []admissionregistration.Variable{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "shouldFail",
 | 
				
			||||||
 | 
									Expression: `true`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "resourceGroup",
 | 
				
			||||||
 | 
									Expression: `has(request.resource.group) ? request.resource.group : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "resourceVersion",
 | 
				
			||||||
 | 
									Expression: `has(request.resource.version) ? request.resource.version : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "resourceResource",
 | 
				
			||||||
 | 
									Expression: `has(request.resource.resource) ? request.resource.resource : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "subresource",
 | 
				
			||||||
 | 
									Expression: `has(request.subResource) ? request.subResource : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "operation",
 | 
				
			||||||
 | 
									Expression: `has(request.operation) ? request.operation : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "name",
 | 
				
			||||||
 | 
									Expression: `has(request.name) ? request.name : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "namespaceName",
 | 
				
			||||||
 | 
									Expression: `has(request.namespace) ? request.namespace : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "objectExists",
 | 
				
			||||||
 | 
									Expression: `object != null ? "true" : "false"`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "objectAPIVersion",
 | 
				
			||||||
 | 
									Expression: `(object != null && has(object.apiVersion)) ? object.apiVersion : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "objectKind",
 | 
				
			||||||
 | 
									Expression: `(object != null && has(object.kind)) ? object.kind : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "oldObjectExists",
 | 
				
			||||||
 | 
									Expression: `oldObject != null ? "true" : "false"`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "oldObjectAPIVersion",
 | 
				
			||||||
 | 
									Expression: `(oldObject != null && has(oldObject.apiVersion)) ? oldObject.apiVersion : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "oldObjectKind",
 | 
				
			||||||
 | 
									Expression: `(oldObject != null && has(oldObject.kind)) ? oldObject.kind : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "optionsExists",
 | 
				
			||||||
 | 
									Expression: `(has(request.options) && request.options != null) ? "true" : "false"`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "optionsKind",
 | 
				
			||||||
 | 
									Expression: `(has(request.options) && has(request.options.kind)) ? request.options.kind : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "optionsAPIVersion",
 | 
				
			||||||
 | 
									Expression: `(has(request.options) && has(request.options.apiVersion)) ? request.options.apiVersion : ""`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "paramsPhase",
 | 
				
			||||||
 | 
									Expression: `params.data.phase`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "paramsVersion",
 | 
				
			||||||
 | 
									Expression: `params.data.version`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "paramsConvert",
 | 
				
			||||||
 | 
									Expression: `params.data.convert`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							// Would be nice to use CEL to create a single map
 | 
				
			||||||
 | 
							// and stringify it. Unfortunately those library functions
 | 
				
			||||||
 | 
							// are not yet available, so we must create a map
 | 
				
			||||||
 | 
							// like so
 | 
				
			||||||
 | 
							Validations: []admissionregistration.Validation{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									// newlines forbidden so use recordSeparator
 | 
				
			||||||
 | 
									Expression:        "!variables.shouldFail",
 | 
				
			||||||
 | 
									MessageExpression: `"` + beginSentinel + `resourceGroup,resourceVersion,resourceResource,subresource,operation,name,namespace,objectExists,objectKind,objectAPIVersion,oldObjectExists,oldObjectKind,oldObjectAPIVersion,optionsExists,optionsKind,optionsAPIVersion,paramsPhase,paramsVersion,paramsConvert` + recordSeparator + `"+variables.resourceGroup + "," + variables.resourceVersion + "," + variables.resourceResource + "," + variables.subresource + "," + variables.operation + "," + variables.name + "," + variables.namespaceName + "," + variables.objectExists + "," + variables.objectKind + "," + variables.objectAPIVersion + "," + variables.oldObjectExists + "," + variables.oldObjectKind + "," + variables.oldObjectAPIVersion + "," + variables.optionsExists + "," + variables.optionsKind + "," + variables.optionsAPIVersion + "," + variables.paramsPhase + "," + variables.paramsVersion + "," + variables.paramsConvert`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							MatchConditions: []admissionregistration.MatchCondition{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "testclient-only",
 | 
				
			||||||
 | 
									Expression: `request.userInfo.username == "` + testClientUsername + `"`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:       "ignore-test-config",
 | 
				
			||||||
 | 
									Expression: `object == null || !has(object.metadata) || !has(object.metadata.annotations) || !has(object.metadata.annotations.skipMatch) || object.metadata.annotations.skipMatch != "yes"`,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createV1beta1ValidatingPolicyAndBinding(client clientset.Interface, convertedRules []admissionregistrationv1beta1.NamedRuleWithOperations) error {
 | 
				
			||||||
 | 
						denyAction := admissionregistrationv1beta1.DenyAction
 | 
				
			||||||
 | 
						exact := admissionregistrationv1beta1.Exact
 | 
				
			||||||
 | 
						equivalent := admissionregistrationv1beta1.Equivalent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var outSpec admissionregistrationv1beta1.ValidatingAdmissionPolicy
 | 
				
			||||||
 | 
						if err := admissionregistrationv1beta1apis.Convert_admissionregistration_ValidatingAdmissionPolicy_To_v1beta1_ValidatingAdmissionPolicy(&testSpec, &outSpec, nil); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exactPolicyTemplate := outSpec.DeepCopy()
 | 
				
			||||||
 | 
						convertedPolicyTemplate := outSpec.DeepCopy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exactPolicyTemplate.SetName("test-policy-v1beta1")
 | 
				
			||||||
 | 
						exactPolicyTemplate.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{
 | 
				
			||||||
 | 
							ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									RuleWithOperations: admissionregistrationv1.RuleWithOperations{
 | 
				
			||||||
 | 
										Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll},
 | 
				
			||||||
 | 
										Rule:       admissionregistrationv1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*/*"}},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							MatchPolicy: &exact,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						convertedPolicyTemplate.SetName("test-policy-v1beta1-convert")
 | 
				
			||||||
 | 
						convertedPolicyTemplate.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{
 | 
				
			||||||
 | 
							ResourceRules: convertedRules,
 | 
				
			||||||
 | 
							MatchPolicy:   &equivalent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exactPolicy, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), exactPolicyTemplate, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						convertPolicy, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), convertedPolicyTemplate, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a param that holds the options for this
 | 
				
			||||||
 | 
						configuration, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), &corev1.ConfigMap{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:      "test-policy-v1beta1-param",
 | 
				
			||||||
 | 
								Namespace: "default",
 | 
				
			||||||
 | 
								Annotations: map[string]string{
 | 
				
			||||||
 | 
									"skipMatch": "yes",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Data: map[string]string{
 | 
				
			||||||
 | 
								"version": "v1beta1",
 | 
				
			||||||
 | 
								"phase":   validation,
 | 
				
			||||||
 | 
								"convert": "false",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configurationConvert, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), &corev1.ConfigMap{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:      "test-policy-v1beta1-convert-param",
 | 
				
			||||||
 | 
								Namespace: "default",
 | 
				
			||||||
 | 
								Annotations: map[string]string{
 | 
				
			||||||
 | 
									"skipMatch": "yes",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Data: map[string]string{
 | 
				
			||||||
 | 
								"version": "v1beta1",
 | 
				
			||||||
 | 
								"phase":   validation,
 | 
				
			||||||
 | 
								"convert": "true",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "test-policy-v1beta1-binding",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: admissionregistrationv1beta1.ValidatingAdmissionPolicyBindingSpec{
 | 
				
			||||||
 | 
								PolicyName:        exactPolicy.GetName(),
 | 
				
			||||||
 | 
								ValidationActions: []admissionregistrationv1beta1.ValidationAction{admissionregistrationv1beta1.Warn},
 | 
				
			||||||
 | 
								ParamRef: &admissionregistrationv1beta1.ParamRef{
 | 
				
			||||||
 | 
									Name:                    configuration.GetName(),
 | 
				
			||||||
 | 
									Namespace:               configuration.GetNamespace(),
 | 
				
			||||||
 | 
									ParameterNotFoundAction: &denyAction,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "test-policy-v1beta1-convert-binding",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: admissionregistrationv1beta1.ValidatingAdmissionPolicyBindingSpec{
 | 
				
			||||||
 | 
								PolicyName:        convertPolicy.GetName(),
 | 
				
			||||||
 | 
								ValidationActions: []admissionregistrationv1beta1.ValidationAction{admissionregistrationv1beta1.Warn},
 | 
				
			||||||
 | 
								ParamRef: &admissionregistrationv1beta1.ParamRef{
 | 
				
			||||||
 | 
									Name:                    configurationConvert.GetName(),
 | 
				
			||||||
 | 
									Namespace:               configurationConvert.GetNamespace(),
 | 
				
			||||||
 | 
									ParameterNotFoundAction: &denyAction,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createV1alpha1ValidatingPolicyAndBinding(client clientset.Interface, convertedRules []admissionregistrationv1alpha1.NamedRuleWithOperations) error {
 | 
				
			||||||
 | 
						exact := admissionregistrationv1alpha1.Exact
 | 
				
			||||||
 | 
						equivalent := admissionregistrationv1alpha1.Equivalent
 | 
				
			||||||
 | 
						denyAction := admissionregistrationv1alpha1.DenyAction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var outSpec admissionregistrationv1alpha1.ValidatingAdmissionPolicy
 | 
				
			||||||
 | 
						if err := admissionregistrationv1alpha1apis.Convert_admissionregistration_ValidatingAdmissionPolicy_To_v1alpha1_ValidatingAdmissionPolicy(&testSpec, &outSpec, nil); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exactPolicyTemplate := outSpec.DeepCopy()
 | 
				
			||||||
 | 
						convertedPolicyTemplate := outSpec.DeepCopy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exactPolicyTemplate.SetName("test-policy-v1alpha1")
 | 
				
			||||||
 | 
						exactPolicyTemplate.Spec.MatchConstraints = &admissionregistrationv1alpha1.MatchResources{
 | 
				
			||||||
 | 
							ResourceRules: []admissionregistrationv1alpha1.NamedRuleWithOperations{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									RuleWithOperations: admissionregistrationv1.RuleWithOperations{
 | 
				
			||||||
 | 
										Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll},
 | 
				
			||||||
 | 
										Rule:       admissionregistrationv1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*/*"}},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							MatchPolicy: &exact,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						convertedPolicyTemplate.SetName("test-policy-v1alpha1-convert")
 | 
				
			||||||
 | 
						convertedPolicyTemplate.Spec.MatchConstraints = &admissionregistrationv1alpha1.MatchResources{
 | 
				
			||||||
 | 
							ResourceRules: convertedRules,
 | 
				
			||||||
 | 
							MatchPolicy:   &equivalent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exactPolicy, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(context.TODO(), exactPolicyTemplate, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						convertPolicy, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(context.TODO(), convertedPolicyTemplate, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a param that holds the options for this
 | 
				
			||||||
 | 
						configuration, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), &corev1.ConfigMap{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:      "test-policy-v1alpha1-param",
 | 
				
			||||||
 | 
								Namespace: "default",
 | 
				
			||||||
 | 
								Annotations: map[string]string{
 | 
				
			||||||
 | 
									"skipMatch": "yes",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Data: map[string]string{
 | 
				
			||||||
 | 
								"version": "v1alpha1",
 | 
				
			||||||
 | 
								"phase":   validation,
 | 
				
			||||||
 | 
								"convert": "false",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configurationConvert, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), &corev1.ConfigMap{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:      "test-policy-v1alpha1-convert-param",
 | 
				
			||||||
 | 
								Namespace: "default",
 | 
				
			||||||
 | 
								Annotations: map[string]string{
 | 
				
			||||||
 | 
									"skipMatch": "yes",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Data: map[string]string{
 | 
				
			||||||
 | 
								"version": "v1alpha1",
 | 
				
			||||||
 | 
								"phase":   validation,
 | 
				
			||||||
 | 
								"convert": "true",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), &admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "test-policy-v1alpha1-binding",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: admissionregistrationv1alpha1.ValidatingAdmissionPolicyBindingSpec{
 | 
				
			||||||
 | 
								PolicyName:        exactPolicy.GetName(),
 | 
				
			||||||
 | 
								ValidationActions: []admissionregistrationv1alpha1.ValidationAction{admissionregistrationv1alpha1.Warn},
 | 
				
			||||||
 | 
								ParamRef: &admissionregistrationv1alpha1.ParamRef{
 | 
				
			||||||
 | 
									Name:                    configuration.GetName(),
 | 
				
			||||||
 | 
									Namespace:               configuration.GetNamespace(),
 | 
				
			||||||
 | 
									ParameterNotFoundAction: &denyAction,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), &admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "test-policy-v1alpha1-convert-binding",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: admissionregistrationv1alpha1.ValidatingAdmissionPolicyBindingSpec{
 | 
				
			||||||
 | 
								PolicyName:        convertPolicy.GetName(),
 | 
				
			||||||
 | 
								ValidationActions: []admissionregistrationv1alpha1.ValidationAction{admissionregistrationv1alpha1.Warn},
 | 
				
			||||||
 | 
								ParamRef: &admissionregistrationv1alpha1.ParamRef{
 | 
				
			||||||
 | 
									Name:                    configurationConvert.GetName(),
 | 
				
			||||||
 | 
									Namespace:               configurationConvert.GetNamespace(),
 | 
				
			||||||
 | 
									ParameterNotFoundAction: &denyAction,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This test shows that policy intercepts all requests for all resources,
 | 
				
			||||||
 | 
					// subresources, verbs, and input versions of policy/binding.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This test tries to mirror very closely the same test for webhook admission
 | 
				
			||||||
 | 
					// test/integration/apiserver/admissionwebhook/admission_test.go testWebhookAdmission
 | 
				
			||||||
 | 
					func TestPolicyAdmission(t *testing.T) {
 | 
				
			||||||
 | 
						defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
 | 
				
			||||||
 | 
						defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.APISelfSubjectReview, true)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						holder := &policyExpectationHolder{
 | 
				
			||||||
 | 
							holder: holder{
 | 
				
			||||||
 | 
								t:                 t,
 | 
				
			||||||
 | 
								gvrToConvertedGVR: map[metav1.GroupVersionResource]metav1.GroupVersionResource{},
 | 
				
			||||||
 | 
								gvrToConvertedGVK: map[metav1.GroupVersionResource]schema.GroupVersionKind{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server := apiservertesting.StartTestServerOrDie(t, nil, []string{
 | 
				
			||||||
 | 
							"--enable-admission-plugins", "ValidatingAdmissionPolicy",
 | 
				
			||||||
 | 
							// turn off admission plugins that add finalizers
 | 
				
			||||||
 | 
							"--disable-admission-plugins=ServiceAccount,StorageObjectInUseProtection",
 | 
				
			||||||
 | 
							// force enable all resources so we can check storage.
 | 
				
			||||||
 | 
							"--runtime-config=api/all=true",
 | 
				
			||||||
 | 
						}, framework.SharedEtcd())
 | 
				
			||||||
 | 
						defer server.TearDownFn()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create admission policy & binding that match everything
 | 
				
			||||||
 | 
						clientConfig := server.ClientConfig
 | 
				
			||||||
 | 
						clientConfig.Impersonate.UserName = testClientUsername
 | 
				
			||||||
 | 
						clientConfig.Impersonate.Groups = []string{"system:masters", "system:authenticated"}
 | 
				
			||||||
 | 
						clientConfig.WarningHandler = holder
 | 
				
			||||||
 | 
						client, err := clientset.NewForConfig(clientConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// create CRDs
 | 
				
			||||||
 | 
						etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, etcd.GetCustomResourceDefinitionData()...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := client.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}}, metav1.CreateOptions{}); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// gather resources to test
 | 
				
			||||||
 | 
						dynamicClient, err := dynamic.NewForConfig(clientConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, resources, err := client.Discovery().ServerGroupsAndResources()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed to get ServerGroupsAndResources with error: %+v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gvrsToTest := []schema.GroupVersionResource{}
 | 
				
			||||||
 | 
						resourcesByGVR := map[schema.GroupVersionResource]metav1.APIResource{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, list := range resources {
 | 
				
			||||||
 | 
							defaultGroupVersion, err := schema.ParseGroupVersion(list.GroupVersion)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("Failed to get GroupVersion for: %+v", list)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, resource := range list.APIResources {
 | 
				
			||||||
 | 
								if resource.Group == "" {
 | 
				
			||||||
 | 
									resource.Group = defaultGroupVersion.Group
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if resource.Version == "" {
 | 
				
			||||||
 | 
									resource.Version = defaultGroupVersion.Version
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								gvr := defaultGroupVersion.WithResource(resource.Name)
 | 
				
			||||||
 | 
								resourcesByGVR[gvr] = resource
 | 
				
			||||||
 | 
								if shouldTestResource(gvr, resource) {
 | 
				
			||||||
 | 
									gvrsToTest = append(gvrsToTest, gvr)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sort.SliceStable(gvrsToTest, func(i, j int) bool {
 | 
				
			||||||
 | 
							if gvrsToTest[i].Group < gvrsToTest[j].Group {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if gvrsToTest[i].Group > gvrsToTest[j].Group {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if gvrsToTest[i].Version < gvrsToTest[j].Version {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if gvrsToTest[i].Version > gvrsToTest[j].Version {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if gvrsToTest[i].Resource < gvrsToTest[j].Resource {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if gvrsToTest[i].Resource > gvrsToTest[j].Resource {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// map unqualified resource names to the fully qualified resource we will expect to be converted to
 | 
				
			||||||
 | 
						// Note: this only works because there are no overlapping resource names in-process that are not co-located
 | 
				
			||||||
 | 
						convertedResources := map[string]schema.GroupVersionResource{}
 | 
				
			||||||
 | 
						// build the webhook rules enumerating the specific group/version/resources we want
 | 
				
			||||||
 | 
						convertedV1beta1Rules := []admissionregistrationv1beta1.NamedRuleWithOperations{}
 | 
				
			||||||
 | 
						convertedV1alpha1Rules := []admissionregistrationv1alpha1.NamedRuleWithOperations{}
 | 
				
			||||||
 | 
						for _, gvr := range gvrsToTest {
 | 
				
			||||||
 | 
							metaGVR := metav1.GroupVersionResource{Group: gvr.Group, Version: gvr.Version, Resource: gvr.Resource}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							convertedGVR, ok := convertedResources[gvr.Resource]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								// this is the first time we've seen this resource
 | 
				
			||||||
 | 
								// record the fully qualified resource we expect
 | 
				
			||||||
 | 
								convertedGVR = gvr
 | 
				
			||||||
 | 
								convertedResources[gvr.Resource] = gvr
 | 
				
			||||||
 | 
								// add an admission rule indicating we can receive this version
 | 
				
			||||||
 | 
								convertedV1beta1Rules = append(convertedV1beta1Rules, admissionregistrationv1beta1.NamedRuleWithOperations{
 | 
				
			||||||
 | 
									RuleWithOperations: admissionregistrationv1.RuleWithOperations{
 | 
				
			||||||
 | 
										Operations: []admissionregistrationv1beta1.OperationType{admissionregistrationv1beta1.OperationAll},
 | 
				
			||||||
 | 
										Rule:       admissionregistrationv1beta1.Rule{APIGroups: []string{gvr.Group}, APIVersions: []string{gvr.Version}, Resources: []string{gvr.Resource}},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								convertedV1alpha1Rules = append(convertedV1alpha1Rules, admissionregistrationv1alpha1.NamedRuleWithOperations{
 | 
				
			||||||
 | 
									RuleWithOperations: admissionregistrationv1.RuleWithOperations{
 | 
				
			||||||
 | 
										Operations: []admissionregistrationv1alpha1.OperationType{admissionregistrationv1alpha1.OperationAll},
 | 
				
			||||||
 | 
										Rule:       admissionregistrationv1alpha1.Rule{APIGroups: []string{gvr.Group}, APIVersions: []string{gvr.Version}, Resources: []string{gvr.Resource}},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// record the expected resource and kind
 | 
				
			||||||
 | 
							holder.gvrToConvertedGVR[metaGVR] = metav1.GroupVersionResource{Group: convertedGVR.Group, Version: convertedGVR.Version, Resource: convertedGVR.Resource}
 | 
				
			||||||
 | 
							holder.gvrToConvertedGVK[metaGVR] = schema.GroupVersionKind{Group: resourcesByGVR[convertedGVR].Group, Version: resourcesByGVR[convertedGVR].Version, Kind: resourcesByGVR[convertedGVR].Kind}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := createV1alpha1ValidatingPolicyAndBinding(client, convertedV1alpha1Rules); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := createV1beta1ValidatingPolicyAndBinding(client, convertedV1beta1Rules); err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Allow the policy & binding to establish
 | 
				
			||||||
 | 
						time.Sleep(1 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start := time.Now()
 | 
				
			||||||
 | 
						count := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test admission on all resources, subresources, and verbs
 | 
				
			||||||
 | 
						for _, gvr := range gvrsToTest {
 | 
				
			||||||
 | 
							resource := resourcesByGVR[gvr]
 | 
				
			||||||
 | 
							t.Run(gvr.Group+"."+gvr.Version+"."+strings.ReplaceAll(resource.Name, "/", "."), func(t *testing.T) {
 | 
				
			||||||
 | 
								for _, verb := range []string{"create", "update", "patch", "connect", "delete", "deletecollection"} {
 | 
				
			||||||
 | 
									if shouldTestResourceVerb(gvr, resource, verb) {
 | 
				
			||||||
 | 
										t.Run(verb, func(t *testing.T) {
 | 
				
			||||||
 | 
											count++
 | 
				
			||||||
 | 
											holder.reset(t)
 | 
				
			||||||
 | 
											testFunc := getTestFunc(gvr, verb)
 | 
				
			||||||
 | 
											testFunc(&testContext{
 | 
				
			||||||
 | 
												t:               t,
 | 
				
			||||||
 | 
												admissionHolder: holder,
 | 
				
			||||||
 | 
												client:          dynamicClient,
 | 
				
			||||||
 | 
												clientset:       client,
 | 
				
			||||||
 | 
												verb:            verb,
 | 
				
			||||||
 | 
												gvr:             gvr,
 | 
				
			||||||
 | 
												resource:        resource,
 | 
				
			||||||
 | 
												resources:       resourcesByGVR,
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
											holder.verify(t)
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if count >= 10 {
 | 
				
			||||||
 | 
							duration := time.Since(start)
 | 
				
			||||||
 | 
							perResourceDuration := time.Duration(int(duration) / count)
 | 
				
			||||||
 | 
							if perResourceDuration >= 150*time.Millisecond {
 | 
				
			||||||
 | 
								t.Errorf("expected resources to process in < 150ms, average was %v", perResourceDuration)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Policy admission holder for test framework
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type policyExpectationHolder struct {
 | 
				
			||||||
 | 
						holder
 | 
				
			||||||
 | 
						warningLock sync.Mutex
 | 
				
			||||||
 | 
						warnings    []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *policyExpectationHolder) reset(t *testing.T) {
 | 
				
			||||||
 | 
						p.warningLock.Lock()
 | 
				
			||||||
 | 
						defer p.warningLock.Unlock()
 | 
				
			||||||
 | 
						p.warnings = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.holder.reset(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (p *policyExpectationHolder) expect(gvr schema.GroupVersionResource, gvk, optionsGVK schema.GroupVersionKind, operation v1beta1.Operation, name, namespace string, object, oldObject, options bool) {
 | 
				
			||||||
 | 
						p.holder.expect(gvr, gvk, optionsGVK, operation, name, namespace, object, oldObject, options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.lock.Lock()
 | 
				
			||||||
 | 
						defer p.lock.Unlock()
 | 
				
			||||||
 | 
						// Set up the recorded map with nil records for all combinations
 | 
				
			||||||
 | 
						p.recorded = map[webhookOptions]*admissionRequest{}
 | 
				
			||||||
 | 
						for _, phase := range []string{validation} {
 | 
				
			||||||
 | 
							for _, converted := range []bool{true, false} {
 | 
				
			||||||
 | 
								for _, version := range []string{"v1alpha1", "v1beta1"} {
 | 
				
			||||||
 | 
									p.recorded[webhookOptions{version: version, phase: phase, converted: converted}] = nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *policyExpectationHolder) verify(t *testing.T) {
 | 
				
			||||||
 | 
						p.warningLock.Lock()
 | 
				
			||||||
 | 
						defer p.warningLock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Process all detected warnings and record in the nested handler
 | 
				
			||||||
 | 
						for _, w := range p.warnings {
 | 
				
			||||||
 | 
							var currentRequest *admissionRequest
 | 
				
			||||||
 | 
							var currentParams webhookOptions
 | 
				
			||||||
 | 
							if idx := strings.Index(w, beginSentinel); idx >= 0 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								csvData := strings.ReplaceAll(w[idx+len(beginSentinel):], recordSeparator, "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								b := bytes.Buffer{}
 | 
				
			||||||
 | 
								b.WriteString(csvData)
 | 
				
			||||||
 | 
								reader := csv.NewReader(&b)
 | 
				
			||||||
 | 
								csvRecords, err := reader.ReadAll()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Fatal(err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								mappedCSV := []map[string]string{}
 | 
				
			||||||
 | 
								var header []string
 | 
				
			||||||
 | 
								for line, record := range csvRecords {
 | 
				
			||||||
 | 
									if line == 0 {
 | 
				
			||||||
 | 
										header = record
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										line := map[string]string{}
 | 
				
			||||||
 | 
										for i := 0; i < len(record); i++ {
 | 
				
			||||||
 | 
											line[header[i]] = record[i]
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										mappedCSV = append(mappedCSV, line)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if len(mappedCSV) != 1 {
 | 
				
			||||||
 | 
									t.Fatal("incorrect # CSV elements in parsed warning")
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								data := mappedCSV[0]
 | 
				
			||||||
 | 
								currentRequest = &admissionRequest{
 | 
				
			||||||
 | 
									Operation: data["operation"],
 | 
				
			||||||
 | 
									Name:      data["name"],
 | 
				
			||||||
 | 
									Namespace: data["namespace"],
 | 
				
			||||||
 | 
									Resource: metav1.GroupVersionResource{
 | 
				
			||||||
 | 
										Group:    data["resourceGroup"],
 | 
				
			||||||
 | 
										Version:  data["resourceVersion"],
 | 
				
			||||||
 | 
										Resource: data["resourceResource"],
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									SubResource: data["subresource"],
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								currentParams = webhookOptions{
 | 
				
			||||||
 | 
									version:   data["paramsVersion"],
 | 
				
			||||||
 | 
									phase:     data["paramsPhase"],
 | 
				
			||||||
 | 
									converted: data["paramsConvert"] == "true",
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if e, ok := data["objectExists"]; ok && e == "true" {
 | 
				
			||||||
 | 
									currentRequest.Object.Object = &unstructured.Unstructured{}
 | 
				
			||||||
 | 
									currentRequest.Object.Object.(*unstructured.Unstructured).SetAPIVersion(data["objectAPIVersion"])
 | 
				
			||||||
 | 
									currentRequest.Object.Object.(*unstructured.Unstructured).SetKind(data["objectKind"])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if e, ok := data["oldObjectExists"]; ok && e == "true" {
 | 
				
			||||||
 | 
									currentRequest.OldObject.Object = &unstructured.Unstructured{}
 | 
				
			||||||
 | 
									currentRequest.OldObject.Object.(*unstructured.Unstructured).SetAPIVersion(data["oldObjectAPIVersion"])
 | 
				
			||||||
 | 
									currentRequest.OldObject.Object.(*unstructured.Unstructured).SetKind(data["oldObjectKind"])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if e, ok := data["optionsExists"]; ok && e == "true" {
 | 
				
			||||||
 | 
									currentRequest.Options.Object = &unstructured.Unstructured{}
 | 
				
			||||||
 | 
									currentRequest.Options.Object.(*unstructured.Unstructured).SetAPIVersion(data["optionsAPIVersion"])
 | 
				
			||||||
 | 
									currentRequest.Options.Object.(*unstructured.Unstructured).SetKind(data["optionsKind"])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								p.holder.record(currentParams.version, currentParams.phase, currentParams.converted, currentRequest)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p.holder.verify(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *policyExpectationHolder) HandleWarningHeader(code int, agent string, message string) {
 | 
				
			||||||
 | 
						if code != 299 || len(message) == 0 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p.warningLock.Lock()
 | 
				
			||||||
 | 
						defer p.warningLock.Unlock()
 | 
				
			||||||
 | 
						p.warnings = append(p.warnings, message)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1095
									
								
								test/integration/apiserver/cel/admission_test_util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1095
									
								
								test/integration/apiserver/cel/admission_test_util.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user