A Gatekeeper constraint passes its gator suite but never fires on Deployments — how do you catch that offline?
answer
- nobody applies a bare Pod
- the workload controller renders the real object
- render the child, then evaluate it
- one template names parent and generated kind
- green suite, zero production matches
basics
~20 sUse gator expand with an ExpansionTemplate: it renders the Pod a Deployment, Job or CronJob would produce, and gator test and verify then review that generated Pod. The false green is a Pod rule tested only on bare Pods.
solid answer
~40 sThe suite was green because every fixture was a bare Pod, and nobody in your organisation applies a bare Pod — they apply Deployments, Jobs and CronJobs whose pod template holds the offending field. Gatekeeper's expansion feature closes that gap offline. You add an `ExpansionTemplate` describing which parent kind produces which child GVK and where its pod template lives (`applyTo`, `templateSource: spec.template`, `generatedGVK`). `gator expand` then prints the Pod a given Deployment would produce, and when an `ExpansionTemplate` is part of the input, `gator test` and `gator verify` evaluate those generated resources as well as the originals. So you replace the bare-Pod fixtures with the workload manifests your developers actually write, and the suite proves the rule fires on the object they will submit — before the constraint ever reaches a cluster.
code
yaml · 14 linesapiVersion: expansion.gatekeeper.sh/v1alpha1
kind: ExpansionTemplate
metadata:
name: expand-deployments
spec:
applyTo:
- groups: ["apps"]
versions: ["v1"]
kinds: ["Deployment"]
templateSource: "spec.template"
generatedGVK:
group: ""
version: "v1"
kind: "Pod"go deeper
Remember that the manifest a developer applies is usually a Deployment or CronJob, not a Pod, and that a rule written against Pods is not automatically looking at what they submitted.
Explain what an ExpansionTemplate declares and how a rendered child resource gets reviewed offline, and be able to say where a Deployment's pod template sits versus a CronJob's.
Diagnose the silent no-match: a green suite whose fixtures were never the real documents. Show how you would rebuild the fixture set around real workload manifests and prove the rule fires before the constraint reaches a cluster.
Speak to the risk of a control that reports as enforced while matching nothing. Decide what evidence a rule must produce before it counts as coverage on any dashboard your organisation shows an auditor.
## The failure being described A rule requires every Pod to declare a readiness probe. The `ConstraintTemplate` compiles, the `Constraint` matches `kinds: ["Pod"]`, the suite has an allow case and a deny case, and both pass. It ships. Weeks later someone notices that no deploy has ever been blocked, and that plenty of workloads have no readiness probe at all. The suite was honest about what it tested and dishonest about what that meant. Every fixture was a hand-written bare Pod. Nobody in the organisation applies a bare Pod; they apply Deployments, Jobs and CronJobs, and the container spec that violates the rule lives inside `spec.template.spec` of a workload controller, not at the top level of a Pod. What happens in a real cluster is worth being precise about, because it is the part candidates most often get backwards. A Pod-matching constraint does still see controller-created Pods: when the ReplicaSet controller creates the Pod, that creation goes through admission like any other request, and the Pod is rejected. But the developer's `kubectl apply` of the Deployment succeeded. They see a green apply and a Deployment that never becomes ready, with the rejection buried in the ReplicaSet's events. Enforcement of a sort happened; useful feedback did not. Either way, your suite never told you which of those two worlds you were in. ## What expansion does Gatekeeper's expansion feature lets the engine reason about the resource a workload controller *would* generate. You declare it with an `ExpansionTemplate` (`expansion.gatekeeper.sh/v1alpha1`), which says three things: - **`applyTo`** — the parent groups, versions and kinds it applies to, for example group `apps`, version `v1`, kind `Deployment`. - **`templateSource`** — the path within the parent where the child's template lives, typically `spec.template`. - **`generatedGVK`** — the group, version and kind of the resource that path produces, for example core `v1` `Pod`. `gator expand` takes your manifests plus that template and prints the generated resources — the Pod a given Deployment would create. That output alone is diagnostic: it shows you, field by field, the object your rule will actually be handed. More importantly, when an `ExpansionTemplate` is part of the input to `gator test` or `gator verify`, the generated resources are reviewed as well as the originals. So a case whose `object` is a Deployment can be asserted to produce a violation from a Pod-matching rule, entirely offline. ## How that changes the suite The fix is not to add cases; it is to change what the fixtures are made of. Replace hand-written bare Pods with the workload manifests your developers write — a Deployment, a CronJob — plus the `ExpansionTemplate` that tells the engine how each one renders. Then: - The deny case proves the rule fires on the manifest a human will actually submit. - The allow case proves a compliant Deployment is not flagged. - A CronJob case is worth keeping separately, because a CronJob nests its pod template one level deeper than a Deployment does, and a `templateSource` path that is right for one is wrong for the other. Run `gator expand` by hand the first time you write a rule for a new parent kind. Seeing the rendered child is how you find out that the path you guessed does not exist and that your rule has been evaluating an empty object. ## The general lesson A policy suite tests two things at once: whether the rule *decides* correctly, and whether it *matches* anything. Rule-level tests cover the first well and the second not at all, because you hand the rule an input shaped exactly the way you assumed. Matching bugs — a wrong kind, an unmatched group, a pod template you never looked at — only show up when the fixture is the real document a developer submits. That is why the highest-value fixtures for a constraint are the workload manifests your organisation actually deploys, and why expansion, which turns those manifests into the objects the rule will be handed, is the piece that makes the offline suite trustworthy. The outcome this prevents is the worst outcome available to a guardrail: not a rule that blocks the wrong thing, which someone will complain about within the hour, but a rule that blocks nothing, passes every test, appears on a compliance dashboard as enforced, and is discovered only when the thing it was supposed to prevent happens.
- What exactly does an ExpansionTemplate have to declare?Three things: `applyTo`, the parent groups, versions and kinds it covers; `templateSource`, the path inside the parent that holds the child's template, usually `spec.template`; and `generatedGVK`, the group, version and kind of the resource that path produces. Get `templateSource` wrong and you expand an empty object, which is why running the expansion by hand once is worth the minute.
- Do gator test and gator verify use expansion, or only gator expand?Both use it. When an `ExpansionTemplate` is part of the input set, the generated resources are reviewed alongside the originals, so a case whose object file is a Deployment can assert a violation from a Pod-matching rule. `gator expand` on its own is the diagnostic view — it prints the generated resource so you can look at it.
- In a real cluster, is a Deployment rejected when the Pod it would create violates a Pod-matching rule?No. The Deployment is admitted; the Pod is rejected later, at the admission of the ReplicaSet controller's create request. The developer sees a successful apply and a workload that never becomes ready, with the real reason in the ReplicaSet's events rather than in their terminal.
Proofreading one letter tells you nothing about the mail merge; you have to print a page from the real template first.
saying these in an interview costs you the question
- Assumes a Pod-matching rule also inspects a Deployment's pod template
- Believes the Deployment itself is rejected at apply time
- Claims controller-created Pods skip admission entirely
- Fixes a matching gap by testing against a scratch cluster instead
- Writes every fixture as a hand-authored bare Pod