In Rego, how do you require that every container in a list has CPU and memory limits?
answer
- plain iteration only says at least one
- all means no counterexample exists
- negation cannot introduce a binding
- the keyword takes a body in braces
- an empty list passes vacuously
basics
~20 sUse the every keyword: every c in the container list, the body asserting the limits exist. The alternative is to find a counterexample with iteration and negate it. Plain iteration cannot express this, because an unbound variable means at least one element.
solid answer
~50 sWrite `every c in input.spec.template.spec.containers { c.resources.limits.cpu; c.resources.limits.memory }`. The `every` expression succeeds only if the body holds for all elements, which is exactly the assertion plain iteration cannot make — a bare `containers[_].resources.limits` is existential. The older equivalent is to define a helper that finds a counterexample (a container whose limits are missing) and then negate it with `not`, because "all" is "there is no element that fails". What you cannot do is inline the negation: `not containers[c].resources.limits` with `c` bound nowhere else is rejected at compile time as an unsafe variable, since `not` never binds. Two edge cases matter for a gate: `every` over an **empty** collection is vacuously true, and if the collection reference itself is missing the whole expression is undefined — either way nothing is reported, so an all-containers check needs a separate assertion that the list exists.
go deeper
Recall that Rego has an every keyword for all-elements assertions and that a bare wildcard reference means at least one. Being able to write the every form is enough at this level.
Explain both spellings — the quantifier and the negated counterexample helper — and why a negated expression cannot bind a variable, which is what the unsafe-variable compile error is telling you.
Show the operational judgment: vacuous truth on an empty list and an undefined collection reference both mean no violation is reported, so state how your rule proves the list it is quantifying over actually exists.
Own the pattern choice across a rule set — offender-collecting helpers give actionable messages and reusable data, while bare quantifiers give a yes or no; decide which one your denial payloads and any future auto-remediation are built on.
"All elements satisfy X" is the assertion a resource-limits gate actually needs, and it is the one thing Rego's default iteration does not give you. There are two correct spellings and one that does not compile. ## 1. The `every` keyword ``` every c in input.spec.template.spec.containers { c.resources.limits.cpu c.resources.limits.memory } ``` `every x in collection { body }` is defined and true when the body succeeds for every element, and fails when any element makes the body fail or leaves it undefined. The key-and-value form `every k, v in obj { ... }` is available for objects. Inside the braces you write ordinary body expressions — a bare reference such as `c.resources.limits.cpu` succeeds if that path exists and its value is not `false`, which is the idiomatic way to say "this field is present". ## 2. Negate a counterexample Before `every` existed this was the only way, and it is still worth knowing because it is how `every` behaves underneath and because the helper it forces you to write is often useful on its own: ``` unlimited contains c.name if { some c in input.spec.template.spec.containers not c.resources.limits } allow if { count(unlimited) == 0 } ``` The helper uses existential iteration to find offenders; the caller asserts there are none. Note that the set-valued helper is always defined (possibly empty), so `count(...) == 0` is safe, whereas `not unlimited` would be testing a set that is empty-but-defined and would therefore fail. ## 3. The version that does not compile ``` allow if { not input.spec.template.spec.containers[c].resources.limits } ``` OPA rejects this: `var c is unsafe`. Safety in Rego requires every variable in a rule to be bound by a **non-negated** expression somewhere in the body. `not` does not produce bindings — it succeeds when its inner expression is undefined or false, and there is nothing to bind in that case. This is not a linter opinion; the rule would be meaningless without it, because there is no set of bindings to enumerate. The same error appears when a variable occurs only in the rule head, or only inside a `not` over a comprehension. ## The two edge cases that decide whether the gate is real **Empty collection.** `every c in [] { ... }` is **true** — vacuous truth, the same convention as "for all" in logic. A template that renders a workload with an empty container list therefore passes an all-containers check. **Missing collection.** If the reference to the collection is itself undefined — for example the pod spec has no `initContainers` field at all — then the `every` expression is undefined, and a body containing it does not succeed. Whether that reads as pass or fail depends entirely on which side of the rule it sits: in an `allow` rule the allow simply does not fire; in a deny rule written as "deny if not every ..." the deny does fire, which is a very different outcome from the empty-list case. So an honest all-containers rule usually pairs the quantifier with an existence check on the list, and treats regular, init and ephemeral container lists deliberately rather than assuming all three are present. ## Choosing between the two forms `every` reads better and keeps the assertion local, so it is the default for a single check. The counterexample helper wins when you need the **names** of the offenders — for a message, a report, or a payload — because `every` tells you only that something failed, not which element. In practice a mature rule set often has both: a helper set that collects offenders, used for the message, and no `every` at all, since `count(offenders) == 0` already says "all of them are fine" and carries the detail with it. ## What an interviewer is listening for That you know iteration is existential and needs an explicit quantifier; that `not` cannot bind, so the negated form needs a helper; and that vacuous truth on an empty list is a real gap in a gate rather than a language trivia point.
- Why does OPA reject a body whose only mention of a variable is inside a not expression?Safety: every variable must be bound by a non-negated expression. `not` succeeds when its inner expression is undefined or false, so it produces no bindings and the evaluator would have no set of values to enumerate. Bind the variable in a helper rule that finds counterexamples, then negate the helper's result instead.
- What does every return over an empty collection, and why does that matter for a gate?It is vacuously true, so an all-elements check passes on an empty list. For a workload gate that means a rendered template with no containers in the list sails through. If emptiness is itself suspicious, assert the list is non-empty alongside the quantifier rather than assuming the quantifier covers it.
- You need the names of the containers that failed. Does every give you that?No — `every` yields only success or failure, with no witness for the failing element. Collect offenders with an existential helper rule or a comprehension, then assert the collection is empty. That gives you the same guarantee plus the list you need for an actionable message.
saying these in an interview costs you the question
- Writes containers[_] and calls it an all-containers check
- Inlines not over a variable nothing else binds
- Assumes every reports which element failed
- Thinks every over an empty list fails
- Confuses a missing collection with an empty one