manual change

This commit is contained in:
Cici Huang 2025-07-08 13:30:06 +00:00
parent 4f3334b7ce
commit a3ecea296c
19 changed files with 1220 additions and 370 deletions

View file

@ -171,6 +171,10 @@ func TestDefaulting(t *testing.T) {
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyBinding"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyBindingList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingAdmissionPolicy"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingAdmissionPolicyList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingAdmissionPolicyBinding"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "MutatingAdmissionPolicyBindingList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingAdmissionPolicy"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingAdmissionPolicyList"}: {},
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingAdmissionPolicyBinding"}: {},

View file

@ -127,3 +127,11 @@ func SetDefaults_ServiceReference(obj *admissionregistrationv1beta1.ServiceRefer
obj.Port = ptr.To[int32](443)
}
}
// SetDefaults_MutatingAdmissionPolicySpec sets defaults for MutatingAdmissionPolicySpec
func SetDefaults_MutatingAdmissionPolicySpec(obj *admissionregistrationv1beta1.MutatingAdmissionPolicySpec) {
if obj.FailurePolicy == nil {
policy := admissionregistrationv1beta1.Fail
obj.FailurePolicy = &policy
}
}

View file

@ -144,6 +144,7 @@ func TestDefaultAdmissionWebhook(t *testing.T) {
func TestDefaultAdmissionPolicy(t *testing.T) {
fail := v1beta1.Fail
never := v1beta1.NeverReinvocationPolicy
equivalent := v1beta1.Equivalent
allScopes := v1beta1.AllScopes
@ -216,6 +217,42 @@ func TestDefaultAdmissionPolicy(t *testing.T) {
},
},
},
{
name: "MutatingAdmissionPolicy",
original: &v1beta1.MutatingAdmissionPolicy{
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{},
ReinvocationPolicy: never,
Mutations: []v1beta1.Mutation{
{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: "fake string",
},
},
},
},
},
expected: &v1beta1.MutatingAdmissionPolicy{
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: &equivalent,
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
FailurePolicy: &fail,
ReinvocationPolicy: never,
Mutations: []v1beta1.Mutation{
{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: "fake string",
},
},
},
},
},
},
}
for _, test := range tests {

View file

@ -81,8 +81,8 @@ func NewStorageFactoryConfigEffectiveVersion(effectiveVersion basecompatibility.
// TODO (https://github.com/kubernetes/kubernetes/issues/108451): remove the override in 1.25.
// apisstorage.Resource("csistoragecapacities").WithVersion("v1beta1"),
coordination.Resource("leasecandidates").WithVersion("v1beta1"),
admissionregistration.Resource("mutatingadmissionpolicies").WithVersion("v1alpha1"),
admissionregistration.Resource("mutatingadmissionpolicybindings").WithVersion("v1alpha1"),
admissionregistration.Resource("mutatingadmissionpolicies").WithVersion("v1beta1"),
admissionregistration.Resource("mutatingadmissionpolicybindings").WithVersion("v1beta1"),
certificates.Resource("clustertrustbundles").WithVersion("v1beta1"),
storage.Resource("volumeattributesclasses").WithVersion("v1beta1"),
storagemigration.Resource("storagemigrations").WithVersion("v1alpha1"),

View file

@ -183,6 +183,25 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
storage[resource] = policyBindingStorage
}
// mutatingadmissionpolicies
if resource := "mutatingadmissionpolicies"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1beta1.SchemeGroupVersion.WithResource(resource)) {
policyStorage, err := mutatingadmissionpolicystorage.NewREST(restOptionsGetter, p.Authorizer, r)
if err != nil {
return storage, err
}
policyGetter = policyStorage
storage[resource] = policyStorage
}
// mutatingadmissionpolicybindings
if resource := "mutatingadmissionpolicybindings"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1beta1.SchemeGroupVersion.WithResource(resource)) {
mutationpolicybindingstorage, err := mutationpolicybindingstorage.NewREST(restOptionsGetter, p.Authorizer, &mutationpolicybindingstorage.DefaultPolicyGetter{Getter: policyGetter}, r)
if err != nil {
return storage, err
}
storage[resource] = mutationpolicybindingstorage
}
return storage, nil
}

View file

@ -54,6 +54,10 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&ValidatingAdmissionPolicyList{},
&ValidatingAdmissionPolicyBinding{},
&ValidatingAdmissionPolicyBindingList{},
&MutatingAdmissionPolicy{},
&MutatingAdmissionPolicyList{},
&MutatingAdmissionPolicyBinding{},
&MutatingAdmissionPolicyBindingList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View file

@ -1073,7 +1073,7 @@ type MutatingWebhook struct {
}
// ReinvocationPolicyType specifies what type of policy the admission hook uses.
type ReinvocationPolicyType string
type ReinvocationPolicyType = v1.ReinvocationPolicyType
const (
// NeverReinvocationPolicy indicates that the webhook must not be called more than once in a
@ -1197,3 +1197,332 @@ type MatchCondition struct {
// Required.
Expression string `json:"expression" protobuf:"bytes,2,opt,name=expression"`
}
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.34
// MutatingAdmissionPolicy describes the definition of an admission mutation policy that mutates the object coming into admission chain.
type MutatingAdmissionPolicy struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the MutatingAdmissionPolicy.
Spec MutatingAdmissionPolicySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.34
// MutatingAdmissionPolicyList is a list of MutatingAdmissionPolicy.
type MutatingAdmissionPolicyList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// List of ValidatingAdmissionPolicy.
Items []MutatingAdmissionPolicy `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// MutatingAdmissionPolicySpec is the specification of the desired behavior of the admission policy.
type MutatingAdmissionPolicySpec struct {
// paramKind specifies the kind of resources used to parameterize this policy.
// If absent, there are no parameters for this policy and the param CEL variable will not be provided to validation expressions.
// If paramKind refers to a non-existent kind, this policy definition is mis-configured and the FailurePolicy is applied.
// If paramKind is specified but paramRef is unset in MutatingAdmissionPolicyBinding, the params variable will be null.
// +optional
ParamKind *ParamKind `json:"paramKind,omitempty" protobuf:"bytes,1,rep,name=paramKind"`
// matchConstraints specifies what resources this policy is designed to validate.
// The MutatingAdmissionPolicy cares about a request if it matches _all_ Constraints.
// However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API
// MutatingAdmissionPolicy cannot match MutatingAdmissionPolicy and MutatingAdmissionPolicyBinding.
// The CREATE, UPDATE and CONNECT operations are allowed. The DELETE operation may not be matched.
// '*' matches CREATE, UPDATE and CONNECT.
// Required.
MatchConstraints *MatchResources `json:"matchConstraints,omitempty" protobuf:"bytes,2,rep,name=matchConstraints"`
// variables contain definitions of variables that can be used in composition of other expressions.
// Each variable is defined as a named CEL expression.
// The variables defined here will be available under `variables` in other expressions of the policy
// except matchConditions because matchConditions are evaluated before the rest of the policy.
//
// The expression of a variable can refer to other variables defined earlier in the list but not those after.
// Thus, variables must be sorted by the order of first appearance and acyclic.
// +listType=atomic
// +optional
Variables []Variable `json:"variables,omitempty" protobuf:"bytes,3,rep,name=variables"`
// mutations contain operations to perform on matching objects.
// mutations may not be empty; a minimum of one mutation is required.
// mutations are evaluated in order, and are reinvoked according to
// the reinvocationPolicy.
// The mutations of a policy are invoked for each binding of this policy
// and reinvocation of mutations occurs on a per binding basis.
//
// +listType=atomic
// +optional
Mutations []Mutation `json:"mutations,omitempty" protobuf:"bytes,4,rep,name=mutations"`
// failurePolicy defines how to handle failures for the admission policy. Failures can
// occur from CEL expression parse errors, type check errors, runtime errors and invalid
// or mis-configured policy definitions or bindings.
//
// A policy is invalid if paramKind refers to a non-existent Kind.
// A binding is invalid if paramRef.name refers to a non-existent resource.
//
// failurePolicy does not define how validations that evaluate to false are handled.
//
// Allowed values are Ignore or Fail. Defaults to Fail.
// +optional
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,5,opt,name=failurePolicy,casttype=FailurePolicyType"`
// matchConditions is a list of conditions that must be met for a request to be validated.
// Match conditions filter requests that have already been matched by the matchConstraints.
// An empty list of matchConditions matches all requests.
// There are a maximum of 64 match conditions allowed.
//
// If a parameter object is provided, it can be accessed via the `params` handle in the same
// manner as validation expressions.
//
// The exact matching logic is (in order):
// 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.
// 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.
// 3. If any matchCondition evaluates to an error (but none are FALSE):
// - If failurePolicy=Fail, reject the request
// - If failurePolicy=Ignore, the policy is skipped
//
// +patchMergeKey=name
// +patchStrategy=merge
// +listType=map
// +listMapKey=name
// +optional
MatchConditions []MatchCondition `json:"matchConditions,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,6,rep,name=matchConditions"`
// reinvocationPolicy indicates whether mutations may be called multiple times per MutatingAdmissionPolicyBinding
// as part of a single admission evaluation.
// Allowed values are "Never" and "IfNeeded".
//
// Never: These mutations will not be called more than once per binding in a single admission evaluation.
//
// IfNeeded: These mutations may be invoked more than once per binding for a single admission request and there is no guarantee of
// order with respect to other admission plugins, admission webhooks, bindings of this policy and admission policies. Mutations are only
// reinvoked when mutations change the object after this mutation is invoked.
// Required.
ReinvocationPolicy ReinvocationPolicyType `json:"reinvocationPolicy,omitempty" protobuf:"bytes,7,opt,name=reinvocationPolicy,casttype=ReinvocationPolicyType"`
}
// Mutation specifies the CEL expression which is used to apply the Mutation.
type Mutation struct {
// patchType indicates the patch strategy used.
// Allowed values are "ApplyConfiguration" and "JSONPatch".
// Required.
//
// +unionDiscriminator
PatchType PatchType `json:"patchType" protobuf:"bytes,2,opt,name=patchType,casttype=PatchType"`
// applyConfiguration defines the desired configuration values of an object.
// The configuration is applied to the admission object using
// [structured merge diff](https://github.com/kubernetes-sigs/structured-merge-diff).
// A CEL expression is used to create apply configuration.
ApplyConfiguration *ApplyConfiguration `json:"applyConfiguration,omitempty" protobuf:"bytes,3,opt,name=applyConfiguration"`
// jsonPatch defines a [JSON patch](https://jsonpatch.com/) operation to perform a mutation to the object.
// A CEL expression is used to create the JSON patch.
JSONPatch *JSONPatch `json:"jsonPatch,omitempty" protobuf:"bytes,4,opt,name=jsonPatch"`
}
// PatchType specifies the type of patch operation for a mutation.
// +enum
type PatchType string
const (
// ApplyConfiguration indicates that the mutation is using apply configuration to mutate the object.
PatchTypeApplyConfiguration PatchType = "ApplyConfiguration"
// JSONPatch indicates that the object is mutated through JSON Patch.
PatchTypeJSONPatch PatchType = "JSONPatch"
)
// ApplyConfiguration defines the desired configuration values of an object.
type ApplyConfiguration struct {
// expression will be evaluated by CEL to create an apply configuration.
// ref: https://github.com/google/cel-spec
//
// Apply configurations are declared in CEL using object initialization. For example, this CEL expression
// returns an apply configuration to set a single field:
//
// Object{
// spec: Object.spec{
// serviceAccountName: "example"
// }
// }
//
// Apply configurations may not modify atomic structs, maps or arrays due to the risk of accidental deletion of
// values not included in the apply configuration.
//
// CEL expressions have access to the object types needed to create apply configurations:
//
// - 'Object' - CEL type of the resource object.
// - 'Object.<fieldName>' - CEL type of object field (such as 'Object.spec')
// - 'Object.<fieldName1>.<fieldName2>...<fieldNameN>` - CEL type of nested field (such as 'Object.spec.containers')
//
// CEL expressions have access to the contents of the API request, organized into CEL variables as well as some other useful variables:
//
// - 'object' - The object from the incoming request. The value is null for DELETE requests.
// - 'oldObject' - The existing object. The value is null for CREATE requests.
// - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
// - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
// - 'namespaceObject' - The namespace object that the incoming object belongs to. The value is null for cluster-scoped resources.
// - 'variables' - Map of composited variables, from its name to its lazily evaluated value.
// For example, a variable named 'foo' can be accessed as 'variables.foo'.
// - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
// - 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
// request resource.
//
// The `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the
// object. No other metadata properties are accessible.
//
// Only property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.
// Required.
Expression string `json:"expression,omitempty" protobuf:"bytes,1,opt,name=expression"`
}
// JSONPatch defines a JSON Patch.
type JSONPatch struct {
// expression will be evaluated by CEL to create a [JSON patch](https://jsonpatch.com/).
// ref: https://github.com/google/cel-spec
//
// expression must return an array of JSONPatch values.
//
// For example, this CEL expression returns a JSON patch to conditionally modify a value:
//
// [
// JSONPatch{op: "test", path: "/spec/example", value: "Red"},
// JSONPatch{op: "replace", path: "/spec/example", value: "Green"}
// ]
//
// To define an object for the patch value, use Object types. For example:
//
// [
// JSONPatch{
// op: "add",
// path: "/spec/selector",
// value: Object.spec.selector{matchLabels: {"environment": "test"}}
// }
// ]
//
// To use strings containing '/' and '~' as JSONPatch path keys, use "jsonpatch.escapeKey". For example:
//
// [
// JSONPatch{
// op: "add",
// path: "/metadata/labels/" + jsonpatch.escapeKey("example.com/environment"),
// value: "test"
// },
// ]
//
// CEL expressions have access to the types needed to create JSON patches and objects:
//
// - 'JSONPatch' - CEL type of JSON Patch operations. JSONPatch has the fields 'op', 'from', 'path' and 'value'.
// See [JSON patch](https://jsonpatch.com/) for more details. The 'value' field may be set to any of: string,
// integer, array, map or object. If set, the 'path' and 'from' fields must be set to a
// [JSON pointer](https://datatracker.ietf.org/doc/html/rfc6901/) string, where the 'jsonpatch.escapeKey()' CEL
// function may be used to escape path keys containing '/' and '~'.
// - 'Object' - CEL type of the resource object.
// - 'Object.<fieldName>' - CEL type of object field (such as 'Object.spec')
// - 'Object.<fieldName1>.<fieldName2>...<fieldNameN>` - CEL type of nested field (such as 'Object.spec.containers')
//
// CEL expressions have access to the contents of the API request, organized into CEL variables as well as some other useful variables:
//
// - 'object' - The object from the incoming request. The value is null for DELETE requests.
// - 'oldObject' - The existing object. The value is null for CREATE requests.
// - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
// - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
// - 'namespaceObject' - The namespace object that the incoming object belongs to. The value is null for cluster-scoped resources.
// - 'variables' - Map of composited variables, from its name to its lazily evaluated value.
// For example, a variable named 'foo' can be accessed as 'variables.foo'.
// - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
// - 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
// request resource.
//
// CEL expressions have access to [Kubernetes CEL function libraries](https://kubernetes.io/docs/reference/using-api/cel/#cel-options-language-features-and-libraries)
// as well as:
//
// - 'jsonpatch.escapeKey' - Performs JSONPatch key escaping. '~' and '/' are escaped as '~0' and `~1' respectively).
//
//
// Only property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.
// Required.
Expression string `json:"expression,omitempty" protobuf:"bytes,1,opt,name=expression"`
}
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.34
// MutatingAdmissionPolicyBinding binds the MutatingAdmissionPolicy with parametrized resources.
// MutatingAdmissionPolicyBinding and the optional parameter resource together define how cluster administrators
// configure policies for clusters.
//
// For a given admission request, each binding will cause its policy to be
// evaluated N times, where N is 1 for policies/bindings that don't use
// params, otherwise N is the number of parameters selected by the binding.
// Each evaluation is constrained by a [runtime cost budget](https://kubernetes.io/docs/reference/using-api/cel/#runtime-cost-budget).
//
// Adding/removing policies, bindings, or params can not affect whether a
// given (policy, binding, param) combination is within its own CEL budget.
type MutatingAdmissionPolicyBinding struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired behavior of the MutatingAdmissionPolicyBinding.
Spec MutatingAdmissionPolicyBindingSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.34
// MutatingAdmissionPolicyBindingList is a list of MutatingAdmissionPolicyBinding.
type MutatingAdmissionPolicyBindingList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// List of PolicyBinding.
Items []MutatingAdmissionPolicyBinding `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// MutatingAdmissionPolicyBindingSpec is the specification of the MutatingAdmissionPolicyBinding.
type MutatingAdmissionPolicyBindingSpec struct {
// policyName references a MutatingAdmissionPolicy name which the MutatingAdmissionPolicyBinding binds to.
// If the referenced resource does not exist, this binding is considered invalid and will be ignored
// Required.
PolicyName string `json:"policyName,omitempty" protobuf:"bytes,1,rep,name=policyName"`
// paramRef specifies the parameter resource used to configure the admission control policy.
// It should point to a resource of the type specified in spec.ParamKind of the bound MutatingAdmissionPolicy.
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the MutatingAdmissionPolicy applied.
// If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.
// +optional
ParamRef *ParamRef `json:"paramRef,omitempty" protobuf:"bytes,2,rep,name=paramRef"`
// matchResources limits what resources match this binding and may be mutated by it.
// Note that if matchResources matches a resource, the resource must also match a policy's matchConstraints and
// matchConditions before the resource may be mutated.
// When matchResources is unset, it does not constrain resource matching, and only the policy's matchConstraints
// and matchConditions must match for the resource to be mutated.
// Additionally, matchResources.resourceRules are optional and do not constraint matching when unset.
// Note that this is differs from MutatingAdmissionPolicy matchConstraints, where resourceRules are required.
// The CREATE, UPDATE and CONNECT operations are allowed. The DELETE operation may not be matched.
// '*' matches CREATE, UPDATE and CONNECT.
// +optional
MatchResources *MatchResources `json:"matchResources,omitempty" protobuf:"bytes,3,rep,name=matchResources"`
}

View file

@ -18,7 +18,7 @@ package mutating
import (
v1 "k8s.io/api/admissionregistration/v1"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
)
@ -66,7 +66,7 @@ func (v *mutatingAdmissionPolicyAccessor) GetFailurePolicy() *v1.FailurePolicyTy
return toV1FailurePolicy(v.Spec.FailurePolicy)
}
func toV1FailurePolicy(failurePolicy *v1alpha1.FailurePolicyType) *v1.FailurePolicyType {
func toV1FailurePolicy(failurePolicy *v1beta1.FailurePolicyType) *v1.FailurePolicyType {
if failurePolicy == nil {
return nil
}
@ -116,7 +116,7 @@ func (v *mutatingAdmissionPolicyBindingAccessor) GetParamRef() *v1.ParamRef {
}
}
func convertV1alpha1ResourceRulesToV1(mc *v1alpha1.MatchResources) *v1.MatchResources {
func convertV1alpha1ResourceRulesToV1(mc *v1beta1.MatchResources) *v1.MatchResources {
if mc == nil {
return nil
}

View file

@ -19,7 +19,7 @@ package mutating
import (
"fmt"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/admissionregistration/v1beta1"
plugincel "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
@ -62,13 +62,13 @@ func compilePolicy(policy *Policy) PolicyEvaluator {
patchOptions.HasPatchTypes = true
for _, m := range policy.Spec.Mutations {
switch m.PatchType {
case v1alpha1.PatchTypeJSONPatch:
case v1beta1.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 v1alpha1.PatchTypeApplyConfiguration:
case v1beta1.PatchTypeApplyConfiguration:
if m.ApplyConfiguration != nil {
accessor := &patch.ApplyConfigurationCondition{Expression: m.ApplyConfiguration.Expression}
compileResult := compiler.CompileMutatingEvaluator(accessor, patchOptions, environment.StoredExpressions)

View file

@ -23,7 +23,7 @@ import (
"testing"
"time"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/admissionregistration/v1beta1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
@ -60,9 +60,9 @@ func TestCompilation(t *testing.T) {
}{
{
name: "applyConfiguration then jsonPatch",
policy: mutations(policy("d1"), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
policy: mutations(policy("d1"), v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + 100
@ -70,9 +70,9 @@ func TestCompilation(t *testing.T) {
}`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeJSONPatch,
JSONPatch: &v1alpha1.JSONPatch{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeJSONPatch,
JSONPatch: &v1beta1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas", value: object.spec.replicas + 10}
]`,
@ -85,17 +85,17 @@ func TestCompilation(t *testing.T) {
{
name: "jsonPatch then applyConfiguration",
policy: mutations(policy("d1"),
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeJSONPatch,
JSONPatch: &v1alpha1.JSONPatch{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeJSONPatch,
JSONPatch: &v1beta1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas", value: object.spec.replicas + 10}
]`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + 100
@ -109,7 +109,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "jsonPatch with variable",
policy: jsonPatches(variables(policy("d1"), v1alpha1.Variable{Name: "desired", Expression: "10"}), v1alpha1.JSONPatch{
policy: jsonPatches(variables(policy("d1"), v1beta1.Variable{Name: "desired", Expression: "10"}), v1beta1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas", value: variables.desired + 1},
]`,
@ -120,7 +120,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "apply configuration with variable",
policy: applyConfigurations(variables(policy("d1"), v1alpha1.Variable{Name: "desired", Expression: "10"}),
policy: applyConfigurations(variables(policy("d1"), v1beta1.Variable{Name: "desired", Expression: "10"}),
`Object{
spec: Object.spec{
replicas: variables.desired + 1
@ -137,7 +137,7 @@ func TestCompilation(t *testing.T) {
spec: Object.spec{
replicas: int(params.data['k1'])
}
}`), &v1alpha1.ParamKind{Kind: "ConfigMap", APIVersion: "v1"}),
}`), &v1beta1.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)}},
@ -145,7 +145,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "jsonPatch with excessive cost",
policy: jsonPatches(variables(policy("d1"), v1alpha1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}), v1alpha1.JSONPatch{
policy: jsonPatches(variables(policy("d1"), v1beta1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}), v1beta1.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
@ -163,14 +163,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
}
}`), v1alpha1.Variable{Name: "list", Expression: "[0,1,2,3,4,5,6,7,8,9]"}),
}`), v1beta1.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"), v1alpha1.JSONPatch{
policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas",
value: request.kind.group == 'apps' && request.kind.version == 'v1' && request.kind.kind == 'Deployment' ? 10 : 0
@ -182,7 +182,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "namespace request variable",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas",
value: namespaceObject.metadata.name == 'ns1' ? 10 : 0
@ -195,7 +195,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "authorizer check",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{
Expression: `[
JSONPatch{op: "replace", path: "/spec/replicas",
value: authorizer.group('').resource('endpoints').check('create').allowed() ? 10 : 0
@ -208,7 +208,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "object type has field access",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
@ -226,7 +226,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "object type has field testing",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
@ -246,7 +246,7 @@ func TestCompilation(t *testing.T) {
},
{
name: "object type equality",
policy: jsonPatches(policy("d1"), v1alpha1.JSONPatch{
policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/metadata/labels",
@ -274,7 +274,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"), v1alpha1.JSONPatch{
policy: jsonPatches(policy("d1"), v1beta1.JSONPatch{
Expression: `[
JSONPatch{
op: "add", path: "/spec",
@ -400,52 +400,52 @@ func TestCompilation(t *testing.T) {
}
}
func policy(name string) *v1alpha1.MutatingAdmissionPolicy {
return &v1alpha1.MutatingAdmissionPolicy{
func policy(name string) *v1beta1.MutatingAdmissionPolicy {
return &v1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{},
Spec: v1beta1.MutatingAdmissionPolicySpec{},
}
}
func variables(policy *v1alpha1.MutatingAdmissionPolicy, variables ...v1alpha1.Variable) *v1alpha1.MutatingAdmissionPolicy {
func variables(policy *v1beta1.MutatingAdmissionPolicy, variables ...v1beta1.Variable) *v1beta1.MutatingAdmissionPolicy {
policy.Spec.Variables = append(policy.Spec.Variables, variables...)
return policy
}
func jsonPatches(policy *v1alpha1.MutatingAdmissionPolicy, jsonPatches ...v1alpha1.JSONPatch) *v1alpha1.MutatingAdmissionPolicy {
func jsonPatches(policy *v1beta1.MutatingAdmissionPolicy, jsonPatches ...v1beta1.JSONPatch) *v1beta1.MutatingAdmissionPolicy {
for _, jsonPatch := range jsonPatches {
policy.Spec.Mutations = append(policy.Spec.Mutations, v1alpha1.Mutation{
policy.Spec.Mutations = append(policy.Spec.Mutations, v1beta1.Mutation{
JSONPatch: &jsonPatch,
PatchType: v1alpha1.PatchTypeJSONPatch,
PatchType: v1beta1.PatchTypeJSONPatch,
})
}
return policy
}
func applyConfigurations(policy *v1alpha1.MutatingAdmissionPolicy, expressions ...string) *v1alpha1.MutatingAdmissionPolicy {
func applyConfigurations(policy *v1beta1.MutatingAdmissionPolicy, expressions ...string) *v1beta1.MutatingAdmissionPolicy {
for _, expression := range expressions {
policy.Spec.Mutations = append(policy.Spec.Mutations, v1alpha1.Mutation{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{Expression: expression},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
policy.Spec.Mutations = append(policy.Spec.Mutations, v1beta1.Mutation{
ApplyConfiguration: &v1beta1.ApplyConfiguration{Expression: expression},
PatchType: v1beta1.PatchTypeApplyConfiguration,
})
}
return policy
}
func paramKind(policy *v1alpha1.MutatingAdmissionPolicy, paramKind *v1alpha1.ParamKind) *v1alpha1.MutatingAdmissionPolicy {
func paramKind(policy *v1beta1.MutatingAdmissionPolicy, paramKind *v1beta1.ParamKind) *v1beta1.MutatingAdmissionPolicy {
policy.Spec.ParamKind = paramKind
return policy
}
func mutations(policy *v1alpha1.MutatingAdmissionPolicy, mutations ...v1alpha1.Mutation) *v1alpha1.MutatingAdmissionPolicy {
func mutations(policy *v1beta1.MutatingAdmissionPolicy, mutations ...v1beta1.Mutation) *v1beta1.MutatingAdmissionPolicy {
policy.Spec.Mutations = append(policy.Spec.Mutations, mutations...)
return policy
}
func matchConstraints(policy *v1alpha1.MutatingAdmissionPolicy, matchConstraints *v1alpha1.MatchResources) *v1alpha1.MutatingAdmissionPolicy {
func matchConstraints(policy *v1beta1.MutatingAdmissionPolicy, matchConstraints *v1beta1.MatchResources) *v1beta1.MutatingAdmissionPolicy {
policy.Spec.MatchConstraints = matchConstraints
return policy
}

View file

@ -22,7 +22,7 @@ import (
"fmt"
"time"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/admissionregistration/v1beta1"
v1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
@ -206,7 +206,7 @@ func (d *dispatcher) dispatchInvocations(
policyReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins()
reinvokeCtx.SetShouldReinvoke()
}
if invocation.Policy.Spec.ReinvocationPolicy == v1alpha1.IfNeededReinvocationPolicy {
if invocation.Policy.Spec.ReinvocationPolicy == v1beta1.IfNeededReinvocationPolicy {
policyReinvokeCtx.AddReinvocablePolicyToPreviouslyInvoked(invocationKey)
}
}

View file

@ -23,7 +23,7 @@ import (
"time"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/admissionregistration/v1beta1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
@ -77,14 +77,14 @@ func TestDispatcher(t *testing.T) {
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: mutations(matchConstraints(policy("policy1"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Policy: mutations(matchConstraints(policy("policy1"), &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -93,9 +93,9 @@ func TestDispatcher(t *testing.T) {
},
},
},
}), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
}), v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + 100
@ -104,7 +104,7 @@ func TestDispatcher(t *testing.T) {
}}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
@ -168,14 +168,14 @@ func TestDispatcher(t *testing.T) {
},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: paramKind(mutations(matchConstraints(policy("policy1"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Policy: paramKind(mutations(matchConstraints(policy("policy1"), &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -184,24 +184,24 @@ func TestDispatcher(t *testing.T) {
},
},
}}),
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
spec: Object.spec{
replicas: object.spec.replicas + int(params.data['key'])
}
}`,
}}),
&v1alpha1.ParamKind{
&v1beta1.ParamKind{
APIVersion: "v1",
Kind: "ConfigMap",
}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
ParamRef: &v1alpha1.ParamRef{Name: "cm1", Namespace: "default"},
ParamRef: &v1beta1.ParamRef{Name: "cm1", Namespace: "default"},
},
}},
},
@ -248,14 +248,14 @@ func TestDispatcher(t *testing.T) {
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: mutations(matchConstraints(policy("policy1"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Policy: mutations(matchConstraints(policy("policy1"), &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -264,9 +264,9 @@ func TestDispatcher(t *testing.T) {
},
},
},
}), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
}), v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy1": string(int(object.?metadata.labels["count"].orValue("1")) + 1)}
@ -275,20 +275,20 @@ func TestDispatcher(t *testing.T) {
}}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
},
{
Policy: mutations(matchConstraints(policy("policy2"), &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Policy: mutations(matchConstraints(policy("policy2"), &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -297,9 +297,9 @@ func TestDispatcher(t *testing.T) {
},
},
},
}), v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
}), v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy2": string(int(object.?metadata.labels["count"].orValue("1")) + 1)}
@ -308,7 +308,7 @@ func TestDispatcher(t *testing.T) {
}}),
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy2",
},
}},
@ -359,19 +359,19 @@ func TestDispatcher(t *testing.T) {
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
Policy: &v1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy1",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -381,9 +381,9 @@ func TestDispatcher(t *testing.T) {
},
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Mutations: []v1beta1.Mutation{{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"environment": "production"}
@ -394,25 +394,25 @@ func TestDispatcher(t *testing.T) {
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
},
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
Policy: &v1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy2",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -422,15 +422,15 @@ func TestDispatcher(t *testing.T) {
},
},
},
MatchConditions: []v1alpha1.MatchCondition{
MatchConditions: []v1beta1.MatchCondition{
{
Name: "prodonly",
Expression: `object.?metadata.labels["environment"].orValue("") == "production"`,
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Mutations: []v1beta1.Mutation{{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy1invoked": "true"}
@ -441,7 +441,7 @@ func TestDispatcher(t *testing.T) {
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy2",
},
}},
@ -493,19 +493,19 @@ func TestDispatcher(t *testing.T) {
}},
policyHooks: []generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]{
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
Policy: &v1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy1",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -515,15 +515,15 @@ func TestDispatcher(t *testing.T) {
},
},
},
MatchConditions: []v1alpha1.MatchCondition{
MatchConditions: []v1beta1.MatchCondition{
{
Name: "prodonly",
Expression: `object.?metadata.labels["environment"].orValue("") == "production"`,
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Mutations: []v1beta1.Mutation{{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"policy1invoked": "true"}
@ -534,25 +534,25 @@ func TestDispatcher(t *testing.T) {
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy1",
},
}},
},
{
Policy: &v1alpha1.MutatingAdmissionPolicy{
Policy: &v1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "policy2",
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
ResourceRules: []v1alpha1.NamedRuleWithOperations{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Rule: v1alpha1.Rule{
RuleWithOperations: v1beta1.RuleWithOperations{
Rule: v1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -562,9 +562,9 @@ func TestDispatcher(t *testing.T) {
},
},
},
Mutations: []v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
Mutations: []v1beta1.Mutation{{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
metadata: Object.metadata{
labels: {"environment": "production"}
@ -575,7 +575,7 @@ func TestDispatcher(t *testing.T) {
},
Bindings: []*PolicyBinding{{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy2",
},
}},

View file

@ -21,7 +21,7 @@ import (
celgo "github.com/google/cel-go/cel"
"io"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/admissionregistration/v1beta1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
@ -53,9 +53,9 @@ func Register(plugins *admission.Plugins) {
})
}
type Policy = v1alpha1.MutatingAdmissionPolicy
type PolicyBinding = v1alpha1.MutatingAdmissionPolicyBinding
type PolicyMutation = v1alpha1.Mutation
type Policy = v1beta1.MutatingAdmissionPolicy
type PolicyBinding = v1beta1.MutatingAdmissionPolicyBinding
type PolicyMutation = v1beta1.Mutation
type PolicyHook = generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]
type Mutator struct {
@ -96,8 +96,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().V1alpha1().MutatingAdmissionPolicies().Informer(),
f.Admissionregistration().V1alpha1().MutatingAdmissionPolicyBindings().Informer(),
f.Admissionregistration().V1beta1().MutatingAdmissionPolicies().Informer(),
f.Admissionregistration().V1beta1().MutatingAdmissionPolicyBindings().Informer(),
NewMutatingAdmissionPolicyAccessor,
NewMutatingAdmissionPolicyBindingAccessor,
compilePolicy,
@ -142,7 +142,7 @@ func (v *Variable) GetName() string {
return v.Name
}
func convertv1alpha1Variables(variables []v1alpha1.Variable) []cel.NamedExpressionAccessor {
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}

View file

@ -22,7 +22,7 @@ import (
"github.com/stretchr/testify/require"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/admissionregistration/v1beta1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -88,25 +88,25 @@ func TestBasicPatch(t *testing.T) {
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
Mutations: []v1beta1.Mutation{
{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
PatchType: v1beta1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},
@ -145,25 +145,25 @@ func TestJSONPatch(t *testing.T) {
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
Mutations: []v1beta1.Mutation{
{
JSONPatch: &v1alpha1.JSONPatch{
JSONPatch: &v1beta1.JSONPatch{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
PatchType: v1beta1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},
@ -207,25 +207,25 @@ func TestSSAPatch(t *testing.T) {
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
Mutations: []v1beta1.Mutation{
{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
PatchType: v1beta1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},
@ -274,25 +274,25 @@ func TestSSAMapList(t *testing.T) {
require.NoError(t, testContext.UpdateAndWait(
&mutating.Policy{
ObjectMeta: metav1.ObjectMeta{Name: "policy"},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1alpha1.MatchResources{
MatchPolicy: ptr.To(v1alpha1.Equivalent),
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &v1beta1.MatchResources{
MatchPolicy: ptr.To(v1beta1.Equivalent),
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
},
Mutations: []v1alpha1.Mutation{
Mutations: []v1beta1.Mutation{
{
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: "ignored, but required",
},
PatchType: v1alpha1.PatchTypeApplyConfiguration,
PatchType: v1beta1.PatchTypeApplyConfiguration,
},
},
},
},
&mutating.PolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: "binding"},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "policy",
},
},

View file

@ -19,6 +19,7 @@ package apimachinery
import (
"context"
"fmt"
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/apiserver/pkg/features"
"k8s.io/kubernetes/test/e2e/feature"
"k8s.io/utils/ptr"
@ -28,7 +29,7 @@ import (
"github.com/onsi/gomega"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -71,17 +72,17 @@ var _ = SIGDescribe("MutatingAdmissionPolicy [Privileged:ClusterAdmin]", feature
*/
framework.It("should mutate a Deployment", func(ctx context.Context) {
ginkgo.By("creating the policy", func() {
policy := &admissionregistrationv1alpha1.MutatingAdmissionPolicy{
policy := &admissionregistrationv1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: f.UniqueName + ".policy.example.com",
},
Spec: admissionregistrationv1alpha1.MutatingAdmissionPolicySpec{
MatchConstraints: &admissionregistrationv1alpha1.MatchResources{
ResourceRules: []admissionregistrationv1alpha1.NamedRuleWithOperations{
Spec: admissionregistrationv1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &admissionregistrationv1beta1.MatchResources{
ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistrationv1alpha1.RuleWithOperations{
Operations: []admissionregistrationv1alpha1.OperationType{"CREATE", "UPDATE"},
Rule: admissionregistrationv1alpha1.Rule{
RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
Operations: []admissionregistrationv1beta1.OperationType{"CREATE", "UPDATE"},
Rule: admissionregistrationv1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
@ -95,10 +96,10 @@ var _ = SIGDescribe("MutatingAdmissionPolicy [Privileged:ClusterAdmin]", feature
},
},
},
Mutations: []admissionregistrationv1alpha1.Mutation{
Mutations: []admissionregistrationv1beta1.Mutation{
{
PatchType: admissionregistrationv1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &admissionregistrationv1alpha1.ApplyConfiguration{
PatchType: admissionregistrationv1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &admissionregistrationv1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -110,21 +111,21 @@ var _ = SIGDescribe("MutatingAdmissionPolicy [Privileged:ClusterAdmin]", feature
},
},
},
ReinvocationPolicy: admissionregistrationv1alpha1.NeverReinvocationPolicy,
ReinvocationPolicy: admissionregistrationv1beta1.NeverReinvocationPolicy,
},
}
policy, err := client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
policy, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
framework.ExpectNoError(err, "create policy")
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
return client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
return client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
}, policy.Name)
binding := createMAPBinding(f.UniqueName+".binding.example.com", f.UniqueName, policy.Name)
binding, err = client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
binding, err = client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
framework.ExpectNoError(err, "create binding")
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
return client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
return client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
}, binding.Name)
// Test the mutation
@ -537,7 +538,7 @@ var _ = SIGDescribe("MutatingAdmissionPolicy [Privileged:ClusterAdmin]", feature
gomega.Expect(evt.Type).To(gomega.Equal(watch.Modified))
vapbWatched, isFS := evt.Object.(*admissionregistrationv1alpha1.MutatingAdmissionPolicyBinding)
if !isFS {
framework.Failf("expected an object of type: %T, but got %T", &admissionregistrationv1alpha1.MutatingAdmissionPolicyBinding{}, evt.Object)
framework.Failf("expected an object of type: %T, but got %T", &admissionregistrationv1beta1.MutatingAdmissionPolicyBinding{}, evt.Object)
}
if vapbWatched.Annotations["patched"] == "true" {
sawAnnotation = true
@ -588,14 +589,436 @@ var _ = SIGDescribe("MutatingAdmissionPolicy [Privileged:ClusterAdmin]", feature
framework.ExpectNoError(err)
gomega.Expect(itemsColWithoutFinalizer).To(gomega.BeEmpty(), "filtered list should have 0 items")
})
/*
Release: v1.34
Testname: MutatingAdmissionPolicy API
Description:
The admissionregistration.k8s.io API group MUST exist in the
/apis discovery document.
The admissionregistration.k8s.io/v1beta11 API group/version MUST exist
in the /apis/admissionregistration.k8s.io discovery document.
The MutatingAdmissionPolicy MUST exist in the
/apis/admissionregistration.k8s.io/v1beta1 discovery document.
The mutatingadmisionpolicy resource must support create, get,
list, watch, update, patch, delete, and deletecollection.
*/
framework.It("should support MutatingAdmissionPolicy API operations", func(ctx context.Context) {
mapVersion := "v1beta1"
ginkgo.By("getting /apis")
{
discoveryGroups, err := f.ClientSet.Discovery().ServerGroups()
framework.ExpectNoError(err)
found := false
for _, group := range discoveryGroups.Groups {
if group.Name == admissionregistrationv1.GroupName {
for _, version := range group.Versions {
if version.Version == mapVersion {
found = true
break
}
}
}
}
if !found {
framework.Failf("expected MutatingAdmissionPolicy API group/version, got %#v", discoveryGroups.Groups)
}
}
ginkgo.By("getting /apis/admissionregistration.k8s.io")
{
group := &metav1.APIGroup{}
err := f.ClientSet.Discovery().RESTClient().Get().AbsPath("/apis/admissionregistration.k8s.io").Do(ctx).Into(group)
framework.ExpectNoError(err)
found := false
for _, version := range group.Versions {
if version.Version == mapVersion {
found = true
break
}
}
if !found {
framework.Failf("expected MutatingAdmissionPolicy API version, got %#v", group.Versions)
}
}
ginkgo.By("getting /apis/admissionregistration.k8s.io/" + mapVersion)
{
resources, err := f.ClientSet.Discovery().ServerResourcesForGroupVersion(admissionregistrationv1beta1.SchemeGroupVersion.String())
framework.ExpectNoError(err)
foundVAP := false
for _, resource := range resources.APIResources {
switch resource.Name {
case "mutatingadmissionpolicies":
foundVAP = true
}
}
if !foundVAP {
framework.Failf("expected mutatingadmissionpolicies, got %#v", resources.APIResources)
}
}
client := f.ClientSet.AdmissionregistrationV1beta1().MutatingAdmissionPolicies()
labelKey, labelValue := "example-e2e-map-label", utilrand.String(8)
label := fmt.Sprintf("%s=%s", labelKey, labelValue)
template := &admissionregistrationv1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "e2e-example-map-",
Labels: map[string]string{
labelKey: labelValue,
},
},
Spec: admissionregistrationv1beta1.MutatingAdmissionPolicySpec{
ReinvocationPolicy: admissionregistrationv1beta1.NeverReinvocationPolicy,
Mutations: []admissionregistrationv1beta1.Mutation{
{
PatchType: admissionregistrationv1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &admissionregistrationv1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
annotations: {
"my-foo-annotation": "myAnnotationValue"
}
}
}`,
},
},
},
MatchConstraints: &admissionregistrationv1beta1.MatchResources{
ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
{
RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
Operations: []admissionregistrationv1beta1.OperationType{"CREATE"},
Rule: admissionregistrationv1beta1.Rule{
APIGroups: []string{"apps"},
APIVersions: []string{"v1"},
Resources: []string{"deployments"},
},
},
},
},
},
},
}
ginkgo.DeferCleanup(func(ctx context.Context) {
err := client.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: label})
framework.ExpectNoError(err)
})
ginkgo.By("creating")
_, err := client.Create(ctx, template, metav1.CreateOptions{})
framework.ExpectNoError(err)
_, err = client.Create(ctx, template, metav1.CreateOptions{})
framework.ExpectNoError(err)
mapCreated, err := client.Create(ctx, template, metav1.CreateOptions{})
framework.ExpectNoError(err)
ginkgo.By("getting")
mapRead, err := client.Get(ctx, mapCreated.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
gomega.Expect(mapRead.UID).To(gomega.Equal(mapCreated.UID))
ginkgo.By("listing")
list, err := client.List(ctx, metav1.ListOptions{LabelSelector: label})
framework.ExpectNoError(err)
ginkgo.By("watching")
framework.Logf("starting watch")
mapWatch, err := client.Watch(ctx, metav1.ListOptions{ResourceVersion: list.ResourceVersion, LabelSelector: label})
framework.ExpectNoError(err)
ginkgo.By("patching")
patchBytes := []byte(`{"metadata":{"annotations":{"patched":"true"}},"spec":{"failurePolicy":"Ignore"}}`)
mapPatched, err := client.Patch(ctx, mapCreated.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{})
framework.ExpectNoError(err)
gomega.Expect(mapPatched.Annotations).To(gomega.HaveKeyWithValue("patched", "true"), "patched object should have the applied annotation")
gomega.Expect(mapPatched.Spec.FailurePolicy).To(gomega.HaveValue(gomega.Equal(admissionregistrationv1beta1.Ignore)), "patched object should have the applied spec")
ginkgo.By("updating")
var mapUpdated *admissionregistrationv1beta1.MutatingAdmissionPolicy
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
mpolicy, err := client.Get(ctx, mapCreated.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
mapToUpdate := mpolicy.DeepCopy()
mapToUpdate.Annotations["updated"] = "true"
fail := admissionregistrationv1beta1.Fail
mapToUpdate.Spec.FailurePolicy = &fail
mapUpdated, err = client.Update(ctx, mapToUpdate, metav1.UpdateOptions{})
return err
})
framework.ExpectNoError(err, "failed to update mutatingadmissionpolicy %q", mapCreated.Name)
gomega.Expect(mapUpdated.Annotations).To(gomega.HaveKeyWithValue("updated", "true"), "updated object should have the applied annotation")
gomega.Expect(mapUpdated.Spec.FailurePolicy).To(gomega.HaveValue(gomega.Equal(admissionregistrationv1beta1.Fail)), "updated object should have the applied spec")
framework.Logf("waiting for watch events with expected annotations")
for sawAnnotation := false; !sawAnnotation; {
select {
case evt, ok := <-mapWatch.ResultChan():
if !ok {
framework.Fail("watch channel should not close")
}
gomega.Expect(evt.Type).To(gomega.Equal(watch.Modified))
mapWatched, isFS := evt.Object.(*admissionregistrationv1beta1.MutatingAdmissionPolicy)
if !isFS {
framework.Failf("expected an object of type: %T, but got %T", &admissionregistrationv1beta1.MutatingAdmissionPolicy{}, evt.Object)
}
if mapWatched.Annotations["patched"] == "true" {
sawAnnotation = true
mapWatch.Stop()
} else {
framework.Logf("missing expected annotations, waiting: %#v", mapWatched.Annotations)
}
case <-time.After(wait.ForeverTestTimeout):
framework.Fail("timed out waiting for watch event")
}
}
ginkgo.By("deleting")
err = client.Delete(ctx, mapCreated.Name, metav1.DeleteOptions{})
framework.ExpectNoError(err)
vapTmp, err := client.Get(ctx, mapCreated.Name, metav1.GetOptions{})
switch {
case err == nil && vapTmp.GetDeletionTimestamp() != nil && len(vapTmp.GetFinalizers()) > 0:
// deletion requested successfully, object is blocked by finalizers
case err == nil:
framework.Failf("expected deleted object, got %#v", vapTmp)
case apierrors.IsNotFound(err):
// deleted successfully
default:
framework.Failf("expected 404, got %#v", err)
}
list, err = client.List(ctx, metav1.ListOptions{LabelSelector: label})
var itemsWithoutFinalizer []admissionregistrationv1beta1.MutatingAdmissionPolicy
for _, item := range list.Items {
if len(item.GetFinalizers()) == 0 {
itemsWithoutFinalizer = append(itemsWithoutFinalizer, item)
}
}
framework.ExpectNoError(err)
gomega.Expect(itemsWithoutFinalizer).To(gomega.HaveLen(2), "filtered list should have 2 items")
ginkgo.By("deleting a collection")
err = client.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: label})
framework.ExpectNoError(err)
list, err = client.List(ctx, metav1.ListOptions{LabelSelector: label})
var itemsColWithoutFinalizer []admissionregistrationv1beta1.MutatingAdmissionPolicy
for _, item := range list.Items {
if item.GetDeletionTimestamp() == nil || len(item.GetFinalizers()) == 0 {
itemsColWithoutFinalizer = append(itemsColWithoutFinalizer, item)
}
}
framework.ExpectNoError(err)
gomega.Expect(itemsColWithoutFinalizer).To(gomega.BeEmpty(), "filtered list should have 0 items")
})
/*
Release: v1.34
Testname: MutatingadmissionPolicyBinding API
Description:
The admissionregistration.k8s.io API group MUST exist in the
/apis discovery document.
The admissionregistration.k8s.io/v1beta1 API group/version MUST exist
in the /apis/admissionregistration.k8s.io discovery document.
The MutatingadmissionPolicyBinding resources MUST exist in the
/apis/admissionregistration.k8s.io/v1beta1 discovery document.
The MutatingadmissionPolicyBinding resource must support create, get,
list, watch, update, patch, delete, and deletecollection.
*/
framework.It("should support MutatingAdmissionPolicyBinding API operations", func(ctx context.Context) {
mapbVersion := "v1beta1"
ginkgo.By("getting /apis")
{
discoveryGroups, err := f.ClientSet.Discovery().ServerGroups()
framework.ExpectNoError(err)
found := false
for _, group := range discoveryGroups.Groups {
if group.Name == admissionregistrationv1.GroupName {
for _, version := range group.Versions {
if version.Version == mapbVersion {
found = true
break
}
}
}
}
if !found {
framework.Failf("expected MutatingAdmissionPolicyBinding API group/version, got %#v", discoveryGroups.Groups)
}
}
ginkgo.By("getting /apis/admissionregistration.k8s.io")
{
group := &metav1.APIGroup{}
err := f.ClientSet.Discovery().RESTClient().Get().AbsPath("/apis/admissionregistration.k8s.io").Do(ctx).Into(group)
framework.ExpectNoError(err)
found := false
for _, version := range group.Versions {
if version.Version == mapbVersion {
found = true
break
}
}
if !found {
framework.Failf("expected MutatingAdmissionPolicyBinding API version, got %#v", group.Versions)
}
}
ginkgo.By("getting /apis/admissionregistration.k8s.io/" + mapbVersion)
{
resources, err := f.ClientSet.Discovery().ServerResourcesForGroupVersion(admissionregistrationv1beta1.SchemeGroupVersion.String())
framework.ExpectNoError(err)
foundVAPB := false
for _, resource := range resources.APIResources {
switch resource.Name {
case "mutatingadmissionpolicybindings":
foundVAPB = true
}
}
if !foundVAPB {
framework.Failf("expected mutatingadmissionpolicybindings, got %#v", resources.APIResources)
}
}
client := f.ClientSet.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings()
labelKey, labelValue := "example-e2e-mapb-label", utilrand.String(8)
label := fmt.Sprintf("%s=%s", labelKey, labelValue)
template := &admissionregistrationv1beta1.MutatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "e2e-example-mapb-",
Labels: map[string]string{
labelKey: labelValue,
},
},
Spec: admissionregistrationv1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
},
}
ginkgo.DeferCleanup(func(ctx context.Context) {
err := client.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: label})
framework.ExpectNoError(err)
})
ginkgo.By("creating")
_, err := client.Create(ctx, template, metav1.CreateOptions{})
framework.ExpectNoError(err)
_, err = client.Create(ctx, template, metav1.CreateOptions{})
framework.ExpectNoError(err)
mapbCreated, err := client.Create(ctx, template, metav1.CreateOptions{})
framework.ExpectNoError(err)
ginkgo.By("getting")
mapbRead, err := client.Get(ctx, mapbCreated.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
gomega.Expect(mapbRead.UID).To(gomega.Equal(mapbCreated.UID))
ginkgo.By("listing")
list, err := client.List(ctx, metav1.ListOptions{LabelSelector: label})
framework.ExpectNoError(err)
ginkgo.By("watching")
framework.Logf("starting watch")
mapbWatch, err := client.Watch(ctx, metav1.ListOptions{ResourceVersion: list.ResourceVersion, LabelSelector: label})
framework.ExpectNoError(err)
ginkgo.By("patching")
patchBytes := []byte(`{"metadata":{"annotations":{"patched":"true"}}}`)
mapbPatched, err := client.Patch(ctx, mapbCreated.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{})
framework.ExpectNoError(err)
gomega.Expect(mapbPatched.Annotations).To(gomega.HaveKeyWithValue("patched", "true"), "patched object should have the applied annotation")
ginkgo.By("updating")
var mapbUpdated *admissionregistrationv1beta1.MutatingAdmissionPolicyBinding
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
mapb, err := client.Get(ctx, mapbCreated.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
mapbToUpdate := mapb.DeepCopy()
mapbToUpdate.Annotations["updated"] = "true"
mapbUpdated, err = client.Update(ctx, mapbToUpdate, metav1.UpdateOptions{})
return err
})
framework.ExpectNoError(err, "failed to update mutatingadmissionpolicybinding %q", mapbCreated.Name)
gomega.Expect(mapbUpdated.Annotations).To(gomega.HaveKeyWithValue("updated", "true"), "updated object should have the applied annotation")
framework.Logf("waiting for watch events with expected annotations")
for sawAnnotation := false; !sawAnnotation; {
select {
case evt, ok := <-mapbWatch.ResultChan():
if !ok {
framework.Fail("watch channel should not close")
}
gomega.Expect(evt.Type).To(gomega.Equal(watch.Modified))
vapbWatched, isFS := evt.Object.(*admissionregistrationv1beta1.MutatingAdmissionPolicyBinding)
if !isFS {
framework.Failf("expected an object of type: %T, but got %T", &admissionregistrationv1beta1.MutatingAdmissionPolicyBinding{}, evt.Object)
}
if vapbWatched.Annotations["patched"] == "true" {
sawAnnotation = true
mapbWatch.Stop()
} else {
framework.Logf("missing expected annotations, waiting: %#v", vapbWatched.Annotations)
}
case <-time.After(wait.ForeverTestTimeout):
framework.Fail("timed out waiting for watch event")
}
}
ginkgo.By("deleting")
err = client.Delete(ctx, mapbCreated.Name, metav1.DeleteOptions{})
framework.ExpectNoError(err)
mapbTmp, err := client.Get(ctx, mapbCreated.Name, metav1.GetOptions{})
switch {
case err == nil && mapbTmp.GetDeletionTimestamp() != nil && len(mapbTmp.GetFinalizers()) > 0:
// deletion requested successfully, object is blocked by finalizers
case err == nil:
framework.Failf("expected deleted object, got %#v", mapbTmp)
case apierrors.IsNotFound(err):
// deleted successfully
default:
framework.Failf("expected 404, got %#v", err)
}
list, err = client.List(ctx, metav1.ListOptions{LabelSelector: label})
var itemsWithoutFinalizer []admissionregistrationv1beta1.MutatingAdmissionPolicyBinding
for _, item := range list.Items {
if len(item.GetFinalizers()) == 0 {
itemsWithoutFinalizer = append(itemsWithoutFinalizer, item)
}
}
framework.ExpectNoError(err)
gomega.Expect(itemsWithoutFinalizer).To(gomega.HaveLen(2), "filtered list should have 2 items")
ginkgo.By("deleting a collection")
err = client.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: label})
framework.ExpectNoError(err)
list, err = client.List(ctx, metav1.ListOptions{LabelSelector: label})
var itemsColWithoutFinalizer []admissionregistrationv1beta1.MutatingAdmissionPolicyBinding
for _, item := range list.Items {
if item.GetDeletionTimestamp() == nil || len(item.GetFinalizers()) == 0 {
itemsColWithoutFinalizer = append(itemsColWithoutFinalizer, item)
}
}
framework.ExpectNoError(err)
gomega.Expect(itemsColWithoutFinalizer).To(gomega.BeEmpty(), "filtered list should have 0 items")
})
})
func createMAPBinding(bindingName string, uniqueLabel string, policyName string) *admissionregistrationv1alpha1.MutatingAdmissionPolicyBinding {
return &admissionregistrationv1alpha1.MutatingAdmissionPolicyBinding{
func createMAPBinding(bindingName string, uniqueLabel string, policyName string) *admissionregistrationv1beta1.MutatingAdmissionPolicyBinding {
return &admissionregistrationv1beta1.MutatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: bindingName},
Spec: admissionregistrationv1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: admissionregistrationv1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: policyName,
MatchResources: &admissionregistrationv1alpha1.MatchResources{
MatchResources: &admissionregistrationv1beta1.MatchResources{
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{uniqueLabel: "true"},
},

View file

@ -155,6 +155,8 @@ var (
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicybindings"): true,
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicies"): true,
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicybindings"): true,
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicies"): true,
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicybindings"): true,
}
parentResources = map[schema.GroupVersionResource]schema.GroupVersionResource{

View file

@ -155,6 +155,8 @@ var (
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicybindings"): true,
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicies"): true,
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicybindings"): true,
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicies"): true,
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicybindings"): true,
// transient resource exemption
gvr("authentication.k8s.io", "v1", "selfsubjectreviews"): true,
gvr("authentication.k8s.io", "v1beta1", "selfsubjectreviews"): true,

View file

@ -30,7 +30,7 @@ import (
"github.com/stretchr/testify/require"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/api/admissionregistration/v1alpha1"
"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,10 +55,11 @@ 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) {
matchEndpointResources := v1alpha1.MatchResources{
ResourceRules: []v1alpha1.NamedRuleWithOperations{
allow := v1beta1.AllowAction
matchEndpointResources := v1beta1.MatchResources{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
RuleWithOperations: v1beta1.RuleWithOperations{
Operations: []admissionregistrationv1.OperationType{"*"},
Rule: admissionregistrationv1.Rule{
APIGroups: []string{""},
@ -72,8 +73,8 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
cases := []struct {
name string
policies []*v1alpha1.MutatingAdmissionPolicy
bindings []*v1alpha1.MutatingAdmissionPolicyBinding
policies []*v1beta1.MutatingAdmissionPolicy
bindings []*v1beta1.MutatingAdmissionPolicyBinding
params []*corev1.ConfigMap
requestOperation admissionregistrationv1.OperationType
@ -84,10 +85,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
}{
{
name: "basic",
policies: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("basic-policy", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("basic-policy", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -99,7 +100,7 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
}),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("basic-policy", nil, nil),
},
requestOperation: admissionregistrationv1.Create,
@ -122,10 +123,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
{
name: "multiple policies",
policies: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("multi-policy-1", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("multi-policy-1", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -136,9 +137,9 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
}`,
},
}),
mutatingPolicy("multi-policy-2", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
mutatingPolicy("multi-policy-2", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -150,7 +151,7 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
}),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("multi-policy-1", nil, nil),
mutatingBinding("multi-policy-2", nil, nil),
},
@ -175,13 +176,13 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
{
name: "policy with native param",
policies: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("policy-with-native", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, &v1alpha1.ParamKind{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("policy-with-native", v1beta1.NeverReinvocationPolicy, matchEndpointResources, &v1beta1.ParamKind{
APIVersion: "v1",
Kind: "ConfigMap",
}, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
}, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -193,9 +194,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
}),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy-with-native", &v1alpha1.ParamRef{
Name: "policy-with-native-param",
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy-with-native", &v1beta1.ParamRef{
Name: "policy-with-native-param",
ParameterNotFoundAction: &allow,
}, nil),
},
params: []*corev1.ConfigMap{
@ -230,19 +232,19 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
{
name: "policy with multiple params quantified by single binding",
policies: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("multi-param-binding", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, &v1alpha1.ParamKind{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("multi-param-binding", v1beta1.NeverReinvocationPolicy, matchEndpointResources, &v1beta1.ParamKind{
APIVersion: "v1",
Kind: "ConfigMap",
}, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
}, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{metadata: Object.metadata{annotations: params.data}}`,
},
}),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
mutatingBinding("multi-param-binding", &v1alpha1.ParamRef{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("multi-param-binding", &v1beta1.ParamRef{
// note empty namespace. all params matching request namespace
// will be used
Selector: &metav1.LabelSelector{
@ -250,7 +252,8 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
"multi-param-binding-param": "true",
},
},
Namespace: "default",
Namespace: "default",
ParameterNotFoundAction: &allow,
}, nil),
},
params: []*corev1.ConfigMap{
@ -302,15 +305,15 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
{
name: "policy with variables",
policies: []*v1alpha1.MutatingAdmissionPolicy{
withMutatingVariables([]v1alpha1.Variable{
policies: []*v1beta1.MutatingAdmissionPolicy{
withMutatingVariables([]v1beta1.Variable{
{Name: "foo1", Expression: `"foo1" + "Value"`},
{Name: "foo2", Expression: `variables.foo1.replace("1", "2")`},
},
mutatingPolicy("policy-with-multiple-mutations", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil,
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
mutatingPolicy("policy-with-multiple-mutations", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil,
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -321,18 +324,18 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
}`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeJSONPatch,
JSONPatch: &v1alpha1.JSONPatch{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeJSONPatch,
JSONPatch: &v1beta1.JSONPatch{
Expression: `[
JSONPatch{op: "test", path: "/metadata/annotations", value: {"foo1": variables.foo1}},
JSONPatch{op: "add", path: "/metadata/annotations/foo2", value: variables.foo2},
]`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -345,7 +348,7 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
)),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy-with-multiple-mutations", nil, nil),
},
requestOperation: admissionregistrationv1.Create,
@ -370,16 +373,16 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
{
name: "match condition matches",
policies: []*v1alpha1.MutatingAdmissionPolicy{
withMutatingMatchConditions([]v1alpha1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}},
mutatingPolicy("policy-match-condition", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
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{
Expression: `Object{metadata: Object.metadata{labels: {"applied": "updated"}}}`,
},
})),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy-match-condition", nil, nil),
},
requestOperation: admissionregistrationv1.Create,
@ -403,11 +406,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: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("policy-1", v1alpha1.IfNeededReinvocationPolicy, matchEndpointResources, nil,
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("policy-1", v1beta1.IfNeededReinvocationPolicy, matchEndpointResources, nil,
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -420,10 +423,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
},
),
mutatingPolicy("policy-2", v1alpha1.IfNeededReinvocationPolicy, matchEndpointResources, nil,
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
mutatingPolicy("policy-2", v1beta1.IfNeededReinvocationPolicy, matchEndpointResources, nil,
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -436,10 +439,10 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
},
),
mutatingPolicy("policy-3", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil,
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
mutatingPolicy("policy-3", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil,
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -453,7 +456,7 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
},
),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy-1", nil, nil),
mutatingBinding("policy-2", nil, nil),
mutatingBinding("policy-3", nil, nil),
@ -493,7 +496,7 @@ 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", v1alpha1.SchemeGroupVersion)}
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()
@ -519,7 +522,7 @@ func TestMutatingAdmissionPolicy(t *testing.T) {
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.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Create(ctx, p, metav1.CreateOptions{FieldManager: "integration-test"})
_, err = client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, p, metav1.CreateOptions{FieldManager: "integration-test"})
require.NoError(t, err)
}
@ -572,10 +575,11 @@ 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) {
matchEndpointResources := v1alpha1.MatchResources{
ResourceRules: []v1alpha1.NamedRuleWithOperations{
allow := v1beta1.AllowAction
matchEndpointResources := v1beta1.MatchResources{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
RuleWithOperations: v1beta1.RuleWithOperations{
Operations: []admissionregistrationv1.OperationType{"*"},
Rule: admissionregistrationv1.Rule{
APIGroups: []string{""},
@ -589,8 +593,8 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
cases := []struct {
name string
policies []*v1alpha1.MutatingAdmissionPolicy
bindings []*v1alpha1.MutatingAdmissionPolicyBinding
policies []*v1beta1.MutatingAdmissionPolicy
bindings []*v1beta1.MutatingAdmissionPolicyBinding
params []*corev1.ConfigMap
requestOperation admissionregistrationv1.OperationType
@ -602,10 +606,10 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
}{
{
name: "unbound policy is no-op",
policies: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("unbound-policy", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("unbound-policy", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -634,16 +638,16 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
{
name: "failure policy ignore",
policies: []*v1alpha1.MutatingAdmissionPolicy{
withMutatingFailurePolicy(v1alpha1.Ignore,
mutatingPolicy("policy", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
policies: []*v1beta1.MutatingAdmissionPolicy{
withMutatingFailurePolicy(v1beta1.Ignore,
mutatingPolicy("policy", v1beta1.NeverReinvocationPolicy, matchEndpointResources, nil, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{spec: Object.spec{invalidField: "invalid apply configuration"}}`,
},
})),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy", nil, nil),
},
requestOperation: admissionregistrationv1.Create,
@ -663,16 +667,16 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
{
name: "match condition does not match",
policies: []*v1alpha1.MutatingAdmissionPolicy{
withMutatingMatchConditions([]v1alpha1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}},
mutatingPolicy("policy-no-match-condition", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
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{
Expression: `Object{metadata: Object.metadata{labels: {"applied": "updated"}}}`,
},
})),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy-no-match-condition", nil, nil),
},
requestOperation: admissionregistrationv1.Create,
@ -694,23 +698,23 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
{
name: "some policy conditions match",
policies: []*v1alpha1.MutatingAdmissionPolicy{
withMutatingMatchConditions([]v1alpha1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("production")`}},
mutatingPolicy("policy-1", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
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{
Expression: `Object{metadata: Object.metadata{labels: {"applied": "wrong"}}}`,
},
})),
withMutatingMatchConditions([]v1alpha1.MatchCondition{{Name: "test-only", Expression: `object.metadata.?labels["environment"] == optional.of("test")`}},
mutatingPolicy("policy-2", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
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{
Expression: `Object{metadata: Object.metadata{labels: {"applied": "updated"}}}`,
},
})),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("policy-1", nil, nil),
mutatingBinding("policy-2", nil, nil),
},
@ -733,11 +737,11 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
{
name: "mutate status subresource",
policies: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("subresource-status", v1alpha1.NeverReinvocationPolicy, v1alpha1.MatchResources{
ResourceRules: []v1alpha1.NamedRuleWithOperations{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("subresource-status", v1beta1.NeverReinvocationPolicy, v1beta1.MatchResources{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
RuleWithOperations: v1beta1.RuleWithOperations{
Operations: []admissionregistrationv1.OperationType{"*"},
Rule: admissionregistrationv1.Rule{
APIGroups: []string{""},
@ -747,9 +751,9 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
},
},
}, nil, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
}, nil, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{
status: Object.status{
conditions: [Object.status.conditions{
@ -761,7 +765,7 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
}),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("subresource-status", nil, nil),
},
requestOperation: admissionregistrationv1.Update,
@ -803,25 +807,27 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
{
name: "multiple bindings with different params",
policies: []*v1alpha1.MutatingAdmissionPolicy{
mutatingPolicy("multi-binding", v1alpha1.NeverReinvocationPolicy, matchEndpointResources, &v1alpha1.ParamKind{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy("multi-binding", v1beta1.NeverReinvocationPolicy, matchEndpointResources, &v1beta1.ParamKind{
APIVersion: "v1",
Kind: "ConfigMap",
}, v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
}, v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{metadata: Object.metadata{annotations: params.data}}`,
},
}),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
mutatingBinding("multi-binding", &v1alpha1.ParamRef{
Name: "multi-binding-param-1",
Namespace: "default",
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("multi-binding", &v1beta1.ParamRef{
Name: "multi-binding-param-1",
Namespace: "default",
ParameterNotFoundAction: &allow,
}, nil),
mutatingBinding("multi-binding", &v1alpha1.ParamRef{
Name: "multi-binding-param-2",
Namespace: "default",
mutatingBinding("multi-binding", &v1beta1.ParamRef{
Name: "multi-binding-param-2",
Namespace: "default",
ParameterNotFoundAction: &allow,
}, nil),
},
params: []*corev1.ConfigMap{
@ -869,24 +875,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: []*v1alpha1.MutatingAdmissionPolicy{
policies: []*v1beta1.MutatingAdmissionPolicy{
mutatingPolicy(
"multi-param-reinvocation",
v1alpha1.IfNeededReinvocationPolicy,
v1beta1.IfNeededReinvocationPolicy,
matchEndpointResources,
&v1alpha1.ParamKind{
&v1beta1.ParamKind{
APIVersion: "v1",
Kind: "ConfigMap",
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{metadata: Object.metadata{annotations: params.data}}`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -900,21 +906,21 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
),
mutatingPolicy(
"policy-with-param-no-reinvoke",
v1alpha1.NeverReinvocationPolicy,
v1beta1.NeverReinvocationPolicy,
matchEndpointResources,
&v1alpha1.ParamKind{
&v1beta1.ParamKind{
APIVersion: "v1",
Kind: "ConfigMap",
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `Object{metadata: Object.metadata{annotations: params.data}}`,
},
},
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
v1beta1.Mutation{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.ApplyConfiguration{
Expression: `
Object{
metadata: Object.metadata{
@ -927,8 +933,8 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
},
),
},
bindings: []*v1alpha1.MutatingAdmissionPolicyBinding{
mutatingBinding("multi-param-reinvocation", &v1alpha1.ParamRef{
bindings: []*v1beta1.MutatingAdmissionPolicyBinding{
mutatingBinding("multi-param-reinvocation", &v1beta1.ParamRef{
// note empty namespace. all params matching request namespace
// will be used
Selector: &metav1.LabelSelector{
@ -936,11 +942,13 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
"multi-param-reinvocation": "true",
},
},
ParameterNotFoundAction: &allow,
}, nil),
mutatingBinding("policy-with-param-no-reinvoke", &v1alpha1.ParamRef{
mutatingBinding("policy-with-param-no-reinvoke", &v1beta1.ParamRef{
// note empty namespace. all params matching request namespace
// will be used
Name: "multi-param-reinvocation-param-3",
Name: "multi-param-reinvocation-param-3",
ParameterNotFoundAction: &allow,
}, nil),
},
params: []*corev1.ConfigMap{
@ -1007,7 +1015,7 @@ 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", v1alpha1.SchemeGroupVersion)}
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()
@ -1027,12 +1035,12 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
}
for _, p := range tc.policies {
_, err = client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Create(ctx, p, metav1.CreateOptions{FieldManager: "integration-test"})
_, err = client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, p, metav1.CreateOptions{FieldManager: "integration-test"})
require.NoError(t, err)
}
for _, b := range tc.bindings {
_, err = client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicyBindings().Create(ctx, b, metav1.CreateOptions{FieldManager: "integration-test"})
_, err = client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Create(ctx, b, metav1.CreateOptions{FieldManager: "integration-test"})
require.NoError(t, err)
}
@ -1093,7 +1101,7 @@ func TestMutatingAdmissionPolicy_Slow(t *testing.T) {
// tested.
func Test_MutatingAdmissionPolicy_CustomResources(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.MutatingAdmissionPolicy, true)
flags := []string{fmt.Sprintf("--runtime-config=%s=true", v1alpha1.SchemeGroupVersion)}
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 {
@ -1111,14 +1119,14 @@ func Test_MutatingAdmissionPolicy_CustomResources(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
policy := withMutatingFailurePolicy(v1alpha1.Fail, mutatingPolicy("match-by-match-policy-equivalent", v1alpha1.IfNeededReinvocationPolicy, v1alpha1.MatchResources{
ResourceRules: []v1alpha1.NamedRuleWithOperations{
policy := withMutatingFailurePolicy(v1beta1.Fail, mutatingPolicy("match-by-match-policy-equivalent", v1beta1.IfNeededReinvocationPolicy, v1beta1.MatchResources{
ResourceRules: []v1beta1.NamedRuleWithOperations{
{
RuleWithOperations: v1alpha1.RuleWithOperations{
Operations: []v1alpha1.OperationType{
RuleWithOperations: v1beta1.RuleWithOperations{
Operations: []v1beta1.OperationType{
"*",
},
Rule: v1alpha1.Rule{
Rule: v1beta1.Rule{
APIGroups: []string{
"awesome.bears.com",
},
@ -1133,16 +1141,16 @@ func Test_MutatingAdmissionPolicy_CustomResources(t *testing.T) {
},
}},
nil,
v1alpha1.Mutation{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
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.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{}); err != nil {
if _, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{}); err != nil {
t.Fatal(err)
}
@ -1213,12 +1221,12 @@ func Test_MutatingAdmissionPolicy_CustomResources(t *testing.T) {
}
func mutatingPolicy(name string, reinvocationPolicy v1alpha1.ReinvocationPolicyType, matchResources v1alpha1.MatchResources, paramKind *v1alpha1.ParamKind, mutations ...v1alpha1.Mutation) *v1alpha1.MutatingAdmissionPolicy {
return &v1alpha1.MutatingAdmissionPolicy{
func mutatingPolicy(name string, reinvocationPolicy v1beta1.ReinvocationPolicyType, matchResources v1beta1.MatchResources, paramKind *v1beta1.ParamKind, mutations ...v1beta1.Mutation) *v1beta1.MutatingAdmissionPolicy {
return &v1beta1.MutatingAdmissionPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1alpha1.MutatingAdmissionPolicySpec{
Spec: v1beta1.MutatingAdmissionPolicySpec{
MatchConstraints: &matchResources,
ParamKind: paramKind,
Mutations: mutations,
@ -1227,24 +1235,24 @@ func mutatingPolicy(name string, reinvocationPolicy v1alpha1.ReinvocationPolicyT
}
}
func withMutatingVariables(variables []v1alpha1.Variable, policy *v1alpha1.MutatingAdmissionPolicy) *v1alpha1.MutatingAdmissionPolicy {
func withMutatingVariables(variables []v1beta1.Variable, policy *v1beta1.MutatingAdmissionPolicy) *v1beta1.MutatingAdmissionPolicy {
policy.Spec.Variables = variables
return policy
}
func withMutatingFailurePolicy(failure v1alpha1.FailurePolicyType, policy *v1alpha1.MutatingAdmissionPolicy) *v1alpha1.MutatingAdmissionPolicy {
func withMutatingFailurePolicy(failure v1beta1.FailurePolicyType, policy *v1beta1.MutatingAdmissionPolicy) *v1beta1.MutatingAdmissionPolicy {
policy.Spec.FailurePolicy = &failure
return policy
}
func withMutatingMatchConditions(matchConditions []v1alpha1.MatchCondition, policy *v1alpha1.MutatingAdmissionPolicy) *v1alpha1.MutatingAdmissionPolicy {
func withMutatingMatchConditions(matchConditions []v1beta1.MatchCondition, policy *v1beta1.MutatingAdmissionPolicy) *v1beta1.MutatingAdmissionPolicy {
policy.Spec.MatchConditions = matchConditions
return policy
}
func withMutatingWaitReadyConstraintAndExpression(policy *v1alpha1.MutatingAdmissionPolicy, testID string) *v1alpha1.MutatingAdmissionPolicy {
func withMutatingWaitReadyConstraintAndExpression(policy *v1beta1.MutatingAdmissionPolicy, testID string) *v1beta1.MutatingAdmissionPolicy {
policy = policy.DeepCopy()
policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, v1alpha1.NamedRuleWithOperations{
policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, v1beta1.NamedRuleWithOperations{
ResourceNames: []string{"test-marker"},
RuleWithOperations: admissionregistrationv1.RuleWithOperations{
Operations: []admissionregistrationv1.OperationType{
@ -1273,9 +1281,9 @@ func withMutatingWaitReadyConstraintAndExpression(policy *v1alpha1.MutatingAdmis
m.ApplyConfiguration.Expression = bypass + m.ApplyConfiguration.Expression
}
}
policy.Spec.Mutations = append([]v1alpha1.Mutation{{
PatchType: v1alpha1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1alpha1.ApplyConfiguration{
policy.Spec.Mutations = append([]v1beta1.Mutation{{
PatchType: v1beta1.PatchTypeApplyConfiguration,
ApplyConfiguration: &v1beta1.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),
},
@ -1283,12 +1291,12 @@ func withMutatingWaitReadyConstraintAndExpression(policy *v1alpha1.MutatingAdmis
return policy
}
func mutatingBinding(policyName string, paramRef *v1alpha1.ParamRef, matchResources *v1alpha1.MatchResources) *v1alpha1.MutatingAdmissionPolicyBinding {
return &v1alpha1.MutatingAdmissionPolicyBinding{
func mutatingBinding(policyName string, paramRef *v1beta1.ParamRef, matchResources *v1beta1.MatchResources) *v1beta1.MutatingAdmissionPolicyBinding {
return &v1beta1.MutatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: policyName + "-binding-" + string(uuid.NewUUID()),
},
Spec: v1alpha1.MutatingAdmissionPolicyBindingSpec{
Spec: v1beta1.MutatingAdmissionPolicyBindingSpec{
PolicyName: policyName,
ParamRef: paramRef,
MatchResources: matchResources,
@ -1296,12 +1304,12 @@ func mutatingBinding(policyName string, paramRef *v1alpha1.ParamRef, matchResour
}
}
func createAndWaitReadyMutating(ctx context.Context, t *testing.T, client kubernetes.Interface, binding *v1alpha1.MutatingAdmissionPolicyBinding, testID string) error {
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 createAndWaitReadyNamespacedWithWarnHandlerMutating(ctx context.Context, t *testing.T, client kubernetes.Interface, binding *v1alpha1.MutatingAdmissionPolicyBinding, ns string, testID string) error {
_, err := client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
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)
}
@ -1326,14 +1334,14 @@ func createAndWaitReadyNamespacedWithWarnHandlerMutating(ctx context.Context, t
return nil
}
func cleanupMutatingPolicy(ctx context.Context, t *testing.T, client kubernetes.Interface, policies []*v1alpha1.MutatingAdmissionPolicy, bindings []*v1alpha1.MutatingAdmissionPolicyBinding, params []*corev1.ConfigMap) error {
func cleanupMutatingPolicy(ctx context.Context, t *testing.T, client kubernetes.Interface, policies []*v1beta1.MutatingAdmissionPolicy, bindings []*v1beta1.MutatingAdmissionPolicyBinding, params []*corev1.ConfigMap) error {
for _, policy := range policies {
if err := client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Delete(ctx, policy.Name, metav1.DeleteOptions{}); err != nil {
if err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().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.AdmissionregistrationV1alpha1().MutatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
_, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return true, nil
}
@ -1344,13 +1352,13 @@ func cleanupMutatingPolicy(ctx context.Context, t *testing.T, client kubernetes.
}
for _, binding := range bindings {
err := client.AdmissionregistrationV1alpha1().MutatingAdmissionPolicyBindings().Delete(ctx, binding.Name, metav1.DeleteOptions{})
err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().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.AdmissionregistrationV1alpha1().MutatingAdmissionPolicyBindings().Get(ctx, binding.Name, metav1.GetOptions{})
_, err := client.AdmissionregistrationV1beta1().MutatingAdmissionPolicyBindings().Get(ctx, binding.Name, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return true, nil
}

View file

@ -483,18 +483,32 @@ func GetEtcdStorageDataForNamespaceServedAt(namespace string, v string, isEmulat
IntroducedVersion: "1.28",
RemovedVersion: "1.34",
},
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"map1b1"},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"reinvocationPolicy": "IfNeeded","mutations":[{"applyConfiguration": {"expression":"Object{metadata: Object.metadata{labels: {'example':'true'}}}"}, "patchType":"ApplyConfiguration"}]}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicies/map1b1",
IntroducedVersion: "1.34",
RemovedVersion: "1.40",
},
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"mpb1b1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com", "parameterNotFoundAction": "Allow"}}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicybindings/mpb1b1",
IntroducedVersion: "1.34",
RemovedVersion: "1.40",
},
// --
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicies"): {
Stub: `{"metadata":{"name":"map1"},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"reinvocationPolicy": "IfNeeded","mutations":[{"applyConfiguration": {"expression":"Object{metadata: Object.metadata{labels: {'example':'true'}}}"}, "patchType":"ApplyConfiguration"}]}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicies/map1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "MutatingAdmissionPolicy"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},
gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingadmissionpolicybindings"): {
Stub: `{"metadata":{"name":"mpb1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com"}}}`,
Stub: `{"metadata":{"name":"mpb1"},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com", "parameterNotFoundAction": "Allow"}}}`,
ExpectedEtcdPath: "/registry/mutatingadmissionpolicybindings/mpb1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "MutatingAdmissionPolicyBinding"),
IntroducedVersion: "1.32",
RemovedVersion: "1.38",
},