forked from argoproj/argo-workflows
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
222 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package errors | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// Externally visible error codes | ||
var ( | ||
CodeUnauthorized = "ERR_UNAUTHORIZED" | ||
CodeBadRequest = "ERR_BAD_REQUEST" | ||
CodeForbidden = "ERR_FORBIDDEN" | ||
CodeNotFound = "ERR_NOT_FOUND" | ||
CodeInternal = "ERR_INTERNAL" | ||
) | ||
|
||
// ArgoError is an error interface that additionally adds support for | ||
// stack trace, error code, and a JSON representation of the error | ||
type ArgoError interface { | ||
Error() string | ||
Code() string | ||
JSON() []byte | ||
StackTrace() errors.StackTrace | ||
Format(s fmt.State, verb rune) | ||
} | ||
|
||
// argoerr is the internal implementation of an Argo error which wraps the error from pkg/errors | ||
type argoerr struct { | ||
code string | ||
message string | ||
stracer stackTracer | ||
} | ||
|
||
// stackTracer is interface for error types that have a stack trace | ||
type stackTracer interface { | ||
Error() string | ||
StackTrace() errors.StackTrace | ||
} | ||
|
||
// New returns an error with the supplied message. | ||
// New also records the stack trace at the point it was called. | ||
func New(code string, message string) error { | ||
err := errors.New(message) | ||
return argoerr{code, message, err.(stackTracer)} | ||
} | ||
|
||
// Errorf returns an error and formats according to a format specifier | ||
func Errorf(code string, format string, args ...interface{}) error { | ||
return New(code, fmt.Sprintf(format, args...)) | ||
} | ||
|
||
// InternalError is a convenience function to create a Internal error with a message | ||
func InternalError(message string) error { | ||
return New(CodeInternal, message) | ||
} | ||
|
||
// InternalErrorf is a convenience function to format an Internal error | ||
func InternalErrorf(format string, args ...interface{}) error { | ||
return Errorf(CodeInternal, format, args) | ||
} | ||
|
||
// InternalWrapError annotates the error with the ERR_INTERNAL code and a stack trace, optional message | ||
func InternalWrapError(err error, message ...string) error { | ||
if len(message) == 0 { | ||
return Wrap(err, CodeInternal, err.Error()) | ||
} | ||
return Wrap(err, CodeInternal, message[0]) | ||
} | ||
|
||
// InternalWrapErrorf annotates the error with the ERR_INTERNAL code and a stack trace, optional message | ||
func InternalWrapErrorf(err error, format string, args ...interface{}) error { | ||
return Wrap(err, CodeInternal, fmt.Sprintf(format, args...)) | ||
} | ||
|
||
// Wrap returns an error annotating err with a stack trace at the point Wrap is called, | ||
// and a new supplied message. The previous original is preserved and accessible via Cause(). | ||
// If err is nil, Wrap returns nil. | ||
func Wrap(err error, code string, message string) error { | ||
if err == nil { | ||
return nil | ||
} | ||
err = errors.Wrap(err, message) | ||
return argoerr{code, message, err.(stackTracer)} | ||
} | ||
|
||
// Cause returns the underlying cause of the error, if possible. | ||
// An error value has a cause if it implements the following | ||
// interface: | ||
// | ||
// type causer interface { | ||
// Cause() error | ||
// } | ||
// | ||
// If the error does not implement Cause, the original error will | ||
// be returned. If the error is nil, nil will be returned without further | ||
// investigation. | ||
func Cause(err error) error { | ||
if argoErr, ok := err.(argoerr); ok { | ||
return errors.Cause(argoErr.stracer) | ||
} | ||
return errors.Cause(err) | ||
} | ||
|
||
func (e argoerr) Error() string { | ||
return fmt.Sprintf("[%s] %s", e.code, e.message) | ||
} | ||
|
||
func (e argoerr) Code() string { | ||
return e.code | ||
} | ||
|
||
func (e argoerr) StackTrace() errors.StackTrace { | ||
return e.stracer.StackTrace() | ||
} | ||
|
||
func (e argoerr) JSON() []byte { | ||
type errBean struct { | ||
Code string `json:"code"` | ||
Message string `json:"message"` | ||
} | ||
eb := errBean{e.code, e.message} | ||
j, _ := json.Marshal(eb) | ||
return j | ||
} | ||
|
||
func (e argoerr) Format(s fmt.State, verb rune) { | ||
switch verb { | ||
case 'v': | ||
if s.Flag('+') { | ||
io.WriteString(s, e.Error()) | ||
for _, pc := range e.StackTrace() { | ||
f := errors.Frame(pc) | ||
fmt.Fprintf(s, "\n%+v", f) | ||
} | ||
return | ||
} | ||
fallthrough | ||
case 's': | ||
io.WriteString(s, e.Error()) | ||
case 'q': | ||
fmt.Fprintf(s, "%q", e.Error()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package errors_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/argoproj/argo/errors" | ||
pkgerr "github.com/pkg/errors" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
// stackTracer is interface for error types that have a stack trace | ||
type stackTracer interface { | ||
StackTrace() pkgerr.StackTrace | ||
} | ||
|
||
// TestErrorf tests the initializer of error package | ||
func TestErrorf(t *testing.T) { | ||
err := errors.Errorf(errors.CodeInternal, "test internal") | ||
assert.Equal(t, err.Error(), "[ERR_INTERNAL] test internal") | ||
} | ||
|
||
// TestWrap ensures we can wrap an error and use Cause() to retrieve the original error | ||
func TestWrap(t *testing.T) { | ||
err := fmt.Errorf("original error message") | ||
argoErr := errors.Wrap(err, "WRAPPED", "wrapped message") | ||
assert.Equal(t, "[WRAPPED] wrapped message", argoErr.Error()) | ||
orig := errors.Cause(argoErr) | ||
assert.Equal(t, err.Error(), orig.Error()) | ||
} | ||
|
||
// TestInternalError verifies | ||
func TestInternalError(t *testing.T) { | ||
err := errors.InternalError("test internal") | ||
assert.Equal(t, "[ERR_INTERNAL] test internal", err.Error()) | ||
|
||
// Test wrapping errors | ||
err = fmt.Errorf("random error") | ||
intWrap := errors.InternalWrapError(err) | ||
_ = intWrap.(stackTracer) | ||
assert.Equal(t, "[ERR_INTERNAL] random error", intWrap.Error()) | ||
intWrap = errors.InternalWrapError(err, "different message") | ||
_ = intWrap.(stackTracer) | ||
assert.Equal(t, "[ERR_INTERNAL] different message", intWrap.Error()) | ||
intWrap = errors.InternalWrapErrorf(err, "hello %s", "world") | ||
_ = intWrap.(stackTracer) | ||
assert.Equal(t, "[ERR_INTERNAL] hello world", intWrap.Error()) | ||
} | ||
|
||
func TestStackTrace(t *testing.T) { | ||
err := errors.New("MYCODE", "my message") | ||
assert.Contains(t, fmt.Sprintf("%+v", err), "errors_test.go") | ||
} |