Need help with fregot?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

fugue
157 Stars 4 Forks Apache License 2.0 365 Commits 9 Opened issues

Description

Fugue Rego Toolkit

Services available

!
?

Need anything else?

Contributors list

fregot

fregot
(Fugue Rego Toolkit) is a set of tools for working with the Rego policy language, which is part of the Open Policy Agent (OPA) policy engine.
fregot
allows you to easily evaluate expressions, debug code, test policies, and more.

Demo: debugging with fregot

(Check out the text-based demo on asciinema so you can copy/paste commands.)

fregot
can be seen an alternative REPL to OPA's built-in interpreter. The goals are a little different -- whereas the OPA agent provides general-purpose components and functionality that are particularly useful with Kubernetes,
fregot
was developed internally at Fugue as a lightweight set of tools to enhance the Rego development experience. It aims to provide:
  • Just the Rego language implementation rather than the full OPA agent
  • Useful tools to debug Rego queries and modules
  • Enhanced error messages
  • Ease of extending and experimenting with different language features

You can use

fregot
to validate just about any kind of JSON against Rego policy. For an example of using
fregot
to test a Terraform plan prior to deployment, see the Example Use Case.

We are also actively working on improving the static analyzer to prevent many kinds of bugs.

Table of Contents

Installation

fregot works on all major platforms, and pre-built binaries are available for macOS and Linux.

Binary

  1. Download the latest binary for macOS or Linux from Releases.

  2. Place the binary somewhere in your

    $PATH
    .

From source

Installation through source is done using standard Haskell tooling -- Cabal and stack both work well.

Using stack

  1. Install stack for your platform.
  2. Clone this repository and
    cd
    into it.
  3. Run
    stack install
    .
  4. Make sure
    $HOME/.local/bin
    is in your
    $PATH
    .

Using Cabal

  1. Install Cabal for your platform.
  2. Clone this repository and
    cd
    into it.
  3. Run
    cabal install
    .
  4. Make sure
    $HOME/.cabal/bin
    is in your
    $PATH
    .

Usage

fregot v0.11.1

Usage: fregot COMMAND

Available options: -h,--help Show this help text --format FORMAT Format for error messages and diagnostics

Available commands: repl Run fregot repl test Run tests in .rego files bundle Bundle .rego files eval Evaluate a rego expression

fregot
understands a number of subcommands. See details and examples below.

fregot repl

fregot repl [PATHS] [--input PATH] [--watch]
: Start a REPL. Optionally, use the
--input [PATH]
flag to specify input and the
--watch
flag to enable watching files. See working with the REPL for details and examples.

fregot test

fregot test [PATHS]
: Run tests.
fregot
will recursively look for Rego files in the given paths and run any rule starting with
test_
.
Usage: fregot test PATHS
  Run tests in .rego files

Available options: PATHS Rego files or directories to test

Tip: You can run this command yourself from the root of this repo!

This command runs all rules starting with

test_
in
ami_id.rego
:
fregot test examples/ami_id/ami_id.rego

You'll see a count of passed, failed, and errored tests:

passed: 2, failed: 0, errored: 0

Take a look at

examples/ami_id/ami_id.rego
to see test examples.

Tip: You can also test Rego files from within the REPL using the

:test
command. See
:test
for details.

fregot bundle

fregot bundle [PATHS]
: Compile a number of Rego files into a single bundle that can be loaded faster than individual files. Experimental. Note that we currently use a different bundle format from OPA.
Usage: fregot bundle (-o|--output BUNDLE) PATHS
  Bundle .rego files

Available options: -o,--output BUNDLE Path of output file PATHS Rego files or directories to bundle

fregot eval

fregot eval [--input PATH] EXPRESSION [PATHS]
: Evaluate a Rego expression in a policy file using a JSON file as input.
Usage: fregot eval [-i|--input PATH] EXPRESSION [PATHS]
  Evaluate a rego expression

Available options: -i,--input PATH Input filepath EXPRESSION Rego expression to evaluate PATHS Rego files or directories to load

Tip: You can run this command yourself from the root of this repo!

This command evaluates the

data.fregot.examples.ami_id.allow
expression from
ami_id.rego
using the input file
repl_input.json
:
fregot eval \
    --input examples/ami_id/repl_input.json \
    'data.fregot.examples.ami_id.allow' \
    examples/ami_id/ami_id.rego

You'll see the value of the expression in the output:

[true]

Note that the expression argument should be formatted as

data.package.rule
according to the package and rule name in the Rego file.

REPL

F u g u e   R E G O   T o o l k i t
fregot v0.11.1 repl - use :help for usage info
repl% :help
Enter an expression to evaluate it.
Enter a rule to add it to the current package.

Other commands: :break Set or remove a breakpoint :help show this info :input set the input document :open open a different package, e.g. :open foo :quit exit the repl :load load a rego file, e.g. :load foo.rego :reload reload modified rego files :continue continue running the debugged program :step step (into) the next rule in the debugged program :next step (over) the next rule in the debugged program :rewind go back to the previous debug suspension :test run tests in the current package :type print the type of a term :where print your location :watch evaluate input after file changes

Shortcuts are supported for commands, e.g., :l for :load.

The REPL is currently the most important part of

fregot
. After loading the files passed on the command line, you end up on an interactive prompt.

There are three ways to interact with the REPL:

  1. Entering a rule adds the rule to the currently open package, e.g.:

    repl% numbers = {4, 8, 15, 16, 23, 42}
    Rule numbers added
    
  2. Entering a query evaluates that query, e.g.:

    repl% numbers[n]; n % 2 == 0; n
    = 4
    | n = 4
    = 16
    | n = 16
    = 8
    | n = 8
    = 42
    | n = 42
    
  3. There are number of special commands that start with

    :
    . Entering
    :help
    shows you the full list of commands.
    repl% :quit
    

See REPL Usage for details and examples for each command.

The open package

The REPL has the concept of an open package, indicated by the prompt. Initially this is

repl
, but you can change this using
:open
. For example, we can add a rule to the package
foo
and change back to
repl
:
repl% :open foo
Warning: package foo contains no rules
foo% a = 1
Rule a added
foo% :open repl
repl% data.foo.a
= 1

A typical workflow is to have an editor open as well as a

fregot repl
. You can then load the file using
:load
, which automatically opens the package:
repl% :load policy.rego
Loading policy.rego...
Loaded package policy
policy%

Once you make changes to the file, just reload it using

:reload
, which reloads all modified Rego files. If the file includes any rules starting with

test_
, you can assess your changes using
:test
, like so:
policy% :reload
Reloaded policy.rego
policy% :test
passed: 1, failed: 0, errored: 0

Debugging

Debugging generally follows these steps:

  1. Set one or more breakpoints with
    :break
  2. Evaluate an expression that activates the breakpoint
  3. Use other debugging commands to step into, step over, rewind, print location, or continue to the next breakpoint

Step 1: Set breakpoint

You can start debugging by setting a breakpoint with

:break
and then evaluating something.

To set a breakpoint, use the

:break
command. The command below sets a breakpoint on the rule
deny
in the currently loaded package,
fregot.examples.demo
:
fregot.examples.demo% :break deny
Set breakpoint at fregot.examples.demo.deny

You can use the

:break
command with either names, or a position in a file (line number). For example:
:break foo            # `foo` in the current package
:break repl.foo       # `foo` in the package repl
:break data.repl.foo  # Same as above
:break foo.rego:9     # Line 9 of `foo.rego`

Once at least one breakpoint is set, you can use

:break
without arguments to display the list. You'll see output like this:
fregot.examples.demo.test_allow
examples/demo/demo.rego:9
fregot.examples.demo.deny

You can also use

:break
on an existing breakpoint to remove it again:
fregot.examples.demo% :break deny
Removed breakpoint at fregot.examples.demo.deny

Step 2: Activate breakpoint

Next, evaluate an expression that activates the breakpoint. If the

repl
package is already loaded and the breakpoint is set on
foo
, we can just evaluate
foo
:
%repl foo

Step 3: Execute other debugging commands

Once the breakpoint is activated, you end up in a debugging context. From here, you can do a number of things:

  • Enter a query to evaluate in the current context: meaning that you can print and evaluate local variables that are in scope.
  • Use
    :continue
    to continue to the next breakpoint.
  • Use
    :step
    and
    :next
    to step into and over the next query, respectively.
  • Use
    :rewind
    to go back to the last step.
  • Use
    :where
    to see your current location.
  • Use
    :quit
    to exit debugging mode. Use
    :quit
    again to exit the REPL.

Debugging notes

  • By default,

    fregot
    turns off optimizations when debugging. This allows you to more naturally follow what the code is doing. However, this may get in your way when trying to debug complex queries that take too long to execute without optimizations.

    To explicitly turn on optimizations (even while debugging), use

    fregot repl -O
    .

REPL Usage

:break

:break [location]
sets a breakpoint. For example, the command below sets a breakpoint at the
allow
rule in the package
fregot.examples.ami_id
:
repl% :break fregot.examples.ami_id.allow
Set breakpoint at fregot.examples.ami_id.allow

To enter debugging mode, make sure you've loaded the package with

:load
, and activate the breakpoint by entering the rule name:

fregot.examples.ami_id% allow

The REPL displays the code at the breakpoint, along with the line number:

fregot.examples.ami_id% allow
23|     count(unapproved_amis) == 0
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^

The REPL prompt then includes the word

debug
, allowing you to enter other debugging commands:
fregot.examples.ami_id(debug)%

Entering

:break
by itself displays a list of breakpoints:
repl% :break

You'll see output like this:

fregot.examples.ami_id.allow
examples/ami_id/ami_id.rego:9

See Step 1: Set breakpoint for more info.

Evaluating local variables

Once you've loaded a file, activated a breakpoint, and entered debugging mode, you can evaluate queries in the current context -- including printing and evaluating local variables.

For example, if you look at break_example.rego, you'll see that

function_a
has the local variable
a
:
function_a {
  a = "Welcome to function a!"
  true
}

If you load the Rego file and try to evaluate

a
without setting a breakpoint first, you'll get an error message that the variable is not in scope:
fregot.examples.break_example% a

fregot (compile error): "a" (line 1, column 1): unknown variable:

1| a
   ^

Undefined variable: a

However, if you set a breakpoint at

function_a
, activate it, and
:step
into it, you can see the value of
a
:
fregot.examples.break_example% :break function_a
Set breakpoint at fregot.examples.break_example.function_a

fregot.examples.break_example% function_a

4| a = "Welcome to function a!" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

fregot.examples.break_example(debug)% :step

5| true ^^^^

fregot.examples.break_example(debug)% a = "Welcome to function a!"

:help

Shortcut

:h

:help
displays help text for the REPL. See REPL for more information.

:input

While in the REPL, you can directly change the input document by using the

:input
command to specify the path of your input document. Input can be JSON or YAML.

For example, if you want to load the input in

example.json
, you would do this:
repl% :input example.json

The command returns no output unless there is an error.

After you've set the input, you can enter

input
without colon or argument to print the input document to the screen:
repl% input
= {"user": "alice"}

Two things to note:

  • The home directory shortcut

    ~
    is not currently supported, so use the absolute path instead; e.g.,
    /Users/alice/input.json
  • If you change the input document, make sure to update it by issuing the

    :reload
    command. (Or, if you've enabled
    --watch
    mode, the REPL will reload the changes automatically!)

You can also set the input when you start the REPL with the

--input PATH
flag:
fregot repl examples/ami_id/ami_id.rego --input input.json

:open

Shortcut

:o

By default, you start in the

repl
package when you run
fregot repl
.
:open
allows you to switch between packages in files you've loaded. For example, you can switch to
fregot.examples.ami_id
like so:
repl% :open fregot.examples.ami_id

The REPL prompt changes to the name of the package you just loaded:

fregot.examples.ami_id%

To change to another package (or return to the

repl
package), you can run
:open [PACKAGE]
.

For more information, see The open package.

:quit

Shortcut

:q

:quit
exits debugging mode if you're in debugging mode, and exits the REPL if you're not:
repl(debug)% :quit

repl% :quit

:load

Shortcut

:l

:load [PATH]
loads a Rego file and automatically opens the package. For example:
repl% :load examples/ami_id/ami_id.rego

You'll see output like this:

Loaded package fregot.examples.ami_id

Once the file is loaded, you can debug it with other commands. You can also enter rules or expressions to evaluate them. For example, this command returns the value of the rule

allow
in the loaded Rego file:
fregot.examples.ami_id% allow

You'll see output like this:

= true

Tip: You can skip the

:load
step by specifying the Rego file paths when you start the REPL:
fregot repl my_policy_1.rego my_policy_2.rego

Directories are searched recursively. This command opens all the Rego files in the

examples
folder:
fregot repl examples

Note: If you change the Rego file after you've loaded it, you'll need to

:reload
it. However, if you've enabled
--watch
mode, the REPL automatically reloads your changes.

:reload

Shortcut

:r

:reload
checks for modified Rego files and reloads them. If you make changes to a loaded file, you can
:reload
it to update it in the REPL.
fregot.examples.ami_id% :reload

You'll see output like this:

Reloaded input.json
Reloaded examples/ami_id/ami_id.rego

For automatic reloading, see

--watch
.

Note that reloading when debugging is not possible as it would modify the code currently running.

:continue

:continue
continues to the next breakpoint:
fregot.examples.ami_id(debug)% :continue

The REPL displays the code at the next breakpoint, along with the line number.

In the following example, we set a breakpoint at

test_step
and at
function_a
(see break_example.rego), activate the
test_step
breakpoint, then use the
:continue
command:
fregot.examples.break_example% :break test_step
Set breakpoint at fregot.examples.break_example.test_step
fregot.examples.break_example% :break function_a
Set breakpoint at fregot.examples.break_example.function_a
fregot.examples.break_example% test_step
14|   function_a
      ^^^^^^^^^^
fregot.examples.break_example(debug)% :continue
4|   a = "Welcome to function a!"
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If there are no more breakpoints, the program finishes running and you'll see the validation results and

(debug) finished
:
(debug) = true
(debug) finished

:step

Shortcut

:s

:step
steps into the next rule in the debugged program:
fregot.examples.break_example(debug)% :step

The REPL displays the next query, along with the line number.

In the following example, we activate the breakpoint

test_step
, then use the
:step
command once to step into
function_a
, and a second time to step into the next query in
function_a
:
fregot.examples.break_example% test_step
14|   function_a
      ^^^^^^^^^^
fregot.examples.break_example(debug)% :step
4|   a = "Welcome to function a!"
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fregot.examples.break_example(debug)% :step
5|   true
     ^^^^

If there are no more queries, you'll see the results of the validation and the output

(debug) finished
:
(debug) = true
(debug) finished

:next

Shortcut

:n

:next
steps over the next rule in the debugged program:
fregot.examples.break_example% :next

The REPL skips to the next complete rule and displays the line number.

In the following example, we activate the breakpoint

test_step
, then use the
:next
command to jump to the next rule,
function_b
:
fregot.examples.break_example% test_step
14|   function_a
      ^^^^^^^^^^
fregot.examples.break_example(debug)% :next
15|   function_b
      ^^^^^^^^^^

:rewind

:rewind
goes back to the previous debug suspension -- it "rewinds" back to the most recent
:step
or
:next
command.

In the following example, we

:step
into
function_a
,
:step
into the next query, and then
:rewind
back to
function_a
(the previous query):
fregot.examples.break_example(debug)% :step
4|   a = "Welcome to function a!"
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fregot.examples.break_example(debug)% :step
5|   true
     ^^^^
fregot.examples.break_example(debug)% :rewind
4|   a = "Welcome to function a!"
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The history that you can rewind is currently limited to 10 steps.

:test

Shortcut

:t

:test
runs tests in the current package. This is similar to the
fregot test
command, but is scoped to a package rather than recursive directories.

For a more detailed example, check out ami_id.rego and compare it to testamiid.rego.

:test
only runs the two tests in
ami_id.rego
:
fregot.examples.ami_id% :test
passed: 2, failed: 0, errored: 0

On the other hand,

fregot test
searches recursively through the given directory and includes the 2 tests in
ami_id.rego
and the 8 tests from
test_ami_id.rego
for a total of 10 tests:
fregot test examples/ami_id/
passed: 10, failed: 0, errored: 0

:type

:type
prints the type of a term in the loaded package:
fregot.examples.ami_id% :type allow
allow : boolean

fregot.examples.ami_id% :type approved_amis approved_amis : set{string}

:where

:where
prints your location in debugging mode using a stack trace:
fregot.examples.ami_id(debug)% :where
23|     count(unapproved_amis) == 0
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^

Stack trace: rule fregot.examples.ami_id.allow at allow:1:1

This comes in handy when you are stepping into and over rules and want to double-check your location in the code.

:watch

To enable

--watch
mode, you must launch the REPL with
fregot repl 
--watch
. This also allows you to use the
:watch [expression]
command.

When you launch the REPL with

fregot repl --watch
, the REPL monitors loaded package and input files for changes and live-reloads them. You can also use the
:watch data.package.rule
command to monitor an expression, and
fregot
will automatically print an updated evaluation when loaded files are changed.

Here's an example. Start the REPL with the

--watch
flag:
fregot repl --watch

Load the Rego and input files:

repl% :load examples/ami_id/ami_id.rego
Loading examples/ami_id/ami_id.rego...
Loaded package fregot.examples.ami_id
fregot.examples.ami_id% :input examples/ami_id/repl_input.json

Now you can make changes to the Rego and/or input files and

fregot
automatically reloads them:
fregot.examples.ami_id%
Reloaded examples/ami_id/ami_id.rego
fregot.examples.ami_id%
Reloaded examples/ami_id/repl_input.json

This allows you to evaluate expressions as you like, and they'll automatically be up-to-date.

Use

:watch data.package.rule
to monitor a particular expression:
fregot.examples.ami_id% :watch data.fregot.examples.ami_id.allow

You can then make changes to the Rego and/or input file, and

fregot
re-evaluates the expression and prints the evaluation:
fregot.examples.ami_id%
Reloaded ami_id.rego
= false

fregot.examples.ami_id% Reloaded repl_input.json = true

Example Use Case

You can use fregot to determine whether a Terraform plan complies with a Rego policy. Incorporate

fregot
into your CI/CD pipeline to prevent noncompliant infrastructure from being deployed.

See examples/amiid/amiid.rego for details.

Additional Reading

Want to learn more? Check out these resources:

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.