Skip to content

Commit 301ac9a

Browse files
authored
Merge pull request #4973 from jpbetz/json-patch
Clarify JSON Patch support in mutating admission policy
2 parents 290206d + e17a22e commit 301ac9a

File tree

1 file changed

+68
-42
lines changed
  • keps/sig-api-machinery/3962-mutating-admission-policies

1 file changed

+68
-42
lines changed

keps/sig-api-machinery/3962-mutating-admission-policies/README.md

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
- [Implementation History](#implementation-history)
6868
- [Drawbacks](#drawbacks)
6969
- [Alternatives](#alternatives)
70-
- [Alternative 1: JSONPatch](#alternative-1-jsonpatch)
7170
- [Alternative 2: Introduce new syntax](#alternative-2-introduce-new-syntax)
7271
- [Infrastructure Needed (Optional)](#infrastructure-needed-optional)
7372
<!-- /toc -->
@@ -123,7 +122,7 @@ API](/keps/sig-api-machinery/3488-cel-admission-control/README.md) for
123122
validating admission policies.
124123

125124
This enhancement proposes an approach where mutations are declared in CEL by
126-
combining CEL's object instantiation and Server Side Apply's merge algorithms.
125+
combining CEL's object instantiation, JSON Patch, and Server Side Apply's merge algorithms.
127126

128127
## Motivation
129128

@@ -180,18 +179,18 @@ Very similar to what we have in ValidatingAdmissionPolicy, this API separates po
180179
- Policy param resources (custom resources or config maps)
181180

182181
The idea is to leverage the CEL power of the object construction and allow users to define how they want to mutate the admission request through CEL expression.
183-
This proposal aims to allow mutations to be expressed using the "apply configuration" introduced by Server Side Apply.
182+
This proposal aims to allow mutations to be expressed using JSON Patch, or the "apply configuration" introduced by Server Side Apply.
184183
And users would be able to define only the fields they care about inside MutatingAdmissionPolicy, the object will be constructed using CEL which would be similar to a Server Side Apply configuration patch and then be merged into the request object using the structural merge strategy.
185184
See sigs.k8s.io/structured-merge-diff for more details.
186185

187186
Note: See the alternative consideration section for the alternatives.
188187

189188
Pros:
190-
- Consistent with Server Side Apply so that we will continue investing SSA as the best way to do patch updates to resources;
191-
- Does not require the users to learn a new syntax;
192-
- Inherit the declarative nature;
193-
- Existing merging strategy to be leverage without rewritten and the effort of maintaining multiple systems;
194-
- Support the existing makers/openapi extensions.
189+
- JSON Patch provides a migration path from mutating admission webhooks, which must use JSON Patch.
190+
- Also build on Server Side Apply so that we will continue investing SSA as the best way to do patch updates to resources;
191+
- Does not require the users to learn a new syntax;
192+
- Inherit the declarative nature;
193+
- Leverages existing merging strategy, markers and openapi extensions.
195194

196195
Cons:
197196
- Lack of deletion support (see the unsetting values section for the details and workaround);
@@ -229,7 +228,7 @@ spec:
229228
reinvocationPolicy: IfNeeded
230229
mutations:
231230
- patchType: "ApplyConfiguration" // "ApplyConfiguration", "JSONPatch" supported.
232-
mutation: >
231+
expression: >
233232
Object{
234233
spec: Object.spec{
235234
initContainers: [
@@ -250,13 +249,42 @@ The "ApplyConfiguration" strategy will prevent user from performing ambiguous ac
250249
The detailed definition of ambiguous action should be reviewed before beta.
251250
For any mutation requires modification regarding with ambiguous action, "JSONPatch" strategy is needed.
252251

253-
Note:
254-
- "JSONPatch" should be supported before the feature going to beta
255-
- The API design of "JSONPatch" should be discussed before second alpha
252+
The "JSONPatch" strategy will use JSON Patch like what is done in Mutating Webhook.
256253

257-
The "JSONPatch" strategy will use JSONPatch like what is done in Mutating Webhook.
258-
@andrewsykim has a [prototype](https://github.com/kubernetes/enhancements/pull/3776) on this earlier.
259-
The following context will focus on "ApplyConfiguration" strategy.
254+
Example JSON Patch:
255+
256+
```yaml
257+
apiVersion: admissionregistration.k8s.io/v1alpha1
258+
kind: MutatingAdmissionPolicy
259+
metadata:
260+
name: "sidecar-policy.example.com"
261+
spec:
262+
paramKind:
263+
group: mutations.example.com
264+
kind: Sidecar
265+
version: v1
266+
matchConstraints:
267+
resourceRules:
268+
- apiGroups: ["apps"]
269+
apiVersions: ["v1"]
270+
operations: ["CREATE"]
271+
resources: ["pods"]
272+
matchConditions:
273+
- name: does-not-already-have-sidecar
274+
expression: "!object.spec.initContainers.exists(ic, ic.name == params.name)"
275+
failurePolicy: Fail
276+
reinvocationPolicy: IfNeeded
277+
mutations:
278+
- patchType: "JSONPatch"
279+
expression: >
280+
JSONPatch{op: "add", path: "/spec/initContainers/-", value: Object.spec.initContainers{
281+
name: params.name,
282+
image: params.image,
283+
args: params.args,
284+
restartPolicy: params.restartPolicy
285+
// ... other container fields injected here ...
286+
}
287+
```
260288

261289
When "ApplyConfiguration" specified, the expression evaluates to an object that has the same type as the incoming object, and the type alias Object refers to the type (see Type Handling for details).
262290

@@ -460,7 +488,8 @@ For example, to remove the env of a sidecar container, filter by its name.
460488

461489
```yaml
462490
mutations:
463-
- mutaton: >
491+
- patchType: "ApplyConfiguration"
492+
expression: >
464493
Object{
465494
spec: Object.spec{
466495
containers: object.spec.containers{
@@ -496,7 +525,8 @@ List with "map" merge type:
496525
- For associatedList with multiple keys like example above, a directive field added could be used to indicate the deletion.
497526
```yaml
498527
mutations:
499-
- mutaton: >
528+
- patchType: "ApplyConfiguration"
529+
expression: >
500530
Object{
501531
spec: Object.spec{
502532
assocListField: [Object.spec.assocListField{
@@ -512,7 +542,8 @@ mutations:
512542
For examples of removing item from List with Map filtered by a subfield:
513543
```yaml
514544
mutations:
515-
- mutaton: >
545+
- patchType: "ApplyConfiguration"
546+
expression: >
516547
Object{
517548
spec: Object.spec{
518549
containers: object.spec.containers.filter(c, c.envvar != "remove-this-container")
@@ -525,7 +556,8 @@ mutations:
525556
For granular list removal, a use case would be removing an item with a sub field named `remove-this-item`.
526557
```yaml
527558
mutations:
528-
- mutation: >
559+
- patchType: "ApplyConfiguration"
560+
expression: >
529561
Object{
530562
spec: Object.spec{
531563
granularList: object.spec.granularList.filter(c, c.subField != "remove-this-item")
@@ -555,7 +587,7 @@ metadata:
555587
spec:
556588
# ...
557589
mutations:
558-
mutation: >
590+
expression: >
559591
Object{
560592
spec: Object.spec{
561593
initContainers: [
@@ -618,7 +650,8 @@ variables:
618650
variables.targetContainers.map(c, {"name": c.name, "env": {"name": "FOO",
619651
"value": "foo"}})
620652
mutations:
621-
- mutation: |
653+
- patchType: "ApplyConfiguration"
654+
expression: |
622655
Object{
623656
spec: Object.spec{
624657
template: Object.spec.template{
@@ -636,7 +669,8 @@ variables:
636669
expression: >-
637670
params.foo == "bar" ? true : "true"
638671
mutations:
639-
- mutation: |
672+
- patchType: "ApplyConfiguration"
673+
expression: |
640674
Object{
641675
spec: Object.spec{
642676
template: Object.spec.template{
@@ -696,7 +730,8 @@ matchConditions:
696730
- name: 'need-default-ingress-class'
697731
expression: '!has(object.spec.ingressClassName)'
698732
mutations:
699-
- mutation: |
733+
- patchType: "ApplyConfiguration"
734+
expression: |
700735
Object{
701736
spec: Object.spec{
702737
ingressClassName: "defaultIngressClass"
@@ -712,7 +747,8 @@ matchConditions:
712747
- name: 'need-default-storage-class'
713748
expression: '!has(object.spec.storageClassName)'
714749
mutations:
715-
- mutation: |
750+
- patchType: "ApplyConfiguration"
751+
expression: |
716752
Object{
717753
spec: Object.spec{
718754
storageClassName: "defaultStorageClass"
@@ -734,7 +770,8 @@ variables:
734770
- name: volumesList
735771
expression: "object.spec.volumes.map(v, v.name)"
736772
mutations:
737-
- mutation: |
773+
- patchType: "ApplyConfiguration"
774+
expression: |
738775
Object{
739776
spec: Object.spec{
740777
volumes: volumeMountsList.filter(n, !(n in volumesList)).map(v, {
@@ -751,7 +788,8 @@ I have a [gist example](https://gist.github.com/cici37/e8181e53069435a307cd78223
751788
Apply default resource requests to Pods that don't specify any.
752789
```yaml
753790
mutations:
754-
- mutation: |
791+
- patchType: "ApplyConfiguration"
792+
expression: |
755793
Object{
756794
spec: Object.spec{
757795
containers: object.spec.containers.filter(c, !has(c.resources)).map(c,
@@ -771,7 +809,8 @@ matchConditions:
771809
- name: 'no-priority-class'
772810
expression: '!has(object.spec.priorityClassName)'
773811
mutations:
774-
- mutation: |
812+
- patchType: "ApplyConfiguration"
813+
expression: |
775814
Object{
776815
spec: Object.spec{
777816
priorityClassName: params.defaultPriorityClass
@@ -824,7 +863,7 @@ Object{
824863
```
825864

826865
#### Use case: modify deprecated field under CRD versions
827-
Support atomic list modification through JSONPatch
866+
Support atomic list modification through JSON Patch
828867

829868

830869
#### Use Case - mutation VS controller fight
@@ -1523,21 +1562,8 @@ What other approaches did you consider, and why did you rule them out? These do
15231562
not need to be as detailed as the proposal, but should include enough
15241563
information to express the idea and why it was not acceptable.
15251564
-->
1526-
Here are the alternative considerations compared to using the apply configurations introduced by Server Side Apply.
1565+
Here are the alternative considerations compared to using JSON Patch and the apply configurations introduced by Server Side Apply.
15271566

1528-
### Alternative 1: JSONPatch
1529-
1530-
Mutating Admission Webhooks express intended mutation in JSONPatch. Previous attempt by Andrew Sy Kim proposed a similar solution. However, because MutatingAdmissionPolicy, running inside the API server, no longer requires a remote call, modification can apply without serialization as JSONPatch.
1531-
- Pros:
1532-
- Same as the current way Mutation Webhook used
1533-
- Support most of the existing use cases
1534-
- Low learning curve while migration from Mutation Webhook
1535-
- Cons:
1536-
- No type checking
1537-
- Long JSON in declarative API
1538-
- Do not support types like `strategicMergePatch`, `JSONMergePatch` and `ApplyConfigurations`
1539-
- Low learning curve while setting up mutations from scratch
1540-
15411567
### Alternative 2: Introduce new syntax
15421568

15431569
Another alternative consideration would be rewriting your own merge algorithm which is a lot of duplicated effort.

0 commit comments

Comments
 (0)