mirror of
https://github.com/octoleo/restic.git
synced 2024-11-26 23:06:32 +00:00
Merge pull request #580 from restic/remove-juju-errors
Change errors library
This commit is contained in:
commit
769f06cea2
@ -3,7 +3,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -17,6 +19,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ForbiddenImports are the packages from the stdlib that should not be used in
|
||||||
|
// our code.
|
||||||
|
var ForbiddenImports = map[string]bool{
|
||||||
|
"errors": true,
|
||||||
|
}
|
||||||
|
|
||||||
var runCrossCompile = flag.Bool("cross-compile", true, "run cross compilation tests")
|
var runCrossCompile = flag.Bool("cross-compile", true, "run cross compilation tests")
|
||||||
var minioServer = flag.String("minio", "", "path to the minio server binary")
|
var minioServer = flag.String("minio", "", "path to the minio server binary")
|
||||||
var debug = flag.Bool("debug", false, "output debug messages")
|
var debug = flag.Bool("debug", false, "output debug messages")
|
||||||
@ -345,7 +353,30 @@ func (env *TravisEnvironment) RunTests() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return runGofmt()
|
if err = runGofmt(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
deps, err := findImports()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
foundForbiddenImports := false
|
||||||
|
for name, imports := range deps {
|
||||||
|
for _, pkg := range imports {
|
||||||
|
if _, ok := ForbiddenImports[pkg]; ok {
|
||||||
|
fmt.Fprintf(os.Stderr, "========== package %v imports forbidden package %v\n", name, pkg)
|
||||||
|
foundForbiddenImports = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundForbiddenImports {
|
||||||
|
return errors.New("CI: forbidden imports found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppveyorEnvironment is the environment on Windows.
|
// AppveyorEnvironment is the environment on Windows.
|
||||||
@ -413,6 +444,46 @@ func updateEnv(env []string, override map[string]string) []string {
|
|||||||
return newEnv
|
return newEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findImports() (map[string][]string, error) {
|
||||||
|
res := make(map[string][]string)
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Getwd() returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gopath := cwd + ":" + filepath.Join(cwd, "vendor")
|
||||||
|
|
||||||
|
cmd := exec.Command("go", "list", "-f", `{{.ImportPath}} {{join .Imports " "}}`, "./src/...")
|
||||||
|
cmd.Env = updateEnv(os.Environ(), map[string]string{"GOPATH": gopath})
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sc := bufio.NewScanner(bytes.NewReader(output))
|
||||||
|
for sc.Scan() {
|
||||||
|
wordScanner := bufio.NewScanner(strings.NewReader(sc.Text()))
|
||||||
|
wordScanner.Split(bufio.ScanWords)
|
||||||
|
|
||||||
|
if !wordScanner.Scan() {
|
||||||
|
return nil, fmt.Errorf("package name not found in line: %s", output)
|
||||||
|
}
|
||||||
|
name := wordScanner.Text()
|
||||||
|
var deps []string
|
||||||
|
|
||||||
|
for wordScanner.Scan() {
|
||||||
|
deps = append(deps, wordScanner.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
res[name] = deps
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func runGofmt() error {
|
func runGofmt() error {
|
||||||
dir, err := os.Getwd()
|
dir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -14,6 +13,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -223,7 +224,7 @@ func (cmd CmdBackup) newArchiveStdinProgress() *restic.Progress {
|
|||||||
func filterExisting(items []string) (result []string, err error) {
|
func filterExisting(items []string) (result []string, err error) {
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
_, err := fs.Lstat(item)
|
_, err := fs.Lstat(item)
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(errors.Cause(err)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ func filterExisting(items []string) (result []string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(result) == 0 {
|
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
|
return
|
||||||
@ -239,7 +240,7 @@ func filterExisting(items []string) (result []string, err error) {
|
|||||||
|
|
||||||
func (cmd CmdBackup) readFromStdin(args []string) error {
|
func (cmd CmdBackup) readFromStdin(args []string) error {
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
return fmt.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()
|
repo, err := cmd.global.OpenRepository()
|
||||||
@ -273,7 +274,7 @@ func (cmd CmdBackup) Execute(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.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))
|
target := make([]string, 0, len(args))
|
||||||
@ -311,7 +312,7 @@ func (cmd CmdBackup) Execute(args []string) error {
|
|||||||
if !cmd.Force && cmd.Parent != "" {
|
if !cmd.Force && cmd.Parent != "" {
|
||||||
id, err := restic.FindSnapshot(repo, cmd.Parent)
|
id, err := restic.FindSnapshot(repo, cmd.Parent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid id %q: %v", cmd.Parent, err)
|
return restic.Fatalf("invalid id %q: %v", cmd.Parent, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parentSnapshotID = &id
|
parentSnapshotID = &id
|
||||||
|
@ -25,10 +25,6 @@ func (cmd CmdCache) Usage() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cmd CmdCache) Execute(args []string) error {
|
func (cmd CmdCache) Execute(args []string) error {
|
||||||
// if len(args) == 0 || len(args) > 2 {
|
|
||||||
// return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage())
|
|
||||||
// }
|
|
||||||
|
|
||||||
repo, err := cmd.global.OpenRepository()
|
repo, err := cmd.global.OpenRepository()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ func (cmd CmdCat) Usage() string {
|
|||||||
|
|
||||||
func (cmd CmdCat) Execute(args []string) error {
|
func (cmd CmdCat) Execute(args []string) error {
|
||||||
if len(args) < 1 || (args[0] != "masterkey" && args[0] != "config" && len(args) != 2) {
|
if len(args) < 1 || (args[0] != "masterkey" && args[0] != "config" && len(args) != 2) {
|
||||||
return fmt.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()
|
repo, err := cmd.global.OpenRepository()
|
||||||
@ -54,7 +53,7 @@ func (cmd CmdCat) Execute(args []string) error {
|
|||||||
id, err = backend.ParseID(args[1])
|
id, err = backend.ParseID(args[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if tpe != "snapshot" {
|
if tpe != "snapshot" {
|
||||||
return err
|
return restic.Fatalf("unable to parse ID: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// find snapshot id with prefix
|
// find snapshot id with prefix
|
||||||
@ -183,7 +182,7 @@ func (cmd CmdCat) Execute(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("blob not found")
|
return restic.Fatal("blob not found")
|
||||||
|
|
||||||
case "tree":
|
case "tree":
|
||||||
debug.Log("cat", "cat tree %v", id.Str())
|
debug.Log("cat", "cat tree %v", id.Str())
|
||||||
@ -204,6 +203,6 @@ func (cmd CmdCat) Execute(args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.New("invalid type")
|
return restic.Fatal("invalid type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@ -66,7 +65,7 @@ func (cmd CmdCheck) newReadProgress(todo restic.Stat) *restic.Progress {
|
|||||||
|
|
||||||
func (cmd CmdCheck) Execute(args []string) error {
|
func (cmd CmdCheck) Execute(args []string) error {
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
return errors.New("check has no arguments")
|
return restic.Fatal("check has no arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := cmd.global.OpenRepository()
|
repo, err := cmd.global.OpenRepository()
|
||||||
@ -104,7 +103,7 @@ func (cmd CmdCheck) Execute(args []string) error {
|
|||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
cmd.global.Warnf("error: %v\n", err)
|
cmd.global.Warnf("error: %v\n", err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("LoadIndex returned errors")
|
return restic.Fatal("LoadIndex returned errors")
|
||||||
}
|
}
|
||||||
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
@ -159,7 +158,7 @@ func (cmd CmdCheck) Execute(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if errorsFound {
|
if errorsFound {
|
||||||
return errors.New("repository contains errors")
|
return restic.Fatal("repository contains errors")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,6 @@ import (
|
|||||||
"restic/repository"
|
"restic/repository"
|
||||||
|
|
||||||
"restic/worker"
|
"restic/worker"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CmdDump struct {
|
type CmdDump struct {
|
||||||
@ -204,7 +202,7 @@ func (cmd CmdDump) DumpIndexes() error {
|
|||||||
|
|
||||||
func (cmd CmdDump) Execute(args []string) error {
|
func (cmd CmdDump) Execute(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("type not specified, Usage: %s", cmd.Usage())
|
return restic.Fatalf("type not specified, Usage: %s", cmd.Usage())
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := cmd.global.OpenRepository()
|
repo, err := cmd.global.OpenRepository()
|
||||||
@ -257,6 +255,6 @@ func (cmd CmdDump) Execute(args []string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("no such type %q", tpe)
|
return restic.Fatalf("no such type %q", tpe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -57,7 +56,7 @@ func parseTime(str string) (time.Time, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Time{}, fmt.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) {
|
func (c CmdFind) findInTree(repo *repository.Repository, id backend.ID, path string) ([]findResult, error) {
|
||||||
@ -137,7 +136,7 @@ func (CmdFind) Usage() string {
|
|||||||
|
|
||||||
func (c CmdFind) Execute(args []string) error {
|
func (c CmdFind) Execute(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("wrong number of arguments, Usage: %s", c.Usage())
|
return restic.Fatalf("wrong number of arguments, Usage: %s", c.Usage())
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -177,7 +176,7 @@ func (c CmdFind) Execute(args []string) error {
|
|||||||
if c.Snapshot != "" {
|
if c.Snapshot != "" {
|
||||||
snapshotID, err := restic.FindSnapshot(repo, c.Snapshot)
|
snapshotID, err := restic.FindSnapshot(repo, c.Snapshot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid id %q: %v", args[1], err)
|
return restic.Fatalf("invalid id %q: %v", args[1], err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.findInSnapshot(repo, snapshotID)
|
return c.findInSnapshot(repo, snapshotID)
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"restic"
|
||||||
|
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ type CmdInit struct {
|
|||||||
|
|
||||||
func (cmd CmdInit) Execute(args []string) error {
|
func (cmd CmdInit) Execute(args []string) error {
|
||||||
if cmd.global.Repo == "" {
|
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)
|
be, err := create(cmd.global.Repo)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"restic"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
@ -69,7 +69,7 @@ func (cmd CmdKey) getNewPassword() string {
|
|||||||
func (cmd CmdKey) addKey(repo *repository.Repository) error {
|
func (cmd CmdKey) addKey(repo *repository.Repository) error {
|
||||||
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key())
|
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.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)
|
cmd.global.Verbosef("saved new key as %s\n", id)
|
||||||
@ -79,7 +79,7 @@ func (cmd CmdKey) addKey(repo *repository.Repository) error {
|
|||||||
|
|
||||||
func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error {
|
func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error {
|
||||||
if name == repo.KeyName() {
|
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)
|
err := repo.Backend().Remove(backend.Key, name)
|
||||||
@ -94,7 +94,7 @@ func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error {
|
|||||||
func (cmd CmdKey) changePassword(repo *repository.Repository) error {
|
func (cmd CmdKey) changePassword(repo *repository.Repository) error {
|
||||||
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key())
|
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.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())
|
err = repo.Backend().Remove(backend.Key, repo.KeyName())
|
||||||
@ -113,7 +113,7 @@ func (cmd CmdKey) Usage() string {
|
|||||||
|
|
||||||
func (cmd CmdKey) Execute(args []string) error {
|
func (cmd CmdKey) Execute(args []string) error {
|
||||||
if len(args) < 1 || (args[0] == "rm" && len(args) != 2) {
|
if len(args) < 1 || (args[0] == "rm" && len(args) != 2) {
|
||||||
return fmt.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()
|
repo, err := cmd.global.OpenRepository()
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"restic"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +25,7 @@ func (cmd CmdList) Usage() string {
|
|||||||
|
|
||||||
func (cmd CmdList) Execute(args []string) error {
|
func (cmd CmdList) Execute(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("type not specified, Usage: %s", cmd.Usage())
|
return restic.Fatalf("type not specified, Usage: %s", cmd.Usage())
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, err := cmd.global.OpenRepository()
|
repo, err := cmd.global.OpenRepository()
|
||||||
@ -69,7 +67,7 @@ func (cmd CmdList) Execute(args []string) error {
|
|||||||
case "locks":
|
case "locks":
|
||||||
t = backend.Lock
|
t = backend.Lock
|
||||||
default:
|
default:
|
||||||
return errors.New("invalid type")
|
return restic.Fatal("invalid type")
|
||||||
}
|
}
|
||||||
|
|
||||||
for id := range repo.List(t, nil) {
|
for id := range repo.List(t, nil) {
|
||||||
|
@ -72,7 +72,7 @@ func (cmd CmdLs) Usage() string {
|
|||||||
|
|
||||||
func (cmd CmdLs) Execute(args []string) error {
|
func (cmd CmdLs) Execute(args []string) error {
|
||||||
if len(args) < 1 || len(args) > 2 {
|
if len(args) < 1 || len(args) > 2 {
|
||||||
return fmt.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()
|
repo, err := cmd.global.OpenRepository()
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
"restic"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
resticfs "restic/fs"
|
resticfs "restic/fs"
|
||||||
"restic/fuse"
|
"restic/fuse"
|
||||||
@ -42,7 +44,7 @@ func (cmd CmdMount) Usage() string {
|
|||||||
|
|
||||||
func (cmd CmdMount) Execute(args []string) error {
|
func (cmd CmdMount) Execute(args []string) error {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return fmt.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()
|
repo, err := cmd.global.OpenRepository()
|
||||||
@ -56,7 +58,7 @@ func (cmd CmdMount) Execute(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mountpoint := args[0]
|
mountpoint := args[0]
|
||||||
if _, err := resticfs.Stat(mountpoint); os.IsNotExist(err) {
|
if _, err := resticfs.Stat(mountpoint); os.IsNotExist(errors.Cause(err)) {
|
||||||
cmd.global.Verbosef("Mountpoint %s doesn't exist, creating it\n", mountpoint)
|
cmd.global.Verbosef("Mountpoint %s doesn't exist, creating it\n", mountpoint)
|
||||||
err = resticfs.Mkdir(mountpoint, os.ModeDir|0700)
|
err = resticfs.Mkdir(mountpoint, os.ModeDir|0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -191,7 +191,7 @@ nextPack:
|
|||||||
removePacks.Insert(packID)
|
removePacks.Insert(packID)
|
||||||
|
|
||||||
if !rewritePacks.Has(packID) {
|
if !rewritePacks.Has(packID) {
|
||||||
return fmt.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)
|
rewritePacks.Delete(packID)
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
@ -36,15 +33,15 @@ func (cmd CmdRestore) Usage() string {
|
|||||||
|
|
||||||
func (cmd CmdRestore) Execute(args []string) error {
|
func (cmd CmdRestore) Execute(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return fmt.Errorf("wrong number of arguments, Usage: %s", cmd.Usage())
|
return restic.Fatalf("wrong number of arguments, Usage: %s", cmd.Usage())
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Target == "" {
|
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 {
|
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]
|
snapshotIDString := args[0]
|
||||||
|
@ -70,7 +70,7 @@ func (cmd CmdSnapshots) Usage() string {
|
|||||||
|
|
||||||
func (cmd CmdSnapshots) Execute(args []string) error {
|
func (cmd CmdSnapshots) Execute(args []string) error {
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
return fmt.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()
|
repo, err := cmd.global.OpenRepository()
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"restic"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -19,6 +19,7 @@ import (
|
|||||||
"restic/repository"
|
"restic/repository"
|
||||||
|
|
||||||
"github.com/jessevdk/go-flags"
|
"github.com/jessevdk/go-flags"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -183,7 +184,7 @@ func readPassword(in io.Reader) (password string, err error) {
|
|||||||
n, err := io.ReadFull(in, buf)
|
n, err := io.ReadFull(in, buf)
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
|
|
||||||
if err != nil && err != io.ErrUnexpectedEOF {
|
if err != nil && errors.Cause(err) != io.ErrUnexpectedEOF {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +247,7 @@ const maxKeys = 20
|
|||||||
// OpenRepository reads the password and opens the repository.
|
// OpenRepository reads the password and opens the repository.
|
||||||
func (o GlobalOptions) OpenRepository() (*repository.Repository, error) {
|
func (o GlobalOptions) OpenRepository() (*repository.Repository, error) {
|
||||||
if o.Repo == "" {
|
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)
|
be, err := open(o.Repo)
|
||||||
@ -262,7 +263,7 @@ func (o GlobalOptions) OpenRepository() (*repository.Repository, error) {
|
|||||||
|
|
||||||
err = s.SearchKey(o.password, maxKeys)
|
err = s.SearchKey(o.password, maxKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to open repo: %v", err)
|
return nil, restic.Fatalf("unable to open repo: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
@ -300,7 +301,7 @@ func open(s string) (backend.Backend, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("open", "invalid repository location: %v", s)
|
debug.Log("open", "invalid repository location: %v", s)
|
||||||
return nil, fmt.Errorf("invalid scheme %q", loc.Scheme)
|
return nil, restic.Fatalf("invalid scheme %q", loc.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the backend specified by URI.
|
// Create the backend specified by URI.
|
||||||
@ -335,5 +336,5 @@ func create(s string) (backend.Backend, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("open", "invalid repository scheme: %v", s)
|
debug.Log("open", "invalid repository scheme: %v", s)
|
||||||
return nil, fmt.Errorf("invalid scheme %q", loc.Scheme)
|
return nil, restic.Fatalf("invalid scheme %q", loc.Scheme)
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
@ -50,7 +51,7 @@ func waitForMount(dir string) error {
|
|||||||
time.Sleep(mountSleep)
|
time.Sleep(mountSleep)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.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{}) {
|
func cmdMount(t testing.TB, global GlobalOptions, dir string, ready, done chan struct{}) {
|
||||||
@ -126,7 +127,7 @@ func TestMount(t *testing.T) {
|
|||||||
|
|
||||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||||
fd, err := os.Open(datafile)
|
fd, err := os.Open(datafile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"restic"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/filter"
|
"restic/filter"
|
||||||
@ -141,7 +144,7 @@ func TestBackup(t *testing.T) {
|
|||||||
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
||||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||||
fd, err := os.Open(datafile)
|
fd, err := os.Open(datafile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -203,7 +206,7 @@ func TestBackupNonExistingFile(t *testing.T) {
|
|||||||
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
||||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||||
fd, err := os.Open(datafile)
|
fd, err := os.Open(datafile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -231,7 +234,7 @@ func TestBackupMissingFile1(t *testing.T) {
|
|||||||
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
||||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||||
fd, err := os.Open(datafile)
|
fd, err := os.Open(datafile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -269,7 +272,7 @@ func TestBackupMissingFile2(t *testing.T) {
|
|||||||
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
||||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||||
fd, err := os.Open(datafile)
|
fd, err := os.Open(datafile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -307,7 +310,7 @@ func TestBackupDirectoryError(t *testing.T) {
|
|||||||
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
||||||
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
datafile := filepath.Join("testdata", "backup-data.tar.gz")
|
||||||
fd, err := os.Open(datafile)
|
fd, err := os.Open(datafile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
t.Skipf("unable to find data file %q, skipping", datafile)
|
t.Skipf("unable to find data file %q, skipping", datafile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -579,7 +582,7 @@ func testFileSize(filename string, size int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if fi.Size() != size {
|
if fi.Size() != size {
|
||||||
return fmt.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
|
return nil
|
||||||
@ -624,7 +627,7 @@ func TestRestoreFilter(t *testing.T) {
|
|||||||
if ok, _ := filter.Match(pat, filepath.Base(test.name)); !ok {
|
if ok, _ := filter.Match(pat, filepath.Base(test.name)); !ok {
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
} else {
|
} else {
|
||||||
Assert(t, os.IsNotExist(err),
|
Assert(t, os.IsNotExist(errors.Cause(err)),
|
||||||
"expected %v to not exist in restore step %v, but it exists, err %v", test.name, i+1, err)
|
"expected %v to not exist in restore step %v, but it exists, err %v", test.name, i+1, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,15 +675,15 @@ func TestRestoreLatest(t *testing.T) {
|
|||||||
|
|
||||||
cmdRestoreLatest(t, global, filepath.Join(env.base, "restore1"), []string{filepath.Dir(p1)}, "")
|
cmdRestoreLatest(t, global, filepath.Join(env.base, "restore1"), []string{filepath.Dir(p1)}, "")
|
||||||
OK(t, testFileSize(p1rAbs, int64(102)))
|
OK(t, testFileSize(p1rAbs, int64(102)))
|
||||||
if _, err := os.Stat(p2rAbs); os.IsNotExist(err) {
|
if _, err := os.Stat(p2rAbs); os.IsNotExist(errors.Cause(err)) {
|
||||||
Assert(t, os.IsNotExist(err),
|
Assert(t, os.IsNotExist(errors.Cause(err)),
|
||||||
"expected %v to not exist in restore, but it exists, err %v", p2rAbs, err)
|
"expected %v to not exist in restore, but it exists, err %v", p2rAbs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdRestoreLatest(t, global, filepath.Join(env.base, "restore2"), []string{filepath.Dir(p2)}, "")
|
cmdRestoreLatest(t, global, filepath.Join(env.base, "restore2"), []string{filepath.Dir(p2)}, "")
|
||||||
OK(t, testFileSize(p2rAbs, int64(103)))
|
OK(t, testFileSize(p2rAbs, int64(103)))
|
||||||
if _, err := os.Stat(p1rAbs); os.IsNotExist(err) {
|
if _, err := os.Stat(p1rAbs); os.IsNotExist(errors.Cause(err)) {
|
||||||
Assert(t, os.IsNotExist(err),
|
Assert(t, os.IsNotExist(errors.Cause(err)),
|
||||||
"expected %v to not exist in restore, but it exists, err %v", p1rAbs, err)
|
"expected %v to not exist in restore, but it exists, err %v", p1rAbs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jessevdk/go-flags"
|
|
||||||
"os"
|
"os"
|
||||||
"restic"
|
"restic"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/jessevdk/go-flags"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -35,12 +37,15 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
debug.Log("main", "command returned error: %#v", err)
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if restic.IsAlreadyLocked(err) {
|
switch {
|
||||||
fmt.Fprintf(os.Stderr, "\nthe `unlock` command can be used to remove stale locks\n")
|
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()
|
RunCleanupHandlers()
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"restic/repository"
|
"restic/repository"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/restic/chunker"
|
"github.com/restic/chunker"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ import (
|
|||||||
func saveTreeJSON(repo *repository.Repository, item interface{}) (backend.ID, error) {
|
func saveTreeJSON(repo *repository.Repository, item interface{}) (backend.ID, error) {
|
||||||
data, err := json.Marshal(item)
|
data, err := json.Marshal(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.ID{}, err
|
return backend.ID{}, errors.Wrap(err, "")
|
||||||
}
|
}
|
||||||
data = append(data, '\n')
|
data = append(data, '\n')
|
||||||
|
|
||||||
@ -48,12 +49,12 @@ func ArchiveReader(repo *repository.Repository, p *Progress, rd io.Reader, name
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
chunk, err := chnker.Next(getBuf())
|
chunk, err := chnker.Next(getBuf())
|
||||||
if err == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, backend.ID{}, err
|
return nil, backend.ID{}, errors.Wrap(err, "chunker.Next()")
|
||||||
}
|
}
|
||||||
|
|
||||||
id := backend.Hash(chunk.Data)
|
id := backend.Hash(chunk.Data)
|
||||||
|
@ -48,6 +48,9 @@ func checkSavedFile(t *testing.T, repo *repository.Repository, treeID backend.ID
|
|||||||
|
|
||||||
buf2 = buf2[:len(buf)]
|
buf2 = buf2[:len(buf)]
|
||||||
_, err = io.ReadFull(rd, buf2)
|
_, err = io.ReadFull(rd, buf2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if !bytes.Equal(buf, buf2) {
|
if !bytes.Equal(buf, buf2) {
|
||||||
t.Fatalf("blob %d (%v) is wrong", i, id.Str())
|
t.Fatalf("blob %d (%v) is wrong", i, id.Str())
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/fs"
|
"restic/fs"
|
||||||
@ -18,8 +20,6 @@ import (
|
|||||||
"restic/repository"
|
"restic/repository"
|
||||||
|
|
||||||
"github.com/restic/chunker"
|
"github.com/restic/chunker"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -113,7 +113,7 @@ func (arch *Archiver) Save(t pack.BlobType, data []byte, id backend.ID) error {
|
|||||||
func (arch *Archiver) SaveTreeJSON(item interface{}) (backend.ID, error) {
|
func (arch *Archiver) SaveTreeJSON(item interface{}) (backend.ID, error) {
|
||||||
data, err := json.Marshal(item)
|
data, err := json.Marshal(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.ID{}, err
|
return backend.ID{}, errors.Wrap(err, "Marshal")
|
||||||
}
|
}
|
||||||
data = append(data, '\n')
|
data = append(data, '\n')
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ func (arch *Archiver) SaveTreeJSON(item interface{}) (backend.ID, error) {
|
|||||||
func (arch *Archiver) reloadFileIfChanged(node *Node, file fs.File) (*Node, error) {
|
func (arch *Archiver) reloadFileIfChanged(node *Node, file fs.File) (*Node, error) {
|
||||||
fi, err := file.Stat()
|
fi, err := file.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
if fi.ModTime() == node.ModTime {
|
if fi.ModTime() == node.ModTime {
|
||||||
@ -178,7 +178,7 @@ func waitForResults(resultChannels [](<-chan saveResult)) ([]saveResult, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(results) != len(resultChannels) {
|
if len(results) != len(resultChannels) {
|
||||||
return nil, fmt.Errorf("chunker returned %v chunks, but only %v blobs saved", len(resultChannels), len(results))
|
return nil, errors.Errorf("chunker returned %v chunks, but only %v blobs saved", len(resultChannels), len(results))
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
@ -198,7 +198,7 @@ func updateNodeContent(node *Node, results []saveResult) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if bytes != node.Size {
|
if bytes != node.Size {
|
||||||
return fmt.Errorf("errors saving node %q: saved %d bytes, wanted %d bytes", node.path, bytes, node.Size)
|
return errors.Errorf("errors saving node %q: saved %d bytes, wanted %d bytes", node.path, bytes, node.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("Archiver.SaveFile", "SaveFile(%q): %v blobs\n", node.path, len(results))
|
debug.Log("Archiver.SaveFile", "SaveFile(%q): %v blobs\n", node.path, len(results))
|
||||||
@ -212,7 +212,7 @@ func (arch *Archiver) SaveFile(p *Progress, node *Node) error {
|
|||||||
file, err := fs.Open(node.path)
|
file, err := fs.Open(node.path)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err = arch.reloadFileIfChanged(node, file)
|
node, err = arch.reloadFileIfChanged(node, file)
|
||||||
@ -225,12 +225,12 @@ func (arch *Archiver) SaveFile(p *Progress, node *Node) error {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
chunk, err := chnker.Next(getBuf())
|
chunk, err := chnker.Next(getBuf())
|
||||||
if err == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "SaveFile() chunker.Next()")
|
return errors.Wrap(err, "chunker.Next")
|
||||||
}
|
}
|
||||||
|
|
||||||
resCh := make(chan saveResult, 1)
|
resCh := make(chan saveResult, 1)
|
||||||
@ -819,7 +819,7 @@ func Scan(dirs []string, filter pipe.SelectFunc, p *Progress) (Stat, error) {
|
|||||||
|
|
||||||
debug.Log("Scan", "Done for %v, err: %v", dir, err)
|
debug.Log("Scan", "Done for %v, err: %v", dir, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Stat{}, err
|
return Stat{}, errors.Wrap(err, "fs.Walk")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,13 +2,14 @@ package restic_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/pack"
|
"restic/pack"
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"restic/repository"
|
"restic/repository"
|
||||||
. "restic/test"
|
. "restic/test"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/restic/chunker"
|
"github.com/restic/chunker"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ func benchmarkChunkEncrypt(b testing.TB, buf, buf2 []byte, rd Rdr, key *crypto.K
|
|||||||
for {
|
for {
|
||||||
chunk, err := ch.Next(buf)
|
chunk, err := ch.Next(buf)
|
||||||
|
|
||||||
if err == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *crypto.Key)
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
chunk, err := ch.Next(buf)
|
chunk, err := ch.Next(buf)
|
||||||
if err == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +293,7 @@ func getRandomData(seed int, size int) []chunker.Chunk {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
c, err := chunker.Next(nil)
|
c, err := chunker.Next(nil)
|
||||||
if err == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
chunks = append(chunks, c)
|
chunks = append(chunks, c)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package backend
|
package backend
|
||||||
|
|
||||||
import "errors"
|
import "github.com/pkg/errors"
|
||||||
|
|
||||||
// ErrNoIDPrefixFound is returned by Find() when no ID for the given prefix
|
// ErrNoIDPrefixFound is returned by Find() when no ID for the given prefix
|
||||||
// could be found.
|
// could be found.
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package backend
|
package backend
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handle is used to store and access data in a backend.
|
// Handle is used to store and access data in a backend.
|
||||||
@ -33,7 +34,7 @@ func (h Handle) Valid() error {
|
|||||||
case Index:
|
case Index:
|
||||||
case Config:
|
case Config:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid Type %q", h.Type)
|
return errors.Errorf("invalid Type %q", h.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.Type == Config {
|
if h.Type == Config {
|
||||||
|
@ -5,7 +5,8 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash returns the ID for data.
|
// Hash returns the ID for data.
|
||||||
@ -24,7 +25,7 @@ func ParseID(s string) (ID, error) {
|
|||||||
b, err := hex.DecodeString(s)
|
b, err := hex.DecodeString(s)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ID{}, err
|
return ID{}, errors.Wrap(err, "hex.DecodeString")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) != IDSize {
|
if len(b) != IDSize {
|
||||||
@ -72,7 +73,7 @@ func (id ID) Equal(other ID) bool {
|
|||||||
func (id ID) EqualString(other string) (bool, error) {
|
func (id ID) EqualString(other string) (bool, error) {
|
||||||
s, err := hex.DecodeString(other)
|
s, err := hex.DecodeString(other)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, errors.Wrap(err, "hex.DecodeString")
|
||||||
}
|
}
|
||||||
|
|
||||||
id2 := ID{}
|
id2 := ID{}
|
||||||
@ -96,12 +97,12 @@ func (id *ID) UnmarshalJSON(b []byte) error {
|
|||||||
var s string
|
var s string
|
||||||
err := json.Unmarshal(b, &s)
|
err := json.Unmarshal(b, &s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Unmarshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = hex.Decode(id[:], []byte(s))
|
_, err = hex.Decode(id[:], []byte(s))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "hex.Decode")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseConfig parses a local backend config.
|
// ParseConfig parses a local backend config.
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/fs"
|
"restic/fs"
|
||||||
@ -35,7 +35,7 @@ func Open(dir string) (*Local, error) {
|
|||||||
// test if all necessary dirs are there
|
// test if all necessary dirs are there
|
||||||
for _, d := range paths(dir) {
|
for _, d := range paths(dir) {
|
||||||
if _, err := fs.Stat(d); err != nil {
|
if _, err := fs.Stat(d); err != nil {
|
||||||
return nil, fmt.Errorf("%s does not exist", d)
|
return nil, errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ func Create(dir string) (*Local, error) {
|
|||||||
for _, d := range paths(dir) {
|
for _, d := range paths(dir) {
|
||||||
err := fs.MkdirAll(d, backend.Modes.Dir)
|
err := fs.MkdirAll(d, backend.Modes.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "MkdirAll")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,13 +110,13 @@ func (b *Local) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
|
|
||||||
f, err := fs.Open(filename(b.p, h.Type, h.Name))
|
f, err := fs.Open(filename(b.p, h.Type, h.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
e := f.Close()
|
e := f.Close()
|
||||||
if err == nil && e != nil {
|
if err == nil && e != nil {
|
||||||
err = e
|
err = errors.Wrap(e, "Close")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ func (b *Local) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "Seek")
|
||||||
}
|
}
|
||||||
|
|
||||||
return io.ReadFull(f, p)
|
return io.ReadFull(f, p)
|
||||||
@ -138,12 +138,12 @@ func (b *Local) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
func writeToTempfile(tempdir string, p []byte) (filename string, err error) {
|
func writeToTempfile(tempdir string, p []byte) (filename string, err error) {
|
||||||
tmpfile, err := ioutil.TempFile(tempdir, "temp-")
|
tmpfile, err := ioutil.TempFile(tempdir, "temp-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "TempFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := tmpfile.Write(p)
|
n, err := tmpfile.Write(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "Write")
|
||||||
}
|
}
|
||||||
|
|
||||||
if n != len(p) {
|
if n != len(p) {
|
||||||
@ -151,17 +151,17 @@ func writeToTempfile(tempdir string, p []byte) (filename string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = tmpfile.Sync(); err != nil {
|
if err = tmpfile.Sync(); err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "Syncn")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = fs.ClearCache(tmpfile)
|
err = fs.ClearCache(tmpfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "ClearCache")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tmpfile.Close()
|
err = tmpfile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "Close")
|
||||||
}
|
}
|
||||||
|
|
||||||
return tmpfile.Name(), nil
|
return tmpfile.Name(), nil
|
||||||
@ -184,14 +184,14 @@ func (b *Local) Save(h backend.Handle, p []byte) (err error) {
|
|||||||
|
|
||||||
// test if new path already exists
|
// test if new path already exists
|
||||||
if _, err := fs.Stat(filename); err == nil {
|
if _, err := fs.Stat(filename); err == nil {
|
||||||
return fmt.Errorf("Rename(): file %v already exists", filename)
|
return errors.Errorf("Rename(): file %v already exists", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create directories if necessary, ignore errors
|
// create directories if necessary, ignore errors
|
||||||
if h.Type == backend.Data {
|
if h.Type == backend.Data {
|
||||||
err = fs.MkdirAll(filepath.Dir(filename), backend.Modes.Dir)
|
err = fs.MkdirAll(filepath.Dir(filename), backend.Modes.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "MkdirAll")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,13 +200,13 @@ func (b *Local) Save(h backend.Handle, p []byte) (err error) {
|
|||||||
h, filepath.Base(tmpfile), filepath.Base(filename), err)
|
h, filepath.Base(tmpfile), filepath.Base(filename), err)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Rename")
|
||||||
}
|
}
|
||||||
|
|
||||||
// set mode to read-only
|
// set mode to read-only
|
||||||
fi, err := fs.Stat(filename)
|
fi, err := fs.Stat(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
return setNewFileMode(filename, fi)
|
return setNewFileMode(filename, fi)
|
||||||
@ -221,7 +221,7 @@ func (b *Local) Stat(h backend.Handle) (backend.BlobInfo, error) {
|
|||||||
|
|
||||||
fi, err := fs.Stat(filename(b.p, h.Type, h.Name))
|
fi, err := fs.Stat(filename(b.p, h.Type, h.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.BlobInfo{}, err
|
return backend.BlobInfo{}, errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
return backend.BlobInfo{Size: fi.Size()}, nil
|
return backend.BlobInfo{Size: fi.Size()}, nil
|
||||||
@ -232,10 +232,10 @@ func (b *Local) Test(t backend.Type, name string) (bool, error) {
|
|||||||
debug.Log("backend.local.Test", "Test %v %v", t, name)
|
debug.Log("backend.local.Test", "Test %v %v", t, name)
|
||||||
_, err := fs.Stat(filename(b.p, t, name))
|
_, err := fs.Stat(filename(b.p, t, name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, err
|
return false, errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -249,7 +249,7 @@ func (b *Local) Remove(t backend.Type, name string) error {
|
|||||||
// reset read-only flag
|
// reset read-only flag
|
||||||
err := fs.Chmod(fn, 0666)
|
err := fs.Chmod(fn, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Chmod")
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs.Remove(fn)
|
return fs.Remove(fn)
|
||||||
@ -262,13 +262,13 @@ func isFile(fi os.FileInfo) bool {
|
|||||||
func readdir(d string) (fileInfos []os.FileInfo, err error) {
|
func readdir(d string) (fileInfos []os.FileInfo, err error) {
|
||||||
f, e := fs.Open(d)
|
f, e := fs.Open(d)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return nil, e
|
return nil, errors.Wrap(e, "Open")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
e := f.Close()
|
e := f.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = e
|
err = errors.Wrap(e, "Close")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package mem
|
package mem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package mem_test
|
package mem_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/backend/mem"
|
"restic/backend/mem"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package backend
|
package backend
|
||||||
|
|
||||||
import "errors"
|
import "github.com/pkg/errors"
|
||||||
|
|
||||||
// MockBackend implements a backend whose functions can be specified. This
|
// MockBackend implements a backend whose functions can be specified. This
|
||||||
// should only be used for tests.
|
// should only be used for tests.
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config contains all configuration necessary to connect to a REST server.
|
// Config contains all configuration necessary to connect to a REST server.
|
||||||
@ -21,7 +22,7 @@ func ParseConfig(s string) (interface{}, error) {
|
|||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "url.Parse")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := Config{URL: u}
|
cfg := Config{URL: u}
|
||||||
|
@ -3,7 +3,6 @@ package rest
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -11,6 +10,8 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ func (b *restBackend) Load(h backend.Handle, p []byte, off int64) (n int, err er
|
|||||||
if off < 0 {
|
if off < 0 {
|
||||||
info, err := b.Stat(h)
|
info, err := b.Stat(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
if -off > info.Size {
|
if -off > info.Size {
|
||||||
@ -91,7 +92,7 @@ func (b *restBackend) Load(h backend.Handle, p []byte, off int64) (n int, err er
|
|||||||
|
|
||||||
req, err := http.NewRequest("GET", restPath(b.url, h), nil)
|
req, err := http.NewRequest("GET", restPath(b.url, h), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "http.NewRequest")
|
||||||
}
|
}
|
||||||
req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(p))))
|
req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(p))))
|
||||||
<-b.connChan
|
<-b.connChan
|
||||||
@ -103,16 +104,16 @@ func (b *restBackend) Load(h backend.Handle, p []byte, off int64) (n int, err er
|
|||||||
e := resp.Body.Close()
|
e := resp.Body.Close()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = e
|
err = errors.Wrap(e, "Close")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "client.Do")
|
||||||
}
|
}
|
||||||
if resp.StatusCode != 200 && resp.StatusCode != 206 {
|
if resp.StatusCode != 200 && resp.StatusCode != 206 {
|
||||||
return 0, fmt.Errorf("unexpected HTTP response code %v", resp.StatusCode)
|
return 0, errors.Errorf("unexpected HTTP response code %v", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return io.ReadFull(resp.Body, p)
|
return io.ReadFull(resp.Body, p)
|
||||||
@ -133,17 +134,17 @@ func (b *restBackend) Save(h backend.Handle, p []byte) (err error) {
|
|||||||
e := resp.Body.Close()
|
e := resp.Body.Close()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = e
|
err = errors.Wrap(e, "Close")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "client.Post")
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return fmt.Errorf("unexpected HTTP response code %v", resp.StatusCode)
|
return errors.Errorf("unexpected HTTP response code %v", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -159,15 +160,15 @@ func (b *restBackend) Stat(h backend.Handle) (backend.BlobInfo, error) {
|
|||||||
resp, err := b.client.Head(restPath(b.url, h))
|
resp, err := b.client.Head(restPath(b.url, h))
|
||||||
b.connChan <- struct{}{}
|
b.connChan <- struct{}{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.BlobInfo{}, err
|
return backend.BlobInfo{}, errors.Wrap(err, "client.Head")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = resp.Body.Close(); err != nil {
|
if err = resp.Body.Close(); err != nil {
|
||||||
return backend.BlobInfo{}, err
|
return backend.BlobInfo{}, errors.Wrap(err, "Close")
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return backend.BlobInfo{}, fmt.Errorf("unexpected HTTP response code %v", resp.StatusCode)
|
return backend.BlobInfo{}, errors.Errorf("unexpected HTTP response code %v", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.ContentLength < 0 {
|
if resp.ContentLength < 0 {
|
||||||
@ -200,14 +201,14 @@ func (b *restBackend) Remove(t backend.Type, name string) error {
|
|||||||
|
|
||||||
req, err := http.NewRequest("DELETE", restPath(b.url, h), nil)
|
req, err := http.NewRequest("DELETE", restPath(b.url, h), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "http.NewRequest")
|
||||||
}
|
}
|
||||||
<-b.connChan
|
<-b.connChan
|
||||||
resp, err := b.client.Do(req)
|
resp, err := b.client.Do(req)
|
||||||
b.connChan <- struct{}{}
|
b.connChan <- struct{}{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "client.Do")
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package rest_test
|
package rest_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/backend/rest"
|
"restic/backend/rest"
|
||||||
"restic/backend/test"
|
"restic/backend/test"
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package s3
|
package s3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config contains all configuration necessary to connect to an s3 compatible
|
// Config contains all configuration necessary to connect to an s3 compatible
|
||||||
@ -31,7 +32,7 @@ func ParseConfig(s string) (interface{}, error) {
|
|||||||
// bucket name and prefix
|
// bucket name and prefix
|
||||||
url, err := url.Parse(s[3:])
|
url, err := url.Parse(s[3:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "url.Parse")
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.Path == "" {
|
if url.Path == "" {
|
||||||
|
@ -2,10 +2,11 @@ package s3
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/minio/minio-go"
|
"github.com/minio/minio-go"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
@ -29,7 +30,7 @@ func Open(cfg Config) (backend.Backend, error) {
|
|||||||
|
|
||||||
client, err := minio.New(cfg.Endpoint, cfg.KeyID, cfg.Secret, !cfg.UseHTTP)
|
client, err := minio.New(cfg.Endpoint, cfg.KeyID, cfg.Secret, !cfg.UseHTTP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "minio.New")
|
||||||
}
|
}
|
||||||
|
|
||||||
be := &s3{client: client, bucketname: cfg.Bucket, prefix: cfg.Prefix}
|
be := &s3{client: client, bucketname: cfg.Bucket, prefix: cfg.Prefix}
|
||||||
@ -38,14 +39,14 @@ func Open(cfg Config) (backend.Backend, error) {
|
|||||||
ok, err := client.BucketExists(cfg.Bucket)
|
ok, err := client.BucketExists(cfg.Bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("s3.Open", "BucketExists(%v) returned err %v, trying to create the bucket", cfg.Bucket, err)
|
debug.Log("s3.Open", "BucketExists(%v) returned err %v, trying to create the bucket", cfg.Bucket, err)
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "client.BucketExists")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
// create new bucket with default ACL in default region
|
// create new bucket with default ACL in default region
|
||||||
err = client.MakeBucket(cfg.Bucket, "")
|
err = client.MakeBucket(cfg.Bucket, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "client.MakeBucket")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,20 +95,20 @@ func (be s3) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
obj, err = be.client.GetObject(be.bucketname, path)
|
obj, err = be.client.GetObject(be.bucketname, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("s3.Load", " err %v", err)
|
debug.Log("s3.Load", " err %v", err)
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "client.GetObject")
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that the object is closed properly.
|
// make sure that the object is closed properly.
|
||||||
defer func() {
|
defer func() {
|
||||||
e := obj.Close()
|
e := obj.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = e
|
err = errors.Wrap(e, "Close")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
info, err := obj.Stat()
|
info, err := obj.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "obj.Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle negative offsets
|
// handle negative offsets
|
||||||
@ -124,7 +125,7 @@ func (be s3) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
|
|
||||||
// return an error if the offset is beyond the end of the file
|
// return an error if the offset is beyond the end of the file
|
||||||
if off > info.Size {
|
if off > info.Size {
|
||||||
return 0, io.EOF
|
return 0, errors.Wrap(io.EOF, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextError error
|
var nextError error
|
||||||
@ -140,7 +141,7 @@ func (be s3) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n, err = obj.ReadAt(p, off)
|
n, err = obj.ReadAt(p, off)
|
||||||
if int64(n) == info.Size-off && err == io.EOF {
|
if int64(n) == info.Size-off && errors.Cause(err) == io.EOF {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +179,7 @@ func (be s3) Save(h backend.Handle, p []byte) (err error) {
|
|||||||
n, err := be.client.PutObject(be.bucketname, path, bytes.NewReader(p), "binary/octet-stream")
|
n, err := be.client.PutObject(be.bucketname, path, bytes.NewReader(p), "binary/octet-stream")
|
||||||
debug.Log("s3.Save", "%v -> %v bytes, err %#v", path, n, err)
|
debug.Log("s3.Save", "%v -> %v bytes, err %#v", path, n, err)
|
||||||
|
|
||||||
return err
|
return errors.Wrap(err, "client.PutObject")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat returns information about a blob.
|
// Stat returns information about a blob.
|
||||||
@ -191,21 +192,21 @@ func (be s3) Stat(h backend.Handle) (bi backend.BlobInfo, err error) {
|
|||||||
obj, err = be.client.GetObject(be.bucketname, path)
|
obj, err = be.client.GetObject(be.bucketname, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("s3.Stat", "GetObject() err %v", err)
|
debug.Log("s3.Stat", "GetObject() err %v", err)
|
||||||
return backend.BlobInfo{}, err
|
return backend.BlobInfo{}, errors.Wrap(err, "client.GetObject")
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that the object is closed properly.
|
// make sure that the object is closed properly.
|
||||||
defer func() {
|
defer func() {
|
||||||
e := obj.Close()
|
e := obj.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = e
|
err = errors.Wrap(e, "Close")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fi, err := obj.Stat()
|
fi, err := obj.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("s3.Stat", "Stat() err %v", err)
|
debug.Log("s3.Stat", "Stat() err %v", err)
|
||||||
return backend.BlobInfo{}, err
|
return backend.BlobInfo{}, errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
return backend.BlobInfo{Size: fi.Size}, nil
|
return backend.BlobInfo{Size: fi.Size}, nil
|
||||||
@ -229,7 +230,7 @@ func (be *s3) Remove(t backend.Type, name string) error {
|
|||||||
path := be.s3path(t, name)
|
path := be.s3path(t, name)
|
||||||
err := be.client.RemoveObject(be.bucketname, path)
|
err := be.client.RemoveObject(be.bucketname, path)
|
||||||
debug.Log("s3.Remove", "%v %v -> err %v", t, name, err)
|
debug.Log("s3.Remove", "%v %v -> err %v", t, name, err)
|
||||||
return err
|
return errors.Wrap(err, "client.RemoveObject")
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns a channel that yields all names of blobs of type t. A
|
// List returns a channel that yields all names of blobs of type t. A
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package s3_test
|
package s3_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/backend/s3"
|
"restic/backend/s3"
|
||||||
"restic/backend/test"
|
"restic/backend/test"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package sftp
|
package sftp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config collects all information required to connect to an sftp server.
|
// Config collects all information required to connect to an sftp server.
|
||||||
@ -26,7 +26,7 @@ func ParseConfig(s string) (interface{}, error) {
|
|||||||
// parse the "sftp://user@host/path" url format
|
// parse the "sftp://user@host/path" url format
|
||||||
url, err := url.Parse(s)
|
url, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "url.Parse")
|
||||||
}
|
}
|
||||||
if url.User != nil {
|
if url.User != nil {
|
||||||
user = url.User.Username()
|
user = url.User.Username()
|
||||||
@ -34,7 +34,7 @@ func ParseConfig(s string) (interface{}, error) {
|
|||||||
host = url.Host
|
host = url.Host
|
||||||
dir = url.Path
|
dir = url.Path
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
return nil, fmt.Errorf("invalid backend %q, no directory specified", s)
|
return nil, errors.Errorf("invalid backend %q, no directory specified", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
dir = dir[1:]
|
dir = dir[1:]
|
||||||
|
@ -12,10 +12,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ func startClient(program string, args ...string) (*SFTP, error) {
|
|||||||
// prefix the errors with the program name
|
// prefix the errors with the program name
|
||||||
stderr, err := cmd.StderrPipe()
|
stderr, err := cmd.StderrPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "cmd.StderrPipe")
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -56,16 +57,16 @@ func startClient(program string, args ...string) (*SFTP, error) {
|
|||||||
// get stdin and stdout
|
// get stdin and stdout
|
||||||
wr, err := cmd.StdinPipe()
|
wr, err := cmd.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "cmd.StdinPipe")
|
||||||
}
|
}
|
||||||
rd, err := cmd.StdoutPipe()
|
rd, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "cmd.StdoutPipe")
|
||||||
}
|
}
|
||||||
|
|
||||||
// start the process
|
// start the process
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "cmd.Start")
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait in a different goroutine
|
// wait in a different goroutine
|
||||||
@ -73,13 +74,13 @@ func startClient(program string, args ...string) (*SFTP, error) {
|
|||||||
go func() {
|
go func() {
|
||||||
err := cmd.Wait()
|
err := cmd.Wait()
|
||||||
debug.Log("sftp.Wait", "ssh command exited, err %v", err)
|
debug.Log("sftp.Wait", "ssh command exited, err %v", err)
|
||||||
ch <- err
|
ch <- errors.Wrap(err, "cmd.Wait")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// open the SFTP session
|
// open the SFTP session
|
||||||
client, err := sftp.NewClientPipe(rd, wr)
|
client, err := sftp.NewClientPipe(rd, wr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to start the sftp session, error: %v", err)
|
return nil, errors.Errorf("unable to start the sftp session, error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SFTP{c: client, cmd: cmd, result: ch}, nil
|
return &SFTP{c: client, cmd: cmd, result: ch}, nil
|
||||||
@ -125,7 +126,7 @@ func Open(dir string, program string, args ...string) (*SFTP, error) {
|
|||||||
// test if all necessary dirs and files are there
|
// test if all necessary dirs and files are there
|
||||||
for _, d := range paths(dir) {
|
for _, d := range paths(dir) {
|
||||||
if _, err := sftp.c.Lstat(d); err != nil {
|
if _, err := sftp.c.Lstat(d); err != nil {
|
||||||
return nil, fmt.Errorf("%s does not exist", d)
|
return nil, errors.Errorf("%s does not exist", d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +182,7 @@ func Create(dir string, program string, args ...string) (*SFTP, error) {
|
|||||||
|
|
||||||
err = sftp.Close()
|
err = sftp.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Close")
|
||||||
}
|
}
|
||||||
|
|
||||||
// open backend
|
// open backend
|
||||||
@ -206,9 +207,8 @@ func (r *SFTP) tempFile() (string, *sftp.File, error) {
|
|||||||
buf := make([]byte, tempfileRandomSuffixLength)
|
buf := make([]byte, tempfileRandomSuffixLength)
|
||||||
_, err := io.ReadFull(rand.Reader, buf)
|
_, err := io.ReadFull(rand.Reader, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, errors.Annotatef(err,
|
return "", nil, errors.Errorf("unable to read %d random bytes for tempfile name: %v",
|
||||||
"unable to read %d random bytes for tempfile name",
|
tempfileRandomSuffixLength, err)
|
||||||
tempfileRandomSuffixLength)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct tempfile name
|
// construct tempfile name
|
||||||
@ -217,7 +217,7 @@ func (r *SFTP) tempFile() (string, *sftp.File, error) {
|
|||||||
// create file in temp dir
|
// create file in temp dir
|
||||||
f, err := r.c.Create(name)
|
f, err := r.c.Create(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, errors.Annotatef(err, "creating tempfile %q failed", name)
|
return "", nil, errors.Errorf("creating tempfile %q failed: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return name, f, nil
|
return name, f, nil
|
||||||
@ -231,7 +231,7 @@ func (r *SFTP) mkdirAll(dir string, mode os.FileMode) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("mkdirAll(%s): entry exists but is not a directory", dir)
|
return errors.Errorf("mkdirAll(%s): entry exists but is not a directory", dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create parent directories
|
// create parent directories
|
||||||
@ -244,11 +244,11 @@ func (r *SFTP) mkdirAll(dir string, mode os.FileMode) error {
|
|||||||
fi, err = r.c.Lstat(dir)
|
fi, err = r.c.Lstat(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// return previous errors
|
// return previous errors
|
||||||
return fmt.Errorf("mkdirAll(%s): unable to create directories: %v, %v", dir, errMkdirAll, errMkdir)
|
return errors.Errorf("mkdirAll(%s): unable to create directories: %v, %v", dir, errMkdirAll, errMkdir)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fi.IsDir() {
|
if !fi.IsDir() {
|
||||||
return fmt.Errorf("mkdirAll(%s): entry exists but is not a directory", dir)
|
return errors.Errorf("mkdirAll(%s): entry exists but is not a directory", dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set mode
|
// set mode
|
||||||
@ -269,21 +269,22 @@ func (r *SFTP) renameFile(oldname string, t backend.Type, name string) error {
|
|||||||
|
|
||||||
// test if new file exists
|
// test if new file exists
|
||||||
if _, err := r.c.Lstat(filename); err == nil {
|
if _, err := r.c.Lstat(filename); err == nil {
|
||||||
return fmt.Errorf("Close(): file %v already exists", filename)
|
return errors.Errorf("Close(): file %v already exists", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := r.c.Rename(oldname, filename)
|
err := r.c.Rename(oldname, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Rename")
|
||||||
}
|
}
|
||||||
|
|
||||||
// set mode to read-only
|
// set mode to read-only
|
||||||
fi, err := r.c.Lstat(filename)
|
fi, err := r.c.Lstat(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Lstat")
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.c.Chmod(filename, fi.Mode()&os.FileMode(^uint32(0222)))
|
err = r.c.Chmod(filename, fi.Mode()&os.FileMode(^uint32(0222)))
|
||||||
|
return errors.Wrap(err, "Chmod")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join joins the given paths and cleans them afterwards. This always uses
|
// Join joins the given paths and cleans them afterwards. This always uses
|
||||||
@ -336,13 +337,13 @@ func (r *SFTP) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
|
|
||||||
f, err := r.c.Open(r.filename(h.Type, h.Name))
|
f, err := r.c.Open(r.filename(h.Type, h.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
e := f.Close()
|
e := f.Close()
|
||||||
if err == nil && e != nil {
|
if err == nil && e != nil {
|
||||||
err = e
|
err = errors.Wrap(e, "Close")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -354,7 +355,7 @@ func (r *SFTP) Load(h backend.Handle, p []byte, off int64) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "Seek")
|
||||||
}
|
}
|
||||||
|
|
||||||
return io.ReadFull(f, p)
|
return io.ReadFull(f, p)
|
||||||
@ -380,7 +381,7 @@ func (r *SFTP) Save(h backend.Handle, p []byte) (err error) {
|
|||||||
|
|
||||||
n, err := tmpfile.Write(p)
|
n, err := tmpfile.Write(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Write")
|
||||||
}
|
}
|
||||||
|
|
||||||
if n != len(p) {
|
if n != len(p) {
|
||||||
@ -389,17 +390,13 @@ func (r *SFTP) Save(h backend.Handle, p []byte) (err error) {
|
|||||||
|
|
||||||
err = tmpfile.Close()
|
err = tmpfile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Close")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.renameFile(filename, h.Type, h.Name)
|
err = r.renameFile(filename, h.Type, h.Name)
|
||||||
debug.Log("sftp.Save", "save %v: rename %v: %v",
|
debug.Log("sftp.Save", "save %v: rename %v: %v",
|
||||||
h, path.Base(filename), err)
|
h, path.Base(filename), err)
|
||||||
if err != nil {
|
return err
|
||||||
return fmt.Errorf("sftp: renameFile: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat returns information about a blob.
|
// Stat returns information about a blob.
|
||||||
@ -415,7 +412,7 @@ func (r *SFTP) Stat(h backend.Handle) (backend.BlobInfo, error) {
|
|||||||
|
|
||||||
fi, err := r.c.Lstat(r.filename(h.Type, h.Name))
|
fi, err := r.c.Lstat(r.filename(h.Type, h.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.BlobInfo{}, err
|
return backend.BlobInfo{}, errors.Wrap(err, "Lstat")
|
||||||
}
|
}
|
||||||
|
|
||||||
return backend.BlobInfo{Size: fi.Size()}, nil
|
return backend.BlobInfo{Size: fi.Size()}, nil
|
||||||
@ -429,12 +426,12 @@ func (r *SFTP) Test(t backend.Type, name string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err := r.c.Lstat(r.filename(t, name))
|
_, err := r.c.Lstat(r.filename(t, name))
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, errors.Wrap(err, "Lstat")
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/backend/sftp"
|
"restic/backend/sftp"
|
||||||
"restic/backend/test"
|
"restic/backend/test"
|
||||||
@ -37,7 +39,7 @@ func init() {
|
|||||||
for _, dir := range strings.Split(TestSFTPPath, ":") {
|
for _, dir := range strings.Split(TestSFTPPath, ":") {
|
||||||
testpath := filepath.Join(dir, "sftp-server")
|
testpath := filepath.Join(dir, "sftp-server")
|
||||||
_, err := os.Stat(testpath)
|
_, err := os.Stat(testpath)
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(errors.Cause(err)) {
|
||||||
sftpserver = testpath
|
sftpserver = testpath
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
. "restic/test"
|
. "restic/test"
|
||||||
)
|
)
|
||||||
@ -223,7 +225,7 @@ func TestLoad(t testing.TB) {
|
|||||||
// if we requested data beyond the end of the file, require
|
// if we requested data beyond the end of the file, require
|
||||||
// ErrUnexpectedEOF error
|
// ErrUnexpectedEOF error
|
||||||
if l > len(d) {
|
if l > len(d) {
|
||||||
if err != io.ErrUnexpectedEOF {
|
if errors.Cause(err) != io.ErrUnexpectedEOF {
|
||||||
t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), int64(o))
|
t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), int64(o))
|
||||||
}
|
}
|
||||||
err = nil
|
err = nil
|
||||||
@ -270,7 +272,7 @@ func TestLoad(t testing.TB) {
|
|||||||
// if we requested data beyond the end of the file, require
|
// if we requested data beyond the end of the file, require
|
||||||
// ErrUnexpectedEOF error
|
// ErrUnexpectedEOF error
|
||||||
if l > len(d) {
|
if l > len(d) {
|
||||||
if err != io.ErrUnexpectedEOF {
|
if errors.Cause(err) != io.ErrUnexpectedEOF {
|
||||||
t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), int64(o))
|
t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), int64(o))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -303,7 +305,7 @@ func TestLoad(t testing.TB) {
|
|||||||
t.Errorf("wrong length for larger buffer returned, want %d, got %d", length, n)
|
t.Errorf("wrong length for larger buffer returned, want %d, got %d", length, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != io.ErrUnexpectedEOF {
|
if errors.Cause(err) != io.ErrUnexpectedEOF {
|
||||||
t.Errorf("wrong error returned for larger buffer: want io.ErrUnexpectedEOF, got %#v", err)
|
t.Errorf("wrong error returned for larger buffer: want io.ErrUnexpectedEOF, got %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +339,7 @@ func TestLoadNegativeOffset(t testing.TB) {
|
|||||||
// if we requested data beyond the end of the file, require
|
// if we requested data beyond the end of the file, require
|
||||||
// ErrUnexpectedEOF error
|
// ErrUnexpectedEOF error
|
||||||
if len(buf) > -o {
|
if len(buf) > -o {
|
||||||
if err != io.ErrUnexpectedEOF {
|
if errors.Cause(err) != io.ErrUnexpectedEOF {
|
||||||
t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), o)
|
t.Errorf("Load(%d, %d) did not return io.ErrUnexpectedEOF", len(buf), o)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package test_test
|
package test_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/backend/mem"
|
"restic/backend/mem"
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package backend
|
package backend
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
// LoadAll reads all data stored in the backend for the handle. The buffer buf
|
// LoadAll reads all data stored in the backend for the handle. The buffer buf
|
||||||
// is resized to accomodate all data in the blob. Errors returned by be.Load()
|
// is resized to accomodate all data in the blob. Errors returned by be.Load()
|
||||||
@ -9,7 +13,7 @@ import "io"
|
|||||||
func LoadAll(be Backend, h Handle, buf []byte) ([]byte, error) {
|
func LoadAll(be Backend, h Handle, buf []byte) ([]byte, error) {
|
||||||
fi, err := be.Stat(h)
|
fi, err := be.Stat(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
if fi.Size > int64(len(buf)) {
|
if fi.Size > int64(len(buf)) {
|
||||||
@ -17,7 +21,7 @@ func LoadAll(be Backend, h Handle, buf []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n, err := be.Load(h, buf, 0)
|
n, err := be.Load(h, buf, 0)
|
||||||
if err == io.ErrUnexpectedEOF {
|
if errors.Cause(err) == io.ErrUnexpectedEOF {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/fs"
|
"restic/fs"
|
||||||
@ -48,13 +48,13 @@ func (c *Cache) Has(t backend.Type, subtype string, id backend.ID) (bool, error)
|
|||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
debug.Log("Cache.Has", "test for file %v: not cached", filename)
|
debug.Log("Cache.Has", "test for file %v: not cached", filename)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("Cache.Has", "test for file %v: error %v", filename, err)
|
debug.Log("Cache.Has", "test for file %v: error %v", filename, err)
|
||||||
return false, err
|
return false, errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("Cache.Has", "test for file %v: is cached", filename)
|
debug.Log("Cache.Has", "test for file %v: is cached", filename)
|
||||||
@ -73,13 +73,13 @@ func (c *Cache) Store(t backend.Type, subtype string, id backend.ID) (io.WriteCl
|
|||||||
dirname := filepath.Dir(filename)
|
dirname := filepath.Dir(filename)
|
||||||
err = fs.MkdirAll(dirname, 0700)
|
err = fs.MkdirAll(dirname, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "MkdirAll")
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := fs.Create(filename)
|
file, err := fs.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Cache.Store", "error creating file %v: %v", filename, err)
|
debug.Log("Cache.Store", "error creating file %v: %v", filename, err)
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Create")
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("Cache.Store", "created file %v", filename)
|
debug.Log("Cache.Store", "created file %v", filename)
|
||||||
@ -106,11 +106,11 @@ func (c *Cache) purge(t backend.Type, subtype string, id backend.ID) error {
|
|||||||
err = fs.Remove(filename)
|
err = fs.Remove(filename)
|
||||||
debug.Log("Cache.purge", "Remove file %v: %v", filename, err)
|
debug.Log("Cache.purge", "Remove file %v: %v", filename, err)
|
||||||
|
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(errors.Cause(err)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return errors.Wrap(err, "Remove")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear removes information from the cache that isn't present in the repository any more.
|
// Clear removes information from the cache that isn't present in the repository any more.
|
||||||
@ -155,21 +155,21 @@ func (c *Cache) list(t backend.Type) ([]cacheEntry, error) {
|
|||||||
case backend.Snapshot:
|
case backend.Snapshot:
|
||||||
dir = filepath.Join(c.base, "snapshots")
|
dir = filepath.Join(c.base, "snapshots")
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("cache not supported for type %v", t)
|
return nil, errors.Errorf("cache not supported for type %v", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := fs.Open(dir)
|
fd, err := fs.Open(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
return []cacheEntry{}, nil
|
return []cacheEntry{}, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
|
|
||||||
fis, err := fd.Readdir(-1)
|
fis, err := fd.Readdir(-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Readdir")
|
||||||
}
|
}
|
||||||
|
|
||||||
entries := make([]cacheEntry, 0, len(fis))
|
entries := make([]cacheEntry, 0, len(fis))
|
||||||
@ -207,7 +207,7 @@ func (c *Cache) filename(t backend.Type, subtype string, id backend.ID) (string,
|
|||||||
return filepath.Join(c.base, "snapshots", filename), nil
|
return filepath.Join(c.base, "snapshots", filename), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("cache not supported for type %v", t)
|
return "", errors.Errorf("cache not supported for type %v", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCacheDir() (string, error) {
|
func getCacheDir() (string, error) {
|
||||||
@ -231,21 +231,21 @@ func getWindowsCacheDir() (string, error) {
|
|||||||
cachedir = filepath.Join(cachedir, "restic")
|
cachedir = filepath.Join(cachedir, "restic")
|
||||||
fi, err := fs.Stat(cachedir)
|
fi, err := fs.Stat(cachedir)
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
err = fs.MkdirAll(cachedir, 0700)
|
err = fs.MkdirAll(cachedir, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "MkdirAll")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cachedir, nil
|
return cachedir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fi.IsDir() {
|
if !fi.IsDir() {
|
||||||
return "", fmt.Errorf("cache dir %v is not a directory", cachedir)
|
return "", errors.Errorf("cache dir %v is not a directory", cachedir)
|
||||||
}
|
}
|
||||||
return cachedir, nil
|
return cachedir, nil
|
||||||
}
|
}
|
||||||
@ -268,10 +268,10 @@ func getXDGCacheDir() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fi, err := fs.Stat(cachedir)
|
fi, err := fs.Stat(cachedir)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
err = fs.MkdirAll(cachedir, 0700)
|
err = fs.MkdirAll(cachedir, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "MkdirAll")
|
||||||
}
|
}
|
||||||
|
|
||||||
fi, err = fs.Stat(cachedir)
|
fi, err = fs.Stat(cachedir)
|
||||||
@ -279,11 +279,11 @@ func getXDGCacheDir() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", errors.Wrap(err, "Stat")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fi.IsDir() {
|
if !fi.IsDir() {
|
||||||
return "", fmt.Errorf("cache dir %v is not a directory", cachedir)
|
return "", errors.Errorf("cache dir %v is not a directory", cachedir)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cachedir, nil
|
return cachedir, nil
|
||||||
|
@ -18,6 +18,9 @@ func TestCache(t *testing.T) {
|
|||||||
|
|
||||||
// archive some files, this should automatically cache all blobs from the snapshot
|
// archive some files, this should automatically cache all blobs from the snapshot
|
||||||
_, _, err = arch.Snapshot(nil, []string{BenchArchiveDirectory}, nil)
|
_, _, err = arch.Snapshot(nil, []string{BenchArchiveDirectory}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: test caching index
|
// TODO: test caching index
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,11 @@ package checker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
@ -84,7 +85,7 @@ func (c *Checker) LoadIndex() (hints []error, errs []error) {
|
|||||||
worker := func(id backend.ID, done <-chan struct{}) error {
|
worker := func(id backend.ID, done <-chan struct{}) error {
|
||||||
debug.Log("LoadIndex", "worker got index %v", id)
|
debug.Log("LoadIndex", "worker got index %v", id)
|
||||||
idx, err := repository.LoadIndexWithDecoder(c.repo, id, repository.DecodeIndex)
|
idx, err := repository.LoadIndexWithDecoder(c.repo, id, repository.DecodeIndex)
|
||||||
if err == repository.ErrOldIndexFormat {
|
if errors.Cause(err) == repository.ErrOldIndexFormat {
|
||||||
debug.Log("LoadIndex", "index %v has old format", id.Str())
|
debug.Log("LoadIndex", "index %v has old format", id.Str())
|
||||||
hints = append(hints, ErrOldIndexFormat{id})
|
hints = append(hints, ErrOldIndexFormat{id})
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ func (c *Checker) LoadIndex() (hints []error, errs []error) {
|
|||||||
debug.Log("LoadIndex", "process index %v", res.ID)
|
debug.Log("LoadIndex", "process index %v", res.ID)
|
||||||
idxID, err := backend.ParseID(res.ID)
|
idxID, err := backend.ParseID(res.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, fmt.Errorf("unable to parse as index ID: %v", res.ID))
|
errs = append(errs, errors.Errorf("unable to parse as index ID: %v", res.ID))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +282,7 @@ func loadTreeFromSnapshot(repo *repository.Repository, id backend.ID) (backend.I
|
|||||||
|
|
||||||
if sn.Tree == nil {
|
if sn.Tree == nil {
|
||||||
debug.Log("Checker.loadTreeFromSnapshot", "snapshot %v has no tree", id.Str())
|
debug.Log("Checker.loadTreeFromSnapshot", "snapshot %v has no tree", id.Str())
|
||||||
return backend.ID{}, fmt.Errorf("snapshot %v has no tree", id)
|
return backend.ID{}, errors.Errorf("snapshot %v has no tree", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return *sn.Tree, nil
|
return *sn.Tree, nil
|
||||||
@ -583,24 +584,24 @@ func (c *Checker) checkTree(id backend.ID, tree *restic.Tree) (errs []error) {
|
|||||||
switch node.Type {
|
switch node.Type {
|
||||||
case "file":
|
case "file":
|
||||||
if node.Content == nil {
|
if node.Content == nil {
|
||||||
errs = append(errs, Error{TreeID: id, Err: fmt.Errorf("file %q has nil blob list", node.Name)})
|
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("file %q has nil blob list", node.Name)})
|
||||||
}
|
}
|
||||||
|
|
||||||
for b, blobID := range node.Content {
|
for b, blobID := range node.Content {
|
||||||
if blobID.IsNull() {
|
if blobID.IsNull() {
|
||||||
errs = append(errs, Error{TreeID: id, Err: fmt.Errorf("file %q blob %d has null ID", node.Name, b)})
|
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("file %q blob %d has null ID", node.Name, b)})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
blobs = append(blobs, blobID)
|
blobs = append(blobs, blobID)
|
||||||
}
|
}
|
||||||
case "dir":
|
case "dir":
|
||||||
if node.Subtree == nil {
|
if node.Subtree == nil {
|
||||||
errs = append(errs, Error{TreeID: id, Err: fmt.Errorf("dir node %q has no subtree", node.Name)})
|
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("dir node %q has no subtree", node.Name)})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.Subtree.IsNull() {
|
if node.Subtree.IsNull() {
|
||||||
errs = append(errs, Error{TreeID: id, Err: fmt.Errorf("dir node %q subtree id is null", node.Name)})
|
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("dir node %q subtree id is null", node.Name)})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,7 +609,7 @@ func (c *Checker) checkTree(id backend.ID, tree *restic.Tree) (errs []error) {
|
|||||||
// nothing to check
|
// nothing to check
|
||||||
|
|
||||||
default:
|
default:
|
||||||
errs = append(errs, Error{TreeID: id, Err: fmt.Errorf("node %q with invalid type %q", node.Name, node.Type)})
|
errs = append(errs, Error{TreeID: id, Err: errors.Errorf("node %q with invalid type %q", node.Name, node.Type)})
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.Name == "" {
|
if node.Name == "" {
|
||||||
@ -670,7 +671,7 @@ func checkPack(r *repository.Repository, id backend.ID) error {
|
|||||||
hash := backend.Hash(buf)
|
hash := backend.Hash(buf)
|
||||||
if !hash.Equal(id) {
|
if !hash.Equal(id) {
|
||||||
debug.Log("Checker.checkPack", "Pack ID does not match, want %v, got %v", id.Str(), hash.Str())
|
debug.Log("Checker.checkPack", "Pack ID does not match, want %v, got %v", id.Str(), hash.Str())
|
||||||
return fmt.Errorf("Pack ID does not match, want %v, got %v", id.Str(), hash.Str())
|
return errors.Errorf("Pack ID does not match, want %v, got %v", id.Str(), hash.Str())
|
||||||
}
|
}
|
||||||
|
|
||||||
blobs, err := pack.List(r.Key(), bytes.NewReader(buf), int64(len(buf)))
|
blobs, err := pack.List(r.Key(), bytes.NewReader(buf), int64(len(buf)))
|
||||||
@ -686,20 +687,20 @@ func checkPack(r *repository.Repository, id backend.ID) error {
|
|||||||
plainBuf, err = crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length])
|
plainBuf, err = crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Checker.checkPack", " error decrypting blob %v: %v", blob.ID.Str(), err)
|
debug.Log("Checker.checkPack", " error decrypting blob %v: %v", blob.ID.Str(), err)
|
||||||
errs = append(errs, fmt.Errorf("blob %v: %v", i, err))
|
errs = append(errs, errors.Errorf("blob %v: %v", i, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := backend.Hash(plainBuf)
|
hash := backend.Hash(plainBuf)
|
||||||
if !hash.Equal(blob.ID) {
|
if !hash.Equal(blob.ID) {
|
||||||
debug.Log("Checker.checkPack", " Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str())
|
debug.Log("Checker.checkPack", " Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str())
|
||||||
errs = append(errs, fmt.Errorf("Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str()))
|
errs = append(errs, errors.Errorf("Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str()))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return fmt.Errorf("pack %v contains %v errors: %v", id.Str(), len(errs), errs)
|
return errors.Errorf("pack %v contains %v errors: %v", id.Str(), len(errs), errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -5,9 +5,10 @@ import (
|
|||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"golang.org/x/crypto/poly1305"
|
"golang.org/x/crypto/poly1305"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -167,7 +168,7 @@ func (m *MACKey) UnmarshalJSON(data []byte) error {
|
|||||||
j := jsonMACKey{}
|
j := jsonMACKey{}
|
||||||
err := json.Unmarshal(data, &j)
|
err := json.Unmarshal(data, &j)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Unmarshal")
|
||||||
}
|
}
|
||||||
copy(m.K[:], j.K)
|
copy(m.K[:], j.K)
|
||||||
copy(m.R[:], j.R)
|
copy(m.R[:], j.R)
|
||||||
@ -205,7 +206,7 @@ func (k *EncryptionKey) UnmarshalJSON(data []byte) error {
|
|||||||
d := make([]byte, aesKeySize)
|
d := make([]byte, aesKeySize)
|
||||||
err := json.Unmarshal(data, &d)
|
err := json.Unmarshal(data, &d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Unmarshal")
|
||||||
}
|
}
|
||||||
copy(k[:], d)
|
copy(k[:], d)
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ package crypto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sscrypt "github.com/elithrar/simple-scrypt"
|
sscrypt "github.com/elithrar/simple-scrypt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/scrypt"
|
"golang.org/x/crypto/scrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ func Calibrate(timeout time.Duration, memory int) (KDFParams, error) {
|
|||||||
|
|
||||||
params, err := sscrypt.Calibrate(timeout, memory, defaultParams)
|
params, err := sscrypt.Calibrate(timeout, memory, defaultParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DefaultKDFParams, err
|
return DefaultKDFParams, errors.Wrap(err, "scrypt.Calibrate")
|
||||||
}
|
}
|
||||||
|
|
||||||
return KDFParams{
|
return KDFParams{
|
||||||
@ -51,7 +51,7 @@ func Calibrate(timeout time.Duration, memory int) (KDFParams, error) {
|
|||||||
// using the supplied parameters N, R and P and the Salt.
|
// using the supplied parameters N, R and P and the Salt.
|
||||||
func KDF(p KDFParams, salt []byte, password string) (*Key, error) {
|
func KDF(p KDFParams, salt []byte, password string) (*Key, error) {
|
||||||
if len(salt) != saltLength {
|
if len(salt) != saltLength {
|
||||||
return nil, fmt.Errorf("scrypt() called with invalid salt bytes (len %d)", len(salt))
|
return nil, errors.Errorf("scrypt() called with invalid salt bytes (len %d)", len(salt))
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we have valid parameters
|
// make sure we have valid parameters
|
||||||
@ -64,7 +64,7 @@ func KDF(p KDFParams, salt []byte, password string) (*Key, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := params.Check(); err != nil {
|
if err := params.Check(); err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Check")
|
||||||
}
|
}
|
||||||
|
|
||||||
derKeys := &Key{}
|
derKeys := &Key{}
|
||||||
@ -72,11 +72,11 @@ func KDF(p KDFParams, salt []byte, password string) (*Key, error) {
|
|||||||
keybytes := macKeySize + aesKeySize
|
keybytes := macKeySize + aesKeySize
|
||||||
scryptKeys, err := scrypt.Key([]byte(password), salt, p.N, p.R, p.P, keybytes)
|
scryptKeys, err := scrypt.Key([]byte(password), salt, p.N, p.R, p.P, keybytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error deriving keys from password: %v", err)
|
return nil, errors.Wrap(err, "scrypt.Key")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(scryptKeys) != keybytes {
|
if len(scryptKeys) != keybytes {
|
||||||
return nil, fmt.Errorf("invalid numbers of bytes expanded from scrypt(): %d", len(scryptKeys))
|
return nil, errors.Errorf("invalid numbers of bytes expanded from scrypt(): %d", len(scryptKeys))
|
||||||
}
|
}
|
||||||
|
|
||||||
// first 32 byte of scrypt output is the encryption key
|
// first 32 byte of scrypt output is the encryption key
|
||||||
|
@ -14,6 +14,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type process struct {
|
type process struct {
|
||||||
@ -59,7 +61,7 @@ func initDebugLogger() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && os.IsNotExist(err) {
|
if err != nil && os.IsNotExist(errors.Cause(err)) {
|
||||||
f, err = fs.OpenFile(debugfile, os.O_WRONLY|os.O_CREATE, 0600)
|
f, err = fs.OpenFile(debugfile, os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
38
src/restic/errors.go
Normal file
38
src/restic/errors.go
Normal 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...))
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrBadString is returned when Match is called with the empty string as the
|
// ErrBadString is returned when Match is called with the empty string as the
|
||||||
@ -84,7 +85,7 @@ func match(patterns, strs []string) (matched bool, err error) {
|
|||||||
for i := len(patterns) - 1; i >= 0; i-- {
|
for i := len(patterns) - 1; i >= 0; i-- {
|
||||||
ok, err := filepath.Match(patterns[i], strs[offset+i])
|
ok, err := filepath.Match(patterns[i], strs[offset+i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, errors.Wrap(err, "Match")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -6,13 +6,15 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Open opens a file for reading, without updating the atime and without caching data on read.
|
// Open opens a file for reading, without updating the atime and without caching data on read.
|
||||||
func Open(name string) (File, error) {
|
func Open(name string) (File, error) {
|
||||||
file, err := os.OpenFile(name, os.O_RDONLY|syscall.O_NOATIME, 0)
|
file, err := os.OpenFile(name, os.O_RDONLY|syscall.O_NOATIME, 0)
|
||||||
if os.IsPermission(err) {
|
if os.IsPermission(errors.Cause(err)) {
|
||||||
file, err = os.OpenFile(name, os.O_RDONLY, 0)
|
file, err = os.OpenFile(name, os.O_RDONLY, 0)
|
||||||
}
|
}
|
||||||
return &nonCachingFile{File: file}, err
|
return &nonCachingFile{File: file}, err
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
package fuse
|
package fuse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
|
@ -5,11 +5,12 @@ package fuse
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
|
|
||||||
"restic"
|
"restic"
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
package fuse
|
package fuse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"restic"
|
||||||
|
"restic/repository"
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
"bazil.org/fuse/fs"
|
"bazil.org/fuse/fs"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"restic"
|
|
||||||
"restic/repository"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Statically ensure that *file implements the given interface
|
// Statically ensure that *file implements the given interface
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"restic"
|
"restic"
|
||||||
@ -12,6 +11,8 @@ import (
|
|||||||
"restic/pack"
|
"restic/pack"
|
||||||
"restic/types"
|
"restic/types"
|
||||||
"restic/worker"
|
"restic/worker"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pack contains information about the contents of a pack.
|
// Pack contains information about the contents of a pack.
|
||||||
@ -180,7 +181,7 @@ func Load(repo types.Repository, p *restic.Progress) (*Index, error) {
|
|||||||
// error is returned.
|
// error is returned.
|
||||||
func (idx *Index) AddPack(id backend.ID, size int64, entries []pack.Blob) error {
|
func (idx *Index) AddPack(id backend.ID, size int64, entries []pack.Blob) error {
|
||||||
if _, ok := idx.Packs[id]; ok {
|
if _, ok := idx.Packs[id]; ok {
|
||||||
return fmt.Errorf("pack %v already present in the index", id.Str())
|
return errors.Errorf("pack %v already present in the index", id.Str())
|
||||||
}
|
}
|
||||||
|
|
||||||
idx.Packs[id] = Pack{Size: size, Entries: entries}
|
idx.Packs[id] = Pack{Size: size, Entries: entries}
|
||||||
@ -203,7 +204,7 @@ func (idx *Index) AddPack(id backend.ID, size int64, entries []pack.Blob) error
|
|||||||
// RemovePack deletes a pack from the index.
|
// RemovePack deletes a pack from the index.
|
||||||
func (idx *Index) RemovePack(id backend.ID) error {
|
func (idx *Index) RemovePack(id backend.ID) error {
|
||||||
if _, ok := idx.Packs[id]; !ok {
|
if _, ok := idx.Packs[id]; !ok {
|
||||||
return fmt.Errorf("pack %v not found in the index", id.Str())
|
return errors.Errorf("pack %v not found in the index", id.Str())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, blob := range idx.Packs[id].Entries {
|
for _, blob := range idx.Packs[id].Entries {
|
||||||
@ -278,7 +279,7 @@ func (idx *Index) FindBlob(h pack.Handle) ([]Location, error) {
|
|||||||
for packID := range blob.Packs {
|
for packID := range blob.Packs {
|
||||||
pack, ok := idx.Packs[packID]
|
pack, ok := idx.Packs[packID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("pack %v not found in index", packID.Str())
|
return nil, errors.Errorf("pack %v not found in index", packID.Str())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range pack.Entries {
|
for _, entry := range pack.Entries {
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
@ -47,7 +49,7 @@ func (e ErrAlreadyLocked) Error() string {
|
|||||||
|
|
||||||
// IsAlreadyLocked returns true iff err is an instance of ErrAlreadyLocked.
|
// IsAlreadyLocked returns true iff err is an instance of ErrAlreadyLocked.
|
||||||
func IsAlreadyLocked(err error) bool {
|
func IsAlreadyLocked(err error) bool {
|
||||||
if _, ok := err.(ErrAlreadyLocked); ok {
|
if _, ok := errors.Cause(err).(ErrAlreadyLocked); ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +191,7 @@ var staleTimeout = 30 * time.Minute
|
|||||||
// process isn't alive any more.
|
// process isn't alive any more.
|
||||||
func (l *Lock) Stale() bool {
|
func (l *Lock) Stale() bool {
|
||||||
debug.Log("Lock.Stale", "testing if lock %v for process %d is stale", l, l.PID)
|
debug.Log("Lock.Stale", "testing if lock %v for process %d is stale", l, l.PID)
|
||||||
if time.Now().Sub(l.Time) > staleTimeout {
|
if time.Since(l.Time) > staleTimeout {
|
||||||
debug.Log("Lock.Stale", "lock is stale, timestamp is too old: %v\n", l.Time)
|
debug.Log("Lock.Stale", "lock is stale, timestamp is too old: %v\n", l.Time)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,11 +18,11 @@ func uidGidInt(u user.User) (uid, gid uint32, err error) {
|
|||||||
var ui, gi int64
|
var ui, gi int64
|
||||||
ui, err = strconv.ParseInt(u.Uid, 10, 32)
|
ui, err = strconv.ParseInt(u.Uid, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return uid, gid, errors.Wrap(err, "ParseInt")
|
||||||
}
|
}
|
||||||
gi, err = strconv.ParseInt(u.Gid, 10, 32)
|
gi, err = strconv.ParseInt(u.Gid, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return uid, gid, errors.Wrap(err, "ParseInt")
|
||||||
}
|
}
|
||||||
uid = uint32(ui)
|
uid = uint32(ui)
|
||||||
gid = uint32(gi)
|
gid = uint32(gi)
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
@ -17,8 +19,6 @@ import (
|
|||||||
"restic/fs"
|
"restic/fs"
|
||||||
"restic/pack"
|
"restic/pack"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node is a file, directory or other item in a backup.
|
// Node is a file, directory or other item in a backup.
|
||||||
@ -114,32 +114,32 @@ func (node *Node) CreateAt(path string, repo *repository.Repository) error {
|
|||||||
switch node.Type {
|
switch node.Type {
|
||||||
case "dir":
|
case "dir":
|
||||||
if err := node.createDirAt(path); err != nil {
|
if err := node.createDirAt(path); err != nil {
|
||||||
return errors.Annotate(err, "createDirAt")
|
return err
|
||||||
}
|
}
|
||||||
case "file":
|
case "file":
|
||||||
if err := node.createFileAt(path, repo); err != nil {
|
if err := node.createFileAt(path, repo); err != nil {
|
||||||
return errors.Annotate(err, "createFileAt")
|
return err
|
||||||
}
|
}
|
||||||
case "symlink":
|
case "symlink":
|
||||||
if err := node.createSymlinkAt(path); err != nil {
|
if err := node.createSymlinkAt(path); err != nil {
|
||||||
return errors.Annotate(err, "createSymlinkAt")
|
return err
|
||||||
}
|
}
|
||||||
case "dev":
|
case "dev":
|
||||||
if err := node.createDevAt(path); err != nil {
|
if err := node.createDevAt(path); err != nil {
|
||||||
return errors.Annotate(err, "createDevAt")
|
return err
|
||||||
}
|
}
|
||||||
case "chardev":
|
case "chardev":
|
||||||
if err := node.createCharDevAt(path); err != nil {
|
if err := node.createCharDevAt(path); err != nil {
|
||||||
return errors.Annotate(err, "createCharDevAt")
|
return err
|
||||||
}
|
}
|
||||||
case "fifo":
|
case "fifo":
|
||||||
if err := node.createFifoAt(path); err != nil {
|
if err := node.createFifoAt(path); err != nil {
|
||||||
return errors.Annotate(err, "createFifoAt")
|
return err
|
||||||
}
|
}
|
||||||
case "socket":
|
case "socket":
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("filetype %q not implemented!\n", node.Type)
|
return errors.Errorf("filetype %q not implemented!\n", node.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := node.restoreMetadata(path)
|
err := node.restoreMetadata(path)
|
||||||
@ -155,13 +155,13 @@ func (node Node) restoreMetadata(path string) error {
|
|||||||
|
|
||||||
err = lchown(path, int(node.UID), int(node.GID))
|
err = lchown(path, int(node.UID), int(node.GID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "Lchown")
|
return errors.Wrap(err, "Lchown")
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.Type != "symlink" {
|
if node.Type != "symlink" {
|
||||||
err = fs.Chmod(path, node.Mode)
|
err = fs.Chmod(path, node.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "Chmod")
|
return errors.Wrap(err, "Chmod")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,15 +183,11 @@ func (node Node) RestoreTimestamps(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node.Type == "symlink" {
|
if node.Type == "symlink" {
|
||||||
if err := node.restoreSymlinkTimestamps(path, utimes); err != nil {
|
return node.restoreSymlinkTimestamps(path, utimes)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := syscall.UtimesNano(path, utimes[:]); err != nil {
|
if err := syscall.UtimesNano(path, utimes[:]); err != nil {
|
||||||
return errors.Annotate(err, "UtimesNano")
|
return errors.Wrap(err, "UtimesNano")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -200,7 +196,7 @@ func (node Node) RestoreTimestamps(path string) error {
|
|||||||
func (node Node) createDirAt(path string) error {
|
func (node Node) createDirAt(path string) error {
|
||||||
err := fs.Mkdir(path, node.Mode)
|
err := fs.Mkdir(path, node.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "Mkdir")
|
return errors.Wrap(err, "Mkdir")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -211,7 +207,7 @@ func (node Node) createFileAt(path string, repo *repository.Repository) error {
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "OpenFile")
|
return errors.Wrap(err, "OpenFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf []byte
|
var buf []byte
|
||||||
@ -228,12 +224,12 @@ func (node Node) createFileAt(path string, repo *repository.Repository) error {
|
|||||||
|
|
||||||
buf, err := repo.LoadBlob(id, pack.Data, buf)
|
buf, err := repo.LoadBlob(id, pack.Data, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "Load")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = f.Write(buf)
|
_, err = f.Write(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "Write")
|
return errors.Wrap(err, "Write")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +243,7 @@ func (node Node) createSymlinkAt(path string) error {
|
|||||||
}
|
}
|
||||||
err := fs.Symlink(node.LinkTarget, path)
|
err := fs.Symlink(node.LinkTarget, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "Symlink")
|
return errors.Wrap(err, "Symlink")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -280,11 +276,11 @@ func (node *Node) UnmarshalJSON(data []byte) error {
|
|||||||
|
|
||||||
err := json.Unmarshal(data, nj)
|
err := json.Unmarshal(data, nj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Unmarshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
nj.Name, err = strconv.Unquote(`"` + nj.Name + `"`)
|
nj.Name, err = strconv.Unquote(`"` + nj.Name + `"`)
|
||||||
return err
|
return errors.Wrap(err, "Unquote")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node Node) Equals(other Node) bool {
|
func (node Node) Equals(other Node) bool {
|
||||||
@ -422,7 +418,7 @@ func (node *Node) fillUser(stat statT) error {
|
|||||||
|
|
||||||
username, err := lookupUsername(strconv.Itoa(int(stat.uid())))
|
username, err := lookupUsername(strconv.Itoa(int(stat.uid())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "fillUser")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
node.User = username
|
node.User = username
|
||||||
@ -470,7 +466,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if err = node.fillUser(stat); err != nil {
|
if err = node.fillUser(stat); err != nil {
|
||||||
return errors.Annotate(err, "fillExtra")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch node.Type {
|
switch node.Type {
|
||||||
@ -480,6 +476,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
|
|||||||
case "dir":
|
case "dir":
|
||||||
case "symlink":
|
case "symlink":
|
||||||
node.LinkTarget, err = fs.Readlink(path)
|
node.LinkTarget, err = fs.Readlink(path)
|
||||||
|
err = errors.Wrap(err, "Readlink")
|
||||||
case "dev":
|
case "dev":
|
||||||
node.Device = uint64(stat.rdev())
|
node.Device = uint64(stat.rdev())
|
||||||
case "chardev":
|
case "chardev":
|
||||||
@ -487,7 +484,7 @@ func (node *Node) fillExtra(path string, fi os.FileInfo) error {
|
|||||||
case "fifo":
|
case "fifo":
|
||||||
case "socket":
|
case "socket":
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("invalid node type %q", node.Type)
|
err = errors.Errorf("invalid node type %q", node.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -5,22 +5,22 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"restic/fs"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
"restic/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
||||||
dir, err := fs.Open(filepath.Dir(path))
|
dir, err := fs.Open(filepath.Dir(path))
|
||||||
defer dir.Close()
|
defer dir.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = utimesNanoAt(int(dir.Fd()), filepath.Base(path), utimes, AT_SYMLINK_NOFOLLOW)
|
err = utimesNanoAt(int(dir.Fd()), filepath.Base(path), utimes, AT_SYMLINK_NOFOLLOW)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Annotate(err, "UtimesNanoAt")
|
return errors.Wrap(err, "UtimesNanoAt")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mknod() creates a filesystem node (file, device
|
// mknod() creates a filesystem node (file, device
|
||||||
|
@ -3,11 +3,12 @@ package pack
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
)
|
)
|
||||||
@ -106,7 +107,7 @@ func (p *Packer) Add(t BlobType, id backend.ID, data []byte) (int, error) {
|
|||||||
p.bytes += uint(n)
|
p.bytes += uint(n)
|
||||||
p.blobs = append(p.blobs, c)
|
p.blobs = append(p.blobs, c)
|
||||||
|
|
||||||
return n, err
|
return n, errors.Wrap(err, "Write")
|
||||||
}
|
}
|
||||||
|
|
||||||
var entrySize = uint(binary.Size(BlobType(0)) + binary.Size(uint32(0)) + backend.IDSize)
|
var entrySize = uint(binary.Size(BlobType(0)) + binary.Size(uint32(0)) + backend.IDSize)
|
||||||
@ -141,7 +142,7 @@ func (p *Packer) Finalize() (uint, error) {
|
|||||||
// append the header
|
// append the header
|
||||||
n, err := p.wr.Write(encryptedHeader)
|
n, err := p.wr.Write(encryptedHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "Write")
|
||||||
}
|
}
|
||||||
|
|
||||||
hdrBytes := bytesHeader + crypto.Extension
|
hdrBytes := bytesHeader + crypto.Extension
|
||||||
@ -154,7 +155,7 @@ func (p *Packer) Finalize() (uint, error) {
|
|||||||
// write length
|
// write length
|
||||||
err = binary.Write(p.wr, binary.LittleEndian, uint32(uint(len(p.blobs))*entrySize+crypto.Extension))
|
err = binary.Write(p.wr, binary.LittleEndian, uint32(uint(len(p.blobs))*entrySize+crypto.Extension))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "binary.Write")
|
||||||
}
|
}
|
||||||
bytesWritten += uint(binary.Size(uint32(0)))
|
bytesWritten += uint(binary.Size(uint32(0)))
|
||||||
|
|
||||||
@ -181,12 +182,12 @@ func (p *Packer) writeHeader(wr io.Writer) (bytesWritten uint, err error) {
|
|||||||
case Tree:
|
case Tree:
|
||||||
entry.Type = 1
|
entry.Type = 1
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("invalid blob type %v", b.Type)
|
return 0, errors.Errorf("invalid blob type %v", b.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := binary.Write(wr, binary.LittleEndian, entry)
|
err := binary.Write(wr, binary.LittleEndian, entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bytesWritten, err
|
return bytesWritten, errors.Wrap(err, "binary.Write")
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesWritten += entrySize
|
bytesWritten += entrySize
|
||||||
@ -236,7 +237,7 @@ func readHeaderLength(rd io.ReaderAt, size int64) (uint32, error) {
|
|||||||
buf := make([]byte, binary.Size(uint32(0)))
|
buf := make([]byte, binary.Size(uint32(0)))
|
||||||
n, err := rd.ReadAt(buf, off)
|
n, err := rd.ReadAt(buf, off)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, errors.Wrap(err, "ReadAt")
|
||||||
}
|
}
|
||||||
|
|
||||||
if n != len(buf) {
|
if n != len(buf) {
|
||||||
@ -267,7 +268,7 @@ func readHeader(rd io.ReaderAt, size int64) ([]byte, error) {
|
|||||||
buf := make([]byte, int(hl))
|
buf := make([]byte, int(hl))
|
||||||
n, err := rd.ReadAt(buf, size-int64(hl)-int64(binary.Size(hl)))
|
n, err := rd.ReadAt(buf, size-int64(hl)-int64(binary.Size(hl)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "ReadAt")
|
||||||
}
|
}
|
||||||
|
|
||||||
if n != len(buf) {
|
if n != len(buf) {
|
||||||
@ -295,12 +296,12 @@ func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []Blob, err error)
|
|||||||
for {
|
for {
|
||||||
e := headerEntry{}
|
e := headerEntry{}
|
||||||
err = binary.Read(hdrRd, binary.LittleEndian, &e)
|
err = binary.Read(hdrRd, binary.LittleEndian, &e)
|
||||||
if err == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "binary.Read")
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := Blob{
|
entry := Blob{
|
||||||
@ -315,7 +316,7 @@ func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []Blob, err error)
|
|||||||
case 1:
|
case 1:
|
||||||
entry.Type = Tree
|
entry.Type = Tree
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid type %d", e.Type)
|
return nil, errors.Errorf("invalid type %d", e.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
entries = append(entries, entry)
|
entries = append(entries, entry)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package pipe
|
package pipe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/fs"
|
"restic/fs"
|
||||||
)
|
)
|
||||||
@ -62,12 +63,12 @@ func (e Dir) Result() chan<- Result { return e.result }
|
|||||||
func readDirNames(dirname string) ([]string, error) {
|
func readDirNames(dirname string) ([]string, error) {
|
||||||
f, err := fs.Open(dirname)
|
f, err := fs.Open(dirname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
names, err := f.Readdirnames(-1)
|
names, err := f.Readdirnames(-1)
|
||||||
f.Close()
|
f.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Readdirnames")
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
return names, nil
|
return names, nil
|
||||||
@ -93,6 +94,7 @@ func walk(basedir, dir string, selectFunc SelectFunc, done <-chan struct{}, jobs
|
|||||||
|
|
||||||
info, err := fs.Lstat(dir)
|
info, err := fs.Lstat(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "Lstat")
|
||||||
debug.Log("pipe.walk", "error for %v: %v, res %p", dir, err, res)
|
debug.Log("pipe.walk", "error for %v: %v, res %p", dir, err, res)
|
||||||
select {
|
select {
|
||||||
case jobs <- Dir{basedir: basedir, path: relpath, info: info, error: err, result: res}:
|
case jobs <- Dir{basedir: basedir, path: relpath, info: info, error: err, result: res}:
|
||||||
@ -146,6 +148,7 @@ func walk(basedir, dir string, selectFunc SelectFunc, done <-chan struct{}, jobs
|
|||||||
entries = append(entries, ch)
|
entries = append(entries, ch)
|
||||||
|
|
||||||
if statErr != nil {
|
if statErr != nil {
|
||||||
|
statErr = errors.Wrap(statErr, "Lstat")
|
||||||
debug.Log("pipe.walk", "sending file job for %v, err %v, res %p", subpath, err, res)
|
debug.Log("pipe.walk", "sending file job for %v, err %v, res %p", subpath, err, res)
|
||||||
select {
|
select {
|
||||||
case jobs <- Entry{info: fi, error: statErr, basedir: basedir, path: filepath.Join(relpath, name), result: ch}:
|
case jobs <- Entry{info: fi, error: statErr, basedir: basedir, path: filepath.Join(relpath, name), result: ch}:
|
||||||
|
@ -2,10 +2,11 @@ package restic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const minTickerTime = time.Second / 60
|
const minTickerTime = time.Second / 60
|
||||||
|
@ -4,10 +4,11 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
|
|
||||||
@ -48,13 +49,13 @@ func CreateConfig() (Config, error) {
|
|||||||
|
|
||||||
cfg.ChunkerPolynomial, err = chunker.RandomPolynomial()
|
cfg.ChunkerPolynomial, err = chunker.RandomPolynomial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Config{}, err
|
return Config{}, errors.Wrap(err, "chunker.RandomPolynomial")
|
||||||
}
|
}
|
||||||
|
|
||||||
newID := make([]byte, repositoryIDSize)
|
newID := make([]byte, repositoryIDSize)
|
||||||
_, err = io.ReadFull(rand.Reader, newID)
|
_, err = io.ReadFull(rand.Reader, newID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Config{}, err
|
return Config{}, errors.Wrap(err, "io.ReadFull")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.ID = hex.EncodeToString(newID)
|
cfg.ID = hex.EncodeToString(newID)
|
||||||
|
@ -3,12 +3,13 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
@ -139,7 +140,7 @@ func (idx *Index) Lookup(id backend.ID, tpe pack.BlobType) (blobs []PackedBlob,
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("Index.Lookup", "id %v not found", id.Str())
|
debug.Log("Index.Lookup", "id %v not found", id.Str())
|
||||||
return nil, fmt.Errorf("id %v not found in index", id)
|
return nil, errors.Errorf("id %v not found in index", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListPack returns a list of blobs contained in a pack.
|
// ListPack returns a list of blobs contained in a pack.
|
||||||
@ -326,7 +327,7 @@ func (idx *Index) generatePackList() ([]*packJSON, error) {
|
|||||||
if blob.packID.IsNull() {
|
if blob.packID.IsNull() {
|
||||||
debug.Log("Index.generatePackList", "blob %v has no packID! (offset %v, length %v)",
|
debug.Log("Index.generatePackList", "blob %v has no packID! (offset %v, length %v)",
|
||||||
h, blob.offset, blob.length)
|
h, blob.offset, blob.length)
|
||||||
return nil, fmt.Errorf("unable to serialize index: pack for blob %v hasn't been written yet", h)
|
return nil, errors.Errorf("unable to serialize index: pack for blob %v hasn't been written yet", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if pack is already in map
|
// see if pack is already in map
|
||||||
@ -455,7 +456,7 @@ func (idx *Index) Dump(w io.Writer) error {
|
|||||||
|
|
||||||
_, err = w.Write(append(buf, '\n'))
|
_, err = w.Write(append(buf, '\n'))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Write")
|
||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("Index.Dump", "done")
|
debug.Log("Index.Dump", "done")
|
||||||
@ -491,7 +492,7 @@ func DecodeIndex(rd io.Reader) (idx *Index, err error) {
|
|||||||
err = ErrOldIndexFormat
|
err = ErrOldIndexFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Decode")
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = NewIndex()
|
idx = NewIndex()
|
||||||
@ -510,7 +511,7 @@ func DecodeIndex(rd io.Reader) (idx *Index, err error) {
|
|||||||
idx.final = true
|
idx.final = true
|
||||||
|
|
||||||
debug.Log("Index.DecodeIndex", "done")
|
debug.Log("Index.DecodeIndex", "done")
|
||||||
return idx, err
|
return idx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeOldIndex loads and unserializes an index in the old format from rd.
|
// DecodeOldIndex loads and unserializes an index in the old format from rd.
|
||||||
@ -522,7 +523,7 @@ func DecodeOldIndex(rd io.Reader) (idx *Index, err error) {
|
|||||||
err = dec.Decode(&list)
|
err = dec.Decode(&list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Index.DecodeOldIndex", "Error %#v", err)
|
debug.Log("Index.DecodeOldIndex", "Error %#v", err)
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Decode")
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = NewIndex()
|
idx = NewIndex()
|
||||||
@ -540,7 +541,7 @@ func DecodeOldIndex(rd io.Reader) (idx *Index, err error) {
|
|||||||
idx.final = true
|
idx.final = true
|
||||||
|
|
||||||
debug.Log("Index.DecodeOldIndex", "done")
|
debug.Log("Index.DecodeOldIndex", "done")
|
||||||
return idx, err
|
return idx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadIndexWithDecoder loads the index and decodes it with fn.
|
// LoadIndexWithDecoder loads the index and decodes it with fn.
|
||||||
|
@ -2,12 +2,13 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
@ -79,7 +80,7 @@ func OpenKey(s *Repository, name string, password string) (*Key, error) {
|
|||||||
}
|
}
|
||||||
k.user, err = crypto.KDF(params, k.Salt, password)
|
k.user, err = crypto.KDF(params, k.Salt, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "crypto.KDF")
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt master keys
|
// decrypt master keys
|
||||||
@ -93,7 +94,7 @@ func OpenKey(s *Repository, name string, password string) (*Key, error) {
|
|||||||
err = json.Unmarshal(buf, k.master)
|
err = json.Unmarshal(buf, k.master)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("OpenKey", "Unmarshal() returned error %v", err)
|
debug.Log("OpenKey", "Unmarshal() returned error %v", err)
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Unmarshal")
|
||||||
}
|
}
|
||||||
k.name = name
|
k.name = name
|
||||||
|
|
||||||
@ -125,7 +126,7 @@ func SearchKey(s *Repository, password string, maxKeys int) (*Key, error) {
|
|||||||
debug.Log("SearchKey", "key %v returned error %v", name[:12], err)
|
debug.Log("SearchKey", "key %v returned error %v", name[:12], err)
|
||||||
|
|
||||||
// ErrUnauthenticated means the password is wrong, try the next key
|
// ErrUnauthenticated means the password is wrong, try the next key
|
||||||
if err == crypto.ErrUnauthenticated {
|
if errors.Cause(err) == crypto.ErrUnauthenticated {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ func LoadKey(s *Repository, name string) (k *Key, err error) {
|
|||||||
k = &Key{}
|
k = &Key{}
|
||||||
err = json.Unmarshal(data, k)
|
err = json.Unmarshal(data, k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Unmarshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
return k, nil
|
return k, nil
|
||||||
@ -162,7 +163,7 @@ func AddKey(s *Repository, password string, template *crypto.Key) (*Key, error)
|
|||||||
if KDFParams == nil {
|
if KDFParams == nil {
|
||||||
p, err := crypto.Calibrate(KDFTimeout, KDFMemory)
|
p, err := crypto.Calibrate(KDFTimeout, KDFMemory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Calibrate")
|
||||||
}
|
}
|
||||||
|
|
||||||
KDFParams = &p
|
KDFParams = &p
|
||||||
@ -211,7 +212,7 @@ func AddKey(s *Repository, password string, template *crypto.Key) (*Key, error)
|
|||||||
// encrypt master keys (as json) with user key
|
// encrypt master keys (as json) with user key
|
||||||
buf, err := json.Marshal(newkey.master)
|
buf, err := json.Marshal(newkey.master)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Marshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
newkey.Data, err = crypto.Encrypt(newkey.user, nil, buf)
|
newkey.Data, err = crypto.Encrypt(newkey.user, nil, buf)
|
||||||
@ -219,7 +220,7 @@ func AddKey(s *Repository, password string, template *crypto.Key) (*Key, error)
|
|||||||
// dump as json
|
// dump as json
|
||||||
buf, err = json.Marshal(newkey)
|
buf, err = json.Marshal(newkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "Marshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
// store in repository and return
|
// store in repository and return
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/pack"
|
"restic/pack"
|
||||||
@ -37,7 +38,7 @@ func (mi *MasterIndex) Lookup(id backend.ID, tpe pack.BlobType) (blobs []PackedB
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug.Log("MasterIndex.Lookup", "id %v not found in any index", id.Str())
|
debug.Log("MasterIndex.Lookup", "id %v not found in any index", id.Str())
|
||||||
return nil, fmt.Errorf("id %v not found in any index", id)
|
return nil, errors.Errorf("id %v not found in any index", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupSize queries all known Indexes for the ID and returns the first match.
|
// LookupSize queries all known Indexes for the ID and returns the first match.
|
||||||
@ -52,7 +53,7 @@ func (mi *MasterIndex) LookupSize(id backend.ID, tpe pack.BlobType) (uint, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, fmt.Errorf("id %v not found in any index", id)
|
return 0, errors.Errorf("id %v not found in any index", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListPack returns the list of blobs in a pack. The first matching index is
|
// ListPack returns the list of blobs in a pack. The first matching index is
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
@ -70,7 +71,7 @@ func (r *packerManager) findPacker(size uint) (packer *pack.Packer, err error) {
|
|||||||
debug.Log("Repo.findPacker", "create new pack for %d bytes", size)
|
debug.Log("Repo.findPacker", "create new pack for %d bytes", size)
|
||||||
tmpfile, err := ioutil.TempFile("", "restic-temp-pack-")
|
tmpfile, err := ioutil.TempFile("", "restic-temp-pack-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, errors.Wrap(err, "ioutil.TempFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
return pack.NewPacker(r.key, tmpfile), nil
|
return pack.NewPacker(r.key, tmpfile), nil
|
||||||
@ -96,18 +97,21 @@ func (r *Repository) savePacker(p *pack.Packer) error {
|
|||||||
tmpfile := p.Writer().(*os.File)
|
tmpfile := p.Writer().(*os.File)
|
||||||
f, err := fs.Open(tmpfile.Name())
|
f, err := fs.Open(tmpfile.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Open")
|
||||||
}
|
}
|
||||||
|
|
||||||
data := make([]byte, n)
|
data := make([]byte, n)
|
||||||
m, err := io.ReadFull(f, data)
|
m, err := io.ReadFull(f, data)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "ReadFul")
|
||||||
|
}
|
||||||
|
|
||||||
if uint(m) != n {
|
if uint(m) != n {
|
||||||
return fmt.Errorf("read wrong number of bytes from %v: want %v, got %v", tmpfile.Name(), n, m)
|
return errors.Errorf("read wrong number of bytes from %v: want %v, got %v", tmpfile.Name(), n, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = f.Close(); err != nil {
|
if err = f.Close(); err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Close")
|
||||||
}
|
}
|
||||||
|
|
||||||
id := backend.Hash(data)
|
id := backend.Hash(data)
|
||||||
@ -123,7 +127,7 @@ func (r *Repository) savePacker(p *pack.Packer) error {
|
|||||||
|
|
||||||
err = fs.Remove(tmpfile.Name())
|
err = fs.Remove(tmpfile.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "Remove")
|
||||||
}
|
}
|
||||||
|
|
||||||
// update blobs in the index
|
// update blobs in the index
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package repository_test
|
package repository_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
. "restic/test"
|
. "restic/test"
|
||||||
|
@ -3,6 +3,8 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandReader allows reading from a rand.Rand.
|
// RandReader allows reading from a rand.Rand.
|
||||||
@ -56,7 +58,7 @@ func (rd *RandReader) Read(p []byte) (int, error) {
|
|||||||
n, err := rd.read(p[:l])
|
n, err := rd.read(p[:l])
|
||||||
pos += n
|
pos += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pos, err
|
return pos, errors.Wrap(err, "Read")
|
||||||
}
|
}
|
||||||
p = p[n:]
|
p = p[n:]
|
||||||
|
|
||||||
@ -64,7 +66,7 @@ func (rd *RandReader) Read(p []byte) (int, error) {
|
|||||||
rd.buf = rd.buf[:7]
|
rd.buf = rd.buf[:7]
|
||||||
n, err = rd.read(rd.buf)
|
n, err = rd.read(rd.buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pos, err
|
return pos, errors.Wrap(err, "Read")
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the remaining bytes from the buffer to p
|
// copy the remaining bytes from the buffer to p
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/pack"
|
"restic/pack"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repack takes a list of packs together with a list of blobs contained in
|
// Repack takes a list of packs together with a list of blobs contained in
|
||||||
@ -22,7 +24,7 @@ func Repack(repo *Repository, packs backend.IDSet, keepBlobs pack.BlobSet) (err
|
|||||||
h := backend.Handle{Type: backend.Data, Name: packID.String()}
|
h := backend.Handle{Type: backend.Data, Name: packID.String()}
|
||||||
|
|
||||||
l, err := repo.Backend().Load(h, buf[:cap(buf)], 0)
|
l, err := repo.Backend().Load(h, buf[:cap(buf)], 0)
|
||||||
if err == io.ErrUnexpectedEOF {
|
if errors.Cause(err) == io.ErrUnexpectedEOF {
|
||||||
err = nil
|
err = nil
|
||||||
buf = buf[:l]
|
buf = buf[:l]
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,12 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/crypto"
|
"restic/crypto"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
@ -101,6 +102,7 @@ func (r *Repository) LoadBlob(id backend.ID, t pack.BlobType, plaintextBuf []byt
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var lastError error
|
||||||
for _, blob := range blobs {
|
for _, blob := range blobs {
|
||||||
debug.Log("Repo.LoadBlob", "id %v found: %v", id.Str(), blob)
|
debug.Log("Repo.LoadBlob", "id %v found: %v", id.Str(), blob)
|
||||||
|
|
||||||
@ -114,33 +116,38 @@ func (r *Repository) LoadBlob(id backend.ID, t pack.BlobType, plaintextBuf []byt
|
|||||||
n, err := r.be.Load(h, ciphertextBuf, int64(blob.Offset))
|
n, err := r.be.Load(h, ciphertextBuf, int64(blob.Offset))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Repo.LoadBlob", "error loading blob %v: %v", blob, err)
|
debug.Log("Repo.LoadBlob", "error loading blob %v: %v", blob, err)
|
||||||
fmt.Fprintf(os.Stderr, "error loading blob %v: %v", id, err)
|
lastError = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if uint(n) != blob.Length {
|
if uint(n) != blob.Length {
|
||||||
debug.Log("Repo.LoadBlob", "error loading blob %v: wrong length returned, want %d, got %d",
|
lastError = errors.Errorf("error loading blob %v: wrong length returned, want %d, got %d",
|
||||||
blob.Length, uint(n))
|
id.Str(), blob.Length, uint(n))
|
||||||
|
debug.Log("Repo.LoadBlob", "lastError: %v", lastError)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt
|
// decrypt
|
||||||
plaintextBuf, err = r.decryptTo(plaintextBuf, ciphertextBuf)
|
plaintextBuf, err = r.decryptTo(plaintextBuf, ciphertextBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "decrypting blob %v failed: %v", id, err)
|
lastError = errors.Errorf("decrypting blob %v failed: %v", id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// check hash
|
// check hash
|
||||||
if !backend.Hash(plaintextBuf).Equal(id) {
|
if !backend.Hash(plaintextBuf).Equal(id) {
|
||||||
fmt.Fprintf(os.Stderr, "blob %v returned invalid hash", id)
|
lastError = errors.Errorf("blob %v returned invalid hash", id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return plaintextBuf, nil
|
return plaintextBuf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("loading blob %v from %v packs failed", id.Str(), len(blobs))
|
if lastError != nil {
|
||||||
|
return nil, lastError
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.Errorf("loading blob %v from %v packs failed", id.Str(), len(blobs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// closeOrErr calls cl.Close() and sets err to the returned error value if
|
// closeOrErr calls cl.Close() and sets err to the returned error value if
|
||||||
@ -237,7 +244,7 @@ func (r *Repository) SaveJSON(t pack.BlobType, item interface{}) (backend.ID, er
|
|||||||
enc := json.NewEncoder(wr)
|
enc := json.NewEncoder(wr)
|
||||||
err := enc.Encode(item)
|
err := enc.Encode(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.ID{}, fmt.Errorf("json.Encode: %v", err)
|
return backend.ID{}, errors.Errorf("json.Encode: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = wr.Bytes()
|
buf = wr.Bytes()
|
||||||
@ -250,7 +257,7 @@ func (r *Repository) SaveJSONUnpacked(t backend.Type, item interface{}) (backend
|
|||||||
debug.Log("Repo.SaveJSONUnpacked", "save new blob %v", t)
|
debug.Log("Repo.SaveJSONUnpacked", "save new blob %v", t)
|
||||||
plaintext, err := json.Marshal(item)
|
plaintext, err := json.Marshal(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.ID{}, fmt.Errorf("json.Encode: %v", err)
|
return backend.ID{}, errors.Wrap(err, "json.Marshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.SaveUnpacked(t, plaintext)
|
return r.SaveUnpacked(t, plaintext)
|
||||||
@ -396,7 +403,7 @@ func LoadIndex(repo *Repository, id backend.ID) (*Index, error) {
|
|||||||
return idx, nil
|
return idx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == ErrOldIndexFormat {
|
if errors.Cause(err) == ErrOldIndexFormat {
|
||||||
fmt.Fprintf(os.Stderr, "index %v has old format\n", id.Str())
|
fmt.Fprintf(os.Stderr, "index %v has old format\n", id.Str())
|
||||||
return LoadIndexWithDecoder(repo, id, DecodeOldIndex)
|
return LoadIndexWithDecoder(repo, id, DecodeOldIndex)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/fs"
|
"restic/fs"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Restorer is used to restore a snapshot to a directory.
|
// Restorer is used to restore a snapshot to a directory.
|
||||||
@ -35,7 +34,7 @@ func NewRestorer(repo *repository.Repository, id backend.ID) (*Restorer, error)
|
|||||||
|
|
||||||
r.sn, err = LoadSnapshot(repo, id)
|
r.sn, err = LoadSnapshot(repo, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Annotate(err, "load snapshot for restorer")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
@ -44,7 +43,7 @@ func NewRestorer(repo *repository.Repository, id backend.ID) (*Restorer, error)
|
|||||||
func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error {
|
func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error {
|
||||||
tree, err := LoadTree(res.repo, treeID)
|
tree, err := LoadTree(res.repo, treeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res.Error(dir, nil, errors.Annotate(err, "LoadTree"))
|
return res.Error(dir, nil, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, node := range tree.Nodes {
|
for _, node := range tree.Nodes {
|
||||||
@ -61,13 +60,13 @@ func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error
|
|||||||
|
|
||||||
if node.Type == "dir" {
|
if node.Type == "dir" {
|
||||||
if node.Subtree == nil {
|
if node.Subtree == nil {
|
||||||
return fmt.Errorf("Dir without subtree in tree %v", treeID.Str())
|
return errors.Errorf("Dir without subtree in tree %v", treeID.Str())
|
||||||
}
|
}
|
||||||
|
|
||||||
subp := filepath.Join(dir, node.Name)
|
subp := filepath.Join(dir, node.Name)
|
||||||
err = res.restoreTo(dst, subp, *node.Subtree)
|
err = res.restoreTo(dst, subp, *node.Subtree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = res.Error(subp, node, errors.Annotate(err, "restore subtree"))
|
err = res.Error(subp, node, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -101,14 +100,14 @@ func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error {
|
|||||||
|
|
||||||
// Create parent directories and retry
|
// Create parent directories and retry
|
||||||
err = fs.MkdirAll(filepath.Dir(dstPath), 0700)
|
err = fs.MkdirAll(filepath.Dir(dstPath), 0700)
|
||||||
if err == nil || err == os.ErrExist {
|
if err == nil || os.IsExist(errors.Cause(err)) {
|
||||||
err = node.CreateAt(dstPath, res.repo)
|
err = node.CreateAt(dstPath, res.repo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Log("Restorer.restoreNodeTo", "error %v", err)
|
debug.Log("Restorer.restoreNodeTo", "error %v", err)
|
||||||
err = res.Error(dstPath, node, errors.Annotate(err, "create node"))
|
err = res.Error(dstPath, node, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
)
|
)
|
||||||
@ -140,7 +141,7 @@ func FindLatestSnapshot(repo *repository.Repository, targets []string, source st
|
|||||||
for snapshotID := range repo.List(backend.Snapshot, make(chan struct{})) {
|
for snapshotID := range repo.List(backend.Snapshot, make(chan struct{})) {
|
||||||
snapshot, err := LoadSnapshot(repo, snapshotID)
|
snapshot, err := LoadSnapshot(repo, snapshotID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return backend.ID{}, fmt.Errorf("Error listing snapshot: %v", err)
|
return backend.ID{}, errors.Errorf("Error listing snapshot: %v", err)
|
||||||
}
|
}
|
||||||
if snapshot.Time.After(latest) && SamePaths(snapshot.Paths, targets) && (source == "" || source == snapshot.Hostname) {
|
if snapshot.Time.After(latest) && SamePaths(snapshot.Paths, targets) && (source == "" || source == snapshot.Hostname) {
|
||||||
latest = snapshot.Time
|
latest = snapshot.Time
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/restic/chunker"
|
"github.com/restic/chunker"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ func (fs fakeFileSystem) saveFile(rd io.Reader) (blobs backend.IDs) {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
chunk, err := ch.Next(getBuf())
|
chunk, err := ch.Next(getBuf())
|
||||||
if err == io.EOF {
|
if errors.Cause(err) == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
"restic/pack"
|
"restic/pack"
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package worker_test
|
package worker_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"restic/worker"
|
"restic/worker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
6
vendor/manifest
vendored
6
vendor/manifest
vendored
@ -19,12 +19,6 @@
|
|||||||
"revision": "1b89bf73cd2c3a911d7b2a279ab085c4a18cf539",
|
"revision": "1b89bf73cd2c3a911d7b2a279ab085c4a18cf539",
|
||||||
"branch": "HEAD"
|
"branch": "HEAD"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"importpath": "github.com/juju/errors",
|
|
||||||
"repository": "https://github.com/juju/errors",
|
|
||||||
"revision": "4567a5e69fd3130ca0d89f69478e7ac025b67452",
|
|
||||||
"branch": "HEAD"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"importpath": "github.com/kr/fs",
|
"importpath": "github.com/kr/fs",
|
||||||
"repository": "https://github.com/kr/fs",
|
"repository": "https://github.com/kr/fs",
|
||||||
|
191
vendor/src/github.com/juju/errors/LICENSE
vendored
191
vendor/src/github.com/juju/errors/LICENSE
vendored
@ -1,191 +0,0 @@
|
|||||||
All files in this repository are licensed as follows. If you contribute
|
|
||||||
to this repository, it is assumed that you license your contribution
|
|
||||||
under the same license unless you state otherwise.
|
|
||||||
|
|
||||||
All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file.
|
|
||||||
|
|
||||||
This software is licensed under the LGPLv3, included below.
|
|
||||||
|
|
||||||
As a special exception to the GNU Lesser General Public License version 3
|
|
||||||
("LGPL3"), the copyright holders of this Library give you permission to
|
|
||||||
convey to a third party a Combined Work that links statically or dynamically
|
|
||||||
to this Library without providing any Minimal Corresponding Source or
|
|
||||||
Minimal Application Code as set out in 4d or providing the installation
|
|
||||||
information set out in section 4e, provided that you comply with the other
|
|
||||||
provisions of LGPL3 and provided that you meet, for the Application the
|
|
||||||
terms and conditions of the license(s) which apply to the Application.
|
|
||||||
|
|
||||||
Except as stated in this special exception, the provisions of LGPL3 will
|
|
||||||
continue to comply in full to this Library. If you modify this Library, you
|
|
||||||
may apply this exception to your version of this Library, but you are not
|
|
||||||
obliged to do so. If you do not wish to do so, delete this exception
|
|
||||||
statement from your version. This exception does not (and cannot) modify any
|
|
||||||
license terms which apply to the Application, with which you must still
|
|
||||||
comply.
|
|
||||||
|
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
|
||||||
This version of the GNU Lesser General Public License incorporates
|
|
||||||
the terms and conditions of version 3 of the GNU General Public
|
|
||||||
License, supplemented by the additional permissions listed below.
|
|
||||||
|
|
||||||
0. Additional Definitions.
|
|
||||||
|
|
||||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
|
||||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
|
||||||
General Public License.
|
|
||||||
|
|
||||||
"The Library" refers to a covered work governed by this License,
|
|
||||||
other than an Application or a Combined Work as defined below.
|
|
||||||
|
|
||||||
An "Application" is any work that makes use of an interface provided
|
|
||||||
by the Library, but which is not otherwise based on the Library.
|
|
||||||
Defining a subclass of a class defined by the Library is deemed a mode
|
|
||||||
of using an interface provided by the Library.
|
|
||||||
|
|
||||||
A "Combined Work" is a work produced by combining or linking an
|
|
||||||
Application with the Library. The particular version of the Library
|
|
||||||
with which the Combined Work was made is also called the "Linked
|
|
||||||
Version".
|
|
||||||
|
|
||||||
The "Minimal Corresponding Source" for a Combined Work means the
|
|
||||||
Corresponding Source for the Combined Work, excluding any source code
|
|
||||||
for portions of the Combined Work that, considered in isolation, are
|
|
||||||
based on the Application, and not on the Linked Version.
|
|
||||||
|
|
||||||
The "Corresponding Application Code" for a Combined Work means the
|
|
||||||
object code and/or source code for the Application, including any data
|
|
||||||
and utility programs needed for reproducing the Combined Work from the
|
|
||||||
Application, but excluding the System Libraries of the Combined Work.
|
|
||||||
|
|
||||||
1. Exception to Section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
You may convey a covered work under sections 3 and 4 of this License
|
|
||||||
without being bound by section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
2. Conveying Modified Versions.
|
|
||||||
|
|
||||||
If you modify a copy of the Library, and, in your modifications, a
|
|
||||||
facility refers to a function or data to be supplied by an Application
|
|
||||||
that uses the facility (other than as an argument passed when the
|
|
||||||
facility is invoked), then you may convey a copy of the modified
|
|
||||||
version:
|
|
||||||
|
|
||||||
a) under this License, provided that you make a good faith effort to
|
|
||||||
ensure that, in the event an Application does not supply the
|
|
||||||
function or data, the facility still operates, and performs
|
|
||||||
whatever part of its purpose remains meaningful, or
|
|
||||||
|
|
||||||
b) under the GNU GPL, with none of the additional permissions of
|
|
||||||
this License applicable to that copy.
|
|
||||||
|
|
||||||
3. Object Code Incorporating Material from Library Header Files.
|
|
||||||
|
|
||||||
The object code form of an Application may incorporate material from
|
|
||||||
a header file that is part of the Library. You may convey such object
|
|
||||||
code under terms of your choice, provided that, if the incorporated
|
|
||||||
material is not limited to numerical parameters, data structure
|
|
||||||
layouts and accessors, or small macros, inline functions and templates
|
|
||||||
(ten or fewer lines in length), you do both of the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the object code that the
|
|
||||||
Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
4. Combined Works.
|
|
||||||
|
|
||||||
You may convey a Combined Work under terms of your choice that,
|
|
||||||
taken together, effectively do not restrict modification of the
|
|
||||||
portions of the Library contained in the Combined Work and reverse
|
|
||||||
engineering for debugging such modifications, if you also do each of
|
|
||||||
the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the Combined Work that
|
|
||||||
the Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
c) For a Combined Work that displays copyright notices during
|
|
||||||
execution, include the copyright notice for the Library among
|
|
||||||
these notices, as well as a reference directing the user to the
|
|
||||||
copies of the GNU GPL and this license document.
|
|
||||||
|
|
||||||
d) Do one of the following:
|
|
||||||
|
|
||||||
0) Convey the Minimal Corresponding Source under the terms of this
|
|
||||||
License, and the Corresponding Application Code in a form
|
|
||||||
suitable for, and under terms that permit, the user to
|
|
||||||
recombine or relink the Application with a modified version of
|
|
||||||
the Linked Version to produce a modified Combined Work, in the
|
|
||||||
manner specified by section 6 of the GNU GPL for conveying
|
|
||||||
Corresponding Source.
|
|
||||||
|
|
||||||
1) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (a) uses at run time
|
|
||||||
a copy of the Library already present on the user's computer
|
|
||||||
system, and (b) will operate properly with a modified version
|
|
||||||
of the Library that is interface-compatible with the Linked
|
|
||||||
Version.
|
|
||||||
|
|
||||||
e) Provide Installation Information, but only if you would otherwise
|
|
||||||
be required to provide such information under section 6 of the
|
|
||||||
GNU GPL, and only to the extent that such information is
|
|
||||||
necessary to install and execute a modified version of the
|
|
||||||
Combined Work produced by recombining or relinking the
|
|
||||||
Application with a modified version of the Linked Version. (If
|
|
||||||
you use option 4d0, the Installation Information must accompany
|
|
||||||
the Minimal Corresponding Source and Corresponding Application
|
|
||||||
Code. If you use option 4d1, you must provide the Installation
|
|
||||||
Information in the manner specified by section 6 of the GNU GPL
|
|
||||||
for conveying Corresponding Source.)
|
|
||||||
|
|
||||||
5. Combined Libraries.
|
|
||||||
|
|
||||||
You may place library facilities that are a work based on the
|
|
||||||
Library side by side in a single library together with other library
|
|
||||||
facilities that are not Applications and are not covered by this
|
|
||||||
License, and convey such a combined library under terms of your
|
|
||||||
choice, if you do both of the following:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work based
|
|
||||||
on the Library, uncombined with any other library facilities,
|
|
||||||
conveyed under the terms of this License.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library that part of it
|
|
||||||
is a work based on the Library, and explaining where to find the
|
|
||||||
accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
6. Revised Versions of the GNU Lesser General Public License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the GNU Lesser General Public License from time to time. Such new
|
|
||||||
versions will be similar in spirit to the present version, but may
|
|
||||||
differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Library as you received it specifies that a certain numbered version
|
|
||||||
of the GNU Lesser General Public License "or any later version"
|
|
||||||
applies to it, you have the option of following the terms and
|
|
||||||
conditions either of that published version or of any later version
|
|
||||||
published by the Free Software Foundation. If the Library as you
|
|
||||||
received it does not specify a version number of the GNU Lesser
|
|
||||||
General Public License, you may choose any version of the GNU Lesser
|
|
||||||
General Public License ever published by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Library as you received it specifies that a proxy can decide
|
|
||||||
whether future versions of the GNU Lesser General Public License shall
|
|
||||||
apply, that proxy's public statement of acceptance of any version is
|
|
||||||
permanent authorization for you to choose that version for the
|
|
||||||
Library.
|
|
11
vendor/src/github.com/juju/errors/Makefile
vendored
11
vendor/src/github.com/juju/errors/Makefile
vendored
@ -1,11 +0,0 @@
|
|||||||
default: check
|
|
||||||
|
|
||||||
check:
|
|
||||||
go test && go test -compiler gccgo
|
|
||||||
|
|
||||||
docs:
|
|
||||||
godoc2md github.com/juju/errors > README.md
|
|
||||||
sed -i 's|\[godoc-link-here\]|[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors)|' README.md
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: default check docs
|
|
536
vendor/src/github.com/juju/errors/README.md
vendored
536
vendor/src/github.com/juju/errors/README.md
vendored
@ -1,536 +0,0 @@
|
|||||||
|
|
||||||
# errors
|
|
||||||
import "github.com/juju/errors"
|
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/juju/errors?status.svg)](https://godoc.org/github.com/juju/errors)
|
|
||||||
|
|
||||||
The juju/errors provides an easy way to annotate errors without losing the
|
|
||||||
orginal error context.
|
|
||||||
|
|
||||||
The exported `New` and `Errorf` functions are designed to replace the
|
|
||||||
`errors.New` and `fmt.Errorf` functions respectively. The same underlying
|
|
||||||
error is there, but the package also records the location at which the error
|
|
||||||
was created.
|
|
||||||
|
|
||||||
A primary use case for this library is to add extra context any time an
|
|
||||||
error is returned from a function.
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
This instead becomes:
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Trace(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
which just records the file and line number of the Trace call, or
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Annotate(err, "more context")
|
|
||||||
}
|
|
||||||
|
|
||||||
which also adds an annotation to the error.
|
|
||||||
|
|
||||||
When you want to check to see if an error is of a particular type, a helper
|
|
||||||
function is normally exported by the package that returned the error, like the
|
|
||||||
`os` package does. The underlying cause of the error is available using the
|
|
||||||
`Cause` function.
|
|
||||||
|
|
||||||
|
|
||||||
os.IsNotExist(errors.Cause(err))
|
|
||||||
|
|
||||||
The result of the `Error()` call on an annotated error is the annotations joined
|
|
||||||
with colons, then the result of the `Error()` method for the underlying error
|
|
||||||
that was the cause.
|
|
||||||
|
|
||||||
|
|
||||||
err := errors.Errorf("original")
|
|
||||||
err = errors.Annotatef(err, "context")
|
|
||||||
err = errors.Annotatef(err, "more context")
|
|
||||||
err.Error() -> "more context: context: original"
|
|
||||||
|
|
||||||
Obviously recording the file, line and functions is not very useful if you
|
|
||||||
cannot get them back out again.
|
|
||||||
|
|
||||||
|
|
||||||
errors.ErrorStack(err)
|
|
||||||
|
|
||||||
will return something like:
|
|
||||||
|
|
||||||
|
|
||||||
first error
|
|
||||||
github.com/juju/errors/annotation_test.go:193:
|
|
||||||
github.com/juju/errors/annotation_test.go:194: annotation
|
|
||||||
github.com/juju/errors/annotation_test.go:195:
|
|
||||||
github.com/juju/errors/annotation_test.go:196: more context
|
|
||||||
github.com/juju/errors/annotation_test.go:197:
|
|
||||||
|
|
||||||
The first error was generated by an external system, so there was no location
|
|
||||||
associated. The second, fourth, and last lines were generated with Trace calls,
|
|
||||||
and the other two through Annotate.
|
|
||||||
|
|
||||||
Sometimes when responding to an error you want to return a more specific error
|
|
||||||
for the situation.
|
|
||||||
|
|
||||||
|
|
||||||
if err := FindField(field); err != nil {
|
|
||||||
return errors.Wrap(err, errors.NotFoundf(field))
|
|
||||||
}
|
|
||||||
|
|
||||||
This returns an error where the complete error stack is still available, and
|
|
||||||
`errors.Cause()` will return the `NotFound` error.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## func AlreadyExistsf
|
|
||||||
``` go
|
|
||||||
func AlreadyExistsf(format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
AlreadyExistsf returns an error which satisfies IsAlreadyExists().
|
|
||||||
|
|
||||||
|
|
||||||
## func Annotate
|
|
||||||
``` go
|
|
||||||
func Annotate(other error, message string) error
|
|
||||||
```
|
|
||||||
Annotate is used to add extra context to an existing error. The location of
|
|
||||||
the Annotate call is recorded with the annotations. The file, line and
|
|
||||||
function are also recorded.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Annotate(err, "failed to frombulate")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## func Annotatef
|
|
||||||
``` go
|
|
||||||
func Annotatef(other error, format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
Annotatef is used to add extra context to an existing error. The location of
|
|
||||||
the Annotate call is recorded with the annotations. The file, line and
|
|
||||||
function are also recorded.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Annotatef(err, "failed to frombulate the %s", arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## func Cause
|
|
||||||
``` go
|
|
||||||
func Cause(err error) error
|
|
||||||
```
|
|
||||||
Cause returns the cause of the given error. This will be either the
|
|
||||||
original error, or the result of a Wrap or Mask call.
|
|
||||||
|
|
||||||
Cause is the usual way to diagnose errors that may have been wrapped by
|
|
||||||
the other errors functions.
|
|
||||||
|
|
||||||
|
|
||||||
## func DeferredAnnotatef
|
|
||||||
``` go
|
|
||||||
func DeferredAnnotatef(err *error, format string, args ...interface{})
|
|
||||||
```
|
|
||||||
DeferredAnnotatef annotates the given error (when it is not nil) with the given
|
|
||||||
format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
|
|
||||||
does nothing. This method is used in a defer statement in order to annotate any
|
|
||||||
resulting error with the same message.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
|
|
||||||
|
|
||||||
|
|
||||||
## func Details
|
|
||||||
``` go
|
|
||||||
func Details(err error) string
|
|
||||||
```
|
|
||||||
Details returns information about the stack of errors wrapped by err, in
|
|
||||||
the format:
|
|
||||||
|
|
||||||
|
|
||||||
[{filename:99: error one} {otherfile:55: cause of error one}]
|
|
||||||
|
|
||||||
This is a terse alternative to ErrorStack as it returns a single line.
|
|
||||||
|
|
||||||
|
|
||||||
## func ErrorStack
|
|
||||||
``` go
|
|
||||||
func ErrorStack(err error) string
|
|
||||||
```
|
|
||||||
ErrorStack returns a string representation of the annotated error. If the
|
|
||||||
error passed as the parameter is not an annotated error, the result is
|
|
||||||
simply the result of the Error() method on that error.
|
|
||||||
|
|
||||||
If the error is an annotated error, a multi-line string is returned where
|
|
||||||
each line represents one entry in the annotation stack. The full filename
|
|
||||||
from the call stack is used in the output.
|
|
||||||
|
|
||||||
|
|
||||||
first error
|
|
||||||
github.com/juju/errors/annotation_test.go:193:
|
|
||||||
github.com/juju/errors/annotation_test.go:194: annotation
|
|
||||||
github.com/juju/errors/annotation_test.go:195:
|
|
||||||
github.com/juju/errors/annotation_test.go:196: more context
|
|
||||||
github.com/juju/errors/annotation_test.go:197:
|
|
||||||
|
|
||||||
|
|
||||||
## func Errorf
|
|
||||||
``` go
|
|
||||||
func Errorf(format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
Errorf creates a new annotated error and records the location that the
|
|
||||||
error is created. This should be a drop in replacement for fmt.Errorf.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
return errors.Errorf("validation failed: %s", message)
|
|
||||||
|
|
||||||
|
|
||||||
## func IsAlreadyExists
|
|
||||||
``` go
|
|
||||||
func IsAlreadyExists(err error) bool
|
|
||||||
```
|
|
||||||
IsAlreadyExists reports whether the error was created with
|
|
||||||
AlreadyExistsf() or NewAlreadyExists().
|
|
||||||
|
|
||||||
|
|
||||||
## func IsNotFound
|
|
||||||
``` go
|
|
||||||
func IsNotFound(err error) bool
|
|
||||||
```
|
|
||||||
IsNotFound reports whether err was created with NotFoundf() or
|
|
||||||
NewNotFound().
|
|
||||||
|
|
||||||
|
|
||||||
## func IsNotImplemented
|
|
||||||
``` go
|
|
||||||
func IsNotImplemented(err error) bool
|
|
||||||
```
|
|
||||||
IsNotImplemented reports whether err was created with
|
|
||||||
NotImplementedf() or NewNotImplemented().
|
|
||||||
|
|
||||||
|
|
||||||
## func IsNotSupported
|
|
||||||
``` go
|
|
||||||
func IsNotSupported(err error) bool
|
|
||||||
```
|
|
||||||
IsNotSupported reports whether the error was created with
|
|
||||||
NotSupportedf() or NewNotSupported().
|
|
||||||
|
|
||||||
|
|
||||||
## func IsNotValid
|
|
||||||
``` go
|
|
||||||
func IsNotValid(err error) bool
|
|
||||||
```
|
|
||||||
IsNotValid reports whether the error was created with NotValidf() or
|
|
||||||
NewNotValid().
|
|
||||||
|
|
||||||
|
|
||||||
## func IsUnauthorized
|
|
||||||
``` go
|
|
||||||
func IsUnauthorized(err error) bool
|
|
||||||
```
|
|
||||||
IsUnauthorized reports whether err was created with Unauthorizedf() or
|
|
||||||
NewUnauthorized().
|
|
||||||
|
|
||||||
|
|
||||||
## func Mask
|
|
||||||
``` go
|
|
||||||
func Mask(other error) error
|
|
||||||
```
|
|
||||||
Mask hides the underlying error type, and records the location of the masking.
|
|
||||||
|
|
||||||
|
|
||||||
## func Maskf
|
|
||||||
``` go
|
|
||||||
func Maskf(other error, format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
Mask masks the given error with the given format string and arguments (like
|
|
||||||
fmt.Sprintf), returning a new error that maintains the error stack, but
|
|
||||||
hides the underlying error type. The error string still contains the full
|
|
||||||
annotations. If you want to hide the annotations, call Wrap.
|
|
||||||
|
|
||||||
|
|
||||||
## func New
|
|
||||||
``` go
|
|
||||||
func New(message string) error
|
|
||||||
```
|
|
||||||
New is a drop in replacement for the standard libary errors module that records
|
|
||||||
the location that the error is created.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
return errors.New("validation failed")
|
|
||||||
|
|
||||||
|
|
||||||
## func NewAlreadyExists
|
|
||||||
``` go
|
|
||||||
func NewAlreadyExists(err error, msg string) error
|
|
||||||
```
|
|
||||||
NewAlreadyExists returns an error which wraps err and satisfies
|
|
||||||
IsAlreadyExists().
|
|
||||||
|
|
||||||
|
|
||||||
## func NewNotFound
|
|
||||||
``` go
|
|
||||||
func NewNotFound(err error, msg string) error
|
|
||||||
```
|
|
||||||
NewNotFound returns an error which wraps err that satisfies
|
|
||||||
IsNotFound().
|
|
||||||
|
|
||||||
|
|
||||||
## func NewNotImplemented
|
|
||||||
``` go
|
|
||||||
func NewNotImplemented(err error, msg string) error
|
|
||||||
```
|
|
||||||
NewNotImplemented returns an error which wraps err and satisfies
|
|
||||||
IsNotImplemented().
|
|
||||||
|
|
||||||
|
|
||||||
## func NewNotSupported
|
|
||||||
``` go
|
|
||||||
func NewNotSupported(err error, msg string) error
|
|
||||||
```
|
|
||||||
NewNotSupported returns an error which wraps err and satisfies
|
|
||||||
IsNotSupported().
|
|
||||||
|
|
||||||
|
|
||||||
## func NewNotValid
|
|
||||||
``` go
|
|
||||||
func NewNotValid(err error, msg string) error
|
|
||||||
```
|
|
||||||
NewNotValid returns an error which wraps err and satisfies IsNotValid().
|
|
||||||
|
|
||||||
|
|
||||||
## func NewUnauthorized
|
|
||||||
``` go
|
|
||||||
func NewUnauthorized(err error, msg string) error
|
|
||||||
```
|
|
||||||
NewUnauthorized returns an error which wraps err and satisfies
|
|
||||||
IsUnauthorized().
|
|
||||||
|
|
||||||
|
|
||||||
## func NotFoundf
|
|
||||||
``` go
|
|
||||||
func NotFoundf(format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
NotFoundf returns an error which satisfies IsNotFound().
|
|
||||||
|
|
||||||
|
|
||||||
## func NotImplementedf
|
|
||||||
``` go
|
|
||||||
func NotImplementedf(format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
NotImplementedf returns an error which satisfies IsNotImplemented().
|
|
||||||
|
|
||||||
|
|
||||||
## func NotSupportedf
|
|
||||||
``` go
|
|
||||||
func NotSupportedf(format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
NotSupportedf returns an error which satisfies IsNotSupported().
|
|
||||||
|
|
||||||
|
|
||||||
## func NotValidf
|
|
||||||
``` go
|
|
||||||
func NotValidf(format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
NotValidf returns an error which satisfies IsNotValid().
|
|
||||||
|
|
||||||
|
|
||||||
## func Trace
|
|
||||||
``` go
|
|
||||||
func Trace(other error) error
|
|
||||||
```
|
|
||||||
Trace adds the location of the Trace call to the stack. The Cause of the
|
|
||||||
resulting error is the same as the error parameter. If the other error is
|
|
||||||
nil, the result will be nil.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Trace(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## func Unauthorizedf
|
|
||||||
``` go
|
|
||||||
func Unauthorizedf(format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
Unauthorizedf returns an error which satisfies IsUnauthorized().
|
|
||||||
|
|
||||||
|
|
||||||
## func Wrap
|
|
||||||
``` go
|
|
||||||
func Wrap(other, newDescriptive error) error
|
|
||||||
```
|
|
||||||
Wrap changes the Cause of the error. The location of the Wrap call is also
|
|
||||||
stored in the error stack.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
newErr := &packageError{"more context", private_value}
|
|
||||||
return errors.Wrap(err, newErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
## func Wrapf
|
|
||||||
``` go
|
|
||||||
func Wrapf(other, newDescriptive error, format string, args ...interface{}) error
|
|
||||||
```
|
|
||||||
Wrapf changes the Cause of the error, and adds an annotation. The location
|
|
||||||
of the Wrap call is also stored in the error stack.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## type Err
|
|
||||||
``` go
|
|
||||||
type Err struct {
|
|
||||||
// contains filtered or unexported fields
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Err holds a description of an error along with information about
|
|
||||||
where the error was created.
|
|
||||||
|
|
||||||
It may be embedded in custom error types to add extra information that
|
|
||||||
this errors package can understand.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func NewErr
|
|
||||||
``` go
|
|
||||||
func NewErr(format string, args ...interface{}) Err
|
|
||||||
```
|
|
||||||
NewErr is used to return an Err for the purpose of embedding in other
|
|
||||||
structures. The location is not specified, and needs to be set with a call
|
|
||||||
to SetLocation.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
|
|
||||||
type FooError struct {
|
|
||||||
errors.Err
|
|
||||||
code int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFooError(code int) error {
|
|
||||||
err := &FooError{errors.NewErr("foo"), code}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func (\*Err) Cause
|
|
||||||
``` go
|
|
||||||
func (e *Err) Cause() error
|
|
||||||
```
|
|
||||||
The Cause of an error is the most recent error in the error stack that
|
|
||||||
meets one of these criteria: the original error that was raised; the new
|
|
||||||
error that was passed into the Wrap function; the most recently masked
|
|
||||||
error; or nil if the error itself is considered the Cause. Normally this
|
|
||||||
method is not invoked directly, but instead through the Cause stand alone
|
|
||||||
function.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func (\*Err) Error
|
|
||||||
``` go
|
|
||||||
func (e *Err) Error() string
|
|
||||||
```
|
|
||||||
Error implements error.Error.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func (\*Err) Location
|
|
||||||
``` go
|
|
||||||
func (e *Err) Location() (filename string, line int)
|
|
||||||
```
|
|
||||||
Location is the file and line of where the error was most recently
|
|
||||||
created or annotated.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func (\*Err) Message
|
|
||||||
``` go
|
|
||||||
func (e *Err) Message() string
|
|
||||||
```
|
|
||||||
Message returns the message stored with the most recent location. This is
|
|
||||||
the empty string if the most recent call was Trace, or the message stored
|
|
||||||
with Annotate or Mask.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func (\*Err) SetLocation
|
|
||||||
``` go
|
|
||||||
func (e *Err) SetLocation(callDepth int)
|
|
||||||
```
|
|
||||||
SetLocation records the source location of the error at callDepth stack
|
|
||||||
frames above the call.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func (\*Err) StackTrace
|
|
||||||
``` go
|
|
||||||
func (e *Err) StackTrace() []string
|
|
||||||
```
|
|
||||||
StackTrace returns one string for each location recorded in the stack of
|
|
||||||
errors. The first value is the originating error, with a line for each
|
|
||||||
other annotation or tracing of the error.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### func (\*Err) Underlying
|
|
||||||
``` go
|
|
||||||
func (e *Err) Underlying() error
|
|
||||||
```
|
|
||||||
Underlying returns the previous error in the error stack, if any. A client
|
|
||||||
should not ever really call this method. It is used to build the error
|
|
||||||
stack and should not be introspected by client calls. Or more
|
|
||||||
specifically, clients should not depend on anything but the `Cause` of an
|
|
||||||
error.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- - -
|
|
||||||
Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
|
|
81
vendor/src/github.com/juju/errors/doc.go
vendored
81
vendor/src/github.com/juju/errors/doc.go
vendored
@ -1,81 +0,0 @@
|
|||||||
// Copyright 2013, 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
/*
|
|
||||||
[godoc-link-here]
|
|
||||||
|
|
||||||
The juju/errors provides an easy way to annotate errors without losing the
|
|
||||||
orginal error context.
|
|
||||||
|
|
||||||
The exported `New` and `Errorf` functions are designed to replace the
|
|
||||||
`errors.New` and `fmt.Errorf` functions respectively. The same underlying
|
|
||||||
error is there, but the package also records the location at which the error
|
|
||||||
was created.
|
|
||||||
|
|
||||||
A primary use case for this library is to add extra context any time an
|
|
||||||
error is returned from a function.
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
This instead becomes:
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Trace(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
which just records the file and line number of the Trace call, or
|
|
||||||
|
|
||||||
if err := SomeFunc(); err != nil {
|
|
||||||
return errors.Annotate(err, "more context")
|
|
||||||
}
|
|
||||||
|
|
||||||
which also adds an annotation to the error.
|
|
||||||
|
|
||||||
When you want to check to see if an error is of a particular type, a helper
|
|
||||||
function is normally exported by the package that returned the error, like the
|
|
||||||
`os` package does. The underlying cause of the error is available using the
|
|
||||||
`Cause` function.
|
|
||||||
|
|
||||||
os.IsNotExist(errors.Cause(err))
|
|
||||||
|
|
||||||
The result of the `Error()` call on an annotated error is the annotations joined
|
|
||||||
with colons, then the result of the `Error()` method for the underlying error
|
|
||||||
that was the cause.
|
|
||||||
|
|
||||||
err := errors.Errorf("original")
|
|
||||||
err = errors.Annotatef(err, "context")
|
|
||||||
err = errors.Annotatef(err, "more context")
|
|
||||||
err.Error() -> "more context: context: original"
|
|
||||||
|
|
||||||
Obviously recording the file, line and functions is not very useful if you
|
|
||||||
cannot get them back out again.
|
|
||||||
|
|
||||||
errors.ErrorStack(err)
|
|
||||||
|
|
||||||
will return something like:
|
|
||||||
|
|
||||||
first error
|
|
||||||
github.com/juju/errors/annotation_test.go:193:
|
|
||||||
github.com/juju/errors/annotation_test.go:194: annotation
|
|
||||||
github.com/juju/errors/annotation_test.go:195:
|
|
||||||
github.com/juju/errors/annotation_test.go:196: more context
|
|
||||||
github.com/juju/errors/annotation_test.go:197:
|
|
||||||
|
|
||||||
The first error was generated by an external system, so there was no location
|
|
||||||
associated. The second, fourth, and last lines were generated with Trace calls,
|
|
||||||
and the other two through Annotate.
|
|
||||||
|
|
||||||
Sometimes when responding to an error you want to return a more specific error
|
|
||||||
for the situation.
|
|
||||||
|
|
||||||
if err := FindField(field); err != nil {
|
|
||||||
return errors.Wrap(err, errors.NotFoundf(field))
|
|
||||||
}
|
|
||||||
|
|
||||||
This returns an error where the complete error stack is still available, and
|
|
||||||
`errors.Cause()` will return the `NotFound` error.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package errors
|
|
122
vendor/src/github.com/juju/errors/error.go
vendored
122
vendor/src/github.com/juju/errors/error.go
vendored
@ -1,122 +0,0 @@
|
|||||||
// Copyright 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Err holds a description of an error along with information about
|
|
||||||
// where the error was created.
|
|
||||||
//
|
|
||||||
// It may be embedded in custom error types to add extra information that
|
|
||||||
// this errors package can understand.
|
|
||||||
type Err struct {
|
|
||||||
// message holds an annotation of the error.
|
|
||||||
message string
|
|
||||||
|
|
||||||
// cause holds the cause of the error as returned
|
|
||||||
// by the Cause method.
|
|
||||||
cause error
|
|
||||||
|
|
||||||
// previous holds the previous error in the error stack, if any.
|
|
||||||
previous error
|
|
||||||
|
|
||||||
// file and line hold the source code location where the error was
|
|
||||||
// created.
|
|
||||||
file string
|
|
||||||
line int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewErr is used to return an Err for the purpose of embedding in other
|
|
||||||
// structures. The location is not specified, and needs to be set with a call
|
|
||||||
// to SetLocation.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// type FooError struct {
|
|
||||||
// errors.Err
|
|
||||||
// code int
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func NewFooError(code int) error {
|
|
||||||
// err := &FooError{errors.NewErr("foo"), code}
|
|
||||||
// err.SetLocation(1)
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
func NewErr(format string, args ...interface{}) Err {
|
|
||||||
return Err{
|
|
||||||
message: fmt.Sprintf(format, args...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Location is the file and line of where the error was most recently
|
|
||||||
// created or annotated.
|
|
||||||
func (e *Err) Location() (filename string, line int) {
|
|
||||||
return e.file, e.line
|
|
||||||
}
|
|
||||||
|
|
||||||
// Underlying returns the previous error in the error stack, if any. A client
|
|
||||||
// should not ever really call this method. It is used to build the error
|
|
||||||
// stack and should not be introspected by client calls. Or more
|
|
||||||
// specifically, clients should not depend on anything but the `Cause` of an
|
|
||||||
// error.
|
|
||||||
func (e *Err) Underlying() error {
|
|
||||||
return e.previous
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Cause of an error is the most recent error in the error stack that
|
|
||||||
// meets one of these criteria: the original error that was raised; the new
|
|
||||||
// error that was passed into the Wrap function; the most recently masked
|
|
||||||
// error; or nil if the error itself is considered the Cause. Normally this
|
|
||||||
// method is not invoked directly, but instead through the Cause stand alone
|
|
||||||
// function.
|
|
||||||
func (e *Err) Cause() error {
|
|
||||||
return e.cause
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message returns the message stored with the most recent location. This is
|
|
||||||
// the empty string if the most recent call was Trace, or the message stored
|
|
||||||
// with Annotate or Mask.
|
|
||||||
func (e *Err) Message() string {
|
|
||||||
return e.message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error implements error.Error.
|
|
||||||
func (e *Err) Error() string {
|
|
||||||
// We want to walk up the stack of errors showing the annotations
|
|
||||||
// as long as the cause is the same.
|
|
||||||
err := e.previous
|
|
||||||
if !sameError(Cause(err), e.cause) && e.cause != nil {
|
|
||||||
err = e.cause
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case err == nil:
|
|
||||||
return e.message
|
|
||||||
case e.message == "":
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s: %v", e.message, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLocation records the source location of the error at callDepth stack
|
|
||||||
// frames above the call.
|
|
||||||
func (e *Err) SetLocation(callDepth int) {
|
|
||||||
_, file, line, _ := runtime.Caller(callDepth + 1)
|
|
||||||
e.file = trimGoPath(file)
|
|
||||||
e.line = line
|
|
||||||
}
|
|
||||||
|
|
||||||
// StackTrace returns one string for each location recorded in the stack of
|
|
||||||
// errors. The first value is the originating error, with a line for each
|
|
||||||
// other annotation or tracing of the error.
|
|
||||||
func (e *Err) StackTrace() []string {
|
|
||||||
return errorStack(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ideally we'd have a way to check identity, but deep equals will do.
|
|
||||||
func sameError(e1, e2 error) bool {
|
|
||||||
return reflect.DeepEqual(e1, e2)
|
|
||||||
}
|
|
161
vendor/src/github.com/juju/errors/error_test.go
vendored
161
vendor/src/github.com/juju/errors/error_test.go
vendored
@ -1,161 +0,0 @@
|
|||||||
// Copyright 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
jc "github.com/juju/testing/checkers"
|
|
||||||
gc "gopkg.in/check.v1"
|
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type errorsSuite struct{}
|
|
||||||
|
|
||||||
var _ = gc.Suite(&errorsSuite{})
|
|
||||||
|
|
||||||
var someErr = errors.New("some error") //err varSomeErr
|
|
||||||
|
|
||||||
func (*errorsSuite) TestErrorString(c *gc.C) {
|
|
||||||
for i, test := range []struct {
|
|
||||||
message string
|
|
||||||
generator func() error
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
message: "uncomparable errors",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.Annotatef(newNonComparableError("uncomparable"), "annotation")
|
|
||||||
return errors.Annotatef(err, "another")
|
|
||||||
},
|
|
||||||
expected: "another: annotation: uncomparable",
|
|
||||||
}, {
|
|
||||||
message: "Errorf",
|
|
||||||
generator: func() error {
|
|
||||||
return errors.Errorf("first error")
|
|
||||||
},
|
|
||||||
expected: "first error",
|
|
||||||
}, {
|
|
||||||
message: "annotated error",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.Errorf("first error")
|
|
||||||
return errors.Annotatef(err, "annotation")
|
|
||||||
},
|
|
||||||
expected: "annotation: first error",
|
|
||||||
}, {
|
|
||||||
message: "test annotation format",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.Errorf("first %s", "error")
|
|
||||||
return errors.Annotatef(err, "%s", "annotation")
|
|
||||||
},
|
|
||||||
expected: "annotation: first error",
|
|
||||||
}, {
|
|
||||||
message: "wrapped error",
|
|
||||||
generator: func() error {
|
|
||||||
err := newError("first error")
|
|
||||||
return errors.Wrap(err, newError("detailed error"))
|
|
||||||
},
|
|
||||||
expected: "detailed error",
|
|
||||||
}, {
|
|
||||||
message: "wrapped annotated error",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.Errorf("first error")
|
|
||||||
err = errors.Annotatef(err, "annotated")
|
|
||||||
return errors.Wrap(err, fmt.Errorf("detailed error"))
|
|
||||||
},
|
|
||||||
expected: "detailed error",
|
|
||||||
}, {
|
|
||||||
message: "annotated wrapped error",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.Errorf("first error")
|
|
||||||
err = errors.Wrap(err, fmt.Errorf("detailed error"))
|
|
||||||
return errors.Annotatef(err, "annotated")
|
|
||||||
},
|
|
||||||
expected: "annotated: detailed error",
|
|
||||||
}, {
|
|
||||||
message: "traced, and annotated",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.New("first error")
|
|
||||||
err = errors.Trace(err)
|
|
||||||
err = errors.Annotate(err, "some context")
|
|
||||||
err = errors.Trace(err)
|
|
||||||
err = errors.Annotate(err, "more context")
|
|
||||||
return errors.Trace(err)
|
|
||||||
},
|
|
||||||
expected: "more context: some context: first error",
|
|
||||||
}, {
|
|
||||||
message: "traced, and annotated, masked and annotated",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.New("first error")
|
|
||||||
err = errors.Trace(err)
|
|
||||||
err = errors.Annotate(err, "some context")
|
|
||||||
err = errors.Maskf(err, "masked")
|
|
||||||
err = errors.Annotate(err, "more context")
|
|
||||||
return errors.Trace(err)
|
|
||||||
},
|
|
||||||
expected: "more context: masked: some context: first error",
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
c.Logf("%v: %s", i, test.message)
|
|
||||||
err := test.generator()
|
|
||||||
ok := c.Check(err.Error(), gc.Equals, test.expected)
|
|
||||||
if !ok {
|
|
||||||
c.Logf("%#v", test.generator())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type embed struct {
|
|
||||||
errors.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEmbed(format string, args ...interface{}) *embed {
|
|
||||||
err := &embed{errors.NewErr(format, args...)}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*errorsSuite) TestNewErr(c *gc.C) {
|
|
||||||
if runtime.Compiler == "gccgo" {
|
|
||||||
c.Skip("gccgo can't determine the location")
|
|
||||||
}
|
|
||||||
err := newEmbed("testing %d", 42) //err embedErr
|
|
||||||
c.Assert(err.Error(), gc.Equals, "testing 42")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, err)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["embedErr"].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ error = (*embed)(nil)
|
|
||||||
|
|
||||||
// This is an uncomparable error type, as it is a struct that supports the
|
|
||||||
// error interface (as opposed to a pointer type).
|
|
||||||
type error_ struct {
|
|
||||||
info string
|
|
||||||
slice []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a non-comparable error
|
|
||||||
func newNonComparableError(message string) error {
|
|
||||||
return error_{info: message}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e error_) Error() string {
|
|
||||||
return e.info
|
|
||||||
}
|
|
||||||
|
|
||||||
func newError(message string) error {
|
|
||||||
return testError{message}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The testError is a value type error for ease of seeing results
|
|
||||||
// when the test fails.
|
|
||||||
type testError struct {
|
|
||||||
message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e testError) Error() string {
|
|
||||||
return e.message
|
|
||||||
}
|
|
235
vendor/src/github.com/juju/errors/errortypes.go
vendored
235
vendor/src/github.com/juju/errors/errortypes.go
vendored
@ -1,235 +0,0 @@
|
|||||||
// Copyright 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// wrap is a helper to construct an *wrapper.
|
|
||||||
func wrap(err error, format, suffix string, args ...interface{}) Err {
|
|
||||||
newErr := Err{
|
|
||||||
message: fmt.Sprintf(format+suffix, args...),
|
|
||||||
previous: err,
|
|
||||||
}
|
|
||||||
newErr.SetLocation(2)
|
|
||||||
return newErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// notFound represents an error when something has not been found.
|
|
||||||
type notFound struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotFoundf returns an error which satisfies IsNotFound().
|
|
||||||
func NotFoundf(format string, args ...interface{}) error {
|
|
||||||
return ¬Found{wrap(nil, format, " not found", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNotFound returns an error which wraps err that satisfies
|
|
||||||
// IsNotFound().
|
|
||||||
func NewNotFound(err error, msg string) error {
|
|
||||||
return ¬Found{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotFound reports whether err was created with NotFoundf() or
|
|
||||||
// NewNotFound().
|
|
||||||
func IsNotFound(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*notFound)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// userNotFound represents an error when an inexistent user is looked up.
|
|
||||||
type userNotFound struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserNotFoundf returns an error which satisfies IsUserNotFound().
|
|
||||||
func UserNotFoundf(format string, args ...interface{}) error {
|
|
||||||
return &userNotFound{wrap(nil, format, " user not found", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUserNotFound returns an error which wraps err and satisfies
|
|
||||||
// IsUserNotFound().
|
|
||||||
func NewUserNotFound(err error, msg string) error {
|
|
||||||
return &userNotFound{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUserNotFound reports whether err was created with UserNotFoundf() or
|
|
||||||
// NewUserNotFound().
|
|
||||||
func IsUserNotFound(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*userNotFound)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// unauthorized represents an error when an operation is unauthorized.
|
|
||||||
type unauthorized struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unauthorizedf returns an error which satisfies IsUnauthorized().
|
|
||||||
func Unauthorizedf(format string, args ...interface{}) error {
|
|
||||||
return &unauthorized{wrap(nil, format, "", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUnauthorized returns an error which wraps err and satisfies
|
|
||||||
// IsUnauthorized().
|
|
||||||
func NewUnauthorized(err error, msg string) error {
|
|
||||||
return &unauthorized{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUnauthorized reports whether err was created with Unauthorizedf() or
|
|
||||||
// NewUnauthorized().
|
|
||||||
func IsUnauthorized(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*unauthorized)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// notImplemented represents an error when something is not
|
|
||||||
// implemented.
|
|
||||||
type notImplemented struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotImplementedf returns an error which satisfies IsNotImplemented().
|
|
||||||
func NotImplementedf(format string, args ...interface{}) error {
|
|
||||||
return ¬Implemented{wrap(nil, format, " not implemented", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNotImplemented returns an error which wraps err and satisfies
|
|
||||||
// IsNotImplemented().
|
|
||||||
func NewNotImplemented(err error, msg string) error {
|
|
||||||
return ¬Implemented{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotImplemented reports whether err was created with
|
|
||||||
// NotImplementedf() or NewNotImplemented().
|
|
||||||
func IsNotImplemented(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*notImplemented)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// alreadyExists represents and error when something already exists.
|
|
||||||
type alreadyExists struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// AlreadyExistsf returns an error which satisfies IsAlreadyExists().
|
|
||||||
func AlreadyExistsf(format string, args ...interface{}) error {
|
|
||||||
return &alreadyExists{wrap(nil, format, " already exists", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAlreadyExists returns an error which wraps err and satisfies
|
|
||||||
// IsAlreadyExists().
|
|
||||||
func NewAlreadyExists(err error, msg string) error {
|
|
||||||
return &alreadyExists{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAlreadyExists reports whether the error was created with
|
|
||||||
// AlreadyExistsf() or NewAlreadyExists().
|
|
||||||
func IsAlreadyExists(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*alreadyExists)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// notSupported represents an error when something is not supported.
|
|
||||||
type notSupported struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotSupportedf returns an error which satisfies IsNotSupported().
|
|
||||||
func NotSupportedf(format string, args ...interface{}) error {
|
|
||||||
return ¬Supported{wrap(nil, format, " not supported", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNotSupported returns an error which wraps err and satisfies
|
|
||||||
// IsNotSupported().
|
|
||||||
func NewNotSupported(err error, msg string) error {
|
|
||||||
return ¬Supported{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotSupported reports whether the error was created with
|
|
||||||
// NotSupportedf() or NewNotSupported().
|
|
||||||
func IsNotSupported(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*notSupported)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// notValid represents an error when something is not valid.
|
|
||||||
type notValid struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotValidf returns an error which satisfies IsNotValid().
|
|
||||||
func NotValidf(format string, args ...interface{}) error {
|
|
||||||
return ¬Valid{wrap(nil, format, " not valid", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNotValid returns an error which wraps err and satisfies IsNotValid().
|
|
||||||
func NewNotValid(err error, msg string) error {
|
|
||||||
return ¬Valid{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotValid reports whether the error was created with NotValidf() or
|
|
||||||
// NewNotValid().
|
|
||||||
func IsNotValid(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*notValid)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// notProvisioned represents an error when something is not yet provisioned.
|
|
||||||
type notProvisioned struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotProvisionedf returns an error which satisfies IsNotProvisioned().
|
|
||||||
func NotProvisionedf(format string, args ...interface{}) error {
|
|
||||||
return ¬Provisioned{wrap(nil, format, " not provisioned", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNotProvisioned returns an error which wraps err that satisfies
|
|
||||||
// IsNotProvisioned().
|
|
||||||
func NewNotProvisioned(err error, msg string) error {
|
|
||||||
return ¬Provisioned{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotProvisioned reports whether err was created with NotProvisionedf() or
|
|
||||||
// NewNotProvisioned().
|
|
||||||
func IsNotProvisioned(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*notProvisioned)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// notAssigned represents an error when something is not yet assigned to
|
|
||||||
// something else.
|
|
||||||
type notAssigned struct {
|
|
||||||
Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotAssignedf returns an error which satisfies IsNotAssigned().
|
|
||||||
func NotAssignedf(format string, args ...interface{}) error {
|
|
||||||
return ¬Assigned{wrap(nil, format, " not assigned", args...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNotAssigned returns an error which wraps err that satisfies
|
|
||||||
// IsNotAssigned().
|
|
||||||
func NewNotAssigned(err error, msg string) error {
|
|
||||||
return ¬Assigned{wrap(err, msg, "")}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotAssigned reports whether err was created with NotAssignedf() or
|
|
||||||
// NewNotAssigned().
|
|
||||||
func IsNotAssigned(err error) bool {
|
|
||||||
err = Cause(err)
|
|
||||||
_, ok := err.(*notAssigned)
|
|
||||||
return ok
|
|
||||||
}
|
|
171
vendor/src/github.com/juju/errors/errortypes_test.go
vendored
171
vendor/src/github.com/juju/errors/errortypes_test.go
vendored
@ -1,171 +0,0 @@
|
|||||||
// Copyright 2013, 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
stderrors "errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
jc "github.com/juju/testing/checkers"
|
|
||||||
gc "gopkg.in/check.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// errorInfo holds information about a single error type: a satisfier
|
|
||||||
// function, wrapping and variable arguments constructors and message
|
|
||||||
// suffix.
|
|
||||||
type errorInfo struct {
|
|
||||||
satisfier func(error) bool
|
|
||||||
argsConstructor func(string, ...interface{}) error
|
|
||||||
wrapConstructor func(error, string) error
|
|
||||||
suffix string
|
|
||||||
}
|
|
||||||
|
|
||||||
// allErrors holds information for all defined errors. When adding new
|
|
||||||
// errors, add them here as well to include them in tests.
|
|
||||||
var allErrors = []*errorInfo{
|
|
||||||
&errorInfo{errors.IsNotFound, errors.NotFoundf, errors.NewNotFound, " not found"},
|
|
||||||
&errorInfo{errors.IsUserNotFound, errors.UserNotFoundf, errors.NewUserNotFound, " user not found"},
|
|
||||||
&errorInfo{errors.IsUnauthorized, errors.Unauthorizedf, errors.NewUnauthorized, ""},
|
|
||||||
&errorInfo{errors.IsNotImplemented, errors.NotImplementedf, errors.NewNotImplemented, " not implemented"},
|
|
||||||
&errorInfo{errors.IsAlreadyExists, errors.AlreadyExistsf, errors.NewAlreadyExists, " already exists"},
|
|
||||||
&errorInfo{errors.IsNotSupported, errors.NotSupportedf, errors.NewNotSupported, " not supported"},
|
|
||||||
&errorInfo{errors.IsNotValid, errors.NotValidf, errors.NewNotValid, " not valid"},
|
|
||||||
&errorInfo{errors.IsNotProvisioned, errors.NotProvisionedf, errors.NewNotProvisioned, " not provisioned"},
|
|
||||||
&errorInfo{errors.IsNotAssigned, errors.NotAssignedf, errors.NewNotAssigned, " not assigned"},
|
|
||||||
}
|
|
||||||
|
|
||||||
type errorTypeSuite struct{}
|
|
||||||
|
|
||||||
var _ = gc.Suite(&errorTypeSuite{})
|
|
||||||
|
|
||||||
func (t *errorInfo) satisfierName() string {
|
|
||||||
value := reflect.ValueOf(t.satisfier)
|
|
||||||
f := runtime.FuncForPC(value.Pointer())
|
|
||||||
return f.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *errorInfo) equal(t0 *errorInfo) bool {
|
|
||||||
if t0 == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return t.satisfierName() == t0.satisfierName()
|
|
||||||
}
|
|
||||||
|
|
||||||
type errorTest struct {
|
|
||||||
err error
|
|
||||||
message string
|
|
||||||
errInfo *errorInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func deferredAnnotatef(err error, format string, args ...interface{}) error {
|
|
||||||
errors.DeferredAnnotatef(&err, format, args...)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustSatisfy(c *gc.C, err error, errInfo *errorInfo) {
|
|
||||||
if errInfo != nil {
|
|
||||||
msg := fmt.Sprintf("%#v must satisfy %v", err, errInfo.satisfierName())
|
|
||||||
c.Check(err, jc.Satisfies, errInfo.satisfier, gc.Commentf(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustNotSatisfy(c *gc.C, err error, errInfo *errorInfo) {
|
|
||||||
if errInfo != nil {
|
|
||||||
msg := fmt.Sprintf("%#v must not satisfy %v", err, errInfo.satisfierName())
|
|
||||||
c.Check(err, gc.Not(jc.Satisfies), errInfo.satisfier, gc.Commentf(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkErrorMatches(c *gc.C, err error, message string, errInfo *errorInfo) {
|
|
||||||
if message == "<nil>" {
|
|
||||||
c.Check(err, gc.IsNil)
|
|
||||||
c.Check(errInfo, gc.IsNil)
|
|
||||||
} else {
|
|
||||||
c.Check(err, gc.ErrorMatches, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runErrorTests(c *gc.C, errorTests []errorTest, checkMustSatisfy bool) {
|
|
||||||
for i, t := range errorTests {
|
|
||||||
c.Logf("test %d: %T: %v", i, t.err, t.err)
|
|
||||||
checkErrorMatches(c, t.err, t.message, t.errInfo)
|
|
||||||
if checkMustSatisfy {
|
|
||||||
mustSatisfy(c, t.err, t.errInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check all other satisfiers to make sure none match.
|
|
||||||
for _, otherErrInfo := range allErrors {
|
|
||||||
if checkMustSatisfy && otherErrInfo.equal(t.errInfo) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mustNotSatisfy(c, t.err, otherErrInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*errorTypeSuite) TestDeferredAnnotatef(c *gc.C) {
|
|
||||||
// Ensure DeferredAnnotatef annotates the errors.
|
|
||||||
errorTests := []errorTest{}
|
|
||||||
for _, errInfo := range allErrors {
|
|
||||||
errorTests = append(errorTests, []errorTest{{
|
|
||||||
deferredAnnotatef(nil, "comment"),
|
|
||||||
"<nil>",
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
deferredAnnotatef(stderrors.New("blast"), "comment"),
|
|
||||||
"comment: blast",
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
deferredAnnotatef(errInfo.argsConstructor("foo %d", 42), "comment %d", 69),
|
|
||||||
"comment 69: foo 42" + errInfo.suffix,
|
|
||||||
errInfo,
|
|
||||||
}, {
|
|
||||||
deferredAnnotatef(errInfo.argsConstructor(""), "comment"),
|
|
||||||
"comment: " + errInfo.suffix,
|
|
||||||
errInfo,
|
|
||||||
}, {
|
|
||||||
deferredAnnotatef(errInfo.wrapConstructor(stderrors.New("pow!"), "woo"), "comment"),
|
|
||||||
"comment: woo: pow!",
|
|
||||||
errInfo,
|
|
||||||
}}...)
|
|
||||||
}
|
|
||||||
|
|
||||||
runErrorTests(c, errorTests, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*errorTypeSuite) TestAllErrors(c *gc.C) {
|
|
||||||
errorTests := []errorTest{}
|
|
||||||
for _, errInfo := range allErrors {
|
|
||||||
errorTests = append(errorTests, []errorTest{{
|
|
||||||
nil,
|
|
||||||
"<nil>",
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
errInfo.argsConstructor("foo %d", 42),
|
|
||||||
"foo 42" + errInfo.suffix,
|
|
||||||
errInfo,
|
|
||||||
}, {
|
|
||||||
errInfo.argsConstructor(""),
|
|
||||||
errInfo.suffix,
|
|
||||||
errInfo,
|
|
||||||
}, {
|
|
||||||
errInfo.wrapConstructor(stderrors.New("pow!"), "prefix"),
|
|
||||||
"prefix: pow!",
|
|
||||||
errInfo,
|
|
||||||
}, {
|
|
||||||
errInfo.wrapConstructor(stderrors.New("pow!"), ""),
|
|
||||||
"pow!",
|
|
||||||
errInfo,
|
|
||||||
}, {
|
|
||||||
errInfo.wrapConstructor(nil, "prefix"),
|
|
||||||
"prefix",
|
|
||||||
errInfo,
|
|
||||||
}}...)
|
|
||||||
}
|
|
||||||
|
|
||||||
runErrorTests(c, errorTests, true)
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
// Copyright 2013, 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleTrace() {
|
|
||||||
var err1 error = fmt.Errorf("something wicked this way comes")
|
|
||||||
var err2 error = nil
|
|
||||||
|
|
||||||
// Tracing a non nil error will return an error
|
|
||||||
fmt.Println(errors.Trace(err1))
|
|
||||||
// Tracing nil will return nil
|
|
||||||
fmt.Println(errors.Trace(err2))
|
|
||||||
|
|
||||||
// Output: something wicked this way comes
|
|
||||||
// <nil>
|
|
||||||
}
|
|
12
vendor/src/github.com/juju/errors/export_test.go
vendored
12
vendor/src/github.com/juju/errors/export_test.go
vendored
@ -1,12 +0,0 @@
|
|||||||
// Copyright 2013, 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors
|
|
||||||
|
|
||||||
// Since variables are declared before the init block, in order to get the goPath
|
|
||||||
// we need to return it rather than just reference it.
|
|
||||||
func GoPath() string {
|
|
||||||
return goPath
|
|
||||||
}
|
|
||||||
|
|
||||||
var TrimGoPath = trimGoPath
|
|
330
vendor/src/github.com/juju/errors/functions.go
vendored
330
vendor/src/github.com/juju/errors/functions.go
vendored
@ -1,330 +0,0 @@
|
|||||||
// Copyright 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// New is a drop in replacement for the standard libary errors module that records
|
|
||||||
// the location that the error is created.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// return errors.New("validation failed")
|
|
||||||
//
|
|
||||||
func New(message string) error {
|
|
||||||
err := &Err{message: message}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf creates a new annotated error and records the location that the
|
|
||||||
// error is created. This should be a drop in replacement for fmt.Errorf.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// return errors.Errorf("validation failed: %s", message)
|
|
||||||
//
|
|
||||||
func Errorf(format string, args ...interface{}) error {
|
|
||||||
err := &Err{message: fmt.Sprintf(format, args...)}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trace adds the location of the Trace call to the stack. The Cause of the
|
|
||||||
// resulting error is the same as the error parameter. If the other error is
|
|
||||||
// nil, the result will be nil.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// if err := SomeFunc(); err != nil {
|
|
||||||
// return errors.Trace(err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func Trace(other error) error {
|
|
||||||
if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := &Err{previous: other, cause: Cause(other)}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Annotate is used to add extra context to an existing error. The location of
|
|
||||||
// the Annotate call is recorded with the annotations. The file, line and
|
|
||||||
// function are also recorded.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// if err := SomeFunc(); err != nil {
|
|
||||||
// return errors.Annotate(err, "failed to frombulate")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func Annotate(other error, message string) error {
|
|
||||||
if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := &Err{
|
|
||||||
previous: other,
|
|
||||||
cause: Cause(other),
|
|
||||||
message: message,
|
|
||||||
}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Annotatef is used to add extra context to an existing error. The location of
|
|
||||||
// the Annotate call is recorded with the annotations. The file, line and
|
|
||||||
// function are also recorded.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// if err := SomeFunc(); err != nil {
|
|
||||||
// return errors.Annotatef(err, "failed to frombulate the %s", arg)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func Annotatef(other error, format string, args ...interface{}) error {
|
|
||||||
if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := &Err{
|
|
||||||
previous: other,
|
|
||||||
cause: Cause(other),
|
|
||||||
message: fmt.Sprintf(format, args...),
|
|
||||||
}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeferredAnnotatef annotates the given error (when it is not nil) with the given
|
|
||||||
// format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
|
|
||||||
// does nothing. This method is used in a defer statement in order to annotate any
|
|
||||||
// resulting error with the same message.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
|
|
||||||
//
|
|
||||||
func DeferredAnnotatef(err *error, format string, args ...interface{}) {
|
|
||||||
if *err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
newErr := &Err{
|
|
||||||
message: fmt.Sprintf(format, args...),
|
|
||||||
cause: Cause(*err),
|
|
||||||
previous: *err,
|
|
||||||
}
|
|
||||||
newErr.SetLocation(1)
|
|
||||||
*err = newErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap changes the Cause of the error. The location of the Wrap call is also
|
|
||||||
// stored in the error stack.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// if err := SomeFunc(); err != nil {
|
|
||||||
// newErr := &packageError{"more context", private_value}
|
|
||||||
// return errors.Wrap(err, newErr)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func Wrap(other, newDescriptive error) error {
|
|
||||||
err := &Err{
|
|
||||||
previous: other,
|
|
||||||
cause: newDescriptive,
|
|
||||||
}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapf changes the Cause of the error, and adds an annotation. The location
|
|
||||||
// of the Wrap call is also stored in the error stack.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// if err := SomeFunc(); err != nil {
|
|
||||||
// return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func Wrapf(other, newDescriptive error, format string, args ...interface{}) error {
|
|
||||||
err := &Err{
|
|
||||||
message: fmt.Sprintf(format, args...),
|
|
||||||
previous: other,
|
|
||||||
cause: newDescriptive,
|
|
||||||
}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mask masks the given error with the given format string and arguments (like
|
|
||||||
// fmt.Sprintf), returning a new error that maintains the error stack, but
|
|
||||||
// hides the underlying error type. The error string still contains the full
|
|
||||||
// annotations. If you want to hide the annotations, call Wrap.
|
|
||||||
func Maskf(other error, format string, args ...interface{}) error {
|
|
||||||
if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := &Err{
|
|
||||||
message: fmt.Sprintf(format, args...),
|
|
||||||
previous: other,
|
|
||||||
}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mask hides the underlying error type, and records the location of the masking.
|
|
||||||
func Mask(other error) error {
|
|
||||||
if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := &Err{
|
|
||||||
previous: other,
|
|
||||||
}
|
|
||||||
err.SetLocation(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cause returns the cause of the given error. This will be either the
|
|
||||||
// original error, or the result of a Wrap or Mask call.
|
|
||||||
//
|
|
||||||
// Cause is the usual way to diagnose errors that may have been wrapped by
|
|
||||||
// the other errors functions.
|
|
||||||
func Cause(err error) error {
|
|
||||||
var diag error
|
|
||||||
if err, ok := err.(causer); ok {
|
|
||||||
diag = err.Cause()
|
|
||||||
}
|
|
||||||
if diag != nil {
|
|
||||||
return diag
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type causer interface {
|
|
||||||
Cause() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type wrapper interface {
|
|
||||||
// Message returns the top level error message,
|
|
||||||
// not including the message from the Previous
|
|
||||||
// error.
|
|
||||||
Message() string
|
|
||||||
|
|
||||||
// Underlying returns the Previous error, or nil
|
|
||||||
// if there is none.
|
|
||||||
Underlying() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type locationer interface {
|
|
||||||
Location() (string, int)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ wrapper = (*Err)(nil)
|
|
||||||
_ locationer = (*Err)(nil)
|
|
||||||
_ causer = (*Err)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Details returns information about the stack of errors wrapped by err, in
|
|
||||||
// the format:
|
|
||||||
//
|
|
||||||
// [{filename:99: error one} {otherfile:55: cause of error one}]
|
|
||||||
//
|
|
||||||
// This is a terse alternative to ErrorStack as it returns a single line.
|
|
||||||
func Details(err error) string {
|
|
||||||
if err == nil {
|
|
||||||
return "[]"
|
|
||||||
}
|
|
||||||
var s []byte
|
|
||||||
s = append(s, '[')
|
|
||||||
for {
|
|
||||||
s = append(s, '{')
|
|
||||||
if err, ok := err.(locationer); ok {
|
|
||||||
file, line := err.Location()
|
|
||||||
if file != "" {
|
|
||||||
s = append(s, fmt.Sprintf("%s:%d", file, line)...)
|
|
||||||
s = append(s, ": "...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cerr, ok := err.(wrapper); ok {
|
|
||||||
s = append(s, cerr.Message()...)
|
|
||||||
err = cerr.Underlying()
|
|
||||||
} else {
|
|
||||||
s = append(s, err.Error()...)
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
s = append(s, '}')
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
s = append(s, ' ')
|
|
||||||
}
|
|
||||||
s = append(s, ']')
|
|
||||||
return string(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorStack returns a string representation of the annotated error. If the
|
|
||||||
// error passed as the parameter is not an annotated error, the result is
|
|
||||||
// simply the result of the Error() method on that error.
|
|
||||||
//
|
|
||||||
// If the error is an annotated error, a multi-line string is returned where
|
|
||||||
// each line represents one entry in the annotation stack. The full filename
|
|
||||||
// from the call stack is used in the output.
|
|
||||||
//
|
|
||||||
// first error
|
|
||||||
// github.com/juju/errors/annotation_test.go:193:
|
|
||||||
// github.com/juju/errors/annotation_test.go:194: annotation
|
|
||||||
// github.com/juju/errors/annotation_test.go:195:
|
|
||||||
// github.com/juju/errors/annotation_test.go:196: more context
|
|
||||||
// github.com/juju/errors/annotation_test.go:197:
|
|
||||||
func ErrorStack(err error) string {
|
|
||||||
return strings.Join(errorStack(err), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorStack(err error) []string {
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want the first error first
|
|
||||||
var lines []string
|
|
||||||
for {
|
|
||||||
var buff []byte
|
|
||||||
if err, ok := err.(locationer); ok {
|
|
||||||
file, line := err.Location()
|
|
||||||
// Strip off the leading GOPATH/src path elements.
|
|
||||||
file = trimGoPath(file)
|
|
||||||
if file != "" {
|
|
||||||
buff = append(buff, fmt.Sprintf("%s:%d", file, line)...)
|
|
||||||
buff = append(buff, ": "...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cerr, ok := err.(wrapper); ok {
|
|
||||||
message := cerr.Message()
|
|
||||||
buff = append(buff, message...)
|
|
||||||
// If there is a cause for this error, and it is different to the cause
|
|
||||||
// of the underlying error, then output the error string in the stack trace.
|
|
||||||
var cause error
|
|
||||||
if err1, ok := err.(causer); ok {
|
|
||||||
cause = err1.Cause()
|
|
||||||
}
|
|
||||||
err = cerr.Underlying()
|
|
||||||
if cause != nil && !sameError(Cause(err), cause) {
|
|
||||||
if message != "" {
|
|
||||||
buff = append(buff, ": "...)
|
|
||||||
}
|
|
||||||
buff = append(buff, cause.Error()...)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buff = append(buff, err.Error()...)
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
lines = append(lines, string(buff))
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// reverse the lines to get the original error, which was at the end of
|
|
||||||
// the list, back to the start.
|
|
||||||
var result []string
|
|
||||||
for i := len(lines); i > 0; i-- {
|
|
||||||
result = append(result, lines[i-1])
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
305
vendor/src/github.com/juju/errors/functions_test.go
vendored
305
vendor/src/github.com/juju/errors/functions_test.go
vendored
@ -1,305 +0,0 @@
|
|||||||
// Copyright 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
jc "github.com/juju/testing/checkers"
|
|
||||||
gc "gopkg.in/check.v1"
|
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type functionSuite struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = gc.Suite(&functionSuite{})
|
|
||||||
|
|
||||||
func (*functionSuite) TestNew(c *gc.C) {
|
|
||||||
err := errors.New("testing") //err newTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "testing")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, err)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["newTest"].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestErrorf(c *gc.C) {
|
|
||||||
err := errors.Errorf("testing %d", 42) //err errorfTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "testing 42")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, err)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["errorfTest"].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestTrace(c *gc.C) {
|
|
||||||
first := errors.New("first")
|
|
||||||
err := errors.Trace(first) //err traceTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "first")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, first)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["traceTest"].String())
|
|
||||||
|
|
||||||
c.Assert(errors.Trace(nil), gc.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestAnnotate(c *gc.C) {
|
|
||||||
first := errors.New("first")
|
|
||||||
err := errors.Annotate(first, "annotation") //err annotateTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "annotation: first")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, first)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["annotateTest"].String())
|
|
||||||
|
|
||||||
c.Assert(errors.Annotate(nil, "annotate"), gc.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestAnnotatef(c *gc.C) {
|
|
||||||
first := errors.New("first")
|
|
||||||
err := errors.Annotatef(first, "annotation %d", 2) //err annotatefTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "annotation 2: first")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, first)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["annotatefTest"].String())
|
|
||||||
|
|
||||||
c.Assert(errors.Annotatef(nil, "annotate"), gc.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestDeferredAnnotatef(c *gc.C) {
|
|
||||||
// NOTE: this test fails with gccgo
|
|
||||||
if runtime.Compiler == "gccgo" {
|
|
||||||
c.Skip("gccgo can't determine the location")
|
|
||||||
}
|
|
||||||
first := errors.New("first")
|
|
||||||
test := func() (err error) {
|
|
||||||
defer errors.DeferredAnnotatef(&err, "deferred %s", "annotate")
|
|
||||||
return first
|
|
||||||
} //err deferredAnnotate
|
|
||||||
err := test()
|
|
||||||
c.Assert(err.Error(), gc.Equals, "deferred annotate: first")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, first)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["deferredAnnotate"].String())
|
|
||||||
|
|
||||||
err = nil
|
|
||||||
errors.DeferredAnnotatef(&err, "deferred %s", "annotate")
|
|
||||||
c.Assert(err, gc.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestWrap(c *gc.C) {
|
|
||||||
first := errors.New("first") //err wrapFirst
|
|
||||||
detailed := errors.New("detailed")
|
|
||||||
err := errors.Wrap(first, detailed) //err wrapTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "detailed")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, detailed)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapFirst"].String())
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapTest"].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestWrapOfNil(c *gc.C) {
|
|
||||||
detailed := errors.New("detailed")
|
|
||||||
err := errors.Wrap(nil, detailed) //err nilWrapTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "detailed")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, detailed)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["nilWrapTest"].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestWrapf(c *gc.C) {
|
|
||||||
first := errors.New("first") //err wrapfFirst
|
|
||||||
detailed := errors.New("detailed")
|
|
||||||
err := errors.Wrapf(first, detailed, "value %d", 42) //err wrapfTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "value 42: detailed")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, detailed)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapfFirst"].String())
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["wrapfTest"].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestWrapfOfNil(c *gc.C) {
|
|
||||||
detailed := errors.New("detailed")
|
|
||||||
err := errors.Wrapf(nil, detailed, "value %d", 42) //err nilWrapfTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "value 42: detailed")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, detailed)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["nilWrapfTest"].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestMask(c *gc.C) {
|
|
||||||
first := errors.New("first")
|
|
||||||
err := errors.Mask(first) //err maskTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "first")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, err)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["maskTest"].String())
|
|
||||||
|
|
||||||
c.Assert(errors.Mask(nil), gc.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestMaskf(c *gc.C) {
|
|
||||||
first := errors.New("first")
|
|
||||||
err := errors.Maskf(first, "masked %d", 42) //err maskfTest
|
|
||||||
c.Assert(err.Error(), gc.Equals, "masked 42: first")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, err)
|
|
||||||
c.Assert(errors.Details(err), jc.Contains, tagToLocation["maskfTest"].String())
|
|
||||||
|
|
||||||
c.Assert(errors.Maskf(nil, "mask"), gc.IsNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestCause(c *gc.C) {
|
|
||||||
c.Assert(errors.Cause(nil), gc.IsNil)
|
|
||||||
c.Assert(errors.Cause(someErr), gc.Equals, someErr)
|
|
||||||
|
|
||||||
fmtErr := fmt.Errorf("simple")
|
|
||||||
c.Assert(errors.Cause(fmtErr), gc.Equals, fmtErr)
|
|
||||||
|
|
||||||
err := errors.Wrap(someErr, fmtErr)
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, fmtErr)
|
|
||||||
|
|
||||||
err = errors.Annotate(err, "annotated")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, fmtErr)
|
|
||||||
|
|
||||||
err = errors.Maskf(err, "maksed")
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, err)
|
|
||||||
|
|
||||||
// Look for a file that we know isn't there.
|
|
||||||
dir := c.MkDir()
|
|
||||||
_, err = os.Stat(filepath.Join(dir, "not-there"))
|
|
||||||
c.Assert(os.IsNotExist(err), jc.IsTrue)
|
|
||||||
|
|
||||||
err = errors.Annotatef(err, "wrap it")
|
|
||||||
// Now the error itself isn't a 'IsNotExist'.
|
|
||||||
c.Assert(os.IsNotExist(err), jc.IsFalse)
|
|
||||||
// However if we use the Check method, it is.
|
|
||||||
c.Assert(os.IsNotExist(errors.Cause(err)), jc.IsTrue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *functionSuite) TestDetails(c *gc.C) {
|
|
||||||
if runtime.Compiler == "gccgo" {
|
|
||||||
c.Skip("gccgo can't determine the location")
|
|
||||||
}
|
|
||||||
c.Assert(errors.Details(nil), gc.Equals, "[]")
|
|
||||||
|
|
||||||
otherErr := fmt.Errorf("other")
|
|
||||||
checkDetails(c, otherErr, "[{other}]")
|
|
||||||
|
|
||||||
err0 := newEmbed("foo") //err TestStack#0
|
|
||||||
checkDetails(c, err0, "[{$TestStack#0$: foo}]")
|
|
||||||
|
|
||||||
err1 := errors.Annotate(err0, "bar") //err TestStack#1
|
|
||||||
checkDetails(c, err1, "[{$TestStack#1$: bar} {$TestStack#0$: foo}]")
|
|
||||||
|
|
||||||
err2 := errors.Trace(err1) //err TestStack#2
|
|
||||||
checkDetails(c, err2, "[{$TestStack#2$: } {$TestStack#1$: bar} {$TestStack#0$: foo}]")
|
|
||||||
}
|
|
||||||
|
|
||||||
type tracer interface {
|
|
||||||
StackTrace() []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*functionSuite) TestErrorStack(c *gc.C) {
|
|
||||||
for i, test := range []struct {
|
|
||||||
message string
|
|
||||||
generator func() error
|
|
||||||
expected string
|
|
||||||
tracer bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
message: "nil",
|
|
||||||
generator: func() error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
message: "raw error",
|
|
||||||
generator: func() error {
|
|
||||||
return fmt.Errorf("raw")
|
|
||||||
},
|
|
||||||
expected: "raw",
|
|
||||||
}, {
|
|
||||||
message: "single error stack",
|
|
||||||
generator: func() error {
|
|
||||||
return errors.New("first error") //err single
|
|
||||||
},
|
|
||||||
expected: "$single$: first error",
|
|
||||||
tracer: true,
|
|
||||||
}, {
|
|
||||||
message: "annotated error",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.New("first error") //err annotated-0
|
|
||||||
return errors.Annotate(err, "annotation") //err annotated-1
|
|
||||||
},
|
|
||||||
expected: "" +
|
|
||||||
"$annotated-0$: first error\n" +
|
|
||||||
"$annotated-1$: annotation",
|
|
||||||
tracer: true,
|
|
||||||
}, {
|
|
||||||
message: "wrapped error",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.New("first error") //err wrapped-0
|
|
||||||
return errors.Wrap(err, newError("detailed error")) //err wrapped-1
|
|
||||||
},
|
|
||||||
expected: "" +
|
|
||||||
"$wrapped-0$: first error\n" +
|
|
||||||
"$wrapped-1$: detailed error",
|
|
||||||
tracer: true,
|
|
||||||
}, {
|
|
||||||
message: "annotated wrapped error",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.Errorf("first error") //err ann-wrap-0
|
|
||||||
err = errors.Wrap(err, fmt.Errorf("detailed error")) //err ann-wrap-1
|
|
||||||
return errors.Annotatef(err, "annotated") //err ann-wrap-2
|
|
||||||
},
|
|
||||||
expected: "" +
|
|
||||||
"$ann-wrap-0$: first error\n" +
|
|
||||||
"$ann-wrap-1$: detailed error\n" +
|
|
||||||
"$ann-wrap-2$: annotated",
|
|
||||||
tracer: true,
|
|
||||||
}, {
|
|
||||||
message: "traced, and annotated",
|
|
||||||
generator: func() error {
|
|
||||||
err := errors.New("first error") //err stack-0
|
|
||||||
err = errors.Trace(err) //err stack-1
|
|
||||||
err = errors.Annotate(err, "some context") //err stack-2
|
|
||||||
err = errors.Trace(err) //err stack-3
|
|
||||||
err = errors.Annotate(err, "more context") //err stack-4
|
|
||||||
return errors.Trace(err) //err stack-5
|
|
||||||
},
|
|
||||||
expected: "" +
|
|
||||||
"$stack-0$: first error\n" +
|
|
||||||
"$stack-1$: \n" +
|
|
||||||
"$stack-2$: some context\n" +
|
|
||||||
"$stack-3$: \n" +
|
|
||||||
"$stack-4$: more context\n" +
|
|
||||||
"$stack-5$: ",
|
|
||||||
tracer: true,
|
|
||||||
}, {
|
|
||||||
message: "uncomparable, wrapped with a value error",
|
|
||||||
generator: func() error {
|
|
||||||
err := newNonComparableError("first error") //err mixed-0
|
|
||||||
err = errors.Trace(err) //err mixed-1
|
|
||||||
err = errors.Wrap(err, newError("value error")) //err mixed-2
|
|
||||||
err = errors.Maskf(err, "masked") //err mixed-3
|
|
||||||
err = errors.Annotate(err, "more context") //err mixed-4
|
|
||||||
return errors.Trace(err) //err mixed-5
|
|
||||||
},
|
|
||||||
expected: "" +
|
|
||||||
"first error\n" +
|
|
||||||
"$mixed-1$: \n" +
|
|
||||||
"$mixed-2$: value error\n" +
|
|
||||||
"$mixed-3$: masked\n" +
|
|
||||||
"$mixed-4$: more context\n" +
|
|
||||||
"$mixed-5$: ",
|
|
||||||
tracer: true,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
c.Logf("%v: %s", i, test.message)
|
|
||||||
err := test.generator()
|
|
||||||
expected := replaceLocations(test.expected)
|
|
||||||
stack := errors.ErrorStack(err)
|
|
||||||
ok := c.Check(stack, gc.Equals, expected)
|
|
||||||
if !ok {
|
|
||||||
c.Logf("%#v", err)
|
|
||||||
}
|
|
||||||
tracer, ok := err.(tracer)
|
|
||||||
c.Check(ok, gc.Equals, test.tracer)
|
|
||||||
if ok {
|
|
||||||
stackTrace := tracer.StackTrace()
|
|
||||||
c.Check(stackTrace, gc.DeepEquals, strings.Split(stack, "\n"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
// Copyright 2013, 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
gc "gopkg.in/check.v1"
|
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test(t *testing.T) {
|
|
||||||
gc.TestingT(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkDetails(c *gc.C, err error, details string) {
|
|
||||||
c.Assert(err, gc.NotNil)
|
|
||||||
expectedDetails := replaceLocations(details)
|
|
||||||
c.Assert(errors.Details(err), gc.Equals, expectedDetails)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkErr(c *gc.C, err, cause error, msg string, details string) {
|
|
||||||
c.Assert(err, gc.NotNil)
|
|
||||||
c.Assert(err.Error(), gc.Equals, msg)
|
|
||||||
c.Assert(errors.Cause(err), gc.Equals, cause)
|
|
||||||
expectedDetails := replaceLocations(details)
|
|
||||||
c.Assert(errors.Details(err), gc.Equals, expectedDetails)
|
|
||||||
}
|
|
||||||
|
|
||||||
func replaceLocations(line string) string {
|
|
||||||
result := ""
|
|
||||||
for {
|
|
||||||
i := strings.Index(line, "$")
|
|
||||||
if i == -1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
result += line[0:i]
|
|
||||||
line = line[i+1:]
|
|
||||||
i = strings.Index(line, "$")
|
|
||||||
if i == -1 {
|
|
||||||
panic("no second $")
|
|
||||||
}
|
|
||||||
result += location(line[0:i]).String()
|
|
||||||
line = line[i+1:]
|
|
||||||
}
|
|
||||||
result += line
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func location(tag string) Location {
|
|
||||||
loc, ok := tagToLocation[tag]
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Sprintf("tag %q not found", tag))
|
|
||||||
}
|
|
||||||
return loc
|
|
||||||
}
|
|
||||||
|
|
||||||
type Location struct {
|
|
||||||
file string
|
|
||||||
line int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (loc Location) String() string {
|
|
||||||
return fmt.Sprintf("%s:%d", loc.file, loc.line)
|
|
||||||
}
|
|
||||||
|
|
||||||
var tagToLocation = make(map[string]Location)
|
|
||||||
|
|
||||||
func setLocationsForErrorTags(filename string) {
|
|
||||||
data, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
filename = "github.com/juju/errors/" + filename
|
|
||||||
lines := strings.Split(string(data), "\n")
|
|
||||||
for i, line := range lines {
|
|
||||||
if j := strings.Index(line, "//err "); j >= 0 {
|
|
||||||
tag := line[j+len("//err "):]
|
|
||||||
if _, found := tagToLocation[tag]; found {
|
|
||||||
panic(fmt.Sprintf("tag %q already processed previously", tag))
|
|
||||||
}
|
|
||||||
tagToLocation[tag] = Location{file: filename, line: i + 1}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
setLocationsForErrorTags("error_test.go")
|
|
||||||
setLocationsForErrorTags("functions_test.go")
|
|
||||||
}
|
|
35
vendor/src/github.com/juju/errors/path.go
vendored
35
vendor/src/github.com/juju/errors/path.go
vendored
@ -1,35 +0,0 @@
|
|||||||
// Copyright 2013, 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// prefixSize is used internally to trim the user specific path from the
|
|
||||||
// front of the returned filenames from the runtime call stack.
|
|
||||||
var prefixSize int
|
|
||||||
|
|
||||||
// goPath is the deduced path based on the location of this file as compiled.
|
|
||||||
var goPath string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
_, file, _, ok := runtime.Caller(0)
|
|
||||||
if ok {
|
|
||||||
// We know that the end of the file should be:
|
|
||||||
// github.com/juju/errors/path.go
|
|
||||||
size := len(file)
|
|
||||||
suffix := len("github.com/juju/errors/path.go")
|
|
||||||
goPath = file[:size-suffix]
|
|
||||||
prefixSize = len(goPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimGoPath(filename string) string {
|
|
||||||
if strings.HasPrefix(filename, goPath) {
|
|
||||||
return filename[prefixSize:]
|
|
||||||
}
|
|
||||||
return filename
|
|
||||||
}
|
|
29
vendor/src/github.com/juju/errors/path_test.go
vendored
29
vendor/src/github.com/juju/errors/path_test.go
vendored
@ -1,29 +0,0 @@
|
|||||||
// Copyright 2013, 2014 Canonical Ltd.
|
|
||||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
|
||||||
|
|
||||||
package errors_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
|
|
||||||
gc "gopkg.in/check.v1"
|
|
||||||
|
|
||||||
"github.com/juju/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type pathSuite struct{}
|
|
||||||
|
|
||||||
var _ = gc.Suite(&pathSuite{})
|
|
||||||
|
|
||||||
func (*pathSuite) TestGoPathSet(c *gc.C) {
|
|
||||||
c.Assert(errors.GoPath(), gc.Not(gc.Equals), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*pathSuite) TestTrimGoPath(c *gc.C) {
|
|
||||||
relativeImport := "github.com/foo/bar/baz.go"
|
|
||||||
filename := path.Join(errors.GoPath(), relativeImport)
|
|
||||||
c.Assert(errors.TrimGoPath(filename), gc.Equals, relativeImport)
|
|
||||||
|
|
||||||
absoluteImport := "/usr/share/foo/bar/baz.go"
|
|
||||||
c.Assert(errors.TrimGoPath(absoluteImport), gc.Equals, absoluteImport)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user