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).
This commit is contained in:
Alexander Neumann 2016-08-28 22:19:48 +02:00
parent 045f545085
commit c55b6ee544
20 changed files with 89 additions and 78 deletions

View File

@ -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

View File

@ -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

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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) {

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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]

View File

@ -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()

View File

@ -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)
}

View File

@ -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{}) {

View File

@ -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

View File

@ -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()

38
src/restic/errors.go Normal file
View File

@ -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...))
}

View File

@ -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