skip to content

What does a Kubernetes AdmissionReview request contain, and what cluster information is missing from it?

level: juniorimportance: must knowfreq 70%

answer

  1. One request, not a cluster dump
  2. Who is asking travels with it
  3. A previous version, only sometimes
  4. A flag saying nothing will be stored
  5. Neighbouring objects never arrive

basics

~20 s

An AdmissionReview request carries one object under review, its previous version on updates, the requesting user and groups, the operation, resource and subresource, and a dryRun flag. It carries no other objects, no cluster state and no history.

solid answer

~40 s

The request half of an AdmissionReview is a single JSON document describing one API call. It has a `uid` for correlating the response, the `operation` (CREATE, UPDATE, DELETE or CONNECT), the `resource` and `subResource` being acted on, `namespace` and `name`, `userInfo` with the authenticated username, uid, groups and extra attributes, `object` (the incoming state), `oldObject` (the stored state, on updates and deletes), a `dryRun` boolean and the request `options`. What is structurally absent matters just as much: no other objects, no counts or quota usage, no neighbouring namespaces, no earlier revisions of this object. A webhook process can of course call the API server itself for those facts, but nothing about them arrives in the payload, and an expression-only policy evaluated inside the API server has no I/O at all.

go deeper

for a junior

Be ready to name the main fields out loud: operation, resource and subResource, name and namespace, userInfo, object, oldObject, dryRun. Then say the one sentence that matters most — it describes a single request, not the cluster.

for a middle

Explain which fields are filled in for which operation, and why the server sends a fixed, self-contained document rather than cluster context. Know that dryRun still runs admission and still surfaces a denial.

for a senior

Show the judgment: given a requirement, say immediately whether it is decidable from this payload or needs a fact that never arrives, and what that costs you. Interviewers are listening for whether you would notice before shipping the rule.

for a principal

Own the consequence for the platform: rules that need facts outside the payload turn a stateless check into a stateful service with its own availability and correctness story, so treat 'is this decidable from one request?' as a design gate, not an implementation detail.

## What the document is When the Kubernetes API server consults an admission policy about a request, it hands over an `AdmissionReview` object from the `admission.k8s.io/v1` group. It has two halves: a `request` the server fills in, and a `response` the policy fills in. Everything a rule may reason about lives in that `request` half. Reading it field by field, once, before writing any rule is the difference between a policy that works and one that quietly never fires. ## The fields | Field | What it holds | | --- | --- | | `uid` | An identifier for this one admission call; the response must echo it back so the server can match them. | | `kind`, `resource` | The group/version/kind and group/version/resource of what is being submitted. | | `subResource` | Empty for the main resource; otherwise the subresource, such as `status`, `scale` or `exec`. | | `requestKind`, `requestResource`, `requestSubResource` | What the client originally asked for, which can differ from the above when the server converts between equivalent API versions. | | `name`, `namespace` | The object's name and namespace. | | `operation` | `CREATE`, `UPDATE`, `DELETE` or `CONNECT`. | | `userInfo` | The authenticated identity: `username`, `uid`, `groups`, and an `extra` map of string lists. | | `object` | The incoming object. | | `oldObject` | The currently stored object. | | `dryRun` | True when the server will run the whole request path and then throw the result away. | | `options` | The operation's options object, for example the `DeleteOptions` sent with a delete. | `object` and `oldObject` are not both filled in on every call. On `CREATE` there is an object and no old one. On `UPDATE` both are present, so the rule can see exactly what changed. On `DELETE` it inverts: there is nothing incoming, so `object` is null and `oldObject` carries the thing about to disappear. `userInfo`, `operation`, `resource`, `name` and `namespace` are populated throughout. ## What is not there, and why that shapes rules The payload describes **one change**. It does not describe the cluster. Nothing in it tells you: - how many other Pods, Services or Ingresses exist, or what they are named; - whether another namespace already claims a hostname or an IP range; - what quota or capacity is currently consumed; - what this object looked like three revisions ago, or who edited it then; - what any controller will later do to the object. That is not an oversight; it is the point. Admission runs synchronously in the path of every matching write, so the server sends the smallest self-contained document that describes the decision, and sends the same shape to every policy. Any rule phrased as a statement about the cluster rather than about this request — "hostnames must be unique", "no more than ten of these per namespace" — is not answerable from the payload alone, and recognising that before you start writing is the whole skill this field tests. Two consequences follow. First, a webhook is a running process, so it *can* fetch extra facts itself, but that is a deliberate design step with its own cost and correctness questions, not something the payload gives you. Second, a policy expressed purely as expressions evaluated inside the API server — the CEL in a `ValidatingAdmissionPolicy`, for example — cannot make network calls at all, so for that style the payload really is the entire universe. ## Fields people misread `dryRun: true` does not mean the policy is skipped or that a denial is ignored. The full request path runs, admission included, and a denial is reported to the client exactly as usual; the server simply never persists the result. The practical rule is that the *decision* must be computed identically, while any external effect the webhook performs on the side — filing a ticket, incrementing a counter, writing to another system — must not happen for a request that will never exist. `userInfo` is the authenticated identity as the API server resolved it, not something the client asserts in the manifest. That makes it usable for exemptions, but it also means an exemption keyed on a username or group is exactly as strong as the controls over who can hold that identity. `name` can be empty on a `CREATE`. When a client submits an object with `generateName` instead of `metadata.name`, the final name is allocated after admission, so a rule that keys off the name must tolerate its absence rather than crash or, worse, silently produce no decision. Finally, the two halves are separate: the request tells you what happened, and nothing in it obliges any particular answer. Everything you conclude has to be derivable from these fields.

  • What is in userInfo, and why does an exemption keyed on it need care?
    It carries the authenticated `username`, a `uid`, the list of `groups` and an `extra` map, as resolved by authentication — not anything the client wrote into the manifest. That makes it trustworthy input for a rule. But an exemption written as "allow if the requester is in group X" is only as strong as the controls over who can end up in group X, so the exemption list becomes a security boundary you now have to govern.
  • What does dryRun: true mean for a policy?
    The API server runs the entire request path, admission included, and then discards the result instead of storing it. Your decision must be computed exactly as it would be for a real request — a denial still reaches the client. What must not happen is any persistent effect the policy makes outside the decision itself, such as recording a ticket or bumping a counter, for a request that will never exist.
  • On a CREATE that uses generateName, why can the request name be empty?
    The final name is allocated by the API server after admission has run, so a submission using `metadata.generateName` arrives with no `metadata.name` and an empty `name` in the request. A rule that reads the name for a prefix check or a message must handle the empty case explicitly; otherwise it either errors or produces no result, and no result is not a denial.

It is a single form handed through a window, not access to the filing cabinet: it says what is being submitted, by whom, and what the last version of that one form said.

saying these in an interview costs you the question

  • Assumes the payload lists other objects in the cluster
  • Thinks oldObject is populated on a CREATE
  • Reads dryRun as meaning admission is skipped
  • Believes earlier revisions of the object are included
  • Assumes metadata.name is always set on CREATE

context