From c55b6ee54481c03279bb41e3b10f440e933f2d1b Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 28 Aug 2016 22:19:48 +0200 Subject: [PATCH] Add restic.Fatal/f This is a new error which implements the restic.Fataler interface. Errors of this type are written to stderr, the restic exits. For all other errors, restic prints the stack trace (if available). --- src/cmds/restic/cmd_backup.go | 10 +++---- src/cmds/restic/cmd_cache.go | 4 --- src/cmds/restic/cmd_cat.go | 8 ++--- src/cmds/restic/cmd_check.go | 8 ++--- src/cmds/restic/cmd_dump.go | 6 ++-- src/cmds/restic/cmd_find.go | 8 ++--- src/cmds/restic/cmd_init.go | 5 ++-- src/cmds/restic/cmd_key.go | 11 ++++--- src/cmds/restic/cmd_list.go | 7 ++--- src/cmds/restic/cmd_ls.go | 4 +-- src/cmds/restic/cmd_mount.go | 5 ++-- src/cmds/restic/cmd_prune.go | 4 +-- src/cmds/restic/cmd_restore.go | 8 ++--- src/cmds/restic/cmd_snapshots.go | 4 +-- src/cmds/restic/global.go | 11 ++++--- src/cmds/restic/integration_fuse_test.go | 4 +-- src/cmds/restic/integration_test.go | 5 ++-- src/cmds/restic/main.go | 14 +++++---- src/restic/errors.go | 38 ++++++++++++++++++++++++ src/restic/progress.go | 3 +- 20 files changed, 89 insertions(+), 78 deletions(-) create mode 100644 src/restic/errors.go diff --git a/src/cmds/restic/cmd_backup.go b/src/cmds/restic/cmd_backup.go index 6888611ee..57628ac59 100644 --- a/src/cmds/restic/cmd_backup.go +++ b/src/cmds/restic/cmd_backup.go @@ -13,8 +13,6 @@ import ( "strings" "time" - "github.com/pkg/errors" - "golang.org/x/crypto/ssh/terminal" ) @@ -232,7 +230,7 @@ func filterExisting(items []string) (result []string, err error) { } if len(result) == 0 { - return nil, errors.New("all target directories/files do not exist") + return nil, restic.Fatal("all target directories/files do not exist") } return @@ -240,7 +238,7 @@ func filterExisting(items []string) (result []string, err error) { func (cmd CmdBackup) readFromStdin(args []string) error { if len(args) != 0 { - return errors.Errorf("when reading from stdin, no additional files can be specified") + return restic.Fatalf("when reading from stdin, no additional files can be specified") } repo, err := cmd.global.OpenRepository() @@ -274,7 +272,7 @@ func (cmd CmdBackup) Execute(args []string) error { } if len(args) == 0 { - return errors.Errorf("wrong number of parameters, Usage: %s", cmd.Usage()) + return restic.Fatalf("wrong number of parameters, Usage: %s", cmd.Usage()) } target := make([]string, 0, len(args)) @@ -312,7 +310,7 @@ func (cmd CmdBackup) Execute(args []string) error { if !cmd.Force && cmd.Parent != "" { id, err := restic.FindSnapshot(repo, cmd.Parent) if err != nil { - return errors.Errorf("invalid id %q: %v", cmd.Parent, err) + return restic.Fatalf("invalid id %q: %v", cmd.Parent, err) } parentSnapshotID = &id diff --git a/src/cmds/restic/cmd_cache.go b/src/cmds/restic/cmd_cache.go index 989571f40..aa4d5765f 100644 --- a/src/cmds/restic/cmd_cache.go +++ b/src/cmds/restic/cmd_cache.go @@ -25,10 +25,6 @@ func (cmd CmdCache) Usage() string { } func (cmd CmdCache) Execute(args []string) error { - // if len(args) == 0 || len(args) > 2 { - // return errors.Errorf("wrong number of parameters, Usage: %s", cmd.Usage()) - // } - repo, err := cmd.global.OpenRepository() if err != nil { return err diff --git a/src/cmds/restic/cmd_cat.go b/src/cmds/restic/cmd_cat.go index 2d5bbc18b..94d2ee023 100644 --- a/src/cmds/restic/cmd_cat.go +++ b/src/cmds/restic/cmd_cat.go @@ -5,8 +5,6 @@ import ( "fmt" "os" - "github.com/pkg/errors" - "restic" "restic/backend" "restic/debug" @@ -34,7 +32,7 @@ func (cmd CmdCat) Usage() string { func (cmd CmdCat) Execute(args []string) error { if len(args) < 1 || (args[0] != "masterkey" && args[0] != "config" && len(args) != 2) { - return errors.Errorf("type or ID not specified, Usage: %s", cmd.Usage()) + return restic.Fatalf("type or ID not specified, Usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() @@ -184,7 +182,7 @@ func (cmd CmdCat) Execute(args []string) error { return err } - return errors.New("blob not found") + return restic.Fatal("blob not found") case "tree": debug.Log("cat", "cat tree %v", id.Str()) @@ -205,6 +203,6 @@ func (cmd CmdCat) Execute(args []string) error { return nil default: - return errors.New("invalid type") + return restic.Fatal("invalid type") } } diff --git a/src/cmds/restic/cmd_check.go b/src/cmds/restic/cmd_check.go index 829996943..a151fc93b 100644 --- a/src/cmds/restic/cmd_check.go +++ b/src/cmds/restic/cmd_check.go @@ -5,8 +5,6 @@ import ( "os" "time" - "github.com/pkg/errors" - "golang.org/x/crypto/ssh/terminal" "restic" @@ -67,7 +65,7 @@ func (cmd CmdCheck) newReadProgress(todo restic.Stat) *restic.Progress { func (cmd CmdCheck) Execute(args []string) error { if len(args) != 0 { - return errors.New("check has no arguments") + return restic.Fatal("check has no arguments") } repo, err := cmd.global.OpenRepository() @@ -105,7 +103,7 @@ func (cmd CmdCheck) Execute(args []string) error { for _, err := range errs { cmd.global.Warnf("error: %v\n", err) } - return errors.Errorf("LoadIndex returned errors") + return restic.Fatal("LoadIndex returned errors") } done := make(chan struct{}) @@ -160,7 +158,7 @@ func (cmd CmdCheck) Execute(args []string) error { } if errorsFound { - return errors.New("repository contains errors") + return restic.Fatal("repository contains errors") } return nil } diff --git a/src/cmds/restic/cmd_dump.go b/src/cmds/restic/cmd_dump.go index 1d17e2f6a..32b789094 100644 --- a/src/cmds/restic/cmd_dump.go +++ b/src/cmds/restic/cmd_dump.go @@ -8,8 +8,6 @@ import ( "io" "os" - "github.com/pkg/errors" - "restic" "restic/backend" "restic/pack" @@ -204,7 +202,7 @@ func (cmd CmdDump) DumpIndexes() error { func (cmd CmdDump) Execute(args []string) error { if len(args) != 1 { - return errors.Errorf("type not specified, Usage: %s", cmd.Usage()) + return restic.Fatalf("type not specified, Usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() @@ -257,6 +255,6 @@ func (cmd CmdDump) Execute(args []string) error { return nil default: - return errors.Errorf("no such type %q", tpe) + return restic.Fatalf("no such type %q", tpe) } } diff --git a/src/cmds/restic/cmd_find.go b/src/cmds/restic/cmd_find.go index 2bd96caf6..1c66cd757 100644 --- a/src/cmds/restic/cmd_find.go +++ b/src/cmds/restic/cmd_find.go @@ -4,8 +4,6 @@ import ( "path/filepath" "time" - "github.com/pkg/errors" - "restic" "restic/backend" "restic/debug" @@ -58,7 +56,7 @@ func parseTime(str string) (time.Time, error) { } } - return time.Time{}, errors.Errorf("unable to parse time: %q", str) + return time.Time{}, restic.Fatalf("unable to parse time: %q", str) } func (c CmdFind) findInTree(repo *repository.Repository, id backend.ID, path string) ([]findResult, error) { @@ -138,7 +136,7 @@ func (CmdFind) Usage() string { func (c CmdFind) Execute(args []string) error { if len(args) != 1 { - return errors.Errorf("wrong number of arguments, Usage: %s", c.Usage()) + return restic.Fatalf("wrong number of arguments, Usage: %s", c.Usage()) } var err error @@ -178,7 +176,7 @@ func (c CmdFind) Execute(args []string) error { if c.Snapshot != "" { snapshotID, err := restic.FindSnapshot(repo, c.Snapshot) if err != nil { - return errors.Errorf("invalid id %q: %v", args[1], err) + return restic.Fatalf("invalid id %q: %v", args[1], err) } return c.findInSnapshot(repo, snapshotID) diff --git a/src/cmds/restic/cmd_init.go b/src/cmds/restic/cmd_init.go index 4bfc4a5c0..49b0907ad 100644 --- a/src/cmds/restic/cmd_init.go +++ b/src/cmds/restic/cmd_init.go @@ -1,8 +1,7 @@ package main import ( - "github.com/pkg/errors" - + "restic" "restic/repository" ) @@ -12,7 +11,7 @@ type CmdInit struct { func (cmd CmdInit) Execute(args []string) error { if cmd.global.Repo == "" { - return errors.New("Please specify repository location (-r)") + return restic.Fatal("Please specify repository location (-r)") } be, err := create(cmd.global.Repo) diff --git a/src/cmds/restic/cmd_key.go b/src/cmds/restic/cmd_key.go index 985a86081..67d5afa64 100644 --- a/src/cmds/restic/cmd_key.go +++ b/src/cmds/restic/cmd_key.go @@ -2,8 +2,7 @@ package main import ( "fmt" - - "github.com/pkg/errors" + "restic" "restic/backend" "restic/repository" @@ -70,7 +69,7 @@ func (cmd CmdKey) getNewPassword() string { func (cmd CmdKey) addKey(repo *repository.Repository) error { id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key()) if err != nil { - return errors.Errorf("creating new key failed: %v\n", err) + return restic.Fatalf("creating new key failed: %v\n", err) } cmd.global.Verbosef("saved new key as %s\n", id) @@ -80,7 +79,7 @@ func (cmd CmdKey) addKey(repo *repository.Repository) error { func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error { if name == repo.KeyName() { - return errors.New("refusing to remove key currently used to access repository") + return restic.Fatal("refusing to remove key currently used to access repository") } err := repo.Backend().Remove(backend.Key, name) @@ -95,7 +94,7 @@ func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error { func (cmd CmdKey) changePassword(repo *repository.Repository) error { id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key()) if err != nil { - return errors.Errorf("creating new key failed: %v\n", err) + return restic.Fatalf("creating new key failed: %v\n", err) } err = repo.Backend().Remove(backend.Key, repo.KeyName()) @@ -114,7 +113,7 @@ func (cmd CmdKey) Usage() string { func (cmd CmdKey) Execute(args []string) error { if len(args) < 1 || (args[0] == "rm" && len(args) != 2) { - return errors.Errorf("wrong number of arguments, Usage: %s", cmd.Usage()) + return restic.Fatalf("wrong number of arguments, Usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() diff --git a/src/cmds/restic/cmd_list.go b/src/cmds/restic/cmd_list.go index 2e0df3935..717d65ade 100644 --- a/src/cmds/restic/cmd_list.go +++ b/src/cmds/restic/cmd_list.go @@ -1,8 +1,7 @@ package main import ( - "github.com/pkg/errors" - + "restic" "restic/backend" ) @@ -26,7 +25,7 @@ func (cmd CmdList) Usage() string { func (cmd CmdList) Execute(args []string) error { if len(args) != 1 { - return errors.Errorf("type not specified, Usage: %s", cmd.Usage()) + return restic.Fatalf("type not specified, Usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() @@ -68,7 +67,7 @@ func (cmd CmdList) Execute(args []string) error { case "locks": t = backend.Lock default: - return errors.New("invalid type") + return restic.Fatal("invalid type") } for id := range repo.List(t, nil) { diff --git a/src/cmds/restic/cmd_ls.go b/src/cmds/restic/cmd_ls.go index aee7f77b9..c55670a93 100644 --- a/src/cmds/restic/cmd_ls.go +++ b/src/cmds/restic/cmd_ls.go @@ -5,8 +5,6 @@ import ( "os" "path/filepath" - "github.com/pkg/errors" - "restic" "restic/backend" "restic/repository" @@ -74,7 +72,7 @@ func (cmd CmdLs) Usage() string { func (cmd CmdLs) Execute(args []string) error { if len(args) < 1 || len(args) > 2 { - return errors.Errorf("wrong number of arguments, Usage: %s", cmd.Usage()) + return restic.Fatalf("wrong number of arguments, Usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() diff --git a/src/cmds/restic/cmd_mount.go b/src/cmds/restic/cmd_mount.go index 5b8186f05..e8bf4f272 100644 --- a/src/cmds/restic/cmd_mount.go +++ b/src/cmds/restic/cmd_mount.go @@ -5,8 +5,7 @@ package main import ( "os" - - "github.com/pkg/errors" + "restic" resticfs "restic/fs" "restic/fuse" @@ -43,7 +42,7 @@ func (cmd CmdMount) Usage() string { func (cmd CmdMount) Execute(args []string) error { if len(args) == 0 { - return errors.Errorf("wrong number of parameters, Usage: %s", cmd.Usage()) + return restic.Fatalf("wrong number of parameters, Usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() diff --git a/src/cmds/restic/cmd_prune.go b/src/cmds/restic/cmd_prune.go index 0b85c76ba..eee330131 100644 --- a/src/cmds/restic/cmd_prune.go +++ b/src/cmds/restic/cmd_prune.go @@ -11,8 +11,6 @@ import ( "restic/repository" "time" - "github.com/pkg/errors" - "golang.org/x/crypto/ssh/terminal" ) @@ -193,7 +191,7 @@ nextPack: removePacks.Insert(packID) if !rewritePacks.Has(packID) { - return errors.Errorf("pack %v is unneeded, but not contained in rewritePacks", packID.Str()) + return restic.Fatalf("pack %v is unneeded, but not contained in rewritePacks", packID.Str()) } rewritePacks.Delete(packID) diff --git a/src/cmds/restic/cmd_restore.go b/src/cmds/restic/cmd_restore.go index ecbd97fa7..9ff57565d 100644 --- a/src/cmds/restic/cmd_restore.go +++ b/src/cmds/restic/cmd_restore.go @@ -1,8 +1,6 @@ package main import ( - "github.com/pkg/errors" - "restic" "restic/backend" "restic/debug" @@ -35,15 +33,15 @@ func (cmd CmdRestore) Usage() string { func (cmd CmdRestore) Execute(args []string) error { if len(args) != 1 { - return errors.Errorf("wrong number of arguments, Usage: %s", cmd.Usage()) + return restic.Fatalf("wrong number of arguments, Usage: %s", cmd.Usage()) } if cmd.Target == "" { - return errors.New("please specify a directory to restore to (--target)") + return restic.Fatal("please specify a directory to restore to (--target)") } if len(cmd.Exclude) > 0 && len(cmd.Include) > 0 { - return errors.New("exclude and include patterns are mutually exclusive") + return restic.Fatal("exclude and include patterns are mutually exclusive") } snapshotIDString := args[0] diff --git a/src/cmds/restic/cmd_snapshots.go b/src/cmds/restic/cmd_snapshots.go index c55709ee3..ccf889d23 100644 --- a/src/cmds/restic/cmd_snapshots.go +++ b/src/cmds/restic/cmd_snapshots.go @@ -8,8 +8,6 @@ import ( "sort" "strings" - "github.com/pkg/errors" - "restic" "restic/backend" ) @@ -72,7 +70,7 @@ func (cmd CmdSnapshots) Usage() string { func (cmd CmdSnapshots) Execute(args []string) error { if len(args) != 0 { - return errors.Errorf("wrong number of arguments, usage: %s", cmd.Usage()) + return restic.Fatalf("wrong number of arguments, usage: %s", cmd.Usage()) } repo, err := cmd.global.OpenRepository() diff --git a/src/cmds/restic/global.go b/src/cmds/restic/global.go index d6ebec59b..9f3a014ed 100644 --- a/src/cmds/restic/global.go +++ b/src/cmds/restic/global.go @@ -4,12 +4,11 @@ import ( "fmt" "io" "os" + "restic" "runtime" "strings" "syscall" - "github.com/pkg/errors" - "restic/backend" "restic/backend/local" "restic/backend/rest" @@ -247,7 +246,7 @@ const maxKeys = 20 // OpenRepository reads the password and opens the repository. func (o GlobalOptions) OpenRepository() (*repository.Repository, error) { if o.Repo == "" { - return nil, errors.New("Please specify repository location (-r)") + return nil, restic.Fatal("Please specify repository location (-r)") } be, err := open(o.Repo) @@ -263,7 +262,7 @@ func (o GlobalOptions) OpenRepository() (*repository.Repository, error) { err = s.SearchKey(o.password, maxKeys) if err != nil { - return nil, errors.Errorf("unable to open repo: %v", err) + return nil, restic.Fatalf("unable to open repo: %v", err) } return s, nil @@ -301,7 +300,7 @@ func open(s string) (backend.Backend, error) { } debug.Log("open", "invalid repository location: %v", s) - return nil, errors.Errorf("invalid scheme %q", loc.Scheme) + return nil, restic.Fatalf("invalid scheme %q", loc.Scheme) } // Create the backend specified by URI. @@ -336,5 +335,5 @@ func create(s string) (backend.Backend, error) { } debug.Log("open", "invalid repository scheme: %v", s) - return nil, errors.Errorf("invalid scheme %q", loc.Scheme) + return nil, restic.Fatalf("invalid scheme %q", loc.Scheme) } diff --git a/src/cmds/restic/integration_fuse_test.go b/src/cmds/restic/integration_fuse_test.go index 5df4931b6..39e24668e 100644 --- a/src/cmds/restic/integration_fuse_test.go +++ b/src/cmds/restic/integration_fuse_test.go @@ -10,8 +10,6 @@ import ( "testing" "time" - "github.com/pkg/errors" - "restic" "restic/backend" "restic/repository" @@ -51,7 +49,7 @@ func waitForMount(dir string) error { time.Sleep(mountSleep) } - return errors.Errorf("subdir %q of dir %s never appeared", mountTestSubdir, dir) + return restic.Fatalf("subdir %q of dir %s never appeared", mountTestSubdir, dir) } func cmdMount(t testing.TB, global GlobalOptions, dir string, ready, done chan struct{}) { diff --git a/src/cmds/restic/integration_test.go b/src/cmds/restic/integration_test.go index ee1ef1893..755f5ec9a 100644 --- a/src/cmds/restic/integration_test.go +++ b/src/cmds/restic/integration_test.go @@ -10,13 +10,12 @@ import ( "os" "path/filepath" "regexp" + "restic" "strings" "syscall" "testing" "time" - "github.com/pkg/errors" - "restic/backend" "restic/debug" "restic/filter" @@ -581,7 +580,7 @@ func testFileSize(filename string, size int64) error { } if fi.Size() != size { - return errors.Errorf("wrong file size for %v: expected %v, got %v", filename, size, fi.Size()) + return restic.Fatalf("wrong file size for %v: expected %v, got %v", filename, size, fi.Size()) } return nil diff --git a/src/cmds/restic/main.go b/src/cmds/restic/main.go index 271ba0e41..5ad0ab128 100644 --- a/src/cmds/restic/main.go +++ b/src/cmds/restic/main.go @@ -37,13 +37,15 @@ func main() { os.Exit(0) } - if err != nil { - debug.Log("main", "command returned error: %#v", err) - fmt.Fprintf(os.Stderr, "%+v\n", err) - } + debug.Log("main", "command returned error: %#v", err) - if restic.IsAlreadyLocked(errors.Cause(err)) { - fmt.Fprintf(os.Stderr, "\nthe `unlock` command can be used to remove stale locks\n") + switch { + case restic.IsAlreadyLocked(errors.Cause(err)): + fmt.Fprintf(os.Stderr, "%v\nthe `unlock` command can be used to remove stale locks\n", err) + case restic.IsFatal(errors.Cause(err)): + fmt.Fprintf(os.Stderr, "%v\n", err) + case err != nil: + fmt.Fprintf(os.Stderr, "%+v\n", err) } RunCleanupHandlers() diff --git a/src/restic/errors.go b/src/restic/errors.go new file mode 100644 index 000000000..1aa7e1fdc --- /dev/null +++ b/src/restic/errors.go @@ -0,0 +1,38 @@ +package restic + +import "fmt" + +// fatalError is an error that should be printed to the user, then the program +// should exit with an error code. +type fatalError string + +func (e fatalError) Error() string { + return string(e) +} + +func (e fatalError) Fatal() bool { + return true +} + +// Fataler is an error which should be printed to the user directly. +// Afterwards, the program should exit with an error. +type Fataler interface { + Fatal() bool +} + +// IsFatal returns true if err is a fatal message that should be printed to the +// user. Then, the program should exit. +func IsFatal(err error) bool { + e, ok := err.(Fataler) + return ok && e.Fatal() +} + +// Fatal returns an error which implements the Fataler interface. +func Fatal(s string) error { + return fatalError(s) +} + +// Fatalf returns an error which implements the Fataler interface. +func Fatalf(s string, data ...interface{}) error { + return fatalError(fmt.Sprintf(s, data...)) +} diff --git a/src/restic/progress.go b/src/restic/progress.go index 2250cd689..49d9420d3 100644 --- a/src/restic/progress.go +++ b/src/restic/progress.go @@ -2,10 +2,11 @@ package restic import ( "fmt" - "golang.org/x/crypto/ssh/terminal" "os" "sync" "time" + + "golang.org/x/crypto/ssh/terminal" ) const minTickerTime = time.Second / 60