Files
releases/v1/bundle/store_test.go
T
Ashutosh Narkar 5d5329dffb bundle: Add info about the correct rego version to parse modules on the store (#7278)
Fixing an issue where the rego-version for individual modules was lost during bundle deactivation (bundle lifecycle) if this version diverged from the active runtime rego-version. This could cause reloading of v0 bundles to fail when OPA was not running with the `--v0-compatible` flag.

Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
Co-authored-by: Johan Fylling <johan.dev@fylling.se>
2025-01-21 13:05:01 +01:00

6356 lines
153 KiB
Go

package bundle
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/open-policy-agent/opa/internal/file/archive"
"github.com/open-policy-agent/opa/internal/storage/mock"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/logging"
"github.com/open-policy-agent/opa/v1/metrics"
"github.com/open-policy-agent/opa/v1/util"
"github.com/open-policy-agent/opa/v1/util/test"
"github.com/open-policy-agent/opa/v1/storage"
"github.com/open-policy-agent/opa/v1/storage/disk"
"github.com/open-policy-agent/opa/v1/storage/inmem"
inmemtst "github.com/open-policy-agent/opa/v1/storage/inmem/test"
)
func TestManifestStoreLifecycleSingleBundle(t *testing.T) {
store := inmemtst.New()
ctx := context.Background()
tb := Manifest{
Revision: "abc123",
Roots: &[]string{"/a/b", "/a/c"},
}
name := "test_bundle"
verifyWriteManifests(ctx, t, store, map[string]Manifest{name: tb}) // write one
verifyReadBundleNames(ctx, t, store, []string{name}) // read one
verifyDeleteManifest(ctx, t, store, name) // delete it
verifyReadBundleNames(ctx, t, store, []string{}) // ensure it was removed
}
func TestManifestStoreLifecycleMultiBundle(t *testing.T) {
store := inmemtst.New()
ctx := context.Background()
bundles := map[string]Manifest{
"bundle1": {
Revision: "abc123",
Roots: &[]string{"/a/b", "/a/c"},
},
"bundle2": {
Revision: "def123",
Roots: &[]string{"/x/y", "/z"},
},
}
verifyWriteManifests(ctx, t, store, bundles) // write multiple
verifyReadBundleNames(ctx, t, store, []string{"bundle1", "bundle2"}) // read them
verifyDeleteManifest(ctx, t, store, "bundle1") // delete one
verifyReadBundleNames(ctx, t, store, []string{"bundle2"}) // ensure it was removed
verifyDeleteManifest(ctx, t, store, "bundle2") // delete the last one
verifyReadBundleNames(ctx, t, store, []string{}) // ensure it was removed
}
func TestLegacyManifestStoreLifecycle(t *testing.T) {
store := inmemtst.New()
ctx := context.Background()
tb := Manifest{
Revision: "abc123",
Roots: &[]string{"/a/b", "/a/c"},
}
// write a "legacy" manifest
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
if err := LegacyWriteManifestToStore(ctx, store, txn, tb); err != nil {
t.Fatalf("Failed to write manifest to store: %s", err)
return err
}
return nil
})
if err != nil {
t.Fatalf("Unexpected error finishing transaction: %s", err)
}
// make sure it can be retrieved
verifyReadLegacyRevision(ctx, t, store, tb.Revision)
// delete it
err = storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
if err := LegacyEraseManifestFromStore(ctx, store, txn); err != nil {
t.Fatalf("Failed to erase manifest from store: %s", err)
return err
}
return nil
})
if err != nil {
t.Fatalf("Unexpected error finishing transaction: %s", err)
}
verifyReadLegacyRevision(ctx, t, store, "")
}
func TestMixedManifestStoreLifecycle(t *testing.T) {
store := inmemtst.New()
ctx := context.Background()
bundles := map[string]Manifest{
"bundle1": {
Revision: "abc123",
Roots: &[]string{"/a/b", "/a/c"},
},
"bundle2": {
Revision: "def123",
Roots: &[]string{"/x/y", "/z"},
},
}
// Write the legacy one first
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
if err := LegacyWriteManifestToStore(ctx, store, txn, bundles["bundle1"]); err != nil {
t.Fatalf("Failed to write manifest to store: %s", err)
return err
}
return nil
})
if err != nil {
t.Fatalf("Unexpected error finishing transaction: %s", err)
}
verifyReadBundleNames(ctx, t, store, []string{})
// Write both new ones
verifyWriteManifests(ctx, t, store, bundles)
verifyReadBundleNames(ctx, t, store, []string{"bundle1", "bundle2"})
// Ensure the original legacy one is still there
verifyReadLegacyRevision(ctx, t, store, bundles["bundle1"].Revision)
}
func verifyDeleteManifest(ctx context.Context, t *testing.T, store storage.Store, name string) {
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
err := EraseManifestFromStore(ctx, store, txn, name)
if err != nil {
t.Fatalf("Failed to delete manifest from store: %s", err)
}
return err
})
if err != nil {
t.Fatalf("Unexpected error finishing transaction: %s", err)
}
}
func verifyWriteManifests(ctx context.Context, t *testing.T, store storage.Store, bundles map[string]Manifest) {
t.Helper()
for name, manifest := range bundles {
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
err := WriteManifestToStore(ctx, store, txn, name, manifest)
if err != nil {
t.Fatalf("Failed to write manifest to store: %s", err)
}
return err
})
if err != nil {
t.Fatalf("Unexpected error finishing transaction: %s", err)
}
}
}
func verifyReadBundleNames(ctx context.Context, t *testing.T, store storage.Store, expected []string) {
t.Helper()
var actualNames []string
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
var err error
actualNames, err = ReadBundleNamesFromStore(ctx, store, txn)
if err != nil && !storage.IsNotFound(err) {
t.Fatalf("Failed to read manifest names from store: %s", err)
return err
}
return nil
})
if err != nil {
t.Fatalf("Unexpected error finishing transaction: %s", err)
}
if len(actualNames) != len(expected) {
t.Fatalf("Expected %d name, found %d \n\t\tActual: %v\n", len(expected), len(actualNames), actualNames)
}
for _, actualName := range actualNames {
found := false
for _, expectedName := range expected {
if actualName == expectedName {
found = true
break
}
}
if !found {
t.Errorf("Found unexpecxted bundle name %s, expected names: %+v", actualName, expected)
}
}
}
func verifyReadLegacyRevision(ctx context.Context, t *testing.T, store storage.Store, expected string) {
t.Helper()
var actual string
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
var err error
if actual, err = LegacyReadRevisionFromStore(ctx, store, txn); err != nil && !storage.IsNotFound(err) {
t.Fatalf("Failed to read manifest revision from store: %s", err)
return err
}
return nil
})
if err != nil {
t.Fatalf("Unexpected error finishing transaction: %s", err)
}
if actual != expected {
t.Fatalf("Expected revision %s, got %s", expected, actual)
}
}
func TestBundleLazyModeNoPolicyOrData(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
bundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Roots: &[]string{"a"},
Revision: "foo",
},
Etag: "foo",
lazyLoadingMode: true,
},
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatal(err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the bundle was activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "foo",
"roots": ["a"]
},
"etag": "foo"
}
}
}
}
`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
}
func TestBundleLifecycle_ModuleRegoVersions(t *testing.T) {
type files [][2]string
type bundles map[string]files
type deactivation struct {
bundles map[string]struct{}
expData string
}
type activation struct {
bundles bundles
lazy bool
readWithBundleName bool
expData string
}
tests := []struct {
note string
updates []interface{}
}{
// single v0 bundle
{
note: "v0 bundle, lazy, read with bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
// Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v0 bundle, not lazy, read with bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: false,
readWithBundleName: true,
// Not lazy mode, bundle store decides that module name should be prefixed with bundle name.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v0 bundle, lazy, read with NO bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: false,
// Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader not initialized with bundle name, so prefix not expected.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"a/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v0 bundle, not lazy, read with NO bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: false,
readWithBundleName: false,
// Not lazy mode, bundle store decides that module name should be prefixed with bundle name.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
// single v1 bundle
{
note: "v1 bundle, lazy, read with bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: true,
readWithBundleName: true,
// Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader initialized with bundle name, so prefix is expected.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v1 bundle, not lazy, read with bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: false,
readWithBundleName: true,
// Not lazy mode, bundle store decides that module name should be prefixed with bundle name.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v1 bundle, lazy, read with NO bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: true,
readWithBundleName: false,
// Lazy mode, bundle reader decides if module name should be prefixed with bundle name; reader not initialized with bundle name, so prefix not expected.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v1 bundle, not lazy, read with NO bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: false,
readWithBundleName: false,
// Not lazy mode, bundle store decides that module name should be prefixed with bundle name.
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "custom bundle without rego-version, lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"]}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v0, lazy replaced by non-lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v0 bundle replaced by v1 bundle, lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v0 bundle replaced by v1 bundle, not lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v0 bundle replaced by custom bundle, not lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"]}`}, // no rego-version
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v1 bundle replaced by v0 bundle, lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "v1 bundle replaced by v0 bundle, not lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "custom bundle replaced by v0 bundle, lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"]}`}, // no rego-version
{"a/policy.rego", `package a
p contains 42 if { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":1}}
}
}`,
},
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "multiple v0 bundles, all dropped",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["b"], "rego_version": 0}`},
{"b/policy.rego", `package b
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}},
"bundle2":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["b"]}}
},
"modules":{"bundle1/a/policy.rego":{"rego_version":0},"bundle2/b/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "multiple v0 bundles, one dropped",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["b"], "rego_version": 0}`},
{"b/policy.rego", `package b
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}},
"bundle2":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["b"]}}
},
"modules":{"bundle1/a/policy.rego":{"rego_version":0},"bundle2/b/policy.rego":{"rego_version":0}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}},
expData: `{
"system":{
"bundles":{
"bundle2":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["b"]}}
},
"modules":{"bundle2/b/policy.rego":{"rego_version":0}}
}
}`,
},
},
},
{
note: "v0 bundle with v1 bundle added",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a"], "rego_version": 0}`},
{"a/policy.rego", `package a
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}}},
"modules":{"bundle1/a/policy.rego":{"rego_version":0}}
}
}`,
},
activation{
bundles: bundles{
"bundle2": {
{"/.manifest", `{"roots": ["b"], "rego_version": 1}`},
{"b/policy.rego", `package b
p contains 42 if { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"rego_version":0,"revision":"","roots":["a"]}},
"bundle2":{"etag":"bar","manifest":{"rego_version":1,"revision":"","roots":["b"]}}
},
"modules":{"bundle1/a/policy.rego":{"rego_version":0},"bundle2/b/policy.rego":{"rego_version":1}}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "mixed-version bundles, lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`},
{"a/policy.rego", `package a
p[42] { true }`},
{"b/policy.rego", `package b
p contains 42 if { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`},
{"c/policy.rego", `package c
p contains 42 if { true }`},
{"d/policy.rego", `package d
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}},
"bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}}
},
"modules":{
"bundle1/a/policy.rego":{"rego_version":0},
"bundle1/b/policy.rego":{"rego_version":1},
"bundle2/c/policy.rego":{"rego_version":1},
"bundle2/d/policy.rego":{"rego_version":0}
}
}
}`,
},
// replacing bundles
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`},
{"a/policy2.rego", `package a
q[42] { true }`},
{"b/policy2.rego", `package b
q contains 42 if { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`},
{"c/policy2.rego", `package c
q contains 42 if { true }`},
{"d/policy2.rego", `package d
q[42] { true }`},
},
},
lazy: true,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}},
"bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}}
},
"modules":{
"bundle1/a/policy2.rego":{"rego_version":0},
"bundle1/b/policy2.rego":{"rego_version":1},
"bundle2/c/policy2.rego":{"rego_version":1},
"bundle2/d/policy2.rego":{"rego_version":0}
}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "mixed-version bundles, lazy, read with NO bundle name",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`},
{"a/policy.rego", `package a
p[42] { true }`},
{"b/policy.rego", `package b
p contains 42 if { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`},
{"c/policy.rego", `package c
p contains 42 if { true }`},
{"d/policy.rego", `package d
p[42] { true }`},
},
},
lazy: true,
readWithBundleName: false,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}},
"bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}}
},
"modules":{
"a/policy.rego":{"rego_version":0},
"b/policy.rego":{"rego_version":1},
"c/policy.rego":{"rego_version":1},
"d/policy.rego":{"rego_version":0}
}
}
}`,
},
// replacing bundles
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`},
{"a/policy2.rego", `package a
q[42] { true }`},
{"b/policy2.rego", `package b
q contains 42 if { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`},
{"c/policy2.rego", `package c
q contains 42 if { true }`},
{"d/policy2.rego", `package d
q[42] { true }`},
},
},
lazy: true,
readWithBundleName: false,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}},
"bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}}
},
"modules":{
"a/policy2.rego":{"rego_version":0},
"b/policy2.rego":{"rego_version":1},
"c/policy2.rego":{"rego_version":1},
"d/policy2.rego":{"rego_version":0}
}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
{
note: "mixed-version bundles, not lazy",
updates: []interface{}{
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy.rego": 1}}`},
{"a/policy.rego", `package a
p[42] { true }`},
{"b/policy.rego", `package b
p contains 42 if { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy.rego": 0}}`},
{"c/policy.rego", `package c
p contains 42 if { true }`},
{"d/policy.rego", `package d
p[42] { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}},
"bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}}
},
"modules":{
"bundle1/a/policy.rego":{"rego_version":0},
"bundle1/b/policy.rego":{"rego_version":1},
"bundle2/c/policy.rego":{"rego_version":1},
"bundle2/d/policy.rego":{"rego_version":0}
}
}
}`,
},
// replacing bundles
activation{
bundles: bundles{
"bundle1": {
{"/.manifest", `{"roots": ["a", "b"], "rego_version": 0, "file_rego_versions": {"/b/policy2.rego": 1}}`},
{"a/policy2.rego", `package a
q[42] { true }`},
{"b/policy2.rego", `package b
q contains 42 if { true }`},
},
"bundle2": {
{"/.manifest", `{"roots": ["c", "d"], "rego_version": 1, "file_rego_versions": {"/d/policy2.rego": 0}}`},
{"c/policy2.rego", `package c
q contains 42 if { true }`},
{"d/policy2.rego", `package d
q[42] { true }`},
},
},
lazy: false,
readWithBundleName: true,
expData: `{
"system":{
"bundles":{
"bundle1":{"etag":"bar","manifest":{"file_rego_versions":{"/b/policy2.rego":1},"rego_version":0,"revision":"","roots":["a","b"]}},
"bundle2":{"etag":"bar","manifest":{"file_rego_versions":{"/d/policy2.rego":0},"rego_version":1,"revision":"","roots":["c","d"]}}
},
"modules":{
"bundle1/a/policy2.rego":{"rego_version":0},
"bundle1/b/policy2.rego":{"rego_version":1},
"bundle2/c/policy2.rego":{"rego_version":1},
"bundle2/d/policy2.rego":{"rego_version":0}
}
}
}`,
},
deactivation{
bundles: map[string]struct{}{"bundle1": {}, "bundle2": {}},
expData: `{"system":{"bundles":{},"modules":{}}}`,
},
},
},
}
for _, tc := range tests {
t.Run(tc.note, func(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
for _, update := range tc.updates {
if act, ok := update.(activation); ok {
bundles := map[string]*Bundle{}
for bundleName, files := range act.bundles {
buf := archive.MustWriteTarGz(files)
loader := NewTarballLoaderWithBaseURL(buf, "")
br := NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(act.lazy)
if act.readWithBundleName {
br = br.WithBundleName(bundleName)
}
bundle, err := br.Read()
if err != nil {
t.Fatal(err)
}
bundles[bundleName] = &bundle
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Start read transaction
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := act.expData
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected:\n\n%s\n\ngot:\n\n%s", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
} else if deact, ok := update.(deactivation); ok {
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := Deactivate(&DeactivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
BundleNames: deact.bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Start read transaction
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := deact.expData // `{"system": {"bundles": {}, "modules": {}}}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected:\n\n%s\n\ngot:\n\n%s", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
}
}
})
}
}
func TestBundleLazyModeLifecycleRaw(t *testing.T) {
files := [][2]string{
{"/a/b/c/data.json", "[1,2,3]"},
{"/a/b/d/data.json", "true"},
{"/a/b/y/data.yaml", `foo: 1`},
{"/example/example.rego", `package example
p contains 42 if { true }
`},
{"/example/example_v0.rego", `package example
q[42] { true }
`},
{"/authz/allow/policy.wasm", `wasm-module`},
{"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`},
{"/.manifest", `{
"revision": "foo",
"roots": ["a", "example", "x", "authz"],
"wasm":[{"entrypoint": "authz/allow", "module": "/authz/allow/policy.wasm"}],
"rego_version": 1,
"file_rego_versions": {"/example/example_v0.rego": 0}
}`},
}
buf := archive.MustWriteTarGz(files)
loader := NewTarballLoaderWithBaseURL(buf, "")
br := NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true)
bundle, err := br.Read()
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
extraMods := map[string]*ast.Module{
"mod1": ast.MustParseModule("package x\np = true"),
}
bundles := map[string]*Bundle{
"bundle1": &bundle,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
ExtraModules: extraMods,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the bundle was activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": {
"c": [1,2,3],
"d": true,
"y": {
"foo": 1
},
"z": true
}
},
"x": {
"y": true
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "foo",
"roots": ["a", "example", "x", "authz"],
"wasm": [
{
"entrypoint": "authz/allow",
"module": "/authz/allow/policy.wasm"
}
],
"rego_version": 1,
"file_rego_versions": {
"/example/example_v0.rego": 0
}
},
"etag": "bar",
"wasm": {
"/authz/allow/policy.wasm": "d2FzbS1tb2R1bGU="
}
}
},
"modules":{
"example/example.rego":{
"rego_version":1
},
"example/example_v0.rego":{
"rego_version":0
}
}
}
}
`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %s, got %s", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Ensure that the extra module was included
if _, ok := compiler.Modules["mod1"]; !ok {
t.Fatalf("expected extra module to be compiled")
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Deactivate(&DeactivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
BundleNames: map[string]struct{}{"bundle1": {}},
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Expect the store to have been cleared out after deactivating the bundle
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err = ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != 0 {
t.Fatalf("expected 0 bundles in store, found %d", len(names))
}
actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `{"system": {"bundles": {}, "modules": {}}}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
mockStore.AssertValid(t)
}
func TestBundleLazyModeLifecycleRawInvalidData(t *testing.T) {
tests := map[string]struct {
files [][2]string
err error
}{
"non-object root": {[][2]string{{"/data.json", `[1,2,3]`}}, fmt.Errorf("root value must be object")},
"invalid yaml": {[][2]string{{"/a/b/data.yaml", `"foo`}}, fmt.Errorf("yaml: found unexpected end of stream")},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
buf := archive.MustWriteTarGz(tc.files)
loader := NewTarballLoaderWithBaseURL(buf, "")
br := NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true)
bundle, err := br.Read()
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
bundles := map[string]*Bundle{
"bundle1": &bundle,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if tc.err != nil && tc.err.Error() != err.Error() {
t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error())
}
})
}
}
func TestBundleLazyModeLifecycle(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
extraMods := map[string]*ast.Module{
"mod1": ast.MustParseModule("package x\np = true"),
}
// v1 bundle
mod1 := `package a
p contains 42 if { true }
`
b1Files := [][2]string{
{"/.manifest", `{"roots": ["a"], "rego_version": 1}`},
{"a/policy.rego", mod1},
{"/data.json", `{"a": {"b": "foo"}}`},
}
buf := archive.MustWriteTarGz(b1Files)
loader := NewTarballLoaderWithBaseURL(buf, "")
br := NewCustomReader(loader).WithBundleEtag("foo").WithLazyLoadingMode(true).WithBundleName("bundle1")
bundle1, err := br.Read()
if err != nil {
t.Fatal(err)
}
// v0 bundle
mod2 := `package b
p[42] { true }
`
b2Files := [][2]string{
{"/.manifest", `{"roots": ["b", "c"], "rego_version": 0}`},
{"b/policy.rego", mod2},
{"/data.json", `{}`},
}
buf = archive.MustWriteTarGz(b2Files)
loader = NewTarballLoaderWithBaseURL(buf, "")
br = NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2")
bundle2, err := br.Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
ExtraModules: extraMods,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the bundle was activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo"
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "",
"roots": ["a"],
"rego_version": 1
},
"etag": "foo"
},
"bundle2": {
"manifest": {
"revision": "",
"roots": ["b", "c"],
"rego_version": 0
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
},
"bundle2/b/policy.rego":{
"rego_version":0
}
}
}
}
`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Ensure that the extra module was included
if _, ok := compiler.Modules["mod1"]; !ok {
t.Fatalf("expected extra module to be compiled")
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Deactivate(&DeactivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
BundleNames: map[string]struct{}{"bundle1": {}, "bundle2": {}},
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Expect the store to have been cleared out after deactivating the bundles
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err = ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != 0 {
t.Fatalf("expected 0 bundles in store, found %d", len(names))
}
actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `{"system": {"bundles": {}, "modules": {}}}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
mockStore.AssertValid(t)
}
func TestBundleLazyModeLifecycleRawNoBundleRoots(t *testing.T) {
files := [][2]string{
{"/a/b/c/data.json", "[1,2,3]"},
{"/a/b/d/data.json", "true"},
{"/a/b/y/data.yaml", `foo: 1`},
{"/example/example.rego", `package example`},
{"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`},
{"/.manifest", `{"revision": "rev-1"}`},
}
buf := archive.MustWriteTarGz(files)
loader := NewTarballLoaderWithBaseURL(buf, "")
br := NewCustomReader(loader).WithBundleEtag("foo").WithLazyLoadingMode(true)
bundle, err := br.Read()
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
bundles := map[string]*Bundle{
"bundle1": &bundle,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the bundle was activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": {
"c": [1,2,3],
"d": true,
"y": {
"foo": 1
},
"z": true
}
},
"x": {
"y": true
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-1",
"roots": [""]
},
"etag": "foo"
}
},
"modules":{
"example/example.rego":{
"rego_version":1
}
}
}
}
`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
files = [][2]string{
{"/c/data.json", `{"hello": "world"}`},
{"/.manifest", `{"revision": "rev-2"}`},
}
buf = archive.MustWriteTarGz(files)
loader = NewTarballLoaderWithBaseURL(buf, "")
br = NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true)
bundle, err = br.Read()
if err != nil {
t.Fatal(err)
}
bundles = map[string]*Bundle{
"bundle1": &bundle,
}
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `
{
"c": {
"hello": "world"
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-2",
"roots": [""]
},
"etag": "bar"
}
}
}
}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
}
func TestBundleLazyModeLifecycleRawNoBundleRootsDiskStorage(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
files := [][2]string{
{"/a/b/c/data.json", "[1,2,3]"},
{"/a/b/d/data.json", "true"},
{"/a/b/y/data.yaml", `foo: 1`},
{"/example/example.rego", `package example`},
{"/data.json", `{"x": {"y": true}, "a": {"b": {"z": true}}}`},
{"/.manifest", `{"revision": "rev-1"}`},
}
buf := archive.MustWriteTarGz(files)
loader := NewTarballLoaderWithBaseURL(buf, "")
br := NewCustomReader(loader).WithBundleEtag("foo").WithLazyLoadingMode(true)
bundle, err := br.Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the bundle was activated
txn = storage.NewTransactionOrDie(ctx, store)
names, err := ReadBundleNamesFromStore(ctx, store, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": {
"c": [1,2,3],
"d": true,
"y": {
"foo": 1
},
"z": true
}
},
"x": {
"y": true
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-1",
"roots": [""]
},
"etag": "foo"
}
},
"modules":{
"example/example.rego":{
"rego_version":1
}
}
}
}
`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
files = [][2]string{
{"/c/data.json", `{"hello": "world"}`},
{"/.manifest", `{"revision": "rev-2"}`},
}
buf = archive.MustWriteTarGz(files)
loader = NewTarballLoaderWithBaseURL(buf, "")
br = NewCustomReader(loader).WithBundleEtag("bar").WithLazyLoadingMode(true)
bundle, err = br.Read()
if err != nil {
t.Fatal(err)
}
bundles = map[string]*Bundle{
"bundle1": &bundle,
}
txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
txn = storage.NewTransactionOrDie(ctx, store)
actual, err = store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `
{
"c": {
"hello": "world"
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-2",
"roots": [""]
},
"etag": "bar"
}
}
}
}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
})
}
func TestBundleLazyModeLifecycleNoBundleRoots(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
b := Bundle{
Manifest: Manifest{Revision: "rev-1"},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo",
"e": {
"f": "bar"
},
"x": [{"name": "john"}, {"name": "jane"}]
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-1",
"roots": [""]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// add a new bundle with no roots. this means all the data from the currently activated should be removed
b = Bundle{
Manifest: Manifest{Revision: "rev-2"},
Data: map[string]interface{}{
"c": map[string]interface{}{
"hello": "world",
},
},
Etag: "bar",
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles = map[string]*Bundle{
"bundle1": &bundle2,
}
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `
{
"c": {
"hello": "world"
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-2",
"roots": [""]
},
"etag": ""
}
}
}
}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
}
func TestBundleLazyModeLifecycleNoBundleRootsDiskStorage(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
b := Bundle{
Manifest: Manifest{Revision: "rev-1"},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundle was activated
txn = storage.NewTransactionOrDie(ctx, store)
names, err := ReadBundleNamesFromStore(ctx, store, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo",
"e": {
"f": "bar"
},
"x": [{"name": "john"}, {"name": "jane"}]
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-1",
"roots": [""]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
// add a new bundle with no roots. this means all the data from the currently activated should be removed
b = Bundle{
Manifest: Manifest{Revision: "rev-2"},
Data: map[string]interface{}{
"c": map[string]interface{}{
"hello": "world",
},
},
Etag: "bar",
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles = map[string]*Bundle{
"bundle1": &bundle2,
}
txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundle was activated
txn = storage.NewTransactionOrDie(ctx, store)
actual, err = store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `
{
"c": {
"hello": "world"
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-2",
"roots": [""]
},
"etag": ""
}
}
}
}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
})
}
func TestBundleLazyModeLifecycleMixBundleTypeActivationDiskStorage(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
b := Bundle{
Manifest: Manifest{
Revision: "snap-1",
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
// create a delta bundle and activate it
// add a new object member
p1 := PatchOperation{
Op: "upsert",
Path: "/x/y",
Value: []string{"foo", "bar"},
}
b = Bundle{
Manifest: Manifest{
Revision: "delta-1",
Roots: &[]string{"x"},
},
Patch: Patch{Data: []PatchOperation{p1}},
Etag: "bar",
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, store)
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo",
"e": {
"f": "bar"
},
"x": [{"name": "john"}, {"name": "jane"}]
},
"x": {
"y": ["foo","bar"]
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "snap-1",
"roots": ["a"]
},
"etag": ""
},
"bundle2": {
"manifest": {
"revision": "delta-1",
"roots": ["x"]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
})
}
func TestBundleLazyModeLifecycleOldBundleEraseDiskStorage(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
b := Bundle{
Manifest: Manifest{Revision: "rev-1", Roots: &[]string{"a"}},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundle was activated
txn = storage.NewTransactionOrDie(ctx, store)
names, err := ReadBundleNamesFromStore(ctx, store, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo",
"e": {
"f": "bar"
},
"x": [{"name": "john"}, {"name": "jane"}]
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-1",
"roots": ["a"]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
// add a new bundle and verify data from the currently activated is removed
b = Bundle{
Manifest: Manifest{Revision: "rev-2", Roots: &[]string{"c"}},
Data: map[string]interface{}{
"c": map[string]interface{}{
"hello": "world",
},
},
Etag: "bar",
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles = map[string]*Bundle{
"bundle1": &bundle2,
}
txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundle was activated
txn = storage.NewTransactionOrDie(ctx, store)
actual, err = store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `
{
"c": {
"hello": "world"
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-2",
"roots": ["c"]
},
"etag": ""
}
}
}
}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
})
}
func TestBundleLazyModeLifecycleRestoreBackupDB(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
b := Bundle{
Manifest: Manifest{Revision: "rev-1", Roots: &[]string{"a"}},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundle was activated
txn = storage.NewTransactionOrDie(ctx, store)
names, err := ReadBundleNamesFromStore(ctx, store, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo",
"e": {
"f": "bar"
},
"x": [{"name": "john"}, {"name": "jane"}]
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-1",
"roots": ["a"]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
// add a new bundle but abort the transaction and verify only old the bundle data is kept in store
b = Bundle{
Manifest: Manifest{Revision: "rev-2", Roots: &[]string{"c"}},
Data: map[string]interface{}{
"c": map[string]interface{}{
"hello": "world",
},
},
Etag: "bar",
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
bundles = map[string]*Bundle{
"bundle1": &bundle2,
}
txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
store.Abort(ctx, txn)
txn = storage.NewTransactionOrDie(ctx, store)
actual, err = store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `
{
"a": {
"b": "foo",
"e": {
"f": "bar"
},
"x": [{"name": "john"}, {"name": "jane"}]
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "rev-1",
"roots": ["a"]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
}
}
}
}`
expected = loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
// check symlink is created
symlink := filepath.Join(dir, "active")
_, err = os.Lstat(symlink)
if err != nil {
t.Fatal(err)
}
// check symlink target
_, err = filepath.EvalSymlinks(symlink)
if err != nil {
t.Fatalf("eval symlinks: %v", err)
}
})
}
func TestDeltaBundleLazyModeLifecycleDiskStorage(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
mod2 := "package b\np = true"
b := Bundle{
Manifest: Manifest{
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
b = Bundle{
Manifest: Manifest{
Roots: &[]string{"b", "c"},
},
Data: nil,
Modules: []ModuleFile{
{
Path: "b/policy.rego",
Raw: []byte(mod2),
Parsed: ast.MustParseModule(mod2),
},
},
Etag: "foo",
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, store)
names, err := ReadBundleNamesFromStore(ctx, store, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Stop the "read" transaction
store.Abort(ctx, txn)
// create a delta bundle and activate it
// add a new object member
p1 := PatchOperation{
Op: "upsert",
Path: "/a/c/d",
Value: []string{"foo", "bar"},
}
// append value to array
p2 := PatchOperation{
Op: "upsert",
Path: "/a/c/d/-",
Value: "baz",
}
// replace a value
p3 := PatchOperation{
Op: "replace",
Path: "a/b",
Value: "bar",
}
// add a new object root
p4 := PatchOperation{
Op: "upsert",
Path: "/c/d",
Value: []string{"foo", "bar"},
}
deltaBundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Revision: "delta-1",
Roots: &[]string{"a"},
},
Patch: Patch{Data: []PatchOperation{p1, p2, p3}},
Etag: "bar",
},
"bundle2": {
Manifest: Manifest{
Revision: "delta-2",
Roots: &[]string{"b", "c"},
},
Patch: Patch{Data: []PatchOperation{p4}},
Etag: "baz",
},
"bundle3": {
Manifest: Manifest{
Roots: &[]string{"d"},
},
Data: map[string]interface{}{
"d": map[string]interface{}{
"e": "foo",
},
},
},
}
txn = storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: deltaBundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// check the modules from the snapshot bundles are on the compiler
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, store)
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "bar",
"c": {
"d": ["foo", "bar", "baz"]
},
"e": {
"f": "bar"
},
"x": [{"name": "john"}, {"name": "jane"}]
},
"c": {"d": ["foo", "bar"]},
"d": {"e": "foo"},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "delta-1",
"roots": ["a"]
},
"etag": "bar"
},
"bundle2": {
"manifest": {
"revision": "delta-2",
"roots": ["b", "c"]
},
"etag": "baz"
},
"bundle3": {
"manifest": {
"revision": "",
"roots": ["d"]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
},
"bundle2/b/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
})
}
func TestBundleLazyModeLifecycleOverlappingBundleRoots(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
b := Bundle{
Manifest: Manifest{
Revision: "foo",
Roots: &[]string{"a/b", "a/c", "a/d"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"c": map[string]interface{}{
"d": "bar",
},
"d": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
b = Bundle{
Manifest: Manifest{
Revision: "bar",
Roots: &[]string{"a/e"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"e": map[string]interface{}{
"f": "bar",
},
},
},
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo",
"c": {
"d": "bar"
},
"e": {
"f": "bar"
},
"d": [{"name": "john"}, {"name": "jane"}]
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "foo",
"roots": ["a/b", "a/c", "a/d"]
},
"etag": ""
},
"bundle2": {
"manifest": {
"revision": "bar",
"roots": ["a/e"]
},
"etag": ""
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
}
func TestBundleLazyModeLifecycleOverlappingBundleRootsDiskStorage(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
b := Bundle{
Manifest: Manifest{
Revision: "foo",
Roots: &[]string{"a/b/c", "a/b/d", "a/b/e"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": map[string]interface{}{
"c": "bar",
"d": []map[string]string{{"name": "john"}, {"name": "jane"}},
"e": []string{"foo", "bar"},
},
},
},
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
b = Bundle{
Manifest: Manifest{
Revision: "bar",
Roots: &[]string{"a/b/f"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": map[string]interface{}{
"f": map[string]interface{}{
"hello": "world",
},
},
},
},
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, store)
names, err := ReadBundleNamesFromStore(ctx, store, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
// Stop the "read" transaction
store.Abort(ctx, txn)
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, store)
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": {
"c": "bar",
"d": [{"name": "john"}, {"name": "jane"}],
"e": ["foo", "bar"],
"f": {"hello": "world"}
}
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "foo",
"roots": ["a/b/c", "a/b/d", "a/b/e"]
},
"etag": ""
},
"bundle2": {
"manifest": {
"revision": "bar",
"roots": ["a/b/f"]
},
"etag": ""
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
})
}
func TestBundleLazyModeLifecycleRawOverlappingBundleRoots(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
files := [][2]string{
{"/a/b/x/data.json", "[1,2,3]"},
{"/a/c/y/data.json", "true"},
{"/a/d/z/data.yaml", `foo: 1`},
{"/data.json", `{"a": {"b": {"z": true}}}`},
{"/.manifest", `{"revision": "foo", "roots": ["a/b", "a/c", "a/d"]}`},
}
buf := archive.MustWriteTarGz(files)
loader := NewTarballLoaderWithBaseURL(buf, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
files = [][2]string{
{"/a/e/x/data.json", "[4,5,6]"},
{"/data.json", `{"a": {"e": {"f": true}}}`},
{"/.manifest", `{"revision": "bar", "roots": ["a/e"]}`},
}
buf = archive.MustWriteTarGz(files)
loader = NewTarballLoaderWithBaseURL(buf, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": {
"x": [1,2,3],
"z": true
},
"c": {
"y": true
},
"d": {
"z": {"foo": 1}
},
"e": {
"x": [4,5,6],
"f": true
}
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "foo",
"roots": ["a/b", "a/c", "a/d"]
},
"etag": ""
},
"bundle2": {
"manifest": {
"revision": "bar",
"roots": ["a/e"]
},
"etag": ""
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
}
func TestBundleLazyModeLifecycleRawOverlappingBundleRootsDiskStorage(t *testing.T) {
ctx := context.Background()
test.WithTempFS(nil, func(dir string) {
store, err := disk.New(ctx, logging.NewNoOpLogger(), nil, disk.Options{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
compiler := ast.NewCompiler()
m := metrics.New()
files := [][2]string{
{"/a/b/u/data.json", "[1,2,3]"},
{"/a/b/v/data.json", "true"},
{"/a/b/w/data.yaml", `foo: 1`},
{"/data.json", `{"a": {"b": {"x": true}}}`},
{"/.manifest", `{"revision": "foo", "roots": ["a/b"]}`},
}
buf := archive.MustWriteTarGz(files)
loader := NewTarballLoaderWithBaseURL(buf, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
files = [][2]string{
{"/a/c/x/data.json", "[4,5,6]"},
{"/data.json", `{"a": {"c": {"y": true}}}`},
{"/.manifest", `{"revision": "bar", "roots": ["a/c"]}`},
}
buf = archive.MustWriteTarGz(files)
loader = NewTarballLoaderWithBaseURL(buf, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: store,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = store.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, store)
names, err := ReadBundleNamesFromStore(ctx, store, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
// Stop the "read" transaction
store.Abort(ctx, txn)
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, store)
actual, err := store.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": {
"u": [1,2,3],
"v": true,
"w": {"foo": 1},
"x": true
},
"c": {
"x": [4,5,6],
"y": true
}
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "foo",
"roots": ["a/b"]
},
"etag": ""
},
"bundle2": {
"manifest": {
"revision": "bar",
"roots": ["a/c"]
},
"etag": ""
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
store.Abort(ctx, txn)
})
}
func TestDeltaBundleLazyModeLifecycle(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
mod2 := "package b\np = true"
b := Bundle{
Manifest: Manifest{
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
b = Bundle{
Manifest: Manifest{
Roots: &[]string{"b", "c"},
},
Data: nil,
Modules: []ModuleFile{
{
Path: "policy.rego",
Raw: []byte(mod2),
Parsed: ast.MustParseModule(mod2),
},
},
Etag: "foo",
lazyLoadingMode: true,
sizeLimitBytes: DefaultSizeLimitBytes + 1,
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// create a delta bundle and activate it
// add a new object member
p1 := PatchOperation{
Op: "upsert",
Path: "/a/c/d",
Value: []string{"foo", "bar"},
}
// append value to array
p2 := PatchOperation{
Op: "upsert",
Path: "/a/c/d/-",
Value: "baz",
}
// insert value in array
p3 := PatchOperation{
Op: "upsert",
Path: "/a/x/1",
Value: map[string]string{"name": "alice"},
}
// replace a value
p4 := PatchOperation{
Op: "replace",
Path: "a/b",
Value: "bar",
}
// remove a value
p5 := PatchOperation{
Op: "remove",
Path: "a/e",
}
// add a new object with an escaped character in the path
p6 := PatchOperation{
Op: "upsert",
Path: "a/y/~0z",
Value: []int{1, 2, 3},
}
// add a new object root
p7 := PatchOperation{
Op: "upsert",
Path: "/c/d",
Value: []string{"foo", "bar"},
}
deltaBundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Revision: "delta-1",
Roots: &[]string{"a"},
},
Patch: Patch{Data: []PatchOperation{p1, p2, p3, p4, p5, p6}},
Etag: "bar",
},
"bundle2": {
Manifest: Manifest{
Revision: "delta-2",
Roots: &[]string{"b", "c"},
},
Patch: Patch{Data: []PatchOperation{p7}},
Etag: "baz",
},
"bundle3": {
Manifest: Manifest{
Roots: &[]string{"d"},
},
Data: map[string]interface{}{
"d": map[string]interface{}{
"e": "foo",
},
},
},
}
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: deltaBundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// check the modules from the snapshot bundles are on the compiler
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "bar",
"c": {
"d": ["foo", "bar", "baz"]
},
"x": [{"name": "john"}, {"name": "alice"}, {"name": "jane"}],
"y": {"~z": [1, 2, 3]}
},
"c": {"d": ["foo", "bar"]},
"d": {"e": "foo"},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "delta-1",
"roots": ["a"]
},
"etag": "bar"
},
"bundle2": {
"manifest": {
"revision": "delta-2",
"roots": ["b", "c"]
},
"etag": "baz"
},
"bundle3": {
"manifest": {
"revision": "",
"roots": ["d"]
},
"etag": ""
}
},
"modules":{
"bundle1/policy.rego":{
"rego_version":1
},
"bundle2/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
mockStore.AssertValid(t)
}
func TestDeltaBundleLazyModeWithDefaultRules(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\ndefault p = true"
mod2 := "package b\ndefault p = true"
b := Bundle{
Manifest: Manifest{
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
}
var buf1 bytes.Buffer
if err := NewWriter(&buf1).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader := NewTarballLoaderWithBaseURL(&buf1, "")
bundle1, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle1").Read()
if err != nil {
t.Fatal(err)
}
b = Bundle{
Manifest: Manifest{
Roots: &[]string{"b", "c"},
},
Data: nil,
Modules: []ModuleFile{
{
Path: "policy.rego",
Raw: []byte(mod2),
Parsed: ast.MustParseModule(mod2),
},
},
Etag: "foo",
lazyLoadingMode: true,
sizeLimitBytes: DefaultSizeLimitBytes + 1,
}
var buf2 bytes.Buffer
if err := NewWriter(&buf2).UseModulePath(true).Write(b); err != nil {
t.Fatal("Unexpected error:", err)
}
loader = NewTarballLoaderWithBaseURL(&buf2, "")
bundle2, err := NewCustomReader(loader).WithLazyLoadingMode(true).WithBundleName("bundle2").Read()
if err != nil {
t.Fatal(err)
}
bundles := map[string]*Bundle{
"bundle1": &bundle1,
"bundle2": &bundle2,
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// create a delta bundle and activate it
// add a new object member
p1 := PatchOperation{
Op: "upsert",
Path: "/a/c/d",
Value: []string{"foo", "bar"},
}
// append value to array
p2 := PatchOperation{
Op: "upsert",
Path: "/a/c/d/-",
Value: "baz",
}
// insert value in array
p3 := PatchOperation{
Op: "upsert",
Path: "/a/x/1",
Value: map[string]string{"name": "alice"},
}
// replace a value
p4 := PatchOperation{
Op: "replace",
Path: "a/b",
Value: "bar",
}
// remove a value
p5 := PatchOperation{
Op: "remove",
Path: "a/e",
}
// add a new object with an escaped character in the path
p6 := PatchOperation{
Op: "upsert",
Path: "a/y/~0z",
Value: []int{1, 2, 3},
}
// add a new object root
p7 := PatchOperation{
Op: "upsert",
Path: "/c/d",
Value: []string{"foo", "bar"},
}
deltaBundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Revision: "delta-1",
Roots: &[]string{"a"},
},
Patch: Patch{Data: []PatchOperation{p1, p2, p3, p4, p5, p6}},
Etag: "bar",
},
"bundle2": {
Manifest: Manifest{
Revision: "delta-2",
Roots: &[]string{"b", "c"},
},
Patch: Patch{Data: []PatchOperation{p7}},
Etag: "baz",
},
"bundle3": {
Manifest: Manifest{
Roots: &[]string{"d"},
},
Data: map[string]interface{}{
"d": map[string]interface{}{
"e": "foo",
},
},
},
}
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
expectedModuleCount := len(compiler.Modules)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: deltaBundles,
})
if expectedModuleCount != len(compiler.Modules) {
t.Fatalf("Expected %d modules, got %d", expectedModuleCount, len(compiler.Modules))
}
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// check the modules from the snapshot bundles are on the compiler
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "bar",
"c": {
"d": ["foo", "bar", "baz"]
},
"x": [{"name": "john"}, {"name": "alice"}, {"name": "jane"}],
"y": {"~z": [1, 2, 3]}
},
"c": {"d": ["foo", "bar"]},
"d": {"e": "foo"},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "delta-1",
"roots": ["a"]
},
"etag": "bar"
},
"bundle2": {
"manifest": {
"revision": "delta-2",
"roots": ["b", "c"]
},
"etag": "baz"
},
"bundle3": {
"manifest": {
"revision": "",
"roots": ["d"]
},
"etag": ""
}
},
"modules":{
"bundle1/policy.rego":{
"rego_version":1
},
"bundle2/policy.rego":{
"rego_version":1
}
}
}
}`
expected := loadExpectedSortedResult(expectedRaw)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %v, got %v", expectedRaw, string(util.MustMarshalJSON(actual)))
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
mockStore.AssertValid(t)
}
func TestBundleLifecycle(t *testing.T) {
tests := []struct {
note string
readAst bool
}{
{
note: "read raw",
readAst: false,
},
{
note: "read ast",
readAst: true,
},
}
for _, tc := range tests {
t.Run(tc.note, func(t *testing.T) {
ctx := context.Background()
mockStore := mock.New(inmem.OptReturnASTValuesOnRead(tc.readAst))
compiler := ast.NewCompiler()
m := metrics.New()
extraMods := map[string]*ast.Module{
"mod1": ast.MustParseModule("package x\np = true"),
}
const mod2 = "package a\np = true"
mod3 := "package b\np = true"
bundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod2),
Parsed: ast.MustParseModule(mod2),
},
},
Etag: "foo"},
"bundle2": {
Manifest: Manifest{
Roots: &[]string{"b", "c"},
},
Data: nil,
Modules: []ModuleFile{
{
Path: "b/policy.rego",
Raw: []byte(mod3),
Parsed: ast.MustParseModule(mod3),
},
},
},
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
ExtraModules: extraMods,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the bundle was activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "foo"
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "",
"roots": ["a"]
},
"etag": "foo"
},
"bundle2": {
"manifest": {
"revision": "",
"roots": ["b", "c"]
},
"etag": ""
}
},
"modules": {
"bundle1/a/policy.rego": {
"rego_version": 1
},
"bundle2/b/policy.rego": {
"rego_version": 1
}
}
}
}
`
assertEqual(t, tc.readAst, expectedRaw, actual)
// Ensure that the extra module was included
if _, ok := compiler.Modules["mod1"]; !ok {
t.Fatalf("expected extra module to be compiled")
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Deactivate(&DeactivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
BundleNames: map[string]struct{}{"bundle1": {}, "bundle2": {}},
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Expect the store to have been cleared out after deactivating the bundles
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err = ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatal(err)
}
if len(names) != 0 {
t.Fatalf("expected 0 bundles in store, found %d", len(names))
}
actual, err = mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw = `{"system": {"bundles": {}, "modules": {}}}`
assertEqual(t, tc.readAst, expectedRaw, actual)
mockStore.AssertValid(t)
})
}
}
func TestDeltaBundleLifecycle(t *testing.T) {
tests := []struct {
note string
readAst bool
}{
{
note: "read raw",
readAst: false,
},
{
note: "read ast",
readAst: true,
},
}
for _, tc := range tests {
t.Run(tc.note, func(t *testing.T) {
ctx := context.Background()
mockStore := mock.New(inmem.OptReturnASTValuesOnRead(tc.readAst))
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
mod2 := "package b\np = true"
bundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"e": map[string]interface{}{
"f": "bar",
},
"x": []map[string]string{{"name": "john"}, {"name": "jane"}},
},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
Etag: "foo",
},
"bundle2": {
Manifest: Manifest{
Roots: &[]string{"b", "c"},
},
Data: nil,
Modules: []ModuleFile{
{
Path: "b/policy.rego",
Raw: []byte(mod2),
Parsed: ast.MustParseModule(mod2),
},
},
},
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundles were activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// create a delta bundle and activate it
// add a new object member
p1 := PatchOperation{
Op: "upsert",
Path: "/a/c/d",
Value: []string{"foo", "bar"},
}
// append value to array
p2 := PatchOperation{
Op: "upsert",
Path: "/a/c/d/-",
Value: "baz",
}
// insert value in array
p3 := PatchOperation{
Op: "upsert",
Path: "/a/x/1",
Value: map[string]string{"name": "alice"},
}
// replace a value
p4 := PatchOperation{
Op: "replace",
Path: "a/b",
Value: "bar",
}
// remove a value
p5 := PatchOperation{
Op: "remove",
Path: "a/e",
}
// add a new object with an escaped character in the path
p6 := PatchOperation{
Op: "upsert",
Path: "a/y/~0z",
Value: []int{1, 2, 3},
}
// add a new object root
p7 := PatchOperation{
Op: "upsert",
Path: "/c/d",
Value: []string{"foo", "bar"},
}
deltaBundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Revision: "delta-1",
Roots: &[]string{"a"},
},
Patch: Patch{Data: []PatchOperation{p1, p2, p3, p4, p5, p6}},
Etag: "bar",
},
"bundle2": {
Manifest: Manifest{
Revision: "delta-2",
Roots: &[]string{"b", "c"},
},
Patch: Patch{Data: []PatchOperation{p7}},
Etag: "baz",
},
"bundle3": {
Manifest: Manifest{
Roots: &[]string{"d"},
},
Data: map[string]interface{}{
"d": map[string]interface{}{
"e": "foo",
},
},
},
}
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: deltaBundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// check the modules from the snapshot bundles are on the compiler
for bundleName, bundle := range bundles {
for modName := range bundle.ParsedModules(bundleName) {
if _, ok := compiler.Modules[modName]; !ok {
t.Fatalf("expected module %s from bundle %s to have been compiled", modName, bundleName)
}
}
}
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"b": "bar",
"c": {
"d": ["foo", "bar", "baz"]
},
"x": [{"name": "john"}, {"name": "alice"}, {"name": "jane"}],
"y": {"~z": [1, 2, 3]}
},
"c": {"d": ["foo", "bar"]},
"d": {"e": "foo"},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "delta-1",
"roots": ["a"]
},
"etag": "bar"
},
"bundle2": {
"manifest": {
"revision": "delta-2",
"roots": ["b", "c"]
},
"etag": "baz"
},
"bundle3": {
"manifest": {
"revision": "",
"roots": ["d"]
},
"etag": ""
}
},
"modules":{
"bundle1/a/policy.rego":{
"rego_version":1
},
"bundle2/b/policy.rego":{
"rego_version":1
}
}
}
}`
assertEqual(t, tc.readAst, expectedRaw, actual)
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
mockStore.AssertValid(t)
})
}
}
func TestDeltaBundleActivate(t *testing.T) {
tests := []struct {
note string
readAst bool
}{
{
note: "read raw",
readAst: false,
},
{
note: "read ast",
readAst: true,
},
}
for _, tc := range tests {
t.Run(tc.note, func(t *testing.T) {
ctx := context.Background()
mockStore := mock.New(inmem.OptReturnASTValuesOnRead(tc.readAst))
compiler := ast.NewCompiler()
m := metrics.New()
// create a delta bundle
p1 := PatchOperation{
Op: "upsert",
Path: "/a/c/d",
Value: []string{"foo", "bar"},
}
deltaBundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Revision: "delta",
Roots: &[]string{"a"},
},
Patch: Patch{Data: []PatchOperation{p1}},
Etag: "foo",
},
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: deltaBundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the delta bundle was activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(deltaBundles) {
t.Fatalf("expected %d bundles in store, found %d", len(deltaBundles), len(names))
}
for _, name := range names {
if _, ok := deltaBundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// Ensure the patches were applied
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedRaw := `
{
"a": {
"c": {
"d": ["foo", "bar"]
}
},
"system": {
"bundles": {
"bundle1": {
"manifest": {
"revision": "delta",
"roots": ["a"]
},
"etag": "foo"
}
}
}
}
`
assertEqual(t, tc.readAst, expectedRaw, actual)
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
mockStore.AssertValid(t)
})
}
}
func assertEqual(t *testing.T, expectAst bool, expected string, actual interface{}) {
t.Helper()
if expectAst {
exp := ast.MustParseTerm(expected)
if ast.Compare(exp, actual) != 0 {
t.Errorf("expected:\n\n%v\n\ngot:\n\n%v", expected, actual)
}
} else {
exp := loadExpectedSortedResult(expected)
if !reflect.DeepEqual(exp, actual) {
t.Errorf("expected:\n\n%v\n\ngot:\n\n%v", expected, actual)
}
}
}
func TestDeltaBundleBadManifest(t *testing.T) {
ctx := context.Background()
mockStore := mock.New()
compiler := ast.NewCompiler()
m := metrics.New()
mod1 := "package a\np = true"
bundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Roots: &[]string{"a"},
},
Modules: []ModuleFile{
{
Path: "a/policy.rego",
Raw: []byte(mod1),
Parsed: ast.MustParseModule(mod1),
},
},
},
}
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
// Ensure the snapshot bundle was activated
txn = storage.NewTransactionOrDie(ctx, mockStore)
names, err := ReadBundleNamesFromStore(ctx, mockStore, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(names) != len(bundles) {
t.Fatalf("expected %d bundles in store, found %d", len(bundles), len(names))
}
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Fatalf("unexpected bundle name found in store: %s", name)
}
}
// Stop the "read" transaction
mockStore.Abort(ctx, txn)
// create a delta bundle with a different manifest from the snapshot bundle
p1 := PatchOperation{
Op: "upsert",
Path: "/a/c/d",
Value: []string{"foo", "bar"},
}
deltaBundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Roots: &[]string{"b"},
},
Patch: Patch{Data: []PatchOperation{p1}},
},
}
txn = storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err = Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: compiler,
Metrics: m,
Bundles: deltaBundles,
})
if err == nil {
t.Fatal("expected error but got nil")
}
expected := "delta bundle 'bundle1' has wasm resolvers or manifest roots that are different from those in the store"
if err.Error() != expected {
t.Fatalf("Expected error %v but got %v", expected, err.Error())
}
mockStore.AssertValid(t)
}
func TestEraseData(t *testing.T) {
storeReadModes := []struct {
note string
readAst bool
}{
{
note: "read raw",
readAst: false,
},
{
note: "read ast",
readAst: true,
},
}
ctx := context.Background()
cases := []struct {
note string
initialData map[string]interface{}
roots []string
expectErr bool
expected string
}{
{
note: "erase all",
initialData: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
"b": "bar",
},
roots: []string{"a", "b"},
expectErr: false,
expected: `{}`,
},
{
note: "erase none",
initialData: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
"b": "bar",
},
roots: []string{},
expectErr: false,
expected: `{"a": {"b": "foo"}, "b": "bar"}`,
},
{
note: "erase partial",
initialData: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
"b": "bar",
},
roots: []string{"a"},
expectErr: false,
expected: `{"b": "bar"}`,
},
{
note: "erase partial path",
initialData: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
"c": map[string]interface{}{
"d": 123,
},
},
},
roots: []string{"a/c/d"},
expectErr: false,
expected: `{"a": {"b": "foo", "c":{}}}`,
},
}
for _, rm := range storeReadModes {
t.Run(rm.note, func(t *testing.T) {
for _, tc := range cases {
t.Run(tc.note, func(t *testing.T) {
mockStore := mock.NewWithData(tc.initialData, inmem.OptReturnASTValuesOnRead(rm.readAst))
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
roots := map[string]struct{}{}
for _, root := range tc.roots {
roots[root] = struct{}{}
}
err := eraseData(ctx, mockStore, txn, roots)
if !tc.expectErr && err != nil {
t.Fatalf("unepected error: %s", err)
} else if tc.expectErr && err == nil {
t.Fatalf("expected error, got: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
mockStore.AssertValid(t)
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
assertEqual(t, rm.readAst, tc.expected, actual)
})
}
})
}
}
func TestErasePolicies(t *testing.T) {
ctx := context.Background()
cases := []struct {
note string
initialPolicies map[string][]byte
roots []string
expectErr bool
expectedRemaining []string
}{
{
note: "erase all",
initialPolicies: map[string][]byte{
"mod1": []byte("package a\np = true"),
},
roots: []string{""},
expectErr: false,
expectedRemaining: []string{},
},
{
note: "erase none",
initialPolicies: map[string][]byte{
"mod1": []byte("package a\np = true"),
"mod2": []byte("package b\np = true"),
},
roots: []string{"c"},
expectErr: false,
expectedRemaining: []string{"mod1", "mod2"},
},
{
note: "erase correct paths",
initialPolicies: map[string][]byte{
"mod1": []byte("package a.test\np = true"),
"mod2": []byte("package a.test_v2\np = true"),
},
roots: []string{"a/test"},
expectErr: false,
expectedRemaining: []string{"mod2"},
},
{
note: "erase some",
initialPolicies: map[string][]byte{
"mod1": []byte("package a\np = true"),
"mod2": []byte("package b\np = true"),
},
roots: []string{"b"},
expectErr: false,
expectedRemaining: []string{"mod1"},
},
{
note: "error: parsing module",
initialPolicies: map[string][]byte{
"mod1": []byte("package a\np = true"),
"mod2": []byte("bad-policy-syntax"),
},
roots: []string{"b"},
expectErr: true,
expectedRemaining: []string{"mod1"},
},
}
for _, tc := range cases {
t.Run(tc.note, func(t *testing.T) {
mockStore := mock.New()
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
for name, mod := range tc.initialPolicies {
err := mockStore.UpsertPolicy(ctx, txn, name, mod)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
}
roots := map[string]struct{}{}
for _, root := range tc.roots {
roots[root] = struct{}{}
}
remaining, _, err := erasePolicies(ctx, mockStore, txn, ast.ParserOptions{}, roots)
if !tc.expectErr && err != nil {
t.Fatalf("unepected error: %s", err)
} else if tc.expectErr && err == nil {
t.Fatalf("expected error, got: %s", err)
}
if !tc.expectErr {
if len(remaining) != len(tc.expectedRemaining) {
t.Fatalf("expected %d modules remaining, got %d", len(tc.expectedRemaining), len(remaining))
}
for _, name := range tc.expectedRemaining {
if _, ok := remaining[name]; !ok {
t.Fatalf("expected remaining module %s not found", name)
}
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
mockStore.AssertValid(t)
txn = storage.NewTransactionOrDie(ctx, mockStore)
actualRemaining, err := mockStore.ListPolicies(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if len(actualRemaining) != len(tc.expectedRemaining) {
t.Fatalf("expected %d modules remaining in the store, got %d", len(tc.expectedRemaining), len(actualRemaining))
}
for _, expectedName := range tc.expectedRemaining {
found := false
for _, actualName := range actualRemaining {
if expectedName == actualName {
found = true
break
}
}
if !found {
t.Fatalf("expected remaining module %s not found", expectedName)
}
}
}
})
}
}
func TestWriteData(t *testing.T) {
storeReadModes := []struct {
note string
readAst bool
}{
{
note: "read raw",
readAst: false,
},
{
note: "read ast",
readAst: true,
},
}
ctx := context.Background()
cases := []struct {
note string
existingData map[string]interface{}
roots []string
data map[string]interface{}
expected string
expectErr bool
}{
{
note: "single root",
roots: []string{"a"},
data: map[string]interface{}{
"a": map[string]interface{}{
"b": map[string]interface{}{
"c": 123,
},
},
},
expected: `{"a": {"b": {"c": 123}}}`,
expectErr: false,
},
{
note: "multiple roots",
roots: []string{"a", "b/c/d"},
data: map[string]interface{}{
"a": "foo",
"b": map[string]interface{}{
"c": map[string]interface{}{
"d": "bar",
},
},
},
expected: `{"a": "foo","b": {"c": {"d": "bar"}}}`,
expectErr: false,
},
{
note: "data not in roots",
roots: []string{"a"},
data: map[string]interface{}{
"a": "foo",
"b": map[string]interface{}{
"c": map[string]interface{}{
"d": "bar",
},
},
},
expected: `{"a": "foo"}`,
expectErr: false,
},
{
note: "no data",
roots: []string{"a"},
existingData: map[string]interface{}{},
data: map[string]interface{}{},
expected: `{}`,
expectErr: false,
},
{
note: "no new data",
roots: []string{"a"},
existingData: map[string]interface{}{
"a": "foo",
},
data: map[string]interface{}{},
expected: `{"a": "foo"}`,
expectErr: false,
},
{
note: "overwrite data",
roots: []string{"a"},
existingData: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
},
data: map[string]interface{}{
"a": "bar",
},
expected: `{"a": "bar"}`,
expectErr: false,
},
}
for _, rm := range storeReadModes {
t.Run(rm.note, func(t *testing.T) {
for _, tc := range cases {
t.Run(tc.note, func(t *testing.T) {
mockStore := mock.NewWithData(tc.existingData, inmem.OptReturnASTValuesOnRead(rm.readAst))
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
err := writeData(ctx, mockStore, txn, tc.roots, tc.data)
if !tc.expectErr && err != nil {
t.Fatalf("unepected error: %s", err)
} else if tc.expectErr && err == nil {
t.Fatalf("expected error, got: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
mockStore.AssertValid(t)
txn = storage.NewTransactionOrDie(ctx, mockStore)
actual, err := mockStore.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
assertEqual(t, rm.readAst, tc.expected, actual)
})
}
})
}
}
func loadExpectedResult(input string) interface{} {
if len(input) == 0 {
return nil
}
var data interface{}
if err := util.UnmarshalJSON([]byte(input), &data); err != nil {
panic(err)
}
return data
}
func loadExpectedSortedResult(input string) interface{} {
data := loadExpectedResult(input)
switch data := data.(type) {
case []interface{}:
return data
default:
return data
}
}
type testWriteModuleCase struct {
note string
bundles map[string]*Bundle // Only need to give raw text and path for modules
extraMods map[string]*ast.Module
compilerMods map[string]*ast.Module
storeData map[string]interface{}
expectErr bool
}
func TestWriteModules(t *testing.T) {
cases := []testWriteModuleCase{
{
note: "module files only",
bundles: map[string]*Bundle{
"bundle1": {
Modules: []ModuleFile{
{
Path: "mod1",
Raw: []byte("package a\np = true"),
},
},
},
},
expectErr: false,
},
{
note: "extra modules only",
extraMods: map[string]*ast.Module{
"mod1": ast.MustParseModule("package a\np = true"),
},
expectErr: false,
},
{
note: "compiler modules only",
compilerMods: map[string]*ast.Module{
"mod1": ast.MustParseModule("package a\np = true"),
},
expectErr: false,
},
{
note: "module files and extra modules",
bundles: map[string]*Bundle{
"bundle1": {
Modules: []ModuleFile{
{
Path: "mod1",
Raw: []byte("package a\np = true"),
},
},
},
},
extraMods: map[string]*ast.Module{
"mod2": ast.MustParseModule("package b\np = false"),
},
expectErr: false,
},
{
note: "module files and compiler modules",
bundles: map[string]*Bundle{
"bundle1": {
Modules: []ModuleFile{
{
Path: "mod1",
Raw: []byte("package a\np = true"),
},
},
},
},
compilerMods: map[string]*ast.Module{
"mod2": ast.MustParseModule("package b\np = false"),
},
expectErr: false,
},
{
note: "extra modules and compiler modules",
extraMods: map[string]*ast.Module{
"mod1": ast.MustParseModule("package a\np = true"),
},
compilerMods: map[string]*ast.Module{
"mod2": ast.MustParseModule("package b\np = false"),
},
expectErr: false,
},
{
note: "compile error: path conflict",
bundles: map[string]*Bundle{
"bundle1": {
Modules: []ModuleFile{
{
Path: "mod1",
Raw: []byte("package a\np = true"),
},
},
},
},
storeData: map[string]interface{}{
"a": map[string]interface{}{
"p": "foo",
},
},
expectErr: true,
},
}
for _, tc := range cases {
testWriteData(t, tc, false)
testWriteData(t, tc, true)
}
}
func testWriteData(t *testing.T, tc testWriteModuleCase, legacy bool) {
t.Helper()
testName := tc.note
if legacy {
testName += "_legacy"
}
t.Run(testName, func(t *testing.T) {
ctx := context.Background()
mockStore := mock.NewWithData(tc.storeData)
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
compiler := ast.NewCompiler().WithPathConflictsCheck(storage.NonEmpty(ctx, mockStore, txn))
m := metrics.New()
// if supplied, pre-parse the module files
for _, b := range tc.bundles {
var parsedMods []ModuleFile
for _, mf := range b.Modules {
parsedMods = append(parsedMods, ModuleFile{
Path: mf.Path,
Raw: mf.Raw,
Parsed: ast.MustParseModule(string(mf.Raw)),
})
}
b.Modules = parsedMods
}
// if supplied, setup the compiler with modules already compiled on it
if len(tc.compilerMods) > 0 {
compiler.Compile(tc.compilerMods)
if len(compiler.Errors) > 0 {
t.Fatalf("unexpected error: %s", compiler.Errors)
}
}
err := writeModules(ctx, mockStore, txn, compiler, m, tc.bundles, tc.extraMods, legacy)
if !tc.expectErr && err != nil {
t.Fatalf("unepected error: %s", err)
} else if tc.expectErr && err == nil {
t.Fatalf("expected error, got: %s", err)
}
if !tc.expectErr {
// ensure all policy files were saved to storage
policies, err := mockStore.ListPolicies(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
expectedNumMods := 0
for _, b := range tc.bundles {
expectedNumMods += len(b.Modules)
}
if len(policies) != expectedNumMods {
t.Fatalf("expected %d policies in storage, found %d", expectedNumMods, len(policies))
}
for bundleName, b := range tc.bundles {
for _, mf := range b.Modules {
found := false
for _, p := range policies {
var expectedPath string
if legacy {
expectedPath = mf.Path
} else {
expectedPath = filepath.Join(bundleName, mf.Path)
}
if p == expectedPath {
found = true
break
}
}
if !found {
t.Fatalf("policy %s not found in storage", mf.Path)
}
}
}
// ensure all the modules were compiled together and we aren't missing any
expectedModCount := expectedNumMods + len(tc.extraMods) + len(tc.compilerMods)
if len(compiler.Modules) != expectedModCount {
t.Fatalf("expected %d modules on compiler, found %d", expectedModCount, len(compiler.Modules))
}
for moduleName := range compiler.Modules {
found := false
if _, ok := tc.extraMods[moduleName]; ok {
continue
}
if _, ok := tc.compilerMods[moduleName]; ok {
continue
}
for bundleName, b := range tc.bundles {
if legacy {
for _, mf := range b.Modules {
if moduleName == mf.Path {
found = true
break
}
}
} else {
for bundleModuleName := range b.ParsedModules(bundleName) {
if moduleName == bundleModuleName {
found = true
break
}
}
}
}
if found {
continue
}
t.Errorf("unexpected module %s on compiler", moduleName)
}
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
mockStore.AssertValid(t)
})
}
func TestDoDFS(t *testing.T) {
cases := []struct {
note string
input map[string]json.RawMessage
path string
roots []string
wantErr bool
err error
}{
{
note: "bundle owns all",
input: nil,
path: "/",
roots: []string{""},
wantErr: false,
},
{
note: "data within roots root case",
input: map[string]json.RawMessage{"a": json.RawMessage(`true`)},
path: "",
roots: []string{"a"},
wantErr: false,
},
{
note: "data within roots nested 1",
input: map[string]json.RawMessage{"d": json.RawMessage(`true`)},
path: filepath.Dir(strings.Trim("a/b/c/data.json", "/")),
roots: []string{"a/b/c"},
wantErr: false,
},
{
note: "data within roots nested 2",
input: map[string]json.RawMessage{"d": json.RawMessage(`{"hello": "world"}`)},
path: filepath.Dir(strings.Trim("a/b/c/data.json", "/")),
roots: []string{"a/b/c"},
wantErr: false,
},
{
note: "data within roots nested 3",
input: map[string]json.RawMessage{"d": json.RawMessage(`{"hello": "world"}`)},
path: filepath.Dir(strings.Trim("a/data.json", "/")),
roots: []string{"a/d"},
wantErr: false,
},
{
note: "data within multiple roots 1",
input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "c": json.RawMessage(`true`)},
path: filepath.Dir(strings.Trim("/data.json", "/")),
roots: []string{"a/b", "c"},
wantErr: false,
},
{
note: "data within multiple roots 2",
input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "c": []byte(`{"d": {"e": {"f": true}}}`)},
path: filepath.Dir(strings.Trim("/data.json", "/")),
roots: []string{"a/b", "c/d/e"},
wantErr: false,
},
{
note: "data outside roots 1",
input: map[string]json.RawMessage{"d": json.RawMessage(`{"hello": "world"}`)},
path: filepath.Dir(strings.Trim("/data.json", "/")),
roots: []string{"a/d"},
wantErr: true,
err: fmt.Errorf("manifest roots [a/d] do not permit data at path '/d' (hint: check bundle directory structure)"),
},
{
note: "data outside roots 2",
input: map[string]json.RawMessage{"a": []byte(`{"b": {"c": {"e": true}}}`)},
path: filepath.Dir(strings.Trim("/x/data.json", "/")),
roots: []string{"x/a/b/c/d"},
wantErr: true,
err: fmt.Errorf("manifest roots [x/a/b/c/d] do not permit data at path '/x/a/b/c/e' (hint: check bundle directory structure)"),
},
{
note: "data outside roots 3",
input: map[string]json.RawMessage{"a": []byte(`{"b": {"c": true}}`)},
path: filepath.Dir(strings.Trim("/data.json", "/")),
roots: []string{"a/b/c/d"},
wantErr: true,
err: fmt.Errorf("manifest roots [a/b/c/d] do not permit data at path '/a/b/c' (hint: check bundle directory structure)"),
},
{
note: "data outside multiple roots",
input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "e": []byte(`{"b": {"c": true}}`)},
path: filepath.Dir(strings.Trim("/data.json", "/")),
roots: []string{"a/b", "c"},
wantErr: true,
err: fmt.Errorf("manifest roots [a/b c] do not permit data at path '/e' (hint: check bundle directory structure)"),
},
{
note: "data outside multiple roots 2",
input: map[string]json.RawMessage{"a": json.RawMessage(`{"b": "c"}`), "c": []byte(`{"d": true}`)},
path: filepath.Dir(strings.Trim("/data.json", "/")),
roots: []string{"a/b", "c/d/e"},
wantErr: true,
err: fmt.Errorf("manifest roots [a/b c/d/e] do not permit data at path '/c/d' (hint: check bundle directory structure)"),
},
}
for _, tc := range cases {
t.Run(tc.note, func(t *testing.T) {
err := doDFS(tc.input, tc.path, tc.roots)
if tc.wantErr {
if err == nil {
t.Fatal("Expected error but got nil")
}
if tc.err != nil && tc.err.Error() != err.Error() {
t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error())
}
} else {
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
}
})
}
}
func TestHasRootsOverlap(t *testing.T) {
ctx := context.Background()
cases := []struct {
note string
storeRoots map[string]*[]string
bundleRoots map[string]*[]string
overlaps bool
}{
{
note: "no overlap with existing roots",
storeRoots: map[string]*[]string{"bundle1": {"a", "b"}},
bundleRoots: map[string]*[]string{"bundle2": {"c"}},
overlaps: false,
},
{
note: "no overlap with existing roots multiple bundles",
storeRoots: map[string]*[]string{"bundle1": {"a", "b"}},
bundleRoots: map[string]*[]string{"bundle2": {"c"}, "bundle3": {"d"}},
overlaps: false,
},
{
note: "no overlap no existing roots",
storeRoots: map[string]*[]string{},
bundleRoots: map[string]*[]string{"bundle1": {"a", "b"}},
overlaps: false,
},
{
note: "no overlap without existing roots multiple bundles",
storeRoots: map[string]*[]string{},
bundleRoots: map[string]*[]string{"bundle1": {"a", "b"}, "bundle2": {"c"}},
overlaps: false,
},
{
note: "overlap without existing roots multiple bundles",
storeRoots: map[string]*[]string{},
bundleRoots: map[string]*[]string{"bundle1": {"a", "b"}, "bundle2": {"a", "c"}},
overlaps: true,
},
{
note: "overlap with existing roots",
storeRoots: map[string]*[]string{"bundle1": {"a", "b"}},
bundleRoots: map[string]*[]string{"bundle2": {"c", "a"}},
overlaps: true,
},
{
note: "overlap with existing roots multiple bundles",
storeRoots: map[string]*[]string{"bundle1": {"a", "b"}},
bundleRoots: map[string]*[]string{"bundle2": {"c", "a"}, "bundle3": {"a"}},
overlaps: true,
},
}
for _, tc := range cases {
t.Run(tc.note, func(t *testing.T) {
mockStore := mock.New()
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
for name, roots := range tc.storeRoots {
err := WriteManifestToStore(ctx, mockStore, txn, name, Manifest{Roots: roots})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
}
bundles := map[string]*Bundle{}
for name, roots := range tc.bundleRoots {
bundles[name] = &Bundle{
Manifest: Manifest{
Roots: roots,
},
}
}
err := hasRootsOverlap(ctx, mockStore, txn, bundles)
if !tc.overlaps && err != nil {
t.Fatalf("unepected error: %s", err)
} else if tc.overlaps && (err == nil || !strings.Contains(err.Error(), "detected overlapping roots in bundle manifest")) {
t.Fatalf("expected overlapping roots error, got: %s", err)
}
err = mockStore.Commit(ctx, txn)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
mockStore.AssertValid(t)
})
}
}
func TestBundleStoreHelpers(t *testing.T) {
storeReadModes := []struct {
note string
readAst bool
}{
{
note: "read raw",
readAst: false,
},
{
note: "read ast",
readAst: true,
},
}
ctx := context.Background()
bundles := map[string]*Bundle{
"bundle1": {
Manifest: Manifest{
Roots: &[]string{},
},
},
"bundle2": {
Manifest: Manifest{
Roots: &[]string{"a"},
Revision: "foo",
Metadata: map[string]interface{}{
"a": "b",
},
WasmResolvers: []WasmResolver{
{
Entrypoint: "foo/bar",
Module: "m.wasm",
},
},
},
Etag: "bar",
WasmModules: []WasmModuleFile{
{
Path: "/m.wasm",
Raw: []byte("d2FzbS1tb2R1bGU="),
},
},
},
}
for _, srm := range storeReadModes {
t.Run(srm.note, func(t *testing.T) {
mockStore := mock.NewWithData(nil, inmem.OptReturnASTValuesOnRead(srm.readAst))
txn := storage.NewTransactionOrDie(ctx, mockStore, storage.WriteParams)
c := ast.NewCompiler()
m := metrics.New()
err := Activate(&ActivateOpts{
Ctx: ctx,
Store: mockStore,
Txn: txn,
Compiler: c,
Metrics: m,
Bundles: bundles,
})
if err != nil {
t.Fatal(err)
}
// Bundle names
if names, err := ReadBundleNamesFromStore(ctx, mockStore, txn); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if len(names) != len(bundles) {
t.Errorf("expected bundle names:\n\n%v\n\nin store, found\n\n%v", bundles, names)
} else {
for _, name := range names {
if _, ok := bundles[name]; !ok {
t.Errorf("expected bundle names:\n\n%v\n\nin store, found\n\n%v", bundles, names)
}
}
}
// Etag
if etag, err := ReadBundleEtagFromStore(ctx, mockStore, txn, "bundle1"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if etag != "" {
t.Errorf("expected empty etag but got %s", etag)
}
if etag, err := ReadBundleEtagFromStore(ctx, mockStore, txn, "bundle2"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if exp := "bar"; etag != exp {
t.Errorf("expected etag %s but got %s", exp, etag)
}
// Revision
if rev, err := ReadBundleRevisionFromStore(ctx, mockStore, txn, "bundle1"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if rev != "" {
t.Errorf("expected empty revision but got %s", rev)
}
if rev, err := ReadBundleRevisionFromStore(ctx, mockStore, txn, "bundle2"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if exp := "foo"; rev != exp {
t.Errorf("expected revision %s but got %s", exp, rev)
}
// Roots
if roots, err := ReadBundleRootsFromStore(ctx, mockStore, txn, "bundle1"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if len(roots) != 0 {
t.Errorf("expected empty roots but got %v", roots)
}
if roots, err := ReadBundleRootsFromStore(ctx, mockStore, txn, "bundle2"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if exp := *bundles["bundle2"].Manifest.Roots; !reflect.DeepEqual(exp, roots) {
t.Errorf("expected roots %v but got %v", exp, roots)
}
// Bundle metadata
if meta, err := ReadBundleMetadataFromStore(ctx, mockStore, txn, "bundle1"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if len(meta) != 0 {
t.Errorf("expected empty metadata but got %v", meta)
}
if meta, err := ReadBundleMetadataFromStore(ctx, mockStore, txn, "bundle2"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if exp := bundles["bundle2"].Manifest.Metadata; !reflect.DeepEqual(exp, meta) {
t.Errorf("expected metadata %v but got %v", exp, meta)
}
// Wasm metadata
if _, err := ReadWasmMetadataFromStore(ctx, mockStore, txn, "bundle1"); err == nil {
t.Fatalf("expected error but got nil")
} else if exp, act := "storage_not_found_error: /bundles/bundle1/manifest/wasm: document does not exist", err.Error(); !strings.Contains(act, exp) {
t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", exp, act)
}
if resolvers, err := ReadWasmMetadataFromStore(ctx, mockStore, txn, "bundle2"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if exp := bundles["bundle2"].Manifest.WasmResolvers; !reflect.DeepEqual(exp, resolvers) {
t.Errorf("expected wasm metadata:\n\n%v\n\nbut got:\n\n%v", exp, resolvers)
}
// Wasm modules
if _, err := ReadWasmModulesFromStore(ctx, mockStore, txn, "bundle1"); err == nil {
t.Fatalf("expected error but got nil")
} else if exp, act := "storage_not_found_error: /bundles/bundle1/wasm: document does not exist", err.Error(); !strings.Contains(act, exp) {
t.Fatalf("expected error:\n\n%s\n\nbut got:\n\n%v", exp, act)
}
if modules, err := ReadWasmModulesFromStore(ctx, mockStore, txn, "bundle2"); err != nil {
t.Fatalf("unexpected error: %s", err)
} else if exp := bundles["bundle2"].WasmModules; len(exp) != len(modules) {
t.Errorf("expected wasm modules:\n\n%v\n\nbut got:\n\n%v", exp, modules)
} else {
for _, exp := range bundles["bundle2"].WasmModules {
act := modules[exp.Path]
if act == nil {
t.Errorf("expected wasm module %s but got nil", exp.Path)
}
if !bytes.Equal(exp.Raw, act) {
t.Errorf("expected wasm module %s to have raw data:\n\n%v\n\nbut got:\n\n%v", exp.Path, exp.Raw, act)
}
}
}
})
}
}