forked from TrueCloudLab/restic
Small refactorings and ceosmetic changes
This commit is contained in:
parent
a906b9febe
commit
c9f1f08019
7 changed files with 97 additions and 111 deletions
76
progress.go
76
progress.go
|
@ -34,7 +34,7 @@ type Stat struct {
|
|||
|
||||
type ProgressFunc func(s Stat, runtime time.Duration, ticker bool)
|
||||
|
||||
// NewProgress returns a new progress reporter. When Start() called, the
|
||||
// NewProgress returns a new progress reporter. When Start() is called, the
|
||||
// function OnStart is executed once. Afterwards the function OnUpdate is
|
||||
// called when new data arrives or at least every d interval. The function
|
||||
// OnDone is called when Done() is called. Both functions are called
|
||||
|
@ -43,7 +43,7 @@ func NewProgress(d time.Duration) *Progress {
|
|||
return &Progress{d: d}
|
||||
}
|
||||
|
||||
// Start runs resets and runs the progress reporter.
|
||||
// Start resets and runs the progress reporter.
|
||||
func (p *Progress) Start() {
|
||||
if p == nil || p.running {
|
||||
return
|
||||
|
@ -63,6 +63,21 @@ func (p *Progress) Start() {
|
|||
go p.reporter()
|
||||
}
|
||||
|
||||
// Reset resets all statistic counters to zero.
|
||||
func (p *Progress) Reset() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.running {
|
||||
panic("resetting a non-running Progress")
|
||||
}
|
||||
|
||||
p.curM.Lock()
|
||||
p.cur = Stat{}
|
||||
p.curM.Unlock()
|
||||
}
|
||||
|
||||
// Report adds the statistics from s to the current state and tries to report
|
||||
// the accumulated statistics via the feedback channel.
|
||||
func (p *Progress) Report(s Stat) {
|
||||
|
@ -79,12 +94,17 @@ func (p *Progress) Report(s Stat) {
|
|||
cur := p.cur
|
||||
p.curM.Unlock()
|
||||
|
||||
// update progress
|
||||
if p.OnUpdate != nil {
|
||||
p.fnM.Lock()
|
||||
p.OnUpdate(cur, time.Since(p.start), false)
|
||||
p.fnM.Unlock()
|
||||
p.updateProgress(cur, false)
|
||||
}
|
||||
|
||||
func (p *Progress) updateProgress(cur Stat, ticker bool) {
|
||||
if p.OnUpdate == nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.fnM.Lock()
|
||||
p.OnUpdate(cur, time.Since(p.start), ticker)
|
||||
p.fnM.Unlock()
|
||||
}
|
||||
|
||||
func (p *Progress) reporter() {
|
||||
|
@ -98,12 +118,7 @@ func (p *Progress) reporter() {
|
|||
p.curM.Lock()
|
||||
cur := p.cur
|
||||
p.curM.Unlock()
|
||||
|
||||
if p.OnUpdate != nil {
|
||||
p.fnM.Lock()
|
||||
p.OnUpdate(cur, time.Since(p.start), true)
|
||||
p.fnM.Unlock()
|
||||
}
|
||||
p.updateProgress(cur, true)
|
||||
case <-p.cancel:
|
||||
p.c.Stop()
|
||||
return
|
||||
|
@ -111,40 +126,23 @@ func (p *Progress) reporter() {
|
|||
}
|
||||
}
|
||||
|
||||
// Reset resets all statistic counters to zero.
|
||||
func (p *Progress) Reset() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.running {
|
||||
panic("resetting a non-running Progress")
|
||||
}
|
||||
|
||||
p.curM.Lock()
|
||||
p.cur = Stat{}
|
||||
p.curM.Unlock()
|
||||
}
|
||||
|
||||
// Done closes the progress report.
|
||||
func (p *Progress) Done() {
|
||||
if p == nil || !p.running {
|
||||
return
|
||||
}
|
||||
|
||||
if p.running {
|
||||
p.running = false
|
||||
p.o.Do(func() {
|
||||
close(p.cancel)
|
||||
})
|
||||
p.running = false
|
||||
p.o.Do(func() {
|
||||
close(p.cancel)
|
||||
})
|
||||
|
||||
cur := p.cur
|
||||
cur := p.cur
|
||||
|
||||
if p.OnDone != nil {
|
||||
p.fnM.Lock()
|
||||
p.OnDone(cur, time.Since(p.start), false)
|
||||
p.fnM.Unlock()
|
||||
}
|
||||
if p.OnDone != nil {
|
||||
p.fnM.Lock()
|
||||
p.OnDone(cur, time.Since(p.start), false)
|
||||
p.fnM.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
82
restorer.go
82
restorer.go
|
@ -19,57 +19,31 @@ type Restorer struct {
|
|||
Filter func(item string, dstpath string, node *Node) bool
|
||||
}
|
||||
|
||||
// NewRestorer creates a restorer preloaded with the content from the snapshot snid.
|
||||
func NewRestorer(s *server.Server, snid backend.ID) (*Restorer, error) {
|
||||
r := &Restorer{s: s}
|
||||
var abortOnAllErrors = func(str string, node *Node, err error) error { return err }
|
||||
|
||||
// NewRestorer creates a restorer preloaded with the content from the snapshot id.
|
||||
func NewRestorer(s *server.Server, id backend.ID) (*Restorer, error) {
|
||||
r := &Restorer{s: s, Error: abortOnAllErrors}
|
||||
|
||||
var err error
|
||||
|
||||
r.sn, err = LoadSnapshot(s, snid)
|
||||
r.sn, err = LoadSnapshot(s, id)
|
||||
if err != nil {
|
||||
return nil, arrar.Annotate(err, "load snapshot for restorer")
|
||||
}
|
||||
|
||||
// abort on all errors
|
||||
r.Error = func(string, *Node, error) error { return err }
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (res *Restorer) to(dst string, dir string, treeID backend.ID) error {
|
||||
func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error {
|
||||
tree, err := LoadTree(res.s, treeID)
|
||||
if err != nil {
|
||||
return res.Error(dir, nil, arrar.Annotate(err, "LoadTree"))
|
||||
}
|
||||
|
||||
for _, node := range tree.Nodes {
|
||||
dstpath := filepath.Join(dst, dir, node.Name)
|
||||
|
||||
if res.Filter == nil ||
|
||||
res.Filter(filepath.Join(dir, node.Name), dstpath, node) {
|
||||
err := node.CreateAt(dstpath, res.s)
|
||||
|
||||
// Did it fail because of ENOENT?
|
||||
if arrar.Check(err, func(err error) bool {
|
||||
if pe, ok := err.(*os.PathError); ok {
|
||||
errn, ok := pe.Err.(syscall.Errno)
|
||||
return ok && errn == syscall.ENOENT
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
// Create parent directories and retry
|
||||
err = os.MkdirAll(filepath.Dir(dstpath), 0700)
|
||||
if err == nil || err == os.ErrExist {
|
||||
err = node.CreateAt(dstpath, res.s)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = res.Error(dstpath, node, arrar.Annotate(err, "create node"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := res.restoreNodeTo(node, dir, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if node.Type == "dir" {
|
||||
|
@ -78,7 +52,7 @@ func (res *Restorer) to(dst string, dir string, treeID backend.ID) error {
|
|||
}
|
||||
|
||||
subp := filepath.Join(dir, node.Name)
|
||||
err = res.to(dst, subp, node.Subtree)
|
||||
err = res.restoreTo(dst, subp, node.Subtree)
|
||||
if err != nil {
|
||||
err = res.Error(subp, node, arrar.Annotate(err, "restore subtree"))
|
||||
if err != nil {
|
||||
|
@ -91,10 +65,44 @@ func (res *Restorer) to(dst string, dir string, treeID backend.ID) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error {
|
||||
dstPath := filepath.Join(dst, dir, node.Name)
|
||||
|
||||
if res.Filter != nil && res.Filter(filepath.Join(dir, node.Name), dstPath, node) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := node.CreateAt(dstPath, res.s)
|
||||
|
||||
// Did it fail because of ENOENT?
|
||||
if arrar.Check(err, func(err error) bool {
|
||||
if pe, ok := err.(*os.PathError); ok {
|
||||
errn, ok := pe.Err.(syscall.Errno)
|
||||
return ok && errn == syscall.ENOENT
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
// Create parent directories and retry
|
||||
err = os.MkdirAll(filepath.Dir(dstPath), 0700)
|
||||
if err == nil || err == os.ErrExist {
|
||||
err = node.CreateAt(dstPath, res.s)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = res.Error(dstPath, node, arrar.Annotate(err, "create node"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreTo creates the directories and files in the snapshot below dir.
|
||||
// Before an item is created, res.Filter is called.
|
||||
func (res *Restorer) RestoreTo(dir string) error {
|
||||
return res.to(dir, "", res.sn.Tree)
|
||||
return res.restoreTo(dir, "", res.sn.Tree)
|
||||
}
|
||||
|
||||
func (res *Restorer) Snapshot() *Snapshot {
|
||||
|
|
|
@ -2,29 +2,17 @@ package restic_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic"
|
||||
"github.com/restic/restic/server"
|
||||
. "github.com/restic/restic/test"
|
||||
)
|
||||
|
||||
func testSnapshot(t *testing.T, s *server.Server) {
|
||||
var err error
|
||||
sn, err := restic.NewSnapshot([]string{"/home/foobar"})
|
||||
OK(t, err)
|
||||
// sn.Tree, err = restic.Blob{ID: backend.ParseID("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2")}
|
||||
// ok(t, err)
|
||||
sn.Time, err = time.Parse(time.RFC3339Nano, "2014-08-03T17:49:05.378595539+02:00")
|
||||
OK(t, err)
|
||||
|
||||
// _, err = sn.Save(be)
|
||||
// ok(t, err)
|
||||
}
|
||||
|
||||
func TestSnapshot(t *testing.T) {
|
||||
func TestNewSnapshot(t *testing.T) {
|
||||
s := SetupBackend(t)
|
||||
defer TeardownBackend(t, s)
|
||||
|
||||
testSnapshot(t, s)
|
||||
paths := []string{"/home/foobar"}
|
||||
|
||||
_, err := restic.NewSnapshot(paths)
|
||||
OK(t, err)
|
||||
}
|
||||
|
|
7
tree.go
7
tree.go
|
@ -60,9 +60,8 @@ func (t Tree) Equals(other *Tree) bool {
|
|||
}
|
||||
|
||||
func (t *Tree) Insert(node *Node) error {
|
||||
pos, _, err := t.find(node.Name)
|
||||
pos, _, err := t.binarySearch(node.Name)
|
||||
if err == nil {
|
||||
// already present
|
||||
return ErrNodeAlreadyInTree
|
||||
}
|
||||
|
||||
|
@ -74,7 +73,7 @@ func (t *Tree) Insert(node *Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t Tree) find(name string) (int, *Node, error) {
|
||||
func (t Tree) binarySearch(name string) (int, *Node, error) {
|
||||
pos := sort.Search(len(t.Nodes), func(i int) bool {
|
||||
return t.Nodes[i].Name >= name
|
||||
})
|
||||
|
@ -87,6 +86,6 @@ func (t Tree) find(name string) (int, *Node, error) {
|
|||
}
|
||||
|
||||
func (t Tree) Find(name string) (*Node, error) {
|
||||
_, node, err := t.find(name)
|
||||
_, node, err := t.binarySearch(name)
|
||||
return node, err
|
||||
}
|
||||
|
|
|
@ -21,8 +21,7 @@ var testFiles = []struct {
|
|||
{"bar/bla/blubb", []byte("This is just a test!\n")},
|
||||
}
|
||||
|
||||
// prepareDir creates a temporary directory and returns it.
|
||||
func prepareDir(t *testing.T) string {
|
||||
func createTempDir(t *testing.T) string {
|
||||
tempdir, err := ioutil.TempDir(*TestTempDir, "restic-test-")
|
||||
OK(t, err)
|
||||
|
||||
|
@ -48,7 +47,7 @@ func prepareDir(t *testing.T) string {
|
|||
}
|
||||
|
||||
func TestTree(t *testing.T) {
|
||||
dir := prepareDir(t)
|
||||
dir := createTempDir(t)
|
||||
defer func() {
|
||||
if *TestCleanup {
|
||||
OK(t, os.RemoveAll(dir))
|
||||
|
@ -89,7 +88,7 @@ func TestNodeComparison(t *testing.T) {
|
|||
n2 := *node
|
||||
Assert(t, node.Equals(n2), "nodes aren't equal")
|
||||
|
||||
n2.Size -= 1
|
||||
n2.Size--
|
||||
Assert(t, !node.Equals(n2), "nodes are equal")
|
||||
}
|
||||
|
||||
|
|
8
walk.go
8
walk.go
|
@ -18,7 +18,7 @@ type WalkTreeJob struct {
|
|||
|
||||
func walkTree(s *server.Server, path string, treeID backend.ID, done chan struct{}, jobCh chan<- WalkTreeJob) {
|
||||
debug.Log("walkTree", "start on %q (%v)", path, treeID.Str())
|
||||
// load tree
|
||||
|
||||
t, err := LoadTree(s, treeID)
|
||||
if err != nil {
|
||||
jobCh <- WalkTreeJob{Path: path, Error: err}
|
||||
|
@ -30,15 +30,15 @@ func walkTree(s *server.Server, path string, treeID backend.ID, done chan struct
|
|||
if node.Type == "dir" {
|
||||
walkTree(s, p, node.Subtree, done, jobCh)
|
||||
} else {
|
||||
jobCh <- WalkTreeJob{Path: p, Node: node, Error: err}
|
||||
jobCh <- WalkTreeJob{Path: p, Node: node}
|
||||
}
|
||||
}
|
||||
|
||||
jobCh <- WalkTreeJob{Path: filepath.Join(path), Tree: t}
|
||||
jobCh <- WalkTreeJob{Path: path, Tree: t}
|
||||
debug.Log("walkTree", "done for %q (%v)", path, treeID.Str())
|
||||
}
|
||||
|
||||
// WalkTree walks the tree specified by ID recursively and sends a job for each
|
||||
// WalkTree walks the tree specified by id recursively and sends a job for each
|
||||
// file and directory it finds. When the channel done is closed, processing
|
||||
// stops.
|
||||
func WalkTree(server *server.Server, id backend.ID, done chan struct{}, jobCh chan<- WalkTreeJob) {
|
||||
|
|
|
@ -30,11 +30,6 @@ func TestWalkTree(t *testing.T) {
|
|||
// flush server, write all packs
|
||||
OK(t, server.Flush())
|
||||
|
||||
// start benchmark
|
||||
// t.ResetTimer()
|
||||
|
||||
// for i := 0; i < t.N; i++ {
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
// start tree walker
|
||||
|
@ -89,5 +84,4 @@ func TestWalkTree(t *testing.T) {
|
|||
Assert(t, fsEntries == treeEntries,
|
||||
"wrong number of entries: %v != %v", fsEntries, treeEntries)
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue