Skip to content

Commit

Permalink
storage/disk: make symlinks work with relative paths
Browse files Browse the repository at this point in the history
So for now, we'll properly create the symlinks without the wrong indirection.

Fixes open-policy-agent#4869.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
  • Loading branch information
srenatus committed Jul 11, 2022
1 parent b18b0a2 commit 0dee8cf
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 73 deletions.
7 changes: 5 additions & 2 deletions storage/disk/disk.go
Expand Up @@ -351,17 +351,20 @@ func (db *Store) Truncate(ctx context.Context, txn storage.Transaction, params s

// update symlink to point to the active db
symlink := filepath.Join(path.Dir(newDB.Opts().Dir), symlinkKey)
// "active" -> "backupXXXX" is what we want, not
// "active" -> "DIR/backupXXX", since that won't work when using a relative directory
target := filepath.Base(newDB.Opts().Dir)
if _, err := os.Lstat(symlink); err == nil {
if err := os.Remove(symlink); err != nil {
return wrapError(err)
}

err = os.Symlink(newDB.Opts().Dir, symlink)
err = os.Symlink(target, symlink)
if err != nil {
return wrapError(err)
}
} else if errors.Is(err, os.ErrNotExist) {
err = os.Symlink(newDB.Opts().Dir, symlink)
err = os.Symlink(target, symlink)
if err != nil {
return wrapError(err)
}
Expand Down
170 changes: 99 additions & 71 deletions storage/disk/disk_test.go
Expand Up @@ -167,63 +167,83 @@ func TestPolicies(t *testing.T) {
})
}

func TestTruncate(t *testing.T) {
func TestTruncateAbsoluteStoragePath(t *testing.T) {
test.WithTempFS(map[string]string{}, func(dir string) {
ctx := context.Background()
s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: nil})
if err != nil {
t.Fatal(err)
}
defer s.Close(ctx)
runTruncateTest(t, dir)
})
}

txn := storage.NewTransactionOrDie(ctx, s, storage.WriteParams)
func TestTruncateRelativeStoragePath(t *testing.T) {
dir := "foobar"
err := os.Mkdir(dir, 0700)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
runTruncateTest(t, dir)
}

var archiveFiles = map[string]string{
"/a/b/c/data.json": "[1,2,3]",
"/a/b/d/data.json": `e: true`,
"/data.json": `{"x": {"y": true}, "a": {"b": {"z": true}}}}`,
"/a/b/y/data.yaml": `foo: 1`,
"/policy.rego": "package foo\n p = 1",
"/roles/policy.rego": "package bar\n p = 1",
}
func runTruncateTest(t *testing.T, dir string) {
ctx := context.Background()
s, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: nil})
if err != nil {
t.Fatal(err)
}
defer s.Close(ctx)

var files [][2]string
for name, content := range archiveFiles {
files = append(files, [2]string{name, content})
}
txn := storage.NewTransactionOrDie(ctx, s, storage.WriteParams)

buf := archive.MustWriteTarGz(files)
b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read()
if err != nil {
t.Fatal(err)
}
var archiveFiles = map[string]string{
"/a/b/c/data.json": "[1,2,3]",
"/a/b/d/data.json": `e: true`,
"/data.json": `{"x": {"y": true}, "a": {"b": {"z": true}}}}`,
"/a/b/y/data.yaml": `foo: 1`,
"/policy.rego": "package foo\n p = 1",
"/roles/policy.rego": "package bar\n p = 1",
}

iterator := bundle.NewIterator(b.Raw)
var files [][2]string
for name, content := range archiveFiles {
files = append(files, [2]string{name, content})
}

err = s.Truncate(ctx, txn, storage.WriteParams, iterator)
if err != nil {
t.Fatalf("Unexpected truncate error: %v", err)
}
buf := archive.MustWriteTarGz(files)
b, err := bundle.NewReader(buf).WithLazyLoadingMode(true).Read()
if err != nil {
t.Fatal(err)
}

// check if symlink is created
symlink := filepath.Join(dir, symlinkKey)
_, err = os.Lstat(symlink)
if err != nil {
t.Fatal(err)
}
iterator := bundle.NewIterator(b.Raw)

if err := s.Commit(ctx, txn); err != nil {
t.Fatalf("Unexpected commit error: %v", err)
}
err = s.Truncate(ctx, txn, storage.WriteParams, iterator)
if err != nil {
t.Fatalf("Unexpected truncate error: %v", err)
}

txn = storage.NewTransactionOrDie(ctx, s)
// check if symlink is created
symlink := filepath.Join(dir, symlinkKey)
_, 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)
}

actual, err := s.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatal(err)
}
if err := s.Commit(ctx, txn); err != nil {
t.Fatalf("Unexpected commit error: %v", err)
}

expected := `
txn = storage.NewTransactionOrDie(ctx, s)

actual, err := s.Read(ctx, txn, storage.MustParsePath("/"))
if err != nil {
t.Fatal(err)
}

expected := `
{
"a": {
"b": {
Expand All @@ -242,40 +262,48 @@ func TestTruncate(t *testing.T) {
}
}
`
jsn := util.MustUnmarshalJSON([]byte(expected))
jsn := util.MustUnmarshalJSON([]byte(expected))

if !reflect.DeepEqual(jsn, actual) {
t.Fatalf("Expected reader's read to be %v but got: %v", jsn, actual)
}
if !reflect.DeepEqual(jsn, actual) {
t.Fatalf("Expected reader's read to be %v but got: %v", jsn, actual)
}

s.Abort(ctx, txn)
s.Abort(ctx, txn)

txn = storage.NewTransactionOrDie(ctx, s)
ids, err := s.ListPolicies(ctx, txn)
if err != nil {
t.Fatal(err)
}
txn = storage.NewTransactionOrDie(ctx, s)
ids, err := s.ListPolicies(ctx, txn)
if err != nil {
t.Fatal(err)
}

expectedIds := map[string]struct{}{"/policy.rego": {}, "/roles/policy.rego": {}}
expectedIds := map[string]struct{}{"/policy.rego": {}, "/roles/policy.rego": {}}

for _, id := range ids {
if _, ok := expectedIds[id]; !ok {
t.Fatalf("Expected list policies to contain %v but got: %v", expectedIds, id)
}
for _, id := range ids {
if _, ok := expectedIds[id]; !ok {
t.Fatalf("Expected list policies to contain %v but got: %v", expectedIds, id)
}
}

bs, err := s.GetPolicy(ctx, txn, "/policy.rego")
expectedBytes := []byte("package foo\n p = 1")
if err != nil || !reflect.DeepEqual(expectedBytes, bs) {
t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err)
}
bs, err := s.GetPolicy(ctx, txn, "/policy.rego")
expectedBytes := []byte("package foo\n p = 1")
if err != nil || !reflect.DeepEqual(expectedBytes, bs) {
t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err)
}

bs, err = s.GetPolicy(ctx, txn, "/roles/policy.rego")
expectedBytes = []byte("package bar\n p = 1")
if err != nil || !reflect.DeepEqual(expectedBytes, bs) {
t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err)
}
})
bs, err = s.GetPolicy(ctx, txn, "/roles/policy.rego")
expectedBytes = []byte("package bar\n p = 1")
if err != nil || !reflect.DeepEqual(expectedBytes, bs) {
t.Fatalf("Expected get policy to return %v but got: %v (err: %v)", expectedBytes, bs, err)
}

// Close and re-open store
if err := s.Close(ctx); err != nil {
t.Fatalf("store close: %v", err)
}

if _, err := New(ctx, logging.NewNoOpLogger(), nil, Options{Dir: dir, Partitions: nil}); err != nil {
t.Fatalf("store re-open: %v", err)
}
}

func TestTruncateMultipleTxn(t *testing.T) {
Expand Down

0 comments on commit 0dee8cf

Please sign in to comment.