func setup(t *testing.T, filename string) *topdown.QueryParams { // policy compilation c := ast.NewCompiler() modules := map[string]*ast.Module{ "test": ast.MustParseModule(policy), } if c.Compile(modules); c.Failed() { t.Fatal("unexpected error:", c.Errors) } // storage setup store := storage.New(storage.Config{ Builtin: loadDataStore(filename), }) // parameter setup ctx := context.Background() request := ast.ObjectTerm(ast.Item(ast.StringTerm("pod"), ast.MustParseTerm(requestedPod))) path := ast.MustParseRef("data.opa.test.scheduler.fit") txn := storage.NewTransactionOrDie(ctx, store) params := topdown.NewQueryParams(ctx, c, store, txn, request.Value, path) return params }
func ExampleREPL_OneShot() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() // Instantiate the policy engine's storage layer. store := storage.New(storage.InMemoryConfig()) // Create a buffer that will receive REPL output. var buf bytes.Buffer // Create a new REPL. repl := repl.New(store, "", &buf, "json", "") // Define a rule inside the REPL. repl.OneShot(ctx, "p :- a = [1, 2, 3, 4], a[_] > 3") // Query the rule defined above. repl.OneShot(ctx, "p") // Inspect the output. Defining rules does not produce output so we only expect // output from the second line of input. fmt.Println(buf.String()) // Output: // true }
func setupBenchmark(nodes int, pods int) *topdown.QueryParams { // policy compilation c := ast.NewCompiler() modules := map[string]*ast.Module{ "test": ast.MustParseModule(policy), } if c.Compile(modules); c.Failed() { panic(c.Errors) } // storage setup store := storage.New(storage.InMemoryConfig()) // parameter setup ctx := context.Background() request := ast.ObjectTerm(ast.Item(ast.StringTerm("pod"), ast.MustParseTerm(requestedPod))) path := ast.MustParseRef("data.opa.test.scheduler.fit") txn := storage.NewTransactionOrDie(ctx, store) params := topdown.NewQueryParams(ctx, c, store, txn, request.Value, path) // data setup setupNodes(ctx, store, txn, nodes) setupRCs(ctx, store, txn, 1) setupPods(ctx, store, txn, pods, nodes) return params }
func TestQueryBindingIterationError(t *testing.T) { ctx := context.Background() store := storage.New(storage.InMemoryConfig()) mock := &queryBindingErrStore{} if err := store.Mount(mock, storage.MustParsePath("/foo/bar")); err != nil { panic(err) } server, err := New(ctx, store, ":8182", false) if err != nil { panic(err) } recorder := httptest.NewRecorder() f := &fixture{ server: server, recorder: recorder, t: t, } get := newReqV1("GET", `/query?q=a=data.foo.bar`, "") f.server.Handler.ServeHTTP(f.recorder, get) if f.recorder.Code != 500 { t.Fatalf("Expected 500 error due to unknown storage error but got: %v", f.recorder) } }
func ExampleQuery() { // 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 module with rules that produce documents that we will query below. module, err := ast.ParseModule("my_module.rego", ` package opa.example p[x] :- q[x], not r[x] q[y] :- a = [1,2,3], y = a[_] r[z] :- b = [2,4], z = b[_] `) mods := map[string]*ast.Module{ "my_module": module, } if compiler.Compile(mods); compiler.Failed() { fmt.Println(compiler.Errors) } if err != nil { // Handle error. } // Instantiate the policy engine's storage layer. store := storage.New(storage.InMemoryConfig()) // 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.Close(ctx, txn) // Prepare query parameters. In this case, there are no additional documents // required by the policy so the request is nil. var request ast.Value params := topdown.NewQueryParams(ctx, compiler, store, txn, request, ast.MustParseRef("data.opa.example.p")) // Execute the query against "p". v1, err1 := topdown.Query(params) // Inspect the result. fmt.Println("v1:", v1[0].Result) fmt.Println("err1:", err1) // Output: // v1: [1 3] // err1: <nil> }
func ExampleStorage_Open() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() // Define two example modules and write them to disk in a temporary directory. ex1 := ` package opa.example p :- q.r != 0 ` ex2 := ` package opa.example q = {"r": 100} ` path, err := ioutil.TempDir("", "") if err != nil { // Handle error. } defer os.RemoveAll(path) if err = ioutil.WriteFile(filepath.Join(path, "ex1.rego"), []byte(ex1), 0644); err != nil { // Handle error. } if err = ioutil.WriteFile(filepath.Join(path, "ex2.rego"), []byte(ex2), 0644); err != nil { // Handle error. } // Instantiate storage layer and configure with a directory to persist policy modules. store := storage.New(storage.InMemoryConfig().WithPolicyDir(path)) if err = store.Open(ctx); err != nil { // Handle error. } // Inspect one of the loaded policies. mod, _, err := storage.GetPolicy(ctx, store, "ex1.rego") if err != nil { // Handle error. } fmt.Println("Expr:", mod.Rules[0].Body[0]) // Output: // Expr: neq(q.r, 0) }
func TestShow(t *testing.T) { ctx := context.Background() store := storage.New(storage.InMemoryConfig()) var buffer bytes.Buffer repl := newRepl(store, &buffer) repl.OneShot(ctx, "package repl_test") repl.OneShot(ctx, "show") assertREPLText(t, buffer, "package repl_test\n") buffer.Reset() repl.OneShot(ctx, "import request.xyz") repl.OneShot(ctx, "show") expected := `package repl_test import request.xyz` + "\n" assertREPLText(t, buffer, expected) buffer.Reset() repl.OneShot(ctx, "import data.foo as bar") repl.OneShot(ctx, "show") expected = `package repl_test import request.xyz import data.foo as bar` + "\n" assertREPLText(t, buffer, expected) buffer.Reset() repl.OneShot(ctx, "p[1] :- true") repl.OneShot(ctx, "p[2] :- true") repl.OneShot(ctx, "show") expected = `package repl_test import request.xyz import data.foo as bar p[1] :- true p[2] :- true` + "\n" assertREPLText(t, buffer, expected) buffer.Reset() repl.OneShot(ctx, "package abc") repl.OneShot(ctx, "show") assertREPLText(t, buffer, "package abc\n") buffer.Reset() repl.OneShot(ctx, "package repl_test") repl.OneShot(ctx, "show") assertREPLText(t, buffer, expected) buffer.Reset() }
func TestUnset(t *testing.T) { ctx := context.Background() store := storage.New(storage.InMemoryConfig()) var buffer bytes.Buffer repl := newRepl(store, &buffer) repl.OneShot(ctx, "magic = 23") repl.OneShot(ctx, "p = 3.14") repl.OneShot(ctx, "unset p") err := repl.OneShot(ctx, "p") if _, ok := err.(ast.Errors); !ok { t.Fatalf("Expected AST error but got: %v", err) } buffer.Reset() repl.OneShot(ctx, "p = 3.14") repl.OneShot(ctx, "p = 3 :- false") repl.OneShot(ctx, "unset p") err = repl.OneShot(ctx, "p") if _, ok := err.(ast.Errors); !ok { t.Fatalf("Expected AST error but got err: %v, output: %v", err, buffer.String()) } if err := repl.OneShot(ctx, "unset "); err == nil { t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) } if err := repl.OneShot(ctx, "unset 1=1"); err == nil { t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) } if err := repl.OneShot(ctx, `unset "p"`); err == nil { t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) } buffer.Reset() repl.OneShot(ctx, `unset q`) if buffer.String() != "warning: no matching rules in current module\n" { t.Fatalf("Expected unset error for missing rule but got: %v", buffer.String()) } buffer.Reset() repl.OneShot(ctx, `magic`) if buffer.String() != "23\n" { t.Fatalf("Expected magic to be defined but got: %v", buffer.String()) } buffer.Reset() repl.OneShot(ctx, `package data.other`) repl.OneShot(ctx, `unset magic`) if buffer.String() != "warning: no matching rules in current module\n" { t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) } }
func TestDump(t *testing.T) { ctx := context.Background() input := `{"a": [1,2,3,4]}` var data map[string]interface{} err := util.UnmarshalJSON([]byte(input), &data) if err != nil { panic(err) } store := storage.New(storage.InMemoryWithJSONConfig(data)) var buffer bytes.Buffer repl := newRepl(store, &buffer) repl.OneShot(ctx, "dump") expectOutput(t, buffer.String(), "{\"a\":[1,2,3,4]}\n") }
func newFixture(t *testing.T) *fixture { ctx := context.Background() store := storage.New(storage.InMemoryConfig().WithPolicyDir(policyDir)) server, err := New(ctx, store, ":8182", false) if err != nil { panic(err) } recorder := httptest.NewRecorder() return &fixture{ server: server, recorder: recorder, t: t, } }
func TestHelp(t *testing.T) { topics["deadbeef"] = topicDesc{ fn: func(w io.Writer) error { fmt.Fprintln(w, "blah blah blah") return nil }, } ctx := context.Background() store := storage.New(storage.InMemoryConfig()) var buffer bytes.Buffer repl := newRepl(store, &buffer) repl.OneShot(ctx, "help deadbeef") expected := "blah blah blah\n" if buffer.String() != expected { t.Fatalf("Unexpected output from help topic: %v", buffer.String()) } }
func (rt *Runtime) init(ctx context.Context, params *Params) error { if len(params.PolicyDir) > 0 { if err := os.MkdirAll(params.PolicyDir, 0755); err != nil { return errors.Wrap(err, "unable to make --policy-dir") } } loaded, err := loadAllPaths(params.Paths) if err != nil { return err } // Open data store and load base documents. store := storage.New(storage.InMemoryConfig().WithPolicyDir(params.PolicyDir)) if err := store.Open(ctx); err != nil { return err } txn, err := store.NewTransaction(ctx) if err != nil { return err } defer store.Close(ctx, txn) if err := store.Write(ctx, txn, storage.AddOp, storage.Path{}, loaded.Documents); err != nil { return errors.Wrapf(err, "storage error") } // Load policies provided via input. if err := compileAndStoreInputs(loaded.Modules, store, txn); err != nil { return errors.Wrapf(err, "compile error") } rt.Store = store return nil }
func TestDumpPath(t *testing.T) { ctx := context.Background() input := `{"a": [1,2,3,4]}` var data map[string]interface{} err := util.UnmarshalJSON([]byte(input), &data) if err != nil { panic(err) } store := storage.New(storage.InMemoryWithJSONConfig(data)) var buffer bytes.Buffer repl := newRepl(store, &buffer) dir, err := ioutil.TempDir("", "dump-path-test") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) file := filepath.Join(dir, "tmpfile") repl.OneShot(ctx, fmt.Sprintf("dump %s", file)) if buffer.String() != "" { t.Errorf("Expected no output but got: %v", buffer.String()) } bs, err := ioutil.ReadFile(file) if err != nil { t.Fatalf("Expected file read to succeed but got: %v", err) } var result map[string]interface{} if err := util.UnmarshalJSON(bs, &result); err != nil { t.Fatalf("Expected json unmarhsal to suceed but got: %v", err) } if !reflect.DeepEqual(data, result) { t.Fatalf("Expected dumped json to equal %v but got: %v", data, result) } }
func executeQuery(data string, compiler *ast.Compiler, tracer topdown.Tracer) { topdown.ResetQueryIDs() d := map[string]interface{}{} if len(data) > 0 { if err := util.UnmarshalJSON([]byte(data), &d); err != nil { panic(err) } } ctx := context.Background() store := storage.New(storage.InMemoryWithJSONConfig(d)) txn := storage.NewTransactionOrDie(ctx, store) defer store.Close(ctx, txn) params := topdown.NewQueryParams(ctx, compiler, store, txn, nil, ast.MustParseRef("data.test.p")) params.Tracer = tracer _, err := topdown.Query(params) if err != nil { panic(err) } }
func newTestStore() *storage.Storage { input := ` { "a": [ { "b": { "c": [true,2,false] } }, { "b": { "c": [false,true,1] } } ] } ` var data map[string]interface{} err := util.UnmarshalJSON([]byte(input), &data) if err != nil { panic(err) } return storage.New(storage.InMemoryWithJSONConfig(data)) }
func ExampleStorage_Write() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() // Define some dummy data to initialize the DataStore with. exampleInput := ` { "users": [ { "name": "alice", "color": "red", "likes": ["clouds", "ships"] }, { "name": "burt", "likes": ["cheese", "wine"] } ] } ` 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(exampleInput)) decoder.UseNumber() if err := decoder.Decode(&data); err != nil { // Handle error. } // Create the new DataStore with the dummy data. store := storage.New(storage.InMemoryWithJSONConfig(data)) // Define dummy data to add to the DataStore. examplePatch := `{ "longitude": 82.501389, "latitude": -62.338889 }` var patch interface{} // See comment above regarding decoder usage. decoder = json.NewDecoder(bytes.NewBufferString(examplePatch)) decoder.UseNumber() if err := decoder.Decode(&patch); err != nil { // Handle error. } txn, err := store.NewTransaction(ctx) if err != nil { // Handle error. } defer store.Close(ctx, txn) // Write values into storage and read result. err0 := store.Write(ctx, txn, storage.AddOp, storage.MustParsePath("/users/0/location"), patch) v1, err1 := store.Read(ctx, txn, storage.MustParsePath("/users/0/location/latitude")) err2 := store.Write(ctx, txn, storage.ReplaceOp, storage.MustParsePath("/users/1/color"), "red") // Inspect the return values. fmt.Println("err0:", err0) fmt.Println("v1:", v1) fmt.Println("err1:", err1) fmt.Println("err2:", err2) // Rollback transaction because write failed. // Output: // err0: <nil> // v1: -62.338889 // err1: <nil> // err2: storage error (code: 1): bad path: /users/1/color, document does not exist }
func TestPrettyTrace(t *testing.T) { module := ` package test p :- q[x], plus(x, 1, n) q[x] :- x = data.a[_] ` ctx := context.Background() compiler := compileModules([]string{module}) data := loadSmallTestData() store := storage.New(storage.InMemoryWithJSONConfig(data)) txn := storage.NewTransactionOrDie(ctx, store) defer store.Close(ctx, txn) params := NewQueryParams(ctx, compiler, store, txn, nil, ast.MustParseRef("data.test.p")) tracer := NewBufferTracer() params.Tracer = tracer _, err := Query(params) if err != nil { panic(err) } expected := `Enter eq(data.test.p, _) | Eval eq(data.test.p, _) | Enter p = true :- data.test.q[x], plus(x, 1, n) | | Eval data.test.q[x] | | Enter q[x] :- eq(x, data.a[_]) | | | Eval eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Redo p = true :- data.test.q[x], plus(x, 1, n) | | Redo data.test.q[x] | | Redo q[x] :- eq(x, data.a[_]) | | | Redo eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Redo p = true :- data.test.q[x], plus(x, 1, n) | | Redo data.test.q[x] | | Redo q[x] :- eq(x, data.a[_]) | | | Redo eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Redo p = true :- data.test.q[x], plus(x, 1, n) | | Redo data.test.q[x] | | Redo q[x] :- eq(x, data.a[_]) | | | Redo eq(x, data.a[_]) | | | Exit q[x] :- eq(x, data.a[_]) | | Eval plus(x, 1, n) | | Exit p = true :- data.test.q[x], plus(x, 1, n) | Exit eq(data.test.p, _) ` a := strings.Split(expected, "\n") var buf bytes.Buffer PrettyTrace(&buf, *tracer) b := strings.Split(buf.String(), "\n") min := len(a) if min > len(b) { min = len(b) } for i := 0; i < min; i++ { if a[i] != b[i] { t.Errorf("Line %v in trace is incorrect. Expected %v but got: %v", i+1, a[i], b[i]) } } if len(a) < len(b) { t.Fatalf("Extra lines in trace:\n%v", strings.Join(b[min:], "\n")) } else if len(b) < len(a) { t.Fatalf("Missing lines in trace:\n%v", strings.Join(a[min:], "\n")) } }
func ExampleEval() { // 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 := storage.New(storage.InMemoryWithJSONConfig(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.Close(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 request, tracing configuration, // etc. can be set on the Topdown object. t := topdown.New(ctx, query, compiler, store, txn) result := []interface{}{} // Execute the query and provide a callbakc function to accumulate the results. err = topdown.Eval(t, func(t *topdown.Topdown) error { // Each variable in the query will have an associated "binding" in the context. x := t.Binding(ast.Var("x")) // The bindings are ast.Value types so we will convert to a native Go value here. v, err := topdown.ValueToInterface(x, t) 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 ExampleStorage_Read() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() // Define some dummy data to initialize the built-in store with. exampleInput := ` { "users": [ { "name": "alice", "color": "red", "likes": ["clouds", "ships"] }, { "name": "burt", "likes": ["cheese", "wine"] } ] } ` 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(exampleInput)) decoder.UseNumber() if err := decoder.Decode(&data); err != nil { // Handle error. } // Instantiate the storage layer. store := storage.New(storage.InMemoryWithJSONConfig(data)) txn, err := store.NewTransaction(ctx) if err != nil { // Handle error. } defer store.Close(ctx, txn) // Read values out of storage. v1, err1 := store.Read(ctx, txn, storage.MustParsePath("/users/1/likes/1")) v2, err2 := store.Read(ctx, txn, storage.MustParsePath("/users/0/age")) // Inspect the return values. fmt.Println("v1:", v1) fmt.Println("err1:", err1) fmt.Println("v2:", v2) fmt.Println("err2:", err2) fmt.Println("err2 is not found:", storage.IsNotFound(err2)) // Output: // v1: wine // err1: <nil> // v2: <nil> // err2: storage error (code: 1): bad path: /users/0/age, document does not exist // err2 is not found: true }