mirror of https://github.com/harness/drone.git
239 lines
7.8 KiB
Go
239 lines
7.8 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/harness/gitness/gitrpc/internal/types"
|
|
"github.com/harness/gitness/gitrpc/rpc"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
)
|
|
|
|
type Error struct {
|
|
Code codes.Code
|
|
Message string
|
|
Err error
|
|
details []proto.Message
|
|
}
|
|
|
|
func (e *Error) Error() string {
|
|
return fmt.Sprintf("%s, err: %v", e.Message, e.Err.Error())
|
|
}
|
|
|
|
func (e *Error) Status() (*status.Status, error) {
|
|
st := status.New(e.Code, e.Message)
|
|
if len(e.details) == 0 {
|
|
return st, nil
|
|
}
|
|
// add details
|
|
proto := st.Proto()
|
|
for _, detail := range e.details {
|
|
marshaled, err := anypb.New(detail)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
proto.Details = append(proto.Details, marshaled)
|
|
}
|
|
return status.FromProto(proto), nil
|
|
}
|
|
|
|
func (e *Error) Details() any {
|
|
return e.details
|
|
}
|
|
|
|
func (e *Error) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// Errorf generates new Error with status code and custom arguments.
|
|
// args can contain format args and additional arg like err which will be logged
|
|
// by middleware and details object type of map. Ordering of args element
|
|
// should first process format args and then error or detail.
|
|
func Errorf(code codes.Code, format string, args ...any) (err error) {
|
|
details := make([]proto.Message, 0, 8)
|
|
newargs := make([]any, 0, len(args))
|
|
|
|
for _, arg := range args {
|
|
if arg == nil {
|
|
continue
|
|
}
|
|
switch t := arg.(type) {
|
|
case error:
|
|
err = t
|
|
case proto.Message:
|
|
details = append(details, t)
|
|
default:
|
|
newargs = append(newargs, arg)
|
|
}
|
|
}
|
|
|
|
return &Error{
|
|
Code: code,
|
|
Message: fmt.Sprintf(format, newargs...),
|
|
Err: err,
|
|
details: details,
|
|
}
|
|
}
|
|
|
|
func wrapError(code codes.Code, err error) error {
|
|
var e *Error
|
|
if errors.As(err, &e) {
|
|
return err
|
|
}
|
|
return &Error{
|
|
Code: code,
|
|
Message: err.Error(),
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
// ErrCanceled wraps err with codes.Canceled, unless err is already a Error error.
|
|
func ErrCanceled(err error) error { return wrapError(codes.Canceled, err) }
|
|
|
|
// ErrDeadlineExceeded wraps err with codes.DeadlineExceeded, unless err is already a Error error.
|
|
func ErrDeadlineExceeded(err error) error { return wrapError(codes.DeadlineExceeded, err) }
|
|
|
|
// ErrInternal wraps err with codes.Internal, unless err is already a Error error.
|
|
func ErrInternal(err error) error { return wrapError(codes.Internal, err) }
|
|
|
|
// ErrInvalidArgument wraps err with codes.InvalidArgument, unless err is already a Error error.
|
|
func ErrInvalidArgument(err error) error { return wrapError(codes.InvalidArgument, err) }
|
|
|
|
// ErrNotFound wraps error with codes.NotFound, unless err is already a Error error.
|
|
func ErrNotFound(err error) error { return wrapError(codes.NotFound, err) }
|
|
|
|
// ErrFailedPrecondition wraps err with codes.FailedPrecondition, unless err is already a Error
|
|
// error.
|
|
func ErrFailedPrecondition(err error) error { return wrapError(codes.FailedPrecondition, err) }
|
|
|
|
// ErrUnavailable wraps err with codes.Unavailable, unless err is already a gRPC error.
|
|
func ErrUnavailable(err error) error { return wrapError(codes.Unavailable, err) }
|
|
|
|
// ErrPermissionDenied wraps err with codes.PermissionDenied, unless err is already a Error error.
|
|
func ErrPermissionDenied(err error) error { return wrapError(codes.PermissionDenied, err) }
|
|
|
|
// ErrAlreadyExists wraps err with codes.AlreadyExists, unless err is already a Error error.
|
|
func ErrAlreadyExists(err error) error { return wrapError(codes.AlreadyExists, err) }
|
|
|
|
// ErrAborted wraps err with codes.Aborted, unless err is already a Error type.
|
|
func ErrAborted(err error) error { return wrapError(codes.Aborted, err) }
|
|
|
|
// ErrCanceledf wraps a formatted error with codes.Canceled, unless the formatted error is a
|
|
// wrapped Error error.
|
|
func ErrCanceledf(format string, a ...interface{}) error {
|
|
return Errorf(codes.Canceled, format, a...)
|
|
}
|
|
|
|
// ErrDeadlineExceededf wraps a formatted error with codes.DeadlineExceeded, unless the formatted
|
|
// error is a wrapped Error error.
|
|
func ErrDeadlineExceededf(format string, a ...interface{}) error {
|
|
return Errorf(codes.DeadlineExceeded, format, a...)
|
|
}
|
|
|
|
// ErrInternalf wraps a formatted error with codes.Internal, unless the formatted error is a
|
|
// wrapped Error error.
|
|
func ErrInternalf(format string, a ...interface{}) error {
|
|
return Errorf(codes.Internal, format, a...)
|
|
}
|
|
|
|
// ErrInvalidArgumentf wraps a formatted error with codes.InvalidArgument, unless the formatted
|
|
// error is a wrapped Error error.
|
|
func ErrInvalidArgumentf(format string, a ...interface{}) error {
|
|
return Errorf(codes.InvalidArgument, format, a...)
|
|
}
|
|
|
|
// ErrNotFoundf wraps a formatted error with codes.NotFound, unless the
|
|
// formatted error is a wrapped Error error.
|
|
func ErrNotFoundf(format string, a ...interface{}) error {
|
|
return Errorf(codes.NotFound, format, a...)
|
|
}
|
|
|
|
// ErrFailedPreconditionf wraps a formatted error with codes.FailedPrecondition, unless the
|
|
// formatted error is a wrapped Error error.
|
|
func ErrFailedPreconditionf(format string, a ...interface{}) error {
|
|
return Errorf(codes.FailedPrecondition, format, a...)
|
|
}
|
|
|
|
// ErrUnavailablef wraps a formatted error with codes.Unavailable, unless the
|
|
// formatted error is a wrapped Error error.
|
|
func ErrUnavailablef(format string, a ...interface{}) error {
|
|
return Errorf(codes.Unavailable, format, a...)
|
|
}
|
|
|
|
// ErrPermissionDeniedf wraps a formatted error with codes.PermissionDenied, unless the formatted
|
|
// error is a wrapped Error error.
|
|
func ErrPermissionDeniedf(format string, a ...interface{}) error {
|
|
return Errorf(codes.PermissionDenied, format, a...)
|
|
}
|
|
|
|
// ErrAlreadyExistsf wraps a formatted error with codes.AlreadyExists, unless the formatted error is
|
|
// a wrapped Error error.
|
|
func ErrAlreadyExistsf(format string, a ...interface{}) error {
|
|
return Errorf(codes.AlreadyExists, format, a...)
|
|
}
|
|
|
|
// ErrAbortedf wraps a formatted error with codes.Aborted, unless the formatted error is a wrapped
|
|
// Error error.
|
|
func ErrAbortedf(format string, a ...interface{}) error {
|
|
return Errorf(codes.Aborted, format, a...)
|
|
}
|
|
|
|
// processGitErrorf translates error.
|
|
func processGitErrorf(err error, format string, args ...interface{}) error {
|
|
var (
|
|
cferr *types.MergeConflictsError
|
|
pferr *types.PathNotFoundError
|
|
)
|
|
const nl = "\n"
|
|
// when we add err as argument it will be part of the new error
|
|
args = append(args, err)
|
|
switch {
|
|
case errors.Is(err, types.ErrNotFound),
|
|
errors.Is(err, types.ErrSHADoesNotMatch),
|
|
errors.Is(err, types.ErrHunkNotFound):
|
|
return ErrNotFound(err)
|
|
case errors.As(err, &pferr):
|
|
rpcErr := &rpc.PathNotFoundError{
|
|
Path: pferr.Path,
|
|
}
|
|
return ErrNotFoundf("failed to find path", rpcErr, err)
|
|
case errors.Is(err, types.ErrAlreadyExists):
|
|
return ErrAlreadyExists(err)
|
|
case errors.Is(err, types.ErrInvalidArgument):
|
|
return ErrInvalidArgument(err)
|
|
case errors.As(err, &cferr):
|
|
stdout := strings.Trim(cferr.StdOut, nl)
|
|
conflictingFiles := strings.Split(stdout, nl)
|
|
files := &rpc.MergeConflictError{
|
|
ConflictingFiles: conflictingFiles,
|
|
}
|
|
return ErrFailedPreconditionf("merging failed due to conflicting changes with the target branch", files, err)
|
|
case types.IsMergeUnrelatedHistoriesError(err):
|
|
return ErrFailedPrecondition(err)
|
|
case errors.Is(err, types.ErrFailedToConnect):
|
|
return ErrInvalidArgument(err)
|
|
default:
|
|
return ErrInternalf(format, args...)
|
|
}
|
|
}
|