diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 52dcefae025..3e4d2e3d98e 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -2006,6 +2006,7 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate genericfeatures.MutatingAdmissionPolicy: { {Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.34"), Default: false, PreRelease: featuregate.Beta}, + {Version: version.MustParse("1.36"), Default: true, PreRelease: featuregate.GA}, }, genericfeatures.OpenAPIEnums: { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go index ff342623a95..b20103c5747 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go @@ -18,7 +18,6 @@ package mutating import ( v1 "k8s.io/api/admissionregistration/v1" - "k8s.io/api/admissionregistration/v1beta1" "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/admission/plugin/policy/generic" ) @@ -59,19 +58,11 @@ func (v *mutatingAdmissionPolicyAccessor) GetParamKind() *v1.ParamKind { } func (v *mutatingAdmissionPolicyAccessor) GetMatchConstraints() *v1.MatchResources { - return convertV1alpha1ResourceRulesToV1(v.Spec.MatchConstraints) + return v.Spec.MatchConstraints } func (v *mutatingAdmissionPolicyAccessor) GetFailurePolicy() *v1.FailurePolicyType { - return toV1FailurePolicy(v.Spec.FailurePolicy) -} - -func toV1FailurePolicy(failurePolicy *v1beta1.FailurePolicyType) *v1.FailurePolicyType { - if failurePolicy == nil { - return nil - } - fp := v1.FailurePolicyType(*failurePolicy) - return &fp + return v.Spec.FailurePolicy } type mutatingAdmissionPolicyBindingAccessor struct { @@ -94,7 +85,7 @@ func (v *mutatingAdmissionPolicyBindingAccessor) GetPolicyName() types.Namespace } func (v *mutatingAdmissionPolicyBindingAccessor) GetMatchResources() *v1.MatchResources { - return convertV1alpha1ResourceRulesToV1(v.Spec.MatchResources) + return v.Spec.MatchResources } func (v *mutatingAdmissionPolicyBindingAccessor) GetParamRef() *v1.ParamRef { @@ -115,30 +106,3 @@ func (v *mutatingAdmissionPolicyBindingAccessor) GetParamRef() *v1.ParamRef { ParameterNotFoundAction: nfa, } } - -func convertV1alpha1ResourceRulesToV1(mc *v1beta1.MatchResources) *v1.MatchResources { - if mc == nil { - return nil - } - - var res v1.MatchResources - res.NamespaceSelector = mc.NamespaceSelector - res.ObjectSelector = mc.ObjectSelector - for _, ex := range mc.ExcludeResourceRules { - res.ExcludeResourceRules = append(res.ExcludeResourceRules, v1.NamedRuleWithOperations{ - ResourceNames: ex.ResourceNames, - RuleWithOperations: ex.RuleWithOperations, - }) - } - for _, ex := range mc.ResourceRules { - res.ResourceRules = append(res.ResourceRules, v1.NamedRuleWithOperations{ - ResourceNames: ex.ResourceNames, - RuleWithOperations: ex.RuleWithOperations, - }) - } - if mc.MatchPolicy != nil { - mp := v1.MatchPolicyType(*mc.MatchPolicy) - res.MatchPolicy = &mp - } - return &res -} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go index 7880601feaa..a0772aab441 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go @@ -19,9 +19,10 @@ package mutating import ( "fmt" - "k8s.io/api/admissionregistration/v1beta1" + "k8s.io/api/admissionregistration/v1" plugincel "k8s.io/apiserver/pkg/admission/plugin/cel" "k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch" + "k8s.io/apiserver/pkg/admission/plugin/policy/validating" "k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions" apiservercel "k8s.io/apiserver/pkg/cel" "k8s.io/apiserver/pkg/cel/environment" @@ -32,7 +33,7 @@ import ( // // Each individual mutation is compiled into MutationEvaluationFunc and // returned is a PolicyEvaluator in the same order as the mutations appeared in the policy. -func compilePolicy(policy *Policy) PolicyEvaluator { +func compilePolicy(policy *v1.MutatingAdmissionPolicy) PolicyEvaluator { opts := plugincel.OptionalVariableDeclarations{HasParams: policy.Spec.ParamKind != nil, HasAuthorizer: true} compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())) if err != nil { @@ -43,7 +44,7 @@ func compilePolicy(policy *Policy) PolicyEvaluator { } // Compile and store variables - compiler.CompileAndStoreVariables(convertv1alpha1Variables(policy.Spec.Variables), opts, environment.StoredExpressions) + compiler.CompileAndStoreVariables(convertV1Variables(policy.Spec.Variables), opts, environment.StoredExpressions) // Compile matchers var matcher matchconditions.Matcher = nil @@ -53,7 +54,7 @@ func compilePolicy(policy *Policy) PolicyEvaluator { for i := range matchConditions { matchExpressionAccessors[i] = (*matchconditions.MatchCondition)(&matchConditions[i]) } - matcher = matchconditions.NewMatcher(compiler.CompileCondition(matchExpressionAccessors, opts, environment.StoredExpressions), toV1FailurePolicy(policy.Spec.FailurePolicy), "policy", "validate", policy.Name) + matcher = matchconditions.NewMatcher(compiler.CompileCondition(matchExpressionAccessors, opts, environment.StoredExpressions), policy.Spec.FailurePolicy, "policy", "validate", policy.Name) } // Compiler patchers @@ -62,13 +63,13 @@ func compilePolicy(policy *Policy) PolicyEvaluator { patchOptions.HasPatchTypes = true for _, m := range policy.Spec.Mutations { switch m.PatchType { - case v1beta1.PatchTypeJSONPatch: + case v1.PatchTypeJSONPatch: if m.JSONPatch != nil { accessor := &patch.JSONPatchCondition{Expression: m.JSONPatch.Expression} compileResult := compiler.CompileMutatingEvaluator(accessor, patchOptions, environment.StoredExpressions) patchers = append(patchers, patch.NewJSONPatcher(compileResult)) } - case v1beta1.PatchTypeApplyConfiguration: + case v1.PatchTypeApplyConfiguration: if m.ApplyConfiguration != nil { accessor := &patch.ApplyConfigurationCondition{Expression: m.ApplyConfiguration.Expression} compileResult := compiler.CompileMutatingEvaluator(accessor, patchOptions, environment.StoredExpressions) @@ -79,3 +80,17 @@ func compilePolicy(policy *Policy) PolicyEvaluator { return PolicyEvaluator{Matcher: matcher, Mutators: patchers, CompositedCompiler: compiler} } + +func convertV1Variables(variables []v1.Variable) []plugincel.NamedExpressionAccessor { + if variables == nil { + return nil + } + res := make([]plugincel.NamedExpressionAccessor, len(variables)) + for i, v := range variables { + res[i] = &validating.Variable{ + Name: v.Name, + Expression: v.Expression, + } + } + return res +} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go index 8c63773af23..296584b2593 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go @@ -24,7 +24,7 @@ import ( "github.com/google/go-cmp/cmp" - "k8s.io/api/admissionregistration/v1beta1" + v1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -50,7 +50,7 @@ func TestCompilation(t *testing.T) { deploymentGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"} testCases := []struct { name string - policy *Policy + policy *v1.MutatingAdmissionPolicy gvr schema.GroupVersionResource object runtime.Object oldObject runtime.Object @@ -61,9 +61,9 @@ func TestCompilation(t *testing.T) { }{ { name: "applyConfiguration then jsonPatch", - policy: mutations(policy("d1"), v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policy: mutations(policy("d1"), v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{ spec: Object.spec{ replicas: object.spec.replicas + 100 @@ -71,9 +71,9 @@ func TestCompilation(t *testing.T) { }`, }, }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeJSONPatch, - JSONPatch: &v1beta1.JSONPatch{ + v1.Mutation{ + PatchType: v1.PatchTypeJSONPatch, + JSONPatch: &v1.JSONPatch{ Expression: `[ JSONPatch{op: "replace", path: "/spec/replicas", value: object.spec.replicas + 10} ]`, @@ -86,17 +86,17 @@ func TestCompilation(t *testing.T) { { name: "jsonPatch then applyConfiguration", policy: mutations(policy("d1"), - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeJSONPatch, - JSONPatch: &v1beta1.JSONPatch{ + v1.Mutation{ + PatchType: v1.PatchTypeJSONPatch, + JSONPatch: &v1.JSONPatch{ Expression: `[ JSONPatch{op: "replace", path: "/spec/replicas", value: object.spec.replicas + 10} ]`, }, }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{ spec: Object.spec{ replicas: object.spec.replicas + 100 @@ -110,7 +110,7 @@ func TestCompilation(t *testing.T) { }, { name: "jsonPatch with variable", - policy: jsonPatches(variables(policy("d1"), v1beta1.Variable{Name: "desired", Expression: "10"}), v1beta1.JSONPatch{ + policy: jsonPatches(variables(policy("d1"), v1.Variable{Name: "desired", Expression: "10"}), v1.JSONPatch{ Expression: `[ JSONPatch{op: "replace", path: "/spec/replicas", value: variables.desired + 1}, ]`, @@ -121,7 +121,7 @@ func TestCompilation(t *testing.T) { }, { name: "apply configuration with variable", - policy: applyConfigurations(variables(policy("d1"), v1beta1.Variable{Name: "desired", Expression: "10"}), + policy: applyConfigurations(variables(policy("d1"), v1.Variable{Name: "desired", Expression: "10"}), `Object{ spec: Object.spec{ replicas: variables.desired + 1 @@ -138,7 +138,7 @@ func TestCompilation(t *testing.T) { spec: Object.spec{ replicas: int(params.data['k1']) } - }`), &v1beta1.ParamKind{Kind: "ConfigMap", APIVersion: "v1"}), + }`), &v1.ParamKind{Kind: "ConfigMap", APIVersion: "v1"}), params: &corev1.ConfigMap{Data: map[string]string{"k1": "100"}}, gvr: deploymentGVR, object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}}, @@ -146,7 +146,7 @@ func TestCompilation(t *testing.T) { }, { name: "jsonPatch with excessive cost", - policy: jsonPatches(variables(policy("d1"), v1beta1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}), v1beta1.JSONPatch{ + policy: jsonPatches(variables(policy("d1"), v1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}), v1.JSONPatch{ Expression: `[ JSONPatch{op: "replace", path: "/spec/replicas", value: variables.list.all(x1, variables.list.all(x2, variables.list.all(x3, variables.list.all(x4, variables.list.all(x5, variables.list.all(x5, "0123456789" == "0123456789"))))))? 1 : 0 @@ -164,14 +164,14 @@ func TestCompilation(t *testing.T) { spec: Object.spec{ replicas: variables.list.all(x1, variables.list.all(x2, variables.list.all(x3, variables.list.all(x4, variables.list.all(x5, variables.list.all(x5, "0123456789" == "0123456789"))))))? 1 : 0 } - }`), v1beta1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}), + }`), v1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}), gvr: deploymentGVR, object: &appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: ptr.To[int32](1)}}, expectedErr: "operation cancelled: actual cost limit exceeded", }, { name: "request variable", - policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{ + policy: jsonPatches(policy("d1"), v1.JSONPatch{ Expression: `[ JSONPatch{op: "replace", path: "/spec/replicas", value: request.kind.group == 'apps' && request.kind.version == 'v1' && request.kind.kind == 'Deployment' ? 10 : 0 @@ -183,7 +183,7 @@ func TestCompilation(t *testing.T) { }, { name: "namespace request variable", - policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{ + policy: jsonPatches(policy("d1"), v1.JSONPatch{ Expression: `[ JSONPatch{op: "replace", path: "/spec/replicas", value: namespaceObject.metadata.name == 'ns1' ? 10 : 0 @@ -196,7 +196,7 @@ func TestCompilation(t *testing.T) { }, { name: "authorizer check", - policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{ + policy: jsonPatches(policy("d1"), v1.JSONPatch{ Expression: `[ JSONPatch{op: "replace", path: "/spec/replicas", value: authorizer.group('').resource('endpoints').check('create').allowed() ? 10 : 0 @@ -209,7 +209,7 @@ func TestCompilation(t *testing.T) { }, { name: "object type has field access", - policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{ + policy: jsonPatches(policy("d1"), v1.JSONPatch{ Expression: `[ JSONPatch{ op: "add", path: "/metadata/labels", @@ -227,7 +227,7 @@ func TestCompilation(t *testing.T) { }, { name: "object type has field testing", - policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{ + policy: jsonPatches(policy("d1"), v1.JSONPatch{ Expression: `[ JSONPatch{ op: "add", path: "/metadata/labels", @@ -247,7 +247,7 @@ func TestCompilation(t *testing.T) { }, { name: "object type equality", - policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{ + policy: jsonPatches(policy("d1"), v1.JSONPatch{ Expression: `[ JSONPatch{ op: "add", path: "/metadata/labels", @@ -275,7 +275,7 @@ func TestCompilation(t *testing.T) { // recompile all expressions with fully bound types before evaluation and report // errors if invalid Object types like this are initialized. name: "object types are not fully type checked", - policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{ + policy: jsonPatches(policy("d1"), v1.JSONPatch{ Expression: `[ JSONPatch{ op: "add", path: "/spec", @@ -401,52 +401,52 @@ func TestCompilation(t *testing.T) { } } -func policy(name string) *v1beta1.MutatingAdmissionPolicy { - return &v1beta1.MutatingAdmissionPolicy{ +func policy(name string) *v1.MutatingAdmissionPolicy { + return &v1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, - Spec: v1beta1.MutatingAdmissionPolicySpec{}, + Spec: v1.MutatingAdmissionPolicySpec{}, } } -func variables(policy *v1beta1.MutatingAdmissionPolicy, variables ...v1beta1.Variable) *v1beta1.MutatingAdmissionPolicy { +func variables(policy *v1.MutatingAdmissionPolicy, variables ...v1.Variable) *v1.MutatingAdmissionPolicy { policy.Spec.Variables = append(policy.Spec.Variables, variables...) return policy } -func jsonPatches(policy *v1beta1.MutatingAdmissionPolicy, jsonPatches ...v1beta1.JSONPatch) *v1beta1.MutatingAdmissionPolicy { +func jsonPatches(policy *v1.MutatingAdmissionPolicy, jsonPatches ...v1.JSONPatch) *v1.MutatingAdmissionPolicy { for _, jsonPatch := range jsonPatches { - policy.Spec.Mutations = append(policy.Spec.Mutations, v1beta1.Mutation{ + policy.Spec.Mutations = append(policy.Spec.Mutations, v1.Mutation{ JSONPatch: &jsonPatch, - PatchType: v1beta1.PatchTypeJSONPatch, + PatchType: v1.PatchTypeJSONPatch, }) } return policy } -func applyConfigurations(policy *v1beta1.MutatingAdmissionPolicy, expressions ...string) *v1beta1.MutatingAdmissionPolicy { +func applyConfigurations(policy *v1.MutatingAdmissionPolicy, expressions ...string) *v1.MutatingAdmissionPolicy { for _, expression := range expressions { - policy.Spec.Mutations = append(policy.Spec.Mutations, v1beta1.Mutation{ - ApplyConfiguration: &v1beta1.ApplyConfiguration{Expression: expression}, - PatchType: v1beta1.PatchTypeApplyConfiguration, + policy.Spec.Mutations = append(policy.Spec.Mutations, v1.Mutation{ + ApplyConfiguration: &v1.ApplyConfiguration{Expression: expression}, + PatchType: v1.PatchTypeApplyConfiguration, }) } return policy } -func paramKind(policy *v1beta1.MutatingAdmissionPolicy, paramKind *v1beta1.ParamKind) *v1beta1.MutatingAdmissionPolicy { +func paramKind(policy *v1.MutatingAdmissionPolicy, paramKind *v1.ParamKind) *v1.MutatingAdmissionPolicy { policy.Spec.ParamKind = paramKind return policy } -func mutations(policy *v1beta1.MutatingAdmissionPolicy, mutations ...v1beta1.Mutation) *v1beta1.MutatingAdmissionPolicy { +func mutations(policy *v1.MutatingAdmissionPolicy, mutations ...v1.Mutation) *v1.MutatingAdmissionPolicy { policy.Spec.Mutations = append(policy.Spec.Mutations, mutations...) return policy } -func matchConstraints(policy *v1beta1.MutatingAdmissionPolicy, matchConstraints *v1beta1.MatchResources) *v1beta1.MutatingAdmissionPolicy { +func matchConstraints(policy *v1.MutatingAdmissionPolicy, matchConstraints *v1.MatchResources) *v1.MutatingAdmissionPolicy { policy.Spec.MatchConstraints = matchConstraints return policy } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go index 3b81ce0d72e..e5b10c7f6d6 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go @@ -22,7 +22,7 @@ import ( "fmt" "time" - "k8s.io/api/admissionregistration/v1beta1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" v1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -49,7 +49,7 @@ func NewDispatcher(a authorizer.Authorizer, m *matching.Matcher, tcm patch.TypeC authz: a, typeConverterManager: tcm, } - res.Dispatcher = generic.NewPolicyDispatcher( + res.Dispatcher = generic.NewPolicyDispatcher[*Policy, *PolicyBinding, PolicyEvaluator]( NewMutatingAdmissionPolicyAccessor, NewMutatingAdmissionPolicyBindingAccessor, m, @@ -210,7 +210,7 @@ func (d *dispatcher) dispatchInvocations( policyReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins() reinvokeCtx.SetShouldReinvoke() } - if invocation.Policy.Spec.ReinvocationPolicy == v1beta1.IfNeededReinvocationPolicy { + if invocation.Policy.Spec.ReinvocationPolicy == admissionregistrationv1.IfNeededReinvocationPolicy { policyReinvokeCtx.AddReinvocablePolicyToPreviouslyInvoked(invocationKey) } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go index d4f01266c06..7885d314408 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go @@ -23,7 +23,6 @@ import ( "time" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - "k8s.io/api/admissionregistration/v1beta1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -33,7 +32,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/admission/plugin/policy/generic" "k8s.io/apiserver/pkg/admission/plugin/policy/matching" "k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch" "k8s.io/client-go/informers" @@ -75,16 +73,16 @@ func TestDispatcher(t *testing.T) { }, }, }}, - policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{ + policyHooks: []PolicyHook{ { - Policy: mutations(matchConstraints(policy("policy1"), &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Policy: mutations(matchConstraints(policy("policy1"), &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -93,18 +91,18 @@ func TestDispatcher(t *testing.T) { }, }, }, - }), v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + }), admissionregistrationv1.Mutation{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ spec: Object.spec{ replicas: object.spec.replicas + 100 } }`, }}), - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy1", }, }}, @@ -166,16 +164,16 @@ func TestDispatcher(t *testing.T) { }, }, }, - policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{ + policyHooks: []PolicyHook{ { - Policy: paramKind(mutations(matchConstraints(policy("policy1"), &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Policy: paramKind(mutations(matchConstraints(policy("policy1"), &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -184,24 +182,24 @@ func TestDispatcher(t *testing.T) { }, }, }}), - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + admissionregistrationv1.Mutation{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ spec: Object.spec{ replicas: object.spec.replicas + int(params.data['key']) } }`, }}), - &v1beta1.ParamKind{ + &admissionregistrationv1.ParamKind{ APIVersion: "v1", Kind: "ConfigMap", }), - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy1", - ParamRef: &v1beta1.ParamRef{Name: "cm1", Namespace: "default"}, + ParamRef: &admissionregistrationv1.ParamRef{Name: "cm1", Namespace: "default"}, }, }}, }, @@ -246,16 +244,16 @@ func TestDispatcher(t *testing.T) { Type: appsv1.RollingUpdateDeploymentStrategyType, }, }}, - policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{ + policyHooks: []PolicyHook{ { - Policy: mutations(matchConstraints(policy("policy1"), &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Policy: mutations(matchConstraints(policy("policy1"), &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -264,31 +262,31 @@ func TestDispatcher(t *testing.T) { }, }, }, - }), v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + }), admissionregistrationv1.Mutation{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ metadata: Object.metadata{ labels: {"policy1": string(int(object.?metadata.labels["count"].orValue("1")) + 1)} } }`, }}), - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy1", }, }}, }, { - Policy: mutations(matchConstraints(policy("policy2"), &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Policy: mutations(matchConstraints(policy("policy2"), &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -297,18 +295,18 @@ func TestDispatcher(t *testing.T) { }, }, }, - }), v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + }), admissionregistrationv1.Mutation{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ metadata: Object.metadata{ labels: {"policy2": string(int(object.?metadata.labels["count"].orValue("1")) + 1)} } }`, }}), - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy2", }, }}, @@ -357,21 +355,21 @@ func TestDispatcher(t *testing.T) { Type: appsv1.RollingUpdateDeploymentStrategyType, }, }}, - policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{ + policyHooks: []PolicyHook{ { - Policy: &v1beta1.MutatingAdmissionPolicy{ + Policy: &admissionregistrationv1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "policy1", }, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: admissionregistrationv1.MutatingAdmissionPolicySpec{ + MatchConstraints: &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -381,9 +379,9 @@ func TestDispatcher(t *testing.T) { }, }, }, - Mutations: []v1beta1.Mutation{{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + Mutations: []admissionregistrationv1.Mutation{{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ metadata: Object.metadata{ labels: {"environment": "production"} @@ -392,27 +390,27 @@ func TestDispatcher(t *testing.T) { }, }, }, - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy1", }, }}, }, { - Policy: &v1beta1.MutatingAdmissionPolicy{ + Policy: &admissionregistrationv1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "policy2", }, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: admissionregistrationv1.MutatingAdmissionPolicySpec{ + MatchConstraints: &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -422,15 +420,15 @@ func TestDispatcher(t *testing.T) { }, }, }, - MatchConditions: []v1beta1.MatchCondition{ + MatchConditions: []admissionregistrationv1.MatchCondition{ { Name: "prodonly", Expression: `object.?metadata.labels["environment"].orValue("") == "production"`, }, }, - Mutations: []v1beta1.Mutation{{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + Mutations: []admissionregistrationv1.Mutation{{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ metadata: Object.metadata{ labels: {"policy1invoked": "true"} @@ -439,9 +437,9 @@ func TestDispatcher(t *testing.T) { }, }, }, - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy2", }, }}, @@ -491,21 +489,21 @@ func TestDispatcher(t *testing.T) { Type: appsv1.RollingUpdateDeploymentStrategyType, }, }}, - policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{ + policyHooks: []PolicyHook{ { - Policy: &v1beta1.MutatingAdmissionPolicy{ + Policy: &admissionregistrationv1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "policy1", }, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: admissionregistrationv1.MutatingAdmissionPolicySpec{ + MatchConstraints: &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -515,15 +513,15 @@ func TestDispatcher(t *testing.T) { }, }, }, - MatchConditions: []v1beta1.MatchCondition{ + MatchConditions: []admissionregistrationv1.MatchCondition{ { Name: "prodonly", Expression: `object.?metadata.labels["environment"].orValue("") == "production"`, }, }, - Mutations: []v1beta1.Mutation{{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + Mutations: []admissionregistrationv1.Mutation{{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ metadata: Object.metadata{ labels: {"policy1invoked": "true"} @@ -532,27 +530,27 @@ func TestDispatcher(t *testing.T) { }, }, }, - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy1", }, }}, }, { - Policy: &v1beta1.MutatingAdmissionPolicy{ + Policy: &admissionregistrationv1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "policy2", }, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: admissionregistrationv1.MutatingAdmissionPolicySpec{ + MatchConstraints: &admissionregistrationv1.MatchResources{ + MatchPolicy: ptr.To(admissionregistrationv1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, - ResourceRules: []v1beta1.NamedRuleWithOperations{ + ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Rule: v1beta1.Rule{ + RuleWithOperations: admissionregistrationv1.RuleWithOperations{ + Rule: admissionregistrationv1.Rule{ APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, @@ -562,9 +560,9 @@ func TestDispatcher(t *testing.T) { }, }, }, - Mutations: []v1beta1.Mutation{{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + Mutations: []admissionregistrationv1.Mutation{{ + PatchType: admissionregistrationv1.PatchTypeApplyConfiguration, + ApplyConfiguration: &admissionregistrationv1.ApplyConfiguration{ Expression: `Object{ metadata: Object.metadata{ labels: {"environment": "production"} @@ -573,9 +571,9 @@ func TestDispatcher(t *testing.T) { }, }, }, - Bindings: []*PolicyBinding{{ + Bindings: []*admissionregistrationv1.MutatingAdmissionPolicyBinding{{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: admissionregistrationv1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy2", }, }}, diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go index 8d14a7a20f6..3cd51f59100 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go @@ -22,7 +22,7 @@ import ( celgo "github.com/google/cel-go/cel" - "k8s.io/api/admissionregistration/v1beta1" + v1 "k8s.io/api/admissionregistration/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" @@ -54,9 +54,9 @@ func Register(plugins *admission.Plugins) { }) } -type Policy = v1beta1.MutatingAdmissionPolicy -type PolicyBinding = v1beta1.MutatingAdmissionPolicyBinding -type PolicyMutation = v1beta1.Mutation +type Policy = v1.MutatingAdmissionPolicy +type PolicyBinding = v1.MutatingAdmissionPolicyBinding + type PolicyHook = generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator] type Mutator struct { @@ -97,8 +97,8 @@ func NewPlugin(_ io.Reader) *Plugin { handler, func(f informers.SharedInformerFactory, client kubernetes.Interface, dynamicClient dynamic.Interface, restMapper meta.RESTMapper) generic.Source[PolicyHook] { return generic.NewPolicySource( - f.Admissionregistration().V1beta1().MutatingAdmissionPolicies().Informer(), - f.Admissionregistration().V1beta1().MutatingAdmissionPolicyBindings().Informer(), + f.Admissionregistration().V1().MutatingAdmissionPolicies().Informer(), + f.Admissionregistration().V1().MutatingAdmissionPolicyBindings().Informer(), NewMutatingAdmissionPolicyAccessor, NewMutatingAdmissionPolicyBindingAccessor, compilePolicy, @@ -142,11 +142,3 @@ func (v *Variable) ReturnTypes() []*celgo.Type { func (v *Variable) GetName() string { return v.Name } - -func convertv1alpha1Variables(variables []v1beta1.Variable) []cel.NamedExpressionAccessor { - namedExpressions := make([]cel.NamedExpressionAccessor, len(variables)) - for i, variable := range variables { - namedExpressions[i] = &Variable{Name: variable.Name, Expression: variable.Expression} - } - return namedExpressions -} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go index e57f17ce8fd..2ab6cfd8f4c 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/require" - "k8s.io/api/admissionregistration/v1beta1" + v1 "k8s.io/api/admissionregistration/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -42,10 +42,10 @@ import ( func setupTest( t *testing.T, - compiler func(*mutating.Policy) mutating.PolicyEvaluator, -) *generic.PolicyTestContext[*mutating.Policy, *mutating.PolicyBinding, mutating.PolicyEvaluator] { + compiler func(*v1.MutatingAdmissionPolicy) mutating.PolicyEvaluator, +) *generic.PolicyTestContext[*v1.MutatingAdmissionPolicy, *v1.MutatingAdmissionPolicyBinding, mutating.PolicyEvaluator] { - testContext, testCancel, err := generic.NewPolicyTestContext[*mutating.Policy, *mutating.PolicyBinding, mutating.PolicyEvaluator]( + testContext, testCancel, err := generic.NewPolicyTestContext[*v1.MutatingAdmissionPolicy, *v1.MutatingAdmissionPolicyBinding, mutating.PolicyEvaluator]( t, mutating.NewMutatingAdmissionPolicyAccessor, mutating.NewMutatingAdmissionPolicyBindingAccessor, @@ -81,33 +81,33 @@ func TestBasicPatch(t *testing.T) { expectedAnnotations := map[string]string{"foo": "bar"} // Treat all policies as setting foo annotation to bar - testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator { + testContext := setupTest(t, func(p *v1.MutatingAdmissionPolicy) mutating.PolicyEvaluator { return mutating.PolicyEvaluator{Mutators: []patch.Patcher{annotationPatcher{expectedAnnotations}}} }) // Set up a policy and binding that match, no params require.NoError(t, testContext.UpdateAndWait( - &mutating.Policy{ + &v1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{Name: "policy"}, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: v1.MutatingAdmissionPolicySpec{ + MatchConstraints: &v1.MatchResources{ + MatchPolicy: ptr.To(v1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, }, - Mutations: []v1beta1.Mutation{ + Mutations: []v1.Mutation{ { - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: "ignored, but required", }, - PatchType: v1beta1.PatchTypeApplyConfiguration, + PatchType: v1.PatchTypeApplyConfiguration, }, }, }, }, - &mutating.PolicyBinding{ + &v1.MutatingAdmissionPolicyBinding{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: v1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy", }, }, @@ -136,7 +136,7 @@ func TestJSONPatch(t *testing.T) { }, } - testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator { + testContext := setupTest(t, func(p *v1.MutatingAdmissionPolicy) mutating.PolicyEvaluator { return mutating.PolicyEvaluator{ Mutators: []patch.Patcher{smdPatcher{patch: patchObj}}, } @@ -144,27 +144,27 @@ func TestJSONPatch(t *testing.T) { // Set up a policy and binding that match, no params require.NoError(t, testContext.UpdateAndWait( - &mutating.Policy{ + &v1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{Name: "policy"}, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: v1.MutatingAdmissionPolicySpec{ + MatchConstraints: &v1.MatchResources{ + MatchPolicy: ptr.To(v1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, }, - Mutations: []v1beta1.Mutation{ + Mutations: []v1.Mutation{ { - JSONPatch: &v1beta1.JSONPatch{ + JSONPatch: &v1.JSONPatch{ Expression: "ignored, but required", }, - PatchType: v1beta1.PatchTypeApplyConfiguration, + PatchType: v1.PatchTypeApplyConfiguration, }, }, }, }, - &mutating.PolicyBinding{ + &v1.MutatingAdmissionPolicyBinding{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: v1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy", }, }, @@ -198,7 +198,7 @@ func TestSSAPatch(t *testing.T) { }, } - testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator { + testContext := setupTest(t, func(p *v1.MutatingAdmissionPolicy) mutating.PolicyEvaluator { return mutating.PolicyEvaluator{ Mutators: []patch.Patcher{smdPatcher{patch: patchObj}}, } @@ -206,27 +206,27 @@ func TestSSAPatch(t *testing.T) { // Set up a policy and binding that match, no params require.NoError(t, testContext.UpdateAndWait( - &mutating.Policy{ + &v1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{Name: "policy"}, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: v1.MutatingAdmissionPolicySpec{ + MatchConstraints: &v1.MatchResources{ + MatchPolicy: ptr.To(v1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, }, - Mutations: []v1beta1.Mutation{ + Mutations: []v1.Mutation{ { - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: "ignored, but required", }, - PatchType: v1beta1.PatchTypeApplyConfiguration, + PatchType: v1.PatchTypeApplyConfiguration, }, }, }, }, - &mutating.PolicyBinding{ + &v1.MutatingAdmissionPolicyBinding{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: v1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy", }, }, @@ -265,7 +265,7 @@ func TestSSAMapList(t *testing.T) { }, } - testContext := setupTest(t, func(p *mutating.Policy) mutating.PolicyEvaluator { + testContext := setupTest(t, func(p *v1.MutatingAdmissionPolicy) mutating.PolicyEvaluator { return mutating.PolicyEvaluator{ Mutators: []patch.Patcher{smdPatcher{patch: patchObj}}, } @@ -273,27 +273,27 @@ func TestSSAMapList(t *testing.T) { // Set up a policy and binding that match, no params require.NoError(t, testContext.UpdateAndWait( - &mutating.Policy{ + &v1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{Name: "policy"}, - Spec: v1beta1.MutatingAdmissionPolicySpec{ - MatchConstraints: &v1beta1.MatchResources{ - MatchPolicy: ptr.To(v1beta1.Equivalent), + Spec: v1.MutatingAdmissionPolicySpec{ + MatchConstraints: &v1.MatchResources{ + MatchPolicy: ptr.To(v1.Equivalent), NamespaceSelector: &metav1.LabelSelector{}, ObjectSelector: &metav1.LabelSelector{}, }, - Mutations: []v1beta1.Mutation{ + Mutations: []v1.Mutation{ { - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: "ignored, but required", }, - PatchType: v1beta1.PatchTypeApplyConfiguration, + PatchType: v1.PatchTypeApplyConfiguration, }, }, }, }, - &mutating.PolicyBinding{ + &v1.MutatingAdmissionPolicyBinding{ ObjectMeta: metav1.ObjectMeta{Name: "binding"}, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: v1.MutatingAdmissionPolicyBindingSpec{ PolicyName: "policy", }, }, diff --git a/staging/src/k8s.io/apiserver/pkg/features/kube_features.go b/staging/src/k8s.io/apiserver/pkg/features/kube_features.go index cf8ce070b79..58a08e203df 100644 --- a/staging/src/k8s.io/apiserver/pkg/features/kube_features.go +++ b/staging/src/k8s.io/apiserver/pkg/features/kube_features.go @@ -421,6 +421,7 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate MutatingAdmissionPolicy: { {Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.34"), Default: false, PreRelease: featuregate.Beta}, + {Version: version.MustParse("1.36"), Default: true, PreRelease: featuregate.GA}, }, OpenAPIEnums: { diff --git a/test/compatibility_lifecycle/reference/feature_list.md b/test/compatibility_lifecycle/reference/feature_list.md index 59b5923a3a1..c484b63a674 100644 --- a/test/compatibility_lifecycle/reference/feature_list.md +++ b/test/compatibility_lifecycle/reference/feature_list.md @@ -123,7 +123,7 @@ | MutablePVNodeAffinity | | | 1.35– | | | | | [code](https://cs.k8s.io/?q=%5CbMutablePVNodeAffinity%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbMutablePVNodeAffinity%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | MutablePodResourcesForSuspendedJobs | :ballot_box_with_check: 1.36+ | | 1.35 | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbMutablePodResourcesForSuspendedJobs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbMutablePodResourcesForSuspendedJobs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | MutableSchedulingDirectivesForSuspendedJobs | :ballot_box_with_check: 1.36+ | | 1.35 | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbMutableSchedulingDirectivesForSuspendedJobs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbMutableSchedulingDirectivesForSuspendedJobs%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | -| MutatingAdmissionPolicy | | | 1.32–1.33 | 1.34– | | | | [code](https://cs.k8s.io/?q=%5CbMutatingAdmissionPolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbMutatingAdmissionPolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | +| MutatingAdmissionPolicy | :ballot_box_with_check: 1.36+ | | 1.32–1.33 | 1.34–1.35 | 1.36– | | | [code](https://cs.k8s.io/?q=%5CbMutatingAdmissionPolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbMutatingAdmissionPolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | NFTablesProxyMode | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.33+ | 1.29–1.30 | 1.31–1.32 | 1.33– | | | [code](https://cs.k8s.io/?q=%5CbNFTablesProxyMode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbNFTablesProxyMode%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | NodeDeclaredFeatures | | | 1.35– | | | | | [code](https://cs.k8s.io/?q=%5CbNodeDeclaredFeatures%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbNodeDeclaredFeatures%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | NodeInclusionPolicyInPodTopologySpread | :ballot_box_with_check: 1.26+ | :closed_lock_with_key: 1.33+ | 1.25 | 1.26–1.32 | 1.33– | | | [code](https://cs.k8s.io/?q=%5CbNodeInclusionPolicyInPodTopologySpread%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbNodeInclusionPolicyInPodTopologySpread%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | diff --git a/test/compatibility_lifecycle/reference/versioned_feature_list.yaml b/test/compatibility_lifecycle/reference/versioned_feature_list.yaml index 519c5cc1310..3dd505b22de 100644 --- a/test/compatibility_lifecycle/reference/versioned_feature_list.yaml +++ b/test/compatibility_lifecycle/reference/versioned_feature_list.yaml @@ -1141,6 +1141,10 @@ lockToDefault: false preRelease: Beta version: "1.34" + - default: true + lockToDefault: false + preRelease: GA + version: "1.36" - name: NFTablesProxyMode versionedSpecs: - default: false diff --git a/test/integration/apiserver/admissionwebhook/admission_test.go b/test/integration/apiserver/admissionwebhook/admission_test.go index 38dbb096366..4d394d7166e 100644 --- a/test/integration/apiserver/admissionwebhook/admission_test.go +++ b/test/integration/apiserver/admissionwebhook/admission_test.go @@ -157,6 +157,8 @@ var ( gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicybindings"): true, gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicies"): true, gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicybindings"): true, + gvr("admissionregistration.k8s.io", "v1", "mutatingadmissionpolicies"): true, + gvr("admissionregistration.k8s.io", "v1", "mutatingadmissionpolicybindings"): true, } parentResources = map[schema.GroupVersionResource]schema.GroupVersionResource{ diff --git a/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go b/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go index e73753e0cfc..437023e2074 100644 --- a/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go +++ b/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go @@ -18,6 +18,7 @@ package cel import ( "context" + "encoding/json" "errors" "fmt" "reflect" @@ -29,8 +30,8 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - "k8s.io/api/admissionregistration/v1beta1" + v1 "k8s.io/api/admissionregistration/v1" + v1beta1 "k8s.io/api/admissionregistration/v1beta1" corev1 "k8s.io/api/core/v1" apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -55,13 +56,13 @@ import ( // and waiting for bindings to become ready by dry-running marker requests until the binding successfully // mutates a marker, and then verifies the policy exactly once. func TestMutatingAdmissionPolicy(t *testing.T) { - allow := v1beta1.AllowAction - matchEndpointResources := v1beta1.MatchResources{ - ResourceRules: []v1beta1.NamedRuleWithOperations{ + allow := v1.AllowAction + matchEndpointResources := v1.MatchResources{ + ResourceRules: []v1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{"*"}, - Rule: admissionregistrationv1.Rule{ + RuleWithOperations: v1.RuleWithOperations{ + Operations: []v1.OperationType{"*"}, + Rule: v1.Rule{ APIGroups: []string{""}, APIVersions: []string{"v1"}, Resources: []string{"endpoints"}, @@ -73,11 +74,11 @@ func TestMutatingAdmissionPolicy(t *testing.T) { cases := []struct { name string - policies []*v1beta1.MutatingAdmissionPolicy - bindings []*v1beta1.MutatingAdmissionPolicyBinding + policies []*v1.MutatingAdmissionPolicy + bindings []*v1.MutatingAdmissionPolicyBinding params []*corev1.ConfigMap - requestOperation admissionregistrationv1.OperationType + requestOperation v1.OperationType requestResource schema.GroupVersionResource subresources []string // Only supported for requestOperation=Update since subresources can not be created requestObject runtime.Object @@ -85,10 +86,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }{ { name: "basic", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("basic-policy", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("basic-policy", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -100,10 +101,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, }), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("basic-policy", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -123,10 +124,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, { name: "multiple policies", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("multi-policy-1", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("multi-policy-1", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -137,9 +138,9 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }`, }, }), - mutatingPolicy("multi-policy-2", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + mutatingPolicy("multi-policy-2", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -151,11 +152,11 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, }), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("multi-policy-1", nil, nil), mutatingBinding("multi-policy-2", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -176,13 +177,13 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, { name: "policy with native param", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("policy-with-native", v1beta1.NeverReinvocationPolicy, matchEndpointResources, &v1beta1.ParamKind{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("policy-with-native", v1.NeverReinvocationPolicy, matchEndpointResources, &v1.ParamKind{ APIVersion: "v1", Kind: "ConfigMap", - }, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + }, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -194,8 +195,8 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, }), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ - mutatingBinding("policy-with-native", &v1beta1.ParamRef{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ + mutatingBinding("policy-with-native", &v1.ParamRef{ Name: "policy-with-native-param", ParameterNotFoundAction: &allow, }, nil), @@ -212,7 +213,7 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, }, }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -232,19 +233,19 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, { name: "policy with multiple params quantified by single binding", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("multi-param-binding", v1beta1.NeverReinvocationPolicy, matchEndpointResources, &v1beta1.ParamKind{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("multi-param-binding", v1.NeverReinvocationPolicy, matchEndpointResources, &v1.ParamKind{ APIVersion: "v1", Kind: "ConfigMap", - }, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + }, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{annotations: params.data}}`, }, }), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ - mutatingBinding("multi-param-binding", &v1beta1.ParamRef{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ + mutatingBinding("multi-param-binding", &v1.ParamRef{ // note empty namespace. all params matching request namespace // will be used Selector: &metav1.LabelSelector{ @@ -283,7 +284,7 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, }, }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -305,15 +306,15 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, { name: "policy with variables", - policies: []*v1beta1.MutatingAdmissionPolicy{ - withMutatingVariables([]v1beta1.Variable{ + policies: []*v1.MutatingAdmissionPolicy{ + withMutatingVariables([]v1.Variable{ {Name: "foo1", Expression: `"foo1" + "Value"`}, {Name: "foo2", Expression: `variables.foo1.replace("1", "2")`}, }, - mutatingPolicy("policy-with-multiple-mutations", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + mutatingPolicy("policy-with-multiple-mutations", v1.NeverReinvocationPolicy, matchEndpointResources, nil, + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -324,18 +325,18 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }`, }, }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeJSONPatch, - JSONPatch: &v1beta1.JSONPatch{ + v1.Mutation{ + PatchType: v1.PatchTypeJSONPatch, + JSONPatch: &v1.JSONPatch{ Expression: `[ JSONPatch{op: "test", path: "/metadata/annotations", value: {"foo1": variables.foo1}}, JSONPatch{op: "add", path: "/metadata/annotations/foo2", value: variables.foo2}, ]`, }, }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -348,10 +349,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, )), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("policy-with-multiple-mutations", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -373,19 +374,19 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, { name: "match condition matches", - policies: []*v1beta1.MutatingAdmissionPolicy{ - withMutatingMatchConditions([]v1beta1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}}, - mutatingPolicy("policy-match-condition", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + withMutatingMatchConditions([]v1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}}, + mutatingPolicy("policy-match-condition", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{labels: {"applied": "updated"}}}`, }, })), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("policy-match-condition", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -406,11 +407,11 @@ func TestMutatingAdmissionPolicy(t *testing.T) { // same as the multiple mutations test, but the mutations are split // across multiple policies name: "multiple policies requiring reinvocation", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("policy-1", v1beta1.IfNeededReinvocationPolicy, matchEndpointResources, nil, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("policy-1", v1.IfNeededReinvocationPolicy, matchEndpointResources, nil, + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -423,10 +424,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, }, ), - mutatingPolicy("policy-2", v1beta1.IfNeededReinvocationPolicy, matchEndpointResources, nil, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + mutatingPolicy("policy-2", v1.IfNeededReinvocationPolicy, matchEndpointResources, nil, + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -439,10 +440,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, }, ), - mutatingPolicy("policy-3", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + mutatingPolicy("policy-3", v1.NeverReinvocationPolicy, matchEndpointResources, nil, + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -456,12 +457,12 @@ func TestMutatingAdmissionPolicy(t *testing.T) { }, ), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("policy-1", nil, nil), mutatingBinding("policy-2", nil, nil), mutatingBinding("policy-3", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -495,76 +496,84 @@ func TestMutatingAdmissionPolicy(t *testing.T) { } // Run all tests in a shared apiserver - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true) - flags := []string{fmt.Sprintf("--runtime-config=%s=true", v1beta1.SchemeGroupVersion)} - server, err := apiservertesting.StartTestServer(t, nil, flags, framework.SharedEtcd()) - require.NoError(t, err) - defer server.TearDownFn() - - client, err := kubernetes.NewForConfig(server.ClientConfig) - require.NoError(t, err) - - dynClient, err := dynamic.NewForConfig(server.ClientConfig) - require.NoError(t, err) - - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - for i, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - - // Create the policies, bindings and params. - for _, param := range tc.params { - _, err = client.CoreV1().ConfigMaps(param.GetNamespace()).Create(ctx, param, metav1.CreateOptions{FieldManager: "integration-test"}) - require.NoError(t, err) + versions := []schema.GroupVersion{v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion} + for _, version := range versions { + t.Run(version.Version, func(t *testing.T) { + var flags []string + if version == v1beta1.SchemeGroupVersion { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true) + flags = []string{fmt.Sprintf("--runtime-config=%s=true", version)} } + server, err := apiservertesting.StartTestServer(t, nil, flags, framework.SharedEtcd()) + require.NoError(t, err) + defer server.TearDownFn() - for _, p := range tc.policies { - // Modify each policy to also mutate marker requests. - p = withMutatingWaitReadyConstraintAndExpression(p, fmt.Sprintf("%d-%s", i, p.Name)) - _, err = client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, p, metav1.CreateOptions{FieldManager: "integration-test"}) - require.NoError(t, err) - } + client, err := kubernetes.NewForConfig(server.ClientConfig) + require.NoError(t, err) - for _, b := range tc.bindings { - // After creating each binding, wait until a marker request is successfully mutated. - err = createAndWaitReadyMutating(ctx, t, client, b, fmt.Sprintf("%d-%s", i, b.Spec.PolicyName)) - require.NoError(t, err) - } + dynClient, err := dynamic.NewForConfig(server.ClientConfig) + require.NoError(t, err) - unstructuredRequestObj := toUnstructured(t, tc.requestObject) - unstructuredExpectedObj := toUnstructured(t, tc.expected) - wipeUncheckedFields(t, unstructuredExpectedObj) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) - defer func() { - if cleanupErr := cleanupMutatingPolicy(ctx, t, client, tc.policies, tc.bindings, tc.params); cleanupErr != nil { - t.Logf("error while cleaning up policy and its bindings: %v", cleanupErr) - } - }() + for i, tc := range cases { + t.Run(tc.name, func(t *testing.T) { - // Verify that the policy is working as expected. - // Note that we do NOT retry requests here. Once the bindings are verified as working via marker - // requests, we expect the policy to work consistently for all subsequent requests. - var resultObj runtime.Object - rsrcClient := clientForType(t, unstructuredRequestObj, tc.requestResource, dynClient) - switch tc.requestOperation { - case admissionregistrationv1.Create: - resultObj, err = rsrcClient.Create(ctx, unstructuredRequestObj, metav1.CreateOptions{ - DryRun: []string{metav1.DryRunAll}, - FieldManager: "integration-test", - }, tc.subresources...) - case admissionregistrationv1.Update: - resultObj, err = rsrcClient.Update(ctx, unstructuredRequestObj, metav1.UpdateOptions{ - DryRun: []string{metav1.DryRunAll}, - FieldManager: "integration-test", - }, tc.subresources...) - require.NoError(t, err) - default: - t.Fatalf("unsupported operation: %v", tc.requestOperation) - } - wipeUncheckedFields(t, resultObj) - if !cmp.Equal(unstructuredExpectedObj, resultObj, cmpopts.EquateEmpty()) { - t.Errorf("unexpected diff:\n%s\n", cmp.Diff(unstructuredRequestObj, resultObj, cmpopts.EquateEmpty())) + // Create the policies, bindings and params. + for _, param := range tc.params { + _, err = client.CoreV1().ConfigMaps(param.GetNamespace()).Create(ctx, param, metav1.CreateOptions{FieldManager: "integration-test"}) + require.NoError(t, err) + } + + for _, p := range tc.policies { + // Modify each policy to also mutate marker requests. + p = withMutatingWaitReadyConstraintAndExpression(p, fmt.Sprintf("%d-%s", i, p.Name)) + err = createPolicy(ctx, t, client, version, p) + require.NoError(t, err) + } + + for _, b := range tc.bindings { + // After creating each binding, wait until a marker request is successfully mutated. + err = createAndWaitReadyMutating(ctx, t, client, version, b, fmt.Sprintf("%d-%s", i, b.Spec.PolicyName)) + require.NoError(t, err) + } + + unstructuredRequestObj := toUnstructured(t, tc.requestObject) + unstructuredExpectedObj := toUnstructured(t, tc.expected) + wipeUncheckedFields(t, unstructuredExpectedObj) + + defer func() { + if cleanupErr := cleanupMutatingPolicy(ctx, t, dynClient, version, tc.policies, tc.bindings, tc.params); cleanupErr != nil { + t.Logf("error while cleaning up policy and its bindings: %v", cleanupErr) + } + }() + + // Verify that the policy is working as expected. + // Note that we do NOT retry requests here. Once the bindings are verified as working via marker + // requests, we expect the policy to work consistently for all subsequent requests. + var resultObj runtime.Object + rsrcClient := clientForType(t, unstructuredRequestObj, tc.requestResource, dynClient) + switch tc.requestOperation { + case v1.Create: + resultObj, err = rsrcClient.Create(ctx, unstructuredRequestObj, metav1.CreateOptions{ + DryRun: []string{metav1.DryRunAll}, + FieldManager: "integration-test", + }, tc.subresources...) + case v1.Update: + resultObj, err = rsrcClient.Update(ctx, unstructuredRequestObj, metav1.UpdateOptions{ + DryRun: []string{metav1.DryRunAll}, + FieldManager: "integration-test", + }, tc.subresources...) + require.NoError(t, err) + default: + t.Fatalf("unsupported operation: %v", tc.requestOperation) + } + wipeUncheckedFields(t, resultObj) + if !cmp.Equal(unstructuredExpectedObj, resultObj, cmpopts.EquateEmpty()) { + t.Errorf("unexpected diff:\n%s\n", cmp.Diff(unstructuredRequestObj, resultObj, cmpopts.EquateEmpty())) + } + }) } }) } @@ -575,13 +584,13 @@ func TestMutatingAdmissionPolicy(t *testing.T) { // a single apiserver and then uses marker requests to check that a binding is ready before testing it exactly once. // Only test cases that cannot be run in TestMutatingAdmissionPolicy should be added here. func TestMutatingAdmissionPolicy_Slow(t *testing.T) { - allow := v1beta1.AllowAction - matchEndpointResources := v1beta1.MatchResources{ - ResourceRules: []v1beta1.NamedRuleWithOperations{ + allow := v1.AllowAction + matchEndpointResources := v1.MatchResources{ + ResourceRules: []v1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{"*"}, - Rule: admissionregistrationv1.Rule{ + RuleWithOperations: v1.RuleWithOperations{ + Operations: []v1.OperationType{"*"}, + Rule: v1.Rule{ APIGroups: []string{""}, APIVersions: []string{"v1"}, Resources: []string{"endpoints"}, @@ -593,11 +602,11 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { cases := []struct { name string - policies []*v1beta1.MutatingAdmissionPolicy - bindings []*v1beta1.MutatingAdmissionPolicyBinding + policies []*v1.MutatingAdmissionPolicy + bindings []*v1.MutatingAdmissionPolicyBinding params []*corev1.ConfigMap - requestOperation admissionregistrationv1.OperationType + requestOperation v1.OperationType requestResource schema.GroupVersionResource subresources []string // Only supported for requestOperation=Update since subresources can not be created initialObject runtime.Object // For requestOperation=Update, this may be used to create the initial object state @@ -606,10 +615,10 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }{ { name: "unbound policy is no-op", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("unbound-policy", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("unbound-policy", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -621,7 +630,7 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, }), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -638,19 +647,19 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, { name: "failure policy ignore", - policies: []*v1beta1.MutatingAdmissionPolicy{ - withMutatingFailurePolicy(v1beta1.Ignore, - mutatingPolicy("policy", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + withMutatingFailurePolicy(v1.Ignore, + mutatingPolicy("policy", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{spec: Object.spec{invalidField: "invalid apply configuration"}}`, }, })), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("policy", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -667,19 +676,19 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, { name: "match condition does not match", - policies: []*v1beta1.MutatingAdmissionPolicy{ - withMutatingMatchConditions([]v1beta1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}}, - mutatingPolicy("policy-no-match-condition", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + withMutatingMatchConditions([]v1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}}, + mutatingPolicy("policy-no-match-condition", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{labels: {"applied": "updated"}}}`, }, })), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("policy-no-match-condition", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -698,27 +707,27 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, { name: "some policy conditions match", - policies: []*v1beta1.MutatingAdmissionPolicy{ - withMutatingMatchConditions([]v1beta1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("production")`}}, - mutatingPolicy("policy-1", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policies: []*v1.MutatingAdmissionPolicy{ + withMutatingMatchConditions([]v1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("production")`}}, + mutatingPolicy("policy-1", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{labels: {"applied": "wrong"}}}`, }, })), - withMutatingMatchConditions([]v1beta1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}}, - mutatingPolicy("policy-2", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + withMutatingMatchConditions([]v1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}}, + mutatingPolicy("policy-2", v1.NeverReinvocationPolicy, matchEndpointResources, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{labels: {"applied": "updated"}}}`, }, })), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("policy-1", nil, nil), mutatingBinding("policy-2", nil, nil), }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -737,13 +746,13 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, { name: "mutate status subresource", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("subresource-status", v1beta1.NeverReinvocationPolicy, v1beta1.MatchResources{ - ResourceRules: []v1beta1.NamedRuleWithOperations{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("subresource-status", v1.NeverReinvocationPolicy, v1.MatchResources{ + ResourceRules: []v1.NamedRuleWithOperations{ { - RuleWithOperations: v1beta1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{"*"}, - Rule: admissionregistrationv1.Rule{ + RuleWithOperations: v1.RuleWithOperations{ + Operations: []v1.OperationType{"*"}, + Rule: v1.Rule{ APIGroups: []string{""}, APIVersions: []string{"v1"}, Resources: []string{"namespaces/status"}, @@ -751,9 +760,9 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, }, }, - }, nil, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + }, nil, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{ status: Object.status{ conditions: [Object.status.conditions{ @@ -765,10 +774,10 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, }), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ mutatingBinding("subresource-status", nil, nil), }, - requestOperation: admissionregistrationv1.Update, + requestOperation: v1.Update, subresources: []string{"status"}, requestResource: corev1.SchemeGroupVersion.WithResource("namespaces"), initialObject: &corev1.Namespace{ @@ -807,24 +816,24 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, { name: "multiple bindings with different params", - policies: []*v1beta1.MutatingAdmissionPolicy{ - mutatingPolicy("multi-binding", v1beta1.NeverReinvocationPolicy, matchEndpointResources, &v1beta1.ParamKind{ + policies: []*v1.MutatingAdmissionPolicy{ + mutatingPolicy("multi-binding", v1.NeverReinvocationPolicy, matchEndpointResources, &v1.ParamKind{ APIVersion: "v1", Kind: "ConfigMap", - }, v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + }, v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{annotations: params.data}}`, }, }), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ - mutatingBinding("multi-binding", &v1beta1.ParamRef{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ + mutatingBinding("multi-binding", &v1.ParamRef{ Name: "multi-binding-param-1", Namespace: "default", ParameterNotFoundAction: &allow, }, nil), - mutatingBinding("multi-binding", &v1beta1.ParamRef{ + mutatingBinding("multi-binding", &v1.ParamRef{ Name: "multi-binding-param-2", Namespace: "default", ParameterNotFoundAction: &allow, @@ -851,7 +860,7 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, }, }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -875,24 +884,24 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { // Same as the other cases, but the reinvocation is caused by // multiple params bound name: "multiple params causing reinvocation", - policies: []*v1beta1.MutatingAdmissionPolicy{ + policies: []*v1.MutatingAdmissionPolicy{ mutatingPolicy( "multi-param-reinvocation", - v1beta1.IfNeededReinvocationPolicy, + v1.IfNeededReinvocationPolicy, matchEndpointResources, - &v1beta1.ParamKind{ + &v1.ParamKind{ APIVersion: "v1", Kind: "ConfigMap", }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{annotations: params.data}}`, }, }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -906,21 +915,21 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { ), mutatingPolicy( "policy-with-param-no-reinvoke", - v1beta1.NeverReinvocationPolicy, + v1.NeverReinvocationPolicy, matchEndpointResources, - &v1beta1.ParamKind{ + &v1.ParamKind{ APIVersion: "v1", Kind: "ConfigMap", }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: `Object{metadata: Object.metadata{annotations: params.data}}`, }, }, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ Expression: ` Object{ metadata: Object.metadata{ @@ -933,8 +942,8 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, ), }, - bindings: []*v1beta1.MutatingAdmissionPolicyBinding{ - mutatingBinding("multi-param-reinvocation", &v1beta1.ParamRef{ + bindings: []*v1.MutatingAdmissionPolicyBinding{ + mutatingBinding("multi-param-reinvocation", &v1.ParamRef{ // note empty namespace. all params matching request namespace // will be used Selector: &metav1.LabelSelector{ @@ -944,7 +953,7 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, ParameterNotFoundAction: &allow, }, nil), - mutatingBinding("policy-with-param-no-reinvoke", &v1beta1.ParamRef{ + mutatingBinding("policy-with-param-no-reinvoke", &v1.ParamRef{ // note empty namespace. all params matching request namespace // will be used Name: "multi-param-reinvocation-param-3", @@ -986,7 +995,7 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, }, }, - requestOperation: admissionregistrationv1.Create, + requestOperation: v1.Create, requestResource: corev1.SchemeGroupVersion.WithResource("endpoints"), requestObject: &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ @@ -1011,88 +1020,93 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { }, }, } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true) - flags := []string{fmt.Sprintf("--runtime-config=%s=true", v1beta1.SchemeGroupVersion)} - server, err := apiservertesting.StartTestServer(t, nil, flags, framework.SharedEtcd()) - require.NoError(t, err) - defer server.TearDownFn() - - client, err := kubernetes.NewForConfig(server.ClientConfig) - require.NoError(t, err) - - dynClient, err := dynamic.NewForConfig(server.ClientConfig) - require.NoError(t, err) - - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - for _, param := range tc.params { - _, err = client.CoreV1().ConfigMaps(param.GetNamespace()).Create(ctx, param, metav1.CreateOptions{FieldManager: "integration-test"}) - require.NoError(t, err) - } - - for _, p := range tc.policies { - _, err = client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, p, metav1.CreateOptions{FieldManager: "integration-test"}) - require.NoError(t, err) - } - - for _, b := range tc.bindings { - _, err = client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Create(ctx, b, metav1.CreateOptions{FieldManager: "integration-test"}) - require.NoError(t, err) - } - - if tc.initialObject != nil { - initClient := clientForType(t, tc.initialObject, tc.requestResource, dynClient) - _, err = initClient.Create(ctx, toUnstructured(t, tc.initialObject), metav1.CreateOptions{}) - require.NoError(t, err) - } - - unstructuredRequestObj := toUnstructured(t, tc.requestObject) - unstructuredExpectedObj := toUnstructured(t, tc.expected) - wipeUncheckedFields(t, unstructuredExpectedObj) - - // Dry Run the request until we get the expected mutated response - var resultObj runtime.Object - err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 5*time.Second, false, func(ctx context.Context) (done bool, err error) { - rsrcClient := clientForType(t, unstructuredRequestObj, tc.requestResource, dynClient) - switch tc.requestOperation { - case admissionregistrationv1.Create: - resultObj, err = rsrcClient.Create(ctx, unstructuredRequestObj, metav1.CreateOptions{ - DryRun: []string{metav1.DryRunAll}, - FieldManager: "integration-test", - }, tc.subresources...) - if err != nil { - t.Logf("error while waiting: %v", err) - return false, nil - } - wipeUncheckedFields(t, resultObj) - return reflect.DeepEqual(unstructuredExpectedObj, resultObj), nil - case admissionregistrationv1.Update: - resultObj, err = rsrcClient.Update(ctx, unstructuredRequestObj, metav1.UpdateOptions{ - DryRun: []string{metav1.DryRunAll}, - FieldManager: "integration-test", - }, tc.subresources...) - if err != nil { - t.Logf("error while waiting: %v", err) - return false, nil - } - wipeUncheckedFields(t, resultObj) - return reflect.DeepEqual(unstructuredExpectedObj, resultObj), nil - default: - t.Fatalf("unsupported operation: %v", tc.requestOperation) + versions := []schema.GroupVersion{v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion} + for _, version := range versions { + for _, tc := range cases { + t.Run(fmt.Sprintf("%s/%s", version.Version, tc.name), func(t *testing.T) { + var flags []string + if version == v1beta1.SchemeGroupVersion { + flags = []string{fmt.Sprintf("--runtime-config=%s=true", version)} + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true) } - return false, nil - }) + server, err := apiservertesting.StartTestServer(t, nil, flags, framework.SharedEtcd()) + require.NoError(t, err) + defer server.TearDownFn() - if errors.Is(err, context.DeadlineExceeded) { - t.Fatalf("failed to get expected result before timeout: %v", cmp.Diff(unstructuredExpectedObj, resultObj)) - } else if err != nil { - t.Fatal(err) - } - }) + client, err := kubernetes.NewForConfig(server.ClientConfig) + require.NoError(t, err) + + dynClient, err := dynamic.NewForConfig(server.ClientConfig) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + for _, param := range tc.params { + _, err = client.CoreV1().ConfigMaps(param.GetNamespace()).Create(ctx, param, metav1.CreateOptions{FieldManager: "integration-test"}) + require.NoError(t, err) + } + + for _, p := range tc.policies { + err = createPolicy(ctx, t, client, version, p) + require.NoError(t, err) + } + + for _, b := range tc.bindings { + err = createBinding(ctx, t, client, version, b) + require.NoError(t, err) + } + + if tc.initialObject != nil { + initClient := clientForType(t, tc.initialObject, tc.requestResource, dynClient) + _, err = initClient.Create(ctx, toUnstructured(t, tc.initialObject), metav1.CreateOptions{}) + require.NoError(t, err) + } + + unstructuredRequestObj := toUnstructured(t, tc.requestObject) + unstructuredExpectedObj := toUnstructured(t, tc.expected) + wipeUncheckedFields(t, unstructuredExpectedObj) + + // Dry Run the request until we get the expected mutated response + var resultObj runtime.Object + err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 5*time.Second, false, func(ctx context.Context) (done bool, err error) { + rsrcClient := clientForType(t, unstructuredRequestObj, tc.requestResource, dynClient) + switch tc.requestOperation { + case v1.Create: + resultObj, err = rsrcClient.Create(ctx, unstructuredRequestObj, metav1.CreateOptions{ + DryRun: []string{metav1.DryRunAll}, + FieldManager: "integration-test", + }, tc.subresources...) + if err != nil { + t.Logf("error while waiting: %v", err) + return false, nil + } + wipeUncheckedFields(t, resultObj) + return reflect.DeepEqual(unstructuredExpectedObj, resultObj), nil + case v1.Update: + resultObj, err = rsrcClient.Update(ctx, unstructuredRequestObj, metav1.UpdateOptions{ + DryRun: []string{metav1.DryRunAll}, + FieldManager: "integration-test", + }, tc.subresources...) + if err != nil { + t.Logf("error while waiting: %v", err) + return false, nil + } + wipeUncheckedFields(t, resultObj) + return reflect.DeepEqual(unstructuredExpectedObj, resultObj), nil + default: + t.Fatalf("unsupported operation: %v", tc.requestOperation) + } + return false, nil + }) + + if errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("failed to get expected result before timeout: %v", cmp.Diff(unstructuredExpectedObj, resultObj)) + } else if err != nil { + t.Fatal(err) + } + }) + } } } @@ -1100,133 +1114,140 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) { // CRDs are also ideal for testing version conversion since old version are not removed, so version conversion is also // tested. func Test_MutatingAdmissionPolicy_CustomResources(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true) - flags := []string{fmt.Sprintf("--runtime-config=%s=true", v1beta1.SchemeGroupVersion)} - server, err := apiservertesting.StartTestServer(t, nil, flags, framework.SharedEtcd()) - etcd.CreateTestCRDs(t, apiextensions.NewForConfigOrDie(server.ClientConfig), false, versionedCustomResourceDefinition()) - if err != nil { - t.Fatal(err) - } - defer server.TearDownFn() + versions := []schema.GroupVersion{v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion} + for _, version := range versions { + t.Run(version.Version, func(t *testing.T) { + var flags []string + if version == v1beta1.SchemeGroupVersion { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true) + flags = []string{fmt.Sprintf("--runtime-config=%s=true", version)} + } + server, err := apiservertesting.StartTestServer(t, nil, flags, framework.SharedEtcd()) + etcd.CreateTestCRDs(t, apiextensions.NewForConfigOrDie(server.ClientConfig), false, versionedCustomResourceDefinition()) + if err != nil { + t.Fatal(err) + } + defer server.TearDownFn() - config := server.ClientConfig + config := server.ClientConfig - client, err := kubernetes.NewForConfig(config) - if err != nil { - t.Fatal(err) - } + client, err := kubernetes.NewForConfig(config) + if err != nil { + t.Fatal(err) + } - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) - policy := withMutatingFailurePolicy(v1beta1.Fail, mutatingPolicy("match-by-match-policy-equivalent", v1beta1.IfNeededReinvocationPolicy, v1beta1.MatchResources{ - ResourceRules: []v1beta1.NamedRuleWithOperations{ - { - RuleWithOperations: v1beta1.RuleWithOperations{ - Operations: []v1beta1.OperationType{ - "*", + policy := withMutatingFailurePolicy(v1.Fail, mutatingPolicy("match-by-match-policy-equivalent", v1.IfNeededReinvocationPolicy, v1.MatchResources{ + ResourceRules: []v1.NamedRuleWithOperations{ + { + RuleWithOperations: v1.RuleWithOperations{ + Operations: []v1.OperationType{ + "*", + }, + Rule: v1.Rule{ + APIGroups: []string{ + "awesome.bears.com", + }, + APIVersions: []string{ + "v1", + }, + Resources: []string{ + "pandas", + }, + }, + }, }, - Rule: v1beta1.Rule{ - APIGroups: []string{ - "awesome.bears.com", - }, - APIVersions: []string{ - "v1", - }, - Resources: []string{ - "pandas", - }, + }}, + nil, + v1.Mutation{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ + Expression: `Object{ metadata: Object.metadata{ labels: {"mutated-panda": "true"} } }`, }, }, - }, - }}, - nil, - v1beta1.Mutation{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ - Expression: `Object{ metadata: Object.metadata{ labels: {"mutated-panda": "true"} } }`, - }, - }, - )) - testID := "policy-equivalent" - policy = withMutatingWaitReadyConstraintAndExpression(policy, testID) - if _, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{}); err != nil { - t.Fatal(err) - } - - policyBinding := mutatingBinding("match-by-match-policy-equivalent", nil, nil) - if err := createAndWaitReadyMutating(ctx, t, client, policyBinding, testID); err != nil { - t.Fatal(err) - } - - v1Resource := &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "awesome.bears.com" + "/" + "v1", - "kind": "Panda", - "metadata": map[string]interface{}{ - "name": "v1-bears", - }, - }, - } - - v2Resource := &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "awesome.bears.com" + "/" + "v2", - "kind": "Panda", - "metadata": map[string]interface{}{ - "name": "v2-bears", - }, - }, - } - - dynamicClient, err := dynamic.NewForConfig(config) - if err != nil { - t.Fatal(err) - } - - // Wait for CRDs to register - err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) { - createdv1, err := dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v1", Resource: "pandas"}).Create(ctx, v1Resource, metav1.CreateOptions{}) - if err != nil { - if strings.Contains(err.Error(), "Resource kind awesome.bears.com/v1, Kind=Panda not found") { - return false, nil + )) + testID := "policy-equivalent" + policy = withMutatingWaitReadyConstraintAndExpression(policy, testID) + if err := createPolicy(ctx, t, client, version, policy); err != nil { + t.Fatal(err) } - return false, nil - } - if createdv1.GetLabels()["mutated-panda"] != "true" { - t.Errorf("expected mutated-panda to be true, got %s", createdv1.GetLabels()) - } - return true, nil - }) - if err != nil { - t.Fatal(err) - } - err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) { - createdv2, err := dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v2", Resource: "pandas"}).Create(ctx, v2Resource, metav1.CreateOptions{}) - if err != nil { - if strings.Contains(err.Error(), "Resource kind awesome.bears.com/v2, Kind=Panda not found") { - return false, nil + policyBinding := mutatingBinding("match-by-match-policy-equivalent", nil, nil) + if err := createAndWaitReadyMutating(ctx, t, client, version, policyBinding, testID); err != nil { + t.Fatal(err) } - return false, nil - } - if createdv2.GetLabels()["mutated-panda"] != "true" { - t.Errorf("expected mutated-panda to be true, got %s", createdv2.GetLabels()) - } - return true, nil - }) - if err != nil { - t.Fatal(err) - } + v1Resource := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "awesome.bears.com" + "/" + "v1", + "kind": "Panda", + "metadata": map[string]interface{}{ + "name": "v1-bears", + }, + }, + } + + v2Resource := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "awesome.bears.com" + "/" + "v2", + "kind": "Panda", + "metadata": map[string]interface{}{ + "name": "v2-bears", + }, + }, + } + + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + + // Wait for CRDs to register + err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) { + createdv1, err := dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v1", Resource: "pandas"}).Create(ctx, v1Resource, metav1.CreateOptions{}) + if err != nil { + if strings.Contains(err.Error(), "Resource kind awesome.bears.com/v1, Kind=Panda not found") { + return false, nil + } + return false, nil + } + if createdv1.GetLabels()["mutated-panda"] != "true" { + t.Errorf("expected mutated-panda to be true, got %s", createdv1.GetLabels()) + } + return true, nil + }) + if err != nil { + t.Fatal(err) + } + + err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) { + createdv2, err := dynamicClient.Resource(schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v2", Resource: "pandas"}).Create(ctx, v2Resource, metav1.CreateOptions{}) + if err != nil { + if strings.Contains(err.Error(), "Resource kind awesome.bears.com/v2, Kind=Panda not found") { + return false, nil + } + return false, nil + } + if createdv2.GetLabels()["mutated-panda"] != "true" { + t.Errorf("expected mutated-panda to be true, got %s", createdv2.GetLabels()) + } + return true, nil + }) + if err != nil { + t.Fatal(err) + } + }) + } } -func mutatingPolicy(name string, reinvocationPolicy v1beta1.ReinvocationPolicyType, matchResources v1beta1.MatchResources, paramKind *v1beta1.ParamKind, mutations ...v1beta1.Mutation) *v1beta1.MutatingAdmissionPolicy { - return &v1beta1.MutatingAdmissionPolicy{ +func mutatingPolicy(name string, reinvocationPolicy v1.ReinvocationPolicyType, matchResources v1.MatchResources, paramKind *v1.ParamKind, mutations ...v1.Mutation) *v1.MutatingAdmissionPolicy { + return &v1.MutatingAdmissionPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, - Spec: v1beta1.MutatingAdmissionPolicySpec{ + Spec: v1.MutatingAdmissionPolicySpec{ MatchConstraints: &matchResources, ParamKind: paramKind, Mutations: mutations, @@ -1235,30 +1256,30 @@ func mutatingPolicy(name string, reinvocationPolicy v1beta1.ReinvocationPolicyTy } } -func withMutatingVariables(variables []v1beta1.Variable, policy *v1beta1.MutatingAdmissionPolicy) *v1beta1.MutatingAdmissionPolicy { +func withMutatingVariables(variables []v1.Variable, policy *v1.MutatingAdmissionPolicy) *v1.MutatingAdmissionPolicy { policy.Spec.Variables = variables return policy } -func withMutatingFailurePolicy(failure v1beta1.FailurePolicyType, policy *v1beta1.MutatingAdmissionPolicy) *v1beta1.MutatingAdmissionPolicy { +func withMutatingFailurePolicy(failure v1.FailurePolicyType, policy *v1.MutatingAdmissionPolicy) *v1.MutatingAdmissionPolicy { policy.Spec.FailurePolicy = &failure return policy } -func withMutatingMatchConditions(matchConditions []v1beta1.MatchCondition, policy *v1beta1.MutatingAdmissionPolicy) *v1beta1.MutatingAdmissionPolicy { +func withMutatingMatchConditions(matchConditions []v1.MatchCondition, policy *v1.MutatingAdmissionPolicy) *v1.MutatingAdmissionPolicy { policy.Spec.MatchConditions = matchConditions return policy } -func withMutatingWaitReadyConstraintAndExpression(policy *v1beta1.MutatingAdmissionPolicy, testID string) *v1beta1.MutatingAdmissionPolicy { +func withMutatingWaitReadyConstraintAndExpression(policy *v1.MutatingAdmissionPolicy, testID string) *v1.MutatingAdmissionPolicy { policy = policy.DeepCopy() - policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, v1beta1.NamedRuleWithOperations{ + policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, v1.NamedRuleWithOperations{ ResourceNames: []string{"test-marker"}, - RuleWithOperations: admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{ + RuleWithOperations: v1.RuleWithOperations{ + Operations: []v1.OperationType{ "CREATE", }, - Rule: admissionregistrationv1.Rule{ + Rule: v1.Rule{ APIGroups: []string{ "", }, @@ -1281,9 +1302,9 @@ func withMutatingWaitReadyConstraintAndExpression(policy *v1beta1.MutatingAdmiss m.ApplyConfiguration.Expression = bypass + m.ApplyConfiguration.Expression } } - policy.Spec.Mutations = append([]v1beta1.Mutation{{ - PatchType: v1beta1.PatchTypeApplyConfiguration, - ApplyConfiguration: &v1beta1.ApplyConfiguration{ + policy.Spec.Mutations = append([]v1.Mutation{{ + PatchType: v1.PatchTypeApplyConfiguration, + ApplyConfiguration: &v1.ApplyConfiguration{ // Only mutate mutation-markers. Expression: fmt.Sprintf(`object.metadata.?labels["mutation-marker"] == optional.of("%v") ? Object{ metadata: Object.metadata{ labels: {"mutated":"%v"}}}: Object{}`, testID, testID), }, @@ -1291,12 +1312,12 @@ func withMutatingWaitReadyConstraintAndExpression(policy *v1beta1.MutatingAdmiss return policy } -func mutatingBinding(policyName string, paramRef *v1beta1.ParamRef, matchResources *v1beta1.MatchResources) *v1beta1.MutatingAdmissionPolicyBinding { - return &v1beta1.MutatingAdmissionPolicyBinding{ +func mutatingBinding(policyName string, paramRef *v1.ParamRef, matchResources *v1.MatchResources) *v1.MutatingAdmissionPolicyBinding { + return &v1.MutatingAdmissionPolicyBinding{ ObjectMeta: metav1.ObjectMeta{ Name: policyName + "-binding-" + string(uuid.NewUUID()), }, - Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{ + Spec: v1.MutatingAdmissionPolicyBindingSpec{ PolicyName: policyName, ParamRef: paramRef, MatchResources: matchResources, @@ -1304,15 +1325,18 @@ func mutatingBinding(policyName string, paramRef *v1beta1.ParamRef, matchResourc } } -func createAndWaitReadyMutating(ctx context.Context, t *testing.T, client kubernetes.Interface, binding *v1beta1.MutatingAdmissionPolicyBinding, testID string) error { - return createAndWaitReadyNamespacedWithWarnHandlerMutating(ctx, t, client, binding, "default", testID) +func createAndWaitReadyMutating(ctx context.Context, t *testing.T, client kubernetes.Interface, version schema.GroupVersion, binding *v1.MutatingAdmissionPolicyBinding, testID string) error { + return createAndWaitReadyNamespacedMutating(ctx, t, client, version, binding, "default", testID) } -func createAndWaitReadyNamespacedWithWarnHandlerMutating(ctx context.Context, t *testing.T, client kubernetes.Interface, binding *v1beta1.MutatingAdmissionPolicyBinding, ns string, testID string) error { - _, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) +func createAndWaitReadyNamespacedMutating(ctx context.Context, t *testing.T, client kubernetes.Interface, version schema.GroupVersion, binding *v1.MutatingAdmissionPolicyBinding, ns string, testID string) error { + if err := createBinding(ctx, t, client, version, binding); err != nil { + return err } + return waitReadyMutating(ctx, t, client, ns, testID) +} + +func waitReadyMutating(ctx context.Context, t *testing.T, client kubernetes.Interface, ns string, testID string) error { marker := &corev1.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "test-marker", Namespace: ns, Labels: map[string]string{"mutation-marker": testID}}} if waitErr := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) { result, err := client.CoreV1().Endpoints(ns).Create(ctx, marker, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}, FieldManager: "mutation-marker-sender"}) @@ -1334,14 +1358,18 @@ func createAndWaitReadyNamespacedWithWarnHandlerMutating(ctx context.Context, t return nil } -func cleanupMutatingPolicy(ctx context.Context, t *testing.T, client kubernetes.Interface, policies []*v1beta1.MutatingAdmissionPolicy, bindings []*v1beta1.MutatingAdmissionPolicyBinding, params []*corev1.ConfigMap) error { +func cleanupMutatingPolicy(ctx context.Context, t *testing.T, client dynamic.Interface, version schema.GroupVersion, policies []*v1.MutatingAdmissionPolicy, bindings []*v1.MutatingAdmissionPolicyBinding, params []*corev1.ConfigMap) error { + policyGVR := version.WithResource("mutatingadmissionpolicies") + bindingGVR := version.WithResource("mutatingadmissionpolicybindings") + cmGVR := corev1.SchemeGroupVersion.WithResource("configmaps") + for _, policy := range policies { - if err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Delete(ctx, policy.Name, metav1.DeleteOptions{}); err != nil { + if err := client.Resource(policyGVR).Delete(ctx, policy.Name, metav1.DeleteOptions{}); err != nil { t.Fatal(err) } if waitErr := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, time.Minute, true, func(ctx context.Context) (bool, error) { - _, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{}) + _, err := client.Resource(policyGVR).Get(ctx, policy.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) { return true, nil } @@ -1352,13 +1380,13 @@ func cleanupMutatingPolicy(ctx context.Context, t *testing.T, client kubernetes. } for _, binding := range bindings { - err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Delete(ctx, binding.Name, metav1.DeleteOptions{}) + err := client.Resource(bindingGVR).Delete(ctx, binding.Name, metav1.DeleteOptions{}) if err != nil { t.Fatal(err) } if waitErr := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, time.Minute, true, func(ctx context.Context) (bool, error) { - _, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Get(ctx, binding.Name, metav1.GetOptions{}) + _, err := client.Resource(bindingGVR).Get(ctx, binding.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) { return true, nil } @@ -1370,13 +1398,13 @@ func cleanupMutatingPolicy(ctx context.Context, t *testing.T, client kubernetes. for _, param := range params { if waitErr := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, time.Minute, true, func(ctx context.Context) (bool, error) { - if err := client.CoreV1().ConfigMaps(param.GetNamespace()).Delete(ctx, param.Name, metav1.DeleteOptions{}); err != nil { + if err := client.Resource(cmGVR).Namespace(param.GetNamespace()).Delete(ctx, param.Name, metav1.DeleteOptions{}); err != nil { if apierrors.IsNotFound(err) { return true, nil } return false, nil } - _, err := client.CoreV1().ConfigMaps(param.GetNamespace()).Get(ctx, param.Name, metav1.GetOptions{}) + _, err := client.Resource(cmGVR).Namespace(param.GetNamespace()).Get(ctx, param.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) { return true, nil } @@ -1423,3 +1451,32 @@ func wipeUncheckedFields(t *testing.T, obj runtime.Object) { acc.SetCreationTimestamp(metav1.Time{}) acc.SetManagedFields(nil) } + +func convertObject[T any](t *testing.T, source interface{}) *T { + data, err := json.Marshal(source) + require.NoError(t, err) + var dest T + err = json.Unmarshal(data, &dest) + require.NoError(t, err) + return &dest +} + +func createPolicy(ctx context.Context, t *testing.T, client kubernetes.Interface, version schema.GroupVersion, policy *v1.MutatingAdmissionPolicy) error { + if version == v1beta1.SchemeGroupVersion { + v1beta1P := convertObject[v1beta1.MutatingAdmissionPolicy](t, policy) + _, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, v1beta1P, metav1.CreateOptions{FieldManager: "integration-test"}) + return err + } + _, err := client.AdmissionregistrationV1().MutatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{FieldManager: "integration-test"}) + return err +} + +func createBinding(ctx context.Context, t *testing.T, client kubernetes.Interface, version schema.GroupVersion, binding *v1.MutatingAdmissionPolicyBinding) error { + if version == v1beta1.SchemeGroupVersion { + v1beta1Binding := convertObject[v1beta1.MutatingAdmissionPolicyBinding](t, binding) + _, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Create(ctx, v1beta1Binding, metav1.CreateOptions{FieldManager: "integration-test"}) + return err + } + _, err := client.AdmissionregistrationV1().MutatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{FieldManager: "integration-test"}) + return err +}