zrepl/daemon/pruner/pruner_test.go

207 lines
4.8 KiB
Go
Raw Normal View History

2018-08-29 19:00:45 +02:00
package pruner
import (
"context"
"fmt"
"github.com/stretchr/testify/assert"
"github.com/zrepl/zrepl/logger"
2018-08-30 11:49:06 +02:00
"github.com/zrepl/zrepl/pruning"
"github.com/zrepl/zrepl/replication/pdu"
"net"
"testing"
"time"
2018-08-29 19:00:45 +02:00
)
type mockFS struct {
path string
snaps []string
}
func (m *mockFS) Filesystem() *pdu.Filesystem {
return &pdu.Filesystem{
Path: m.path,
}
}
func (m *mockFS) FilesystemVersions() []*pdu.FilesystemVersion {
versions := make([]*pdu.FilesystemVersion, len(m.snaps))
for i, v := range m.snaps {
versions[i] = &pdu.FilesystemVersion{
2018-08-30 11:49:06 +02:00
Type: pdu.FilesystemVersion_Snapshot,
Name: v,
2018-08-29 19:00:45 +02:00
Creation: pdu.FilesystemVersionCreation(time.Unix(0, 0)),
Guid: uint64(i),
2018-08-29 19:00:45 +02:00
}
}
return versions
}
type mockTarget struct {
2018-08-30 11:49:06 +02:00
fss []mockFS
destroyed map[string][]string
listVersionsErrs map[string][]error
2018-08-29 19:00:45 +02:00
listFilesystemsErr []error
2018-08-30 11:49:06 +02:00
destroyErrs map[string][]error
2018-08-29 19:00:45 +02:00
}
func (t *mockTarget) ListFilesystems(ctx context.Context, req *pdu.ListFilesystemReq) (*pdu.ListFilesystemRes, error) {
2018-08-29 19:00:45 +02:00
if len(t.listFilesystemsErr) > 0 {
e := t.listFilesystemsErr[0]
t.listFilesystemsErr = t.listFilesystemsErr[1:]
return nil, e
}
fss := make([]*pdu.Filesystem, len(t.fss))
for i := range fss {
fss[i] = t.fss[i].Filesystem()
}
return &pdu.ListFilesystemRes{Filesystems: fss}, nil
2018-08-29 19:00:45 +02:00
}
func (t *mockTarget) ListFilesystemVersions(ctx context.Context, req *pdu.ListFilesystemVersionsReq) (*pdu.ListFilesystemVersionsRes, error) {
fs := req.Filesystem
2018-08-29 19:00:45 +02:00
if len(t.listVersionsErrs[fs]) != 0 {
e := t.listVersionsErrs[fs][0]
t.listVersionsErrs[fs] = t.listVersionsErrs[fs][1:]
return nil, e
}
for _, mfs := range t.fss {
if mfs.path != fs {
continue
}
return &pdu.ListFilesystemVersionsRes{Versions: mfs.FilesystemVersions()}, nil
2018-08-29 19:00:45 +02:00
}
return nil, fmt.Errorf("filesystem %s does not exist", fs)
}
2018-08-30 11:49:06 +02:00
func (t *mockTarget) DestroySnapshots(ctx context.Context, req *pdu.DestroySnapshotsReq) (*pdu.DestroySnapshotsRes, error) {
fs, snaps := req.Filesystem, req.Snapshots
2018-08-29 19:00:45 +02:00
if len(t.destroyErrs[fs]) != 0 {
e := t.destroyErrs[fs][0]
t.destroyErrs[fs] = t.destroyErrs[fs][1:]
return nil, e
}
destroyed := t.destroyed[fs]
2018-08-30 11:49:06 +02:00
res := make([]*pdu.DestroySnapshotRes, len(snaps))
for i, s := range snaps {
2018-08-29 19:00:45 +02:00
destroyed = append(destroyed, s.Name)
2018-08-30 11:49:06 +02:00
res[i] = &pdu.DestroySnapshotRes{Error: "", Snapshot: s}
2018-08-29 19:00:45 +02:00
}
t.destroyed[fs] = destroyed
2018-08-30 11:49:06 +02:00
return &pdu.DestroySnapshotsRes{Results: res}, nil
2018-08-29 19:00:45 +02:00
}
type mockCursor struct {
snapname string
guid uint64
}
2018-08-30 11:49:06 +02:00
type mockHistory struct {
2018-08-29 19:00:45 +02:00
errs map[string][]error
cursors map[string]*mockCursor
2018-08-29 19:00:45 +02:00
}
func (r *mockHistory) ReplicationCursor(ctx context.Context, req *pdu.ReplicationCursorReq) (*pdu.ReplicationCursorRes, error) {
fs := req.Filesystem
2018-08-29 19:00:45 +02:00
if len(r.errs[fs]) > 0 {
e := r.errs[fs][0]
r.errs[fs] = r.errs[fs][1:]
return nil, e
2018-08-29 19:00:45 +02:00
}
return &pdu.ReplicationCursorRes{Result: &pdu.ReplicationCursorRes_Guid{Guid: 0}}, nil
}
2018-08-29 19:00:45 +02:00
type stubNetErr struct {
msg string
temporary, timeout bool
2018-08-29 19:00:45 +02:00
}
var _ net.Error = stubNetErr{}
func (e stubNetErr) Error() string {
return e.msg
}
func (e stubNetErr) Temporary() bool { return e.temporary }
func (e stubNetErr) Timeout() bool { return e.timeout }
2018-08-29 19:00:45 +02:00
func TestPruner_Prune(t *testing.T) {
var _ net.Error = &net.OpError{} // we use it below
target := &mockTarget{
listFilesystemsErr: []error{
stubNetErr{msg: "fakerror0", temporary: true},
2018-08-29 19:00:45 +02:00
},
listVersionsErrs: map[string][]error{
"zroot/foo": {
stubNetErr{msg: "fakeerror1", temporary: true},
stubNetErr{msg: "fakeerror2", temporary: true,},
2018-08-29 19:00:45 +02:00
},
},
destroyErrs: map[string][]error{
"zroot/baz": {
stubNetErr{msg: "fakeerror3", temporary: true}, // first error puts it back in the queue
stubNetErr{msg:"permanent error"}, // so it will be last when pruner gives up due to permanent err
2018-08-29 19:00:45 +02:00
},
},
destroyed: make(map[string][]string),
fss: []mockFS{
{
path: "zroot/foo",
snaps: []string{
"keep_a",
"keep_b",
"drop_c",
"keep_d",
},
},
{
path: "zroot/bar",
snaps: []string{
"keep_e",
"keep_f",
"drop_g",
},
},
{
path: "zroot/baz",
snaps: []string{
"keep_h",
"drop_i",
},
},
},
}
2018-08-30 11:49:06 +02:00
history := &mockHistory{
2018-08-29 19:00:45 +02:00
errs: map[string][]error{
"zroot/foo": {
stubNetErr{msg: "fakeerror4", temporary: true},
2018-08-29 19:00:45 +02:00
},
},
}
keepRules := []pruning.KeepRule{pruning.MustKeepRegex("^keep", false)}
2018-08-29 19:00:45 +02:00
p := Pruner{
args: args{
ctx: WithLogger(context.Background(), logger.NewTestLogger(t)),
target: target,
receiver: history,
rules: keepRules,
retryWait: 10*time.Millisecond,
},
state: Plan,
}
p.Prune()
2018-08-29 19:00:45 +02:00
exp := map[string][]string{
"zroot/foo": {"drop_c"},
2018-08-30 11:49:06 +02:00
"zroot/bar": {"drop_g"},
2018-08-29 19:00:45 +02:00
}
assert.Equal(t, exp, target.destroyed)
//assert.Equal(t, map[string][]error{}, target.listVersionsErrs, "retried")
}