diff --git a/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go b/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go new file mode 100644 index 0000000000000..67ba6cec97f84 --- /dev/null +++ b/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go @@ -0,0 +1,150 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cel + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + "k8s.io/api/admissionregistration/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + genericfeatures "k8s.io/apiserver/pkg/features" + utilfeature "k8s.io/apiserver/pkg/util/feature" + clientset "k8s.io/client-go/kubernetes" + featuregatetesting "k8s.io/component-base/featuregate/testing" + apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" + "k8s.io/kubernetes/test/integration/framework" + "k8s.io/utils/ptr" +) + +func TestMutatingAdmissionPolicy(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true)() + server, err := apiservertesting.StartTestServer(t, nil, []string{ + "--enable-admission-plugins", "MutatingAdmissionPolicy", + }, framework.SharedEtcd()) + require.NoError(t, err) + defer server.TearDownFn() + + client, err := clientset.NewForConfig(server.ClientConfig) + require.NoError(t, err) + + // Create a basic MAP and binding the sets an annotation + _, err = client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Create( + context.TODO(), + &v1alpha1.MutatingAdmissionPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "basic-policy", + }, + Spec: v1alpha1.MutatingAdmissionPolicySpec{ + MatchConstraints: &v1alpha1.MatchResources{ + ResourceRules: []v1alpha1.NamedRuleWithOperations{ + { + RuleWithOperations: v1alpha1.RuleWithOperations{ + Operations: []admissionregistrationv1.OperationType{ + "*", + }, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{ + "*", + }, + APIVersions: []string{ + "*", + }, + Resources: []string{ + "*", + }, + }, + }, + }, + }, + }, + Mutations: []v1alpha1.Mutation{ + v1alpha1.Mutation{ + ReinvocationPolicy: ptr.To(v1alpha1.NeverReinvocationPolicy), + Expression: ` + Object{ + metadata: Object.metadata{ + annotations: { + "my-foo-annotation": "myAnnotationValue" + } + } + }`, + }, + }, + }, + }, + metav1.CreateOptions{ + FieldManager: "integration-test", + }, + ) + require.NoError(t, err) + + _, err = client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicyBindings().Create( + context.TODO(), + &v1alpha1.MutatingAdmissionPolicyBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "basic-policy-binding", + }, + Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{ + PolicyName: "basic-policy", + }, + }, + metav1.CreateOptions{ + FieldManager: "integration-test", + }, + ) + require.NoError(t, err) + + // Keep updating a configmap until it gets the mutation + _, err = client.CoreV1().ConfigMaps("default").Create( + context.TODO(), + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-configmap", + }, + }, + metav1.CreateOptions{}, + ) + require.NoError(t, err) + + // Wait for the mutation to be applied. May take a second for it to + // sync. Just keep updating unti we observer our new annotation + require.NoError(t, wait.PollUntilContextTimeout(context.TODO(), 100*time.Millisecond, 5*time.Second, false, func(ctx context.Context) (done bool, err error) { + currentObj, err := client.CoreV1().ConfigMaps("default").Update( + context.TODO(), + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-configmap", + }, + }, + metav1.UpdateOptions{}, + ) + if err != nil { + return false, err + } + if currentObj.Annotations["my-foo-annotation"] == "myAnnotationValue" { + return true, nil + } + return false, nil + })) + +}