alicloud/common.go (1,612 lines of code) (raw):
package alicloud
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"os/user"
"path/filepath"
"reflect"
"runtime"
"sort"
"strconv"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"github.com/denverdino/aliyungo/cs"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
"github.com/aliyun/aliyun-datahub-sdk-go/datahub"
sls "github.com/aliyun/aliyun-log-go-sdk"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
"github.com/aliyun/fc-go-sdk"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"gopkg.in/yaml.v2"
"math"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/denverdino/aliyungo/common"
"github.com/google/uuid"
"github.com/mitchellh/go-homedir"
)
type PayType string
const (
PrePaid = PayType("PrePaid")
PostPaid = PayType("PostPaid")
Prepaid = PayType("Prepaid")
Postpaid = PayType("Postpaid")
Serverless = PayType("Serverless")
)
const (
NormalMode = "normal"
SafetyMode = "safety"
)
type DdosbgpInsatnceType string
const (
Enterprise = DdosbgpInsatnceType("Enterprise")
Professional = DdosbgpInsatnceType("Professional")
)
type DdosbgpInstanceIpType string
const (
IPv4 = DdosbgpInstanceIpType("IPv4")
IPv6 = DdosbgpInstanceIpType("IPv6")
)
type NetType string
const (
Internet = NetType("Internet")
Intranet = NetType("Intranet")
)
type NetworkType string
const (
Classic = NetworkType("Classic")
Vpc = NetworkType("Vpc")
ClassicInternet = NetworkType("classic_internet")
ClassicIntranet = NetworkType("classic_intranet")
PUBLIC = NetworkType("PUBLIC")
PRIVATE = NetworkType("PRIVATE")
)
type NodeType string
const (
WORKER = NodeType("WORKER")
KIBANA = NodeType("KIBANA")
)
type ActionType string
const (
OPEN = ActionType("OPEN")
CLOSE = ActionType("CLOSE")
)
type TimeType string
const (
Hour = TimeType("Hour")
Day = TimeType("Day")
Week = TimeType("Week")
Month = TimeType("Month")
Year = TimeType("Year")
)
type IpVersion string
const (
IPV4 = IpVersion("ipv4")
IPV6 = IpVersion("ipv6")
)
type Status string
const (
Pending = Status("Pending")
Creating = Status("Creating")
Running = Status("Running")
Available = Status("Available")
Unavailable = Status("Unavailable")
Modifying = Status("Modifying")
Deleting = Status("Deleting")
Starting = Status("Starting")
Stopping = Status("Stopping")
Stopped = Status("Stopped")
Normal = Status("Normal")
Changing = Status("Changing")
Online = Status("online")
Configuring = Status("configuring")
Associating = Status("Associating")
Unassociating = Status("Unassociating")
InUse = Status("InUse")
DiskInUse = Status("In_use")
Active = Status("Active")
Inactive = Status("Inactive")
Idle = Status("Idle")
SoldOut = Status("SoldOut")
InService = Status("InService")
Removing = Status("Removing")
DisabledStatus = Status("Disabled")
Init = Status("Init")
Provisioning = Status("Provisioning")
Updating = Status("Updating")
FinancialLocked = Status("FinancialLocked")
PUBLISHED = Status("Published")
NOPUBLISHED = Status("NonPublished")
Deleted = Status("Deleted")
Null = Status("Null")
Enable = Status("Enable")
BINDED = Status("BINDED")
)
type IPType string
const (
Inner = IPType("Inner")
Private = IPType("Private")
Public = IPType("Public")
)
type ResourceType string
const (
ResourceTypeInstance = ResourceType("Instance")
ResourceTypeDisk = ResourceType("Disk")
ResourceTypeVSwitch = ResourceType("VSwitch")
ResourceTypeRds = ResourceType("Rds")
ResourceTypePolarDB = ResourceType("PolarDB")
IoOptimized = ResourceType("IoOptimized")
ResourceTypeRkv = ResourceType("KVStore")
ResourceTypeFC = ResourceType("FunctionCompute")
ResourceTypeElasticsearch = ResourceType("Elasticsearch")
ResourceTypeSlb = ResourceType("Slb")
ResourceTypeMongoDB = ResourceType("MongoDB")
ResourceTypeGpdb = ResourceType("Gpdb")
ResourceTypeHBase = ResourceType("HBase")
ResourceTypeAdb = ResourceType("ADB")
ResourceTypeCassandra = ResourceType("Cassandra")
)
type InternetChargeType string
const (
PayByBandwidth = InternetChargeType("PayByBandwidth")
PayByTraffic = InternetChargeType("PayByTraffic")
PayBy95 = InternetChargeType("PayBy95")
)
type AccountSite string
const (
DomesticSite = AccountSite("Domestic")
IntlSite = AccountSite("International")
)
const (
SnapshotCreatingInProcessing = Status("progressing")
SnapshotCreatingAccomplished = Status("accomplished")
SnapshotCreatingFailed = Status("failed")
SnapshotPolicyCreating = Status("Creating")
SnapshotPolicyAvailable = Status("available")
SnapshotPolicyNormal = Status("Normal")
)
// timeout for common product, ecs e.g.
const DefaultTimeout = 120
const Timeout5Minute = 300
const DefaultTimeoutMedium = 500
// timeout for long time progerss product, rds e.g.
const DefaultLongTimeout = 1000
const DefaultIntervalMini = 2
const DefaultIntervalShort = 5
const DefaultIntervalMedium = 10
const DefaultIntervalLong = 20
const (
PageNumSmall = 1
PageSizeSmall = 10
PageSizeMedium = 20
PageSizeLarge = 50
PageSizeXLarge = 100
)
// Protocol represents network protocol
type Protocol string
// Constants of protocol definition
const (
Http = Protocol("http")
Https = Protocol("https")
Tcp = Protocol("tcp")
Udp = Protocol("udp")
All = Protocol("all")
Icmp = Protocol("icmp")
Gre = Protocol("gre")
)
const (
// HeaderEnableEBTrigger header key for enabling eventbridge trigger
// TODO: delete the header after eventbridge trigger is totally exposed to user
HeaderEnableEBTrigger = "x-fc-enable-eventbridge-trigger"
)
// ValidProtocols network protocol list
var ValidProtocols = []Protocol{Http, Https, Tcp, Udp}
// simple array value check method, support string type only
func isProtocolValid(value string) bool {
res := false
for _, v := range ValidProtocols {
if string(v) == value {
res = true
}
}
return res
}
// default region for all resource
const DEFAULT_REGION = "cn-beijing"
const INT_MAX = 2147483647
// symbol of multiIZ
const MULTI_IZ_SYMBOL = "MAZ"
const COMMA_SEPARATED = ","
const COLON_SEPARATED = ":"
const SLASH_SEPARATED = "/"
const LOCAL_HOST_IP = "127.0.0.1"
// Takes the result of flatmap.Expand for an array of strings
// and returns a []string
func expandStringList(configured []interface{}) []string {
vs := make([]string, 0, len(configured))
for _, v := range configured {
if v == nil {
continue
}
vs = append(vs, v.(string))
}
return vs
}
// Takes list of string to strings. Expand to an array
// of raw strings and returns a []interface{}
func convertListStringToListInterface(list []string) []interface{} {
vs := make([]interface{}, 0, len(list))
for _, v := range list {
vs = append(vs, v)
}
return vs
}
func expandIntList(configured []interface{}) []int {
vs := make([]int, 0, len(configured))
for _, v := range configured {
vs = append(vs, v.(int))
}
return vs
}
// Convert the result for an array and returns a Json string
func convertListToJsonString(configured []interface{}) string {
if len(configured) < 1 {
return ""
}
result := "["
for i, v := range configured {
if v == nil {
continue
}
result += "\"" + v.(string) + "\""
if i < len(configured)-1 {
result += ","
}
}
result += "]"
return result
}
func convertJsonStringToStringList(src interface{}) (result []interface{}) {
if err, ok := src.([]interface{}); !ok {
panic(err)
}
for _, v := range src.([]interface{}) {
result = append(result, fmt.Sprint(formatInt(v)))
}
return
}
func encodeToBase64String(configured []string) string {
result := ""
for i, v := range configured {
result += v
if i < len(configured)-1 {
result += ","
}
}
return base64.StdEncoding.EncodeToString([]byte(result))
}
func decodeFromBase64String(configured string) (result []string, err error) {
decodeString, err := base64.StdEncoding.DecodeString(configured)
if err != nil {
return result, err
}
result = strings.Split(string(decodeString), ",")
return result, nil
}
func convertJsonStringToMap(configured string) (map[string]interface{}, error) {
result := make(map[string]interface{})
if err := json.Unmarshal([]byte(configured), &result); err != nil {
return nil, err
}
return result, nil
}
// Convert the result for an array and returns a comma separate
func convertListToCommaSeparate(configured []interface{}) string {
if len(configured) < 1 {
return ""
}
result := ""
for i, v := range configured {
rail := ","
if i == len(configured)-1 {
rail = ""
}
result += v.(string) + rail
}
return result
}
func filterEmptyStrings(arr []interface{}) []interface{} {
var result []interface{}
for _, str := range arr {
if fmt.Sprint(str) != "" {
result = append(result, str)
}
}
return result
}
func convertBoolToString(configured bool) string {
return strconv.FormatBool(configured)
}
func convertStringToBool(configured string) bool {
v, _ := strconv.ParseBool(configured)
return v
}
func convertIntergerToString(configured int) string {
return strconv.Itoa(configured)
}
func convertFloat64ToString(configured float64) string {
return strconv.FormatFloat(configured, 'E', -1, 64)
}
func convertJsonStringToList(configured string) ([]interface{}, error) {
result := make([]interface{}, 0)
if err := json.Unmarshal([]byte(configured), &result); err != nil {
return nil, err
}
return result, nil
}
func expandArrayToMap(originMap map[string]interface{}, arrayValues []interface{}, arrayKey string) map[string]interface{} {
for i, val := range arrayValues {
key := fmt.Sprintf("%s.%d", arrayKey, i+1)
originMap[key] = fmt.Sprint(val)
}
return originMap
}
func convertJsonStringToObject(configured interface{}) map[string]interface{} {
result := make(map[string]interface{})
if err := json.Unmarshal([]byte(configured.(string)), &result); err != nil {
return nil
}
return result
}
func convertObjectToJsonString(m interface{}) string {
if result, err := json.Marshal(m); err != nil {
return ""
} else {
return string(result)
}
}
func convertMaptoJsonString(m map[string]interface{}) (string, error) {
//sm := make(map[string]string, len(m))
//for k, v := range m {
// sm[k] = v.(string)
//}
if result, err := json.Marshal(m); err != nil {
return "", err
} else {
return string(result), nil
}
}
func convertMapToJsonStringIgnoreError(m map[string]interface{}) string {
if result, err := json.Marshal(m); err != nil {
return ""
} else {
return string(result)
}
}
func convertInterfaceToJsonString(m interface{}) (string, error) {
if result, err := json.Marshal(m); err != nil {
return "", err
} else {
return string(result), nil
}
}
func convertListMapToJsonString(configured []map[string]interface{}) (string, error) {
if len(configured) < 1 {
return "[]", nil
}
result := "["
for i, m := range configured {
if m == nil {
continue
}
sm := make(map[string]interface{}, len(m))
for k, v := range m {
sm[k] = v
}
item, err := json.Marshal(sm)
if err == nil {
result += string(item)
if i < len(configured)-1 {
result += ","
}
}
}
result += "]"
return result, nil
}
func convertMapFloat64ToJsonString(m map[string]interface{}) (string, error) {
sm := make(map[string]json.Number, len(m))
for k, v := range m {
sm[k] = v.(json.Number)
}
if result, err := json.Marshal(sm); err != nil {
return "", err
} else {
return string(result), nil
}
}
func StringPointer(s string) *string {
return &s
}
func BoolPointer(b bool) *bool {
return &b
}
func Int32Pointer(i int32) *int32 {
return &i
}
func Int64Pointer(i int64) *int64 {
return &i
}
func IntMin(x, y int) int {
if x < y {
return x
}
return y
}
const ServerSideEncryptionAes256 = "AES256"
const ServerSideEncryptionKMS = "KMS"
const ServerSideEncryptionSM4 = "SM4"
type OptimizedType string
const (
IOOptimized = OptimizedType("optimized")
NoneOptimized = OptimizedType("none")
)
type TagResourceType string
const (
TagResourceImage = TagResourceType("image")
TagResourceInstance = TagResourceType("instance")
TagResourceAcl = TagResourceType("acl")
TagResourceCertificate = TagResourceType("certificate")
TagResourceDisk = TagResourceType("disk")
TagResourceSecurityGroup = TagResourceType("securitygroup")
TagResourceCdn = TagResourceType("DOMAIN")
TagResourceApp = TagResourceType("app")
TagResourceTopic = TagResourceType("topic")
TagResourceCluster = TagResourceType("cluster")
)
type KubernetesNodeType string
const (
KubernetesNodeMaster = ResourceType("Master")
KubernetesNodeWorker = ResourceType("Worker")
)
func getPagination(pageNumber, pageSize int) (pagination common.Pagination) {
pagination.PageSize = pageSize
pagination.PageNumber = pageNumber
return
}
const CharityPageUrl = "http://promotion.alicdn.com/help/oss/error.html"
func userDataHashSum(user_data string) string {
// Check whether the user_data is not Base64 encoded.
// Always calculate hash of base64 decoded value since we
// check against double-encoding when setting it
v, base64DecodeError := base64.StdEncoding.DecodeString(user_data)
if base64DecodeError != nil {
v = []byte(user_data)
}
return string(v)
}
// Remove useless blank in the string.
func Trim(v string) string {
if len(v) < 1 {
return v
}
return strings.Trim(v, " ")
}
func ConvertIntegerToInt(value requests.Integer) (v int, err error) {
if strings.TrimSpace(string(value)) == "" {
return
}
v, err = strconv.Atoi(string(value))
if err != nil {
return v, fmt.Errorf("Converting integer %s to int got an error: %#v.", value, err)
}
return
}
func GetUserHomeDir() (string, error) {
usr, err := user.Current()
if err != nil {
return "", fmt.Errorf("Get current user got an error: %#v.", err)
}
return usr.HomeDir, nil
}
func writeToFile(filePath string, data interface{}) error {
var out string
switch data.(type) {
case string:
out = data.(string)
break
case nil:
return nil
default:
bs, err := json.MarshalIndent(data, "", "\t")
if err != nil {
return fmt.Errorf("MarshalIndent data %#v got an error: %#v", data, err)
}
out = string(bs)
}
if strings.HasPrefix(filePath, "~") {
home, err := GetUserHomeDir()
if err != nil {
return err
}
if home != "" {
filePath = strings.Replace(filePath, "~", home, 1)
}
}
if _, err := os.Stat(filePath); err == nil {
if err := os.Remove(filePath); err != nil {
return err
}
}
return ioutil.WriteFile(filePath, []byte(out), 422)
}
type Invoker struct {
catchers []*Catcher
}
type Catcher struct {
Reason string
RetryCount int
RetryWaitSeconds int
}
var ClientErrorCatcher = Catcher{AliyunGoClientFailure, 10, 5}
var ServiceBusyCatcher = Catcher{"ServiceUnavailable", 10, 5}
var ThrottlingCatcher = Catcher{Throttling, 50, 2}
func NewInvoker() Invoker {
i := Invoker{}
i.AddCatcher(ClientErrorCatcher)
i.AddCatcher(ServiceBusyCatcher)
i.AddCatcher(ThrottlingCatcher)
return i
}
func (a *Invoker) AddCatcher(catcher Catcher) {
a.catchers = append(a.catchers, &catcher)
}
func (a *Invoker) Run(f func() error) error {
err := f()
if err == nil {
return nil
}
for _, catcher := range a.catchers {
if IsExpectedErrors(err, []string{catcher.Reason}) {
catcher.RetryCount--
if catcher.RetryCount <= 0 {
return fmt.Errorf("Retry timeout and got an error: %#v.", err)
} else {
time.Sleep(time.Duration(catcher.RetryWaitSeconds) * time.Second)
return a.Run(f)
}
}
}
return err
}
func buildClientToken(action string) string {
token := strings.TrimSpace(fmt.Sprintf("TF-%s-%d-%s", action, time.Now().Unix(), strings.Trim(uuid.New().String(), "-")))
if len(token) > 64 {
token = token[0:64]
}
return token
}
func getNextpageNumber(number requests.Integer) (requests.Integer, error) {
page, err := strconv.Atoi(string(number))
if err != nil {
return "", err
}
return requests.NewInteger(page + 1), nil
}
func terraformToAPI(field string) string {
var result string
for _, v := range strings.Split(field, "_") {
if len(v) > 0 {
result = fmt.Sprintf("%s%s%s", result, strings.ToUpper(string(v[0])), v[1:])
}
}
return result
}
func compareJsonTemplateAreEquivalent(tem1, tem2 string) (bool, error) {
obj1 := make(map[string]interface{})
err := json.Unmarshal([]byte(tem1), &obj1)
if err != nil {
return false, err
}
canonicalJson1, _ := json.Marshal(obj1)
obj2 := make(map[string]interface{})
err = json.Unmarshal([]byte(tem2), &obj2)
if err != nil {
return false, err
}
canonicalJson2, _ := json.Marshal(obj2)
equal := bytes.Compare(canonicalJson1, canonicalJson2) == 0
if !equal {
log.Printf("[DEBUG] Canonical template are not equal.\nFirst: %s\nSecond: %s\n",
canonicalJson1, canonicalJson2)
}
return equal, nil
}
func compareArrayJsonTemplateAreEquivalent(tem1, tem2 string) (bool, error) {
obj1 := make([]map[string]interface{}, 0)
err := json.Unmarshal([]byte(tem1), &obj1)
if err != nil {
return false, err
}
canonicalJson1, _ := json.Marshal(obj1)
obj2 := make([]map[string]interface{}, 0)
err = json.Unmarshal([]byte(tem2), &obj2)
if err != nil {
return false, err
}
canonicalJson2, _ := json.Marshal(obj2)
equal := bytes.Compare(canonicalJson1, canonicalJson2) == 0
if !equal {
log.Printf("[DEBUG] Canonical template are not equal.\nFirst: %s\nSecond: %s\n",
canonicalJson1, canonicalJson2)
}
return equal, nil
}
func compareYamlTemplateAreEquivalent(tem1, tem2 string) (bool, error) {
var obj1 interface{}
err := yaml.Unmarshal([]byte(tem1), &obj1)
if err != nil {
return false, err
}
canonicalYaml1, _ := yaml.Marshal(obj1)
var obj2 interface{}
err = yaml.Unmarshal([]byte(tem2), &obj2)
if err != nil {
return false, err
}
canonicalYaml2, _ := yaml.Marshal(obj2)
equal := bytes.Compare(canonicalYaml1, canonicalYaml2) == 0
if !equal {
log.Printf("[DEBUG] Canonical template are not equal.\nFirst: %s\nSecond: %s\n",
canonicalYaml1, canonicalYaml2)
}
return equal, nil
}
// loadFileContent returns contents of a file in a given path
func loadFileContent(v string) ([]byte, error) {
filename, err := homedir.Expand(v)
if err != nil {
return nil, err
}
fileContent, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return fileContent, nil
}
func debugOn() bool {
for _, part := range strings.Split(os.Getenv("DEBUG"), ",") {
if strings.TrimSpace(part) == "terraform" {
return true
}
}
return false
}
func addDebug(action, content interface{}, requestInfo ...interface{}) {
if debugOn() {
trace := "[DEBUG TRACE]:\n"
for skip := 1; skip < 5; skip++ {
_, filepath, line, _ := runtime.Caller(skip)
trace += fmt.Sprintf("%s:%d\n", filepath, line)
}
if len(requestInfo) > 0 {
var request = struct {
Domain string
Version string
UserAgent string
ActionName string
Method string
Product string
Region string
AK string
}{}
switch requestInfo[0].(type) {
case *requests.RpcRequest:
tmp := requestInfo[0].(*requests.RpcRequest)
request.Domain = tmp.GetDomain()
request.Version = tmp.GetVersion()
request.ActionName = tmp.GetActionName()
request.Method = tmp.GetMethod()
request.Product = tmp.GetProduct()
request.Region = tmp.GetRegionId()
case *requests.RoaRequest:
tmp := requestInfo[0].(*requests.RoaRequest)
request.Domain = tmp.GetDomain()
request.Version = tmp.GetVersion()
request.ActionName = tmp.GetActionName()
request.Method = tmp.GetMethod()
request.Product = tmp.GetProduct()
request.Region = tmp.GetRegionId()
case *requests.CommonRequest:
tmp := requestInfo[0].(*requests.CommonRequest)
request.Domain = tmp.GetDomain()
request.Version = tmp.GetVersion()
request.ActionName = tmp.GetActionName()
request.Method = tmp.GetMethod()
request.Product = tmp.GetProduct()
request.Region = tmp.GetRegionId()
case *fc.Client:
client := requestInfo[0].(*fc.Client)
request.Version = client.Config.APIVersion
request.Product = "FC"
request.ActionName = fmt.Sprintf("%s", action)
case *sls.Client:
request.Product = "LOG"
request.ActionName = fmt.Sprintf("%s", action)
case *tablestore.TableStoreClient:
request.Product = "OTS"
request.ActionName = fmt.Sprintf("%s", action)
case *oss.Client:
request.Product = "OSS"
request.ActionName = fmt.Sprintf("%s", action)
case *datahub.DataHub:
request.Product = "DataHub"
request.ActionName = fmt.Sprintf("%s", action)
case *cs.Client:
request.Product = "CS"
request.ActionName = fmt.Sprintf("%s", action)
}
requestContent := ""
if len(requestInfo) > 1 {
switch requestInfo[1].(type) {
case *tea.SDKError:
requestContent = fmt.Sprintf("%#v", requestInfo[1].(*tea.SDKError).Error())
default:
requestContent = fmt.Sprintf("%#v", requestInfo[1])
}
}
if len(requestInfo) == 1 {
if v, ok := requestInfo[0].(map[string]interface{}); ok {
if res, err := json.Marshal(&v); err == nil {
requestContent = string(res)
}
if res, err := json.Marshal(&content); err == nil {
content = string(res)
}
}
}
content = fmt.Sprintf("%vDomain:%v, Version:%v, ActionName:%v, Method:%v, Product:%v, Region:%v\n\n"+
"*************** %s Request ***************\n%#v\n",
content, request.Domain, request.Version, request.ActionName,
request.Method, request.Product, request.Region, request.ActionName, requestContent)
}
//fmt.Printf(DefaultDebugMsg, action, content, trace)
log.Printf(DefaultDebugMsg, action, content, trace)
}
}
// Return a ComplexError which including extra error message, error occurred file and path
func GetFunc(level int) string {
pc, _, _, ok := runtime.Caller(level)
if !ok {
log.Printf("[ERROR] runtime.Caller error in GetFuncName.")
return ""
}
return strings.TrimPrefix(filepath.Ext(runtime.FuncForPC(pc).Name()), ".")
}
func ParseResourceIds(id string) (parts []string, err error) {
parts = strings.Split(id, ":")
return parts, err
}
func ParseResourceId(id string, length int) (parts []string, err error) {
parts = strings.Split(id, ":")
if len(parts) != length {
err = WrapError(fmt.Errorf("Invalid Resource Id %s. Expected parts' length %d, got %d", id, length, len(parts)))
}
return parts, err
}
func ParseResourceIdN(id string, length int) (parts []string, err error) {
parts = strings.SplitN(id, ":", length)
if len(parts) != length {
err = WrapError(fmt.Errorf("Invalid Resource Id %s. Expected parts' length %d, got %d", id, length, len(parts)))
}
return parts, err
}
func ParseResourceIdWithEscaped(id string, length int) (parts []string, err error) {
parts = make([]string, 0)
var currentPart strings.Builder
for i := 0; i < len(id); i++ {
if id[i] == '\\' {
i++
if i < len(id) {
currentPart.WriteByte(id[i])
}
} else if id[i] == ':' {
parts = append(parts, currentPart.String())
currentPart.Reset()
} else {
currentPart.WriteByte(id[i])
}
}
if currentPart.Len() > 0 {
parts = append(parts, currentPart.String())
}
if len(parts) != length {
err = WrapError(fmt.Errorf("Invalid Resource Id %s. Expected parts' length %d, got %d", id, length, len(parts)))
}
return parts, err
}
func EscapeColons(s string) string {
return strings.ReplaceAll(s, ":", "\\:")
}
func ParseSlbListenerId(id string) (parts []string, err error) {
parts = strings.Split(id, ":")
if len(parts) != 2 && len(parts) != 3 {
err = WrapError(fmt.Errorf("Invalid alicloud_slb_listener Id %s. Expected Id format is <slb id>:<protocol>:< frontend>.", id))
}
return parts, err
}
func GetCenChildInstanceType(id string) (c string, e error) {
if strings.HasPrefix(id, "vpc") {
return ChildInstanceTypeVpc, nil
} else if strings.HasPrefix(id, "vbr") {
return ChildInstanceTypeVbr, nil
} else if strings.HasPrefix(id, "ccn") {
return ChildInstanceTypeCcn, nil
} else {
return c, fmt.Errorf("CEN child instance ID invalid. Now, it only supports VPC or VBR or CCN instance.")
}
}
func BuildStateConf(pending, target []string, timeout, delay time.Duration, f resource.StateRefreshFunc) *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: pending,
Target: target,
Refresh: f,
Timeout: timeout,
Delay: delay,
MinTimeout: 3 * time.Second,
}
}
func incrementalWait(firstDuration time.Duration, increaseDuration time.Duration) func() {
retryCount := 1
return func() {
var waitTime time.Duration
if retryCount == 1 {
waitTime = firstDuration
} else if retryCount > 1 {
waitTime += increaseDuration
}
time.Sleep(waitTime)
retryCount++
}
}
// If auto renew, the period computed from computePeriodByUnit will be changed
// This method used to compute a period accourding to current period and unit
func computePeriodByUnit(createTime, endTime interface{}, currentPeriod int, periodUnit string) (int, error) {
if createTime == nil {
return 0, WrapError(fmt.Errorf("createTime should not be nil"))
}
if endTime == nil {
return 0, WrapError(fmt.Errorf("endTime should not be nil"))
}
var createTimeStr, endTimeStr string
switch value := createTime.(type) {
case int64:
createTimeStr = time.Unix(createTime.(int64), 0).Format(time.RFC3339)
endTimeStr = time.Unix(endTime.(int64), 0).Format(time.RFC3339)
case string:
createTimeStr = createTime.(string)
endTimeStr = endTime.(string)
default:
return 0, WrapError(fmt.Errorf("Unsupported time type: %#v", value))
}
// currently, there is time value does not format as standard RFC3339
UnStandardRFC3339 := "2006-01-02T15:04Z07:00"
create, err := time.Parse(time.RFC3339, createTimeStr)
if err != nil {
log.Printf("Parase the CreateTime %#v failed and error is: %#v.", createTime, err)
create, err = time.Parse(UnStandardRFC3339, createTimeStr)
if err != nil {
return 0, WrapError(err)
}
}
end, err := time.Parse(time.RFC3339, endTimeStr)
if err != nil {
log.Printf("Parase the EndTime %#v failed and error is: %#v.", endTime, err)
end, err = time.Parse(UnStandardRFC3339, endTimeStr)
if err != nil {
return 0, WrapError(err)
}
}
var period int
switch periodUnit {
case "Month":
period = int(math.Floor(end.Sub(create).Hours() / 24 / 30))
case "Week":
period = int(math.Floor(end.Sub(create).Hours() / 24 / 7))
case "Year":
period = int(math.Floor(end.Sub(create).Hours() / 24 / 365))
default:
err = fmt.Errorf("Unexpected period unit %s", periodUnit)
}
// The period at least is 1
if period < 1 {
period = 1
}
if period > 12 {
period = 12
}
// period can not be modified and if the new period is changed, using the previous one.
if currentPeriod > 0 && currentPeriod != period {
period = currentPeriod
}
return period, WrapError(err)
}
func checkWaitForReady(object interface{}, conditions map[string]interface{}) (bool, map[string]interface{}, error) {
if conditions == nil {
return false, nil, nil
}
objectType := reflect.TypeOf(object)
objectValue := reflect.ValueOf(object)
values := make(map[string]interface{})
for key, value := range conditions {
if _, ok := objectType.FieldByName(key); ok {
current := objectValue.FieldByName(key)
values[key] = current
if fmt.Sprintf("%v", current) != fmt.Sprintf("%v", value) {
return false, values, nil
}
} else {
return false, values, WrapError(fmt.Errorf("There is missing attribute %s in the object.", key))
}
}
return true, values, nil
}
// When using teadsl, we need to convert float, int64 and int32 to int for comparison.
func formatInt(src interface{}) int {
if src == nil {
return 0
}
attrType := reflect.TypeOf(src)
switch attrType.String() {
case "float64":
return int(src.(float64))
case "float32":
return int(src.(float32))
case "int64":
return int(src.(int64))
case "int32":
return int(src.(int32))
case "int":
return src.(int)
case "string":
vv := fmt.Sprint(src)
if vv == "" {
return 0
}
v, err := strconv.Atoi(vv)
if err != nil {
panic(err)
}
return v
case "json.Number":
v, err := strconv.Atoi(src.(json.Number).String())
if err != nil {
panic(err)
}
return v
default:
panic(fmt.Sprintf("Not support type %s", attrType.String()))
}
}
func formatBool(src interface{}) bool {
if src == nil {
return false
}
attrType := reflect.TypeOf(src)
switch attrType.String() {
case "bool":
return src.(bool)
case "string":
vv := fmt.Sprint(src)
if vv == "" {
return false
}
v, err := strconv.ParseBool(vv)
if err != nil {
panic(err)
}
return v
default:
panic(fmt.Sprintf("Not support type %s", attrType.String()))
}
}
func formatFloat64(src interface{}) float64 {
if src == nil {
return 0
}
attrType := reflect.TypeOf(src)
switch attrType.String() {
case "float64":
return src.(float64)
case "float32":
return float64(src.(float32))
case "int64":
return float64(src.(int64))
case "int32":
return float64(src.(int32))
case "int":
return float64(src.(int))
case "string":
vv := fmt.Sprint(src)
if vv == "" {
return 0
}
v, err := strconv.ParseFloat(vv, 64)
if err != nil {
panic(err)
}
return v
case "json.Number":
v, err := src.(json.Number).Float64()
if err != nil {
panic(err)
}
return v
default:
panic(fmt.Sprintf("Not support type %s", attrType.String()))
}
}
func convertArrayObjectToJsonString(src interface{}) (string, error) {
res, err := json.Marshal(&src)
if err != nil {
return "", err
}
return string(res), nil
}
func convertArrayToString(src interface{}, sep string) string {
if src == nil {
return ""
}
items := make([]string, 0)
for _, v := range src.([]interface{}) {
items = append(items, fmt.Sprint(v))
}
return strings.Join(items, sep)
}
func splitMultiZoneId(id string) (ids []string) {
if !(strings.Contains(id, MULTI_IZ_SYMBOL) || strings.Contains(id, "(")) {
return
}
firstIndex := strings.Index(id, MULTI_IZ_SYMBOL)
secondIndex := strings.Index(id, "(")
for _, p := range strings.Split(id[secondIndex+1:len(id)-1], COMMA_SEPARATED) {
ids = append(ids, id[:firstIndex]+string(p))
}
return
}
func Case2Camel(name string) string {
name = strings.Replace(name, "_", " ", -1)
name = strings.Title(name)
return strings.Replace(name, " ", "", -1)
}
func FirstLower(s string) string {
if s == "" {
return ""
}
return strings.ToLower(s[:1]) + s[1:]
}
// SplitSlice Divides the slice into blocks of the specified size
func SplitSlice(xs []interface{}, chunkSize int) [][]interface{} {
if len(xs) == 0 {
return nil
}
divided := make([][]interface{}, (len(xs)+chunkSize-1)/chunkSize)
prev := 0
i := 0
till := len(xs) - chunkSize
for prev < till {
next := prev + chunkSize
divided[i] = xs[prev:next]
prev = next
i++
}
divided[i] = xs[prev:]
return divided
}
func isPagingRequest(d *schema.ResourceData) bool {
v, ok := d.GetOk("page_number")
return ok && v.(int) > 0
}
func setPagingRequest(d *schema.ResourceData, request map[string]interface{}, maxPageSize int) {
if maxPageSize == 0 {
maxPageSize = PageSizeLarge
}
if v, ok := d.GetOk("page_number"); ok && v.(int) > 0 {
request["PageNumber"] = v.(int)
} else {
request["PageNumber"] = 1
}
if v, ok := d.GetOk("page_size"); ok && v.(int) > 0 {
request["PageSize"] = v.(int)
} else {
request["PageSize"] = maxPageSize
}
return
}
func mapMerge(target, merged map[string]interface{}) map[string]interface{} {
for key, value := range merged {
if _, exist := target[key]; !exist {
target[key] = value
} else {
// key existed in both src,target
switch merged[key].(type) {
case []interface{}:
sourceSlice := value.([]interface{})
targetSlice := make([]interface{}, len(sourceSlice))
copy(targetSlice, target[key].([]interface{}))
for index, val := range sourceSlice {
switch val.(type) {
case map[string]interface{}:
targetMap, ok := targetSlice[index].(map[string]interface{})
if ok {
targetSlice[index] = mapMerge(targetMap, val.(map[string]interface{}))
} else {
targetSlice[index] = mapMerge(map[string]interface{}{}, val.(map[string]interface{}))
}
default:
targetSlice[index] = val
}
}
target[key] = targetSlice
case map[string]interface{}:
target[key] = mapMerge(target[key].(map[string]interface{}), merged[key].(map[string]interface{}))
default:
target[key] = merged[key]
}
}
}
return target
}
func mapSort(target map[string]string) []string {
result := make([]string, 0)
for key := range target {
result = append(result, key)
}
sort.Strings(result)
return result
}
func newInstanceDiff(resourceName string, attributes, attributesDiff map[string]interface{}, state *terraform.InstanceState) (*terraform.InstanceDiff, error) {
p := Provider().(*schema.Provider).ResourcesMap
dOld, _ := schema.InternalMap(p[resourceName].Schema).Data(state, nil)
dNew, _ := schema.InternalMap(p[resourceName].Schema).Data(state, nil)
for key, value := range attributes {
err := dOld.Set(key, value)
if err != nil {
return nil, WrapErrorf(err, "[ERROR] the field %s setting error.", key)
}
}
for key, value := range attributesDiff {
attributes[key] = value
}
for key, value := range attributes {
err := dNew.Set(key, value)
if err != nil {
return nil, WrapErrorf(err, "[ERROR] the field %s setting error.", key)
}
}
diff := terraform.NewInstanceDiff()
objectKey := ""
for _, key := range mapSort(dNew.State().Attributes) {
newValue := dNew.State().Attributes[key]
if objectKey != "" && !strings.HasPrefix(key, objectKey) {
objectKey = ""
}
if objectKey == "" {
for _, suffix := range []string{"#", "%"} {
if strings.HasSuffix(key, suffix) {
objectKey = strings.TrimSuffix(key, suffix)
break
}
}
}
oldValue, ok := dOld.State().Attributes[key]
if ok && oldValue == newValue {
continue
}
if oldValue == "" {
for _, suffix := range []string{"#", "%"} {
if strings.HasSuffix(key, suffix) {
oldValue = "0"
}
}
}
diff.SetAttribute(key, &terraform.ResourceAttrDiff{Old: oldValue, New: newValue})
if objectKey != "" {
for removeKey, removeValue := range dOld.State().Attributes {
if strings.HasPrefix(removeKey, objectKey) {
if _, ok := dNew.State().Attributes[removeKey]; !ok {
// If the attribue has complex elements, there should remove the key, not setting it to empty
if len(strings.Split(removeKey, ".")) > 2 {
diff.DelAttribute(removeKey)
} else {
diff.SetAttribute(removeKey, &terraform.ResourceAttrDiff{Old: removeValue, New: ""})
}
}
}
}
objectKey = ""
}
}
return diff, nil
}
func compareMapWithIgnoreEquivalent(tem1, tem2 map[string]interface{}, ignore []string) bool {
if len(tem1) != len(tem2) {
return false
}
OuterLoop:
for key1, val1 := range tem1 {
for _, item := range ignore {
if key1 == item {
continue OuterLoop
}
}
val2 := tem2[key1]
if val2 != val1 {
return false
}
}
return true
}
func Interface2String(val interface{}) string {
if v, ok := val.(string); ok {
return v
}
return fmt.Sprint(val)
}
func Interface2StrSlice(ii []interface{}) []string {
ss := make([]string, 0, len(ii))
for _, i := range ii {
s := Interface2String(i)
ss = append(ss, s)
}
return ss
}
func Str2InterfaceSlice(ss []string) []interface{} {
ii := make([]interface{}, 0, len(ss))
for _, s := range ss {
ii = append(ii, s)
}
return ii
}
func Interface2Bool(i interface{}) bool {
if i == nil {
return false
}
t := reflect.TypeOf(i).Kind()
switch t {
case reflect.String:
return convertStringToBool(i.(string))
case reflect.Bool:
return i.(bool)
default:
return false
}
}
func IsEmpty(i interface{}) bool {
if i == nil {
return true
}
switch reflect.TypeOf(i).Kind() {
case reflect.String:
return fmt.Sprint(i) == ""
case reflect.Int:
return i.(int) <= 0
case reflect.Int8:
return i.(int8) <= 0
case reflect.Int16:
return i.(int16) <= 0
case reflect.Int32:
return i.(int32) <= 0
case reflect.Int64:
return i.(int64) <= 0
case reflect.Float32:
return i.(float32) <= 0
case reflect.Float64:
return i.(float64) <= 0
case reflect.Map:
return len(i.(map[string]interface{})) <= 0
case reflect.Ptr:
return reflect.ValueOf(i).IsNil()
default:
return false
}
}
func IsNil(i interface{}) bool {
if i == nil {
return true
}
switch reflect.TypeOf(i).Kind() {
case reflect.String:
return fmt.Sprint(i) == ""
case reflect.Slice:
return len(i.([]interface{})) <= 0
case reflect.Map:
return len(i.(map[string]interface{})) <= 0
case reflect.Ptr:
return reflect.ValueOf(i).IsNil()
default:
return false
}
}
func GetDaysBetween2Date(format string, date1Str string, date2Str string) (int, error) {
var day int
t1, err := time.ParseInLocation(format, date1Str, time.Local)
if err != nil {
return 0, err
}
t2, err := time.ParseInLocation(format, date2Str, time.Local)
if err != nil {
return 0, err
}
swap := false
if t1.Unix() > t2.Unix() {
t1, t2 = t2, t1
swap = true
}
t1_ := t1.Add(time.Duration(t2.Sub(t1).Milliseconds()%86400000) * time.Millisecond)
day = int(t2.Sub(t1).Hours() / 24)
if t1_.Day() != t1.Day() {
day += 1
}
if swap {
day = -day
}
return day, nil
}
func compareCmsHybridMonitorFcTaskYamlConfigAreEquivalent(tem1, tem2 string) (bool, error) {
type MetricList struct {
MetricList []string `yaml:"metric_list"`
}
type Product struct {
MetricInfo []MetricList `yaml:"metric_info"`
Namespace string `yaml:"namespace"`
}
type Products struct {
Products []Product
}
var P1 Products
err := yaml.Unmarshal([]byte(tem1), &P1)
if err != nil {
fmt.Sprintln(false)
}
y1 := make([]string, 0)
for _, product := range P1.Products {
s1, _ := json.Marshal(product)
y1 = append(y1, string(s1))
}
var P2 Products
err = yaml.Unmarshal([]byte(tem2), &P2)
if err != nil {
fmt.Sprintln(false)
}
y2 := make([]string, 0)
for _, product := range P2.Products {
s2, _ := json.Marshal(product)
y2 = append(y2, string(s2))
}
sort.Strings(y1)
sort.Strings(y2)
return reflect.DeepEqual(y1, y2), nil
}
func getOneStringOrAllStringSlice(stringSli []interface{}) interface{} {
if len(stringSli) == 1 {
return stringSli[0].(string)
}
sli := make([]string, len(stringSli))
for i, v := range stringSli {
sli[i] = v.(string)
}
return sli
}
func Unique(strings []string) []string {
dict := make(map[string]bool)
var ss []string
for _, s := range strings {
if s == "" {
continue
}
if _, ok := dict[s]; !ok {
dict[s] = true
ss = append(ss, s)
}
}
return ss
}
func IsSubCollection(sub []string, full []string) bool {
for _, s := range sub {
var find bool
for _, f := range full {
if s == f {
find = true
break
}
}
if !find {
return false
}
}
return true
}
func MergeMaps(maps ...map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for _, m := range maps {
for key, value := range m {
item, existed := result[key]
if !existed {
result[key] = value
continue
}
newValue, ok := value.([]map[string]interface{})
if !ok || len(newValue) != 1 {
continue
}
if preValue, ok := item.([]map[string]interface{}); ok && len(preValue) == 1 {
result[key] = MergeMaps(preValue[0], newValue[0])
}
}
}
return result
}
func InArray(target string, strArray []string) bool {
for _, element := range strArray {
if target == element {
return true
}
}
return false
}
func rpcParam(method, version, action string) *openapi.Params {
return &openapi.Params{
Action: tea.String(action),
Version: tea.String(version),
Pathname: tea.String("/"),
Method: tea.String(method),
AuthType: tea.String("AK"),
Style: tea.String("RPC"),
ReqBodyType: tea.String("formData"),
BodyType: tea.String("json"),
}
}
func roaParam(method, version, action, path string) *openapi.Params {
return &openapi.Params{
Action: tea.String(action),
Version: tea.String(version),
Pathname: tea.String(path),
Method: tea.String(method),
AuthType: tea.String("AK"),
Style: tea.String("ROA"),
ReqBodyType: tea.String("formData"),
BodyType: tea.String("json"),
}
}
func xmlParam(method, version, action, path string) *openapi.Params {
return &openapi.Params{
Action: tea.String(action),
Version: tea.String(version),
Protocol: tea.String("HTTPS"),
Pathname: tea.String(path),
Method: tea.String(method),
AuthType: tea.String("AK"),
ReqBodyType: tea.String("xml"),
BodyType: tea.String("xml"),
}
}
func jsonXmlParam(method, version, action, path string) *openapi.Params {
return &openapi.Params{
Action: tea.String(action),
Version: tea.String(version),
Protocol: tea.String("HTTPS"),
Pathname: tea.String(path),
Method: tea.String(method),
AuthType: tea.String("AK"),
ReqBodyType: tea.String("json"),
BodyType: tea.String("xml"),
}
}
func xmlJsonParam(method, version, action, path string) *openapi.Params {
return &openapi.Params{
Action: tea.String(action),
Version: tea.String(version),
Protocol: tea.String("HTTPS"),
Pathname: tea.String(path),
Method: tea.String(method),
AuthType: tea.String("AK"),
ReqBodyType: tea.String("xml"),
BodyType: tea.String("json"),
}
}
type MyMap map[string]interface{}
type xmlMapEntry struct {
XMLName xml.Name
Value interface{} `xml:",chardata"`
}
func (m MyMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if len(m) == 0 {
return nil
}
err := e.EncodeToken(start)
if err != nil {
return err
}
for k, v := range m {
e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})
}
return e.EncodeToken(start.End())
}
func expandSingletonToList(singleton interface{}) []interface{} {
vs := make([]interface{}, 0)
vs = append(vs, singleton)
return vs
}
func MD5(b []byte) string {
ctx := md5.New()
ctx.Write(b)
return hex.EncodeToString(ctx.Sum(nil))
}
func ConvertTags(tagsMap map[string]interface{}) []map[string]interface{} {
tags := make([]map[string]interface{}, 0)
for key, value := range tagsMap {
if value != nil {
if v, ok := value.(string); ok {
tags = append(tags, map[string]interface{}{
"Key": key,
"Value": v,
})
}
}
}
return tags
}
func ConvertTagsForKms(tagsMap map[string]interface{}) []map[string]interface{} {
tags := make([]map[string]interface{}, 0)
for key, value := range tagsMap {
if value != nil {
if v, ok := value.(string); ok {
tags = append(tags, map[string]interface{}{
"TagKey": key,
"TagValue": v,
})
}
}
}
return tags
}
func expandTagsToMap(originMap map[string]interface{}, tags []map[string]interface{}) map[string]interface{} {
for i, tag := range tags {
for key, value := range tag {
if key == "Key" || key == "Value" {
newKey := "Tag" + "." + strconv.Itoa(i+1) + "." + key
originMap[newKey] = fmt.Sprintf("%v", value)
}
}
}
return originMap
}
func convertChargeTypeToPaymentType(source interface{}) interface{} {
switch source {
case "PostPaid", "Postpaid":
return "PayAsYouGo"
case "PrePaid", "Prepaid":
return "Subscription"
}
return source
}
func convertPaymentTypeToChargeType(source interface{}) interface{} {
switch source {
case "PayAsYouGo":
return "PostPaid"
case "Subscription":
return "PrePaid"
}
return source
}
func bytesToTB(bytes int64) float64 {
const (
KiB = 1024
MiB = KiB * KiB
GiB = MiB * KiB
TiB = GiB * KiB
)
return float64(bytes) / float64(TiB)
}
func compressIPv6OrCIDR(input string) (string, error) {
if input == "" {
return input, nil
}
if strings.Contains(input, "/") {
ip, _, err := net.ParseCIDR(input)
if err != nil {
return "", err
}
if ip == nil {
return input, nil
}
mask := strings.SplitN(input, "/", 2)[1]
return fmt.Sprintf("%s/%s", ip.String(), mask), nil
}
ip := net.ParseIP(input)
if ip == nil {
return input, nil
}
return ip.String(), nil
}
func randIntRange(min int, max int) int {
return min + acctest.RandIntRange(min, max)
}