When does a Kyverno mutate rule need patchesJson6902 instead of patchStrategicMerge?
answer
- merge by field versus merge by position
- one of them can delete
- containers match on name
- the dash means append
- add does not create the parent
basics
~20 sReach for patchesJson6902 when you must remove an element, append to an unkeyed list, or patch a custom resource. Strategic merge stays the default because it merges by field, matching a container by name rather than by index.
solid answer
~50 s`patchStrategicMerge` is a partial object that Kyverno merges into the incoming resource; for well-known Kubernetes lists it merges by the list's merge key, so a container is matched by its `name` rather than by its position. That makes it declarative and resilient to a team reordering their containers. `patchesJson6902` is an RFC 6902 JSON Patch: an ordered list of `add`, `remove`, `replace`, `move`, `copy` and `test` operations addressed by JSON Pointer, so `/spec/containers/0/env/-` means "append to the env list of whichever container is first". You need it when strategic merge cannot say what you mean: deleting an entry, appending to an unkeyed list, or patching a custom resource where no merge key is registered. Its cost is index arithmetic that breaks when the list changes, and an `add` whose parent path does not exist fails rather than creating it.
code
yaml · 8 linesmutate:
patchesJson6902: |-
- op: add
path: "/spec/template/spec/containers/0/env/-"
value:
name: BACKUP_CLASS
value: gold
...go deeper
Know that Kyverno offers two ways to write a patch, and that the strategic-merge one looks like a small slice of the resource itself. Be able to point at which is which in a policy file.
Explain the mechanics both ways: merge key versus JSON Pointer, the operations JSON Patch offers, the meaning of the trailing dash, and why add refuses to create a missing parent.
Justify a default and its exceptions, and say how you would keep an index-addressed patch from landing on an injected sidecar in someone else's namespace.
Frame it as a maintainability call: which dialect your policy library standardises on, what review rule you apply to index paths, and how you test patches against real manifests before they reach a cluster.
## Two dialects, one job A Kyverno `mutate` rule can express its change in either of two patch dialects, and interviewers ask which you would pick because the wrong one produces a rule that works on your test manifest and misbehaves on everyone else's. ### patchStrategicMerge You write a *partial object* in the shape of the resource: ``` spec: template: spec: containers: - (name): "*" imagePullPolicy: Always ``` Kyverno merges this into the incoming object. Fields you do not mention are untouched. Two properties make it the default choice: - **It merges by field, not by position.** For the built-in Kubernetes types, lists carry a declared merge key — for containers that key is `name` — so the patch finds the right element even if the author reordered their containers or added one. - **It supports Kyverno's anchors**, so you can write add-if-not-present (`+()`) defaults and conditional (`()`) guards inside the patch itself. ### patchesJson6902 This is a standard JSON Patch: an ordered sequence of operations, each with an `op`, a `path` written as a JSON Pointer, and usually a `value`. ``` - op: add path: "/metadata/annotations/backup.example.com~1class" value: "gold" - op: remove path: "/spec/containers/1" ``` The operations available are `add`, `remove`, `replace`, `move`, `copy` and `test`. Positions in arrays are numeric indexes, and the special segment `-` means "the end of the array", so `add` to `/spec/containers/0/env/-` appends an env var. `/` and `~` inside a key are escaped as `~1` and `~0`, which is why annotation paths look strange. ## When strategic merge is not enough - **Removal.** A strategic merge patch is additive in spirit; JSON Patch has an explicit `remove` operation, and deleting a field or a list element is its natural job. - **Unkeyed lists.** Lists of plain strings — `args`, `command`, a list of hostnames — have no merge key, so there is no field by which to match an element. Appending with `/-` is unambiguous where a merge is not. - **Custom resources.** Merge-key metadata comes from the built-in API types. A CRD's lists have no registered merge key, so list handling under a strategic merge is not something you should rely on; an explicit pointer path is predictable. - **Order-sensitive edits.** JSON Patch operations run in sequence and can `test` before they act, which strategic merge has no vocabulary for. ## The costs you must name **Index arithmetic is brittle.** `/spec/containers/0` is whichever container happens to be first. A team that adds a sidecar, or a tool that injects one, silently moves your target. Strategic merge with a name match does not have this failure mode, which is why you should prefer it whenever it can express the change. **`add` does not create parents.** In JSON Patch, adding to `/spec/template/spec/containers/0/env/-` requires `env` to already exist on that container. If the container defines no environment variables, the operation fails to apply and the rule reports an error rather than quietly doing nothing useful. The usual fixes are a precondition that checks the path exists, a first operation that creates the empty list, or moving the change to a strategic merge patch that will build the path for you. **Escaping bites.** Annotation and label keys contain `/` and `.`; the slash must be written `~1` in the pointer. A patch that appears to do nothing is often a mis-escaped path. ## How to answer the question The strong answer is a default plus a list of exceptions: strategic merge unless you need removal, an append to an unkeyed list, a custom resource, or ordered operations — and when you do reach for JSON Patch, say out loud what you are doing about the index fragility and the missing-parent failure. The weak answer treats the two as interchangeable styles, or claims JSON Patch is "more powerful" without naming what it gives up. A last practical note: both dialects can carry Kyverno variables, so either patch can compute its value from the request. The choice between them is about *addressing* the field, not about how dynamic the value is.
- Why is /spec/containers/0 considered fragile in a cluster-wide policy?Because index zero is whichever container happens to be listed first, and that is not a stable identity. A team reordering their manifest, or a sidecar injected by another controller, moves your target to a different container — so the patch lands on the wrong one instead of failing loudly. A strategic merge patch that matches on the container's `name` does not have that failure mode.
- A JSON 6902 add op targets a container's env list, but that container declares no env. What happens?The operation fails: JSON Patch `add` requires the parent location to exist, and it does not create it for you. The rule reports an error rather than silently patching nothing. Fix it with a precondition that checks the path, an earlier operation that creates the empty list, or a strategic merge patch, which builds the intermediate structure as part of the merge.
- Can you still use Kyverno anchors inside a patchesJson6902 patch?No — anchors such as `+()` and `()` belong to Kyverno's strategic-merge and validate pattern syntax. JSON Patch has its own conditional vocabulary: the `test` operation, which makes the rest of the patch fail if the value at a path is not what you expect. If you want add-if-not-present semantics, either use strategic merge or guard the rule with a precondition.
saying these in an interview costs you the question
- Treats the two dialects as interchangeable styles
- Assumes JSON Patch add creates missing parent paths
- Thinks strategic merge matches list elements by index
- Uses index paths in a cluster-wide policy without mentioning sidecars
- Claims strategic merge can delete a list element cleanly