blob: 978d1d37f51b45b75c9dae1eec3760579aeb9cb0 [file] [log] [blame]
/*
* 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"
"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_GATEWAY_METHOD = "ERROR_YAML_INVALID_API_GATEWAY_METHOD"
ERROR_RUNTIME_PARSER_FAILURE = "ERROR_RUNTIME_PARSER_FAILURE"
)
/*
* 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")
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
}
/*
* 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
}
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
}