-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
100 changed files
with
2,485 additions
and
682 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package dryrun | ||
|
||
import ( | ||
"context" | ||
"hash" | ||
"io" | ||
|
||
"github.com/rubiojr/rapi/internal/debug" | ||
"github.com/rubiojr/rapi/restic" | ||
) | ||
|
||
// Backend passes reads through to an underlying layer and accepts writes, but | ||
// doesn't do anything. Also removes are ignored. | ||
// So in fact, this backend silently ignores all operations that would modify | ||
// the repo and does normal operations else. | ||
// This is used for `backup --dry-run`. | ||
type Backend struct { | ||
b restic.Backend | ||
} | ||
|
||
// statically ensure that RetryBackend implements restic.Backend. | ||
var _ restic.Backend = &Backend{} | ||
|
||
// New returns a new backend that saves all data in a map in memory. | ||
func New(be restic.Backend) *Backend { | ||
b := &Backend{b: be} | ||
debug.Log("created new dry backend") | ||
return b | ||
} | ||
|
||
// Save adds new Data to the backend. | ||
func (be *Backend) Save(ctx context.Context, h restic.Handle, rd restic.RewindReader) error { | ||
if err := h.Valid(); err != nil { | ||
return err | ||
} | ||
|
||
debug.Log("faked saving %v bytes at %v", rd.Length(), h) | ||
|
||
// don't save anything, just return ok | ||
return nil | ||
} | ||
|
||
// Remove deletes a file from the backend. | ||
func (be *Backend) Remove(ctx context.Context, h restic.Handle) error { | ||
return nil | ||
} | ||
|
||
// Location returns the location of the backend. | ||
func (be *Backend) Location() string { | ||
return "DRY:" + be.b.Location() | ||
} | ||
|
||
// Delete removes all data in the backend. | ||
func (be *Backend) Delete(ctx context.Context) error { | ||
return nil | ||
} | ||
|
||
func (be *Backend) Close() error { | ||
return be.b.Close() | ||
} | ||
|
||
func (be *Backend) Hasher() hash.Hash { | ||
return be.b.Hasher() | ||
} | ||
|
||
func (be *Backend) IsNotExist(err error) bool { | ||
return be.b.IsNotExist(err) | ||
} | ||
|
||
func (be *Backend) List(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) error { | ||
return be.b.List(ctx, t, fn) | ||
} | ||
|
||
func (be *Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(io.Reader) error) error { | ||
return be.b.Load(ctx, h, length, offset, fn) | ||
} | ||
|
||
func (be *Backend) Stat(ctx context.Context, h restic.Handle) (restic.FileInfo, error) { | ||
return be.b.Stat(ctx, h) | ||
} | ||
|
||
func (be *Backend) Test(ctx context.Context, h restic.Handle) (bool, error) { | ||
return be.b.Test(ctx, h) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package dryrun_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"sort" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/rubiojr/rapi/restic" | ||
|
||
"github.com/rubiojr/rapi/backend/dryrun" | ||
"github.com/rubiojr/rapi/backend/mem" | ||
) | ||
|
||
// make sure that Backend implements backend.Backend | ||
var _ restic.Backend = &dryrun.Backend{} | ||
|
||
func newBackends() (*dryrun.Backend, restic.Backend) { | ||
m := mem.New() | ||
return dryrun.New(m), m | ||
} | ||
|
||
func TestDry(t *testing.T) { | ||
ctx := context.TODO() | ||
|
||
d, m := newBackends() | ||
// Since the dry backend is a mostly write-only overlay, the standard backend test suite | ||
// won't pass. Instead, perform a series of operations over the backend, testing the state | ||
// at each step. | ||
steps := []struct { | ||
be restic.Backend | ||
op string | ||
fname string | ||
content string | ||
wantErr string | ||
}{ | ||
{d, "loc", "", "DRY:RAM", ""}, | ||
{d, "delete", "", "", ""}, | ||
{d, "stat", "a", "", "not found"}, | ||
{d, "list", "", "", ""}, | ||
{d, "save", "", "", "invalid"}, | ||
{d, "test", "a", "", ""}, | ||
{m, "save", "a", "baz", ""}, // save a directly to the mem backend | ||
{d, "save", "b", "foob", ""}, // b is not saved | ||
{d, "save", "b", "xxx", ""}, // no error as b is not saved | ||
{d, "test", "a", "1", ""}, | ||
{d, "test", "b", "", ""}, | ||
{d, "stat", "", "", "invalid"}, | ||
{d, "stat", "a", "a 3", ""}, | ||
{d, "load", "a", "baz", ""}, | ||
{d, "load", "b", "", "not found"}, | ||
{d, "list", "", "a", ""}, | ||
{d, "remove", "c", "", ""}, | ||
{d, "stat", "b", "", "not found"}, | ||
{d, "list", "", "a", ""}, | ||
{d, "remove", "a", "", ""}, // a is in fact not removed | ||
{d, "list", "", "a", ""}, | ||
{m, "remove", "a", "", ""}, // remove a from the mem backend | ||
{d, "list", "", "", ""}, | ||
{d, "close", "", "", ""}, | ||
{d, "close", "", "", ""}, | ||
} | ||
|
||
for i, step := range steps { | ||
var err error | ||
var boolRes bool | ||
|
||
handle := restic.Handle{Type: restic.PackFile, Name: step.fname} | ||
switch step.op { | ||
case "save": | ||
err = step.be.Save(ctx, handle, restic.NewByteReader([]byte(step.content), step.be.Hasher())) | ||
case "test": | ||
boolRes, err = step.be.Test(ctx, handle) | ||
if boolRes != (step.content != "") { | ||
t.Errorf("%d. Test(%q) = %v, want %v", i, step.fname, boolRes, step.content != "") | ||
} | ||
case "list": | ||
fileList := []string{} | ||
err = step.be.List(ctx, restic.PackFile, func(fi restic.FileInfo) error { | ||
fileList = append(fileList, fi.Name) | ||
return nil | ||
}) | ||
sort.Strings(fileList) | ||
files := strings.Join(fileList, " ") | ||
if files != step.content { | ||
t.Errorf("%d. List = %q, want %q", i, files, step.content) | ||
} | ||
case "loc": | ||
loc := step.be.Location() | ||
if loc != step.content { | ||
t.Errorf("%d. Location = %q, want %q", i, loc, step.content) | ||
} | ||
case "delete": | ||
err = step.be.Delete(ctx) | ||
case "remove": | ||
err = step.be.Remove(ctx, handle) | ||
case "stat": | ||
var fi restic.FileInfo | ||
fi, err = step.be.Stat(ctx, handle) | ||
if err == nil { | ||
fis := fmt.Sprintf("%s %d", fi.Name, fi.Size) | ||
if fis != step.content { | ||
t.Errorf("%d. Stat = %q, want %q", i, fis, step.content) | ||
} | ||
} | ||
case "load": | ||
data := "" | ||
err = step.be.Load(ctx, handle, 100, 0, func(rd io.Reader) error { | ||
buf, err := ioutil.ReadAll(rd) | ||
data = string(buf) | ||
return err | ||
}) | ||
if data != step.content { | ||
t.Errorf("%d. Load = %q, want %q", i, data, step.content) | ||
} | ||
case "close": | ||
err = step.be.Close() | ||
default: | ||
t.Fatalf("%d. unknown step operation %q", i, step.op) | ||
} | ||
if step.wantErr != "" { | ||
if err == nil { | ||
t.Errorf("%d. %s error = nil, want %q", i, step.op, step.wantErr) | ||
} else if !strings.Contains(err.Error(), step.wantErr) { | ||
t.Errorf("%d. %s error = %q, doesn't contain %q", i, step.op, err, step.wantErr) | ||
} else if step.wantErr == "not found" && !step.be.IsNotExist(err) { | ||
t.Errorf("%d. IsNotExist(%s error) = false, want true", i, step.op) | ||
} | ||
|
||
} else if err != nil { | ||
t.Errorf("%d. %s error = %q, want nil", i, step.op, err) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.