diff --git a/pkg/cloudcommon/validators/errors.go b/pkg/cloudcommon/validators/errors.go index 9c541e9063..375688ad5f 100644 --- a/pkg/cloudcommon/validators/errors.go +++ b/pkg/cloudcommon/validators/errors.go @@ -112,7 +112,7 @@ func newInvalidStructError(key string, err error) error { params := []interface{}{key} jsonClientErr, ok := err.(*httputils.JSONClientError) if ok { - errFmt += httperrors.MsgTmplToFmt(jsonClientErr.Data.Id) + errFmt += httputils.MsgTmplToFmt(jsonClientErr.Data.Id) for _, f := range jsonClientErr.Data.Fields { params = append(params, f) } diff --git a/pkg/cloudprovider/consts.go b/pkg/cloudprovider/consts.go index 58e0c56233..075cf1a982 100644 --- a/pkg/cloudprovider/consts.go +++ b/pkg/cloudprovider/consts.go @@ -16,6 +16,8 @@ package cloudprovider import ( "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/httperrors" ) const ( @@ -27,15 +29,13 @@ const ( CloudVMStatusDeploying = "deploying" CloudVMStatusOther = "other" - ErrUnauthenticated = errors.Error("not authenticated") - ErrUnauthorized = errors.Error("not authorized") - ErrNotFound = errors.Error("id not found") - ErrDuplicateId = errors.Error("duplicate id") - ErrInvalidStatus = errors.Error("invalid status") - ErrTimeout = errors.Error("timeout") - ErrNotImplemented = errors.Error("Not implemented") - ErrNotSupported = errors.Error("Not supported") - ErrInvalidProvider = errors.Error("Invalid provider") - ErrNoBalancePermission = errors.Error("No balance permission") - ErrBadRequest = errors.Error("bad request") + ErrNotFound = errors.ErrNotFound + ErrDuplicateId = errors.ErrDuplicateId + ErrInvalidStatus = errors.ErrInvalidStatus + ErrTimeout = errors.ErrTimeout + ErrNotImplemented = errors.ErrNotImplemented + ErrNotSupported = errors.ErrNotSupported + + ErrInvalidProvider = httperrors.ErrInvalidProvider + ErrNoBalancePermission = httperrors.ErrNoBalancePermission ) diff --git a/pkg/httperrors/consts.go b/pkg/httperrors/consts.go index e9d2ef95f8..8356f4a623 100644 --- a/pkg/httperrors/consts.go +++ b/pkg/httperrors/consts.go @@ -19,16 +19,134 @@ import ( ) const ( - ErrUnauthenticated = errors.Error("not authenticated") - ErrUnauthorized = errors.Error("not authorized") - ErrNotFound = errors.Error("id not found") - ErrDuplicateId = errors.Error("duplicate id") - ErrInvalidStatus = errors.Error("invalid status") - ErrTimeout = errors.Error("timeout") - ErrNotImplemented = errors.Error("Not implemented") - ErrNotSupported = errors.Error("Not supported") - ErrBadRequest = errors.Error("bad request") - ErrOutOfRange = errors.Error("out of range") - ErrForbidden = errors.Error("not allowed") - ErrOutOfLimit = errors.Error("out of limit") + ErrBadGateway = errors.Error("BadGateway") + ErrNotImplemented = errors.ErrNotImplemented + ErrInternalError = errors.Error("InternalServerError") + ErrResourceNotReady = errors.Error("ResourceNotReadyError") + ErrPayment = errors.Error("PaymentError") + + ErrImageNotFound = errors.Error("ImageNotFoundError") + ErrResourceNotFound = errors.Error("ResourceNotFoundError") + ErrSpecNotFound = errors.Error("SpecNotFoundError") + ErrActionNotFound = errors.Error("ActionNotFoundError") + ErrTenantNotFound = errors.Error("TenantNotFoundError") + ErrUserNotFound = errors.Error("UserNotFoundError") + + ErrServerStatus = errors.Error("ServerStatusError") + ErrInvalidStatus = errors.ErrInvalidStatus + + ErrInputParameter = errors.Error("InputParameterError") + ErrWeakPassword = errors.Error("WeakPasswordError") + ErrMissingParameter = errors.Error("MissingParameterError") + + ErrInsufficientResource = errors.Error("InsufficientResourceError") + ErrOutOfResource = errors.Error("OutOfResource") + ErrOutOfQuota = errors.Error("OutOfQuotaError") + ErrOutOfRange = errors.Error("OutOfRange") + ErrOutOfLimit = errors.Error("OutOfLimit") + + ErrNotSufficientPrivilege = errors.Error("NotSufficientPrivilegeError") + + ErrUnsupportedOperation = errors.Error("UnsupportOperationError") + ErrNotSupported = errors.ErrNotSupported + + ErrNotEmpty = errors.Error("NotEmptyError") + ErrBadRequest = errors.Error("BadRequestError") + + ErrUnauthorized = errors.Error("UnauthorizedError") + ErrInvalidCredential = errors.Error("InvalidCredentialError") + ErrUnauthenticated = ErrInvalidCredential + ErrForbidden = errors.Error("ForbiddenError") + + ErrNotFound = errors.ErrNotFound + + ErrNotAcceptable = errors.Error("NotAcceptableError") + + ErrDuplicateName = errors.Error("DuplicateNameError") + ErrDuplicateResource = errors.Error("DuplicateResourceError") + ErrConflict = errors.Error("ConflictError") + ErrDuplicateId = errors.ErrDuplicateId + + ErrResourceBusy = errors.Error("ResourceBusyError") + ErrRequireLicense = errors.Error("RequireLicenseError") + + ErrTimeout = errors.ErrTimeout + ErrProtectedResource = errors.Error("ProtectedResourceError") + ErrNoProject = errors.Error("NoProjectError") + + ErrInvalidProvider = errors.Error("InvalidProvider") + ErrNoBalancePermission = errors.Error("NoBalancePermission") ) + +var ( + httpErrorCode = map[errors.Error]int{ + errors.ErrClient: 400, + errors.ErrServer: 500, + errors.ErrUnclassified: 500, + + ErrInvalidProvider: 400, + ErrNoBalancePermission: 403, + + ErrBadGateway: 502, + ErrNotImplemented: 501, + ErrInternalError: 500, + ErrResourceNotReady: 500, + ErrPayment: 402, + + ErrImageNotFound: 404, + ErrResourceNotFound: 404, + ErrSpecNotFound: 404, + ErrActionNotFound: 404, + ErrTenantNotFound: 404, + ErrUserNotFound: 404, + + ErrServerStatus: 400, + ErrInvalidStatus: 400, + + ErrInputParameter: 400, + ErrWeakPassword: 400, + ErrMissingParameter: 400, + + ErrInsufficientResource: 400, + ErrOutOfResource: 500, + ErrOutOfQuota: 400, + ErrOutOfRange: 400, + ErrOutOfLimit: 400, + + ErrNotSufficientPrivilege: 403, + + ErrUnsupportedOperation: 406, + ErrNotSupported: 406, + + ErrNotEmpty: 406, + ErrBadRequest: 400, + + ErrUnauthorized: 401, + ErrInvalidCredential: 401, + ErrForbidden: 403, + + ErrNotFound: 404, + + ErrNotAcceptable: 406, + + ErrDuplicateName: 409, + ErrDuplicateResource: 409, + ErrConflict: 409, + ErrDuplicateId: 409, + + ErrResourceBusy: 409, + + ErrRequireLicense: 402, + + ErrTimeout: 504, + ErrProtectedResource: 403, + ErrNoProject: 403, + } +) + +func RegisterErrorHttpCode(err errors.Error, code int) { + if _, ok := httpErrorCode[err]; ok { + panic("Error has been registered: " + string(err)) + } + httpErrorCode[err] = code +} diff --git a/pkg/httperrors/errors.go b/pkg/httperrors/errors.go index de38d4510e..7ead5ce6b6 100644 --- a/pkg/httperrors/errors.go +++ b/pkg/httperrors/errors.go @@ -15,156 +15,45 @@ package httperrors import ( - "bytes" - "fmt" + "yunion.io/x/pkg/errors" "yunion.io/x/onecloud/pkg/util/httputils" ) -func NewJsonClientError(code int, title string, msg string, error httputils.Error) *httputils.JSONClientError { - err := httputils.JSONClientError{Code: code, Class: title, Details: msg, Data: error} - return &err -} - -func msgFmtToTmpl(msgFmt string) string { - // 将%s %d之类格式化字符串转换成{0}、{1}格式 - // 注意: 1.不支持复杂类型的转换例如%.2f , %[1]d, % x - // 2.原始msgFmt中如果包含{0},{1}形式的字符串同样会引发错误。 - // 在抛出error msgFmt时应注意避免 - fmtstr := false - lst := []rune(msgFmt) - lastIndex := len(lst) - 1 - temp := bytes.Buffer{} - index := 0 - for i, c := range lst { - switch c { - case '%': - if fmtstr || i == lastIndex { - temp.WriteRune(c) - fmtstr = false - } else { - fmtstr = true - } - case 'v', 'T', 't', 'b', 'c', 'd', 'o', 'q', 'x', 'X', 'U', 'e', 'E', 'f', 'F', 'g', 'G', 's', 'p': - if fmtstr { - temp.WriteRune('{') - temp.WriteString(fmt.Sprintf("%d", index)) - temp.WriteRune('}') - index++ - fmtstr = false - } else { - temp.WriteRune(c) - } - - default: - if fmtstr { - temp.WriteRune('%') - } - temp.WriteRune(c) - fmtstr = false - } - } - - return temp.String() -} - -func MsgTmplToFmt(tmpl string) string { - return msgTmplToFmt(tmpl) -} - -func msgTmplToFmt(tmpl string) string { - b := &bytes.Buffer{} - for i := 0; i < len(tmpl); { - r := tmpl[i] - if r != '{' { - b.WriteByte(r) - i++ - continue - } - - j := i + 1 - for ; j < len(tmpl); j++ { - r := tmpl[j] - if r < '0' || r > '9' { - break - } - } - if j == len(tmpl) { - b.WriteString(tmpl[i:]) - return b.String() - } - if j > i+1 && tmpl[j] == '}' { - b.WriteString("%s") - i = j + 1 - } else { - b.WriteString(tmpl[i:j]) - i = j - } - } - return b.String() -} - -func errorMessage(msgFmt string, params ...interface{}) (string, httputils.Error) { - fields := make([]string, len(params)) - for i, v := range params { - fields[i] = fmt.Sprint(v) - } - - err := httputils.Error{ - Id: msgFmtToTmpl(msgFmt), - Fields: fields, - } - - msg := msgFmt - if len(params) > 0 { - msg = fmt.Sprintf(msg, params...) - } - return msg, err -} - func NewBadGatewayError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(502, "BadGateway", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrBadGateway], string(ErrBadGateway), msg, params...) } func NewNotImplementedError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(501, "NotImplemented", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrNotImplemented], string(ErrNotImplemented), msg, params...) } func NewInternalServerError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(500, "InternalServerError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrInternalError], string(ErrInternalError), msg, params...) } func NewResourceNotReadyError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(500, "ResourceNotReadyError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrResourceNotReady], string(ErrResourceNotReady), msg, params...) } func NewOutOfResourceError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(500, "NewOutOfResourceError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrOutOfResource], string(ErrOutOfResource), msg, params...) } func NewServerStatusError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(400, "ServerStatusError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrServerStatus], string(ErrServerStatus), msg, params...) } func NewPaymentError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(402, "PaymentError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrPayment], string(ErrPayment), msg, params...) } func NewImageNotFoundError(imageId string) *httputils.JSONClientError { - msg, err := errorMessage("Image %s not found", imageId) - return NewJsonClientError(404, "ImageNotFoundError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrImageNotFound], string(ErrImageNotFound), "Image %s not found", imageId) } func NewResourceNotFoundError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(404, "ResourceNotFoundError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrResourceNotFound], string(ErrResourceNotFound), msg, params...) } func NewResourceNotFoundError2(keyword, id string) *httputils.JSONClientError { @@ -172,145 +61,141 @@ func NewResourceNotFoundError2(keyword, id string) *httputils.JSONClientError { } func NewSpecNotFoundError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(404, "SpecNotFoundError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrSpecNotFound], string(ErrSpecNotFound), msg, params...) } func NewActionNotFoundError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(404, "ActionNotFoundError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrActionNotFound], string(ErrActionNotFound), msg, params...) } func NewTenantNotFoundError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(404, "TenantNotFoundError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrTenantNotFound], string(ErrTenantNotFound), msg, params...) } func NewUserNotFoundError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(404, "UserNotFoundError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrUserNotFound], string(ErrUserNotFound), msg, params...) } func NewInvalidStatusError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(400, "InvalidStatusError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrInvalidStatus], string(ErrInvalidStatus), msg, params...) } func NewInputParameterError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(400, "InputParameterError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrInputParameter], string(ErrInputParameter), msg, params...) } func NewWeakPasswordError() *httputils.JSONClientError { - msg, err := errorMessage("password must be 12 chars of at least one digit, letter, uppercase letter and punctuate") - return NewJsonClientError(400, "WeakPasswordError", msg, err) + msg := ("password must be 12 chars of at least one digit, letter, uppercase letter and punctuate") + return httputils.NewJsonClientError(httpErrorCode[ErrWeakPassword], string(ErrWeakPassword), msg) } func NewMissingParameterError(paramName string) *httputils.JSONClientError { - msg, err := errorMessage("Missing parameter %s", paramName) - return NewJsonClientError(400, "MissingParameterError", msg, err) + msg := "Missing parameter %s" + return httputils.NewJsonClientError(httpErrorCode[ErrMissingParameter], string(ErrMissingParameter), msg, paramName) } func NewInsufficientResourceError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(400, "InsufficientResourceError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrInsufficientResource], string(ErrInsufficientResource), msg, params...) } func NewOutOfQuotaError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(400, "OutOfQuotaError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrOutOfQuota], string(ErrOutOfQuota), msg, params...) +} + +func NewOutOfRangeError(msg string, params ...interface{}) *httputils.JSONClientError { + return httputils.NewJsonClientError(httpErrorCode[ErrOutOfRange], string(ErrOutOfRange), msg, params...) +} + +func NewOutOfLimitError(msg string, params ...interface{}) *httputils.JSONClientError { + return httputils.NewJsonClientError(httpErrorCode[ErrOutOfLimit], string(ErrOutOfLimit), msg, params...) } func NewNotSufficientPrivilegeError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(403, "NotSufficientPrivilegeError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrNotSufficientPrivilege], string(ErrNotSufficientPrivilege), msg, params...) } func NewUnsupportOperationError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(406, "UnsupportOperationError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrUnsupportedOperation], string(ErrUnsupportedOperation), msg, params...) +} + +func NewNotSupportedError(msg string, params ...interface{}) *httputils.JSONClientError { + return httputils.NewJsonClientError(httpErrorCode[ErrNotSupported], string(ErrNotSupported), msg, params...) } func NewNotEmptyError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(406, "NotEmptyError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrNotEmpty], string(ErrNotEmpty), msg, params...) } func NewBadRequestError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(400, "BadRequestError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrBadRequest], string(ErrBadRequest), msg, params...) } func NewUnauthorizedError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(401, "UnauthorizedError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrUnauthorized], string(ErrUnauthorized), msg, params...) } func NewInvalidCredentialError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(401, "InvalidCredentialError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrInvalidCredential], string(ErrInvalidCredential), msg, params...) } func NewForbiddenError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(403, "ForbiddenError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrForbidden], string(ErrForbidden), msg, params...) } func NewNotFoundError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(404, "NotFoundError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrNotFound], string(ErrNotFound), msg, params...) } func NewNotAcceptableError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(406, "NotAcceptableError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrNotAcceptable], string(ErrNotAcceptable), msg, params...) } func NewDuplicateNameError(resName string, resId string) *httputils.JSONClientError { - msg, err := errorMessage("Duplicate %s %s", resName, resId) - return NewJsonClientError(409, "DuplicateNameError", msg, err) + msg := "Duplicate name %s %s" + return httputils.NewJsonClientError(httpErrorCode[ErrDuplicateName], string(ErrDuplicateName), msg, resName, resId) +} + +func NewDuplicateIdError(resName string, resId string) *httputils.JSONClientError { + msg := "Duplicate ID %s %s" + return httputils.NewJsonClientError(httpErrorCode[ErrDuplicateId], string(ErrDuplicateId), msg, resName, resId) } func NewDuplicateResourceError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params) - return NewJsonClientError(409, "DuplicateResourceError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrDuplicateResource], string(ErrDuplicateResource), msg, params...) } func NewConflictError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(409, "ConflictError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrConflict], string(ErrConflict), msg, params...) } func NewResourceBusyError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(409, "ResourceBusyError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrResourceBusy], string(ErrResourceBusy), msg, params...) } func NewRequireLicenseError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(402, "RequireLicenseError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrRequireLicense], string(ErrRequireLicense), msg, params...) } func NewTimeoutError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(504, "TimeoutError", msg, err) -} - -func NewGeneralError(err error) *httputils.JSONClientError { - switch err.(type) { - case *httputils.JSONClientError: - return err.(*httputils.JSONClientError) - default: - return NewInternalServerError(err.Error()) - } + return httputils.NewJsonClientError(httpErrorCode[ErrTimeout], string(ErrTimeout), msg, params...) } func NewProtectedResourceError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(403, "ProtectedResourceError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrProtectedResource], string(ErrProtectedResource), msg, params...) } func NewNoProjectError(msg string, params ...interface{}) *httputils.JSONClientError { - msg, err := errorMessage(msg, params...) - return NewJsonClientError(403, "NoProjectError", msg, err) + return httputils.NewJsonClientError(httpErrorCode[ErrNoProject], string(ErrNoProject), msg, params...) +} + +func NewServerError(msg string, params ...interface{}) *httputils.JSONClientError { + return httputils.NewJsonClientError(httpErrorCode[errors.ErrServer], string(errors.ErrServer), msg, params...) +} + +func NewClientError(msg string, params ...interface{}) *httputils.JSONClientError { + return httputils.NewJsonClientError(httpErrorCode[errors.ErrClient], string(errors.ErrClient), msg, params...) +} + +func NewUnclassifiedError(msg string, params ...interface{}) *httputils.JSONClientError { + return httputils.NewJsonClientError(httpErrorCode[errors.ErrUnclassified], string(errors.ErrUnclassified), msg, params) } diff --git a/pkg/httperrors/general.go b/pkg/httperrors/general.go new file mode 100644 index 0000000000..e2562f9081 --- /dev/null +++ b/pkg/httperrors/general.go @@ -0,0 +1,49 @@ +// Copyright 2019 Yunion +// +// 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 httperrors + +import ( + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/httputils" +) + +func NewGeneralError(err error) *httputils.JSONClientError { + switch nerr := err.(type) { + case *httputils.JSONClientError: + return nerr + case errors.Error: + code, ok := httpErrorCode[nerr] + if !ok { + code = 500 + } + return httputils.NewJsonClientError(code, string(nerr), err.Error()) + default: + root := errors.Cause(err) + switch nerr := root.(type) { + case *httputils.JSONClientError: + nerr.Details = err.Error() + return nerr + case errors.Error: + code, ok := httpErrorCode[nerr] + if !ok { + code = 500 + } + return httputils.NewJsonClientError(code, string(nerr), err.Error()) + default: + return NewUnclassifiedError(err.Error()) + } + } +} diff --git a/pkg/util/httputils/error.go b/pkg/util/httputils/error.go new file mode 100644 index 0000000000..1f5e2a7f1f --- /dev/null +++ b/pkg/util/httputils/error.go @@ -0,0 +1,112 @@ +package httputils + +import ( + "bytes" + "fmt" +) + +// New a http Json client error +// code: http error code, >=400 +// class: error class +// msg: message +// params: message format parameters +func NewJsonClientError(code int, class string, msg string, params ...interface{}) *JSONClientError { + details, err := errorMessage(msg, params...) + return &JSONClientError{Code: code, Class: class, Details: details, Data: err} +} + +func errorMessage(msgFmt string, params ...interface{}) (string, Error) { + fields := make([]string, len(params)) + for i, v := range params { + fields[i] = fmt.Sprint(v) + } + + err := Error{ + Id: msgFmtToTmpl(msgFmt), + Fields: fields, + } + + msg := msgFmt + if len(params) > 0 { + msg = fmt.Sprintf(msg, params...) + } + return msg, err +} + +func msgFmtToTmpl(msgFmt string) string { + // 将%s %d之类格式化字符串转换成{0}、{1}格式 + // 注意: 1.不支持复杂类型的转换例如%.2f , %[1]d, % x + // 2.原始msgFmt中如果包含{0},{1}形式的字符串同样会引发错误。 + // 在抛出error msgFmt时应注意避免 + fmtstr := false + lst := []rune(msgFmt) + lastIndex := len(lst) - 1 + temp := bytes.Buffer{} + index := 0 + for i, c := range lst { + switch c { + case '%': + if fmtstr || i == lastIndex { + temp.WriteRune(c) + fmtstr = false + } else { + fmtstr = true + } + case 'v', 'T', 't', 'b', 'c', 'd', 'o', 'q', 'x', 'X', 'U', 'e', 'E', 'f', 'F', 'g', 'G', 's', 'p': + if fmtstr { + temp.WriteRune('{') + temp.WriteString(fmt.Sprintf("%d", index)) + temp.WriteRune('}') + index++ + fmtstr = false + } else { + temp.WriteRune(c) + } + + default: + if fmtstr { + temp.WriteRune('%') + } + temp.WriteRune(c) + fmtstr = false + } + } + + return temp.String() +} + +func MsgTmplToFmt(tmpl string) string { + return msgTmplToFmt(tmpl) +} + +func msgTmplToFmt(tmpl string) string { + b := &bytes.Buffer{} + for i := 0; i < len(tmpl); { + r := tmpl[i] + if r != '{' { + b.WriteByte(r) + i++ + continue + } + + j := i + 1 + for ; j < len(tmpl); j++ { + r := tmpl[j] + if r < '0' || r > '9' { + break + } + } + if j == len(tmpl) { + b.WriteString(tmpl[i:]) + return b.String() + } + if j > i+1 && tmpl[j] == '}' { + b.WriteString("%s") + i = j + 1 + } else { + b.WriteString(tmpl[i:j]) + i = j + } + } + return b.String() +} diff --git a/pkg/util/httputils/httputils.go b/pkg/util/httputils/httputils.go index aadeefcd20..21cefd870e 100644 --- a/pkg/util/httputils/httputils.go +++ b/pkg/util/httputils/httputils.go @@ -33,6 +33,7 @@ import ( "github.com/moul/http2curl" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/trace" @@ -81,6 +82,18 @@ func (e *JSONClientError) Error() string { return jsonutils.Marshal(errMsg).String() } +func (err *JSONClientError) Cause() error { + if len(err.Class) > 0 { + return errors.Error(err.Class) + } else if err.Code >= 500 { + return errors.ErrServer + } else if err.Code >= 400 { + return errors.ErrClient + } else { + return errors.ErrUnclassified + } +} + func ErrorCode(err error) int { if err == nil { return 0 diff --git a/pkg/util/httputils/httputils_test.go b/pkg/util/httputils/httputils_test.go index 0098332bdb..dfdf0527bd 100644 --- a/pkg/util/httputils/httputils_test.go +++ b/pkg/util/httputils/httputils_test.go @@ -22,6 +22,7 @@ import ( "testing" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" ) type SErrorMsg struct { @@ -125,3 +126,18 @@ func TestError(t *testing.T) { } } + +func TestErrorCause(t *testing.T) { + err := errors.Error("TestError") + jsonError := &JSONClientError{ + Code: 400, + Class: string(err), + Details: "detailed test error", + } + wrapError := errors.Wrap(jsonError, "wrap1") + if errors.Cause(wrapError) == err { + t.Logf("%s", wrapError) + } else { + t.Errorf("wrapErro.Cause should be err: %#v != %#v", errors.Cause(wrapError), err) + } +}