diff --git a/pkg/api/testing/defaulting_test.go b/pkg/api/testing/defaulting_test.go index 2e04d1dcda1..28641516aeb 100644 --- a/pkg/api/testing/defaulting_test.go +++ b/pkg/api/testing/defaulting_test.go @@ -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"}: {}, diff --git a/pkg/apis/admissionregistration/v1beta1/defaults.go b/pkg/apis/admissionregistration/v1beta1/defaults.go index 3b58100dabf..5c9b188f304 100644 --- a/pkg/apis/admissionregistration/v1beta1/defaults.go +++ b/pkg/apis/admissionregistration/v1beta1/defaults.go @@ -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 + } +} diff --git a/pkg/apis/admissionregistration/v1beta1/defaults_test.go b/pkg/apis/admissionregistration/v1beta1/defaults_test.go index 40a2fea8eb6..e02ff2cdc54 100644 --- a/pkg/apis/admissionregistration/v1beta1/defaults_test.go +++ b/pkg/apis/admissionregistration/v1beta1/defaults_test.go @@ -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 { diff --git a/pkg/kubeapiserver/default_storage_factory_builder.go b/pkg/kubeapiserver/default_storage_factory_builder.go index d68ad01bda8..bf89889e413 100644 --- a/pkg/kubeapiserver/default_storage_factory_builder.go +++ b/pkg/kubeapiserver/default_storage_factory_builder.go @@ -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"), diff --git a/pkg/registry/admissionregistration/rest/storage_apiserver.go b/pkg/registry/admissionregistration/rest/storage_apiserver.go index f596a2aa4f4..4d46bd99f41 100644 --- a/pkg/registry/admissionregistration/rest/storage_apiserver.go +++ b/pkg/registry/admissionregistration/rest/storage_apiserver.go @@ -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 } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/register.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/register.go index 363233a2f9a..be64c4a5fab 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/register.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/register.go @@ -54,6 +54,10 @@ func addKnownTypes(scheme *runtime.Scheme) error { &ValidatingAdmissionPolicyList{}, &ValidatingAdmissionPolicyBinding{}, &ValidatingAdmissionPolicyBindingList{}, + &MutatingAdmissionPolicy{}, + &MutatingAdmissionPolicyList{}, + &MutatingAdmissionPolicyBinding{}, + &MutatingAdmissionPolicyBindingList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go index 0f590312392..cffdda82c9a 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go @@ -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.' - CEL type of object field (such as 'Object.spec') + // - 'Object.....` - 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.' - CEL type of object field (such as 'Object.spec') + // - 'Object.....` - 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"` +} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go index e5ef242fa37..ff342623a95 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/accessor.go @@ -18,7 +18,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 } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go index 710b8ef1ea4..d8b5c11a576 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation.go @@ -19,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) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go index ef0859d3a6e..c5be5befc16 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/compilation_test.go @@ -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 } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go index e077cf94248..153b268d671 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go @@ -22,7 +22,7 @@ import ( "fmt" "time" - "k8s.io/api/admissionregistration/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) } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go index 8526e8b72f8..d4f01266c06 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher_test.go @@ -23,7 +23,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", }, }}, diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go index 527bc6a53c0..20e4d2d0c47 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin.go @@ -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} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go index 9d7652fd04d..bb82db25a0a 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/plugin_test.go @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/require" - "k8s.io/api/admissionregistration/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", }, }, diff --git a/test/e2e/apimachinery/mutatingadmissionpolicy.go b/test/e2e/apimachinery/mutatingadmissionpolicy.go index a2ef92c66e1..e6309e12d3e 100644 --- a/test/e2e/apimachinery/mutatingadmissionpolicy.go +++ b/test/e2e/apimachinery/mutatingadmissionpolicy.go @@ -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"}, }, diff --git a/test/integration/apiserver/admissionwebhook/admission_test.go b/test/integration/apiserver/admissionwebhook/admission_test.go index 6080169193c..38dbb096366 100644 --- a/test/integration/apiserver/admissionwebhook/admission_test.go +++ b/test/integration/apiserver/admissionwebhook/admission_test.go @@ -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{ diff --git a/test/integration/apiserver/cel/admission_test_util.go b/test/integration/apiserver/cel/admission_test_util.go index 3d3e3a8936e..4397bdea62c 100644 --- a/test/integration/apiserver/cel/admission_test_util.go +++ b/test/integration/apiserver/cel/admission_test_util.go @@ -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, diff --git a/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go b/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go index e95fbdf401b..e73753e0cfc 100644 --- a/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go +++ b/test/integration/apiserver/cel/mutatingadmissionpolicy_test.go @@ -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 } diff --git a/test/integration/etcd/data.go b/test/integration/etcd/data.go index 60941b2a7a8..8acec8419bf 100644 --- a/test/integration/etcd/data.go +++ b/test/integration/etcd/data.go @@ -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", },