debug

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 27, 2025 License: Apache-2.0 Imports: 19 Imported by: 1

README

OPA Debug API

This directory contains the OPA Debug API. The Debug API facilitates programmatic debugging of Rego policies, on top of which 3rd parties can build tools for debugging.

This API takes inspiration from the Debug Adapter Protocol (DAP), and follows the conventions established therein for managing threads, breakpoints, and variable scopes.

[!TIP] The Debug API current actively supported in two clients VS Code and Neovim. Both Regal's Debug Adapter as the backend, which is based on this API.

[!WARNING] The Debug API is experimental and subject to change.

Creating a Debug Session

debugger := debug.NewDebugger()

ctx := context.Background()
evalProps := debug.EvalProperties{
    Query: "data.example.allow = x",
    InputPath: "/path/to/input.json",
    LaunchProperties: LaunchProperties{
        DataPaths: []string{"/path/to/data.json", "/path/to/policy.rego"},
    },
}
session, err := s.debugger.LaunchEval(ctx, evalProps)
if err != nil {
    // handle error
}

// The session is launched in a paused state.
// Before resuming the session, here is the opportunity to set breakpoints

// Resume execution of all threads associated with the session
err = session.ResumeAll()
if err != nil {
    // handle error
}

Managing Breakpoints

Breakpoints can be added, removed, and enumerated.

Breakpoints are added to file-and-row locations in a module, and are triggered when the policy evaluation reaches that location. Breakpoints can be added at any time during policy evaluation.

// Add a breakpoint
br, err := session.AddBreakpoint(location.Location{
    File: "/path/to/policy.rego",
    Row: 10,
})
if err != nil {
    // handle error
}

// ...

// Remove the breakpoint
_, err = session.RemoveBreakpoint(br.ID)
if err != nil {
    // handle error
}

Stepping Through Policy Evaluation

When evaluation execution is paused, either immidiately after launching a session or when a breakpoint is hit, the session can be stepped through.

Step Over

StepOver() executes the next expression in the current scope and then stops on the next expression in the same scope, not stopping on expressions in sub-scopes; e.g. execution of referenced rule, called function, comprehension, or every expression.

threads, err := session.Threads()
if err != nil {
    // handle error
}

if err := session.StepOver(threads[0].ID); err != nil {
    // handle error
}
Example 1
allow if {
  x := f(input) >-+
  x == 1          |
}                 |
                  |
f(x) := y if {  <-+
  y := x + 1
}
Example 2
allow if {
  every x in l { >-+
    x < 10       <-+
  }
  input.x == 1
Step In

StepIn() executes the next expression in the current scope and then stops on the next expression in the same scope or sub-scope; stepping into any referenced rule, called function, comprehension, or every expression.

if err := session.StepIn(threads[0].ID); err != nil {
    // handle error
}
Example 1
allow if {
  x := f(input) >-+
  x == 1          |
}                 |
                  |
f(x) := y if {  <-+
  y := x + 1
}
Example 2
allow if {
  every x in l { >-+
    x < 10       <-+
  }
  input.x == 1
}
Step Out

StepOut() steps out of the current scope (rule, function, comprehension, every expression) and stops on the next expression in the parent scope.

if err := session.StepOut(threads[0].ID); err != nil {
    // handle error
}
Example 1
allow if {
  x := f(input) <-+
  x == 1          |
}                 |
                  |
f(x) := y if {    |
  y := x + 1    >-+
}
Example 2
allow if {
  every x in l {
    x < 10       >-+
  }                |
  input.x == 1   <-+
}

Fetching Variable Values

The current values of local and global variables are organized into scopes:

  • Local: contains variables defined in the current rule, function, comprehension, or every expression.
  • Virtual Cache: contains the state of the global Virtual Cache, where calculated return values for rules and functions are stored.
  • Input: contains the input document.
  • Data: contains the data document.
  • Result Set: contains the result set of the current query. This scope is only available on the final expression of the query evaluation.
scopes, err := session.Scopes(thread.ID)
if err != nil {
    // handle error
}

var localScope debug.Scope
for _, scope := range scopes {
    if scope.Name == "Local" {
        localScope = scope
        break
    }
}

variables, err := session.Variables(localScope.VariablesReference())
if err != nil {
    // handle error
}

// Enumerate and process variables

Documentation

Overview

Package debug EXPERIMENTAL: This package is under active development and is subject to change.

Index

Constants

View Source
const (
	ExceptionEventType  = "exception"
	StdoutEventType     = "stdout"
	StoppedEventType    = "stopped"
	TerminatedEventType = "terminated"
	ThreadEventType     = "thread"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Breakpoint

type Breakpoint interface {
	ID() BreakpointID
	Location() location.Location
}

type BreakpointID

type BreakpointID int

type Debugger

type Debugger interface {
	// LaunchEval starts a new eval debug session with the given LaunchEvalProperties.
	// The returned session is in a stopped state, and must be resumed to start execution.
	LaunchEval(ctx context.Context, props LaunchEvalProperties, opts ...LaunchOption) (Session, error)
}

Debugger is the interface for launching OPA debugger Session(s). This implementation is similar in structure to the Debug Adapter Protocol (DAP) to make such integrations easier, but is not intended to be a direct implementation. See: https://microsoft.github.io/debug-adapter-protocol/specification

EXPERIMENTAL: These interfaces are under active development and is subject to change.

func NewDebugger

func NewDebugger(options ...DebuggerOption) Debugger

type DebuggerOption

type DebuggerOption func(*debugger)

func SetEventHandler

func SetEventHandler(handler EventHandler) DebuggerOption

func SetLogger

func SetLogger(logger logging.Logger) DebuggerOption

type Event

type Event struct {
	Type    EventType
	Thread  ThreadID
	Message string
	// contains filtered or unexported fields
}

func (Event) String

func (d Event) String() string

type EventHandler

type EventHandler func(Event)

type EventType

type EventType string

type FrameID

type FrameID int

type LaunchEvalProperties

type LaunchEvalProperties struct {
	LaunchProperties
	Query     string
	Input     interface{}
	InputPath string
}

type LaunchOption

type LaunchOption func(options *launchOptions)

func RegoOption

func RegoOption(opt func(*rego.Rego)) LaunchOption

RegoOption adds a rego option to the internal Rego instance. Options may be overridden by the debugger, and it is recommended to use LaunchEvalProperties for commonly used options.

type LaunchProperties

type LaunchProperties struct {
	BundlePaths         []string
	DataPaths           []string
	StopOnResult        bool
	StopOnEntry         bool
	StopOnFail          bool
	EnablePrint         bool
	SkipOps             []topdown.Op
	StrictBuiltinErrors bool
	RuleIndexing        bool
}

func (LaunchProperties) String

func (lp LaunchProperties) String() string

type LaunchTestProperties

type LaunchTestProperties struct {
	LaunchProperties
	Run string
}

type Scope

type Scope interface {
	// Name returns the human-readable name of the scope.
	Name() string

	// NamedVariables returns the number of named variables in the scope.
	NamedVariables() int

	// VariablesReference returns a reference to the variables in the scope.
	VariablesReference() VarRef

	// Location returns the in-source location of the scope.
	Location() *location.Location
}

Scope represents the variable state of a StackFrame.

type Session

type Session interface {
	// Resume resumes execution of the thread with the given ID.
	Resume(threadID ThreadID) error

	// ResumeAll resumes execution of all threads in the session.
	ResumeAll() error

	// StepOver executes the next expression in the current scope and then stops on the next expression in the same scope,
	// not stopping on expressions in sub-scopes; e.g. execution of referenced rule, called function, comprehension, or every expression.
	//
	// Example 1:
	//
	//	allow if {
	//	  x := f(input) >-+
	//	  x == 1        <-+
	//	}
	//
	// Example 2:
	//
	//	allow if {
	//	  every x in l { >-+
	//	    x < 10         |
	//	  }                |
	//	  input.x == 1   <-+
	//	}
	StepOver(threadID ThreadID) error

	// StepIn executes the next expression in the current scope and then stops on the next expression in the same scope or sub-scope;
	// stepping into any referenced rule, called function, comprehension, or every expression.
	//
	// Example 1:
	//
	//	allow if {
	//	  x := f(input) >-+
	//	  x == 1          |
	//	}                 |
	//	                  |
	//	f(x) := y if {  <-+
	//	  y := x + 1
	//	}
	//
	// Example 2:
	//
	//	allow if {
	//	  every x in l { >-+
	//	    x < 10       <-+
	//	  }
	//	  input.x == 1
	//	}
	StepIn(threadID ThreadID) error

	// StepOut steps out of the current scope (rule, function, comprehension, every expression) and stops on the next expression in the parent scope.
	//
	// Example 1:
	//
	//	allow if {
	//	  x := f(input) <-+
	//	  x == 1          |
	//	}                 |
	//	                  |
	//	f(x) := y if {    |
	//	  y := x + 1    >-+
	//	}
	//
	// Example 2:
	//
	//	allow if {
	//	  every x in l {
	//	    x < 10       >-+
	//	  }                |
	//	  input.x == 1   <-+
	//	}
	StepOut(threadID ThreadID) error

	// Threads returns a list of all threads in the session.
	Threads() ([]Thread, error)

	// Breakpoints returns a list of all set breakpoints.
	Breakpoints() ([]Breakpoint, error)

	// AddBreakpoint sets a breakpoint at the given location.
	AddBreakpoint(loc location.Location) (Breakpoint, error)

	// RemoveBreakpoint removes a given breakpoint.
	// The removed breakpoint is returned. If the breakpoint does not exist, nil is returned.
	RemoveBreakpoint(ID BreakpointID) (Breakpoint, error)

	// ClearBreakpoints removes all breakpoints.
	ClearBreakpoints() error

	// StackTrace returns the StackTrace for the thread with the given ID.
	// The stack trace is ordered from the most recent frame to the least recent frame.
	StackTrace(threadID ThreadID) (StackTrace, error)

	// Scopes returns the Scope list for the frame with the given ID.
	Scopes(frameID FrameID) ([]Scope, error)

	// Variables returns the Variable list for the given reference.
	Variables(varRef VarRef) ([]Variable, error)

	// Terminate stops all threads in the session.
	Terminate() error
}

type StackFrame

type StackFrame interface {
	// ID returns the unique identifier for the frame.
	ID() FrameID

	// Name returns the human-readable name of the frame.
	Name() string

	// Location returns the location of the frame in the source code.
	Location() *location.Location

	// Thread returns the ID of the thread that the frame is associated with.
	Thread() ThreadID

	// String returns a human-readable string representation of the frame.
	String() string

	// Equal returns true if the frame is equal to the other frame.
	Equal(other StackFrame) bool
}

type StackTrace

type StackTrace []StackFrame

StackTrace represents a StackFrame stack.

func (StackTrace) Equal

func (s StackTrace) Equal(other StackTrace) bool

type Thread

type Thread interface {
	// ID returns the unique identifier for the thread.
	ID() ThreadID
	// Name returns the human-readable name of the thread.
	Name() string
}

Thread represents a single thread of execution.

type ThreadID

type ThreadID int

type VarRef

type VarRef int

type Variable

type Variable interface {
	// Name returns the name of the variable.
	Name() string

	// Type returns the type of the variable.
	Type() string

	// Value returns the value of the variable.
	Value() string

	// VariablesReference returns a reference to the variables that are children of this variable.
	// E.g. this variable is a collection, such as an array, set, or object.
	VariablesReference() VarRef
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL