mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
135 lines
4.5 KiB
Go
135 lines
4.5 KiB
Go
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"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
)
|
|
|
|
func rewrapError(err error, code codes.Code) error {
|
|
return status.Error(code, err.Error())
|
|
}
|
|
|
|
func gitErrToGRPC(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
errMsg := err.Error()
|
|
if grpcStatus := UnwrapGRPCStatus(err); grpcStatus != nil {
|
|
errMsg = grpcStatus.Message()
|
|
}
|
|
|
|
if errMsg == 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 apierrors.IsNotFound(err):
|
|
err = rewrapError(err, codes.NotFound)
|
|
case apierrors.IsAlreadyExists(err):
|
|
err = rewrapError(err, codes.AlreadyExists)
|
|
case apierrors.IsInvalid(err):
|
|
err = rewrapError(err, codes.InvalidArgument)
|
|
case apierrors.IsMethodNotSupported(err):
|
|
err = rewrapError(err, codes.Unimplemented)
|
|
case apierrors.IsServiceUnavailable(err):
|
|
err = rewrapError(err, codes.Unavailable)
|
|
case apierrors.IsBadRequest(err):
|
|
err = rewrapError(err, codes.FailedPrecondition)
|
|
case apierrors.IsUnauthorized(err):
|
|
err = rewrapError(err, codes.Unauthenticated)
|
|
case apierrors.IsForbidden(err):
|
|
err = rewrapError(err, codes.PermissionDenied)
|
|
case apierrors.IsTimeout(err):
|
|
err = rewrapError(err, codes.DeadlineExceeded)
|
|
case apierrors.IsServerTimeout(err):
|
|
err = rewrapError(err, codes.Unavailable)
|
|
case apierrors.IsConflict(err):
|
|
err = rewrapError(err, codes.Aborted)
|
|
case apierrors.IsTooManyRequests(err):
|
|
err = rewrapError(err, codes.ResourceExhausted)
|
|
case apierrors.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 any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, 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 any, ss grpc.ServerStream, _ *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 any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, 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 any, ss grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
err := handler(srv, ss)
|
|
return kubeErrToGRPC(err)
|
|
}
|
|
}
|