wskderrors/wskdeployerror.go (397 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 wskderrors import ( "fmt" "github.com/apache/openwhisk-wskdeploy/wski18n" "github.com/apache/openwhisk-wskdeploy/wskprint" "io/ioutil" "net/http" "path/filepath" "runtime" "strings" ) const ( // Error message compositional strings STR_UNKNOWN_VALUE = "Unknown value" STR_COMMAND = "Command" STR_ERROR_CODE = "Error code" STR_FILE = "File" STR_PARAMETER = "Parameter" STR_TYPE = "Type" STR_EXPECTED = "Expected" STR_ACTUAL = "Actual" STR_NEWLINE = "\n" STR_ACTION = "Action" STR_RUNTIME = "Runtime" STR_SUPPORTED_RUNTIMES = "Supported Runtimes" STR_HTTP_STATUS = "HTTP Response Status" STR_HTTP_BODY = "HTTP Response Body" STR_SUPPORTED_WEB_EXPORTS = "Supported Web Exports" STR_WEB_EXPORT = "web-export" STR_API = "API" STR_API_METHOD = "API gateway method" STR_API_SUPPORTED_METHODS = "API gateway supported methods" // Formatting STR_INDENT_1 = "==>" // Error Types ERROR_COMMAND_FAILED = "ERROR_COMMAND_FAILED" ERROR_WHISK_CLIENT_ERROR = "ERROR_WHISK_CLIENT_ERROR" ERROR_WHISK_CLIENT_INVALID_CONFIG = "ERROR_WHISK_CLIENT_INVALID_CONFIG" ERROR_FILE_READ_ERROR = "ERROR_FILE_READ_ERROR" ERROR_MANIFEST_FILE_NOT_FOUND = "ERROR_MANIFEST_FILE_NOT_FOUND" ERROR_YAML_FILE_FORMAT_ERROR = "ERROR_YAML_FILE_FORMAT_ERROR" ERROR_YAML_PARSER_ERROR = "ERROR_YAML_PARSER_ERROR" ERROR_YAML_PARAMETER_TYPE_MISMATCH = "ERROR_YAML_PARAMETER_TYPE_MISMATCH" ERROR_YAML_INVALID_PARAMETER_TYPE = "ERROR_YAML_INVALID_PARAMETER_TYPE" ERROR_YAML_INVALID_RUNTIME = "ERROR_YAML_INVALID_RUNTIME" ERROR_YAML_INVALID_WEB_EXPORT = "ERROR_YAML_INVALID_WEB_EXPORT" ERROR_YAML_INVALID_API = "ERROR_YAML_INVALID_API" ERROR_YAML_INVALID_API_GATEWAY_METHOD = "ERROR_YAML_INVALID_API_GATEWAY_METHOD" ERROR_RUNTIME_PARSER_FAILURE = "ERROR_RUNTIME_PARSER_FAILURE" ERROR_ACTION_ANNOTATION = "ERROR_ACTION_ANNOTATION" ) /* * BaseError */ type WskDeployBaseErr struct { ErrorType string FileName string LineNum int Message string MessageFormat string } func NewWskDeployBaseError(typ string, fn string, ln int, msg string) *WskDeployBaseErr { var err = &WskDeployBaseErr{ ErrorType: typ, FileName: fn, LineNum: ln, } err.SetMessage(msg) return err } func (e *WskDeployBaseErr) Error() string { return fmt.Sprintf("%s [%d]: [%s]: %s\n", e.FileName, e.LineNum, e.ErrorType, e.Message) } func (e *WskDeployBaseErr) SetFileName(fileName string) { e.FileName = filepath.Base(fileName) } func (e *WskDeployBaseErr) SetLineNum(lineNum int) { e.LineNum = lineNum } func (e *WskDeployBaseErr) SetErrorType(errorType string) { e.ErrorType = errorType } func (e *WskDeployBaseErr) SetMessageFormat(fmt string) { e.MessageFormat = fmt } func (e *WskDeployBaseErr) GetMessage() string { return e.Message } func (e *WskDeployBaseErr) GetMessageFormat() string { return e.MessageFormat } func (e *WskDeployBaseErr) SetMessage(message interface{}) { if message != nil { switch message.(type) { case string: e.Message = message.(string) case error: err := message.(error) e.appendErrorDetails(err) } } } func (e *WskDeployBaseErr) AppendDetail(detail string) { e.appendDetail(detail) } func (e *WskDeployBaseErr) appendDetail(detail string) { fmt := fmt.Sprintf("\n%s %s", STR_INDENT_1, detail) e.Message = e.Message + fmt } func (e *WskDeployBaseErr) appendErrorDetails(err error) { if err != nil { errorMsg := err.Error() var detailMsg string msgs := strings.Split(errorMsg, STR_NEWLINE) for i := 0; i < len(msgs); i++ { detailMsg = msgs[i] e.appendDetail(strings.TrimSpace(detailMsg)) } } } // func Caller(skip int) (pc uintptr, file string, line int, ok bool) func (e *WskDeployBaseErr) SetCallerByStackFrameSkip(skip int) { _, fname, lineNum, _ := runtime.Caller(skip) e.SetFileName(fname) e.SetLineNum(lineNum) } /* * CommandError */ type CommandError struct { WskDeployBaseErr Command string } func NewCommandError(cmd string, errorMessage string) *CommandError { var err = &CommandError{ Command: cmd, } err.SetErrorType(ERROR_COMMAND_FAILED) err.SetCallerByStackFrameSkip(2) err.SetMessageFormat("%s: [%s]: %s") str := fmt.Sprintf(err.MessageFormat, STR_COMMAND, cmd, errorMessage) err.SetMessage(str) return err } /* * WhiskClientError */ type WhiskClientError struct { WskDeployBaseErr ErrorCode int } func NewWhiskClientError(errorMessage string, code int, response *http.Response) *WhiskClientError { var err = &WhiskClientError{ ErrorCode: code, } err.SetErrorType(ERROR_WHISK_CLIENT_ERROR) err.SetCallerByStackFrameSkip(2) err.SetMessageFormat("%s: %d: %s") var str = fmt.Sprintf(err.MessageFormat, STR_ERROR_CODE, code, errorMessage) if response != nil { responseData, _ := ioutil.ReadAll(response.Body) err.SetMessageFormat("%s: %d: %s: %s: %s %s: %s") // do not add body in case of a success // when response.Status is 200, response.Body contains the entire action source code // we should not expose the action source when the HTTP request was successful if strings.Contains(response.Status, "200 OK") { str = fmt.Sprintf(err.MessageFormat, STR_ERROR_CODE, code, errorMessage, STR_HTTP_STATUS, response.Status, "", "") } else { str = fmt.Sprintf(err.MessageFormat, STR_ERROR_CODE, code, errorMessage, STR_HTTP_STATUS, response.Status, STR_HTTP_BODY, string(responseData)) } } err.SetMessage(str) return err } /* * WhiskClientInvalidConfigError */ type WhiskClientInvalidConfigError struct { WskDeployBaseErr } func NewWhiskClientInvalidConfigError(errorMessage string) *WhiskClientInvalidConfigError { var err = &WhiskClientInvalidConfigError{} err.SetErrorType(ERROR_WHISK_CLIENT_INVALID_CONFIG) err.SetCallerByStackFrameSkip(2) err.SetMessage(errorMessage) return err } /* * FileError */ type FileError struct { WskDeployBaseErr ErrorFileName string ErrorFilePath string } func (e *FileError) SetErrorFilePath(fpath string) { e.ErrorFilePath = fpath e.ErrorFileName = filepath.Base(fpath) } func (e *FileError) SetErrorFileName(fname string) { e.ErrorFilePath = fname } func (e *FileError) Error() string { return fmt.Sprintf("%s [%d]: [%s]: "+STR_FILE+": [%s]: %s\n", e.FileName, e.LineNum, e.ErrorType, e.ErrorFileName, e.Message) } /* * FileReadError */ type FileReadError struct { FileError } func NewFileReadError(fpath string, errMessage interface{}) *FileReadError { var err = &FileReadError{} err.SetErrorType(ERROR_FILE_READ_ERROR) err.SetCallerByStackFrameSkip(2) err.SetErrorFilePath(fpath) err.SetMessage(errMessage) return err } /* * ManifestFileNotFoundError */ type ErrorManifestFileNotFound struct { FileError } func NewErrorManifestFileNotFound(fpath string, errMessage interface{}) *ErrorManifestFileNotFound { var err = &ErrorManifestFileNotFound{} err.SetErrorType(ERROR_MANIFEST_FILE_NOT_FOUND) err.SetCallerByStackFrameSkip(2) err.SetErrorFilePath(fpath) err.SetMessage(errMessage) return err } /* * YAMLFileFormatError */ type YAMLFileFormatError struct { FileError } func NewYAMLFileFormatError(fpath string, errorMessage interface{}) *YAMLFileFormatError { var err = &YAMLFileFormatError{} err.SetErrorType(ERROR_YAML_FILE_FORMAT_ERROR) err.SetCallerByStackFrameSkip(2) err.SetErrorFilePath(fpath) err.SetMessage(errorMessage) return err } /* * ParameterTypeMismatchError */ type ParameterTypeMismatchError struct { FileError Parameter string ExpectedType string ActualType string } func NewParameterTypeMismatchError(fpath string, param string, expectedType string, actualType string) *ParameterTypeMismatchError { var err = &ParameterTypeMismatchError{ ExpectedType: expectedType, ActualType: actualType, } err.SetErrorType(ERROR_YAML_PARAMETER_TYPE_MISMATCH) err.SetCallerByStackFrameSkip(2) err.SetErrorFilePath(fpath) err.SetMessageFormat("%s [%s]: %s %s: [%s], %s: [%s]") str := fmt.Sprintf(err.MessageFormat, STR_PARAMETER, param, STR_TYPE, STR_EXPECTED, expectedType, STR_ACTUAL, actualType) err.SetMessage(str) return err } /* * InvalidParameterType */ type InvalidParameterTypeError struct { FileError Parameter string ActualType string } func NewInvalidParameterTypeError(fpath string, param string, actualType string) *ParameterTypeMismatchError { var err = &ParameterTypeMismatchError{ ActualType: actualType, } err.SetErrorFilePath(fpath) err.SetErrorType(ERROR_YAML_INVALID_PARAMETER_TYPE) err.SetCallerByStackFrameSkip(2) err.SetMessageFormat("%s [%s]: %s [%s]") str := fmt.Sprintf(err.MessageFormat, STR_PARAMETER, param, STR_TYPE, actualType) err.SetMessage(str) return err } /* * YAMLParserErr */ type YAMLParserError struct { FileError lines []string msgs []string } func NewYAMLParserErr(fpath string, msg interface{}) *YAMLParserError { var err = &YAMLParserError{} err.SetErrorType(ERROR_YAML_PARSER_ERROR) err.SetErrorFilePath(fpath) err.SetCallerByStackFrameSkip(2) err.SetMessage(msg) return err } /* * InvalidRuntime */ type InvalidRuntimeError struct { FileError Runtime string SupportedRuntimes []string } func NewInvalidRuntimeError(errMessage string, fpath string, action string, runtime string, supportedRuntimes []string) *InvalidRuntimeError { var err = &InvalidRuntimeError{ SupportedRuntimes: supportedRuntimes, } err.SetErrorFilePath(fpath) err.SetErrorType(ERROR_YAML_INVALID_RUNTIME) err.SetCallerByStackFrameSkip(2) str := fmt.Sprintf("%s %s [%s]: %s [%s]: %s [%s]", errMessage, STR_ACTION, action, STR_RUNTIME, runtime, STR_SUPPORTED_RUNTIMES, strings.Join(supportedRuntimes, ", ")) err.SetMessage(str) return err } /* * InvalidWebExport */ type InvalidWebExportError struct { FileError Webexport string SupportedWebexports []string } func NewInvalidWebExportError(fpath string, action string, webexport string, supportedWebexports []string) *InvalidWebExportError { var err = &InvalidWebExportError{ SupportedWebexports: supportedWebexports, } err.SetErrorFilePath(fpath) err.SetErrorType(ERROR_YAML_INVALID_WEB_EXPORT) err.SetCallerByStackFrameSkip(2) str := fmt.Sprintf("%s [%s]: %s [%s]: %s [%s]", STR_ACTION, action, STR_WEB_EXPORT, webexport, STR_SUPPORTED_WEB_EXPORTS, strings.Join(supportedWebexports, ", ")) err.SetMessage(str) return err } /* * Invalid API Gateway Method */ type InvalidAPIGatewayMethodError struct { FileError method string SupportedMethods []string } func NewInvalidAPIGatewayMethodError(fpath string, api string, method string, supportedMethods []string) *InvalidAPIGatewayMethodError { var err = &InvalidAPIGatewayMethodError{ SupportedMethods: supportedMethods, } err.SetErrorFilePath(fpath) err.SetErrorType(ERROR_YAML_INVALID_API_GATEWAY_METHOD) err.SetCallerByStackFrameSkip(2) str := fmt.Sprintf("%s [%s]: %s [%s]: %s [%s]", STR_API, api, STR_API_METHOD, method, STR_API_SUPPORTED_METHODS, strings.Join(supportedMethods, ", ")) err.SetMessage(str) return err } /* * Invalid Web Action API */ type InvalidWebActionAPIError struct { WskDeployBaseErr } func NewInvalidWebActionError(apiName string, actionName string, isSequence bool) *InvalidWebActionAPIError { var err = &InvalidWebActionAPIError{} i18nErrorID := wski18n.ID_ERR_API_MISSING_WEB_ACTION_X_action_X_api_X if isSequence { i18nErrorID = wski18n.ID_ERR_API_MISSING_WEB_SEQUENCE_X_sequence_X_api_X } errString := wski18n.T(i18nErrorID, map[string]interface{}{ wski18n.KEY_SEQUENCE: actionName, wski18n.KEY_API: apiName}) wskprint.PrintOpenWhiskWarning(errString) err.SetErrorType(ERROR_YAML_INVALID_API) err.SetCallerByStackFrameSkip(2) err.SetMessage(errString) return err } /* * Failed to Retrieve/Parse Runtime */ type RuntimeParserError struct { WskDeployBaseErr } func NewRuntimeParserError(errorMsg string) *RuntimeParserError { var err = &RuntimeParserError{} err.SetErrorType(ERROR_RUNTIME_PARSER_FAILURE) err.SetCallerByStackFrameSkip(2) err.SetMessage(errorMsg) return err } /* * Failed to Retrieve/Parse Runtime */ type DeployError struct { WskDeployBaseErr } func NewActionSecureKeyError(errorMsg string) *DeployError { var err = &DeployError{} err.SetErrorType(ERROR_ACTION_ANNOTATION) err.SetCallerByStackFrameSkip(2) err.SetMessage(errorMsg) return err } func IsCustomError(err error) bool { switch err.(type) { case *CommandError: case *WhiskClientError: case *WhiskClientInvalidConfigError: case *FileError: case *FileReadError: case *ErrorManifestFileNotFound: case *YAMLFileFormatError: case *ParameterTypeMismatchError: case *InvalidParameterTypeError: case *YAMLParserError: return true } return false } func AppendDetailToErrorMessage(detail string, add string, location int) string { if len(detail) == 0 { detail = "\n" } _, fname, lineNum, _ := runtime.Caller(location) detail += fmt.Sprintf(" >> %s [%v]: %s", filepath.Base(fname), lineNum, add) return detail }