mirror of
https://github.com/restic/restic.git
synced 2025-03-16 00:00:05 +01:00
Implement a progress bar for restores
* Adds a Scan function for Restores (similar to the one in the Archiver) - this returns the size & number of items to be restored for the specified repository snapshot (instead of the filesystem) * We pass the scan results into the RestoreTo function which sorts out the Progress bar initialization * We can't do this in the restorer constructor like in the archiver because the filters aren't set up yet * At this stage this is just the same progress bar that is used for archiving * The bytes completed stats are reported by the Node itself (which involves passing the progress reference through) * he Restorer reports the other stats - namely items completed & error count
This commit is contained in:
parent
28202edf33
commit
d8377140f1
4 changed files with 50 additions and 10 deletions
|
@ -47,6 +47,10 @@ func init() {
|
|||
flags.StringSliceVar(&restoreOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
|
||||
}
|
||||
|
||||
func newRestoreProgress(gopts GlobalOptions, todo restic.Stat) *restic.Progress {
|
||||
return newArchiveProgress(gopts, todo)
|
||||
}
|
||||
|
||||
func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.Fatalf("no snapshot ID specified")
|
||||
|
@ -132,5 +136,10 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error {
|
|||
|
||||
Verbosef("restoring %s to %s\n", res.Snapshot(), opts.Target)
|
||||
|
||||
return res.RestoreTo(opts.Target)
|
||||
stat, err := res.Scan()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return res.RestoreTo(opts.Target, newRestoreProgress(gopts, stat))
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ func nodeTypeFromFileInfo(fi os.FileInfo) string {
|
|||
}
|
||||
|
||||
// CreateAt creates the node at the given path and restores all the meta data.
|
||||
func (node *Node) CreateAt(path string, repo Repository) error {
|
||||
func (node *Node) CreateAt(path string, repo Repository, p *Progress) error {
|
||||
debug.Log("create node %v at %v", node.Name, path)
|
||||
|
||||
switch node.Type {
|
||||
|
@ -106,7 +106,7 @@ func (node *Node) CreateAt(path string, repo Repository) error {
|
|||
return err
|
||||
}
|
||||
case "file":
|
||||
if err := node.createFileAt(path, repo); err != nil {
|
||||
if err := node.createFileAt(path, repo, p); err != nil {
|
||||
return err
|
||||
}
|
||||
case "symlink":
|
||||
|
@ -191,7 +191,7 @@ func (node Node) createDirAt(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (node Node) createFileAt(path string, repo Repository) error {
|
||||
func (node Node) createFileAt(path string, repo Repository, p *Progress) error {
|
||||
f, err := fs.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)
|
||||
defer f.Close()
|
||||
|
||||
|
@ -221,6 +221,8 @@ func (node Node) createFileAt(path string, repo Repository) error {
|
|||
if err != nil {
|
||||
return errors.Wrap(err, "Write")
|
||||
}
|
||||
|
||||
p.Report(Stat{Bytes: uint64(size)})
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -153,7 +153,7 @@ func TestNodeRestoreAt(t *testing.T) {
|
|||
|
||||
for _, test := range nodeTests {
|
||||
nodePath := filepath.Join(tempdir, test.Name)
|
||||
OK(t, test.CreateAt(nodePath, nil))
|
||||
OK(t, test.CreateAt(nodePath, nil, nil))
|
||||
|
||||
if test.Type == "symlink" && runtime.GOOS == "windows" {
|
||||
continue
|
||||
|
|
|
@ -12,8 +12,9 @@ import (
|
|||
|
||||
// Restorer is used to restore a snapshot to a directory.
|
||||
type Restorer struct {
|
||||
repo Repository
|
||||
sn *Snapshot
|
||||
repo Repository
|
||||
sn *Snapshot
|
||||
progressBar *Progress
|
||||
|
||||
Error func(dir string, node *Node, err error) error
|
||||
SelectFilter func(item string, node *Node) bool
|
||||
|
@ -39,6 +40,23 @@ func NewRestorer(repo Repository, id ID) (*Restorer, error) {
|
|||
return r, nil
|
||||
}
|
||||
|
||||
// Scan traverses the directories/files to be restored to collect restic.Stat information
|
||||
func (res *Restorer) Scan() (Stat, error) {
|
||||
var stat Stat
|
||||
|
||||
err := res.walk("", *res.sn.Tree, func(node *Node, dir string) error {
|
||||
if node.Type == "dir" {
|
||||
stat.Add(Stat{Dirs: 1})
|
||||
} else {
|
||||
stat.Add(Stat{Files: 1, Bytes: node.Size})
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return stat, err
|
||||
}
|
||||
|
||||
func (res *Restorer) walk(dir string, treeID ID, callback func(*Node, string) error) error {
|
||||
tree, err := res.repo.LoadTree(treeID)
|
||||
if err != nil {
|
||||
|
@ -105,7 +123,7 @@ func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error {
|
|||
debug.Log("node %v, dir %v, dst %v", node.Name, dir, dst)
|
||||
dstPath := filepath.Join(dst, dir, node.Name)
|
||||
|
||||
err := node.CreateAt(dstPath, res.repo)
|
||||
err := node.CreateAt(dstPath, res.repo, res.progressBar)
|
||||
if err != nil {
|
||||
debug.Log("node.CreateAt(%s) error %v", dstPath, err)
|
||||
}
|
||||
|
@ -117,18 +135,25 @@ func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error {
|
|||
// Create parent directories and retry
|
||||
err = fs.MkdirAll(filepath.Dir(dstPath), 0700)
|
||||
if err == nil || os.IsExist(errors.Cause(err)) {
|
||||
err = node.CreateAt(dstPath, res.repo)
|
||||
err = node.CreateAt(dstPath, res.repo, res.progressBar)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
debug.Log("error %v", err)
|
||||
res.progressBar.Report(Stat{Errors: 1})
|
||||
err = res.Error(dstPath, node, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if node.Type == "dir" {
|
||||
res.progressBar.Report(Stat{Dirs: 1})
|
||||
} else {
|
||||
res.progressBar.Report(Stat{Files: 1})
|
||||
}
|
||||
|
||||
debug.Log("successfully restored %v", node.Name)
|
||||
|
||||
return nil
|
||||
|
@ -136,7 +161,11 @@ func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error {
|
|||
|
||||
// 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 {
|
||||
func (res *Restorer) RestoreTo(dir string, p *Progress) error {
|
||||
res.progressBar = p
|
||||
res.progressBar.Start()
|
||||
defer res.progressBar.Done()
|
||||
|
||||
return res.restoreTo(dir, "", *res.sn.Tree)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue