-
Notifications
You must be signed in to change notification settings - Fork 5.5k
/
errors.go
135 lines (122 loc) · 4.5 KB
/
errors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package grpc
import (
"context"
"errors"
giterr "github.com/go-git/go-git/v5/plumbing/transport"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
apierr "k8s.io/apimachinery/pkg/api/errors"
)
func rewrapError(err error, code codes.Code) error {
return status.Errorf(code, err.Error())
}
func gitErrToGRPC(err error) error {
if err == nil {
return err
}
var errMsg = err.Error()
if grpcStatus := UnwrapGRPCStatus(err); grpcStatus != nil {
errMsg = grpcStatus.Message()
}
switch errMsg {
case giterr.ErrRepositoryNotFound.Error():
err = rewrapError(errors.New(errMsg), codes.NotFound)
}
return err
}
// UnwrapGRPCStatus will attempt to cast the given error into a grpc Status
// object unwrapping all existing inner errors. Will return nil if none of the
// nested errors can be casted.
func UnwrapGRPCStatus(err error) *status.Status {
if se, ok := err.(interface{ GRPCStatus() *status.Status }); ok {
return se.GRPCStatus()
}
e := errors.Unwrap(err)
if e == nil {
return nil
}
return UnwrapGRPCStatus(e)
}
// kubeErrToGRPC converts a Kubernetes error into a gRPC code + error. The gRPC code we translate
// it to is significant, because it eventually maps back to an HTTP status code determined by
// grpc-gateway. See:
// https://github.com/grpc-ecosystem/grpc-gateway/blob/v2.11.3/runtime/errors.go#L36
// https://go.dev/src/net/http/status.go
func kubeErrToGRPC(err error) error {
/*
Unmapped source Kubernetes API errors as of 2022-10-05:
* IsGone => 410 (DEPRECATED by ResourceExpired)
* IsResourceExpired => 410
* IsUnexpectedServerError
* IsUnexpectedObjectError
Unmapped target gRPC codes as of 2022-10-05:
* Canceled Code = 1
* Unknown Code = 2
* OutOfRange Code = 11
* DataLoss Code = 15
*/
switch {
case apierr.IsNotFound(err):
err = rewrapError(err, codes.NotFound)
case apierr.IsAlreadyExists(err):
err = rewrapError(err, codes.AlreadyExists)
case apierr.IsInvalid(err):
err = rewrapError(err, codes.InvalidArgument)
case apierr.IsMethodNotSupported(err):
err = rewrapError(err, codes.Unimplemented)
case apierr.IsServiceUnavailable(err):
err = rewrapError(err, codes.Unavailable)
case apierr.IsBadRequest(err):
err = rewrapError(err, codes.FailedPrecondition)
case apierr.IsUnauthorized(err):
err = rewrapError(err, codes.Unauthenticated)
case apierr.IsForbidden(err):
err = rewrapError(err, codes.PermissionDenied)
case apierr.IsTimeout(err):
err = rewrapError(err, codes.DeadlineExceeded)
case apierr.IsServerTimeout(err):
err = rewrapError(err, codes.Unavailable)
case apierr.IsConflict(err):
err = rewrapError(err, codes.Aborted)
case apierr.IsTooManyRequests(err):
err = rewrapError(err, codes.ResourceExhausted)
case apierr.IsInternalError(err):
err = rewrapError(err, codes.Internal)
default:
// This is necessary as GRPC Status don't support wrapped errors:
// https://github.com/grpc/grpc-go/issues/2934
if grpcStatus := UnwrapGRPCStatus(err); grpcStatus != nil {
err = status.Error(grpcStatus.Code(), grpcStatus.Message())
}
}
return err
}
// ErrorCodeGitUnaryServerInterceptor replaces Kubernetes errors with relevant gRPC equivalents, if any.
func ErrorCodeGitUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
resp, err = handler(ctx, req)
return resp, gitErrToGRPC(err)
}
}
// ErrorCodeGitStreamServerInterceptor replaces Kubernetes errors with relevant gRPC equivalents, if any.
func ErrorCodeGitStreamServerInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
err := handler(srv, ss)
return gitErrToGRPC(err)
}
}
// ErrorCodeK8sUnaryServerInterceptor replaces Kubernetes errors with relevant gRPC equivalents, if any.
func ErrorCodeK8sUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
resp, err = handler(ctx, req)
return resp, kubeErrToGRPC(err)
}
}
// ErrorCodeK8sStreamServerInterceptor replaces Kubernetes errors with relevant gRPC equivalents, if any.
func ErrorCodeK8sStreamServerInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
err := handler(srv, ss)
return kubeErrToGRPC(err)
}
}