Files
releases/internal/runtime/init/init_test.go
T
Johan Fylling 7bb6dbe36b Preparing for v1 API
Moving (most) source to v1 root package to prepare for v0/v1 API separation.

Signed-off-by: Johan Fylling <johan.dev@fylling.se>
2024-12-12 15:09:03 +01:00

530 lines
13 KiB
Go

// Copyright 2020 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package init
import (
"bytes"
"context"
"encoding/json"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"testing"
"github.com/open-policy-agent/opa/internal/file/archive"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/bundle"
"github.com/open-policy-agent/opa/v1/loader"
"github.com/open-policy-agent/opa/v1/storage"
inmem "github.com/open-policy-agent/opa/v1/storage/inmem/test"
"github.com/open-policy-agent/opa/v1/util"
"github.com/open-policy-agent/opa/v1/util/test"
"github.com/open-policy-agent/opa/version"
)
func TestInit(t *testing.T) {
mod1 := `package a.b.c
import data.a.foo
p = true { foo = "bar" }
p = true { 1 = 2 }`
mod2 := `package b.c.d
import data.b.foo
p = true { foo = "bar" }
p = true { 1 = 2 }`
tests := []struct {
note string
fs map[string]string
loadParams []string
expectedData map[string]string
expectedMods []string
asBundle bool
}{
{
note: "load files",
fs: map[string]string{
"datafile": `{"foo": "bar", "x": {"y": {"z": [1]}}}`,
"policyFile": mod1,
},
loadParams: []string{"datafile", "policyFile"},
expectedData: map[string]string{
"/foo": "bar",
},
expectedMods: []string{mod1},
asBundle: false,
},
{
note: "load bundle",
fs: map[string]string{
"datafile": `{"foo": "bar", "x": {"y": {"z": [1]}}}`, // Should be ignored
"data.json": `{"foo": "not-bar"}`,
"policy.rego": mod1,
},
loadParams: []string{"/"},
expectedData: map[string]string{
"/foo": "not-bar",
},
expectedMods: []string{mod1},
asBundle: true,
},
{
note: "load multiple bundles",
fs: map[string]string{
"/bundle1/a/data.json": `{"foo": "bar1", "x": {"y": {"z": [1]}}}`, // Should be ignored
"/bundle1/a/policy.rego": mod1,
"/bundle1/a/.manifest": `{"roots": ["a"]}`,
"/bundle2/b/data.json": `{"foo": "bar2"}`,
"/bundle2/b/policy.rego": mod2,
"/bundle2/b/.manifest": `{"roots": ["b"]}`,
},
loadParams: []string{"bundle1", "bundle2"},
expectedData: map[string]string{
"/a/foo": "bar1",
"/b/foo": "bar2",
},
expectedMods: []string{mod1, mod2},
asBundle: true,
},
{
note: "preserve OPA version",
fs: map[string]string{
"/root/system/version/data.json": `{"version": "XYZ"}`, // Should be overwritten
},
loadParams: []string{"root"},
expectedData: map[string]string{
"/system/version/version": version.Version,
},
asBundle: true,
},
}
ctx := context.Background()
for _, useMemoryFS := range []bool{false, true} {
for _, tc := range tests {
t.Run(tc.note, func(t *testing.T) {
test.WithTestFS(tc.fs, useMemoryFS, func(rootDir string, fsys fs.FS) {
paths := []string{}
for _, fileName := range tc.loadParams {
paths = append(paths, filepath.Join(rootDir, fileName))
}
// Create a new store and perform a file load/insert sequence.
store := inmem.New()
err := storage.Txn(ctx, store, storage.WriteParams, func(txn storage.Transaction) error {
loaded, err := LoadPaths(paths, nil, tc.asBundle, nil, true, false, nil, fsys)
if err != nil {
return err
}
_, err = InsertAndCompile(ctx, InsertAndCompileOptions{
Store: store,
Txn: txn,
Files: loaded.Files,
Bundles: loaded.Bundles,
MaxErrors: -1,
})
return err
})
if err != nil {
t.Fatal(err)
}
// Verify the loading was successful as expected.
txn := storage.NewTransactionOrDie(ctx, store)
for storePath, expected := range tc.expectedData {
node, err := store.Read(ctx, txn, storage.MustParsePath(storePath))
if util.Compare(node, expected) != 0 || err != nil {
t.Fatalf("Expected %v but got %v (err: %v)", expected, node, err)
}
}
ids, err := store.ListPolicies(ctx, txn)
if err != nil {
t.Fatal(err)
}
if len(tc.expectedMods) != len(ids) {
t.Fatalf("Expected %d modules, got %d", len(tc.expectedMods), len(ids))
}
actualMods := map[string]struct{}{}
for _, id := range ids {
result, err := store.GetPolicy(ctx, txn, id)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
actualMods[string(result)] = struct{}{}
}
for _, expectedMod := range tc.expectedMods {
if _, found := actualMods[expectedMod]; !found {
t.Fatalf("Expected %v but got: %v", expectedMod, actualMods)
}
}
_, err = store.Read(ctx, txn, storage.MustParsePath("/system/version"))
if err != nil {
t.Fatal(err)
}
})
})
}
}
}
func TestLoadTarGzsInBundleAndNonBundleMode(t *testing.T) {
type bundleInfo struct {
fileName string
files [][2]string
expBundle bundle.Bundle
}
bundle1TarGz := bundleInfo{
fileName: "bundle1.tar.gz",
files: [][2]string{
{"/a/data.json", `{"foo": "bar1", "x": {"y": {"z": [1]}}}`},
{"/a/.manifest", `{"roots": ["a"]}`},
},
expBundle: bundle.Bundle{
Manifest: bundle.Manifest{
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"foo": "bar1",
"x": map[string]interface{}{
"y": map[string]interface{}{
"z": []interface{}{json.Number("1")},
},
},
},
},
},
}
bundle2TarGz := bundleInfo{
fileName: "bundle2.tar.gz",
files: [][2]string{
{"/b/data.json", `{"foo": "bar2", "x": {"y": {"z": [1]}}}`},
{"/b/.manifest", `{"roots": ["b"]}`},
},
expBundle: bundle.Bundle{
Manifest: bundle.Manifest{
Roots: &[]string{"b"},
},
Data: map[string]interface{}{
"b": map[string]interface{}{
"foo": "bar2",
"x": map[string]interface{}{
"y": map[string]interface{}{
"z": []interface{}{json.Number("1")},
},
},
},
},
},
}
bundle1Folder := map[string]string{
"/bundle1/a/data.json": `{"foo1": "bar2", "x": {"y": {"z": [2]}}}`,
"/bundle1/a/.manifest": `{"roots": ["a"]}`,
"/bundle1/a/foo.rego": `package a.b.y`,
}
modulePath := "/bundle1/a/foo.rego"
module := `package a.b.y`
bundle1FolderInfo := bundleInfo{
fileName: "bundle1",
expBundle: bundle.Bundle{
Manifest: bundle.Manifest{
Roots: &[]string{"a"},
},
Data: map[string]interface{}{
"a": map[string]interface{}{
"foo1": "bar2",
"x": map[string]interface{}{
"y": map[string]interface{}{
"z": []interface{}{json.Number("2")},
},
},
},
},
Modules: []bundle.ModuleFile{
{
URL: modulePath,
Path: modulePath,
Parsed: ast.MustParseModule(module),
Raw: []byte(module),
},
},
},
}
tests := []struct {
note string
bundleInfoTC []bundleInfo
folderContent map[string]string
expectedBundles int
expectedModules int
asBundle bool
}{
{
note: "load multiple bundles. one tar.gz and one folder. Bundle mode is true",
folderContent: bundle1Folder,
bundleInfoTC: []bundleInfo{
bundle1TarGz,
bundle1FolderInfo,
},
expectedBundles: 2,
expectedModules: 0,
asBundle: true,
},
{
note: "load multiple bundles. one tar.gz and one folder. Bundle mode is false",
folderContent: bundle1Folder,
bundleInfoTC: []bundleInfo{
bundle1TarGz,
bundle1FolderInfo,
},
expectedBundles: 1,
expectedModules: 1,
asBundle: false,
},
{
note: "load multiple bundles. two tar.gz and one folder. Bundle mode is true",
folderContent: bundle1Folder,
bundleInfoTC: []bundleInfo{
bundle1TarGz,
bundle2TarGz,
bundle1FolderInfo,
},
expectedBundles: 3,
expectedModules: 0,
asBundle: true,
},
{
note: "load multiple bundles. two tar.gz and one folder. Bundle mode is false",
folderContent: bundle1Folder,
bundleInfoTC: []bundleInfo{
bundle1TarGz,
bundle2TarGz,
bundle1FolderInfo,
},
expectedBundles: 2,
expectedModules: 1,
asBundle: false,
},
{
note: "load just one folder. Bundle mode is true",
folderContent: bundle1Folder,
bundleInfoTC: []bundleInfo{
bundle1FolderInfo,
},
expectedBundles: 1,
expectedModules: 0,
asBundle: true,
},
{
note: "load just one folder. Bundle mode is false",
folderContent: bundle1Folder,
bundleInfoTC: []bundleInfo{
bundle1FolderInfo,
},
expectedBundles: 0,
expectedModules: 1,
asBundle: false,
},
}
for _, tc := range tests {
t.Run(tc.note, func(t *testing.T) {
test.WithTempFS(tc.folderContent, func(rootDir string) {
paths := []string{}
for _, bdlInfo := range tc.bundleInfoTC {
if strings.HasSuffix(bdlInfo.fileName, ".tar.gz") {
// Create the tar gz files temporarily
buf := archive.MustWriteTarGz(bdlInfo.files)
bundleFile := filepath.Join(rootDir, bdlInfo.fileName)
out, err := os.Create(bundleFile)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
_, err = out.Write(buf.Bytes())
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
}
paths = append(paths, filepath.Join(rootDir, bdlInfo.fileName))
}
loaded, err := LoadPaths(paths, nil, tc.asBundle, nil, true, false, nil, nil)
if err != nil {
t.Fatal("Failed LoadPaths ", err)
}
if tc.expectedBundles != len(loaded.Bundles) {
t.Fatalf("Expected %d bundles, got %d", tc.expectedBundles, len(loaded.Bundles))
}
if tc.expectedModules != len(loaded.Files.Modules) {
t.Fatalf("Expected %d modules, got %d", tc.expectedModules, len(loaded.Files.Modules))
}
// Testing the content
for path, actual := range loaded.Bundles {
for _, bdlInfo := range tc.bundleInfoTC {
if strings.HasSuffix(path, bdlInfo.fileName) {
var buf bytes.Buffer
if err := bundle.NewWriter(&buf).Write(bdlInfo.expBundle); err != nil {
t.Fatal(err)
}
expected, err := bundle.NewReader(&buf).Read()
if err != nil {
t.Fatal(err)
}
// adjusting the URL and Path due to /tmp/ path
if len(bdlInfo.expBundle.Modules) > 0 {
expected.Modules[0].URL = rootDir + expected.Modules[0].URL
expected.Modules[0].Path = rootDir + expected.Modules[0].Path
}
if !expected.Equal(*actual) {
t.Fatalf("\nExpected: %+v\nGot: %+v", expected, actual)
}
}
}
}
})
})
}
}
func TestWalkPaths(t *testing.T) {
files := map[string]string{
"/bundle1/a/data.json": `{"foo": "bar1", "x": {"y": {"z": [1]}}}`,
"/bundle1/a/policy.rego": `package example.foo`,
"/bundle1/a/.manifest": `{"roots": ["a"]}`,
"/bundle2/b/data.json": `{"foo": "bar2"}`,
"/bundle2/b/policy.rego": `package authz`,
"/bundle2/b/.manifest": `{"roots": ["b"]}`,
}
test.WithTempFS(files, func(rootDir string) {
paths := []string{}
paths = append(paths, filepath.Join(rootDir, "bundle1"))
paths = append(paths, filepath.Join(rootDir, "bundle2"))
// bundle mode
loaded, err := WalkPaths(paths, nil, true)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if len(loaded.BundlesLoader) != len(paths) {
t.Fatalf("Expected %v bundle loaders but got %v", len(paths), len(loaded.BundlesLoader))
}
// check files
result := []string{}
for _, bl := range loaded.BundlesLoader {
for {
f, err := bl.DirectoryLoader.NextFile()
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
result = append(result, f.Path())
if _, ok := files[strings.TrimPrefix(f.URL(), rootDir)]; !ok {
t.Fatalf("unexpected file %v", f.Path())
}
}
}
if len(result) != len(files) {
t.Fatalf("Expected %v files across bundles but got %v", len(files), len(result))
}
// non-bundle mode
loaded, err = WalkPaths(paths, nil, false)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if len(loaded.FileDescriptors) != len(files) {
t.Fatalf("Expected %v files across directories but got %v", len(files), len(loaded.FileDescriptors))
}
for _, d := range loaded.FileDescriptors {
path := path.Join(d.Root, d.Path)
path = strings.TrimPrefix(path, rootDir)
if _, ok := files[path]; !ok {
t.Fatalf("unexpected file %v", path)
}
}
})
}
func TestLoadPathsBundleModeWithFilter(t *testing.T) {
files := map[string]string{
"a/data.json": `{"foo": "not-bar"}`,
"policy.rego": "package foo\n p = 1",
"policy_test.rego": "package foo\n test_p { p }",
"a/.manifest": `{"roots": ["a", "foo"]}`,
}
test.WithTempFS(files, func(rootDir string) {
paths := []string{rootDir}
// bundle mode
loaded, err := LoadPaths(paths, func(abspath string, info os.FileInfo, depth int) bool {
return loader.GlobExcludeName("*_test.rego", 1)(abspath, info, depth)
}, true, nil, true, false, nil, nil)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}
if len(loaded.Bundles) != len(paths) {
t.Fatalf("Expected %v bundle loaders but got %v", len(paths), len(loaded.Bundles))
}
b, ok := loaded.Bundles[rootDir]
if !ok {
t.Fatalf("expected bundle %v", rootDir)
}
expected := 1
if len(b.Modules) != expected {
t.Fatalf("expected %v module but got %v", expected, len(b.Modules))
}
})
}