yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/qcloud.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package qcloud 16 17 import ( 18 "bytes" 19 "context" 20 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "net/http" 24 "net/url" 25 "strconv" 26 "strings" 27 "time" 28 29 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" 30 sdkerrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" 31 tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http" 32 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" 33 "github.com/tencentyun/cos-go-sdk-v5" 34 "github.com/tencentyun/cos-go-sdk-v5/debug" 35 36 "yunion.io/x/jsonutils" 37 "yunion.io/x/log" 38 "yunion.io/x/pkg/errors" 39 "yunion.io/x/pkg/util/timeutils" 40 "yunion.io/x/pkg/utils" 41 42 api "yunion.io/x/cloudmux/pkg/apis/compute" 43 "yunion.io/x/cloudmux/pkg/cloudprovider" 44 "yunion.io/x/onecloud/pkg/httperrors" 45 "yunion.io/x/onecloud/pkg/util/httputils" 46 ) 47 48 const ( 49 CLOUD_PROVIDER_QCLOUD = api.CLOUD_PROVIDER_QCLOUD 50 CLOUD_PROVIDER_QCLOUD_CN = "腾讯云" 51 CLOUD_PROVIDER_QCLOUD_EN = "QCloud" 52 53 QCLOUD_DEFAULT_REGION = "ap-beijing" 54 55 QCLOUD_API_VERSION = "2017-03-12" 56 QCLOUD_CLB_API_VERSION = "2018-03-17" 57 QCLOUD_BILLING_API_VERSION = "2018-07-09" 58 QCLOUD_AUDIT_API_VERSION = "2019-03-19" 59 QCLOUD_CAM_API_VERSION = "2019-01-16" 60 QCLOUD_CDB_API_VERSION = "2017-03-20" 61 QCLOUD_MARIADB_API_VERSION = "2017-03-12" 62 QCLOUD_POSTGRES_API_VERSION = "2017-03-12" 63 QCLOUD_SQLSERVER_API_VERSION = "2018-03-28" 64 QCLOUD_REDIS_API_VERSION = "2018-04-12" 65 QCLOUD_MEMCACHED_API_VERSION = "2019-03-18" 66 QCLOUD_SSL_API_VERSION = "2019-12-05" 67 QCLOUD_CDN_API_VERSION = "2018-06-06" 68 QCLOUD_MONGODB_API_VERSION = "2019-07-25" 69 QCLOUD_ES_API_VERSION = "2018-04-16" 70 QCLOUD_DCDB_API_VERSION = "2018-04-11" 71 QCLOUD_KAFKA_API_VERSION = "2019-08-19" 72 QCLOUD_TKE_API_VERSION = "2018-05-25" 73 QCLOUD_DNS_API_VERSION = "2021-03-23" 74 QCLOUD_STS_API_VERSION = "2018-08-13" 75 ) 76 77 type QcloudClientConfig struct { 78 cpcfg cloudprovider.ProviderConfig 79 80 secretId string 81 secretKey string 82 appId string 83 84 debug bool 85 } 86 87 func NewQcloudClientConfig(secretId, secretKey string) *QcloudClientConfig { 88 cfg := &QcloudClientConfig{ 89 secretId: secretId, 90 secretKey: secretKey, 91 } 92 return cfg 93 } 94 95 func (cfg *QcloudClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *QcloudClientConfig { 96 cfg.cpcfg = cpcfg 97 return cfg 98 } 99 100 func (cfg *QcloudClientConfig) AppId(appId string) *QcloudClientConfig { 101 cfg.appId = appId 102 return cfg 103 } 104 105 func (cfg *QcloudClientConfig) Debug(debug bool) *QcloudClientConfig { 106 cfg.debug = debug 107 return cfg 108 } 109 110 type SQcloudClient struct { 111 *QcloudClientConfig 112 ownerId string 113 ownerName string 114 115 iregions []cloudprovider.ICloudRegion 116 ibuckets []cloudprovider.ICloudBucket 117 } 118 119 func NewQcloudClient(cfg *QcloudClientConfig) (*SQcloudClient, error) { 120 client := SQcloudClient{ 121 QcloudClientConfig: cfg, 122 } 123 err := client.fetchRegions() 124 if err != nil { 125 return nil, errors.Wrap(err, "fetchRegions") 126 } 127 return &client, nil 128 } 129 130 // 默认接口请求频率限制:20次/秒 131 // 部分接口支持金融区地域。由于金融区和非金融区是隔离不互通的,因此当公共参数 Region 为金融区地域(例如 ap-shanghai-fsi)时,需要同时指定带金融区地域的域名,最好和 Region 的地域保持一致,例如:clb.ap-shanghai-fsi.tencentcloudapi.com 132 // https://cloud.tencent.com/document/product/416/6479 133 func apiDomain(product string, params map[string]string) string { 134 regionId, _ := params["Region"] 135 return apiDomainByRegion(product, regionId) 136 } 137 138 func apiDomainByRegion(product, regionId string) string { 139 if strings.HasSuffix(regionId, "-fsi") { 140 return product + "." + regionId + ".tencentcloudapi.com" 141 } else { 142 return product + ".tencentcloudapi.com" 143 } 144 } 145 146 func jsonRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool, retry bool) (jsonutils.JSONObject, error) { 147 domain := apiDomain("cvm", params) 148 return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params, updateFunc, debug, retry) 149 } 150 151 func tkeRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 152 domain := "tke.tencentcloudapi.com" 153 return _jsonRequest(client, domain, QCLOUD_TKE_API_VERSION, apiName, params, updateFunc, debug, true) 154 } 155 156 func vpcRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 157 domain := apiDomain("vpc", params) 158 return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params, updateFunc, debug, true) 159 } 160 161 func auditRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 162 domain := apiDomain("cloudaudit", params) 163 return _jsonRequest(client, domain, QCLOUD_AUDIT_API_VERSION, apiName, params, updateFunc, debug, true) 164 } 165 166 func cbsRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 167 domain := apiDomain("cbs", params) 168 return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params, updateFunc, debug, true) 169 } 170 171 func accountRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 172 domain := "account.api.qcloud.com" 173 return _phpJsonRequest(client, &wssJsonResponse{}, domain, "/v2/index.php", "", apiName, params, updateFunc, debug) 174 } 175 176 // es 177 func esRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 178 domain := apiDomain("es", params) 179 return _jsonRequest(client, domain, QCLOUD_ES_API_VERSION, apiName, params, updateFunc, debug, true) 180 } 181 182 // kafka 183 func kafkaRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 184 domain := apiDomain("ckafka", params) 185 return _jsonRequest(client, domain, QCLOUD_KAFKA_API_VERSION, apiName, params, updateFunc, debug, true) 186 } 187 188 // redis 189 func redisRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 190 domain := apiDomain("redis", params) 191 return _jsonRequest(client, domain, QCLOUD_REDIS_API_VERSION, apiName, params, updateFunc, debug, true) 192 } 193 194 // tdsql 195 func dcdbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 196 domain := apiDomain("dcdb", params) 197 return _jsonRequest(client, domain, QCLOUD_DCDB_API_VERSION, apiName, params, updateFunc, debug, true) 198 } 199 200 // mongodb 201 func mongodbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 202 domain := apiDomain("mongodb", params) 203 return _jsonRequest(client, domain, QCLOUD_MONGODB_API_VERSION, apiName, params, updateFunc, debug, true) 204 } 205 206 // memcached 207 func memcachedRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 208 domain := apiDomain("memcached", params) 209 return _jsonRequest(client, domain, QCLOUD_MEMCACHED_API_VERSION, apiName, params, updateFunc, debug, true) 210 } 211 212 // loadbalancer服务 api 3.0 213 func clbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 214 domain := apiDomain("clb", params) 215 return _jsonRequest(client, domain, QCLOUD_CLB_API_VERSION, apiName, params, updateFunc, debug, true) 216 } 217 218 // loadbalancer服务 api 2017 219 func lbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 220 domain := "lb.api.qcloud.com" 221 return _phpJsonRequest(client, &lbJsonResponse{}, domain, "/v2/index.php", "", apiName, params, updateFunc, debug) 222 } 223 224 // cdb 225 func cdbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 226 domain := apiDomain("cdb", params) 227 return _jsonRequest(client, domain, QCLOUD_CDB_API_VERSION, apiName, params, updateFunc, debug, true) 228 } 229 230 // mariadb 231 func mariadbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 232 domain := apiDomain("mariadb", params) 233 return _jsonRequest(client, domain, QCLOUD_MARIADB_API_VERSION, apiName, params, updateFunc, debug, true) 234 } 235 236 // postgres 237 func postgresRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 238 domain := apiDomain("postgres", params) 239 return _jsonRequest(client, domain, QCLOUD_POSTGRES_API_VERSION, apiName, params, updateFunc, debug, true) 240 } 241 242 // sqlserver 243 func sqlserverRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 244 domain := apiDomain("sqlserver", params) 245 return _jsonRequest(client, domain, QCLOUD_SQLSERVER_API_VERSION, apiName, params, updateFunc, debug, true) 246 } 247 248 // deprecated: ssl 证书服务 249 func wssRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 250 domain := "wss.api.qcloud.com" 251 return _phpJsonRequest(client, &wssJsonResponse{}, domain, "/v2/index.php", "", apiName, params, updateFunc, debug) 252 } 253 254 // ssl 证书服务 255 func sslRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 256 domain := "ssl.tencentcloudapi.com" 257 return _jsonRequest(client, domain, QCLOUD_SSL_API_VERSION, apiName, params, updateFunc, debug, true) 258 } 259 260 // dnspod 解析服务 261 func dnsRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 262 domain := "dnspod.tencentcloudapi.com" 263 return _jsonRequest(client, domain, QCLOUD_DNS_API_VERSION, apiName, params, updateFunc, debug, true) 264 } 265 266 // 2017版API 267 func vpc2017Request(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 268 domain := "vpc.api.qcloud.com" 269 return _phpJsonRequest(client, &vpc2017JsonResponse{}, domain, "/v2/index.php", "", apiName, params, updateFunc, debug) 270 } 271 272 func billingRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 273 domain := "billing.tencentcloudapi.com" 274 return _jsonRequest(client, domain, QCLOUD_BILLING_API_VERSION, apiName, params, updateFunc, debug, true) 275 } 276 277 func camRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 278 domain := "cam.tencentcloudapi.com" 279 return _jsonRequest(client, domain, QCLOUD_CAM_API_VERSION, apiName, params, updateFunc, debug, true) 280 } 281 282 func monitorRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), 283 debug bool) (jsonutils.JSONObject, error) { 284 domain := "monitor.tencentcloudapi.com" 285 return _jsonRequest(client, domain, QCLOUD_API_VERSION_METRICS, apiName, params, updateFunc, debug, true) 286 } 287 288 func cdnRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), 289 debug bool) (jsonutils.JSONObject, error) { 290 domain := "cdn.tencentcloudapi.com" 291 return _jsonRequest(client, domain, QCLOUD_CDN_API_VERSION, apiName, params, updateFunc, debug, true) 292 } 293 294 func stsRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), 295 debug bool) (jsonutils.JSONObject, error) { 296 domain := "sts.tencentcloudapi.com" 297 return _jsonRequest(client, domain, QCLOUD_STS_API_VERSION, apiName, params, updateFunc, debug, true) 298 } 299 300 // ============phpJsonRequest============ 301 type qcloudResponse interface { 302 tchttp.Response 303 GetResponse() *interface{} 304 } 305 306 type phpJsonRequest struct { 307 tchttp.BaseRequest 308 Path string 309 } 310 311 func (r *phpJsonRequest) GetUrl() string { 312 url := r.BaseRequest.GetUrl() 313 if url == "" { 314 return url 315 } 316 317 index := strings.Index(url, "?") 318 if index == -1 { 319 // POST request 320 return strings.TrimSuffix(url, "/") + r.Path 321 } 322 323 p1, p2 := url[:index], url[index:] 324 p1 = strings.TrimSuffix(p1, "/") 325 return p1 + r.Path + p2 326 } 327 328 func (r *phpJsonRequest) GetPath() string { 329 return r.Path 330 } 331 332 // 2017vpc专用response 333 type vpc2017JsonResponse struct { 334 Code int `json:"code"` 335 CodeDesc string `json:"codeDesc"` 336 Message string `json:"message"` 337 TotalCount int `json:"totalCount"` 338 TaskId int64 `json:"taskId"` 339 PeeringConnectionId string `json:"peeringConnectionId"` 340 Response *interface{} `json:"data"` 341 } 342 343 func (r *vpc2017JsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error) { 344 resp := &vpc2017JsonResponse{} 345 err = json.Unmarshal(body, resp) 346 if err != nil { 347 return 348 } 349 if resp.Code != 0 { 350 return sdkerrors.NewTencentCloudSDKError(resp.CodeDesc, resp.Message, "") 351 } 352 353 return nil 354 } 355 356 func (r *vpc2017JsonResponse) GetResponse() *interface{} { 357 if r.Response == nil { 358 result, _ := jsonutils.Parse([]byte(`{"data":[],"totalCount":0}`)) 359 resultMap := result.(*jsonutils.JSONDict) 360 resultMap.Update(jsonutils.Marshal(r)) 361 return func(resp interface{}) *interface{} { 362 return &resp 363 }(resultMap) 364 } 365 return func(resp interface{}) *interface{} { 366 return &resp 367 }(jsonutils.Marshal(r)) 368 } 369 370 // SSL证书专用response 371 type wssJsonResponse struct { 372 Code int `json:"code"` 373 CodeDesc string `json:"codeDesc"` 374 ProjectId int `json:"projectId"` 375 Message string `json:"message"` 376 Response *interface{} `json:"data"` 377 } 378 379 func (r *wssJsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error) { 380 resp := &wssJsonResponse{} 381 err = json.Unmarshal(body, resp) 382 if err != nil { 383 return 384 } 385 if resp.Code != 0 { 386 return sdkerrors.NewTencentCloudSDKError(resp.CodeDesc, resp.Message, "") 387 } 388 389 return nil 390 } 391 392 func (r *wssJsonResponse) GetResponse() *interface{} { 393 if r.Response == nil { 394 return func(resp interface{}) *interface{} { 395 return &resp 396 }(jsonutils.Marshal(r)) 397 } 398 return r.Response 399 } 400 401 // 2017版负载均衡API专用response 402 type lbJsonResponse struct { 403 Response map[string]interface{} 404 } 405 406 func (r *lbJsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error) { 407 resp := &wssJsonResponse{} 408 err = json.Unmarshal(body, resp) 409 if err != nil { 410 return 411 } 412 if resp.Code != 0 { 413 return sdkerrors.NewTencentCloudSDKError(resp.CodeDesc, resp.Message, "") 414 } 415 416 // hook 由于目前只能从这个方法中拿到原始的body.这里将原始body hook 到 Response 417 err = json.Unmarshal(body, &r.Response) 418 if err != nil { 419 return 420 } 421 422 return nil 423 } 424 425 func (r *lbJsonResponse) GetResponse() *interface{} { 426 return func(resp interface{}) *interface{} { 427 return &resp 428 }(r.Response) 429 } 430 431 // 3.0版本通用response 432 type QcloudResponse struct { 433 *tchttp.BaseResponse 434 Response *interface{} `json:"Response"` 435 } 436 437 func (r *QcloudResponse) GetResponse() *interface{} { 438 return r.Response 439 } 440 441 func _jsonRequest(client *common.Client, domain string, version string, apiName string, params map[string]string, updateFun func(string, string), debug bool, retry bool) (jsonutils.JSONObject, error) { 442 req := &tchttp.BaseRequest{} 443 _profile := profile.NewClientProfile() 444 _profile.SignMethod = common.SHA256 445 client.WithProfile(_profile) 446 service := strings.Split(domain, ".")[0] 447 req.Init().WithApiInfo(service, version, apiName) 448 req.SetDomain(domain) 449 450 for k, v := range params { 451 if strings.HasSuffix(k, "Ids.0") && len(v) == 0 { 452 return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s = %s", k, v) 453 } 454 req.GetParams()[k] = v 455 } 456 457 resp := &QcloudResponse{ 458 BaseResponse: &tchttp.BaseResponse{}, 459 } 460 ret, err := _baseJsonRequest(client, req, resp, apiName, debug, retry) 461 if err != nil { 462 if errors.Cause(err) == httperrors.ErrNoPermission && updateFun != nil { 463 updateFun(service, apiName) 464 } 465 return nil, err 466 } 467 return ret, nil 468 } 469 470 // 老版本腾讯云api。 适用于类似 https://cvm.api.qcloud.com/v2/index.php 这样的带/v2/index.php路径的接口 471 // todo: 添加自定义response参数 472 func _phpJsonRequest(client *common.Client, resp qcloudResponse, domain string, path string, version string, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) { 473 req := &phpJsonRequest{Path: path} 474 _profile := profile.NewClientProfile() 475 _profile.SignMethod = common.SHA256 476 client.WithProfile(_profile) 477 service := strings.Split(domain, ".")[0] 478 req.Init().WithApiInfo(service, version, apiName) 479 req.SetDomain(domain) 480 481 for k, v := range params { 482 if strings.HasSuffix(k, "Ids.0") && len(v) == 0 { 483 return nil, cloudprovider.ErrNotFound 484 } 485 req.GetParams()[k] = v 486 } 487 488 ret, err := _baseJsonRequest(client, req, resp, apiName, debug, true) 489 if err != nil { 490 if errors.Cause(err) == httperrors.ErrNoPermission && updateFunc != nil { 491 updateFunc(service, apiName) 492 } 493 return nil, err 494 } 495 return ret, nil 496 } 497 498 func _baseJsonRequest(client *common.Client, req tchttp.Request, resp qcloudResponse, apiName string, debug bool, retry bool) (jsonutils.JSONObject, error) { 499 tryMax := 1 500 if retry { 501 tryMax = 3 502 } 503 var err error 504 for i := 1; i <= tryMax; i++ { 505 err = client.Send(req, resp) 506 if err == nil { 507 break 508 } 509 needRetry := false 510 e, ok := err.(*sdkerrors.TencentCloudSDKError) 511 if ok { 512 if strings.HasPrefix(e.Code, "UnauthorizedOperation.") || 513 strings.HasPrefix(e.Code, "AuthFailure.") || 514 utils.IsInStringArray(e.Code, []string{ 515 "SecretidNotAuthAccessResource", 516 "UnauthorizedOperation", 517 "InvalidParameter.PermissionDenied", 518 "AuthFailure", 519 }) { 520 return nil, errors.Wrapf(httperrors.ErrNoPermission, err.Error()) 521 } 522 if utils.IsInStringArray(e.Code, []string{ 523 "AuthFailure.SecretIdNotFound", 524 "AuthFailure.SignatureFailure", 525 }) { 526 return nil, errors.Wrapf(httperrors.ErrInvalidAccessKey, err.Error()) 527 } 528 if utils.IsInStringArray(e.Code, []string{ 529 "InvalidParameter.RoleNotExist", 530 "ResourceNotFound", 531 "FailedOperation.CertificateNotFound", 532 }) { 533 return nil, errors.Wrapf(cloudprovider.ErrNotFound, err.Error()) 534 } 535 536 if e.Code == "UnsupportedRegion" { 537 return nil, cloudprovider.ErrNotSupported 538 } 539 if e.Code == "InvalidParameterValue" && apiName == "GetMonitorData" && strings.Contains(e.Message, "the instance has been destroyed") { 540 return nil, cloudprovider.ErrNotFound 541 } 542 543 if utils.IsInStringArray(e.Code, []string{ 544 "InternalError", 545 "MutexOperation.TaskRunning", // Code=DesOperation.MutexTaskRunning, Message=Mutex task is running, please try later 546 "InvalidInstance.NotSupported", // Code=InvalidInstance.NotSupported, Message=The request does not support the instances `ins-bg54517v` which are in operation or in a special state., 重装系统后立即关机有可能会引发 Code=InvalidInstance.NotSupported 错误, 重试可以避免任务失败 547 "InvalidAddressId.StatusNotPermit", // Code=InvalidAddressId.StatusNotPermit, Message=The status `"UNBINDING"` for AddressId `"eip-m3kix9kx"` is not permit to do this operation., EIP删除需要重试 548 "RequestLimitExceeded", 549 "OperationDenied.InstanceOperationInProgress", // 调整配置后开机 Code=OperationDenied.InstanceOperationInProgress, Message=实例`['ins-nksicizg']`操作进行中,请等待, RequestId=c9951005-b22c-43c1-84aa-d923d49addcf 550 }) { 551 needRetry = true 552 } 553 } 554 555 if !needRetry { 556 for _, msg := range []string{ 557 "EOF", 558 "TLS handshake timeout", 559 "try later", 560 "i/o timeout", 561 } { 562 if strings.Contains(err.Error(), msg) { 563 needRetry = true 564 break 565 } 566 } 567 } 568 569 if needRetry { 570 log.Errorf("request url %s\nparams: %s\nerror: %v\ntry after %d seconds", req.GetDomain(), jsonutils.Marshal(req.GetParams()).PrettyString(), err, i*10) 571 time.Sleep(time.Second * time.Duration(i*10)) 572 continue 573 } 574 log.Errorf("request url: %s\nparams: %s\nresponse: %v\nerror: %v", req.GetDomain(), jsonutils.Marshal(req.GetParams()).PrettyString(), resp.GetResponse(), err) 575 return nil, err 576 } 577 if debug { 578 log.Debugf("request: %s", req.GetParams()) 579 response := resp.GetResponse() 580 if response != nil { 581 log.Debugf("response: %s", jsonutils.Marshal(response).PrettyString()) 582 } 583 } 584 if err != nil { 585 return nil, err 586 } 587 return jsonutils.Marshal(resp.GetResponse()), nil 588 } 589 590 func (client *SQcloudClient) GetRegions() []SRegion { 591 regions := make([]SRegion, len(client.iregions)) 592 for i := 0; i < len(regions); i++ { 593 region := client.iregions[i].(*SRegion) 594 regions[i] = *region 595 } 596 return regions 597 } 598 599 func (client *SQcloudClient) getDefaultClient(params map[string]string) (*common.Client, error) { 600 regionId := QCLOUD_DEFAULT_REGION 601 if len(params) > 0 { 602 if region, ok := params["Region"]; ok { 603 regionId = region 604 } 605 } 606 return client.getSdkClient(regionId) 607 } 608 609 func (client *SQcloudClient) getSdkClient(regionId string) (*common.Client, error) { 610 cli, err := common.NewClientWithSecretId(client.secretId, client.secretKey, regionId) 611 if err != nil { 612 return nil, err 613 } 614 httpClient := client.cpcfg.AdaptiveTimeoutHttpClient() 615 ts, _ := httpClient.Transport.(*http.Transport) 616 cli.WithHttpTransport(cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response), error) { 617 body, err := ioutil.ReadAll(req.Body) 618 if err != nil { 619 return nil, errors.Wrapf(err, "ioutil.ReadAll") 620 } 621 req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) 622 params, err := url.ParseQuery(string(body)) 623 if err != nil { 624 return nil, errors.Wrapf(err, "ParseQuery(%s)", string(body)) 625 } 626 service := strings.Split(req.URL.Host, ".")[0] 627 action := params.Get("Action") 628 respCheck := func(resp *http.Response) { 629 if client.cpcfg.UpdatePermission != nil { 630 client.cpcfg.UpdatePermission(service, action) 631 } 632 } 633 if client.cpcfg.ReadOnly { 634 for _, prefix := range []string{"Get", "List", "Describe"} { 635 if strings.HasPrefix(action, prefix) { 636 return respCheck, nil 637 } 638 } 639 return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, action) 640 } 641 return respCheck, nil 642 })) 643 return cli, nil 644 } 645 646 func (client *SQcloudClient) tkeRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 647 cli, err := client.getDefaultClient(params) 648 if err != nil { 649 return nil, err 650 } 651 return tkeRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 652 } 653 654 func (client *SQcloudClient) vpcRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 655 cli, err := client.getDefaultClient(params) 656 if err != nil { 657 return nil, err 658 } 659 return vpcRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 660 } 661 662 func (client *SQcloudClient) auditRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 663 cli, err := client.getDefaultClient(params) 664 if err != nil { 665 return nil, err 666 } 667 return auditRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 668 } 669 670 func (client *SQcloudClient) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 671 cli, err := client.getDefaultClient(params) 672 if err != nil { 673 return nil, err 674 } 675 return cbsRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 676 } 677 678 func (client *SQcloudClient) accountRequestRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 679 cli, err := client.getDefaultClient(params) 680 if err != nil { 681 return nil, err 682 } 683 return accountRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 684 } 685 686 func (client *SQcloudClient) clbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 687 cli, err := client.getDefaultClient(params) 688 if err != nil { 689 return nil, err 690 } 691 return clbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 692 } 693 694 func (client *SQcloudClient) lbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 695 cli, err := client.getDefaultClient(params) 696 if err != nil { 697 return nil, err 698 } 699 return lbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 700 } 701 702 func (client *SQcloudClient) cdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 703 cli, err := client.getDefaultClient(params) 704 if err != nil { 705 return nil, err 706 } 707 return cdbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 708 } 709 710 func (client *SQcloudClient) esRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 711 cli, err := client.getDefaultClient(params) 712 if err != nil { 713 return nil, err 714 } 715 716 return esRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 717 } 718 719 func (client *SQcloudClient) kafkaRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 720 cli, err := client.getDefaultClient(params) 721 if err != nil { 722 return nil, err 723 } 724 725 return kafkaRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 726 } 727 728 func (client *SQcloudClient) redisRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 729 cli, err := client.getDefaultClient(params) 730 if err != nil { 731 return nil, err 732 } 733 734 return redisRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 735 } 736 737 func (client *SQcloudClient) dcdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 738 cli, err := client.getDefaultClient(params) 739 if err != nil { 740 return nil, err 741 } 742 743 return dcdbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 744 } 745 746 func (client *SQcloudClient) mongodbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 747 cli, err := client.getDefaultClient(params) 748 if err != nil { 749 return nil, err 750 } 751 752 return mongodbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 753 } 754 755 func (client *SQcloudClient) memcachedRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 756 cli, err := client.getDefaultClient(params) 757 if err != nil { 758 return nil, err 759 } 760 761 return memcachedRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 762 } 763 764 func (client *SQcloudClient) mariadbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 765 cli, err := client.getDefaultClient(params) 766 if err != nil { 767 return nil, err 768 } 769 return mariadbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 770 } 771 772 func (client *SQcloudClient) postgresRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 773 cli, err := client.getDefaultClient(params) 774 if err != nil { 775 return nil, err 776 } 777 return postgresRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 778 } 779 780 func (client *SQcloudClient) sqlserverRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 781 cli, err := client.getDefaultClient(params) 782 if err != nil { 783 return nil, err 784 } 785 return sqlserverRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 786 } 787 788 // deprecated 789 func (client *SQcloudClient) wssRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 790 cli, err := client.getDefaultClient(params) 791 if err != nil { 792 return nil, err 793 } 794 return wssRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 795 } 796 797 func (client *SQcloudClient) sslRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 798 cli, err := client.getDefaultClient(params) 799 if err != nil { 800 return nil, err 801 } 802 return sslRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 803 } 804 805 func (client *SQcloudClient) dnsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 806 cli, err := client.getDefaultClient(params) 807 if err != nil { 808 return nil, err 809 } 810 return dnsRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 811 } 812 813 func (client *SQcloudClient) vpc2017Request(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 814 cli, err := client.getDefaultClient(params) 815 if err != nil { 816 return nil, err 817 } 818 return vpc2017Request(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 819 } 820 821 func (client *SQcloudClient) billingRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 822 cli, err := client.getDefaultClient(params) 823 if err != nil { 824 return nil, err 825 } 826 return billingRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 827 } 828 829 func (client *SQcloudClient) camRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 830 cli, err := client.getDefaultClient(params) 831 if err != nil { 832 return nil, err 833 } 834 return camRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 835 } 836 837 func (client *SQcloudClient) cdnRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 838 cli, err := client.getDefaultClient(params) 839 if err != nil { 840 return nil, err 841 } 842 return cdnRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 843 } 844 845 func (client *SQcloudClient) stsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 846 cli, err := client.getDefaultClient(params) 847 if err != nil { 848 return nil, err 849 } 850 return stsRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug) 851 } 852 853 func (client *SQcloudClient) jsonRequest(apiName string, params map[string]string, retry bool) (jsonutils.JSONObject, error) { 854 cli, err := client.getDefaultClient(params) 855 if err != nil { 856 return nil, err 857 } 858 return jsonRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug, retry) 859 } 860 861 func (client *SQcloudClient) fetchRegions() error { 862 body, err := client.jsonRequest("DescribeRegions", nil, true) 863 if err != nil { 864 log.Errorf("fetchRegions fail %s", err) 865 return err 866 } 867 868 regions := make([]SRegion, 0) 869 err = body.Unmarshal(®ions, "RegionSet") 870 if err != nil { 871 log.Errorf("unmarshal json error %s", err) 872 return err 873 } 874 client.iregions = make([]cloudprovider.ICloudRegion, len(regions)) 875 for i := 0; i < len(regions); i++ { 876 regions[i].client = client 877 client.iregions[i] = ®ions[i] 878 } 879 return nil 880 } 881 882 func (client *SQcloudClient) getCosClient(bucket *SBucket) (*cos.Client, error) { 883 var baseUrl *cos.BaseURL 884 if bucket != nil { 885 u, _ := url.Parse(bucket.getBucketUrl()) 886 baseUrl = &cos.BaseURL{ 887 BucketURL: u, 888 } 889 } 890 ts := &http.Transport{ 891 Proxy: client.cpcfg.ProxyFunc, 892 } 893 cosClient := cos.NewClient( 894 baseUrl, 895 &http.Client{ 896 Transport: &cos.AuthorizationTransport{ 897 SecretID: client.secretId, 898 SecretKey: client.secretKey, 899 Transport: &debug.DebugRequestTransport{ 900 RequestHeader: client.debug, 901 RequestBody: client.debug, 902 ResponseHeader: client.debug, 903 ResponseBody: client.debug, 904 Transport: cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response), error) { 905 method, path := req.Method, req.URL.Path 906 respCheck := func(resp *http.Response) { 907 if resp.StatusCode == 403 { 908 if client.cpcfg.UpdatePermission != nil { 909 client.cpcfg.UpdatePermission("cos", fmt.Sprintf("%s %s", method, path)) 910 } 911 } 912 } 913 if client.cpcfg.ReadOnly { 914 if req.Method == "GET" || req.Method == "HEAD" { 915 return respCheck, nil 916 } 917 return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path) 918 } 919 return respCheck, nil 920 }), 921 }, 922 }, 923 }, 924 ) 925 return cosClient, nil 926 } 927 928 func (self *SQcloudClient) invalidateIBuckets() { 929 self.ibuckets = nil 930 } 931 932 func (self *SQcloudClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) { 933 if self.ibuckets == nil { 934 err := self.fetchBuckets() 935 if err != nil { 936 return nil, errors.Wrap(err, "fetchBuckets") 937 } 938 } 939 return self.ibuckets, nil 940 } 941 942 func (client *SQcloudClient) verifyAppId() error { 943 region, err := client.getDefaultRegion() 944 if err != nil { 945 return errors.Wrap(err, "getDefaultRegion") 946 } 947 bucket := SBucket{ 948 region: region, 949 Name: "yuniondocument", 950 } 951 cli, err := client.getCosClient(&bucket) 952 if err != nil { 953 return errors.Wrap(err, "getCosClient") 954 } 955 resp, err := cli.Bucket.Head(context.Background()) 956 if resp != nil { 957 defer httputils.CloseResponse(resp.Response) 958 if resp.StatusCode < 400 || resp.StatusCode == 404 { 959 return nil 960 } 961 return errors.Error(fmt.Sprintf("invalid AppId: %d", resp.StatusCode)) 962 } 963 return errors.Wrap(err, "Head") 964 } 965 966 func (client *SQcloudClient) fetchBuckets() error { 967 coscli, err := client.getCosClient(nil) 968 if err != nil { 969 return errors.Wrap(err, "GetCosClient") 970 } 971 s, _, err := coscli.Service.Get(context.Background()) 972 if err != nil { 973 return errors.Wrap(err, "coscli.Service.Get") 974 } 975 client.ownerId = s.Owner.ID 976 client.ownerName = s.Owner.DisplayName 977 978 ret := make([]cloudprovider.ICloudBucket, 0) 979 for i := range s.Buckets { 980 bInfo := s.Buckets[i] 981 createAt, _ := timeutils.ParseTimeStr(bInfo.CreationDate) 982 slashPos := strings.LastIndexByte(bInfo.Name, '-') 983 appId := bInfo.Name[slashPos+1:] 984 if appId != client.appId { 985 log.Errorf("[%s %s] Inconsistent appId: %s expect %s", bInfo.Name, bInfo.Region, appId, client.appId) 986 } 987 name := bInfo.Name[:slashPos] 988 region, err := client.getIRegionByRegionId(bInfo.Region) 989 var zone cloudprovider.ICloudZone 990 if err != nil { 991 log.Errorf("fail to find region %s", bInfo.Region) 992 // possibly a zone, try zone 993 regionStr := func() string { 994 info := strings.Split(bInfo.Region, "-") 995 if num, _ := strconv.Atoi(info[len(info)-1]); num > 0 { 996 return strings.TrimSuffix(bInfo.Region, fmt.Sprintf("-%d", num)) 997 } 998 return bInfo.Region 999 }() 1000 region, err = client.getIRegionByRegionId(regionStr) 1001 if err != nil { 1002 log.Errorf("fail to find region %s", regionStr) 1003 continue 1004 } 1005 zone, _ = region.(*SRegion).getZoneById(bInfo.Region) 1006 log.Debugf("find zonal bucket %s", zone.GetId()) 1007 } 1008 b := SBucket{ 1009 region: region.(*SRegion), 1010 appId: appId, 1011 Name: name, 1012 Location: bInfo.Region, 1013 CreateDate: createAt, 1014 } 1015 if zone != nil { 1016 b.zone = zone.(*SZone) 1017 } 1018 ret = append(ret, &b) 1019 } 1020 client.ibuckets = ret 1021 return nil 1022 } 1023 1024 func (client *SQcloudClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { 1025 err := client.fetchRegions() 1026 if err != nil { 1027 return nil, err 1028 } 1029 subAccount := cloudprovider.SSubAccount{} 1030 subAccount.Name = client.cpcfg.Name 1031 subAccount.Account = client.secretId 1032 subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL 1033 subAccount.DefaultProjectId = "0" 1034 if len(client.appId) > 0 { 1035 subAccount.Account = fmt.Sprintf("%s/%s", client.secretId, client.appId) 1036 } 1037 return []cloudprovider.SSubAccount{subAccount}, nil 1038 } 1039 1040 func (self *SQcloudClient) GetAccountId() string { 1041 if len(self.ownerId) == 0 { 1042 caller, err := self.GetCallerIdentity() 1043 if err == nil { 1044 self.ownerId = caller.AccountId 1045 } 1046 } 1047 return self.ownerId 1048 } 1049 1050 func (client *SQcloudClient) GetIamLoginUrl() string { 1051 return fmt.Sprintf("https://cloud.tencent.com/login/subAccount") 1052 } 1053 1054 func (client *SQcloudClient) GetIRegions() []cloudprovider.ICloudRegion { 1055 return client.iregions 1056 } 1057 1058 func (client *SQcloudClient) getDefaultRegion() (*SRegion, error) { 1059 iregion, err := client.getIRegionByRegionId(QCLOUD_DEFAULT_REGION) 1060 if err != nil { 1061 return nil, errors.Wrap(err, "getIRegionByRegionId") 1062 } 1063 return iregion.(*SRegion), nil 1064 } 1065 1066 func (client *SQcloudClient) getIRegionByRegionId(id string) (cloudprovider.ICloudRegion, error) { 1067 for i := 0; i < len(client.iregions); i++ { 1068 if client.iregions[i].GetId() == id { 1069 return client.iregions[i], nil 1070 } 1071 } 1072 return nil, cloudprovider.ErrNotFound 1073 } 1074 1075 func (client *SQcloudClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { 1076 for i := 0; i < len(client.iregions); i++ { 1077 if client.iregions[i].GetGlobalId() == id { 1078 return client.iregions[i], nil 1079 } 1080 } 1081 return nil, cloudprovider.ErrNotFound 1082 } 1083 1084 func (client *SQcloudClient) GetRegion(regionId string) *SRegion { 1085 for i := 0; i < len(client.iregions); i++ { 1086 if client.iregions[i].GetId() == regionId { 1087 return client.iregions[i].(*SRegion) 1088 } 1089 } 1090 return nil 1091 } 1092 1093 func (client *SQcloudClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) { 1094 for i := 0; i < len(client.iregions); i++ { 1095 ihost, err := client.iregions[i].GetIHostById(id) 1096 if err == nil { 1097 return ihost, nil 1098 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 1099 return nil, err 1100 } 1101 } 1102 return nil, cloudprovider.ErrNotFound 1103 } 1104 1105 func (client *SQcloudClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { 1106 for i := 0; i < len(client.iregions); i++ { 1107 ihost, err := client.iregions[i].GetIVpcById(id) 1108 if err == nil { 1109 return ihost, nil 1110 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 1111 return nil, err 1112 } 1113 } 1114 return nil, cloudprovider.ErrNotFound 1115 } 1116 1117 func (client *SQcloudClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { 1118 for i := 0; i < len(client.iregions); i++ { 1119 ihost, err := client.iregions[i].GetIStorageById(id) 1120 if err == nil { 1121 return ihost, nil 1122 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 1123 return nil, err 1124 } 1125 } 1126 return nil, cloudprovider.ErrNotFound 1127 } 1128 1129 type SAccountBalance struct { 1130 AvailableAmount float64 1131 AvailableCashAmount float64 1132 CreditAmount float64 1133 MybankCreditAmount float64 1134 Currency string 1135 } 1136 1137 func (client *SQcloudClient) QueryAccountBalance() (*SAccountBalance, error) { 1138 balance := SAccountBalance{} 1139 body, err := client.billingRequest("DescribeAccountBalance", nil) 1140 if err != nil { 1141 if isError(err, []string{"UnauthorizedOperation.NotFinanceAuth"}) { 1142 return nil, cloudprovider.ErrNoBalancePermission 1143 } 1144 log.Errorf("DescribeAccountBalance fail %s", err) 1145 return nil, err 1146 } 1147 log.Debugf("%s", body) 1148 balanceCent, _ := body.Float("Balance") 1149 balance.AvailableAmount = balanceCent / 100.0 1150 return &balance, nil 1151 } 1152 1153 func (client *SQcloudClient) GetIProjects() ([]cloudprovider.ICloudProject, error) { 1154 projects, err := client.GetProjects() 1155 if err != nil { 1156 return nil, errors.Wrap(err, "GetProjects") 1157 } 1158 projects = append(projects, SProject{ProjectId: "0", ProjectName: "默认项目"}) 1159 iprojects := []cloudprovider.ICloudProject{} 1160 for i := 0; i < len(projects); i++ { 1161 projects[i].client = client 1162 iprojects = append(iprojects, &projects[i]) 1163 } 1164 return iprojects, nil 1165 } 1166 1167 func (self *SQcloudClient) GetCapabilities() []string { 1168 caps := []string{ 1169 cloudprovider.CLOUD_CAPABILITY_PROJECT, 1170 cloudprovider.CLOUD_CAPABILITY_COMPUTE, 1171 cloudprovider.CLOUD_CAPABILITY_NETWORK, 1172 cloudprovider.CLOUD_CAPABILITY_EIP, 1173 cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, 1174 cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, 1175 cloudprovider.CLOUD_CAPABILITY_RDS, 1176 cloudprovider.CLOUD_CAPABILITY_CACHE, 1177 cloudprovider.CLOUD_CAPABILITY_EVENT, 1178 cloudprovider.CLOUD_CAPABILITY_CLOUDID, 1179 cloudprovider.CLOUD_CAPABILITY_DNSZONE, 1180 cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP, 1181 cloudprovider.CLOUD_CAPABILITY_INTERVPCNETWORK, 1182 cloudprovider.CLOUD_CAPABILITY_SAML_AUTH, 1183 cloudprovider.CLOUD_CAPABILITY_QUOTA + cloudprovider.READ_ONLY_SUFFIX, 1184 cloudprovider.CLOUD_CAPABILITY_MONGO_DB + cloudprovider.READ_ONLY_SUFFIX, 1185 cloudprovider.CLOUD_CAPABILITY_ES + cloudprovider.READ_ONLY_SUFFIX, 1186 cloudprovider.CLOUD_CAPABILITY_KAFKA + cloudprovider.READ_ONLY_SUFFIX, 1187 cloudprovider.CLOUD_CAPABILITY_CDN + cloudprovider.READ_ONLY_SUFFIX, 1188 cloudprovider.CLOUD_CAPABILITY_CONTAINER + cloudprovider.READ_ONLY_SUFFIX, 1189 } 1190 return caps 1191 }