alicloud/errors.go (350 lines of code) (raw):
package alicloud
import (
"regexp"
"strings"
"github.com/alibabacloud-go/tea/tea"
sls "github.com/aliyun/aliyun-log-go-sdk"
"fmt"
"log"
"runtime"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors"
"github.com/aliyun/aliyun-datahub-sdk-go/datahub"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/aliyun/fc-go-sdk"
"github.com/denverdino/aliyungo/common"
)
const (
// common
NotFound = "NotFound"
ResourceNotfound = "ResourceNotfound"
ServiceUnavailable = "ServiceUnavailable"
InstanceNotFound = "Instance.Notfound"
ForbiddenInstance = "Forbidden.InstanceNotFound"
MessageInstanceNotFound = "instance is not found"
Throttling = "Throttling"
ThrottlingUser = "Throttling.User"
// RAM Instance Not Found
RamInstanceNotFound = "Forbidden.InstanceNotFound"
AliyunGoClientFailure = "AliyunGoClientFailure"
LogClientTimeout = "Client.Timeout exceeded while awaiting headers"
InvalidFileSystemStatus_Ordering = "InvalidFileSystemStatus.Ordering"
NotFoundArticle = "not found article by given param"
)
var SlbIsBusy = []string{"SystemBusy", "OperationBusy", "ServiceIsStopping", "BackendServer.configuring", "ServiceIsConfiguring"}
var EcsNotFound = []string{"InvalidInstanceId.NotFound", "Forbidden.InstanceNotFound"}
var DiskInvalidOperation = []string{"IncorrectDiskStatus", "IncorrectInstanceStatus", "OperationConflict", "InternalError", "InvalidOperation.Conflict", "IncorrectDiskStatus.Initializing"}
var NetworkInterfaceInvalidOperations = []string{"InvalidOperation.InvalidEniState", "InvalidOperation.InvalidEcsState", "InvalidOperation.InvalidEniType", "InvalidOperation.HasMemberEniAttached", "OperationConflict", "ServiceUnavailable", "InternalError"}
var OperationDeniedDBStatus = []string{"LockTimeout", "InstanceConnectTimeoutFault", "ConcurrentTaskExceeded", "OperationDenied.DBStatus", "Database.ConnectError", "OperationDenied.DBInstanceStatus", "OperationDenied.DBClusterStatus", "InternalError", "OperationDenied.OutofUsage", "IncorrectDBInstanceState"}
var DBReadInstanceNotReadyStatus = []string{"OperationDenied.ReadDBInstanceStatus", "OperationDenied.MasterDBInstanceState", "ReadDBInstance.Mismatch"}
var NasNotFound = []string{"InvalidMountTarget.NotFound", "InvalidFileSystem.NotFound", "Forbidden.NasNotFound", "InvalidLBid.NotFound", "VolumeUnavailable"}
var SnapshotInvalidOperations = []string{"OperationConflict", "ServiceUnavailable", "InternalError", "SnapshotCreatedDisk", "SnapshotCreatedImage"}
var DiskNotSupportOnlineChangeErrors = []string{"InvalidDiskCategory.NotSupported", "InvalidRegion.NotSupport", "IncorrectInstanceStatus", "IncorrectDiskStatus", "InvalidOperation.InstanceTypeNotSupport"}
var DBInstanceTDEErrors = []string{"InvaildEngineInRegion.ValueNotSupported", "InstanceEngineType.NotSupport", "OperationDenied.DBInstanceType", "IncorrectDBInstanceType", "IncorrectEngineVersion", "DBSizeExceeded", "InvalidDBName.NotFound", "DbossGeneralError"}
// details at: https://help.aliyun.com/document_detail/27300.html
var OtsTableIsTemporarilyUnavailable = []string{"no such host", "OTSServerBusy", "OTSPartitionUnavailable", "OTSInternalServerError",
"OTSTimeout", "OTSServerUnavailable", "OTSRowOperationConflict", "OTSTableNotReady", "OTSNotEnoughCapacityUnit", "Too frequent table operations."}
var OtsTunnelIsTemporarilyUnavailable = []string{"no such host", "OTSTunnelServerUnavailable"}
var OtsSecondaryIndexIsTemporarilyUnavailable = []string{"no such host", "OTSServerUnavailable"}
var OtsSearchIndexIsTemporarilyUnavailable = []string{"no such host", "OTSServerUnavailable"}
// An Error represents a custom error for Terraform failure response
type ProviderError struct {
errorCode string
message string
}
func (e *ProviderError) Error() string {
return fmt.Sprintf("[ERROR] Terraform Alicloud Provider Error: Code: %s Message: %s", e.errorCode, e.message)
}
func (err *ProviderError) ErrorCode() string {
return err.errorCode
}
func (err *ProviderError) Message() string {
return err.message
}
func GetNotFoundErrorFromString(str string) error {
return &ProviderError{
errorCode: InstanceNotFound,
message: str,
}
}
func NotFoundError(err error) bool {
if err == nil {
return false
}
if e, ok := err.(*ComplexError); ok {
if e.Err != nil && strings.HasPrefix(e.Err.Error(), ResourceNotfound) {
return true
}
return NotFoundError(e.Cause)
}
if err == nil {
return false
}
if e, ok := err.(*tea.SDKError); ok {
return tea.IntValue(e.StatusCode) == 404 || regexp.MustCompile(NotFound).MatchString(tea.StringValue(e.Message))
}
if e, ok := err.(*errors.ServerError); ok {
return e.ErrorCode() == InstanceNotFound || e.ErrorCode() == RamInstanceNotFound || e.ErrorCode() == NotFound || e.HttpStatus() == 404 || strings.Contains(strings.ToLower(e.Message()), MessageInstanceNotFound)
}
if e, ok := err.(*ProviderError); ok {
return e.ErrorCode() == InstanceNotFound || e.ErrorCode() == RamInstanceNotFound || e.ErrorCode() == NotFound || strings.Contains(strings.ToLower(e.Message()), MessageInstanceNotFound)
}
if e, ok := err.(*common.Error); ok {
return e.Code == InstanceNotFound || e.Code == RamInstanceNotFound || e.Code == NotFound || strings.Contains(strings.ToLower(e.Message), MessageInstanceNotFound)
}
if e, ok := err.(oss.ServiceError); ok {
return e.StatusCode == 404 || strings.HasPrefix(e.Code, "NoSuch") || strings.HasPrefix(e.Message, "No Row found")
}
return false
}
func IsExpectedErrors(err error, expectCodes []string) bool {
if err == nil {
return false
}
if e, ok := err.(*ComplexError); ok {
return IsExpectedErrors(e.Cause, expectCodes)
}
if e, ok := err.(*tea.SDKError); ok {
for _, code := range expectCodes {
// The second statement aims to match the tea sdk history bug
if *e.Code == code || strings.HasPrefix(code, *e.Code) || strings.Contains(*e.Data, code) {
return true
}
}
return false
}
if e, ok := err.(*errors.ServerError); ok {
for _, code := range expectCodes {
if e.ErrorCode() == code || strings.Contains(e.Message(), code) {
return true
}
}
return false
}
if e, ok := err.(*ProviderError); ok {
for _, code := range expectCodes {
if e.ErrorCode() == code || strings.Contains(e.Message(), code) {
return true
}
}
return false
}
if e, ok := err.(*common.Error); ok {
for _, code := range expectCodes {
if e.Code == code || fmt.Sprint(e.StatusCode) == code || strings.Contains(e.Message, code) {
return true
}
}
return false
}
if e, ok := err.(*sls.Error); ok {
for _, code := range expectCodes {
if e.Code == code || strings.Contains(e.Message, code) || strings.Contains(e.String(), code) {
return true
}
}
return false
}
if e, ok := err.(oss.ServiceError); ok {
for _, code := range expectCodes {
if e.Code == code || strings.Contains(e.Message, code) {
return true
}
}
return false
}
if e, ok := err.(*fc.ServiceError); ok {
for _, code := range expectCodes {
if e.ErrorCode == code || strings.Contains(e.ErrorMessage, code) {
return true
}
}
return false
}
if e, ok := err.(*datahub.DatahubClientError); ok {
for _, code := range expectCodes {
if e.Code == code || strings.Contains(e.Message, code) {
return true
}
}
return false
}
for _, code := range expectCodes {
if strings.Contains(err.Error(), code) {
return true
}
}
return false
}
func NeedRetry(err error) bool {
if err == nil {
return false
}
postRegex := regexp.MustCompile("^Post [\"]*https://.*")
if postRegex.MatchString(err.Error()) {
return true
}
throttlingRegex := regexp.MustCompile("Throttling")
codeRegex := regexp.MustCompile("^code: 5[\\d]{2}")
if e, ok := err.(*tea.SDKError); ok {
if strings.Contains(*e.Message, "code: 500, 您已开通过") {
return false
}
if strings.Contains(*e.Message, "Client.Timeout") {
return true
}
if *e.Code == ServiceUnavailable || *e.Code == "Rejected.Throttling" || throttlingRegex.MatchString(*e.Code) || codeRegex.MatchString(*e.Message) {
return true
}
}
if e, ok := err.(*errors.ServerError); ok {
return e.ErrorCode() == ServiceUnavailable || e.ErrorCode() == "Rejected.Throttling" || throttlingRegex.MatchString(e.ErrorCode()) || codeRegex.MatchString(e.Message())
}
if e, ok := err.(*common.Error); ok {
return e.Code == ServiceUnavailable || e.Code == "Rejected.Throttling" || throttlingRegex.MatchString(e.Code) || codeRegex.MatchString(e.Message)
}
return false
}
func NoCodeRegexRetry(err error) bool {
if err == nil {
return false
}
postRegex := regexp.MustCompile("^Post [\"]*https://.*")
if postRegex.MatchString(err.Error()) {
return true
}
throttlingRegex := regexp.MustCompile("Throttling")
if e, ok := err.(*tea.SDKError); ok {
if strings.Contains(*e.Message, "code: 500, 您已开通过") {
return false
}
if strings.Contains(*e.Message, "Client.Timeout") {
return true
}
if *e.Code == ServiceUnavailable || *e.Code == "Rejected.Throttling" || throttlingRegex.MatchString(*e.Code) {
return true
}
}
if e, ok := err.(*errors.ServerError); ok {
return e.ErrorCode() == ServiceUnavailable || e.ErrorCode() == "Rejected.Throttling" || throttlingRegex.MatchString(e.ErrorCode())
}
if e, ok := err.(*common.Error); ok {
return e.Code == ServiceUnavailable || e.Code == "Rejected.Throttling" || throttlingRegex.MatchString(e.Code)
}
return false
}
func IsExpectedErrorCodes(code string, errorCodes []string) bool {
if code == "" {
return false
}
for _, v := range errorCodes {
if v == code {
return true
}
}
return false
}
func GetTimeErrorFromString(str string) error {
return &ProviderError{
errorCode: "WaitForTimeout",
message: str,
}
}
func GetNotFoundMessage(product, id string) string {
return fmt.Sprintf("The specified %s %s is not found.", product, id)
}
func GetTimeoutMessage(product, status string) string {
return fmt.Sprintf("Waitting for %s %s is timeout.", product, status)
}
func GetCreateFailedMessage(product string) string {
return fmt.Sprintf("The specified %s is create failed.", product)
}
type ErrorSource string
const (
AlibabaCloudSdkGoERROR = ErrorSource("[SDK alibaba-cloud-sdk-go ERROR]")
AliyunLogGoSdkERROR = ErrorSource("[SDK aliyun-log-go-sdk ERROR]")
AliyunDatahubSdkGo = ErrorSource("[SDK aliyun-datahub-sdk-go ERROR]")
AliyunOssGoSdk = ErrorSource("[SDK aliyun-oss-go-sdk ERROR]")
FcGoSdk = ErrorSource("[SDK fc-go-sdk ERROR]")
DenverdinoAliyungo = ErrorSource("[SDK denverdino/aliyungo ERROR]")
AliyunTablestoreGoSdk = ErrorSource("[SDK aliyun-tablestore-go-sdk ERROR]")
AliMnsERROR = ErrorSource("[SDK ali_mns ERROR]")
ProviderERROR = ErrorSource("[Provider ERROR]")
)
// ComplexError is a format error which including origin error, extra error message, error occurred file and line
// Cause: a error is a origin error that comes from SDK, some exceptions and so on
// Err: a new error is built from extra message
// Path: the file path of error occurred
// Line: the file line of error occurred
type ComplexError struct {
Cause error
Err error
Path string
Line int
}
func (e ComplexError) Error() string {
if e.Cause == nil {
e.Cause = Error("<nil cause>")
}
if e.Err == nil {
return fmt.Sprintf("\u001B[31m[ERROR]\u001B[0m %s:%d:\n%s", e.Path, e.Line, e.Cause.Error())
}
return fmt.Sprintf("\u001B[31m[ERROR]\u001B[0m %s:%d: %s:\n%s", e.Path, e.Line, e.Err.Error(), e.Cause.Error())
}
func Error(format string, args ...interface{}) error {
return fmt.Errorf(format, args...)
}
func NotFoundErr(args ...interface{}) error {
return fmt.Errorf(notFoundFmt, args...)
}
// Return a ComplexError which including error occurred file and path
func WrapError(cause error) error {
if cause == nil {
return nil
}
_, filepath, line, ok := runtime.Caller(1)
if !ok {
log.Printf("\u001B[31m[ERROR]\u001B[0m runtime.Caller error in WrapError.")
return WrapComplexError(cause, nil, "", -1)
}
parts := strings.Split(filepath, "/")
if len(parts) > 3 {
filepath = strings.Join(parts[len(parts)-3:], "/")
}
return WrapComplexError(cause, nil, filepath, line)
}
// Return a ComplexError which including extra error message, error occurred file and path
func WrapErrorf(cause error, msg string, args ...interface{}) error {
if cause == nil && strings.TrimSpace(msg) == "" {
return nil
}
_, filepath, line, ok := runtime.Caller(1)
if !ok {
log.Printf("\u001B[31m[ERROR]\u001B[0m runtime.Caller error in WrapErrorf.")
return WrapComplexError(cause, Error("%s", msg), "", -1)
}
parts := strings.Split(filepath, "/")
if len(parts) > 3 {
filepath = strings.Join(parts[len(parts)-3:], "/")
}
// The second parameter of args is requestId, if the error message is NotFoundMsg the requestId need to be returned.
if msg == NotFoundMsg && len(args) == 2 {
msg += RequestIdMsg
}
return WrapComplexError(cause, fmt.Errorf(msg, args...), filepath, line)
}
func WrapComplexError(cause, err error, filepath string, fileline int) error {
return &ComplexError{
Cause: cause,
Err: err,
Path: filepath,
Line: fileline,
}
}
// A default message of ComplexError's Err. It is format to Resource <resource-id> <operation> Failed!!! <error source>
const DefaultErrorMsg = "Resource %s %s Failed!!! %s"
const ResponseCodeMsg = "Resource %s %s Failed!!! %v"
const RequestIdMsg = "RequestId: %s"
const notFoundFmt = "The specified %s %s is not found."
const NotFoundMsg = ResourceNotfound + "!!! %s"
const NotFoundWithResponse = ResourceNotfound + "!!! Response: %v"
const NotFoundWithError = ResourceNotfound + "!!! Error: %v"
const DefaultTimeoutMsg = "Resource %s %s Timeout!!! %s"
const DeleteTimeoutMsg = "Resource %s Still Exists. %s Timeout!!! %s"
const WaitTimeoutMsg = "Resource %s %s Timeout In %d Seconds. Got: %s Expected: %s !!! %s"
const DataDefaultErrorMsg = "Datasource %s %s Failed!!! %s"
const SweepDefaultErrorMsg = "Sweep %s %s Failed!!!"
const IdMsg = "Resource id:%s "
const FailedGetAttributeMsg = "Getting resource %s attribute by path %s failed!!! Response: %v."
const DefaultDebugMsg = "\n*************** %s Response *************** \n%s\n%s******************************\n\n"
const FailedToReachTargetStatus = "Failed to reach target status. Last status: %s."
const FailedToReachTargetStatusWithResponse = "Resource %s failed to reach target status. Last response: %s"
const FailedToReachTargetStatusWithError = "Resource %s failed to reach target status. Last error: %s"
const FailedToReachTargetStatusWithRequestId = FailedToReachTargetStatus + " Last RequestId: %s."
const FailedToReachTargetAttribute = "Failed to reach value for target attribute. Current value is %s."
const RequiredWhenMsg = "attribute '%s' is required when '%s' is %v"