skip to content

In a Kyverno mutate rule, what does the +() add-if-not-present anchor do?

level: juniorimportance: must knowfreq 64%

answer

  1. default versus mandate, one character
  2. the patch yields to what is there
  3. absent field only
  4. plain key overwrites, +() does not
  5. add-if-not-present anchor

basics

~20 s

The +() anchor in a Kyverno strategic-merge mutate patch sets a field only when the incoming object does not already have it. If the field is already present, the existing value is left alone rather than overwritten.

solid answer

~50 s

In `patchStrategicMerge`, a plain key is an assignment: whatever value you write replaces whatever the submitted object had. Wrapping the key in `+()` turns it into add-if-not-present, so `+(automountServiceAccountToken): false` sets the field on Pods that omit it and leaves a Pod that deliberately set `true` untouched. That distinction is the whole difference between a platform default and a platform mandate. Anchors live in the patch body, not in the `match` block, and Kyverno has others with different meanings — notably the conditional anchor `()`, which does not write anything at all: it gates whether its sibling keys apply based on whether the anchored value matches. A weak default is usually written with `+()`; if you genuinely need the value forced, you use a plain key and accept that you are overwriting somebody's deliberate choice.

code

yaml · 11 lines
yaml
rules:
  - name: default-automount-off
    match:
      any:
        - resources:
            kinds:
              - Pod
    mutate:
      patchStrategicMerge:
        spec:
          +(automountServiceAccountToken): false

go deeper

for a junior

Recall the one-line rule: plain key sets the field no matter what, +() sets it only when the object left it out. Be able to read a short patch aloud and say which Pods it changes.

for a middle

Explain the merge mechanics: the patch is a partial object, anchors live in the patch body rather than the match block, and an absent field is a different state from an explicit false.

for a senior

Show judgment about when a default should yield and when a baseline should override, and say what trail the override leaves for the team whose value you replaced.

for a principal

Own the posture question: which security fields your platform defaults and which it insists on, and how you keep that list small enough that teams still trust the engine.

## What a mutate rule is doing Kyverno is a Kubernetes policy engine whose rules run as admission plugins: when someone submits a Pod, Deployment or PersistentVolumeClaim, Kyverno sees the object before it is persisted. A `validate` rule answers yes or no. A **`mutate` rule rewrites the object** — the thing stored in etcd is not quite the thing the author submitted. The most common way to express that rewrite is `mutate.patchStrategicMerge`, which is a *partial object*: you write the shape of the fields you care about and Kyverno merges it into the incoming object. Nothing you leave out is touched. ## Plain key versus anchor A plain key in a strategic-merge patch is an assignment. ``` spec: automountServiceAccountToken: false ``` This sets the field to `false` on every matching Pod, including the one whose author deliberately wrote `true` because their workload calls the API server. The team's value disappears, and unless someone reads the mutation record on the object, nobody notices until the pod fails. Wrapping the key in `+()` changes the semantics to **add if not present**: ``` spec: +(automountServiceAccountToken): false ``` Now the patch applies only where the field is absent. A Pod that already says `true` keeps `true`; a Pod that says `false` keeps `false`; a Pod that says nothing gets `false`. This is the correct spelling of a *default*. ## The anchor is not the match block Anchors are a feature of the patch (and, in validate rules, of the pattern) — they are not selectors. Deciding *which* objects a rule applies to is the job of `match` and `exclude`. Deciding *what happens to a particular field within* an object that already matched is the job of the anchor. Candidates who put the anchor logic in `match` end up with a rule that skips whole objects when they only meant to skip one field. ## The other anchor you will be asked to distinguish The conditional anchor `()` writes nothing. It says: if the value at this key matches what I wrote, apply the sibling keys in this map; otherwise skip this subtree. It is how you express "for containers whose name is X, set Y", and inside lists it is also how you address elements at all — for example a `(name): "*"` conditional anchor inside the containers list applies the sibling keys to every container. Confusing `()` with `+()` is the classic mix-up: one is a guard, the other is a write that yields to an existing value. ## Why interviewers care The `+()` anchor is where a policy team's posture becomes visible in one character. `+(...)` says "I am filling a gap you left". A plain key says "I am overriding you". Both are legitimate — a security baseline sometimes must win — but they have very different consequences for the workload owner who later diffs the running object against the manifest in their repository and finds a field they never typed. If you take the overwrite path, you owe that owner an obvious trail: Kyverno records what it changed in the `policies.kyverno.io/last-applied-patches` annotation on the mutated object, and emits events and policy report entries naming the policy and rule. ## Practical notes - `+()` composes with nesting: you can anchor a leaf several levels down, and the parent path is created if the merge needs it. - A default expressed with `+()` is stable if the same rule sees the object again — it has nothing to add the second time. - Setting a boolean to `false` with `+()` is a common source of confusion because `false` looks like "unset"; it is not. An absent field and an explicit `false` are different states, and only the absent one is patched. - If you find yourself wanting `+()` *and* a hard guarantee that the value is `false`, you want two rules: a mutate that defaults it and a validate that rejects an explicit `true`. Mutation alone is never a guarantee, because the author can always set the value themselves.

  • The Pod already sets automountServiceAccountToken to true. What is stored with a +() patch, and what with a plain key?
    With `+(automountServiceAccountToken): false` the stored object keeps `true` — the anchor only writes when the field is absent. With a plain `automountServiceAccountToken: false` the stored object becomes `false`; the author's value is replaced and the request is still admitted, so they get no error. That silent replacement is exactly why the mutation record on the object matters.
  • How is the conditional anchor () different from +()?
    `()` never writes. It is a guard: if the value at that key matches, Kyverno applies the sibling keys in the same map; if it does not match, that subtree is skipped entirely. `+()` is a write that stands down when the field already exists. So `()` selects where the patch applies, `+()` decides whether one field is filled in.
  • If a rule defaults a field with +(), can you still claim the field is guaranteed cluster-wide?
    No. Add-if-not-present explicitly defers to whatever the author set, so any team can opt out by setting the value themselves. If you need a guarantee you pair the mutate rule with a validate rule that rejects the disallowed value, or you drop the anchor and accept that you are overriding people. Mutation is a default; enforcement is a separate rule.

A plain key is a landlord repainting your flat whatever colour you chose; +() is one who paints only the walls you left bare.

saying these in an interview costs you the question

  • Says +() overwrites the existing value
  • Confuses the +() write with the () conditional guard
  • Puts anchor logic in the match block instead of the patch
  • Claims a mutate default guarantees the field cluster-wide
  • Treats an explicit false as the same state as an absent field

context