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 }