Exemple #1
0
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
}
Exemple #2
0
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
}
Exemple #3
0
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
}
Exemple #4
0
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)
	}
}
Exemple #5
0
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>

}
Exemple #6
0
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)

}
Exemple #7
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()
}
Exemple #8
0
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())
	}
}
Exemple #9
0
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")
}
Exemple #10
0
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,
	}
}
Exemple #11
0
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())
	}
}
Exemple #12
0
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
}
Exemple #13
0
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)
	}
}
Exemple #14
0
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)
	}
}
Exemple #15
0
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))
}
Exemple #16
0
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

}
Exemple #17
0
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"))
	}
}
Exemple #18
0
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>
}
Exemple #19
0
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
}