docs: Updates examples to use some...in, add link to debugger (#8806)

Based on input from user here in slack:
https://openpolicyagent.slack.com/archives/C08V59T3NAF/p1781788845493869

This also makes some updates based on common topics in the new docs
chat. Some/every appears to be particularly confusing and the
installation instructions might be better if more prominent.

---------

Signed-off-by: Charlie Egan <charlie_egan@apple.com>
This commit is contained in:
Charlie Egan
2026-06-26 09:02:39 +01:00
committed by GitHub
parent 141fc51bd6
commit 8b59ff6e48
3 changed files with 109 additions and 93 deletions
-2
View File
@@ -5,8 +5,6 @@ title: Editor and IDE Support
OPA can be integrated into editors and IDEs to provide features like syntax highlighting, query
evaluation, policy coverage, and more.
## Integrations
| Editor | Link | Note |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| Visual Studio Code | [marketplace.visualstudio.com/items?itemName=tsandall.opa](https://marketplace.visualstudio.com/items?itemName=tsandall.opa) | Supports Language Server and Debug Adapter |
+80 -79
View File
@@ -9,15 +9,14 @@ OPA provides a high-level declarative language that lets you specify policy as
code and simple APIs to offload policy decision-making from your software. You
can use OPA to enforce policies in microservices, Kubernetes, CI/CD pipelines,
API gateways, and more.
OPA is proud to be a graduated
[Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/announcements/2021/02/04/cloud-native-computing-foundation-announces-open-policy-agent-graduation/)
project.
This page covers core concepts in OPA's policy language
([Rego](./docs/policy-language)) as well as how to download and run OPA.
## What is OPA?
> **Prefer to get hands on?** Checkout the
> [online playground](https://play.openpolicyagent.org)
> or the
> [CLI examples](#2-try-opa-eval) below.
OPA [decouples](./docs/philosophy#policy-decoupling) policy decision-making from policy
enforcement. When your software needs to make policy decisions it **queries**
@@ -30,14 +29,13 @@ import OverviewDiagram from './assets/OverviewDiagram';
OPA generates policy decisions by evaluating the query input against
policies and data. OPA and Rego are domain-agnostic so you can describe almost
any kind of invariant in your policies. For example:
any kind of rule in your policies. For example:
- Which users can access which resources.
- Which subnets egress traffic is allowed to.
- Which clusters a workload must be deployed to.
- Which registries binaries can be downloaded from.
- Which OS capabilities a container can execute with.
- Which times of day the system can be accessed at.
- What roles must a user have to access a particular resource
- Which network ranges egress traffic is allowed to
- Allowed registries to source container images
- When during the day the system can be accessed
- The System Calls a binary in a container can make
Policy decisions are not limited to simple yes/no or allow/deny answers. Like
query inputs, your policies can generate arbitrary structured data as output.
@@ -96,7 +94,7 @@ find servers that violate the policy.
The following section explores how OPA can help implement this policy.
## Writing Policy with Rego
## Writing Policies with Rego
OPA policies are expressed in a high-level declarative language called Rego.
Rego (pronounced "ray-go") is purpose-built for expressing policies over complex
@@ -295,72 +293,66 @@ This approach is problematic, there may be too many networks to list statically,
the number of networks may not be known in advance.
Like other declarative languages (e.g., SQL), iteration in Rego happens
implicitly when you inject variables into expressions. The solution for this
case is to substitute the array index with a variable:
implicitly. The solution for this case is to use `some ... in ...` to iterate
over the collection:
```rego
package servers
exists_public_network if {
some i
input.networks[i].public == true
some network in input.networks
network.public == true
}
```
<RunSnippet files="#input.json" command="data.servers.exists_public_network"/>
Now the query asks for values of `i` that make the overall expression true. When
you substitute variables in references, OPA automatically finds variable
assignments that satisfy all of the expressions in the query. Just like
intermediate variables, OPA returns the values of the variables.
OPA evaluates the rule body for each element bound to `network`. If any element
satisfies all expressions in the body, the rule is defined.
You can substitute as many variables as you want to do nested iteration. For
example, to find out if any servers expose the insecure `"http"` protocol you
could write:
```rego
package servers
http_server if {
some i, j
input.servers[i].protocols[j] == "http"
}
```
<RunSnippet files="#input.json" command="data.servers.http_server"/>
If variables appear multiple times the assignments satisfy all of the
expressions. For example, to find the ids of ports connected to public networks,
For example, to find out if any servers expose the insecure `"http"` protocol
you could write:
```rego
package servers
exposed_ports contains port_id if {
some i, j
port_id := input.ports[i].id
input.ports[i].network == input.networks[j].id
input.networks[j].public
http_server if {
some server in input.servers
"http" in server.protocols
}
```
<RunSnippet files="#input.json" command="data.servers.http_server"/>
Or perhaps to find the IDs of ports connected to public networks:
```rego
package servers
exposed_ports contains port.id if {
some port in input.ports
some network in input.networks
port.network == network.id
network.public == true
}
```
<RunSnippet files="#input.json" command="data.servers.exposed_ports"/>
Just like references that refer to non-existent fields or expressions that fail
to match, if OPA is unable to find any variable assignments that satisfy all of
the expressions, the result is undefined.
Just like references that refer to non-existent fields or expressions that fail to match,
if OPA is unable to find matches that satisfy all of the expressions, the result is undefined.
```rego
package servers
ssh_server if {
some i
# there is no assignment of i that satisfies the expression
input.servers[i].protocols[i] == "ssh"
email_server if {
some server in input.servers
# no servers have this protocol
"imap" in server.protocols
}
```
<RunSnippet files="#input.json" command="data.servers.ssh_server"/>
<RunSnippet files="#input.json" command="data.servers.email_server"/>
#### FOR SOME and FOR ALL
@@ -373,7 +365,7 @@ to express _FOR SOME_ and _FOR ALL_ more explicitly.
and will bind its variables (key, value position) to the collection items.
It introduces new bindings to the evaluation of the rest of the rule body.
Using `some`, the rules introduced above can be expressed in different ways:
Using `some`, the iteration patterns introduced above can be used to build named rules:
```rego
package servers
@@ -456,8 +448,8 @@ package rules
# head
exists_public_network := true if {
# body
some net in input.networks # some network exists and..
net.public # it is public.
some network in input.networks # some network exists and..
network.public == true # it is public.
}
```
@@ -469,8 +461,8 @@ You can query for the value generated by rules just like any other value (such a
package rules
exists_public_network := true if {
some net in input.networks # some network exists and..
net.public # it is public.
some network in input.networks # some network exists and..
network.public == true # it is public.
}
another_rule := {
@@ -504,8 +496,8 @@ You could rewrite the example above as follows without changing the meaning:
```rego
exists_public_network if {
some net in input.networks
net.public
some network in input.networks
network.public == true
}
```
@@ -546,8 +538,8 @@ not the same as false.) Below, OPA is given a different set of input networks
package rules
exists_public_network if {
some net in input.networks
net.public
some network in input.networks
network.public == true
}
```
@@ -641,11 +633,13 @@ package example.logical_or
default shell_accessible := false
shell_accessible if {
input.servers[_].protocols[_] == "telnet"
some server in input.servers
"telnet" in server.protocols
}
shell_accessible if {
input.servers[_].protocols[_] == "ssh"
some server in input.servers
"ssh" in server.protocols
}
```
@@ -665,13 +659,13 @@ could be modified to generate a set of servers that expose `"telnet"` or
package example.logical_or
shell_accessible contains server.id if {
server := input.servers[_]
server.protocols[_] == "telnet"
some server in input.servers
"telnet" in server.protocols
}
shell_accessible contains server.id if {
server := input.servers[_]
server.protocols[_] == "ssh"
some server in input.servers
"ssh" in server.protocols
}
```
@@ -716,9 +710,9 @@ violation contains message if { # a server is in the violation set if...
public_servers contains server if { # a server exists in the public_servers set if...
some server in input.servers # it exists in the input.servers collection and...
some port in server.ports # it references a port in the input.ports collection and...
some port in server.ports # it has a port...
some input_port in input.ports
port == input_port.id
port == input_port.id # which is referenced in the list of ports
# the port references a network in the input.networks collection and...
some input_network in input.networks
@@ -896,7 +890,7 @@ shasum -c $BINARY_NAME.sha256
### 2. Try `opa eval`
The simplest way to interact with OPA is via the command-line using the [`opa eval` sub-command](./docs/cli#eval).
A simple way to interact with OPA is via the command-line using the [`opa eval` sub-command](./docs/cli#eval).
It can be used to evaluate arbitrary Rego expressions and policies.
`opa eval` supports a large number of options for controlling evaluation.
Commonly used flags include:
@@ -944,22 +938,23 @@ allow if { # allow is true if...
}
violation contains server.id if { # a server is in the violation set if...
some server
public_servers[server] # it exists in the 'public_servers' set and...
server.protocols[_] == "http" # it contains the insecure "http" protocol.
some server in public_servers # it exists in the 'public_servers' set and...
"http" in server.protocols # it contains the insecure "http" protocol.
}
violation contains server.id if { # a server is in the violation set if...
server := input.servers[_] # it exists in the input.servers collection and...
server.protocols[_] == "telnet" # it contains the "telnet" protocol.
some server in input.servers # it exists in the input.servers collection and...
"telnet" in server.protocols # it contains the "telnet" protocol.
}
public_servers contains server if { # a server exists in the 'public_servers' set if...
some i, j
server := input.servers[_] # it exists in the input.servers collection and...
server.ports[_] == input.ports[i].id # it references a port in the input.ports collection and...
input.ports[i].network == input.networks[j].id # the port references a network in the input.networks collection and...
input.networks[j].public # the network is public.
some server in input.servers # it exists in the input.servers collection and...
some port in server.ports # it references a port in the input.ports collection and...
some input_port in input.ports
port == input_port.id
some input_network in input.networks # the port references a network in the input.networks collection and...
input_port.network == input_network.id # the network is public.
input_network.public
}
```
@@ -975,6 +970,12 @@ public_servers contains server if { # a server exists in the 'p
echo $?
```
:::tip
Looking for the IDE experience? Rego and OPA have mature developer tooling
with support for live evaluation and debugging in
[editors and IDEs](./docs/editor-and-ide-support)
:::
### 3. Try `opa run` (interactive)
OPA includes an interactive shell or REPL (Read-Eval-Print-Loop) accessible via
+29 -12
View File
@@ -1735,10 +1735,24 @@ deny contains msg if { msg := "forbidden" }
## Some Keyword
The `some` keyword allows queries to explicitly declare local variables. Use the
`some` keyword in rules that contain unification statements or references with
variable operands **if** variables contained in those statements are not
declared using `:=` .
The `some` keyword in Rego can be used in both the `some ... in` form
or in a standalone way to declare free variables. Both forms are used in rules
to check if a solution to the rule exists. For examples, here a rule checks a
user's roles for admin:
```rego
allow if {
some role in input.user.roles
role.id == "admin"
}
```
`some` can also be used to declare variables upfront in a rule, without
binding a value. During evaluation, Rego will search to see if a solution exists
for the rule while adhering to the use of the variables as constraints.
This is useful if the rule contains unification statements or
references with variable operands (if variables contained in those
statements are not declared using the assignment operator `:=`).
| Statement | Example | Variables |
| -------------------------------- | -------------------------------- | ----------- |
@@ -1781,11 +1795,17 @@ The `some` keyword is not required but it's recommended to avoid situations like
the one above where introduction of a rule inside a package could change
behaviour of other rules.
For using the `some` keyword with iteration, see
More details on the `some ... in` form can be found in
[the documentation of the `in` operator](#membership-and-iteration-in).
## Every Keyword
The `every` keyword allows policy authors to express 'For All' constraints
in their rules in a readable way.
The keyword takes a key argument (optional) and value argument to be used for
further checks, a domain to select items from, and a block of further
statements to check (the "body").
```rego
package example
@@ -1803,21 +1823,16 @@ names_with_dev if {
<RunSnippet files="#example_data.rego" command="data.example.names_with_dev"/>
The `every` keyword takes an (optional) key argument, a value argument, a domain, and a
block of further queries, its "body".
The keyword is used to explicitly assert that its body is true for _any element in the domain_.
It will iterate over the domain, bind its variables, and check that the body holds
for those bindings.
If one of the bindings does not yield a successful evaluation of the body, the overall
statement is undefined.
If the domain is empty, the overall statement is true.
Evaluating `every` does **not** introduce new bindings into the rule evaluation.
Used with a key argument, the index, or property name (for objects), comes into the
scope of the body evaluation:
Used with the optional key argument, the index, or property name (for objects),
comes into the scope of the body evaluation:
```rego
package example
@@ -1840,8 +1855,10 @@ set_domain if {
<RunSnippet command="data.example"/>
:::info
Negating `every` is forbidden. If you need to express `not every x in xs { p(x) }`
please use `some x in xs; not p(x)` instead.
:::
## With Keyword