diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 53ce7e3c04c..62cc20c9bef 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/jbenet/go-ipfs", - "GoVersion": "go1.3", + "GoVersion": "go1.3.3", "Packages": [ "./..." ], @@ -60,6 +60,14 @@ "ImportPath": "github.com/coreos/go-semver/semver", "Rev": "6fe83ccda8fb9b7549c9ab4ba47f47858bc950aa" }, + { + "ImportPath": "github.com/facebookgo/stack", + "Rev": "4da6d991fc3c389efa512151354d643eb5fae4e2" + }, + { + "ImportPath": "github.com/facebookgo/stackerr", + "Rev": "060fbf9364c89acd41bf710e9e92915a90e7a5b5" + }, { "ImportPath": "github.com/gonuts/flag", "Rev": "741a6cbd37a30dedc93f817e7de6aaf0ca38a493" diff --git a/Godeps/_workspace/src/github.com/facebookgo/stack/.travis.yml b/Godeps/_workspace/src/github.com/facebookgo/stack/.travis.yml new file mode 100644 index 00000000000..2cc62c5e853 --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stack/.travis.yml @@ -0,0 +1,24 @@ +language: go + +go: + - 1.2 + - 1.3 + +matrix: + fast_finish: true + +before_install: + - go get -v code.google.com/p/go.tools/cmd/vet + - go get -v github.com/golang/lint/golint + - go get -v code.google.com/p/go.tools/cmd/cover + +install: + - go install -race -v std + - go get -race -t -v ./... + - go install -race -v ./... + +script: + - go vet ./... + - $HOME/gopath/bin/golint . + - go test -cpu=2 -race -v ./... + - go test -cpu=2 -covermode=atomic ./... diff --git a/Godeps/_workspace/src/github.com/facebookgo/stack/readme.md b/Godeps/_workspace/src/github.com/facebookgo/stack/readme.md new file mode 100644 index 00000000000..680f7ad4188 --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stack/readme.md @@ -0,0 +1,4 @@ +stack [![Build Status](https://secure.travis-ci.org/facebookgo/stack.png)](http://travis-ci.org/facebookgo/stack) +===== + +Documentation: https://godoc.org/github.com/facebookgo/stack diff --git a/Godeps/_workspace/src/github.com/facebookgo/stack/stack.go b/Godeps/_workspace/src/github.com/facebookgo/stack/stack.go new file mode 100644 index 00000000000..c045daf57da --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stack/stack.go @@ -0,0 +1,197 @@ +// Package stack provides utilities to capture and pass around stack traces. +// +// This is useful for building errors that know where they originated from, to +// track where a certain log event occured and so on. +package stack + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +const maxStackSize = 32 + +// Frame identifies a file, line & function name in the stack. +type Frame struct { + File string + Line int + Name string +} + +// String provides the standard file:line representation. +func (f Frame) String() string { + return fmt.Sprintf("%s:%d %s", f.File, f.Line, f.Name) +} + +// Stack represents an ordered set of Frames. +type Stack []Frame + +// String provides the standard multi-line stack trace. +func (s Stack) String() string { + var b bytes.Buffer + writeStack(&b, s) + return b.String() +} + +// Multi represents a number of Stacks. This is useful to allow tracking a +// value as it travels thru code. +type Multi struct { + stacks []Stack +} + +// Stacks returns the tracked Stacks. +func (m *Multi) Stacks() []Stack { + return m.stacks +} + +// Add the given Stack to this Multi. +func (m *Multi) Add(s Stack) { + m.stacks = append(m.stacks, s) +} + +// AddCallers adds the Callers Stack to this Multi. The argument skip is +// the number of stack frames to ascend, with 0 identifying the caller of +// Callers. +func (m *Multi) AddCallers(skip int) { + m.Add(Callers(skip + 1)) +} + +// String provides a human readable multi-line stack trace. +func (m *Multi) String() string { + var b bytes.Buffer + for i, s := range m.stacks { + if i != 0 { + fmt.Fprintf(&b, "\n(Stack %d)\n", i+1) + } + writeStack(&b, s) + } + return b.String() +} + +// Caller returns a single Frame for the caller. The argument skip is the +// number of stack frames to ascend, with 0 identifying the caller of Callers. +func Caller(skip int) Frame { + pc, file, line, _ := runtime.Caller(skip + 1) + fun := runtime.FuncForPC(pc) + return Frame{ + File: StripGOPATH(file), + Line: line, + Name: StripPackage(fun.Name()), + } +} + +// Callers returns a Stack of Frames for the callers. The argument skip is the +// number of stack frames to ascend, with 0 identifying the caller of Callers. +func Callers(skip int) Stack { + pcs := make([]uintptr, maxStackSize) + num := runtime.Callers(skip+2, pcs) + stack := make(Stack, num) + for i, pc := range pcs[:num] { + fun := runtime.FuncForPC(pc) + file, line := fun.FileLine(pc) + stack[i].File = StripGOPATH(file) + stack[i].Line = line + stack[i].Name = StripPackage(fun.Name()) + } + return stack +} + +// CallersMulti returns a Multi which includes one Stack for the +// current callers. The argument skip is the number of stack frames to ascend, +// with 0 identifying the caller of CallersMulti. +func CallersMulti(skip int) *Multi { + m := new(Multi) + m.AddCallers(skip + 1) + return m +} + +func writeStack(b *bytes.Buffer, s Stack) { + var width int + for _, f := range s { + if l := len(f.File) + numDigits(f.Line) + 1; l > width { + width = l + } + } + last := len(s) - 1 + for i, f := range s { + b.WriteString(f.File) + b.WriteRune(rune(':')) + n, _ := fmt.Fprintf(b, "%d", f.Line) + for i := width - len(f.File) - n; i != 0; i-- { + b.WriteRune(rune(' ')) + } + b.WriteString(f.Name) + if i != last { + b.WriteRune(rune('\n')) + } + } +} + +func numDigits(i int) int { + var n int + for { + n++ + i = i / 10 + if i == 0 { + return n + } + } +} + +// This can be set by a build script. It will be the colon separated equivalent +// of the environment variable. +var gopath string + +// This is the processed version based on either the above variable set by the +// build or from the GOPATH environment variable. +var gopaths []string + +func init() { + // prefer the variable set at build time, otherwise fallback to the + // environment variable. + if gopath == "" { + gopath = os.Getenv("GOPATH") + } + + for _, p := range strings.Split(gopath, ":") { + if p != "" { + gopaths = append(gopaths, filepath.Join(p, "src")+"/") + } + } + + // Also strip GOROOT for maximum cleanliness + gopaths = append(gopaths, filepath.Join(runtime.GOROOT(), "src", "pkg")+"/") +} + +// StripGOPATH strips the GOPATH prefix from the file path f. +// In development, this will be done using the GOPATH environment variable. +// For production builds, where the GOPATH environment will not be set, the +// GOPATH can be included in the binary by passing ldflags, for example: +// +// GO_LDFLAGS="$GO_LDFLAGS -X github.com/facebookgo/stack.gopath $GOPATH" +// go install "-ldflags=$GO_LDFLAGS" my/pkg +func StripGOPATH(f string) string { + for _, p := range gopaths { + if strings.HasPrefix(f, p) { + return f[len(p):] + } + } + return f +} + +// StripPackage strips the package name from the given Func.Name. +func StripPackage(n string) string { + slashI := strings.LastIndex(n, "/") + if slashI == -1 { + slashI = 0 // for built-in packages + } + dotI := strings.Index(n[slashI:], ".") + if dotI == -1 { + return n + } + return n[slashI+dotI+1:] +} diff --git a/Godeps/_workspace/src/github.com/facebookgo/stack/stack_test.go b/Godeps/_workspace/src/github.com/facebookgo/stack/stack_test.go new file mode 100644 index 00000000000..a6bdd880a60 --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stack/stack_test.go @@ -0,0 +1,102 @@ +package stack_test + +import ( + "regexp" + "strings" + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/facebookgo/stack" +) + +func indirect1() stack.Stack { + return stack.Callers(0) +} + +func indirect2() stack.Stack { + return indirect1() +} + +func indirect3() stack.Stack { + return indirect2() +} + +func TestCallers(t *testing.T) { + s := indirect3() + matches := []string{ + "^github.com/facebookgo/stack/stack_test.go:12 +indirect1$", + "^github.com/facebookgo/stack/stack_test.go:16 +indirect2$", + "^github.com/facebookgo/stack/stack_test.go:20 +indirect3$", + "^github.com/facebookgo/stack/stack_test.go:24 +TestCallers$", + } + match(t, s.String(), matches) +} + +func TestCallersMulti(t *testing.T) { + m := stack.CallersMulti(0) + const expected = "github.com/facebookgo/stack/stack_test.go:35 TestCallersMulti" + first := m.Stacks()[0][0].String() + if first != expected { + t.Fatalf(`expected "%s" got "%s"`, expected, first) + } +} + +func TestCallersMultiWithTwo(t *testing.T) { + m := stack.CallersMulti(0) + m.AddCallers(0) + matches := []string{ + "^github.com/facebookgo/stack/stack_test.go:44 +TestCallersMultiWithTwo$", + "", + "", + `^\(Stack 2\)$`, + "^github.com/facebookgo/stack/stack_test.go:46 +TestCallersMultiWithTwo$", + } + match(t, m.String(), matches) +} + +type typ struct{} + +func (m typ) indirect1() stack.Stack { + return stack.Callers(0) +} + +func (m typ) indirect2() stack.Stack { + return m.indirect1() +} + +func (m typ) indirect3() stack.Stack { + return m.indirect2() +} + +func TestCallersWithStruct(t *testing.T) { + var m typ + s := m.indirect3() + matches := []string{ + "^github.com/facebookgo/stack/stack_test.go:59 +typ.indirect1$", + "^github.com/facebookgo/stack/stack_test.go:63 +typ.indirect2$", + "^github.com/facebookgo/stack/stack_test.go:67 +typ.indirect3$", + "^github.com/facebookgo/stack/stack_test.go:72 +TestCallersWithStruct$", + } + match(t, s.String(), matches) +} + +func TestCaller(t *testing.T) { + f := stack.Caller(0) + const expected = "github.com/facebookgo/stack/stack_test.go:83 TestCaller" + if f.String() != expected { + t.Fatalf(`expected "%s" got "%s"`, expected, f) + } +} + +func match(t testing.TB, s string, matches []string) { + lines := strings.Split(s, "\n") + for i, m := range matches { + if !regexp.MustCompile(m).MatchString(lines[i]) { + t.Fatalf( + "did not find expected match \"%s\" on line %d in:\n%s", + m, + i, + s, + ) + } + } +} diff --git a/Godeps/_workspace/src/github.com/facebookgo/stackerr/.travis.yml b/Godeps/_workspace/src/github.com/facebookgo/stackerr/.travis.yml new file mode 100644 index 00000000000..2cc62c5e853 --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stackerr/.travis.yml @@ -0,0 +1,24 @@ +language: go + +go: + - 1.2 + - 1.3 + +matrix: + fast_finish: true + +before_install: + - go get -v code.google.com/p/go.tools/cmd/vet + - go get -v github.com/golang/lint/golint + - go get -v code.google.com/p/go.tools/cmd/cover + +install: + - go install -race -v std + - go get -race -t -v ./... + - go install -race -v ./... + +script: + - go vet ./... + - $HOME/gopath/bin/golint . + - go test -cpu=2 -race -v ./... + - go test -cpu=2 -covermode=atomic ./... diff --git a/Godeps/_workspace/src/github.com/facebookgo/stackerr/readme.md b/Godeps/_workspace/src/github.com/facebookgo/stackerr/readme.md new file mode 100644 index 00000000000..8837ed070b5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stackerr/readme.md @@ -0,0 +1,4 @@ +stackerr [![Build Status](https://secure.travis-ci.org/facebookgo/stackerr.png)](http://travis-ci.org/facebookgo/stackerr) +======== + +Documentation: https://godoc.org/github.com/facebookgo/stackerr diff --git a/Godeps/_workspace/src/github.com/facebookgo/stackerr/stackerr.go b/Godeps/_workspace/src/github.com/facebookgo/stackerr/stackerr.go new file mode 100644 index 00000000000..e5f1dde0537 --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stackerr/stackerr.go @@ -0,0 +1,97 @@ +// Package stackerr provides a way to augment errors with one or more stack +// traces to allow for easier debugging. +package stackerr + +import ( + "errors" + "fmt" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/facebookgo/stack" +) + +// Error provides the wrapper that adds multiple Stacks to an error. Each Stack +// represents a location in code thru which this error was wrapped. +type Error struct { + multiStack *stack.Multi + underlying error +} + +// Error provides a multi line error string that includes the stack trace. +func (e *Error) Error() string { + return fmt.Sprintf("%s\n%s", e.underlying, e.multiStack) +} + +// MultiStack identifies the locations this error was wrapped at. +func (e *Error) MultiStack() *stack.Multi { + return e.multiStack +} + +// Underlying returns the error that is being wrapped. +func (e *Error) Underlying() error { + return e.underlying +} + +type hasMultiStack interface { + MultiStack() *stack.Multi +} + +// WrapSkip the error and add the current Stack. The argument skip is the +// number of stack frames to ascend, with 0 identifying the caller of Wrap. If +// the error to be wrapped has a MultiStack, the current stack will be added to +// it. If the error to be wrapped is nil, a nil error is returned. +func WrapSkip(err error, skip int) error { + // nil errors are returned back as nil. + if err == nil { + return nil + } + + // we're adding another Stack to an already wrapped error. + if se, ok := err.(hasMultiStack); ok { + se.MultiStack().AddCallers(skip + 1) + return err + } + + // we're create a freshly wrapped error. + return &Error{ + multiStack: stack.CallersMulti(skip + 1), + underlying: err, + } +} + +// Wrap provides a convenience function that calls WrapSkip with skip=0. That +// is, the Stack starts with the caller of Wrap. +func Wrap(err error) error { + return WrapSkip(err, 1) +} + +// New returns a new error that includes the Stack. +func New(s string) error { + return WrapSkip(errors.New(s), 1) +} + +// Newf formats and returns a new error that includes the Stack. +func Newf(format string, args ...interface{}) error { + return WrapSkip(fmt.Errorf(format, args...), 1) +} + +type hasUnderlying interface { + Underlying() error +} + +// Underlying returns all the underlying errors by iteratively checking if the +// error has an Underlying error. If e is nil, the returned slice will be nil. +func Underlying(e error) []error { + var errs []error + for { + if e == nil { + return errs + } + errs = append(errs, e) + + if eh, ok := e.(hasUnderlying); ok { + e = eh.Underlying() + } else { + e = nil + } + } +} diff --git a/Godeps/_workspace/src/github.com/facebookgo/stackerr/stackerr_test.go b/Godeps/_workspace/src/github.com/facebookgo/stackerr/stackerr_test.go new file mode 100644 index 00000000000..fb9d07e6b20 --- /dev/null +++ b/Godeps/_workspace/src/github.com/facebookgo/stackerr/stackerr_test.go @@ -0,0 +1,82 @@ +package stackerr_test + +import ( + "errors" + "fmt" + "regexp" + "strings" + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/facebookgo/stackerr" +) + +func TestNew(t *testing.T) { + const errStr = "foo bar baz" + e := stackerr.New(errStr) + matches := []string{ + errStr, + "^github.com/facebookgo/stackerr/stackerr_test.go:15 +TestNew$", + } + match(t, e.Error(), matches) +} + +func TestNewf(t *testing.T) { + const fmtStr = "%s 42" + const errStr = "foo bar baz" + e := stackerr.Newf(fmtStr, errStr) + matches := []string{ + fmt.Sprintf(fmtStr, errStr), + "^github.com/facebookgo/stackerr/stackerr_test.go:26 +TestNewf$", + } + match(t, e.Error(), matches) +} + +func TestWrap(t *testing.T) { + const errStr = "foo bar baz" + e := stackerr.Wrap(errors.New(errStr)) + matches := []string{ + errStr, + "^github.com/facebookgo/stackerr/stackerr_test.go:36 +TestWrap$", + } + match(t, e.Error(), matches) +} + +func TestNilWrap(t *testing.T) { + if stackerr.WrapSkip(nil, 1) != nil { + t.Fatal("did not get nil error") + } +} + +func TestDoubleWrap(t *testing.T) { + e := stackerr.New("") + if stackerr.WrapSkip(e, 1) != e { + t.Fatal("double wrap failure") + } +} + +func TestLog(t *testing.T) { + t.Log(stackerr.New("hello")) +} + +func TestUnderlying(t *testing.T) { + e1 := errors.New("") + e2 := stackerr.Wrap(e1) + errs := stackerr.Underlying(e2) + if len(errs) != 2 || errs[0] != e2 || errs[1] != e1 { + t.Fatal("failed Underlying") + } +} + +func match(t testing.TB, s string, matches []string) { + lines := strings.Split(s, "\n") + for i, m := range matches { + if !regexp.MustCompile(m).MatchString(lines[i]) { + t.Fatalf( + "did not find expected match \"%s\" on line %d in:\n%s", + m, + i, + s, + ) + } + } +} diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 5f349c19c1f..45633df7ca7 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -55,6 +55,15 @@ type cmdDetails struct { cannotRunOnClient bool cannotRunOnDaemon bool doesNotUseRepo bool + + // initializesConfig describes commands that initialize the config. + // pre-command hooks that require configs must not be run before this + // command + initializesConfig bool + + // preemptsAutoUpdate describes commands that must be executed without the + // pre-command update hook + preemptsAutoUpdate bool } func (d *cmdDetails) String() string { @@ -71,14 +80,14 @@ func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo } // properties so that other code can make decisions about whether to invoke a // command or return an error to the user. var cmdDetailsMap = map[*cmds.Command]cmdDetails{ - initCmd: cmdDetails{cannotRunOnDaemon: true, doesNotUseRepo: true}, + initCmd: cmdDetails{initializesConfig: true, cannotRunOnDaemon: true, doesNotUseRepo: true}, daemonCmd: cmdDetails{cannotRunOnDaemon: true}, commandsClientCmd: cmdDetails{doesNotUseRepo: true}, commands.CommandsDaemonCmd: cmdDetails{doesNotUseRepo: true}, commands.DiagCmd: cmdDetails{cannotRunOnClient: true}, commands.VersionCmd: cmdDetails{doesNotUseRepo: true}, - commands.UpdateCmd: cmdDetails{cannotRunOnDaemon: true}, - commands.UpdateCheckCmd: cmdDetails{}, - commands.UpdateLogCmd: cmdDetails{}, + commands.UpdateCmd: cmdDetails{preemptsAutoUpdate: true, cannotRunOnDaemon: true}, + commands.UpdateCheckCmd: cmdDetails{preemptsAutoUpdate: true}, + commands.UpdateLogCmd: cmdDetails{preemptsAutoUpdate: true}, commands.LogCmd: cmdDetails{cannotRunOnClient: true}, } diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 4bbe1da92f4..5647f552eb1 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -20,6 +20,7 @@ import ( daemon "github.com/jbenet/go-ipfs/daemon2" updates "github.com/jbenet/go-ipfs/updates" u "github.com/jbenet/go-ipfs/util" + "github.com/jbenet/go-ipfs/util/debugerror" ) // log is the command logger @@ -169,6 +170,7 @@ func (i *cmdInvocation) Parse(args []string) error { if err != nil { return err } + log.Debugf("config path is %s", configPath) // this sets up the function that will initialize the config lazily. ctx := i.req.Context() @@ -200,21 +202,61 @@ func (i *cmdInvocation) requestedHelp() (short bool, long bool, err error) { return longHelp, shortHelp, nil } +func callPreCommandHooks(details cmdDetails, req cmds.Request, root *cmds.Command) error { + + log.Debug("Calling pre-command hooks...") + + // some hooks only run when the command is executed locally + daemon, err := commandShouldRunOnDaemon(details, req, root) + if err != nil { + return err + } + + // check for updates when 1) commands is going to be run locally, 2) the + // command does not initialize the config, and 3) the command does not + // pre-empt updates + if !daemon && !details.initializesConfig && !details.preemptsAutoUpdate { + + log.Debug("Calling hook: Check for updates") + + cfg, err := req.Context().GetConfig() + if err != nil { + return err + } + // Check for updates and potentially install one. + if err := updates.CliCheckForUpdates(cfg, req.Context().ConfigRoot); err != nil { + return err + } + } + + return nil +} + func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { var res cmds.Response - useDaemon, err := commandShouldRunOnDaemon(req, root) + details, err := commandDetails(req.Path(), root) + if err != nil { + return nil, err + } + + useDaemon, err := commandShouldRunOnDaemon(*details, req, root) if err != nil { return nil, err } - cfg, err := req.Context().GetConfig() + err = callPreCommandHooks(*details, req, root) if err != nil { return nil, err } if useDaemon { + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + addr, err := ma.NewMultiaddr(cfg.Addresses.API) if err != nil { return nil, err @@ -236,11 +278,6 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { } else { log.Info("Executing command locally") - // Check for updates and potentially install one. - if err := updates.CliCheckForUpdates(cfg, req.Context().ConfigRoot); err != nil { - return nil, err - } - // this sets up the function that will initialize the node // this is so that we can construct the node lazily. ctx := req.Context() @@ -266,13 +303,11 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { return res, nil } -func commandShouldRunOnDaemon(req cmds.Request, root *cmds.Command) (bool, error) { - path := req.Path() - // root command. - if len(path) < 1 { - return false, nil - } - +// commandDetails returns a command's details for the command given by |path| +// within the |root| command tree. +// +// Returns an error if the command is not found in the Command tree. +func commandDetails(path []string, root *cmds.Command) (*cmdDetails, error) { var details cmdDetails // find the last command in path that has a cmdDetailsMap entry cmd := root @@ -280,7 +315,7 @@ func commandShouldRunOnDaemon(req cmds.Request, root *cmds.Command) (bool, error var found bool cmd, found = cmd.Subcommands[cmp] if !found { - return false, fmt.Errorf("subcommand %s should be in root", cmp) + return nil, debugerror.Errorf("subcommand %s should be in root", cmp) } if cmdDetails, found := cmdDetailsMap[cmd]; found { @@ -288,6 +323,21 @@ func commandShouldRunOnDaemon(req cmds.Request, root *cmds.Command) (bool, error } } log.Debugf("cmd perms for +%v: %s", path, details.String()) + return &details, nil +} + +// commandShouldRunOnDaemon determines, from commmand details, whether a +// command ought to be executed on an IPFS daemon. +// +// It returns true if the command should be executed on a daemon and false if +// it should be executed on a client. It returns an error if the command must +// NOT be executed on either. +func commandShouldRunOnDaemon(details cmdDetails, req cmds.Request, root *cmds.Command) (bool, error) { + path := req.Path() + // root command. + if len(path) < 1 { + return false, nil + } if details.cannotRunOnClient && details.cannotRunOnDaemon { return false, fmt.Errorf("command disabled: %s", path[0]) diff --git a/config/config.go b/config/config.go index d03a26b116b..1416bb139c0 100644 --- a/config/config.go +++ b/config/config.go @@ -5,11 +5,11 @@ import ( "crypto" "crypto/x509" "encoding/base64" - "errors" "os" "path/filepath" u "github.com/jbenet/go-ipfs/util" + "github.com/jbenet/go-ipfs/util/debugerror" ) var log = u.Logger("config") @@ -129,7 +129,7 @@ func (i *Identity) DecodePrivateKey(passphrase string) (crypto.PrivateKey, error func Load(filename string) (*Config, error) { // if nothing is there, fail. User must run 'ipfs init' if _, err := os.Stat(filename); os.IsNotExist(err) { - return nil, errors.New("ipfs not initialized, please run 'ipfs init'") + return nil, debugerror.New("ipfs not initialized, please run 'ipfs init'") } var cfg Config diff --git a/util/debugerror/debugerror.go b/util/debugerror/debugerror.go new file mode 100644 index 00000000000..2b6370f98fe --- /dev/null +++ b/util/debugerror/debugerror.go @@ -0,0 +1,30 @@ +// package debugerror provides a way to augment errors with additional +// information to allow for easier debugging. +package debugerror + +import ( + "errors" + "fmt" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/facebookgo/stackerr" + "github.com/jbenet/go-ipfs/util" +) + +func Errorf(format string, a ...interface{}) error { + return Wrap(fmt.Errorf(format, a...)) +} + +// New returns an error that contains a stack trace (in debug mode) +func New(s string) error { + if util.Debug { + return stackerr.New(s) + } + return errors.New(s) +} + +func Wrap(err error) error { + if util.Debug { + return stackerr.Wrap(err) + } + return err +}