fserrors: use errors.Walk for the wrapped error types

s3-about
Fabian Möller 2019-02-09 20:39:14 +01:00 committed by Nick Craig-Wood
parent d0ff07bdb0
commit d04b0b856a
1 changed files with 61 additions and 72 deletions

View File

@ -5,11 +5,10 @@ import (
"fmt"
"io"
"net/http"
"reflect"
"strings"
"time"
"github.com/pkg/errors"
"github.com/ncw/rclone/lib/errors"
)
// Retrier is an optional interface for error as to whether the
@ -64,17 +63,21 @@ func RetryError(err error) error {
return wrappedRetryError{err}
}
func (err wrappedRetryError) Cause() error {
return err.error
}
// IsRetryError returns true if err conforms to the Retry interface
// and calling the Retry method returns true.
func IsRetryError(err error) bool {
if err == nil {
func IsRetryError(err error) (isRetry bool) {
errors.Walk(err, func(err error) bool {
if r, ok := err.(Retrier); ok {
isRetry = r.Retry()
return true
}
return false
}
_, err = Cause(err)
if r, ok := err.(Retrier); ok {
return r.Retry()
}
return false
})
return
}
// Fataler is an optional interface for error as to whether the
@ -109,17 +112,21 @@ func FatalError(err error) error {
return wrappedFatalError{err}
}
func (err wrappedFatalError) Cause() error {
return err.error
}
// IsFatalError returns true if err conforms to the Fatal interface
// and calling the Fatal method returns true.
func IsFatalError(err error) bool {
if err == nil {
func IsFatalError(err error) (isFatal bool) {
errors.Walk(err, func(err error) bool {
if r, ok := err.(Fataler); ok {
isFatal = r.Fatal()
return true
}
return false
}
_, err = Cause(err)
if r, ok := err.(Fataler); ok {
return r.Fatal()
}
return false
})
return
}
// NoRetrier is an optional interface for error as to whether the
@ -154,17 +161,21 @@ func NoRetryError(err error) error {
return wrappedNoRetryError{err}
}
func (err wrappedNoRetryError) Cause() error {
return err.error
}
// IsNoRetryError returns true if err conforms to the NoRetry
// interface and calling the NoRetry method returns true.
func IsNoRetryError(err error) bool {
if err == nil {
func IsNoRetryError(err error) (isNoRetry bool) {
errors.Walk(err, func(err error) bool {
if r, ok := err.(NoRetrier); ok {
isNoRetry = r.NoRetry()
return true
}
return false
}
_, err = Cause(err)
if r, ok := err.(NoRetrier); ok {
return r.NoRetry()
}
return false
})
return
}
// RetryAfter is an optional interface for error as to whether the
@ -203,15 +214,15 @@ var _ RetryAfter = ErrorRetryAfter{}
// RetryAfterErrorTime returns the time that the RetryAfter error
// indicates or a Zero time.Time
func RetryAfterErrorTime(err error) time.Time {
if err == nil {
return time.Time{}
}
_, err = Cause(err)
if do, ok := err.(RetryAfter); ok {
return do.RetryAfter()
}
return time.Time{}
func RetryAfterErrorTime(err error) (retryAfter time.Time) {
errors.Walk(err, func(err error) bool {
if r, ok := err.(RetryAfter); ok {
retryAfter = r.RetryAfter()
return true
}
return false
})
return
}
// IsRetryAfterError returns true if err is an ErrorRetryAfter
@ -223,8 +234,7 @@ func IsRetryAfterError(err error) bool {
// library errors too. It returns true if any of the intermediate
// errors had a Timeout() or Temporary() method which returned true.
func Cause(cause error) (retriable bool, err error) {
err = cause
for prev := err; err != nil; prev = err {
errors.Walk(cause, func(c error) bool {
// Check for net error Timeout()
if x, ok := err.(interface {
Timeout() bool
@ -238,41 +248,10 @@ func Cause(cause error) (retriable bool, err error) {
}); ok && x.Temporary() {
retriable = true
}
// Unwrap 1 level if possible
err = errors.Cause(err)
if err == nil {
// errors.Cause can return nil which isn't
// desirable so pick the previous error in
// this case.
err = prev
}
if reflect.DeepEqual(err, prev) {
// Unpack any struct or *struct with a field
// of name Err which satisfies the error
// interface. This includes *url.Error,
// *net.OpError, *os.SyscallError and many
// others in the stdlib
errType := reflect.TypeOf(err)
errValue := reflect.ValueOf(err)
if errValue.IsValid() && errType.Kind() == reflect.Ptr {
errType = errType.Elem()
errValue = errValue.Elem()
}
if errValue.IsValid() && errType.Kind() == reflect.Struct {
if errField := errValue.FieldByName("Err"); errField.IsValid() {
errFieldValue := errField.Interface()
if newErr, ok := errFieldValue.(error); ok {
err = newErr
}
}
}
}
if reflect.DeepEqual(err, prev) {
break
}
}
return retriable, err
err = c
return false
})
return
}
// retriableErrorStrings is a list of phrases which when we find it
@ -344,3 +323,13 @@ func ShouldRetryHTTP(resp *http.Response, retryErrorCodes []int) bool {
}
return false
}
type causer interface {
Cause() error
}
var (
_ causer = wrappedRetryError{}
_ causer = wrappedFatalError{}
_ causer = wrappedNoRetryError{}
)