github.com/cs3org/reva/v2@v2.27.7/pkg/rgrpc/status/status.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  // Package status contains helpers functions
    20  // to create grpc Status with contextual information,
    21  // like traces.
    22  package status
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"net/http"
    28  
    29  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    30  	"github.com/cs3org/reva/v2/pkg/errtypes"
    31  	"go.opentelemetry.io/otel/trace"
    32  	"google.golang.org/grpc/codes"
    33  	"google.golang.org/grpc/status"
    34  )
    35  
    36  // NewOK returns a Status with CODE_OK.
    37  func NewOK(ctx context.Context) *rpc.Status {
    38  	return &rpc.Status{
    39  		Code:  rpc.Code_CODE_OK,
    40  		Trace: getTrace(ctx),
    41  	}
    42  }
    43  
    44  // NewNotFound returns a Status with CODE_NOT_FOUND.
    45  func NewNotFound(ctx context.Context, msg string) *rpc.Status {
    46  	return &rpc.Status{
    47  		Code:    rpc.Code_CODE_NOT_FOUND,
    48  		Message: msg,
    49  		Trace:   getTrace(ctx),
    50  	}
    51  }
    52  
    53  // NewInvalid returns a Status with CODE_INVALID_ARGUMENT.
    54  func NewInvalid(ctx context.Context, msg string) *rpc.Status {
    55  	return &rpc.Status{
    56  		Code:    rpc.Code_CODE_INVALID_ARGUMENT,
    57  		Message: msg,
    58  		Trace:   getTrace(ctx),
    59  	}
    60  }
    61  
    62  // NewInternal returns a Status with CODE_INTERNAL.
    63  func NewInternal(ctx context.Context, msg string) *rpc.Status {
    64  	return &rpc.Status{
    65  		Code:    rpc.Code_CODE_INTERNAL,
    66  		Message: msg,
    67  		Trace:   getTrace(ctx),
    68  	}
    69  }
    70  
    71  // NewUnauthenticated returns a Status with CODE_UNAUTHENTICATED.
    72  func NewUnauthenticated(ctx context.Context, err error, msg string) *rpc.Status {
    73  	return &rpc.Status{
    74  		Code:    rpc.Code_CODE_UNAUTHENTICATED,
    75  		Message: msg,
    76  		Trace:   getTrace(ctx),
    77  	}
    78  }
    79  
    80  // NewPermissionDenied returns a Status with PERMISSION_DENIED.
    81  func NewPermissionDenied(ctx context.Context, err error, msg string) *rpc.Status {
    82  	return &rpc.Status{
    83  		Code:    rpc.Code_CODE_PERMISSION_DENIED,
    84  		Message: msg,
    85  		Trace:   getTrace(ctx),
    86  	}
    87  }
    88  
    89  // NewAborted returns a Status with ABORTED.
    90  func NewAborted(ctx context.Context, err error, msg string) *rpc.Status {
    91  	return &rpc.Status{
    92  		Code:    rpc.Code_CODE_ABORTED,
    93  		Message: msg,
    94  		Trace:   getTrace(ctx),
    95  	}
    96  }
    97  
    98  // NewFailedPrecondition returns a Status with FAILED_PRECONDITION.
    99  func NewFailedPrecondition(ctx context.Context, err error, msg string) *rpc.Status {
   100  	return &rpc.Status{
   101  		Code:    rpc.Code_CODE_FAILED_PRECONDITION,
   102  		Message: msg,
   103  		Trace:   getTrace(ctx),
   104  	}
   105  }
   106  
   107  // NewInsufficientStorage returns a Status with INSUFFICIENT_STORAGE.
   108  func NewInsufficientStorage(ctx context.Context, err error, msg string) *rpc.Status {
   109  	return &rpc.Status{
   110  		Code:    rpc.Code_CODE_INSUFFICIENT_STORAGE,
   111  		Message: msg,
   112  		Trace:   getTrace(ctx),
   113  	}
   114  }
   115  
   116  // NewUnimplemented returns a Status with CODE_UNIMPLEMENTED.
   117  func NewUnimplemented(ctx context.Context, err error, msg string) *rpc.Status {
   118  	return &rpc.Status{
   119  		Code:    rpc.Code_CODE_UNIMPLEMENTED,
   120  		Message: msg,
   121  		Trace:   getTrace(ctx),
   122  	}
   123  }
   124  
   125  // NewAlreadyExists returns a Status with CODE_ALREADY_EXISTS.
   126  func NewAlreadyExists(ctx context.Context, err error, msg string) *rpc.Status {
   127  	return &rpc.Status{
   128  		Code:    rpc.Code_CODE_ALREADY_EXISTS,
   129  		Message: msg,
   130  		Trace:   getTrace(ctx),
   131  	}
   132  }
   133  
   134  // NewInvalidArg returns a Status with CODE_INVALID_ARGUMENT.
   135  func NewInvalidArg(ctx context.Context, msg string) *rpc.Status {
   136  	return &rpc.Status{Code: rpc.Code_CODE_INVALID_ARGUMENT,
   137  		Message: msg,
   138  		Trace:   getTrace(ctx),
   139  	}
   140  }
   141  
   142  // NewConflict returns a Status with Code_CODE_ABORTED.
   143  //
   144  // Deprecated: NewConflict exists for historical compatibility
   145  // and should not be used. To create a Status with code ABORTED,
   146  // use NewAborted.
   147  func NewConflict(ctx context.Context, err error, msg string) *rpc.Status {
   148  	return &rpc.Status{
   149  		Code:    rpc.Code_CODE_ABORTED,
   150  		Message: msg,
   151  		Trace:   getTrace(ctx),
   152  	}
   153  }
   154  
   155  // NewLocked returns a status Code_CODE_LOCKED
   156  func NewLocked(ctx context.Context, msg string) *rpc.Status {
   157  	return &rpc.Status{
   158  		Code:    rpc.Code_CODE_LOCKED,
   159  		Message: msg,
   160  		Trace:   getTrace(ctx),
   161  	}
   162  }
   163  
   164  // NewStatusFromErrType returns a status that corresponds to the given errtype
   165  func NewStatusFromErrType(ctx context.Context, msg string, err error) *rpc.Status {
   166  	switch e := err.(type) {
   167  	case nil:
   168  		return NewOK(ctx)
   169  	case errtypes.NotFound:
   170  		return NewNotFound(ctx, msg+": "+err.Error())
   171  	case errtypes.IsNotFound:
   172  		return NewNotFound(ctx, msg+": "+err.Error())
   173  	case errtypes.AlreadyExists:
   174  		return NewAlreadyExists(ctx, err, msg+": "+err.Error())
   175  	case errtypes.InvalidCredentials:
   176  		return NewPermissionDenied(ctx, e, msg+": "+err.Error())
   177  	case errtypes.IsInvalidCredentials:
   178  		// TODO this maps badly
   179  		return NewUnauthenticated(ctx, err, msg+": "+err.Error())
   180  	case errtypes.PermissionDenied:
   181  		return NewPermissionDenied(ctx, e, msg+": "+err.Error())
   182  	case errtypes.Locked:
   183  		// FIXME a locked error returns the current lockid
   184  		// FIXME use NewAborted as per the rpc code docs
   185  		return NewLocked(ctx, msg+": "+err.Error())
   186  	case errtypes.Aborted:
   187  		return NewAborted(ctx, e, msg+": "+err.Error())
   188  	case errtypes.PreconditionFailed:
   189  		return NewFailedPrecondition(ctx, e, msg+": "+err.Error())
   190  	case errtypes.IsNotSupported:
   191  		return NewUnimplemented(ctx, err, msg+":"+err.Error())
   192  	case errtypes.BadRequest:
   193  		return NewInvalid(ctx, msg+":"+err.Error())
   194  	}
   195  
   196  	// map GRPC status codes coming from the auth middleware
   197  	grpcErr := err
   198  	for {
   199  		st, ok := status.FromError(grpcErr)
   200  		if ok {
   201  			switch st.Code() {
   202  			case codes.NotFound:
   203  				return NewNotFound(ctx, msg+": "+err.Error())
   204  			case codes.Unauthenticated:
   205  				return NewUnauthenticated(ctx, err, msg+": "+err.Error())
   206  			case codes.PermissionDenied:
   207  				return NewPermissionDenied(ctx, err, msg+": "+err.Error())
   208  			case codes.Unimplemented:
   209  				return NewUnimplemented(ctx, err, msg+": "+err.Error())
   210  			}
   211  		}
   212  		// the actual error can be wrapped multiple times
   213  		grpcErr = errors.Unwrap(grpcErr)
   214  		if grpcErr == nil {
   215  			break
   216  		}
   217  	}
   218  
   219  	return NewInternal(ctx, msg+":"+err.Error())
   220  }
   221  
   222  // NewErrorFromCode returns a standardized Error for a given RPC code.
   223  func NewErrorFromCode(code rpc.Code, pkgname string) error {
   224  	return errors.New(pkgname + ": grpc failed with code " + code.String())
   225  }
   226  
   227  // internal function to attach the trace to a context
   228  func getTrace(ctx context.Context) string {
   229  	span := trace.SpanFromContext(ctx)
   230  	return span.SpanContext().TraceID().String()
   231  }
   232  
   233  // a mapping from the CS3 status codes to http codes
   234  var httpStatusCode = map[rpc.Code]int{
   235  	rpc.Code_CODE_ABORTED:              http.StatusConflict, // webdav uses 412 PreconditionFailed for locks and etags
   236  	rpc.Code_CODE_ALREADY_EXISTS:       http.StatusConflict,
   237  	rpc.Code_CODE_CANCELLED:            499, // Client Closed Request
   238  	rpc.Code_CODE_DATA_LOSS:            http.StatusInternalServerError,
   239  	rpc.Code_CODE_DEADLINE_EXCEEDED:    http.StatusGatewayTimeout,
   240  	rpc.Code_CODE_FAILED_PRECONDITION:  http.StatusPreconditionFailed,
   241  	rpc.Code_CODE_INSUFFICIENT_STORAGE: http.StatusInsufficientStorage,
   242  	rpc.Code_CODE_INTERNAL:             http.StatusInternalServerError,
   243  	rpc.Code_CODE_INVALID:              http.StatusInternalServerError,
   244  	rpc.Code_CODE_INVALID_ARGUMENT:     http.StatusBadRequest,
   245  	rpc.Code_CODE_NOT_FOUND:            http.StatusNotFound,
   246  	rpc.Code_CODE_OK:                   http.StatusOK,
   247  	rpc.Code_CODE_OUT_OF_RANGE:         http.StatusBadRequest,
   248  	rpc.Code_CODE_PERMISSION_DENIED:    http.StatusForbidden,
   249  	rpc.Code_CODE_REDIRECTION:          http.StatusTemporaryRedirect, // or permanent?
   250  	rpc.Code_CODE_RESOURCE_EXHAUSTED:   http.StatusTooManyRequests,
   251  	rpc.Code_CODE_UNAUTHENTICATED:      http.StatusUnauthorized,
   252  	rpc.Code_CODE_UNAVAILABLE:          http.StatusServiceUnavailable,
   253  	rpc.Code_CODE_UNIMPLEMENTED:        http.StatusNotImplemented,
   254  	rpc.Code_CODE_UNKNOWN:              http.StatusInternalServerError,
   255  	rpc.Code_CODE_LOCKED:               http.StatusLocked,
   256  	rpc.Code_CODE_TOO_EARLY:            http.StatusTooEarly,
   257  }
   258  
   259  // HTTPStatusFromCode returns an HTTP status code for the rpc code. It returns
   260  // an internal server error (500) if the code is unknown
   261  func HTTPStatusFromCode(code rpc.Code) int {
   262  	if s, ok := httpStatusCode[code]; ok {
   263  		return s
   264  	}
   265  	return http.StatusInternalServerError
   266  }