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