Documentation ¶
Overview ¶
Package topdown provides low-level query evaluation support.
The topdown implementation is a modified version of the standard top-down evaluation algorithm used in Datalog. References and comprehensions are evaluated eagerly while all other terms are evaluated lazily.
Index ¶
- Constants
- func IsCancel(err error) bool
- func IsError(err error) bool
- func PrettyTrace(w io.Writer, trace []*Event)
- func RegisterBuiltinFunc(name string, f BuiltinFunc)
- func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1)
- func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2)
- func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3)
- func RegisterFunctionalBuiltinVoid1(name string, fun FunctionalBuiltinVoid1)
- type BufferTracer
- type BuiltinContext
- type BuiltinEmpty
- type BuiltinFunc
- type Cancel
- type Error
- type Event
- type FunctionalBuiltin1
- type FunctionalBuiltin2
- type FunctionalBuiltin3
- type FunctionalBuiltinVoid1
- type Op
- type Query
- func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error
- func (q *Query) PartialIter(ctx context.Context, iter func(ast.Body) error) error
- func (q *Query) PartialRun(ctx context.Context) ([]ast.Body, error)
- func (q *Query) Run(ctx context.Context) (QueryResultSet, error)
- func (q *Query) WithCancel(cancel Cancel) *Query
- func (q *Query) WithCompiler(compiler *ast.Compiler) *Query
- func (q *Query) WithInput(input *ast.Term) *Query
- func (q *Query) WithMetrics(metrics metrics.Metrics) *Query
- func (q *Query) WithPartial(terms []*ast.Term) *Query
- func (q *Query) WithStore(store storage.Store) *Query
- func (q *Query) WithTracer(tracer Tracer) *Query
- func (q *Query) WithTransaction(txn storage.Transaction) *Query
- type QueryResult
- type QueryResultSet
- type Tracer
Examples ¶
Constants ¶
const ( // InternalErr represents an unknown evaluation error. InternalErr string = "eval_internal_error" // CancelErr indicates the evaluation process was cancelled. CancelErr string = "eval_cancel_error" // ConflictErr indicates a conflict was encountered during evaluation. For // instance, a conflict occurs if a rule produces multiple, differing values // for the same key in an object. Conflict errors indicate the policy does // not account for the data loaded into the policy engine. ConflictErr string = "eval_conflict_error" // TypeErr indicates evaluation stopped because an expression was applied to // a value of an inappropriate type. TypeErr string = "eval_type_error" )
Variables ¶
This section is empty.
Functions ¶
func PrettyTrace ¶ added in v0.2.0
PrettyTrace pretty prints the trace to the writer.
func RegisterBuiltinFunc ¶
func RegisterBuiltinFunc(name string, f BuiltinFunc)
RegisterBuiltinFunc adds a new built-in function to the evaluation engine.
func RegisterFunctionalBuiltin1 ¶ added in v0.4.0
func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1)
RegisterFunctionalBuiltin1 adds a new built-in function to the evaluation engine.
Example ¶
package main import ( "context" "fmt" "strings" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/topdown" "github.com/open-policy-agent/opa/topdown/builtins" "github.com/open-policy-agent/opa/types" ) func main() { // Rego includes a number of built-in functions ("built-ins") for performing // standard operations like string manipulation, regular expression // matching, and computing aggregates. // // This test shows how to add a new built-in to Rego and OPA. // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() // The ast package contains a registry that enumerates the built-ins // included in Rego. When adding a new built-in, you must update the // registry to include your built-in. Otherwise, the compiler will complain // when it encounters your built-in. builtin := &ast.Builtin{ Name: "mybuiltins.upper", Decl: types.NewFunction( types.Args(types.S), types.S, ), } ast.RegisterBuiltin(builtin) // This is the implementation of the built-in that will be called during // query evaluation. builtinImpl := func(a ast.Value) (ast.Value, error) { str, err := builtins.StringOperand(a, 1) if err != nil { return nil, err } if str.Equal(ast.String("magic")) { // topdown.BuiltinEmpty indicates to the evaluation engine that the // expression is false/not defined. return nil, topdown.BuiltinEmpty{} } return ast.String(strings.ToUpper(string(str))), nil } // See documentation for registering functions that take different numbers // of arguments. topdown.RegisterFunctionalBuiltin1(builtin.Name, builtinImpl) // At this point, the new built-in has been registered and can be used in // queries. Our custom built-in converts strings to upper case but is not // defined for the input "magic". compiler := ast.NewCompiler() query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`mybuiltins.upper("custom", x); not mybuiltins.upper("magic", "MAGIC")`)) if err != nil { // Handle error. } // Evaluate the query. q := topdown.NewQuery(query).WithCompiler(compiler) q.Iter(ctx, func(qr topdown.QueryResult) error { fmt.Println("x:", qr[ast.Var("x")]) return nil }) // If you add a new built-in function to OPA, you should: // // 1. Update the Language Reference: http://www.openpolicyagent.org/docs/language-reference.html. // 2. Add an integration test to the topdown package. }
Output: x: "CUSTOM"
func RegisterFunctionalBuiltin2 ¶ added in v0.4.0
func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2)
RegisterFunctionalBuiltin2 adds a new built-in function to the evaluation engine.
func RegisterFunctionalBuiltin3 ¶ added in v0.4.0
func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3)
RegisterFunctionalBuiltin3 adds a new built-in function to the evaluation engine.
func RegisterFunctionalBuiltinVoid1 ¶ added in v0.5.0
func RegisterFunctionalBuiltinVoid1(name string, fun FunctionalBuiltinVoid1)
RegisterFunctionalBuiltinVoid1 adds a new built-in function to the evaluation engine.
Types ¶
type BufferTracer ¶ added in v0.2.0
type BufferTracer []*Event
BufferTracer implements the Tracer interface by simply buffering all events received.
func NewBufferTracer ¶ added in v0.2.0
func NewBufferTracer() *BufferTracer
NewBufferTracer returns a new BufferTracer.
func (*BufferTracer) Enabled ¶ added in v0.2.0
func (b *BufferTracer) Enabled() bool
Enabled always returns true if the BufferTracer is instantiated.
func (*BufferTracer) Trace ¶ added in v0.2.0
func (b *BufferTracer) Trace(evt *Event)
Trace adds the event to the buffer.
type BuiltinContext ¶ added in v0.5.11
BuiltinContext contains context from the evaluator that may be used by built-in functions.
type BuiltinEmpty ¶ added in v0.4.0
type BuiltinEmpty struct{}
BuiltinEmpty is used to signal that the built-in function evaluated, but the result is undefined so evaluation should not continue.
func (BuiltinEmpty) Error ¶ added in v0.4.0
func (BuiltinEmpty) Error() string
type BuiltinFunc ¶
BuiltinFunc defines a generic interface for built-in functions.
type Cancel ¶ added in v0.5.1
type Cancel interface { Cancel() Cancelled() bool }
Cancel defines the interface for cancelling topdown queries. Cancel operations are thread-safe and idempotent.
type Error ¶
type Error struct { Code string `json:"code"` Message string `json:"message"` Location *ast.Location `json:"location,omitempty"` }
Error is the error type returned by the Eval and Query functions when an evaluation error occurs.
type Event ¶ added in v0.2.0
type Event struct { Op Op // Identifies type of event. Node interface{} // Contains AST node relevant to the event. QueryID uint64 // Identifies the query this event belongs to. ParentID uint64 // Identifies the parent query this event belongs to. Locals *ast.ValueMap // Contains local variable bindings from the query context. }
Event contains state associated with a tracing event.
type FunctionalBuiltin1 ¶ added in v0.4.0
FunctionalBuiltin1 defines an interface for simple functional built-ins.
Implement this interface if your built-in function takes one input and produces one output.
If an error occurs, the functional built-in should return a descriptive message. The message should not be prefixed with the built-in name as the framework takes care of this.
type FunctionalBuiltin2 ¶ added in v0.4.0
FunctionalBuiltin2 defines an interface for simple functional built-ins.
Implement this interface if your built-in function takes two inputs and produces one output.
If an error occurs, the functional built-in should return a descriptive message. The message should not be prefixed with the built-in name as the framework takes care of this.
type FunctionalBuiltin3 ¶ added in v0.4.0
FunctionalBuiltin3 defines an interface for simple functional built-ins.
Implement this interface if your built-in function takes three inputs and produces one output.
If an error occurs, the functional built-in should return a descriptive message. The message should not be prefixed with the built-in name as the framework takes care of this.
type FunctionalBuiltinVoid1 ¶ added in v0.5.0
FunctionalBuiltinVoid1 defines an interface for simple functional built-ins.
Implement this interface if your built-in function takes one input and produces no outputs.
If an error occurs, the functional built-in should return a descriptive message. The message should not be prefixed with the built-in name as the framework takes care of this.
type Op ¶ added in v0.2.0
type Op string
Op defines the types of tracing events.
const ( // EnterOp is emitted when a new query is about to be evaluated. EnterOp Op = "Enter" // ExitOp is emitted when a query has evaluated to true. ExitOp Op = "Exit" // EvalOp is emitted when an expression is about to be evaluated. EvalOp Op = "Eval" // RedoOp is emitted when an expression, rule, or query is being re-evaluated. RedoOp Op = "Redo" // FailOp is emitted when an expression evaluates to false. FailOp Op = "Fail" )
type Query ¶
type Query struct {
// contains filtered or unexported fields
}
Query provides a configurable interface for performing query evaluation.
func (*Query) Iter ¶ added in v0.5.11
Iter executes the query and invokes the iter function with query results produced by evaluating the query.
Example ¶
package main import ( "bytes" "context" "encoding/json" "fmt" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/storage/inmem" "github.com/open-policy-agent/opa/topdown" ) func main() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() compiler := ast.NewCompiler() // Define a dummy query and some data that the query will execute against. query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`data.a[_] = x; x >= 2`)) if err != nil { // Handle error. } var data map[string]interface{} // OPA uses Go's standard JSON library but assumes that numbers have been // decoded as json.Number instead of float64. You MUST decode with UseNumber // enabled. decoder := json.NewDecoder(bytes.NewBufferString(`{"a": [1,2,3,4]}`)) decoder.UseNumber() if err := decoder.Decode(&data); err != nil { // Handle error. } // Instantiate the policy engine's storage layer. store := inmem.NewFromObject(data) // Create a new transaction. Transactions allow the policy engine to // evaluate the query over a consistent snapshot fo the storage layer. txn, err := store.NewTransaction(ctx) if err != nil { // Handle error. } defer store.Abort(ctx, txn) // Prepare the evaluation parameters. Evaluation executes against the policy // engine's storage. In this case, we seed the storage with a single array // of number. Other parameters such as the input, tracing configuration, // etc. can be set on the query object. q := topdown.NewQuery(query). WithCompiler(compiler). WithStore(store). WithTransaction(txn) result := []interface{}{} // Execute the query and provide a callback function to accumulate the results. err = q.Iter(ctx, func(qr topdown.QueryResult) error { // Each variable in the query will have an associated binding. x := qr[ast.Var("x")] // The bindings are ast.Value types so we will convert to a native Go value here. v, err := ast.JSON(x.Value) if err != nil { return err } result = append(result, v) return nil }) // Inspect the query result. fmt.Println("result:", result) fmt.Println("err:", err) }
Output: result: [2 3 4] err: <nil>
func (*Query) PartialIter ¶ added in v0.5.11
PartialIter executes the query invokes the iter function with partially evaluated queries produced by evaluating the query with a partial set.
func (*Query) PartialRun ¶ added in v0.5.11
PartialRun is a wrapper around PartialIter that accumulates results and returns them in one shot.
func (*Query) Run ¶ added in v0.5.11
func (q *Query) Run(ctx context.Context) (QueryResultSet, error)
Run is a wrapper around Iter that accumulates query results and returns them in one shot.
Example ¶
package main import ( "bytes" "context" "encoding/json" "fmt" "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/storage/inmem" "github.com/open-policy-agent/opa/topdown" ) func main() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() compiler := ast.NewCompiler() // Define a dummy query and some data that the query will execute against. query, err := compiler.QueryCompiler().Compile(ast.MustParseBody(`data.a[_] = x; x >= 2`)) if err != nil { // Handle error. } var data map[string]interface{} // OPA uses Go's standard JSON library but assumes that numbers have been // decoded as json.Number instead of float64. You MUST decode with UseNumber // enabled. decoder := json.NewDecoder(bytes.NewBufferString(`{"a": [1,2,3,4]}`)) decoder.UseNumber() if err := decoder.Decode(&data); err != nil { // Handle error. } // Instantiate the policy engine's storage layer. store := inmem.NewFromObject(data) // Create a new transaction. Transactions allow the policy engine to // evaluate the query over a consistent snapshot fo the storage layer. txn, err := store.NewTransaction(ctx) if err != nil { // Handle error. } defer store.Abort(ctx, txn) // Prepare the evaluation parameters. Evaluation executes against the policy // engine's storage. In this case, we seed the storage with a single array // of number. Other parameters such as the input, tracing configuration, // etc. can be set on the query object. q := topdown.NewQuery(query). WithCompiler(compiler). WithStore(store). WithTransaction(txn) rs, err := q.Run(ctx) // Inspect the query result set. fmt.Println("len:", len(rs)) for i := range rs { fmt.Printf("rs[%d][\"x\"]: %v\n", i, rs[i]["x"]) } fmt.Println("err:", err) }
Output: len: 3 rs[0]["x"]: 2 rs[1]["x"]: 3 rs[2]["x"]: 4 err: <nil>
func (*Query) WithCancel ¶ added in v0.5.11
WithCancel sets the cancellation object to use for the query. Set this if you need to abort queries based on a deadline. This is optional.
func (*Query) WithCompiler ¶ added in v0.5.11
WithCompiler sets the compiler to use for the query.
func (*Query) WithInput ¶ added in v0.5.11
WithInput sets the input object to use for the query. References rooted at input will be evaluated against this value. This is optional.
func (*Query) WithMetrics ¶ added in v0.5.11
WithMetrics sets the metrics collection to add evaluation metrics to. This is optional.
func (*Query) WithPartial ¶ added in v0.5.11
WithPartial sets the initial set of vars or refs to treat as unavailable during query evaluation. This is typically required for partial evaluation.
func (*Query) WithTracer ¶ added in v0.5.11
WithTracer sets the query tracer to use during evaluation. This is optional.
func (*Query) WithTransaction ¶ added in v0.5.11
func (q *Query) WithTransaction(txn storage.Transaction) *Query
WithTransaction sets the transaction to use for the query. All queries should be performed over a consistent snapshot of the storage layer.
type QueryResult ¶ added in v0.2.1
QueryResult represents a single result returned by a query. The result contains bindings for all variables that appear in the query.
type QueryResultSet ¶ added in v0.2.1
type QueryResultSet []QueryResult
QueryResultSet represents a collection of results returned by a query.