Declarative Policy, Not Imperative Code
Rego (pronounced "ray-go") is OPA's purpose-built query language. It is fundamentally different from languages like Python or Go. You don't write step-by-step instructions ("first do this, then check that, then return this"). Instead, you write declarations about what a violation looks like. The OPA engine figures out how to evaluate them.
The mental model: instead of writing "IF label is missing THEN deny", you write "A violation EXISTS WHEN a label is missing." The engine evaluates whether that condition is true for the given input.
The Input Object
When Gatekeeper calls your Rego, it provides a structured JSON object called input. The most important fields are:
input.review.object— the full Kubernetes resource being created/updated (the YAML you applied, as JSON).input.parameters— the values from theparametersfield of the matching Constraint.
A Complete Rego Example: Required Labels
package k8srequiredlabels
# The 'violation' rule is special — Gatekeeper looks for this rule.
# If it produces any results, the request is denied.
violation[{"msg": msg}] {
# Step 1: Extract the set of labels actually present on the resource.
# The '|' syntax is a set comprehension — like a set() in Python.
provided := {label | input.review.object.metadata.labels[label]}
# Step 2: Extract the set of required labels from the Constraint's parameters.
# input.parameters.labels is an array; '_' iterates over it.
required := {label | label := input.parameters.labels[_]}
# Step 3: Set subtraction — find labels that are required but not provided.
missing := required - provided
# Step 4: If 'missing' is non-empty, this block evaluates to true,
# and the violation is triggered with a formatted message.
count(missing) > 0
msg := sprintf("Resource is missing required labels: %v", [missing])
}
Key insight: Every statement inside the violation block is an implicit AND. All conditions must be true for the violation to fire. If any single condition is false (e.g., count(missing) > 0 is false because no labels are missing), the entire block short-circuits and no violation is produced.
The ConstraintTemplate YAML That Wraps This Rego
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("Resource is missing required labels: %v", [missing])
}