k8s.io/kubernetes@v1.29.3/test/integration/apiserver/cel/validatingadmissionpolicy_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cel
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"os"
    26  	"strconv"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  	"text/template"
    31  	"time"
    32  
    33  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    34  	apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    35  	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
    36  	auditinternal "k8s.io/apiserver/pkg/apis/audit"
    37  	auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
    38  	genericfeatures "k8s.io/apiserver/pkg/features"
    39  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    40  	"k8s.io/client-go/rest"
    41  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    42  
    43  	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
    44  	apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    45  	authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
    46  	"k8s.io/kubernetes/test/integration/authutil"
    47  	"k8s.io/kubernetes/test/integration/etcd"
    48  	"k8s.io/kubernetes/test/integration/framework"
    49  	"k8s.io/kubernetes/test/utils"
    50  
    51  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    52  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    53  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    54  	"k8s.io/apimachinery/pkg/runtime/schema"
    55  	"k8s.io/apimachinery/pkg/types"
    56  	"k8s.io/apimachinery/pkg/util/sets"
    57  	"k8s.io/apimachinery/pkg/util/wait"
    58  	"k8s.io/client-go/dynamic"
    59  	clientset "k8s.io/client-go/kubernetes"
    60  
    61  	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
    62  	admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
    63  	authorizationv1 "k8s.io/api/authorization/v1"
    64  	v1 "k8s.io/api/core/v1"
    65  	rbacv1 "k8s.io/api/rbac/v1"
    66  )
    67  
    68  // Test_ValidateNamespace_NoParams tests a ValidatingAdmissionPolicy that validates creation of a Namespace with no params.
    69  func Test_ValidateNamespace_NoParams(t *testing.T) {
    70  	forbiddenReason := metav1.StatusReasonForbidden
    71  
    72  	testcases := []struct {
    73  		name          string
    74  		policy        *admissionregistrationv1beta1.ValidatingAdmissionPolicy
    75  		policyBinding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding
    76  		namespace     *v1.Namespace
    77  		err           string
    78  		failureReason metav1.StatusReason
    79  	}{
    80  		{
    81  			name: "namespace name contains suffix enforced by validating admission policy, using object metadata fields",
    82  			policy: withValidations([]admissionregistrationv1beta1.Validation{
    83  				{
    84  					Expression: "object.metadata.name.endsWith('k8s')",
    85  				},
    86  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
    87  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
    88  			namespace: &v1.Namespace{
    89  				ObjectMeta: metav1.ObjectMeta{
    90  					Name: "test-k8s",
    91  				},
    92  			},
    93  			err: "",
    94  		},
    95  		{
    96  			name: "namespace name does NOT contain suffix enforced by validating admission policyusing, object metadata fields",
    97  			policy: withValidations([]admissionregistrationv1beta1.Validation{
    98  				{
    99  					Expression: "object.metadata.name.endsWith('k8s')",
   100  				},
   101  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   102  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   103  			namespace: &v1.Namespace{
   104  				ObjectMeta: metav1.ObjectMeta{
   105  					Name: "test-foobar",
   106  				},
   107  			},
   108  			err:           "namespaces \"test-foobar\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
   109  			failureReason: metav1.StatusReasonInvalid,
   110  		},
   111  		{
   112  			name: "namespace name does NOT contain suffix enforced by validating admission policy using object metadata fields, AND validating expression returns StatusReasonForbidden",
   113  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   114  				{
   115  					Expression: "object.metadata.name.endsWith('k8s')",
   116  					Reason:     &forbiddenReason,
   117  				},
   118  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   119  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   120  			namespace: &v1.Namespace{
   121  				ObjectMeta: metav1.ObjectMeta{
   122  					Name: "forbidden-test-foobar",
   123  				},
   124  			},
   125  			err:           "namespaces \"forbidden-test-foobar\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
   126  			failureReason: metav1.StatusReasonForbidden,
   127  		},
   128  		{
   129  			name: "namespace name contains suffix enforced by validating admission policy, using request field",
   130  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   131  				{
   132  					Expression: "request.name.endsWith('k8s')",
   133  				},
   134  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   135  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   136  			namespace: &v1.Namespace{
   137  				ObjectMeta: metav1.ObjectMeta{
   138  					Name: "test-k8s",
   139  				},
   140  			},
   141  			err: "",
   142  		},
   143  		{
   144  			name: "namespace name does NOT contains suffix enforced by validating admission policy, using request field",
   145  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   146  				{
   147  					Expression: "request.name.endsWith('k8s')",
   148  				},
   149  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   150  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   151  			namespace: &v1.Namespace{
   152  				ObjectMeta: metav1.ObjectMeta{
   153  					Name: "test-k8s",
   154  				},
   155  			},
   156  			err: "",
   157  		},
   158  		{
   159  			name: "runtime error when validating namespace, but failurePolicy=Ignore",
   160  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   161  				{
   162  					Expression: "object.nonExistentProperty == 'someval'",
   163  				},
   164  			}, withFailurePolicy(admissionregistrationv1beta1.Ignore, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   165  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   166  			namespace: &v1.Namespace{
   167  				ObjectMeta: metav1.ObjectMeta{
   168  					Name: "test-k8s",
   169  				},
   170  			},
   171  			err: "",
   172  		},
   173  		{
   174  			name: "runtime error when validating namespace, but failurePolicy=Fail",
   175  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   176  				{
   177  					Expression: "object.nonExistentProperty == 'someval'",
   178  				},
   179  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
   180  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   181  			namespace: &v1.Namespace{
   182  				ObjectMeta: metav1.ObjectMeta{
   183  					Name: "test-k8s",
   184  				},
   185  			},
   186  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: expression 'object.nonExistentProperty == 'someval'' resulted in error: no such key: nonExistentProperty",
   187  			failureReason: metav1.StatusReasonInvalid,
   188  		},
   189  		{
   190  			name: "runtime error due to unguarded params",
   191  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   192  				{
   193  					Expression: "object.metadata.name.startsWith(params.metadata.name)",
   194  				},
   195  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   196  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   197  			namespace: &v1.Namespace{
   198  				ObjectMeta: metav1.ObjectMeta{
   199  					Name: "test-k8s",
   200  				},
   201  			},
   202  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: expression 'object.metadata.name.startsWith(params.metadata.name)' resulted in error: no such key: metadata",
   203  			failureReason: metav1.StatusReasonInvalid,
   204  		},
   205  		{
   206  			name: "with check against unguarded params using has()",
   207  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   208  				{
   209  					Expression: "has(params.metadata) && has(params.metadata.name) && object.metadata.name.endsWith(params.metadata.name)",
   210  				},
   211  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   212  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   213  			namespace: &v1.Namespace{
   214  				ObjectMeta: metav1.ObjectMeta{
   215  					Name: "test-k8s",
   216  				},
   217  			},
   218  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: has(params.metadata) && has(params.metadata.name) && object.metadata.name.endsWith(params.metadata.name)",
   219  			failureReason: metav1.StatusReasonInvalid,
   220  		},
   221  		{
   222  			name: "with check against null params",
   223  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   224  				{
   225  					Expression: "(params != null && object.metadata.name.endsWith(params.metadata.name))",
   226  				},
   227  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   228  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   229  			namespace: &v1.Namespace{
   230  				ObjectMeta: metav1.ObjectMeta{
   231  					Name: "test-k8s",
   232  				},
   233  			},
   234  			err:           "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: (params != null && object.metadata.name.endsWith(params.metadata.name))",
   235  			failureReason: metav1.StatusReasonInvalid,
   236  		},
   237  		{
   238  			name: "with check against unguarded params using has() and default check",
   239  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   240  				{
   241  					Expression: "(has(params.metadata) && has(params.metadata.name) && object.metadata.name.startsWith(params.metadata.name)) || object.metadata.name.endsWith('k8s')",
   242  				},
   243  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   244  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   245  			namespace: &v1.Namespace{
   246  				ObjectMeta: metav1.ObjectMeta{
   247  					Name: "test-k8s",
   248  				},
   249  			},
   250  			err: "",
   251  		},
   252  		{
   253  			name: "with check against null params and default check",
   254  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   255  				{
   256  					Expression: "(params != null && object.metadata.name.startsWith(params.metadata.name)) || object.metadata.name.endsWith('k8s')",
   257  				},
   258  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   259  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   260  			namespace: &v1.Namespace{
   261  				ObjectMeta: metav1.ObjectMeta{
   262  					Name: "test-k8s",
   263  				},
   264  			},
   265  			err: "",
   266  		},
   267  		{
   268  			name: "with check against namespaceObject",
   269  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   270  				{
   271  					Expression: "namespaceObject == null", // because namespace itself is cluster-scoped.
   272  				},
   273  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   274  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
   275  			namespace: &v1.Namespace{
   276  				ObjectMeta: metav1.ObjectMeta{
   277  					Name: "test-k8s",
   278  				},
   279  			},
   280  			err: "",
   281  		},
   282  	}
   283  	for _, testcase := range testcases {
   284  		t.Run(testcase.name, func(t *testing.T) {
   285  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   286  
   287  			server, err := apiservertesting.StartTestServer(t, nil, []string{
   288  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   289  			}, framework.SharedEtcd())
   290  			if err != nil {
   291  				t.Fatal(err)
   292  			}
   293  			defer server.TearDownFn()
   294  
   295  			config := server.ClientConfig
   296  
   297  			client, err := clientset.NewForConfig(config)
   298  			if err != nil {
   299  				t.Fatal(err)
   300  			}
   301  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
   302  			if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   303  				t.Fatal(err)
   304  			}
   305  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
   306  				t.Fatal(err)
   307  			}
   308  
   309  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
   310  
   311  			checkExpectedError(t, err, testcase.err)
   312  			checkFailureReason(t, err, testcase.failureReason)
   313  		})
   314  	}
   315  }
   316  func Test_ValidateAnnotationsAndWarnings(t *testing.T) {
   317  	testcases := []struct {
   318  		name             string
   319  		policy           *admissionregistrationv1beta1.ValidatingAdmissionPolicy
   320  		policyBinding    *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding
   321  		object           *v1.ConfigMap
   322  		err              string
   323  		failureReason    metav1.StatusReason
   324  		auditAnnotations map[string]string
   325  		warnings         sets.Set[string]
   326  	}{
   327  		{
   328  			name: "with audit annotations",
   329  			policy: withAuditAnnotations([]admissionregistrationv1beta1.AuditAnnotation{
   330  				{
   331  					Key:             "example-key",
   332  					ValueExpression: "'object name: ' + object.metadata.name",
   333  				},
   334  				{
   335  					Key:             "exclude-key",
   336  					ValueExpression: "null",
   337  				},
   338  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withConfigMapMatch(makePolicy("validate-audit-annotations"))))),
   339  			policyBinding: makeBinding("validate-audit-annotations-binding", "validate-audit-annotations", ""),
   340  			object: &v1.ConfigMap{
   341  				ObjectMeta: metav1.ObjectMeta{
   342  					Name: "test1-k8s",
   343  				},
   344  			},
   345  			err: "",
   346  			auditAnnotations: map[string]string{
   347  				"validate-audit-annotations/example-key": `object name: test1-k8s`,
   348  			},
   349  		},
   350  		{
   351  			name: "with audit annotations with invalid expression",
   352  			policy: withAuditAnnotations([]admissionregistrationv1beta1.AuditAnnotation{
   353  				{
   354  					Key:             "example-key",
   355  					ValueExpression: "string(params.metadata.name)", // runtime error, params is null
   356  				},
   357  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withConfigMapMatch(makePolicy("validate-audit-annotations-invalid"))))),
   358  			policyBinding: makeBinding("validate-audit-annotations-invalid-binding", "validate-audit-annotations-invalid", ""),
   359  			object: &v1.ConfigMap{
   360  				ObjectMeta: metav1.ObjectMeta{
   361  					Name: "test2-k8s",
   362  				},
   363  			},
   364  			err:           "configmaps \"test2-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-audit-annotations-invalid' with binding 'validate-audit-annotations-invalid-binding' denied request: expression 'string(params.metadata.name)' resulted in error: no such key: metadata",
   365  			failureReason: metav1.StatusReasonInvalid,
   366  		},
   367  		{
   368  			name: "with audit annotations with invalid expression and ignore failure policy",
   369  			policy: withAuditAnnotations([]admissionregistrationv1beta1.AuditAnnotation{
   370  				{
   371  					Key:             "example-key",
   372  					ValueExpression: "string(params.metadata.name)", // runtime error, params is null
   373  				},
   374  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Ignore, withConfigMapMatch(makePolicy("validate-audit-annotations-invalid-ignore"))))),
   375  			policyBinding: makeBinding("validate-audit-annotations-invalid-ignore-binding", "validate-audit-annotations-invalid-ignore", ""),
   376  			object: &v1.ConfigMap{
   377  				ObjectMeta: metav1.ObjectMeta{
   378  					Name: "test3-k8s",
   379  				},
   380  			},
   381  			err: "",
   382  		},
   383  		{
   384  			name: "with warn validationActions",
   385  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   386  				{
   387  					Expression: "object.metadata.name.endsWith('k8s')",
   388  				},
   389  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withConfigMapMatch(makePolicy("validate-actions-warn"))))),
   390  			policyBinding: withValidationActions([]admissionregistrationv1beta1.ValidationAction{admissionregistrationv1beta1.Warn}, makeBinding("validate-actions-warn-binding", "validate-actions-warn", "")),
   391  			object: &v1.ConfigMap{
   392  				ObjectMeta: metav1.ObjectMeta{
   393  					Name: "test4-nope",
   394  				},
   395  			},
   396  			warnings: sets.New("Validation failed for ValidatingAdmissionPolicy 'validate-actions-warn' with binding 'validate-actions-warn-binding': failed expression: object.metadata.name.endsWith('k8s')"),
   397  		},
   398  		{
   399  			name: "with audit validationActions",
   400  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   401  				{
   402  					Expression: "object.metadata.name.endsWith('k8s')",
   403  				},
   404  			}, withParams(configParamKind(), withFailurePolicy(admissionregistrationv1beta1.Fail, withConfigMapMatch(makePolicy("validate-actions-audit"))))),
   405  			policyBinding: withValidationActions([]admissionregistrationv1beta1.ValidationAction{admissionregistrationv1beta1.Deny, admissionregistrationv1beta1.Audit}, makeBinding("validate-actions-audit-binding", "validate-actions-audit", "")),
   406  			object: &v1.ConfigMap{
   407  				ObjectMeta: metav1.ObjectMeta{
   408  					Name: "test5-nope",
   409  				},
   410  			},
   411  			err:           "configmaps \"test5-nope\" is forbidden: ValidatingAdmissionPolicy 'validate-actions-audit' with binding 'validate-actions-audit-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
   412  			failureReason: metav1.StatusReasonInvalid,
   413  			auditAnnotations: map[string]string{
   414  				"validation.policy.admission.k8s.io/validation_failure": `[{"message":"failed expression: object.metadata.name.endsWith('k8s')","policy":"validate-actions-audit","binding":"validate-actions-audit-binding","expressionIndex":1,"validationActions":["Deny","Audit"]}]`,
   415  			},
   416  		},
   417  	}
   418  
   419  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   420  
   421  	// prepare audit policy file
   422  	policyFile, err := os.CreateTemp("", "audit-policy.yaml")
   423  	if err != nil {
   424  		t.Fatalf("Failed to create audit policy file: %v", err)
   425  	}
   426  	defer os.Remove(policyFile.Name())
   427  	if _, err := policyFile.Write([]byte(auditPolicy)); err != nil {
   428  		t.Fatalf("Failed to write audit policy file: %v", err)
   429  	}
   430  	if err := policyFile.Close(); err != nil {
   431  		t.Fatalf("Failed to close audit policy file: %v", err)
   432  	}
   433  
   434  	// prepare audit log file
   435  	logFile, err := os.CreateTemp("", "audit.log")
   436  	if err != nil {
   437  		t.Fatalf("Failed to create audit log file: %v", err)
   438  	}
   439  	defer os.Remove(logFile.Name())
   440  
   441  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   442  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   443  		"--audit-policy-file", policyFile.Name(),
   444  		"--audit-log-version", "audit.k8s.io/v1",
   445  		"--audit-log-mode", "blocking",
   446  		"--audit-log-path", logFile.Name(),
   447  	}, framework.SharedEtcd())
   448  	if err != nil {
   449  		t.Fatal(err)
   450  	}
   451  	defer server.TearDownFn()
   452  
   453  	config := server.ClientConfig
   454  
   455  	warnHandler := newWarningHandler()
   456  	config.WarningHandler = warnHandler
   457  	config.Impersonate.UserName = testReinvocationClientUsername
   458  	client, err := clientset.NewForConfig(config)
   459  	if err != nil {
   460  		t.Fatal(err)
   461  	}
   462  	for i, testcase := range testcases {
   463  		t.Run(testcase.name, func(t *testing.T) {
   464  			testCaseID := strconv.Itoa(i)
   465  			ns := "auditannotations-" + testCaseID
   466  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, metav1.CreateOptions{})
   467  			if err != nil {
   468  				t.Fatal(err)
   469  			}
   470  
   471  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
   472  			if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   473  				t.Fatal(err)
   474  			}
   475  
   476  			if err := createAndWaitReadyNamespacedWithWarnHandler(t, client, withMatchNamespace(testcase.policyBinding, ns), nil, ns, warnHandler); err != nil {
   477  				t.Fatal(err)
   478  			}
   479  			warnHandler.reset()
   480  			testcase.object.Namespace = ns
   481  			_, err = client.CoreV1().ConfigMaps(ns).Create(context.TODO(), testcase.object, metav1.CreateOptions{})
   482  
   483  			code := int32(201)
   484  			if testcase.err != "" {
   485  				code = 422
   486  			}
   487  
   488  			auditAnnotationFilter := func(key, val string) bool {
   489  				_, ok := testcase.auditAnnotations[key]
   490  				return ok
   491  			}
   492  
   493  			checkExpectedError(t, err, testcase.err)
   494  			checkFailureReason(t, err, testcase.failureReason)
   495  			checkExpectedWarnings(t, warnHandler, testcase.warnings)
   496  			checkAuditEvents(t, logFile, expectedAuditEvents(testcase.auditAnnotations, ns, code), auditAnnotationFilter)
   497  		})
   498  	}
   499  }
   500  
   501  // Test_ValidateNamespace_WithConfigMapParams tests a ValidatingAdmissionPolicy that validates creation of a Namespace,
   502  // using ConfigMap as a param reference.
   503  func Test_ValidateNamespace_WithConfigMapParams(t *testing.T) {
   504  	testcases := []struct {
   505  		name          string
   506  		policy        *admissionregistrationv1beta1.ValidatingAdmissionPolicy
   507  		policyBinding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding
   508  		configMap     *v1.ConfigMap
   509  		namespace     *v1.Namespace
   510  		err           string
   511  		failureReason metav1.StatusReason
   512  	}{
   513  		{
   514  			name: "namespace name contains suffix enforced by validating admission policy",
   515  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   516  				{
   517  					Expression: "object.metadata.name.endsWith(params.data.namespaceSuffix)",
   518  				},
   519  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withParams(configParamKind(), withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   520  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", "validate-namespace-suffix-param"),
   521  			configMap: makeConfigParams("validate-namespace-suffix-param", map[string]string{
   522  				"namespaceSuffix": "k8s",
   523  			}),
   524  			namespace: &v1.Namespace{
   525  				ObjectMeta: metav1.ObjectMeta{
   526  					Name: "test-k8s",
   527  				},
   528  			},
   529  			err: "",
   530  		},
   531  		{
   532  			name: "namespace name does NOT contain suffix enforced by validating admission policy",
   533  			policy: withValidations([]admissionregistrationv1beta1.Validation{
   534  				{
   535  					Expression: "object.metadata.name.endsWith(params.data.namespaceSuffix)",
   536  				},
   537  			}, withFailurePolicy(admissionregistrationv1beta1.Fail, withParams(configParamKind(), withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
   538  			policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", "validate-namespace-suffix-param"),
   539  			configMap: makeConfigParams("validate-namespace-suffix-param", map[string]string{
   540  				"namespaceSuffix": "k8s",
   541  			}),
   542  			namespace: &v1.Namespace{
   543  				ObjectMeta: metav1.ObjectMeta{
   544  					Name: "test-foo",
   545  				},
   546  			},
   547  			err:           "namespaces \"test-foo\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith(params.data.namespaceSuffix)",
   548  			failureReason: metav1.StatusReasonInvalid,
   549  		},
   550  	}
   551  
   552  	for _, testcase := range testcases {
   553  		t.Run(testcase.name, func(t *testing.T) {
   554  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   555  			server, err := apiservertesting.StartTestServer(t, nil, []string{
   556  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   557  			}, framework.SharedEtcd())
   558  			if err != nil {
   559  				t.Fatal(err)
   560  			}
   561  			defer server.TearDownFn()
   562  
   563  			config := server.ClientConfig
   564  
   565  			client, err := clientset.NewForConfig(config)
   566  			if err != nil {
   567  				t.Fatal(err)
   568  			}
   569  
   570  			if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), testcase.configMap, metav1.CreateOptions{}); err != nil {
   571  				t.Fatal(err)
   572  			}
   573  
   574  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
   575  			if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   576  				t.Fatal(err)
   577  			}
   578  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
   579  				t.Fatal(err)
   580  			}
   581  
   582  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
   583  
   584  			checkExpectedError(t, err, testcase.err)
   585  			checkFailureReason(t, err, testcase.failureReason)
   586  		})
   587  	}
   588  }
   589  
   590  func TestMultiplePolicyBindings(t *testing.T) {
   591  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   592  	server, err := apiservertesting.StartTestServer(t, nil, nil, framework.SharedEtcd())
   593  	if err != nil {
   594  		t.Fatal(err)
   595  	}
   596  	defer server.TearDownFn()
   597  
   598  	config := server.ClientConfig
   599  
   600  	client, err := clientset.NewForConfig(config)
   601  	if err != nil {
   602  		t.Fatal(err)
   603  	}
   604  
   605  	paramKind := &admissionregistrationv1beta1.ParamKind{
   606  		APIVersion: "v1",
   607  		Kind:       "ConfigMap",
   608  	}
   609  	policy := withPolicyExistsLabels([]string{"paramIdent"}, withParams(paramKind, withPolicyMatch("secrets", withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("test-policy")))))
   610  	policy.Spec.Validations = []admissionregistrationv1beta1.Validation{
   611  		{
   612  			Expression: "params.data.autofail != 'true' && (params.data.conditional == 'false' || object.metadata.name.startsWith(params.data.check))",
   613  		},
   614  	}
   615  	policy = withWaitReadyConstraintAndExpression(policy)
   616  	if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
   617  		t.Fatal(err)
   618  	}
   619  
   620  	autoFailParams := makeConfigParams("autofail-params", map[string]string{
   621  		"autofail": "true",
   622  	})
   623  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), autoFailParams, metav1.CreateOptions{}); err != nil {
   624  		t.Fatal(err)
   625  	}
   626  	autofailBinding := withBindingExistsLabels([]string{"autofail-binding-label"}, policy, makeBinding("autofail-binding", "test-policy", "autofail-params"))
   627  	if err := createAndWaitReady(t, client, autofailBinding, map[string]string{"paramIdent": "true", "autofail-binding-label": "true"}); err != nil {
   628  		t.Fatal(err)
   629  	}
   630  
   631  	autoPassParams := makeConfigParams("autopass-params", map[string]string{
   632  		"autofail":    "false",
   633  		"conditional": "false",
   634  	})
   635  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), autoPassParams, metav1.CreateOptions{}); err != nil {
   636  		t.Fatal(err)
   637  	}
   638  	autopassBinding := withBindingExistsLabels([]string{"autopass-binding-label"}, policy, makeBinding("autopass-binding", "test-policy", "autopass-params"))
   639  	if err := createAndWaitReady(t, client, autopassBinding, map[string]string{"paramIdent": "true", "autopass-binding-label": "true"}); err != nil {
   640  		t.Fatal(err)
   641  	}
   642  
   643  	condpassParams := makeConfigParams("condpass-params", map[string]string{
   644  		"autofail":    "false",
   645  		"conditional": "true",
   646  		"check":       "prefix-",
   647  	})
   648  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), condpassParams, metav1.CreateOptions{}); err != nil {
   649  		t.Fatal(err)
   650  	}
   651  	condpassBinding := withBindingExistsLabels([]string{"condpass-binding-label"}, policy, makeBinding("condpass-binding", "test-policy", "condpass-params"))
   652  	if err := createAndWaitReady(t, client, condpassBinding, map[string]string{"paramIdent": "true", "condpass-binding-label": "true"}); err != nil {
   653  		t.Fatal(err)
   654  	}
   655  
   656  	autofailingSecret := &v1.Secret{
   657  		ObjectMeta: metav1.ObjectMeta{
   658  			Name: "autofailing-secret",
   659  			Labels: map[string]string{
   660  				"paramIdent":             "someVal",
   661  				"autofail-binding-label": "true",
   662  			},
   663  		},
   664  	}
   665  	_, err = client.CoreV1().Secrets("default").Create(context.TODO(), autofailingSecret, metav1.CreateOptions{})
   666  	if err == nil {
   667  		t.Fatal("expected secret creation to fail due to autofail-binding")
   668  	}
   669  	checkForFailedRule(t, err)
   670  	checkFailureReason(t, err, metav1.StatusReasonInvalid)
   671  
   672  	autopassingSecret := &v1.Secret{
   673  		ObjectMeta: metav1.ObjectMeta{
   674  			Name: "autopassing-secret",
   675  			Labels: map[string]string{
   676  				"paramIdent":             "someVal",
   677  				"autopass-binding-label": "true",
   678  			},
   679  		},
   680  	}
   681  	if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), autopassingSecret, metav1.CreateOptions{}); err != nil {
   682  		t.Fatalf("expected secret creation to succeed, got: %s", err)
   683  	}
   684  
   685  	condpassingSecret := &v1.Secret{
   686  		ObjectMeta: metav1.ObjectMeta{
   687  			Name: "prefix-condpassing-secret",
   688  			Labels: map[string]string{
   689  				"paramIdent":             "someVal",
   690  				"condpass-binding-label": "true",
   691  			},
   692  		},
   693  	}
   694  	if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), condpassingSecret, metav1.CreateOptions{}); err != nil {
   695  		t.Fatalf("expected secret creation to succeed, got: %s", err)
   696  	}
   697  
   698  	condfailingSecret := &v1.Secret{
   699  		ObjectMeta: metav1.ObjectMeta{
   700  			Name: "condfailing-secret",
   701  			Labels: map[string]string{
   702  				"paramIdent":             "someVal",
   703  				"condpass-binding-label": "true",
   704  			},
   705  		},
   706  	}
   707  	_, err = client.CoreV1().Secrets("default").Create(context.TODO(), condfailingSecret, metav1.CreateOptions{})
   708  	if err == nil {
   709  		t.Fatal("expected secret creation to fail due to autofail-binding")
   710  	}
   711  	checkForFailedRule(t, err)
   712  	checkFailureReason(t, err, metav1.StatusReasonInvalid)
   713  }
   714  
   715  // Test_PolicyExemption tests that ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding resources
   716  // are exempt from policy rules.
   717  func Test_PolicyExemption(t *testing.T) {
   718  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   719  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   720  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   721  	}, framework.SharedEtcd())
   722  	if err != nil {
   723  		t.Fatal(err)
   724  	}
   725  	defer server.TearDownFn()
   726  
   727  	config := server.ClientConfig
   728  
   729  	client, err := clientset.NewForConfig(config)
   730  	if err != nil {
   731  		t.Fatal(err)
   732  	}
   733  
   734  	policy := makePolicy("test-policy")
   735  	policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{
   736  		ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
   737  			{
   738  				RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
   739  					Operations: []admissionregistrationv1.OperationType{
   740  						"*",
   741  					},
   742  					Rule: admissionregistrationv1.Rule{
   743  						APIGroups: []string{
   744  							"*",
   745  						},
   746  						APIVersions: []string{
   747  							"*",
   748  						},
   749  						Resources: []string{
   750  							"*",
   751  						},
   752  					},
   753  				},
   754  			},
   755  		},
   756  	}
   757  
   758  	policy.Spec.Validations = []admissionregistrationv1beta1.Validation{{
   759  		Expression: "false",
   760  		Message:    "marker denied; policy is ready",
   761  	}}
   762  
   763  	policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
   764  	if err != nil {
   765  		t.Fatal(err)
   766  	}
   767  
   768  	policyBinding := makeBinding("test-policy-binding", "test-policy", "")
   769  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
   770  		t.Fatal(err)
   771  	}
   772  
   773  	// validate that operations to ValidatingAdmissionPolicy are exempt from an existing policy that catches all resources
   774  	policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Get(context.TODO(), policy.Name, metav1.GetOptions{})
   775  	if err != nil {
   776  		t.Fatal(err)
   777  	}
   778  	ignoreFailurePolicy := admissionregistrationv1beta1.Ignore
   779  	policy.Spec.FailurePolicy = &ignoreFailurePolicy
   780  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Update(context.TODO(), policy, metav1.UpdateOptions{})
   781  	if err != nil {
   782  		t.Error(err)
   783  	}
   784  
   785  	policyBinding, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Get(context.TODO(), policyBinding.Name, metav1.GetOptions{})
   786  	if err != nil {
   787  		t.Fatal(err)
   788  	}
   789  
   790  	// validate that operations to ValidatingAdmissionPolicyBindings are exempt from an existing policy that catches all resources
   791  	policyBindingCopy := policyBinding.DeepCopy()
   792  	policyBindingCopy.Spec.PolicyName = "different-binding"
   793  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Update(context.TODO(), policyBindingCopy, metav1.UpdateOptions{})
   794  	if err != nil {
   795  		t.Error(err)
   796  	}
   797  }
   798  
   799  // Test_ValidatingAdmissionPolicy_UpdateParamKind validates the behavior of ValidatingAdmissionPolicy when
   800  // only the ParamKind is updated. This test creates a policy where namespaces must have a prefix that matches
   801  // the ParamKind set in the policy. Switching the ParamKind should result in only namespaces with prefixes matching
   802  // the new ParamKind to be allowed. For example, when Paramkind is v1/ConfigMap, only namespaces prefixed with "configmap"
   803  // is allowed and when ParamKind is updated to v1/Secret, only namespaces prefixed with "secret" is allowed, etc.
   804  func Test_ValidatingAdmissionPolicy_UpdateParamKind(t *testing.T) {
   805  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   806  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   807  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   808  	}, framework.SharedEtcd())
   809  	if err != nil {
   810  		t.Fatal(err)
   811  	}
   812  	defer server.TearDownFn()
   813  
   814  	config := server.ClientConfig
   815  
   816  	client, err := clientset.NewForConfig(config)
   817  	if err != nil {
   818  		t.Fatal(err)
   819  	}
   820  
   821  	allowedPrefixesParamsConfigMap := &v1.ConfigMap{
   822  		ObjectMeta: metav1.ObjectMeta{
   823  			Name: "allowed-prefixes",
   824  		},
   825  	}
   826  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), allowedPrefixesParamsConfigMap, metav1.CreateOptions{}); err != nil {
   827  		t.Fatal(err)
   828  	}
   829  
   830  	allowedPrefixesParamSecret := &v1.Secret{
   831  		ObjectMeta: metav1.ObjectMeta{
   832  			Name: "allowed-prefixes",
   833  		},
   834  	}
   835  	if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), allowedPrefixesParamSecret, metav1.CreateOptions{}); err != nil {
   836  		t.Fatal(err)
   837  	}
   838  
   839  	paramKind := &admissionregistrationv1beta1.ParamKind{
   840  		APIVersion: "v1",
   841  		Kind:       "ConfigMap",
   842  	}
   843  
   844  	policy := withValidations([]admissionregistrationv1beta1.Validation{
   845  		{
   846  			Expression: "object.metadata.name.startsWith(params.kind.lowerAscii())",
   847  			Message:    "wrong paramKind",
   848  		},
   849  	}, withParams(paramKind, withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("allowed-prefixes")))))
   850  	policy = withWaitReadyConstraintAndExpression(policy)
   851  	policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
   852  	if err != nil {
   853  		t.Fatal(err)
   854  	}
   855  
   856  	allowedPrefixesBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "allowed-prefixes")
   857  	if err := createAndWaitReady(t, client, allowedPrefixesBinding, nil); err != nil {
   858  		t.Fatal(err)
   859  	}
   860  
   861  	// validate that namespaces starting with "configmap-" are allowed
   862  	// and namespaces starting with "secret-" are disallowed
   863  	allowedNamespace := &v1.Namespace{
   864  		ObjectMeta: metav1.ObjectMeta{
   865  			GenerateName: "configmap-",
   866  		},
   867  	}
   868  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
   869  	if err != nil {
   870  		t.Error(err)
   871  	}
   872  
   873  	disallowedNamespace := &v1.Namespace{
   874  		ObjectMeta: metav1.ObjectMeta{
   875  			GenerateName: "secret-",
   876  		},
   877  	}
   878  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
   879  	if err == nil {
   880  		t.Error("unexpected nil error")
   881  	}
   882  	if !strings.Contains(err.Error(), "wrong paramKind") {
   883  		t.Errorf("unexpected error message: %v", err)
   884  	}
   885  	checkFailureReason(t, err, metav1.StatusReasonInvalid)
   886  
   887  	// update the policy ParamKind to reference a Secret
   888  	paramKind = &admissionregistrationv1beta1.ParamKind{
   889  		APIVersion: "v1",
   890  		Kind:       "Secret",
   891  	}
   892  	policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Get(context.TODO(), policy.Name, metav1.GetOptions{})
   893  	if err != nil {
   894  		t.Error(err)
   895  	}
   896  	policy.Spec.ParamKind = paramKind
   897  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Update(context.TODO(), policy, metav1.UpdateOptions{})
   898  	if err != nil {
   899  		t.Error(err)
   900  	}
   901  
   902  	// validate that namespaces starting with "secret-" are allowed
   903  	// and namespaces starting with "configmap-" are disallowed
   904  	// wait loop is required here since ConfigMaps were previousy allowed and we need to wait for the new policy
   905  	// to be enforced
   906  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
   907  		disallowedNamespace = &v1.Namespace{
   908  			ObjectMeta: metav1.ObjectMeta{
   909  				GenerateName: "configmap-",
   910  			},
   911  		}
   912  
   913  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
   914  		if err == nil {
   915  			return false, nil
   916  		}
   917  
   918  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
   919  			return false, nil
   920  		}
   921  
   922  		if !strings.Contains(err.Error(), "wrong paramKind") {
   923  			return false, err
   924  		}
   925  
   926  		return true, nil
   927  	}); waitErr != nil {
   928  		t.Errorf("timed out waiting: %v", err)
   929  	}
   930  
   931  	allowedNamespace = &v1.Namespace{
   932  		ObjectMeta: metav1.ObjectMeta{
   933  			GenerateName: "secret-",
   934  		},
   935  	}
   936  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
   937  	if err != nil {
   938  		t.Error(err)
   939  	}
   940  }
   941  
   942  // Test_ValidatingAdmissionPolicy_UpdateParamRef validates the behavior of ValidatingAdmissionPolicy when
   943  // only the ParamRef in the binding is updated. This test creates a policy where namespaces must have a prefix that matches
   944  // the ParamRef set in the policy binding. The paramRef in the binding is then updated to a different object.
   945  func Test_ValidatingAdmissionPolicy_UpdateParamRef(t *testing.T) {
   946  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
   947  	server, err := apiservertesting.StartTestServer(t, nil, []string{
   948  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
   949  	}, framework.SharedEtcd())
   950  	if err != nil {
   951  		t.Fatal(err)
   952  	}
   953  	defer server.TearDownFn()
   954  
   955  	config := server.ClientConfig
   956  
   957  	client, err := clientset.NewForConfig(config)
   958  	if err != nil {
   959  		t.Fatal(err)
   960  	}
   961  
   962  	allowedPrefixesParamsConfigMap1 := &v1.ConfigMap{
   963  		ObjectMeta: metav1.ObjectMeta{
   964  			Name:      "test-1",
   965  			Namespace: "default",
   966  		},
   967  	}
   968  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), allowedPrefixesParamsConfigMap1, metav1.CreateOptions{}); err != nil {
   969  		t.Fatal(err)
   970  	}
   971  
   972  	allowedPrefixesParamsConfigMap2 := &v1.ConfigMap{
   973  		ObjectMeta: metav1.ObjectMeta{
   974  			Name:      "test-2",
   975  			Namespace: "default",
   976  		},
   977  	}
   978  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), allowedPrefixesParamsConfigMap2, metav1.CreateOptions{}); err != nil {
   979  		t.Fatal(err)
   980  	}
   981  
   982  	policy := withValidations([]admissionregistrationv1beta1.Validation{
   983  		{
   984  			Expression: "object.metadata.name.startsWith(params.metadata.name)",
   985  			Message:    "wrong paramRef",
   986  		},
   987  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("allowed-prefixes")))))
   988  	policy = withWaitReadyConstraintAndExpression(policy)
   989  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
   990  	if err != nil {
   991  		t.Fatal(err)
   992  	}
   993  
   994  	// validate that namespaces starting with "test-1" are allowed
   995  	// and namespaces starting with "test-2-" are disallowed
   996  	allowedPrefixesBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "test-1")
   997  	if err := createAndWaitReady(t, client, allowedPrefixesBinding, nil); err != nil {
   998  		t.Fatal(err)
   999  	}
  1000  
  1001  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1002  		disallowedNamespace := &v1.Namespace{
  1003  			ObjectMeta: metav1.ObjectMeta{
  1004  				GenerateName: "test-2-",
  1005  			},
  1006  		}
  1007  
  1008  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1009  		if err == nil {
  1010  			return false, nil
  1011  		}
  1012  
  1013  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1014  			return false, nil
  1015  		}
  1016  
  1017  		if !strings.Contains(err.Error(), "wrong paramRef") {
  1018  			return false, err
  1019  		}
  1020  
  1021  		return true, nil
  1022  	}); waitErr != nil {
  1023  		t.Errorf("timed out waiting: %v", err)
  1024  	}
  1025  
  1026  	allowedNamespace := &v1.Namespace{
  1027  		ObjectMeta: metav1.ObjectMeta{
  1028  			GenerateName: "test-1-",
  1029  		},
  1030  	}
  1031  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1032  	if err != nil {
  1033  		t.Error(err)
  1034  	}
  1035  
  1036  	// Update the paramRef in the policy binding to use the test-2 ConfigMap
  1037  	policyBinding, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Get(context.TODO(), allowedPrefixesBinding.Name, metav1.GetOptions{})
  1038  	if err != nil {
  1039  		t.Fatal(err)
  1040  	}
  1041  
  1042  	denyAction := admissionregistrationv1beta1.DenyAction
  1043  	policyBindingCopy := policyBinding.DeepCopy()
  1044  	policyBindingCopy.Spec.ParamRef = &admissionregistrationv1beta1.ParamRef{
  1045  		Name:                    "test-2",
  1046  		Namespace:               "default",
  1047  		ParameterNotFoundAction: &denyAction,
  1048  	}
  1049  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Update(context.TODO(), policyBindingCopy, metav1.UpdateOptions{})
  1050  	if err != nil {
  1051  		t.Error(err)
  1052  	}
  1053  
  1054  	// validate that namespaces starting with "test-2" are allowed
  1055  	// and namespaces starting with "test-1" are disallowed
  1056  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1057  		disallowedNamespace := &v1.Namespace{
  1058  			ObjectMeta: metav1.ObjectMeta{
  1059  				GenerateName: "test-1-",
  1060  			},
  1061  		}
  1062  
  1063  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1064  		if err == nil {
  1065  			return false, nil
  1066  		}
  1067  
  1068  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1069  			return false, nil
  1070  		}
  1071  
  1072  		if !strings.Contains(err.Error(), "wrong paramRef") {
  1073  			return false, err
  1074  		}
  1075  
  1076  		return true, nil
  1077  	}); waitErr != nil {
  1078  		t.Errorf("timed out waiting: %v", err)
  1079  	}
  1080  
  1081  	allowedNamespace = &v1.Namespace{
  1082  		ObjectMeta: metav1.ObjectMeta{
  1083  			GenerateName: "test-2-",
  1084  		},
  1085  	}
  1086  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1087  	if err != nil {
  1088  		t.Error(err)
  1089  	}
  1090  }
  1091  
  1092  // Test_ValidatingAdmissionPolicy_UpdateParamResource validates behavior of a policy after updates to the param resource.
  1093  func Test_ValidatingAdmissionPolicy_UpdateParamResource(t *testing.T) {
  1094  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1095  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1096  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1097  	}, framework.SharedEtcd())
  1098  	if err != nil {
  1099  		t.Fatal(err)
  1100  	}
  1101  	defer server.TearDownFn()
  1102  
  1103  	config := server.ClientConfig
  1104  
  1105  	client, err := clientset.NewForConfig(config)
  1106  	if err != nil {
  1107  		t.Fatal(err)
  1108  	}
  1109  
  1110  	paramConfigMap := &v1.ConfigMap{
  1111  		ObjectMeta: metav1.ObjectMeta{
  1112  			Name:      "allowed-prefix",
  1113  			Namespace: "default",
  1114  		},
  1115  		Data: map[string]string{
  1116  			"prefix": "test-1",
  1117  		},
  1118  	}
  1119  	paramConfigMap, err = client.CoreV1().ConfigMaps(paramConfigMap.Namespace).Create(context.TODO(), paramConfigMap, metav1.CreateOptions{})
  1120  	if err != nil {
  1121  		t.Fatal(err)
  1122  	}
  1123  
  1124  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1125  		{
  1126  			Expression: "object.metadata.name.startsWith(params.data['prefix'])",
  1127  			Message:    "wrong prefix",
  1128  		},
  1129  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("allowed-prefixes")))))
  1130  	policy = withWaitReadyConstraintAndExpression(policy)
  1131  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1132  	if err != nil {
  1133  		t.Fatal(err)
  1134  	}
  1135  
  1136  	// validate that namespaces starting with "test-1" are allowed
  1137  	// and namespaces starting with "test-2-" are disallowed
  1138  	allowedPrefixesBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "allowed-prefix")
  1139  	if err := createAndWaitReady(t, client, allowedPrefixesBinding, nil); err != nil {
  1140  		t.Fatal(err)
  1141  	}
  1142  
  1143  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1144  		disallowedNamespace := &v1.Namespace{
  1145  			ObjectMeta: metav1.ObjectMeta{
  1146  				GenerateName: "test-2-",
  1147  			},
  1148  		}
  1149  
  1150  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1151  		if err == nil {
  1152  			return false, nil
  1153  		}
  1154  
  1155  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1156  			return false, nil
  1157  		}
  1158  
  1159  		if !strings.Contains(err.Error(), "wrong prefix") {
  1160  			return false, err
  1161  		}
  1162  
  1163  		return true, nil
  1164  	}); waitErr != nil {
  1165  		t.Errorf("timed out waiting: %v", err)
  1166  	}
  1167  
  1168  	allowedNamespace := &v1.Namespace{
  1169  		ObjectMeta: metav1.ObjectMeta{
  1170  			GenerateName: "test-1-",
  1171  		},
  1172  	}
  1173  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1174  	if err != nil {
  1175  		t.Error(err)
  1176  	}
  1177  
  1178  	// Update the param resource to use "test-2" as the new allwoed prefix
  1179  	paramConfigMapCopy := paramConfigMap.DeepCopy()
  1180  	paramConfigMapCopy.Data = map[string]string{
  1181  		"prefix": "test-2",
  1182  	}
  1183  	_, err = client.CoreV1().ConfigMaps(paramConfigMapCopy.Namespace).Update(context.TODO(), paramConfigMapCopy, metav1.UpdateOptions{})
  1184  	if err != nil {
  1185  		t.Fatal(err)
  1186  	}
  1187  
  1188  	// validate that namespaces starting with "test-2" are allowed
  1189  	// and namespaces starting with "test-1" are disallowed
  1190  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1191  		disallowedNamespace := &v1.Namespace{
  1192  			ObjectMeta: metav1.ObjectMeta{
  1193  				GenerateName: "test-1-",
  1194  			},
  1195  		}
  1196  
  1197  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1198  		if err == nil {
  1199  			return false, nil
  1200  		}
  1201  
  1202  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1203  			return false, nil
  1204  		}
  1205  
  1206  		if !strings.Contains(err.Error(), "wrong prefix") {
  1207  			return false, err
  1208  		}
  1209  
  1210  		return true, nil
  1211  	}); waitErr != nil {
  1212  		t.Errorf("timed out waiting: %v", err)
  1213  	}
  1214  
  1215  	allowedNamespace = &v1.Namespace{
  1216  		ObjectMeta: metav1.ObjectMeta{
  1217  			GenerateName: "test-2-",
  1218  		},
  1219  	}
  1220  	_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1221  	if err != nil {
  1222  		t.Error(err)
  1223  	}
  1224  }
  1225  
  1226  func Test_ValidatingAdmissionPolicy_MatchByObjectSelector(t *testing.T) {
  1227  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1228  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1229  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1230  	}, framework.SharedEtcd())
  1231  	if err != nil {
  1232  		t.Fatal(err)
  1233  	}
  1234  	defer server.TearDownFn()
  1235  
  1236  	config := server.ClientConfig
  1237  
  1238  	client, err := clientset.NewForConfig(config)
  1239  	if err != nil {
  1240  		t.Fatal(err)
  1241  	}
  1242  
  1243  	labelSelector := &metav1.LabelSelector{
  1244  		MatchLabels: map[string]string{
  1245  			"foo": "bar",
  1246  		},
  1247  	}
  1248  
  1249  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1250  		{
  1251  			Expression: "false",
  1252  			Message:    "matched by object selector!",
  1253  		},
  1254  	}, withConfigMapMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("match-by-object-selector"))))
  1255  	policy = withObjectSelector(labelSelector, policy)
  1256  	policy = withWaitReadyConstraintAndExpression(policy)
  1257  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1258  	if err != nil {
  1259  		t.Fatal(err)
  1260  	}
  1261  
  1262  	policyBinding := makeBinding("match-by-object-selector-binding", "match-by-object-selector", "")
  1263  	if err := createAndWaitReady(t, client, policyBinding, map[string]string{"foo": "bar"}); err != nil {
  1264  		t.Fatal(err)
  1265  	}
  1266  
  1267  	matchedConfigMap := &v1.ConfigMap{
  1268  		ObjectMeta: metav1.ObjectMeta{
  1269  			Name:      "denied",
  1270  			Namespace: "default",
  1271  			Labels: map[string]string{
  1272  				"foo": "bar",
  1273  			},
  1274  		},
  1275  	}
  1276  
  1277  	_, err = client.CoreV1().ConfigMaps(matchedConfigMap.Namespace).Create(context.TODO(), matchedConfigMap, metav1.CreateOptions{})
  1278  	if !strings.Contains(err.Error(), "matched by object selector!") {
  1279  		t.Errorf("unexpected error: %v", err)
  1280  	}
  1281  
  1282  	allowedConfigMap := &v1.ConfigMap{
  1283  		ObjectMeta: metav1.ObjectMeta{
  1284  			Name:      "allowed",
  1285  			Namespace: "default",
  1286  		},
  1287  	}
  1288  
  1289  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1290  		t.Errorf("unexpected error: %v", err)
  1291  	}
  1292  }
  1293  
  1294  func Test_ValidatingAdmissionPolicy_MatchByNamespaceSelector(t *testing.T) {
  1295  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1296  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1297  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1298  	}, framework.SharedEtcd())
  1299  	if err != nil {
  1300  		t.Fatal(err)
  1301  	}
  1302  	defer server.TearDownFn()
  1303  
  1304  	config := server.ClientConfig
  1305  
  1306  	client, err := clientset.NewForConfig(config)
  1307  	if err != nil {
  1308  		t.Fatal(err)
  1309  	}
  1310  
  1311  	// only configmaps in default will be allowed.
  1312  	labelSelector := &metav1.LabelSelector{
  1313  		MatchExpressions: []metav1.LabelSelectorRequirement{
  1314  			{
  1315  				Key:      "kubernetes.io/metadata.name",
  1316  				Operator: "NotIn",
  1317  				Values:   []string{"default"},
  1318  			},
  1319  		},
  1320  	}
  1321  
  1322  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1323  		{
  1324  			Expression: "false",
  1325  			Message:    "matched by namespace selector!",
  1326  		},
  1327  	}, withConfigMapMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("match-by-namespace-selector"))))
  1328  	policy = withNamespaceSelector(labelSelector, policy)
  1329  	policy = withWaitReadyConstraintAndExpression(policy)
  1330  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1331  	if err != nil {
  1332  		t.Fatal(err)
  1333  	}
  1334  
  1335  	policyBinding := makeBinding("match-by-namespace-selector-binding", "match-by-namespace-selector", "")
  1336  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), policyBinding, metav1.CreateOptions{})
  1337  	if err != nil {
  1338  		t.Fatal(err)
  1339  	}
  1340  
  1341  	namespace := &v1.Namespace{
  1342  		ObjectMeta: metav1.ObjectMeta{
  1343  			Name: "not-default",
  1344  		},
  1345  	}
  1346  	if _, err := client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}); err != nil {
  1347  		t.Fatal(err)
  1348  	}
  1349  
  1350  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1351  		matchedConfigMap := &v1.ConfigMap{
  1352  			ObjectMeta: metav1.ObjectMeta{
  1353  				GenerateName: "denied-",
  1354  				Namespace:    "not-default",
  1355  			},
  1356  		}
  1357  
  1358  		_, err := client.CoreV1().ConfigMaps(matchedConfigMap.Namespace).Create(context.TODO(), matchedConfigMap, metav1.CreateOptions{})
  1359  		// policy not enforced yet, try again
  1360  		if err == nil {
  1361  			return false, nil
  1362  		}
  1363  
  1364  		if !strings.Contains(err.Error(), "matched by namespace selector!") {
  1365  			return false, err
  1366  		}
  1367  
  1368  		return true, nil
  1369  
  1370  	}); waitErr != nil {
  1371  		t.Errorf("timed out waiting: %v", waitErr)
  1372  	}
  1373  
  1374  	allowedConfigMap := &v1.ConfigMap{
  1375  		ObjectMeta: metav1.ObjectMeta{
  1376  			Name:      "allowed",
  1377  			Namespace: "default",
  1378  		},
  1379  	}
  1380  
  1381  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1382  		t.Errorf("unexpected error: %v", err)
  1383  	}
  1384  }
  1385  
  1386  func Test_ValidatingAdmissionPolicy_MatchByResourceNames(t *testing.T) {
  1387  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1388  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1389  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1390  	}, framework.SharedEtcd())
  1391  	if err != nil {
  1392  		t.Fatal(err)
  1393  	}
  1394  	defer server.TearDownFn()
  1395  
  1396  	config := server.ClientConfig
  1397  
  1398  	client, err := clientset.NewForConfig(config)
  1399  	if err != nil {
  1400  		t.Fatal(err)
  1401  	}
  1402  
  1403  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1404  		{
  1405  			Expression: "false",
  1406  			Message:    "matched by resource names!",
  1407  		},
  1408  	}, withConfigMapMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("match-by-resource-names"))))
  1409  	policy.Spec.MatchConstraints.ResourceRules[0].ResourceNames = []string{"matched-by-resource-name"}
  1410  	policy = withWaitReadyConstraintAndExpression(policy)
  1411  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1412  	if err != nil {
  1413  		t.Fatal(err)
  1414  	}
  1415  
  1416  	policyBinding := makeBinding("match-by-resource-names-binding", "match-by-resource-names", "")
  1417  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1418  		t.Fatal(err)
  1419  	}
  1420  
  1421  	matchedConfigMap := &v1.ConfigMap{
  1422  		ObjectMeta: metav1.ObjectMeta{
  1423  			Name:      "matched-by-resource-name",
  1424  			Namespace: "default",
  1425  		},
  1426  	}
  1427  
  1428  	_, err = client.CoreV1().ConfigMaps(matchedConfigMap.Namespace).Create(context.TODO(), matchedConfigMap, metav1.CreateOptions{})
  1429  	if !strings.Contains(err.Error(), "matched by resource names!") {
  1430  		t.Errorf("unexpected error: %v", err)
  1431  	}
  1432  
  1433  	allowedConfigMap := &v1.ConfigMap{
  1434  		ObjectMeta: metav1.ObjectMeta{
  1435  			Name:      "not-matched-by-resource-name",
  1436  			Namespace: "default",
  1437  		},
  1438  	}
  1439  
  1440  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1441  		t.Errorf("unexpected error: %v", err)
  1442  	}
  1443  }
  1444  
  1445  func Test_ValidatingAdmissionPolicy_MatchWithExcludeResources(t *testing.T) {
  1446  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1447  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1448  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1449  	}, framework.SharedEtcd())
  1450  	if err != nil {
  1451  		t.Fatal(err)
  1452  	}
  1453  	defer server.TearDownFn()
  1454  
  1455  	config := server.ClientConfig
  1456  
  1457  	client, err := clientset.NewForConfig(config)
  1458  	if err != nil {
  1459  		t.Fatal(err)
  1460  	}
  1461  
  1462  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1463  		{
  1464  			Expression: "false",
  1465  			Message:    "not matched by exclude resources!",
  1466  		},
  1467  	}, withPolicyMatch("*", withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("match-by-resource-names"))))
  1468  
  1469  	policy = withExcludePolicyMatch("configmaps", policy)
  1470  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1471  	if err != nil {
  1472  		t.Fatal(err)
  1473  	}
  1474  
  1475  	policyBinding := makeBinding("match-by-resource-names-binding", "match-by-resource-names", "")
  1476  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), policyBinding, metav1.CreateOptions{})
  1477  	if err != nil {
  1478  		t.Fatal(err)
  1479  	}
  1480  
  1481  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1482  		secret := &v1.Secret{
  1483  			ObjectMeta: metav1.ObjectMeta{
  1484  				GenerateName: "not-matched-by-exclude-resources",
  1485  				Namespace:    "default",
  1486  			},
  1487  		}
  1488  
  1489  		_, err := client.CoreV1().Secrets(secret.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
  1490  		// policy not enforced yet, try again
  1491  		if err == nil {
  1492  			return false, nil
  1493  		}
  1494  
  1495  		if !strings.Contains(err.Error(), "not matched by exclude resources!") {
  1496  			return false, err
  1497  		}
  1498  
  1499  		return true, nil
  1500  
  1501  	}); waitErr != nil {
  1502  		t.Errorf("timed out waiting: %v", waitErr)
  1503  	}
  1504  
  1505  	allowedConfigMap := &v1.ConfigMap{
  1506  		ObjectMeta: metav1.ObjectMeta{
  1507  			Name:      "matched-by-exclude-resources",
  1508  			Namespace: "default",
  1509  		},
  1510  	}
  1511  
  1512  	if _, err := client.CoreV1().ConfigMaps(allowedConfigMap.Namespace).Create(context.TODO(), allowedConfigMap, metav1.CreateOptions{}); err != nil {
  1513  		t.Errorf("unexpected error: %v", err)
  1514  	}
  1515  }
  1516  
  1517  func Test_ValidatingAdmissionPolicy_MatchWithMatchPolicyEquivalent(t *testing.T) {
  1518  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1519  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1520  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1521  	}, framework.SharedEtcd())
  1522  	etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, versionedCustomResourceDefinition())
  1523  	if err != nil {
  1524  		t.Fatal(err)
  1525  	}
  1526  	defer server.TearDownFn()
  1527  
  1528  	config := server.ClientConfig
  1529  
  1530  	client, err := clientset.NewForConfig(config)
  1531  	if err != nil {
  1532  		t.Fatal(err)
  1533  	}
  1534  
  1535  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1536  		{
  1537  			Expression: "false",
  1538  			Message:    "matched by equivalent match policy!",
  1539  		},
  1540  	}, withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("match-by-match-policy-equivalent")))
  1541  	policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{
  1542  		ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
  1543  			{
  1544  				RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
  1545  					Operations: []admissionregistrationv1.OperationType{
  1546  						"*",
  1547  					},
  1548  					Rule: admissionregistrationv1.Rule{
  1549  						APIGroups: []string{
  1550  							"awesome.bears.com",
  1551  						},
  1552  						APIVersions: []string{
  1553  							"v1",
  1554  						},
  1555  						Resources: []string{
  1556  							"pandas",
  1557  						},
  1558  					},
  1559  				},
  1560  			},
  1561  		},
  1562  	}
  1563  	policy = withWaitReadyConstraintAndExpression(policy)
  1564  	if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  1565  		t.Fatal(err)
  1566  	}
  1567  
  1568  	policyBinding := makeBinding("match-by-match-policy-equivalent-binding", "match-by-match-policy-equivalent", "")
  1569  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1570  		t.Fatal(err)
  1571  	}
  1572  
  1573  	v1Resource := &unstructured.Unstructured{
  1574  		Object: map[string]interface{}{
  1575  			"apiVersion": "awesome.bears.com" + "/" + "v1",
  1576  			"kind":       "Panda",
  1577  			"metadata": map[string]interface{}{
  1578  				"name": "v1-bears",
  1579  			},
  1580  		},
  1581  	}
  1582  
  1583  	v2Resource := &unstructured.Unstructured{
  1584  		Object: map[string]interface{}{
  1585  			"apiVersion": "awesome.bears.com" + "/" + "v2",
  1586  			"kind":       "Panda",
  1587  			"metadata": map[string]interface{}{
  1588  				"name": "v2-bears",
  1589  			},
  1590  		},
  1591  	}
  1592  
  1593  	dynamicClient, err := dynamic.NewForConfig(config)
  1594  	if err != nil {
  1595  		t.Fatal(err)
  1596  	}
  1597  
  1598  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v1", Resource: "pandas"}).Create(context.TODO(), v1Resource, metav1.CreateOptions{})
  1599  	if !strings.Contains(err.Error(), "matched by equivalent match policy!") {
  1600  		t.Errorf("v1 panadas did not match against policy, err: %v", err)
  1601  	}
  1602  
  1603  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v2", Resource: "pandas"}).Create(context.TODO(), v2Resource, metav1.CreateOptions{})
  1604  	if !strings.Contains(err.Error(), "matched by equivalent match policy!") {
  1605  		t.Errorf("v2 panadas did not match against policy, err: %v", err)
  1606  	}
  1607  }
  1608  
  1609  func Test_ValidatingAdmissionPolicy_MatchWithMatchPolicyExact(t *testing.T) {
  1610  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1611  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1612  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1613  	}, framework.SharedEtcd())
  1614  	etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, versionedCustomResourceDefinition())
  1615  	if err != nil {
  1616  		t.Fatal(err)
  1617  	}
  1618  	defer server.TearDownFn()
  1619  
  1620  	config := server.ClientConfig
  1621  
  1622  	client, err := clientset.NewForConfig(config)
  1623  	if err != nil {
  1624  		t.Fatal(err)
  1625  	}
  1626  
  1627  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1628  		{
  1629  			Expression: "false",
  1630  			Message:    "matched by exact match policy!",
  1631  		},
  1632  	}, withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("match-by-match-policy-exact")))
  1633  	matchPolicyExact := admissionregistrationv1beta1.Exact
  1634  	policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{
  1635  		MatchPolicy: &matchPolicyExact,
  1636  		ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
  1637  			{
  1638  				RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
  1639  					Operations: []admissionregistrationv1.OperationType{
  1640  						"*",
  1641  					},
  1642  					Rule: admissionregistrationv1.Rule{
  1643  						APIGroups: []string{
  1644  							"awesome.bears.com",
  1645  						},
  1646  						APIVersions: []string{
  1647  							"v1",
  1648  						},
  1649  						Resources: []string{
  1650  							"pandas",
  1651  						},
  1652  					},
  1653  				},
  1654  			},
  1655  		},
  1656  	}
  1657  	policy = withWaitReadyConstraintAndExpression(policy)
  1658  	if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  1659  		t.Fatal(err)
  1660  	}
  1661  
  1662  	policyBinding := makeBinding("match-by-match-policy-exact-binding", "match-by-match-policy-exact", "")
  1663  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1664  		t.Fatal(err)
  1665  	}
  1666  
  1667  	v1Resource := &unstructured.Unstructured{
  1668  		Object: map[string]interface{}{
  1669  			"apiVersion": "awesome.bears.com" + "/" + "v1",
  1670  			"kind":       "Panda",
  1671  			"metadata": map[string]interface{}{
  1672  				"name": "v1-bears",
  1673  			},
  1674  		},
  1675  	}
  1676  
  1677  	v2Resource := &unstructured.Unstructured{
  1678  		Object: map[string]interface{}{
  1679  			"apiVersion": "awesome.bears.com" + "/" + "v2",
  1680  			"kind":       "Panda",
  1681  			"metadata": map[string]interface{}{
  1682  				"name": "v2-bears",
  1683  			},
  1684  		},
  1685  	}
  1686  
  1687  	dynamicClient, err := dynamic.NewForConfig(config)
  1688  	if err != nil {
  1689  		t.Fatal(err)
  1690  	}
  1691  
  1692  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v1", Resource: "pandas"}).Create(context.TODO(), v1Resource, metav1.CreateOptions{})
  1693  	if !strings.Contains(err.Error(), "matched by exact match policy!") {
  1694  		t.Errorf("v1 panadas did not match against policy, err: %v", err)
  1695  	}
  1696  
  1697  	// v2 panadas is allowed since policy specificed match policy Exact and only matched against v1
  1698  	_, err = dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v2", Resource: "pandas"}).Create(context.TODO(), v2Resource, metav1.CreateOptions{})
  1699  	if err != nil {
  1700  		t.Error(err)
  1701  	}
  1702  }
  1703  
  1704  // Test_ValidatingAdmissionPolicy_PolicyDeletedThenRecreated validates that deleting a ValidatingAdmissionPolicy
  1705  // removes the policy from the apiserver admission chain and recreating it re-enables it.
  1706  func Test_ValidatingAdmissionPolicy_PolicyDeletedThenRecreated(t *testing.T) {
  1707  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1708  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1709  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1710  	}, framework.SharedEtcd())
  1711  	if err != nil {
  1712  		t.Fatal(err)
  1713  	}
  1714  	defer server.TearDownFn()
  1715  
  1716  	config := server.ClientConfig
  1717  
  1718  	client, err := clientset.NewForConfig(config)
  1719  	if err != nil {
  1720  		t.Fatal(err)
  1721  	}
  1722  
  1723  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1724  		{
  1725  			Expression: "object.metadata.name.startsWith('test')",
  1726  			Message:    "wrong prefix",
  1727  		},
  1728  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("allowed-prefixes")))))
  1729  	policy = withWaitReadyConstraintAndExpression(policy)
  1730  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1731  	if err != nil {
  1732  		t.Fatal(err)
  1733  	}
  1734  
  1735  	// validate that namespaces starting with "test" are allowed
  1736  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "")
  1737  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1738  		t.Fatal(err)
  1739  	}
  1740  
  1741  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1742  		disallowedNamespace := &v1.Namespace{
  1743  			ObjectMeta: metav1.ObjectMeta{
  1744  				GenerateName: "not-test-",
  1745  			},
  1746  		}
  1747  
  1748  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1749  		if err == nil {
  1750  			return false, nil
  1751  		}
  1752  
  1753  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1754  			return false, nil
  1755  		}
  1756  
  1757  		if !strings.Contains(err.Error(), "wrong prefix") {
  1758  			return false, err
  1759  		}
  1760  
  1761  		return true, nil
  1762  	}); waitErr != nil {
  1763  		t.Errorf("timed out waiting: %v", err)
  1764  	}
  1765  
  1766  	// delete the binding object and validate that policy is not enforced
  1767  	if err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(context.TODO(), "allowed-prefixes", metav1.DeleteOptions{}); err != nil {
  1768  		t.Fatal(err)
  1769  	}
  1770  
  1771  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1772  		allowedNamespace := &v1.Namespace{
  1773  			ObjectMeta: metav1.ObjectMeta{
  1774  				GenerateName: "not-test-",
  1775  			},
  1776  		}
  1777  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1778  		if err == nil {
  1779  			return true, nil
  1780  		}
  1781  
  1782  		// old policy is still enforced, try again
  1783  		if strings.Contains(err.Error(), "wrong prefix") {
  1784  			return false, nil
  1785  		}
  1786  
  1787  		return false, err
  1788  	}); waitErr != nil {
  1789  		t.Errorf("timed out waiting: %v", err)
  1790  	}
  1791  
  1792  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1793  	if err != nil {
  1794  		t.Fatal(err)
  1795  	}
  1796  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1797  		disallowedNamespace := &v1.Namespace{
  1798  			ObjectMeta: metav1.ObjectMeta{
  1799  				GenerateName: "not-test-",
  1800  			},
  1801  		}
  1802  
  1803  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1804  		if err == nil {
  1805  			return false, nil
  1806  		}
  1807  
  1808  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1809  			return false, nil
  1810  		}
  1811  
  1812  		if !strings.Contains(err.Error(), "wrong prefix") {
  1813  			return false, err
  1814  		}
  1815  
  1816  		return true, nil
  1817  	}); waitErr != nil {
  1818  		t.Errorf("timed out waiting: %v", err)
  1819  	}
  1820  }
  1821  
  1822  // Test_ValidatingAdmissionPolicy_BindingDeletedThenRecreated validates that deleting a ValidatingAdmissionPolicyBinding
  1823  // removes the policy from the apiserver admission chain and recreating it re-enables it.
  1824  func Test_ValidatingAdmissionPolicy_BindingDeletedThenRecreated(t *testing.T) {
  1825  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1826  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1827  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1828  	}, framework.SharedEtcd())
  1829  	if err != nil {
  1830  		t.Fatal(err)
  1831  	}
  1832  	defer server.TearDownFn()
  1833  
  1834  	config := server.ClientConfig
  1835  
  1836  	client, err := clientset.NewForConfig(config)
  1837  	if err != nil {
  1838  		t.Fatal(err)
  1839  	}
  1840  
  1841  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1842  		{
  1843  			Expression: "object.metadata.name.startsWith('test')",
  1844  			Message:    "wrong prefix",
  1845  		},
  1846  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("allowed-prefixes")))))
  1847  	policy = withWaitReadyConstraintAndExpression(policy)
  1848  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1849  	if err != nil {
  1850  		t.Fatal(err)
  1851  	}
  1852  
  1853  	// validate that namespaces starting with "test" are allowed
  1854  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "")
  1855  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1856  		t.Fatal(err)
  1857  	}
  1858  
  1859  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1860  		disallowedNamespace := &v1.Namespace{
  1861  			ObjectMeta: metav1.ObjectMeta{
  1862  				GenerateName: "not-test-",
  1863  			},
  1864  		}
  1865  
  1866  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1867  		if err == nil {
  1868  			return false, nil
  1869  		}
  1870  
  1871  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1872  			return false, nil
  1873  		}
  1874  
  1875  		if !strings.Contains(err.Error(), "wrong prefix") {
  1876  			return false, err
  1877  		}
  1878  
  1879  		return true, nil
  1880  	}); waitErr != nil {
  1881  		t.Errorf("timed out waiting: %v", err)
  1882  	}
  1883  
  1884  	// delete the binding object and validate that policy is not enforced
  1885  	if err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Delete(context.TODO(), "allowed-prefixes-binding", metav1.DeleteOptions{}); err != nil {
  1886  		t.Fatal(err)
  1887  	}
  1888  
  1889  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1890  		allowedNamespace := &v1.Namespace{
  1891  			ObjectMeta: metav1.ObjectMeta{
  1892  				GenerateName: "not-test-",
  1893  			},
  1894  		}
  1895  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  1896  		if err == nil {
  1897  			return true, nil
  1898  		}
  1899  
  1900  		// old policy is still enforced, try again
  1901  		if strings.Contains(err.Error(), "wrong prefix") {
  1902  			return false, nil
  1903  		}
  1904  
  1905  		return false, err
  1906  	}); waitErr != nil {
  1907  		t.Errorf("timed out waiting: %v", err)
  1908  	}
  1909  
  1910  	// recreate the policy binding and test that policy is enforced again
  1911  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), policyBinding, metav1.CreateOptions{})
  1912  	if err != nil {
  1913  		t.Fatal(err)
  1914  	}
  1915  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1916  		disallowedNamespace := &v1.Namespace{
  1917  			ObjectMeta: metav1.ObjectMeta{
  1918  				GenerateName: "not-test-",
  1919  			},
  1920  		}
  1921  
  1922  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1923  		if err == nil {
  1924  			return false, nil
  1925  		}
  1926  
  1927  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  1928  			return false, nil
  1929  		}
  1930  
  1931  		if !strings.Contains(err.Error(), "wrong prefix") {
  1932  			return false, err
  1933  		}
  1934  
  1935  		return true, nil
  1936  	}); waitErr != nil {
  1937  		t.Errorf("timed out waiting: %v", err)
  1938  	}
  1939  }
  1940  
  1941  // Test_ValidatingAdmissionPolicy_ParamResourceDeletedThenRecreated validates that deleting a param resource referenced
  1942  // by a binding renders the policy as invalid. Recreating the param resource re-enables the policy.
  1943  func Test_ValidatingAdmissionPolicy_ParamResourceDeletedThenRecreated(t *testing.T) {
  1944  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  1945  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  1946  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  1947  	}, framework.SharedEtcd())
  1948  	if err != nil {
  1949  		t.Fatal(err)
  1950  	}
  1951  	defer server.TearDownFn()
  1952  
  1953  	config := server.ClientConfig
  1954  
  1955  	client, err := clientset.NewForConfig(config)
  1956  	if err != nil {
  1957  		t.Fatal(err)
  1958  	}
  1959  
  1960  	param := &v1.ConfigMap{
  1961  		ObjectMeta: metav1.ObjectMeta{
  1962  			Name:      "test",
  1963  			Namespace: "default",
  1964  		},
  1965  	}
  1966  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), param, metav1.CreateOptions{}); err != nil {
  1967  		t.Fatal(err)
  1968  	}
  1969  
  1970  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  1971  		{
  1972  			Expression: "object.metadata.name.startsWith(params.metadata.name)",
  1973  			Message:    "wrong prefix",
  1974  		},
  1975  	}, withParams(configParamKind(), withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("allowed-prefixes")))))
  1976  	policy = withWaitReadyConstraintAndExpression(policy)
  1977  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  1978  	if err != nil {
  1979  		t.Fatal(err)
  1980  	}
  1981  
  1982  	// validate that namespaces starting with "test" are allowed
  1983  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "test")
  1984  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  1985  		t.Fatal(err)
  1986  	}
  1987  
  1988  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  1989  		disallowedNamespace := &v1.Namespace{
  1990  			ObjectMeta: metav1.ObjectMeta{
  1991  				GenerateName: "not-test-",
  1992  			},
  1993  		}
  1994  
  1995  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  1996  		if err == nil {
  1997  			return false, nil
  1998  		}
  1999  
  2000  		if strings.Contains(err.Error(), "not yet synced to use for admission") {
  2001  			return false, nil
  2002  		}
  2003  
  2004  		if !strings.Contains(err.Error(), "wrong prefix") {
  2005  			return false, err
  2006  		}
  2007  
  2008  		return true, nil
  2009  	}); waitErr != nil {
  2010  		t.Errorf("timed out waiting: %v", err)
  2011  	}
  2012  
  2013  	// delete param object and validate that policy is invalid
  2014  	if err := client.CoreV1().ConfigMaps("default").Delete(context.TODO(), "test", metav1.DeleteOptions{}); err != nil {
  2015  		t.Fatal(err)
  2016  	}
  2017  
  2018  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2019  		allowedNamespace := &v1.Namespace{
  2020  			ObjectMeta: metav1.ObjectMeta{
  2021  				GenerateName: "not-test-",
  2022  			},
  2023  		}
  2024  
  2025  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), allowedNamespace, metav1.CreateOptions{})
  2026  		// old policy is still enforced, try again
  2027  		if strings.Contains(err.Error(), "wrong prefix") {
  2028  			return false, nil
  2029  		}
  2030  
  2031  		if !strings.Contains(err.Error(), "failed to configure binding: no params found for policy binding with `Deny` parameterNotFoundAction") {
  2032  			return false, err
  2033  		}
  2034  
  2035  		return true, nil
  2036  	}); waitErr != nil {
  2037  		t.Errorf("timed out waiting: %v", err)
  2038  	}
  2039  
  2040  	// recreate the param resource and validate namespace is disallowed again
  2041  	if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), param, metav1.CreateOptions{}); err != nil {
  2042  		t.Fatal(err)
  2043  	}
  2044  
  2045  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2046  		disallowedNamespace := &v1.Namespace{
  2047  			ObjectMeta: metav1.ObjectMeta{
  2048  				GenerateName: "not-test-",
  2049  			},
  2050  		}
  2051  
  2052  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
  2053  		// cache not synced with new object yet, try again
  2054  		if strings.Contains(err.Error(), "failed to configure binding: no params found for policy binding with `Deny` parameterNotFoundAction") {
  2055  			return false, nil
  2056  		}
  2057  
  2058  		if !strings.Contains(err.Error(), "wrong prefix") {
  2059  			return false, err
  2060  		}
  2061  
  2062  		return true, nil
  2063  	}); waitErr != nil {
  2064  		t.Errorf("timed out waiting: %v", err)
  2065  	}
  2066  }
  2067  
  2068  // TestCRDParams tests that a CustomResource can be used as a param resource for a ValidatingAdmissionPolicy.
  2069  func TestCRDParams(t *testing.T) {
  2070  	testcases := []struct {
  2071  		name          string
  2072  		resource      *unstructured.Unstructured
  2073  		policy        *admissionregistrationv1beta1.ValidatingAdmissionPolicy
  2074  		policyBinding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding
  2075  		namespace     *v1.Namespace
  2076  		err           string
  2077  		failureReason metav1.StatusReason
  2078  	}{
  2079  		{
  2080  			name: "a rule that uses data from a CRD param resource does NOT pass",
  2081  			resource: &unstructured.Unstructured{Object: map[string]interface{}{
  2082  				"apiVersion": "awesome.bears.com/v1",
  2083  				"kind":       "Panda",
  2084  				"metadata": map[string]interface{}{
  2085  					"name": "config-obj",
  2086  				},
  2087  				"spec": map[string]interface{}{
  2088  					"nameCheck": "crd-test-k8s",
  2089  				},
  2090  			}},
  2091  			policy: withValidations([]admissionregistrationv1beta1.Validation{
  2092  				{
  2093  					Expression: "params.spec.nameCheck == object.metadata.name",
  2094  				},
  2095  			}, withNamespaceMatch(withParams(withCRDParamKind("Panda", "awesome.bears.com", "v1"), withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("test-policy"))))),
  2096  			policyBinding: makeBinding("crd-policy-binding", "test-policy", "config-obj"),
  2097  			namespace: &v1.Namespace{
  2098  				ObjectMeta: metav1.ObjectMeta{
  2099  					Name: "incorrect-name",
  2100  				},
  2101  			},
  2102  			err:           `namespaces "incorrect-name" is forbidden: ValidatingAdmissionPolicy 'test-policy' with binding 'crd-policy-binding' denied request: failed expression: params.spec.nameCheck == object.metadata.name`,
  2103  			failureReason: metav1.StatusReasonInvalid,
  2104  		},
  2105  		{
  2106  			name: "a rule that uses data from a CRD param resource that does pass",
  2107  			resource: &unstructured.Unstructured{Object: map[string]interface{}{
  2108  				"apiVersion": "awesome.bears.com/v1",
  2109  				"kind":       "Panda",
  2110  				"metadata": map[string]interface{}{
  2111  					"name": "config-obj",
  2112  				},
  2113  				"spec": map[string]interface{}{
  2114  					"nameCheck": "crd-test-k8s",
  2115  				},
  2116  			}},
  2117  			policy: withValidations([]admissionregistrationv1beta1.Validation{
  2118  				{
  2119  					Expression: "params.spec.nameCheck == object.metadata.name",
  2120  				},
  2121  			}, withNamespaceMatch(withParams(withCRDParamKind("Panda", "awesome.bears.com", "v1"), withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("test-policy"))))),
  2122  			policyBinding: makeBinding("crd-policy-binding", "test-policy", "config-obj"),
  2123  			namespace: &v1.Namespace{
  2124  				ObjectMeta: metav1.ObjectMeta{
  2125  					Name: "crd-test-k8s",
  2126  				},
  2127  			},
  2128  			err: ``,
  2129  		},
  2130  	}
  2131  
  2132  	for _, testcase := range testcases {
  2133  		t.Run(testcase.name, func(t *testing.T) {
  2134  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2135  			server, err := apiservertesting.StartTestServer(t, nil, []string{
  2136  				"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2137  			}, framework.SharedEtcd())
  2138  			if err != nil {
  2139  				t.Fatal(err)
  2140  			}
  2141  			defer server.TearDownFn()
  2142  
  2143  			config := server.ClientConfig
  2144  
  2145  			client, err := clientset.NewForConfig(config)
  2146  			if err != nil {
  2147  				t.Fatal(err)
  2148  			}
  2149  
  2150  			crd := versionedCustomResourceDefinition()
  2151  			etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, crd)
  2152  			dynamicClient, err := dynamic.NewForConfig(config)
  2153  			if err != nil {
  2154  				t.Fatal(err)
  2155  			}
  2156  			gvr := schema.GroupVersionResource{
  2157  				Group:    crd.Spec.Group,
  2158  				Version:  crd.Spec.Versions[0].Name,
  2159  				Resource: crd.Spec.Names.Plural,
  2160  			}
  2161  			crClient := dynamicClient.Resource(gvr)
  2162  			_, err = crClient.Create(context.TODO(), testcase.resource, metav1.CreateOptions{})
  2163  			if err != nil {
  2164  				t.Fatalf("error creating %s: %s", gvr, err)
  2165  			}
  2166  
  2167  			policy := withWaitReadyConstraintAndExpression(testcase.policy)
  2168  			if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2169  				t.Fatal(err)
  2170  			}
  2171  			// remove default namespace since the CRD is cluster-scoped
  2172  			testcase.policyBinding.Spec.ParamRef.Namespace = ""
  2173  			if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
  2174  				t.Fatal(err)
  2175  			}
  2176  
  2177  			_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
  2178  
  2179  			checkExpectedError(t, err, testcase.err)
  2180  			checkFailureReason(t, err, testcase.failureReason)
  2181  		})
  2182  	}
  2183  }
  2184  
  2185  func TestBindingRemoval(t *testing.T) {
  2186  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2187  	server, err := apiservertesting.StartTestServer(t, nil, []string{
  2188  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2189  	}, framework.SharedEtcd())
  2190  	if err != nil {
  2191  		t.Fatal(err)
  2192  	}
  2193  	defer server.TearDownFn()
  2194  
  2195  	config := server.ClientConfig
  2196  
  2197  	client, err := clientset.NewForConfig(config)
  2198  	if err != nil {
  2199  		t.Fatal(err)
  2200  	}
  2201  
  2202  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  2203  		{
  2204  			Expression: "false",
  2205  			Message:    "policy still in effect",
  2206  		},
  2207  	}, withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("test-policy"))))
  2208  	policy = withWaitReadyConstraintAndExpression(policy)
  2209  	if _, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2210  		t.Fatal(err)
  2211  	}
  2212  
  2213  	binding := makeBinding("test-binding", "test-policy", "test-params")
  2214  	if err := createAndWaitReady(t, client, binding, nil); err != nil {
  2215  		t.Fatal(err)
  2216  	}
  2217  	// check that the policy is active
  2218  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2219  		namespace := &v1.Namespace{
  2220  			ObjectMeta: metav1.ObjectMeta{
  2221  				GenerateName: "check-namespace",
  2222  			},
  2223  		}
  2224  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
  2225  		if err != nil {
  2226  			if strings.Contains(err.Error(), "policy still in effect") {
  2227  				return true, nil
  2228  			} else {
  2229  				// unexpected error while attempting namespace creation
  2230  				return true, err
  2231  			}
  2232  		}
  2233  		return false, nil
  2234  	}); waitErr != nil {
  2235  		t.Errorf("timed out waiting: %v", waitErr)
  2236  	}
  2237  	if err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Delete(context.TODO(), "test-binding", metav1.DeleteOptions{}); err != nil {
  2238  		t.Fatal(err)
  2239  	}
  2240  
  2241  	// wait for binding to be deleted
  2242  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2243  
  2244  		_, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Get(context.TODO(), "test-binding", metav1.GetOptions{})
  2245  		if err != nil {
  2246  			if apierrors.IsNotFound(err) {
  2247  				return true, nil
  2248  			} else {
  2249  				return true, err
  2250  			}
  2251  		}
  2252  
  2253  		return false, nil
  2254  	}); waitErr != nil {
  2255  		t.Errorf("timed out waiting: %v", waitErr)
  2256  	}
  2257  
  2258  	// policy should be considered in an invalid state and namespace creation should be allowed
  2259  	if waitErr := wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) {
  2260  		namespace := &v1.Namespace{
  2261  			ObjectMeta: metav1.ObjectMeta{
  2262  				GenerateName: "test-namespace",
  2263  			},
  2264  		}
  2265  		_, err = client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
  2266  		if err != nil {
  2267  			t.Logf("namespace creation failed: %s", err)
  2268  			return false, nil
  2269  		}
  2270  
  2271  		return true, nil
  2272  	}); waitErr != nil {
  2273  		t.Errorf("expected namespace creation to succeed but timed out waiting: %v", waitErr)
  2274  	}
  2275  }
  2276  
  2277  // Test_ValidateSecondaryAuthorization tests a ValidatingAdmissionPolicy that performs secondary authorization checks
  2278  // for both users and service accounts.
  2279  func Test_ValidateSecondaryAuthorization(t *testing.T) {
  2280  	testcases := []struct {
  2281  		name             string
  2282  		rbac             *rbacv1.PolicyRule
  2283  		expression       string
  2284  		allowed          bool
  2285  		extraAccountFn   func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
  2286  		extraAccountRbac *rbacv1.PolicyRule
  2287  	}{
  2288  		{
  2289  			name: "principal is allowed to create a specific deployment",
  2290  			rbac: &rbacv1.PolicyRule{
  2291  				Verbs:         []string{"create"},
  2292  				APIGroups:     []string{"apps"},
  2293  				Resources:     []string{"deployments/status"},
  2294  				ResourceNames: []string{"charmander"},
  2295  			},
  2296  			expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('default').namespace('default').name('charmander').check('create').allowed()",
  2297  			allowed:    true,
  2298  		},
  2299  		{
  2300  			name:       "principal is not allowed to create a specific deployment",
  2301  			expression: "authorizer.group('apps').resource('deployments').subresource('status').namespace('default').name('charmander').check('create').allowed()",
  2302  			allowed:    false,
  2303  		},
  2304  		{
  2305  			name: "principal is authorized for custom verb on current resource",
  2306  			rbac: &rbacv1.PolicyRule{
  2307  				Verbs:     []string{"anthropomorphize"},
  2308  				APIGroups: []string{""},
  2309  				Resources: []string{"namespaces"},
  2310  			},
  2311  			expression: "authorizer.requestResource.check('anthropomorphize').allowed()",
  2312  			allowed:    true,
  2313  		},
  2314  		{
  2315  			name:       "principal is not authorized for custom verb on current resource",
  2316  			expression: "authorizer.requestResource.check('anthropomorphize').allowed()",
  2317  			allowed:    false,
  2318  		},
  2319  		{
  2320  			name:           "serviceaccount is authorized for custom verb on current resource",
  2321  			extraAccountFn: serviceAccountClient("default", "extra-acct"),
  2322  			extraAccountRbac: &rbacv1.PolicyRule{
  2323  				Verbs:     []string{"anthropomorphize"},
  2324  				APIGroups: []string{""},
  2325  				Resources: []string{"pods"},
  2326  			},
  2327  			expression: "authorizer.serviceAccount('default', 'extra-acct').group('').resource('pods').check('anthropomorphize').allowed()",
  2328  			allowed:    true,
  2329  		},
  2330  	}
  2331  
  2332  	for _, testcase := range testcases {
  2333  		t.Run(testcase.name, func(t *testing.T) {
  2334  			clients := map[string]func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset{
  2335  				"user":           secondaryAuthorizationUserClient,
  2336  				"serviceaccount": secondaryAuthorizationServiceAccountClient,
  2337  			}
  2338  
  2339  			for clientName, clientFn := range clients {
  2340  				t.Run(clientName, func(t *testing.T) {
  2341  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2342  					server, err := apiservertesting.StartTestServer(t, nil, []string{
  2343  						"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2344  						"--authorization-mode=RBAC",
  2345  						"--anonymous-auth",
  2346  					}, framework.SharedEtcd())
  2347  					if err != nil {
  2348  						t.Fatal(err)
  2349  					}
  2350  					defer server.TearDownFn()
  2351  
  2352  					// For test set up such as creating policies, bindings and RBAC rules.
  2353  					adminClient := clientset.NewForConfigOrDie(server.ClientConfig)
  2354  
  2355  					// Principal is always allowed to create and update namespaces so that the admission requests to test
  2356  					// authorization expressions can be sent by the principal.
  2357  					rules := []rbacv1.PolicyRule{{
  2358  						Verbs:     []string{"create", "update"},
  2359  						APIGroups: []string{""},
  2360  						Resources: []string{"namespaces"},
  2361  					}}
  2362  					if testcase.rbac != nil {
  2363  						rules = append(rules, *testcase.rbac)
  2364  					}
  2365  
  2366  					client := clientFn(t, adminClient, server.ClientConfig, rules)
  2367  
  2368  					if testcase.extraAccountFn != nil {
  2369  						var extraRules []rbacv1.PolicyRule
  2370  						if testcase.extraAccountRbac != nil {
  2371  							extraRules = append(rules, *testcase.extraAccountRbac)
  2372  						}
  2373  						testcase.extraAccountFn(t, adminClient, server.ClientConfig, extraRules)
  2374  					}
  2375  
  2376  					policy := withWaitReadyConstraintAndExpression(withValidations([]admissionregistrationv1beta1.Validation{
  2377  						{
  2378  							Expression: testcase.expression,
  2379  						},
  2380  					}, withFailurePolicy(admissionregistrationv1beta1.Fail, withNamespaceMatch(makePolicy("validate-authz")))))
  2381  					if _, err := adminClient.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
  2382  						t.Fatal(err)
  2383  					}
  2384  					if err := createAndWaitReady(t, adminClient, makeBinding("validate-authz-binding", "validate-authz", ""), nil); err != nil {
  2385  						t.Fatal(err)
  2386  					}
  2387  
  2388  					ns := &v1.Namespace{
  2389  						ObjectMeta: metav1.ObjectMeta{
  2390  							Name: "test-authz",
  2391  						},
  2392  					}
  2393  					_, err = client.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
  2394  
  2395  					var expected metav1.StatusReason = ""
  2396  					if !testcase.allowed {
  2397  						expected = metav1.StatusReasonInvalid
  2398  					}
  2399  					checkFailureReason(t, err, expected)
  2400  				})
  2401  			}
  2402  		})
  2403  	}
  2404  }
  2405  
  2406  func TestCRDsOnStartup(t *testing.T) {
  2407  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  2408  
  2409  	testContext, testCancel := context.WithCancel(context.Background())
  2410  	defer testCancel()
  2411  
  2412  	// Start server and create CRD, and validatingadmission policy and binding
  2413  	etcdConfig := framework.SharedEtcd()
  2414  	server := apiservertesting.StartTestServerOrDie(t, nil, []string{
  2415  		"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2416  		"--authorization-mode=RBAC",
  2417  		"--anonymous-auth",
  2418  	}, etcdConfig)
  2419  	client := clientset.NewForConfigOrDie(server.ClientConfig)
  2420  	dynamicClient := dynamic.NewForConfigOrDie(server.ClientConfig)
  2421  	apiextclient := apiextensionsclientset.NewForConfigOrDie(server.ClientConfig)
  2422  	myCRD := &apiextensionsv1.CustomResourceDefinition{
  2423  		ObjectMeta: metav1.ObjectMeta{
  2424  			Name: "foos.cr.bar.com",
  2425  		},
  2426  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
  2427  			Group: "cr.bar.com",
  2428  			Scope: apiextensionsv1.NamespaceScoped,
  2429  			Names: apiextensionsv1.CustomResourceDefinitionNames{
  2430  				Plural: "foos",
  2431  				Kind:   "Foo",
  2432  			},
  2433  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
  2434  				{
  2435  					Name:    "v1",
  2436  					Served:  true,
  2437  					Storage: true,
  2438  					Schema:  fixtures.AllowAllSchema(),
  2439  				},
  2440  			},
  2441  		},
  2442  	}
  2443  
  2444  	// Create a bunch of fake CRDs to make the initial startup sync take a long time
  2445  	for i := 0; i < 100; i++ {
  2446  		crd := myCRD.DeepCopy()
  2447  		crd.Name = fmt.Sprintf("foos%d.cr.bar.com", i)
  2448  		crd.Spec.Names.Plural = fmt.Sprintf("foos%d", i)
  2449  		crd.Spec.Names.Kind = fmt.Sprintf("Foo%d", i)
  2450  
  2451  		if _, err := apiextclient.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), crd, metav1.CreateOptions{}); err != nil {
  2452  			t.Fatal(err)
  2453  		}
  2454  	}
  2455  
  2456  	etcd.CreateTestCRDs(t, apiextclient, false, myCRD)
  2457  	crdGVK := schema.GroupVersionKind{
  2458  		Group:   "cr.bar.com",
  2459  		Version: "v1",
  2460  		Kind:    "Foo",
  2461  	}
  2462  	crdGVR := crdGVK.GroupVersion().WithResource("foos")
  2463  
  2464  	param := &unstructured.Unstructured{
  2465  		Object: map[string]interface{}{
  2466  			"metadata": map[string]interface{}{
  2467  				"name":      "test",
  2468  				"namespace": "default",
  2469  			},
  2470  			"foo": "bar",
  2471  		},
  2472  	}
  2473  	param.GetObjectKind().SetGroupVersionKind(crdGVK)
  2474  
  2475  	if _, err := dynamicClient.Resource(crdGVR).Namespace("default").Create(context.TODO(), param, metav1.CreateOptions{}); err != nil {
  2476  		t.Fatal(err)
  2477  	}
  2478  
  2479  	policy := withValidations([]admissionregistrationv1beta1.Validation{
  2480  		{
  2481  			Expression: "object.metadata.name.startsWith(params.metadata.name)",
  2482  			Message:    "wrong prefix",
  2483  		},
  2484  	}, withParams(withCRDParamKind(crdGVK.Kind, crdGVK.Group, crdGVK.Version), withNamespaceMatch(withFailurePolicy(admissionregistrationv1beta1.Fail, makePolicy("allowed-prefixes")))))
  2485  	policy = withWaitReadyConstraintAndExpression(policy)
  2486  	_, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{})
  2487  	if err != nil {
  2488  		t.Fatal(err)
  2489  	}
  2490  
  2491  	// validate that namespaces starting with "test" are allowed
  2492  	policyBinding := makeBinding("allowed-prefixes-binding", "allowed-prefixes", "test")
  2493  	if err := createAndWaitReady(t, client, policyBinding, nil); err != nil {
  2494  		t.Fatal(err)
  2495  	}
  2496  
  2497  	doCheck := func(client clientset.Interface) {
  2498  		if waitErr := wait.PollUntilContextTimeout(testContext, time.Millisecond*100, 3*time.Minute, true, func(ctx context.Context) (bool, error) {
  2499  			disallowedNamespace := &v1.Namespace{
  2500  				ObjectMeta: metav1.ObjectMeta{
  2501  					GenerateName: "not-test-",
  2502  				},
  2503  			}
  2504  
  2505  			_, err = client.CoreV1().Namespaces().Create(testContext, disallowedNamespace, metav1.CreateOptions{})
  2506  			if err == nil {
  2507  				return false, nil
  2508  			}
  2509  
  2510  			if strings.Contains(err.Error(), "not yet synced to use for admission") {
  2511  				return false, nil
  2512  			}
  2513  
  2514  			if strings.Contains(err.Error(), "failed to find resource referenced by paramKind") {
  2515  				return false, nil
  2516  			}
  2517  
  2518  			if !strings.Contains(err.Error(), "wrong prefix") {
  2519  				return false, err
  2520  			}
  2521  
  2522  			return true, nil
  2523  		}); waitErr != nil {
  2524  			t.Errorf("timed out waiting: %v", err)
  2525  		}
  2526  	}
  2527  
  2528  	// Show that the policy & binding are correctly working before restarting
  2529  	// to use the paramKind and deliver an error
  2530  	doCheck(client)
  2531  	server.TearDownFn()
  2532  
  2533  	// Start the server.
  2534  	server = apiservertesting.StartTestServerOrDie(
  2535  		t,
  2536  		&apiservertesting.TestServerInstanceOptions{},
  2537  		[]string{
  2538  			"--enable-admission-plugins", "ValidatingAdmissionPolicy",
  2539  			"--authorization-mode=RBAC",
  2540  			"--anonymous-auth",
  2541  		},
  2542  		etcdConfig)
  2543  	defer server.TearDownFn()
  2544  
  2545  	// Now that the server is restarted, show again that the policy & binding are correctly working
  2546  	client = clientset.NewForConfigOrDie(server.ClientConfig)
  2547  
  2548  	doCheck(client)
  2549  
  2550  }
  2551  
  2552  type clientFn func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset
  2553  
  2554  func secondaryAuthorizationUserClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2555  	clientConfig = rest.CopyConfig(clientConfig)
  2556  	clientConfig.Impersonate = rest.ImpersonationConfig{
  2557  		UserName: "alice",
  2558  		UID:      "1234",
  2559  	}
  2560  	client := clientset.NewForConfigOrDie(clientConfig)
  2561  
  2562  	for _, rule := range rules {
  2563  		authutil.GrantUserAuthorization(t, context.TODO(), adminClient, "alice", rule)
  2564  	}
  2565  	return client
  2566  }
  2567  
  2568  func secondaryAuthorizationServiceAccountClient(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2569  	return serviceAccountClient("default", "test-service-acct")(t, adminClient, clientConfig, rules)
  2570  }
  2571  
  2572  func serviceAccountClient(namespace, name string) clientFn {
  2573  	return func(t *testing.T, adminClient *clientset.Clientset, clientConfig *rest.Config, rules []rbacv1.PolicyRule) *clientset.Clientset {
  2574  		clientConfig = rest.CopyConfig(clientConfig)
  2575  		sa, err := adminClient.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: name}}, metav1.CreateOptions{})
  2576  		if err != nil {
  2577  			t.Fatal(err)
  2578  		}
  2579  		uid := sa.UID
  2580  
  2581  		clientConfig.Impersonate = rest.ImpersonationConfig{
  2582  			UserName: "system:serviceaccount:" + namespace + ":" + name,
  2583  			UID:      string(uid),
  2584  		}
  2585  		client := clientset.NewForConfigOrDie(clientConfig)
  2586  
  2587  		for _, rule := range rules {
  2588  			authutil.GrantServiceAccountAuthorization(t, context.TODO(), adminClient, name, namespace, rule)
  2589  		}
  2590  		return client
  2591  	}
  2592  }
  2593  
  2594  func withWaitReadyConstraintAndExpression(policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2595  	policy = policy.DeepCopy()
  2596  	policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, admissionregistrationv1beta1.NamedRuleWithOperations{
  2597  		ResourceNames: []string{"test-marker"},
  2598  		RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
  2599  			Operations: []admissionregistrationv1.OperationType{
  2600  				"UPDATE",
  2601  			},
  2602  			Rule: admissionregistrationv1.Rule{
  2603  				APIGroups: []string{
  2604  					"",
  2605  				},
  2606  				APIVersions: []string{
  2607  					"v1",
  2608  				},
  2609  				Resources: []string{
  2610  					"endpoints",
  2611  				},
  2612  			},
  2613  		},
  2614  	})
  2615  	policy.Spec.Validations = append([]admissionregistrationv1beta1.Validation{{
  2616  		Expression: "object.metadata.name != 'test-marker'",
  2617  		Message:    "marker denied; policy is ready",
  2618  	}}, policy.Spec.Validations...)
  2619  	return policy
  2620  }
  2621  
  2622  func createAndWaitReady(t *testing.T, client clientset.Interface, binding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string) error {
  2623  	return createAndWaitReadyNamespaced(t, client, binding, matchLabels, "default")
  2624  }
  2625  
  2626  func createAndWaitReadyNamespaced(t *testing.T, client clientset.Interface, binding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string, ns string) error {
  2627  	return createAndWaitReadyNamespacedWithWarnHandler(t, client, binding, matchLabels, ns, newWarningHandler())
  2628  }
  2629  
  2630  func createAndWaitReadyNamespacedWithWarnHandler(t *testing.T, client clientset.Interface, binding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string, ns string, handler *warningHandler) error {
  2631  	marker := &v1.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "test-marker", Namespace: ns, Labels: matchLabels}}
  2632  	defer func() {
  2633  		err := client.CoreV1().Endpoints(ns).Delete(context.TODO(), marker.Name, metav1.DeleteOptions{})
  2634  		if err != nil {
  2635  			t.Logf("error deleting marker: %v", err)
  2636  		}
  2637  	}()
  2638  	marker, err := client.CoreV1().Endpoints(ns).Create(context.TODO(), marker, metav1.CreateOptions{})
  2639  	if err != nil {
  2640  		return err
  2641  	}
  2642  
  2643  	_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), binding, metav1.CreateOptions{})
  2644  	if err != nil {
  2645  		return err
  2646  	}
  2647  
  2648  	if waitErr := wait.PollImmediate(time.Millisecond*5, wait.ForeverTestTimeout, func() (bool, error) {
  2649  		handler.reset()
  2650  		_, err := client.CoreV1().Endpoints(ns).Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
  2651  		if handler.hasObservedMarker() {
  2652  			return true, nil
  2653  		}
  2654  		if err != nil && strings.Contains(err.Error(), "marker denied; policy is ready") {
  2655  			return true, nil
  2656  		} else if err != nil && strings.Contains(err.Error(), "not yet synced to use for admission") {
  2657  			t.Logf("waiting for policy to be ready. Marker: %v. Admission not synced yet: %v", marker, err)
  2658  			return false, nil
  2659  		} else {
  2660  			t.Logf("waiting for policy to be ready. Marker: %v, Last marker patch response: %v", marker, err)
  2661  			return false, err
  2662  		}
  2663  	}); waitErr != nil {
  2664  		return waitErr
  2665  	}
  2666  	t.Logf("Marker ready: %v", marker)
  2667  	handler.reset()
  2668  	return nil
  2669  }
  2670  
  2671  func withMatchNamespace(binding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding, ns string) *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding {
  2672  	binding.Spec.MatchResources = &admissionregistrationv1beta1.MatchResources{
  2673  		NamespaceSelector: &metav1.LabelSelector{
  2674  			MatchExpressions: []metav1.LabelSelectorRequirement{
  2675  				{
  2676  					Key:      "kubernetes.io/metadata.name",
  2677  					Operator: metav1.LabelSelectorOpIn,
  2678  					Values:   []string{ns},
  2679  				},
  2680  			},
  2681  		},
  2682  	}
  2683  	return binding
  2684  }
  2685  
  2686  func makePolicy(name string) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2687  	return &admissionregistrationv1beta1.ValidatingAdmissionPolicy{
  2688  		ObjectMeta: metav1.ObjectMeta{Name: name},
  2689  	}
  2690  }
  2691  
  2692  func withParams(params *admissionregistrationv1beta1.ParamKind, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2693  	policy.Spec.ParamKind = params
  2694  	return policy
  2695  }
  2696  
  2697  func configParamKind() *admissionregistrationv1beta1.ParamKind {
  2698  	return &admissionregistrationv1beta1.ParamKind{
  2699  		APIVersion: "v1",
  2700  		Kind:       "ConfigMap",
  2701  	}
  2702  }
  2703  
  2704  func withFailurePolicy(failure admissionregistrationv1beta1.FailurePolicyType, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2705  	policy.Spec.FailurePolicy = &failure
  2706  	return policy
  2707  }
  2708  
  2709  func withNamespaceMatch(policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2710  	return withPolicyMatch("namespaces", policy)
  2711  }
  2712  
  2713  func withConfigMapMatch(policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2714  	return withPolicyMatch("configmaps", policy)
  2715  }
  2716  
  2717  func withObjectSelector(labelSelector *metav1.LabelSelector, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2718  	policy.Spec.MatchConstraints.ObjectSelector = labelSelector
  2719  	return policy
  2720  }
  2721  
  2722  func withNamespaceSelector(labelSelector *metav1.LabelSelector, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2723  	policy.Spec.MatchConstraints.NamespaceSelector = labelSelector
  2724  	return policy
  2725  }
  2726  
  2727  func withPolicyMatch(resource string, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2728  	policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{
  2729  		ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
  2730  			{
  2731  				RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
  2732  					Operations: []admissionregistrationv1.OperationType{
  2733  						"*",
  2734  					},
  2735  					Rule: admissionregistrationv1.Rule{
  2736  						APIGroups: []string{
  2737  							"",
  2738  						},
  2739  						APIVersions: []string{
  2740  							"*",
  2741  						},
  2742  						Resources: []string{
  2743  							resource,
  2744  						},
  2745  					},
  2746  				},
  2747  			},
  2748  		},
  2749  	}
  2750  	return policy
  2751  }
  2752  
  2753  func withExcludePolicyMatch(resource string, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2754  	policy.Spec.MatchConstraints.ExcludeResourceRules = []admissionregistrationv1beta1.NamedRuleWithOperations{
  2755  		{
  2756  			RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
  2757  				Operations: []admissionregistrationv1.OperationType{
  2758  					"*",
  2759  				},
  2760  				Rule: admissionregistrationv1.Rule{
  2761  					APIGroups: []string{
  2762  						"",
  2763  					},
  2764  					APIVersions: []string{
  2765  						"*",
  2766  					},
  2767  					Resources: []string{
  2768  						resource,
  2769  					},
  2770  				},
  2771  			},
  2772  		},
  2773  	}
  2774  	return policy
  2775  }
  2776  
  2777  func withPolicyExistsLabels(labels []string, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2778  	if policy.Spec.MatchConstraints == nil {
  2779  		policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{}
  2780  	}
  2781  	matchExprs := buildExistsSelector(labels)
  2782  	policy.Spec.MatchConstraints.ObjectSelector = &metav1.LabelSelector{
  2783  		MatchExpressions: matchExprs,
  2784  	}
  2785  	return policy
  2786  }
  2787  
  2788  func withValidations(validations []admissionregistrationv1beta1.Validation, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2789  	policy.Spec.Validations = validations
  2790  	return policy
  2791  }
  2792  
  2793  func withAuditAnnotations(auditAnnotations []admissionregistrationv1beta1.AuditAnnotation, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
  2794  	policy.Spec.AuditAnnotations = auditAnnotations
  2795  	return policy
  2796  }
  2797  
  2798  func makeBinding(name, policyName, paramName string) *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding {
  2799  	var paramRef *admissionregistrationv1beta1.ParamRef
  2800  	if paramName != "" {
  2801  		denyAction := admissionregistrationv1beta1.DenyAction
  2802  		paramRef = &admissionregistrationv1beta1.ParamRef{
  2803  			Name:                    paramName,
  2804  			Namespace:               "default",
  2805  			ParameterNotFoundAction: &denyAction,
  2806  		}
  2807  	}
  2808  	return &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{
  2809  		ObjectMeta: metav1.ObjectMeta{Name: name},
  2810  		Spec: admissionregistrationv1beta1.ValidatingAdmissionPolicyBindingSpec{
  2811  			PolicyName:        policyName,
  2812  			ParamRef:          paramRef,
  2813  			ValidationActions: []admissionregistrationv1beta1.ValidationAction{admissionregistrationv1beta1.Deny},
  2814  		},
  2815  	}
  2816  }
  2817  
  2818  func withValidationActions(validationActions []admissionregistrationv1beta1.ValidationAction, binding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding) *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding {
  2819  	binding.Spec.ValidationActions = validationActions
  2820  	return binding
  2821  }
  2822  
  2823  func withBindingExistsLabels(labels []string, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy, binding *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding) *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding {
  2824  	if policy != nil {
  2825  		// shallow copy
  2826  		constraintsCopy := *policy.Spec.MatchConstraints
  2827  		binding.Spec.MatchResources = &constraintsCopy
  2828  	}
  2829  	matchExprs := buildExistsSelector(labels)
  2830  	binding.Spec.MatchResources.ObjectSelector = &metav1.LabelSelector{
  2831  		MatchExpressions: matchExprs,
  2832  	}
  2833  	return binding
  2834  }
  2835  
  2836  func buildExistsSelector(labels []string) []metav1.LabelSelectorRequirement {
  2837  	matchExprs := make([]metav1.LabelSelectorRequirement, len(labels))
  2838  	for i := 0; i < len(labels); i++ {
  2839  		matchExprs[i].Key = labels[i]
  2840  		matchExprs[i].Operator = metav1.LabelSelectorOpExists
  2841  	}
  2842  	return matchExprs
  2843  }
  2844  
  2845  func makeConfigParams(name string, data map[string]string) *v1.ConfigMap {
  2846  	return &v1.ConfigMap{
  2847  		ObjectMeta: metav1.ObjectMeta{Name: name},
  2848  		Data:       data,
  2849  	}
  2850  }
  2851  
  2852  func checkForFailedRule(t *testing.T, err error) {
  2853  	if !strings.Contains(err.Error(), "failed expression") {
  2854  		t.Fatalf("unexpected error (expected to find \"failed expression\"): %s", err)
  2855  	}
  2856  	if strings.Contains(err.Error(), "evaluation error") {
  2857  		t.Fatalf("CEL rule evaluation failed: %s", err)
  2858  	}
  2859  }
  2860  
  2861  func checkFailureReason(t *testing.T, err error, expectedReason metav1.StatusReason) {
  2862  	if err == nil && expectedReason == "" {
  2863  		// no reason was given, no error was passed - early exit
  2864  		return
  2865  	}
  2866  	switch e := err.(type) {
  2867  	case apierrors.APIStatus:
  2868  		reason := e.Status().Reason
  2869  		if reason != expectedReason {
  2870  			t.Logf("actual error reason: %v", reason)
  2871  			t.Logf("expected failure reason: %v", expectedReason)
  2872  			t.Error("Unexpected error reason")
  2873  		}
  2874  	default:
  2875  		t.Errorf("Unexpected error: %v", err)
  2876  	}
  2877  }
  2878  
  2879  func checkExpectedWarnings(t *testing.T, recordedWarnings *warningHandler, expectedWarnings sets.Set[string]) {
  2880  	if !recordedWarnings.equals(expectedWarnings) {
  2881  		t.Errorf("Expected warnings '%v' but got '%v", expectedWarnings, recordedWarnings)
  2882  	}
  2883  }
  2884  
  2885  func checkAuditEvents(t *testing.T, logFile *os.File, auditEvents []utils.AuditEvent, filter utils.AuditAnnotationsFilter) {
  2886  	stream, err := os.OpenFile(logFile.Name(), os.O_RDWR, 0600)
  2887  	if err != nil {
  2888  		t.Errorf("unexpected error: %v", err)
  2889  	}
  2890  	defer stream.Close()
  2891  
  2892  	if auditEvents != nil {
  2893  		missing, err := utils.CheckAuditLinesFiltered(stream, auditEvents, auditv1.SchemeGroupVersion, filter)
  2894  		if err != nil {
  2895  			t.Errorf("unexpected error checking audit lines: %v", err)
  2896  		}
  2897  		if len(missing.MissingEvents) > 0 {
  2898  			t.Errorf("failed to get expected events -- missing: %s", missing)
  2899  		}
  2900  	}
  2901  	if err := stream.Truncate(0); err != nil {
  2902  		t.Errorf("unexpected error truncate file: %v", err)
  2903  	}
  2904  	if _, err := stream.Seek(0, 0); err != nil {
  2905  		t.Errorf("unexpected error reset offset: %v", err)
  2906  	}
  2907  }
  2908  
  2909  func withCRDParamKind(kind, crdGroup, crdVersion string) *admissionregistrationv1beta1.ParamKind {
  2910  	return &admissionregistrationv1beta1.ParamKind{
  2911  		APIVersion: crdGroup + "/" + crdVersion,
  2912  		Kind:       kind,
  2913  	}
  2914  }
  2915  
  2916  func checkExpectedError(t *testing.T, err error, expectedErr string) {
  2917  	if err == nil && expectedErr == "" {
  2918  		return
  2919  	}
  2920  	if err == nil && expectedErr != "" {
  2921  		t.Logf("actual error: %v", err)
  2922  		t.Logf("expected error: %v", expectedErr)
  2923  		t.Fatal("got nil error but expected an error")
  2924  	}
  2925  
  2926  	if err != nil && expectedErr == "" {
  2927  		t.Logf("actual error: %v", err)
  2928  		t.Logf("expected error: %v", expectedErr)
  2929  		t.Fatal("got error but expected none")
  2930  	}
  2931  
  2932  	if err.Error() != expectedErr {
  2933  		t.Logf("actual validation error: %v", err)
  2934  		t.Logf("expected validation error: %v", expectedErr)
  2935  		t.Error("unexpected validation error")
  2936  	}
  2937  }
  2938  
  2939  // Copied from etcd.GetCustomResourceDefinitionData
  2940  func versionedCustomResourceDefinition() *apiextensionsv1.CustomResourceDefinition {
  2941  	return &apiextensionsv1.CustomResourceDefinition{
  2942  		ObjectMeta: metav1.ObjectMeta{
  2943  			Name: "pandas.awesome.bears.com",
  2944  		},
  2945  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
  2946  			Group: "awesome.bears.com",
  2947  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
  2948  				{
  2949  					Name:    "v1",
  2950  					Served:  true,
  2951  					Storage: true,
  2952  					Schema:  fixtures.AllowAllSchema(),
  2953  					Subresources: &apiextensionsv1.CustomResourceSubresources{
  2954  						Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
  2955  						Scale: &apiextensionsv1.CustomResourceSubresourceScale{
  2956  							SpecReplicasPath:   ".spec.replicas",
  2957  							StatusReplicasPath: ".status.replicas",
  2958  							LabelSelectorPath:  func() *string { path := ".status.selector"; return &path }(),
  2959  						},
  2960  					},
  2961  				},
  2962  				{
  2963  					Name:    "v2",
  2964  					Served:  true,
  2965  					Storage: false,
  2966  					Schema:  fixtures.AllowAllSchema(),
  2967  					Subresources: &apiextensionsv1.CustomResourceSubresources{
  2968  						Status: &apiextensionsv1.CustomResourceSubresourceStatus{},
  2969  						Scale: &apiextensionsv1.CustomResourceSubresourceScale{
  2970  							SpecReplicasPath:   ".spec.replicas",
  2971  							StatusReplicasPath: ".status.replicas",
  2972  							LabelSelectorPath:  func() *string { path := ".status.selector"; return &path }(),
  2973  						},
  2974  					},
  2975  				},
  2976  			},
  2977  			Scope: apiextensionsv1.ClusterScoped,
  2978  			Names: apiextensionsv1.CustomResourceDefinitionNames{
  2979  				Plural: "pandas",
  2980  				Kind:   "Panda",
  2981  			},
  2982  		},
  2983  	}
  2984  }
  2985  
  2986  type warningHandler struct {
  2987  	lock           sync.Mutex
  2988  	warnings       sets.Set[string]
  2989  	observedMarker bool
  2990  }
  2991  
  2992  func newWarningHandler() *warningHandler {
  2993  	return &warningHandler{warnings: sets.New[string]()}
  2994  }
  2995  
  2996  func (w *warningHandler) reset() {
  2997  	w.lock.Lock()
  2998  	defer w.lock.Unlock()
  2999  	w.warnings = sets.New[string]()
  3000  	w.observedMarker = false
  3001  }
  3002  
  3003  func (w *warningHandler) equals(s sets.Set[string]) bool {
  3004  	w.lock.Lock()
  3005  	defer w.lock.Unlock()
  3006  	return w.warnings.Equal(s)
  3007  }
  3008  
  3009  func (w *warningHandler) hasObservedMarker() bool {
  3010  	w.lock.Lock()
  3011  	defer w.lock.Unlock()
  3012  	return w.observedMarker
  3013  }
  3014  
  3015  func (w *warningHandler) HandleWarningHeader(code int, _ string, message string) {
  3016  	if strings.HasSuffix(message, "marker denied; policy is ready") {
  3017  		func() {
  3018  			w.lock.Lock()
  3019  			defer w.lock.Unlock()
  3020  			w.observedMarker = true
  3021  		}()
  3022  	}
  3023  	if code != 299 || len(message) == 0 {
  3024  		return
  3025  	}
  3026  	w.lock.Lock()
  3027  	defer w.lock.Unlock()
  3028  	w.warnings.Insert(message)
  3029  }
  3030  
  3031  func expectedAuditEvents(auditAnnotations map[string]string, ns string, code int32) []utils.AuditEvent {
  3032  	return []utils.AuditEvent{
  3033  		{
  3034  			Level:                  auditinternal.LevelRequest,
  3035  			Stage:                  auditinternal.StageResponseComplete,
  3036  			RequestURI:             fmt.Sprintf("/api/v1/namespaces/%s/configmaps", ns),
  3037  			Verb:                   "create",
  3038  			Code:                   code,
  3039  			User:                   "system:apiserver",
  3040  			ImpersonatedUser:       testReinvocationClientUsername,
  3041  			ImpersonatedGroups:     "system:authenticated",
  3042  			Resource:               "configmaps",
  3043  			Namespace:              ns,
  3044  			AuthorizeDecision:      "allow",
  3045  			RequestObject:          true,
  3046  			ResponseObject:         false,
  3047  			CustomAuditAnnotations: auditAnnotations,
  3048  		},
  3049  	}
  3050  }
  3051  
  3052  const (
  3053  	testReinvocationClientUsername = "webhook-reinvocation-integration-client"
  3054  	auditPolicy                    = `
  3055  apiVersion: audit.k8s.io/v1
  3056  kind: Policy
  3057  rules:
  3058    - level: Request
  3059      resources:
  3060        - group: "" # core
  3061          resources: ["configmaps"]
  3062  `
  3063  )
  3064  
  3065  func TestAuthorizationDecisionCaching(t *testing.T) {
  3066  	for _, tc := range []struct {
  3067  		name        string
  3068  		validations []admissionregistrationv1beta1.Validation
  3069  	}{
  3070  		{
  3071  			name: "hit",
  3072  			validations: []admissionregistrationv1beta1.Validation{
  3073  				{
  3074  					Expression: "authorizer.requestResource.check('test').reason() == authorizer.requestResource.check('test').reason()",
  3075  				},
  3076  			},
  3077  		},
  3078  		{
  3079  			name: "miss",
  3080  			validations: []admissionregistrationv1beta1.Validation{
  3081  				{
  3082  					Expression: "authorizer.requestResource.subresource('a').check('test').reason() == '1'",
  3083  				},
  3084  				{
  3085  					Expression: "authorizer.requestResource.subresource('b').check('test').reason() == '2'",
  3086  				},
  3087  				{
  3088  					Expression: "authorizer.requestResource.subresource('c').check('test').reason() == '3'",
  3089  				},
  3090  			},
  3091  		},
  3092  	} {
  3093  		t.Run(tc.name, func(t *testing.T) {
  3094  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ValidatingAdmissionPolicy, true)()
  3095  
  3096  			ctx, cancel := context.WithCancel(context.TODO())
  3097  			defer cancel()
  3098  
  3099  			var nChecks int
  3100  			webhook := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  3101  				var review authorizationv1.SubjectAccessReview
  3102  				if err := json.NewDecoder(r.Body).Decode(&review); err != nil {
  3103  					http.Error(w, err.Error(), http.StatusBadRequest)
  3104  				}
  3105  
  3106  				review.Status.Allowed = true
  3107  				if review.Spec.ResourceAttributes.Verb == "test" {
  3108  					nChecks++
  3109  					review.Status.Reason = fmt.Sprintf("%d", nChecks)
  3110  				}
  3111  
  3112  				w.Header().Set("Content-Type", "application/json")
  3113  				if err := json.NewEncoder(w).Encode(review); err != nil {
  3114  					http.Error(w, err.Error(), http.StatusInternalServerError)
  3115  				}
  3116  			}))
  3117  			defer webhook.Close()
  3118  
  3119  			kcfd, err := os.CreateTemp("", "kubeconfig-")
  3120  			if err != nil {
  3121  				t.Fatal(err)
  3122  			}
  3123  			func() {
  3124  				defer kcfd.Close()
  3125  				tmpl, err := template.New("kubeconfig").Parse(`
  3126  apiVersion: v1
  3127  kind: Config
  3128  clusters:
  3129    - name: test-authz-service
  3130      cluster:
  3131        server: {{ .Server }}
  3132  users:
  3133    - name: test-api-server
  3134  current-context: webhook
  3135  contexts:
  3136  - context:
  3137      cluster: test-authz-service
  3138      user: test-api-server
  3139    name: webhook
  3140  `)
  3141  				if err != nil {
  3142  					t.Fatal(err)
  3143  				}
  3144  				err = tmpl.Execute(kcfd, struct {
  3145  					Server string
  3146  				}{
  3147  					Server: webhook.URL,
  3148  				})
  3149  				if err != nil {
  3150  					t.Fatal(err)
  3151  				}
  3152  			}()
  3153  
  3154  			client, config, teardown := framework.StartTestServer(ctx, t, framework.TestServerSetup{
  3155  				ModifyServerRunOptions: func(options *options.ServerRunOptions) {
  3156  					options.Admission.GenericAdmission.EnablePlugins = append(options.Admission.GenericAdmission.EnablePlugins, "ValidatingAdmissionPolicy")
  3157  					options.APIEnablement.RuntimeConfig.Set("api/all=true")
  3158  
  3159  					options.Authorization.Modes = []string{authzmodes.ModeWebhook}
  3160  					options.Authorization.WebhookConfigFile = kcfd.Name()
  3161  					options.Authorization.WebhookVersion = "v1"
  3162  					// Bypass webhook cache to observe the policy plugin's cache behavior.
  3163  					options.Authorization.WebhookCacheAuthorizedTTL = 0
  3164  					options.Authorization.WebhookCacheUnauthorizedTTL = 0
  3165  				},
  3166  			})
  3167  			defer teardown()
  3168  
  3169  			policy := &admissionregistrationv1beta1.ValidatingAdmissionPolicy{
  3170  				ObjectMeta: metav1.ObjectMeta{
  3171  					Name: "test-authorization-decision-caching-policy",
  3172  				},
  3173  				Spec: admissionregistrationv1beta1.ValidatingAdmissionPolicySpec{
  3174  					MatchConstraints: &admissionregistrationv1beta1.MatchResources{
  3175  						ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
  3176  							{
  3177  								ResourceNames: []string{"test-authorization-decision-caching-namespace"},
  3178  								RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
  3179  									Operations: []admissionregistrationv1.OperationType{
  3180  										admissionregistrationv1.Create,
  3181  									},
  3182  									Rule: admissionregistrationv1.Rule{
  3183  										APIGroups:   []string{""},
  3184  										APIVersions: []string{"v1"},
  3185  										Resources:   []string{"namespaces"},
  3186  									},
  3187  								},
  3188  							},
  3189  						},
  3190  					},
  3191  					Validations: tc.validations,
  3192  				},
  3193  			}
  3194  
  3195  			policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, withWaitReadyConstraintAndExpression(policy), metav1.CreateOptions{})
  3196  			if err != nil {
  3197  				t.Fatal(err)
  3198  			}
  3199  
  3200  			if err := createAndWaitReady(t, client, makeBinding(policy.Name+"-binding", policy.Name, ""), nil); err != nil {
  3201  				t.Fatal(err)
  3202  			}
  3203  
  3204  			config = rest.CopyConfig(config)
  3205  			config.Impersonate = rest.ImpersonationConfig{
  3206  				UserName: "alice",
  3207  				UID:      "1234",
  3208  			}
  3209  			client, err = clientset.NewForConfig(config)
  3210  			if err != nil {
  3211  				t.Fatal(err)
  3212  			}
  3213  
  3214  			if _, err := client.CoreV1().Namespaces().Create(
  3215  				ctx,
  3216  				&v1.Namespace{
  3217  					ObjectMeta: metav1.ObjectMeta{
  3218  						Name: "test-authorization-decision-caching-namespace",
  3219  					},
  3220  				},
  3221  				metav1.CreateOptions{},
  3222  			); err != nil {
  3223  				t.Fatal(err)
  3224  			}
  3225  		})
  3226  	}
  3227  }