204 lines
5.4 KiB
Go
204 lines
5.4 KiB
Go
// Copyright 2019 The Go Cloud Development Kit Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package gcerr provides an error type for Go CDK APIs.
|
|
package gcerr
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
|
|
"gocloud.dev/internal/retry"
|
|
"golang.org/x/xerrors"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
// An ErrorCode describes the error's category.
|
|
type ErrorCode int
|
|
|
|
const (
|
|
// Returned by the Code function on a nil error. It is not a valid
|
|
// code for an error.
|
|
OK ErrorCode = 0
|
|
|
|
// The error could not be categorized.
|
|
Unknown ErrorCode = 1
|
|
|
|
// The resource was not found.
|
|
NotFound ErrorCode = 2
|
|
|
|
// The resource exists, but it should not.
|
|
AlreadyExists ErrorCode = 3
|
|
|
|
// A value given to a Go CDK API is incorrect.
|
|
InvalidArgument ErrorCode = 4
|
|
|
|
// Something unexpected happened. Internal errors always indicate
|
|
// bugs in the Go CDK (or possibly the underlying service).
|
|
Internal ErrorCode = 5
|
|
|
|
// The feature is not implemented.
|
|
Unimplemented ErrorCode = 6
|
|
|
|
// The system was in the wrong state.
|
|
FailedPrecondition ErrorCode = 7
|
|
|
|
// The caller does not have permission to execute the specified operation.
|
|
PermissionDenied ErrorCode = 8
|
|
|
|
// Some resource has been exhausted, typically because a service resource limit
|
|
// has been reached.
|
|
ResourceExhausted ErrorCode = 9
|
|
|
|
// The operation was canceled.
|
|
Canceled ErrorCode = 10
|
|
|
|
// The operation timed out.
|
|
DeadlineExceeded ErrorCode = 11
|
|
)
|
|
|
|
// When adding a new error code, try to use the names defined in google.golang.org/grpc/codes.
|
|
|
|
// Do not change the numbers assigned to codes: past values may be stored in metric databases.
|
|
|
|
// Call "go generate" whenever you change the above list of error codes.
|
|
// To get stringer:
|
|
// go get golang.org/x/tools/cmd/stringer
|
|
// Make sure $GOPATH/bin or $GOBIN in on your path.
|
|
|
|
//go:generate stringer -type=ErrorCode
|
|
|
|
// An Error describes a Go CDK error.
|
|
type Error struct {
|
|
Code ErrorCode
|
|
msg string
|
|
frame xerrors.Frame
|
|
err error
|
|
}
|
|
|
|
func (e *Error) Error() string {
|
|
return fmt.Sprint(e)
|
|
}
|
|
|
|
func (e *Error) Format(s fmt.State, c rune) {
|
|
xerrors.FormatError(e, s, c)
|
|
}
|
|
|
|
func (e *Error) FormatError(p xerrors.Printer) (next error) {
|
|
if e.msg == "" {
|
|
p.Printf("code=%v", e.Code)
|
|
} else {
|
|
p.Printf("%s (code=%v)", e.msg, e.Code)
|
|
}
|
|
e.frame.Format(p)
|
|
return e.err
|
|
}
|
|
|
|
// Unwrap returns the error underlying the receiver, which may be nil.
|
|
func (e *Error) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
// New returns a new error with the given code, underlying error and message. Pass 1
|
|
// for the call depth if New is called from the function raising the error; pass 2 if
|
|
// it is called from a helper function that was invoked by the original function; and
|
|
// so on.
|
|
func New(c ErrorCode, err error, callDepth int, msg string) *Error {
|
|
return &Error{
|
|
Code: c,
|
|
msg: msg,
|
|
frame: xerrors.Caller(callDepth),
|
|
err: err,
|
|
}
|
|
}
|
|
|
|
// Newf uses format and args to format a message, then calls New.
|
|
func Newf(c ErrorCode, err error, format string, args ...interface{}) *Error {
|
|
return New(c, err, 2, fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// DoNotWrap reports whether an error should not be wrapped in the Error
|
|
// type from this package.
|
|
// It returns true if err is a retry error, a context error, io.EOF, or if it wraps
|
|
// one of those.
|
|
func DoNotWrap(err error) bool {
|
|
if xerrors.Is(err, io.EOF) {
|
|
return true
|
|
}
|
|
if xerrors.Is(err, context.Canceled) {
|
|
return true
|
|
}
|
|
if xerrors.Is(err, context.DeadlineExceeded) {
|
|
return true
|
|
}
|
|
var r *retry.ContextError
|
|
if xerrors.As(err, &r) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GRPCCode extracts the gRPC status code and converts it into an ErrorCode.
|
|
// It returns Unknown if the error isn't from gRPC.
|
|
func GRPCCode(err error) ErrorCode {
|
|
switch status.Code(err) {
|
|
case codes.NotFound:
|
|
return NotFound
|
|
case codes.AlreadyExists:
|
|
return AlreadyExists
|
|
case codes.InvalidArgument:
|
|
return InvalidArgument
|
|
case codes.Internal:
|
|
return Internal
|
|
case codes.Unimplemented:
|
|
return Unimplemented
|
|
case codes.FailedPrecondition:
|
|
return FailedPrecondition
|
|
case codes.PermissionDenied:
|
|
return PermissionDenied
|
|
case codes.ResourceExhausted:
|
|
return ResourceExhausted
|
|
case codes.Canceled:
|
|
return Canceled
|
|
case codes.DeadlineExceeded:
|
|
return DeadlineExceeded
|
|
default:
|
|
return Unknown
|
|
}
|
|
}
|
|
|
|
// ErrorAs is a helper for the ErrorAs method of an API's portable type.
|
|
// It performs some initial nil checks, and does a single level of unwrapping
|
|
// when err is a *gcerr.Error. Then it calls its errorAs argument, which should
|
|
// be a driver implementation of ErrorAs.
|
|
func ErrorAs(err error, target interface{}, errorAs func(error, interface{}) bool) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
if target == nil {
|
|
panic("ErrorAs target cannot be nil")
|
|
}
|
|
val := reflect.ValueOf(target)
|
|
if val.Type().Kind() != reflect.Ptr || val.IsNil() {
|
|
panic("ErrorAs target must be a non-nil pointer")
|
|
}
|
|
if e, ok := err.(*Error); ok {
|
|
err = e.Unwrap()
|
|
}
|
|
return errorAs(err, target)
|
|
}
|