yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/aliyun.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 aliyun 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "net/url" 23 "strings" 24 "time" 25 26 "github.com/aliyun/alibaba-cloud-sdk-go/sdk" 27 "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" 28 alierr "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" 29 "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 30 "github.com/aliyun/aliyun-oss-go-sdk/oss" 31 "github.com/pkg/errors" 32 33 "yunion.io/x/jsonutils" 34 "yunion.io/x/log" 35 v "yunion.io/x/pkg/util/version" 36 "yunion.io/x/pkg/utils" 37 38 "yunion.io/x/onecloud/pkg/compute/options" 39 "yunion.io/x/onecloud/pkg/httperrors" 40 "yunion.io/x/onecloud/pkg/util/httputils" 41 42 api "yunion.io/x/cloudmux/pkg/apis/compute" 43 "yunion.io/x/cloudmux/pkg/cloudprovider" 44 ) 45 46 const ( 47 ALIYUN_INTERNATIONAL_CLOUDENV = "InternationalCloud" 48 ALIYUN_FINANCE_CLOUDENV = "FinanceCloud" 49 50 CLOUD_PROVIDER_ALIYUN = api.CLOUD_PROVIDER_ALIYUN 51 CLOUD_PROVIDER_ALIYUN_CN = "阿里云" 52 CLOUD_PROVIDER_ALIYUN_EN = "Aliyun" 53 54 ALIYUN_DEFAULT_REGION = "cn-hangzhou" 55 56 ALIYUN_API_VERSION = "2014-05-26" 57 ALIYUN_API_VERSION_VPC = "2016-04-28" 58 ALIYUN_API_VERSION_LB = "2014-05-15" 59 ALIYUN_API_VERSION_KVS = "2015-01-01" 60 61 ALIYUN_API_VERSION_TRIAL = "2020-07-06" 62 63 ALIYUN_BSS_API_VERSION = "2017-12-14" 64 65 ALIYUN_RAM_API_VERSION = "2015-05-01" 66 ALIYUN_RDS_API_VERSION = "2014-08-15" 67 ALIYUN_RM_API_VERSION = "2020-03-31" 68 ALIYUN_STS_API_VERSION = "2015-04-01" 69 ALIYUN_PVTZ_API_VERSION = "2018-01-01" 70 ALIYUN_ALIDNS_API_VERSION = "2015-01-09" 71 ALIYUN_CBN_API_VERSION = "2017-09-12" 72 ALIYUN_CDN_API_VERSION = "2018-05-10" 73 ALIYUN_IMS_API_VERSION = "2019-08-15" 74 ALIYUN_NAS_API_VERSION = "2017-06-26" 75 ALIYUN_WAF_API_VERSION = "2019-09-10" 76 ALIYUN_MONGO_DB_API_VERSION = "2015-12-01" 77 ALIYUN_ES_API_VERSION = "2017-06-13" 78 ALIYUN_KAFKA_API_VERSION = "2019-09-16" 79 ALIYUN_K8S_API_VERSION = "2015-12-15" 80 ALIYUN_OTS_API_VERSION = "2016-06-20" 81 82 ALIYUN_SERVICE_ECS = "ecs" 83 ALIYUN_SERVICE_VPC = "vpc" 84 ALIYUN_SERVICE_RDS = "rds" 85 ALIYUN_SERVICE_SLB = "slb" 86 ALIYUN_SERVICE_KVS = "kvs" 87 ALIYUN_SERVICE_NAS = "nas" 88 ALIYUN_SERVICE_CDN = "cdn" 89 ALIYUN_SERVICE_MONGO_DB = "mongodb" 90 ) 91 92 var ( 93 // https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.18.675f2b8cu8CN5K#concept-zt4-cvy-5db 94 OSS_FINANCE_REGION_MAP = map[string]string{ 95 "cn-hzfinance": "cn-hangzhou", 96 "cn-shanghai-finance-1-pub": "cn-shanghai-finance-1", 97 "cn-szfinance": "cn-shenzhen-finance-1", 98 99 "cn-hzjbp": "cn-hangzhou", 100 "cn-shanghai-finance-1": "cn-shanghai-finance-1", 101 "cn-shenzhen-finance-1": "cn-shenzhen-finance-1", 102 } 103 ) 104 105 type AliyunClientConfig struct { 106 cpcfg cloudprovider.ProviderConfig 107 cloudEnv string // 服务区域 InternationalCloud | FinanceCloud 108 accessKey string 109 accessSecret string 110 debug bool 111 } 112 113 func NewAliyunClientConfig(cloudEnv, accessKey, accessSecret string) *AliyunClientConfig { 114 cfg := &AliyunClientConfig{ 115 cloudEnv: cloudEnv, 116 accessKey: accessKey, 117 accessSecret: accessSecret, 118 } 119 return cfg 120 } 121 122 func (cfg *AliyunClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *AliyunClientConfig { 123 cfg.cpcfg = cpcfg 124 return cfg 125 } 126 127 func (cfg *AliyunClientConfig) Debug(debug bool) *AliyunClientConfig { 128 cfg.debug = debug 129 return cfg 130 } 131 132 func (cfg AliyunClientConfig) Copy() AliyunClientConfig { 133 return cfg 134 } 135 136 type SAliyunClient struct { 137 *AliyunClientConfig 138 139 ownerId string 140 141 nasEndpoints map[string]string 142 vpcEndpoints map[string]string 143 144 resourceGroups []SResourceGroup 145 146 iregions []cloudprovider.ICloudRegion 147 iBuckets []cloudprovider.ICloudBucket 148 } 149 150 func NewAliyunClient(cfg *AliyunClientConfig) (*SAliyunClient, error) { 151 client := SAliyunClient{ 152 AliyunClientConfig: cfg, 153 nasEndpoints: map[string]string{}, 154 vpcEndpoints: map[string]string{}, 155 } 156 return &client, client.fetchRegions() 157 } 158 159 func jsonRequest(client *sdk.Client, domain, apiVersion, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) { 160 if debug { 161 log.Debugf("request %s %s %s %s", domain, apiVersion, apiName, params) 162 } 163 var resp jsonutils.JSONObject 164 var err error 165 for i := 1; i < 4; i++ { 166 resp, err = _jsonRequest(client, domain, apiVersion, apiName, params) 167 retry := false 168 if err != nil { 169 for _, code := range []string{ 170 "ErrorClusterNotFound", 171 } { 172 if strings.Contains(err.Error(), code) { 173 return nil, errors.Wrap(cloudprovider.ErrNotFound, err.Error()) 174 } 175 } 176 if e, ok := errors.Cause(err).(*alierr.ServerError); ok { 177 code := e.ErrorCode() 178 switch code { 179 case "InternalError": 180 if apiName == "QueryAccountBalance" { 181 return nil, errors.Wrapf(httperrors.ErrNoPermission, err.Error()) 182 } 183 return nil, err 184 case "InvalidAccessKeyId.NotFound", 185 "InvalidAccessKeyId", 186 "NoEnabledAccessKey", 187 "InvalidAccessKeyId.Inactive", 188 "Forbidden.AccessKeyDisabled", 189 "Forbidden.AccessKey": 190 return nil, errors.Wrapf(httperrors.ErrInvalidAccessKey, err.Error()) 191 case "404 Not Found", "InstanceNotFound": 192 return nil, errors.Wrap(cloudprovider.ErrNotFound, err.Error()) 193 case "OperationDenied.NoStock": 194 return nil, errors.Wrapf(err, "所请求的套餐在指定的区域内已售罄;尝试其他套餐或选择其他区域和可用区。") 195 case "InvalidInstance.NotSupported", 196 "SignatureNonceUsed", // SignatureNonce 重复。每次请求的 SignatureNonce 在 15 分钟内不能重复。 197 "BackendServer.configuring", // 负载均衡的前一个配置项正在配置中,请稍后再试。 198 "Operation.Conflict", // 您当前的操作可能与其他人的操作产生了冲突,请稍后重试。 199 "OperationDenied.ResourceControl", // 指定的区域处于资源控制中,请稍后再试。 200 "ServiceIsStopping", // 监听正在停止,请稍后重试。 201 "ProcessingSameRequest", // 正在处理相同的请求。请稍后再试。 202 "ResourceInOperating", // 当前资源正在操作中,请求稍后重试。 203 "InvalidFileSystemStatus.Ordering", // Message: The filesystem is ordering now, please check it later. 204 "OperationUnsupported.EipNatBWPCheck": // create nat snat 205 retry = true 206 default: 207 if strings.HasPrefix(code, "EntityNotExist.") || strings.HasSuffix(code, ".NotFound") || strings.HasSuffix(code, "NotExist") { 208 if strings.HasPrefix(apiName, "Delete") { 209 return jsonutils.NewDict(), nil 210 } 211 return nil, errors.Wrap(cloudprovider.ErrNotFound, err.Error()) 212 } 213 return nil, err 214 } 215 } else { 216 for _, code := range []string{ 217 "EOF", 218 "i/o timeout", 219 "TLS handshake timeout", 220 "Client.Timeout exceeded while awaiting headers", 221 "connection reset by peer", 222 "server misbehaving", 223 "try later", 224 "Another operation is being performed", // Another operation is being performed on the DB instance or the DB instance is faulty(赋予RDS账号权限) 225 } { 226 if strings.Contains(err.Error(), code) { 227 retry = true 228 break 229 } 230 } 231 } 232 } 233 if retry { 234 if debug { 235 log.Debugf("Retry %d...", i) 236 } 237 time.Sleep(time.Second * time.Duration(i*10)) 238 continue 239 } 240 if debug { 241 log.Debugf("Response: %s", resp) 242 } 243 return resp, err 244 } 245 return resp, err 246 } 247 248 func _jsonRequest(client *sdk.Client, domain string, version string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { 249 req := requests.NewCommonRequest() 250 req.Domain = domain 251 req.Version = version 252 req.ApiName = apiName 253 if params != nil { 254 for k, v := range params { 255 req.QueryParams[k] = v 256 } 257 } 258 req.Scheme = "https" 259 req.GetHeaders()["User-Agent"] = "vendor/yunion-OneCloud@" + v.Get().GitVersion 260 method := requests.POST 261 for prefix, _method := range map[string]string{ 262 "Get": requests.GET, 263 "Describe": requests.GET, 264 "List": requests.GET, 265 "Delete": requests.DELETE, 266 } { 267 if strings.HasPrefix(apiName, prefix) { 268 method = _method 269 break 270 } 271 } 272 if strings.HasPrefix(domain, "elasticsearch") { 273 req.Product = "elasticsearch" 274 req.ServiceCode = "elasticsearch" 275 pathPattern, ok := params["PathPattern"] 276 if !ok { 277 return nil, errors.Errorf("Roa request missing pathPattern") 278 } 279 delete(params, "PathPattern") 280 req.PathPattern = pathPattern 281 req.Method = method 282 } else if strings.HasPrefix(domain, "alikafka") { //alikafka DeleteInstance必须显式指定Method 283 req.Method = requests.POST 284 } else if strings.HasPrefix(domain, "cs") { //容器 285 pathPattern, ok := params["PathPattern"] 286 if !ok { 287 return nil, errors.Errorf("Roa request missing pathPattern") 288 } 289 delete(params, "PathPattern") 290 req.PathPattern = pathPattern 291 req.Method = method 292 req.GetHeaders()["Content-Type"] = "application/json" 293 } 294 295 resp, err := processCommonRequest(client, req) 296 if err != nil { 297 return nil, errors.Wrapf(err, "processCommonRequest with params %s", params) 298 } 299 body, err := jsonutils.Parse(resp.GetHttpContentBytes()) 300 if err != nil { 301 return nil, errors.Wrapf(err, "jsonutils.Parse") 302 } 303 //{"Code":"InvalidInstanceType.ValueNotSupported","HostId":"ecs.aliyuncs.com","Message":"The specified instanceType beyond the permitted range.","RequestId":"0042EE30-0EDF-48A7-A414-56229D4AD532"} 304 //{"Code":"200","Message":"successful","PageNumber":1,"PageSize":50,"RequestId":"BB4C970C-0E23-48DC-A3B0-EB21FFC70A29","RouterTableList":{"RouterTableListType":[{"CreationTime":"2017-03-19T13:37:40Z","Description":"","ResourceGroupId":"rg-acfmwie3cqoobmi","RouteTableId":"vtb-j6c60lectdi80rk5xz43g","RouteTableName":"","RouteTableType":"System","RouterId":"vrt-j6c00qrol733dg36iq4qj","RouterType":"VRouter","VSwitchIds":{"VSwitchId":["vsw-j6c3gig5ub4fmi2veyrus"]},"VpcId":"vpc-j6c86z3sh8ufhgsxwme0q"}]},"Success":true,"TotalCount":1} 305 //{"Code":"Success","Data":{"CashCoupon":[]},"Message":"Successful!","RequestId":"87AD7E9A-3F8F-460F-9934-FFFE502325EE","Success":true} 306 if body.Contains("Code") { 307 code, _ := body.GetString("Code") 308 if len(code) > 0 && !utils.IsInStringArray(code, []string{"200", "Success"}) { 309 return nil, fmt.Errorf(body.String()) 310 } 311 } 312 return body, nil 313 } 314 315 func (self *SAliyunClient) getNasEndpoint(regionId string) string { 316 err := self.fetchNasEndpoints() 317 if err != nil { 318 return "nas.aliyuncs.com" 319 } 320 ep, ok := self.nasEndpoints[regionId] 321 if ok && len(ep) > 0 { 322 return ep 323 } 324 return "nas.aliyuncs.com" 325 } 326 327 func (self *SAliyunClient) fetchNasEndpoints() error { 328 if len(self.nasEndpoints) > 0 { 329 return nil 330 } 331 client, err := self.getDefaultClient() 332 if err != nil { 333 return errors.Wrapf(err, "getDefaultClient") 334 } 335 resp, err := jsonRequest(client, "nas.aliyuncs.com", ALIYUN_NAS_API_VERSION, "DescribeRegions", nil, self.debug) 336 if err != nil { 337 return errors.Wrapf(err, "DescribeRegions") 338 } 339 regions := []SRegion{} 340 err = resp.Unmarshal(®ions, "Regions", "Region") 341 if err != nil { 342 return errors.Wrapf(err, "resp.Unmarshal") 343 } 344 for _, region := range regions { 345 self.nasEndpoints[region.RegionId] = region.RegionEndpoint 346 } 347 return nil 348 } 349 350 func (self *SAliyunClient) getDefaultClient() (*sdk.Client, error) { 351 client, err := self.getSdkClient(ALIYUN_DEFAULT_REGION) 352 return client, err 353 } 354 355 func (self *SAliyunClient) getVpcEndpoint(regionId string) string { 356 err := self.fetchVpcEndpoints() 357 if err != nil { 358 return "vpc.aliyuncs.com" 359 } 360 ep, ok := self.vpcEndpoints[regionId] 361 if ok && len(ep) > 0 { 362 return ep 363 } 364 return "vpc.aliyuncs.com" 365 } 366 367 func (self *SAliyunClient) fetchVpcEndpoints() error { 368 if len(self.vpcEndpoints) > 0 { 369 return nil 370 } 371 client, err := self.getDefaultClient() 372 if err != nil { 373 return errors.Wrapf(err, "getDefaultClient") 374 } 375 resp, err := jsonRequest(client, "vpc.aliyuncs.com", ALIYUN_API_VERSION_VPC, "DescribeRegions", nil, self.debug) 376 if err != nil { 377 return errors.Wrapf(err, "DescribeRegions") 378 } 379 regions := []SRegion{} 380 err = resp.Unmarshal(®ions, "Regions", "Region") 381 if err != nil { 382 return errors.Wrapf(err, "resp.Unmarshal") 383 } 384 for _, region := range regions { 385 self.vpcEndpoints[region.RegionId] = region.RegionEndpoint 386 } 387 return nil 388 } 389 390 func (self *SAliyunClient) getSdkClient(regionId string) (*sdk.Client, error) { 391 transport := httputils.GetAdaptiveTransport(true) 392 transport.Proxy = self.cpcfg.ProxyFunc 393 client, err := sdk.NewClientWithOptions( 394 regionId, 395 &sdk.Config{ 396 HttpTransport: transport, 397 Transport: cloudprovider.GetCheckTransport(transport, func(req *http.Request) (func(resp *http.Response), error) { 398 params, err := url.ParseQuery(req.URL.RawQuery) 399 if err != nil { 400 return nil, errors.Wrapf(err, "ParseQuery(%s)", req.URL.RawQuery) 401 } 402 service := strings.Split(req.URL.Host, ".")[0] 403 action := params.Get("Action") 404 respCheck := func(resp *http.Response) { 405 if self.cpcfg.UpdatePermission != nil && resp.StatusCode >= 400 && resp.ContentLength > 0 { 406 body, err := ioutil.ReadAll(resp.Body) 407 if err != nil { 408 return 409 } 410 resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) 411 obj, err := jsonutils.Parse(body) 412 if err != nil { 413 return 414 } 415 ret := struct{ Code string }{} 416 obj.Unmarshal(&ret) 417 if utils.IsInStringArray(ret.Code, []string{ 418 "NoPermission", 419 "SubAccountNoPermission", 420 }) || utils.HasPrefix(ret.Code, "Forbidden") || 421 action == "QueryAccountBalance" && ret.Code == "InternalError" { 422 self.cpcfg.UpdatePermission(service, action) 423 } 424 } 425 } 426 for _, prefix := range []string{"Get", "List", "Describe", "Query"} { 427 if strings.HasPrefix(action, prefix) { 428 return respCheck, nil 429 } 430 } 431 if self.cpcfg.ReadOnly { 432 return respCheck, errors.Wrapf(cloudprovider.ErrAccountReadOnly, action) 433 } 434 return respCheck, nil 435 }), 436 }, 437 &credentials.BaseCredential{ 438 AccessKeyId: self.accessKey, 439 AccessKeySecret: self.accessSecret, 440 }, 441 ) 442 return client, err 443 } 444 445 func (self *SAliyunClient) imsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 446 cli, err := self.getDefaultClient() 447 if err != nil { 448 return nil, err 449 } 450 params = self.SetResourceGropuId(params) 451 return jsonRequest(cli, "ims.aliyuncs.com", ALIYUN_IMS_API_VERSION, apiName, params, self.debug) 452 } 453 454 func (self *SAliyunClient) rmRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 455 cli, err := self.getDefaultClient() 456 if err != nil { 457 return nil, err 458 } 459 return jsonRequest(cli, "resourcemanager.aliyuncs.com", ALIYUN_RM_API_VERSION, apiName, params, self.debug) 460 } 461 462 func (self *SAliyunClient) ecsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 463 cli, err := self.getDefaultClient() 464 if err != nil { 465 return nil, err 466 } 467 params = self.SetResourceGropuId(params) 468 return jsonRequest(cli, "ecs.aliyuncs.com", ALIYUN_API_VERSION, apiName, params, self.debug) 469 } 470 471 func (self *SAliyunClient) pvtzRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 472 cli, err := self.getDefaultClient() 473 if err != nil { 474 return nil, err 475 } 476 params = self.SetResourceGropuId(params) 477 return jsonRequest(cli, "pvtz.aliyuncs.com", ALIYUN_PVTZ_API_VERSION, apiName, params, self.debug) 478 } 479 480 func (self *SAliyunClient) alidnsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 481 cli, err := self.getDefaultClient() 482 if err != nil { 483 return nil, err 484 } 485 params = self.SetResourceGropuId(params) 486 return jsonRequest(cli, "alidns.aliyuncs.com", ALIYUN_ALIDNS_API_VERSION, apiName, params, self.debug) 487 } 488 489 func (self *SAliyunClient) cbnRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 490 cli, err := self.getDefaultClient() 491 if err != nil { 492 return nil, err 493 } 494 params = self.SetResourceGropuId(params) 495 return jsonRequest(cli, "cbn.aliyuncs.com", ALIYUN_CBN_API_VERSION, apiName, params, self.debug) 496 } 497 498 func (self *SAliyunClient) cdnRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 499 cli, err := self.getDefaultClient() 500 if err != nil { 501 return nil, err 502 } 503 params = self.SetResourceGropuId(params) 504 return jsonRequest(cli, "cdn.aliyuncs.com", ALIYUN_CDN_API_VERSION, apiName, params, self.debug) 505 } 506 507 func (self *SAliyunClient) fetchRegions() error { 508 body, err := self.ecsRequest("DescribeRegions", map[string]string{"AcceptLanguage": "zh-CN"}) 509 if err != nil { 510 return errors.Wrapf(err, "DescribeRegions") 511 } 512 513 regions := make([]SRegion, 0) 514 err = body.Unmarshal(®ions, "Regions", "Region") 515 if err != nil { 516 return errors.Wrapf(err, "resp.Unmarshal") 517 } 518 self.iregions = make([]cloudprovider.ICloudRegion, len(regions)) 519 for i := 0; i < len(regions); i += 1 { 520 regions[i].client = self 521 self.iregions[i] = ®ions[i] 522 } 523 return nil 524 } 525 526 // oss endpoint 527 // https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.6.6E8ZkO 528 func getOSSExternalDomain(regionId string) string { 529 return fmt.Sprintf("oss-%s.aliyuncs.com", regionId) 530 } 531 532 func getOSSInternalDomain(regionId string) string { 533 return fmt.Sprintf("oss-%s-internal.aliyuncs.com", regionId) 534 } 535 536 // https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.6.XqEgD1 537 func (client *SAliyunClient) getOssClientByEndpoint(endpoint string) (*oss.Client, error) { 538 // NOTE 539 // 540 // oss package as of version 20181116160301-c6838fdc33ed does not 541 // respect http.ProxyFromEnvironment. 542 // 543 // The ClientOption Proxy, AuthProxy lacks the feature NO_PROXY has 544 // which can be used to whitelist ips, domains from http_proxy, 545 // https_proxy setting 546 // oss use no timeout client so as to send/download large files 547 httpClient := client.cpcfg.AdaptiveTimeoutHttpClient() 548 transport, _ := httpClient.Transport.(*http.Transport) 549 httpClient.Transport = cloudprovider.GetCheckTransport(transport, func(req *http.Request) (func(resp *http.Response), error) { 550 path, method := req.URL.Path, req.Method 551 respCheck := func(resp *http.Response) { 552 if client.cpcfg.UpdatePermission != nil && resp.StatusCode == 403 { 553 client.cpcfg.UpdatePermission("oss", fmt.Sprintf("%s %s", method, path)) 554 } 555 } 556 if client.cpcfg.ReadOnly { 557 if req.Method == "GET" || req.Method == "HEAD" { 558 return respCheck, nil 559 } 560 return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.RawPath) 561 } 562 return respCheck, nil 563 }) 564 cliOpts := []oss.ClientOption{ 565 oss.HTTPClient(httpClient), 566 } 567 if !strings.HasPrefix(endpoint, "http") { 568 endpoint = "https://" + endpoint 569 } 570 cli, err := oss.New(endpoint, client.accessKey, client.accessSecret, cliOpts...) 571 if err != nil { 572 return nil, errors.Wrap(err, "oss.New") 573 } 574 return cli, nil 575 } 576 577 func (client *SAliyunClient) getOssClient(regionId string) (*oss.Client, error) { 578 ep := getOSSExternalDomain(regionId) 579 return client.getOssClientByEndpoint(ep) 580 } 581 582 func (self *SAliyunClient) getRegionByRegionId(id string) (cloudprovider.ICloudRegion, error) { 583 _id, ok := OSS_FINANCE_REGION_MAP[id] 584 if ok { 585 id = _id 586 } 587 for i := 0; i < len(self.iregions); i += 1 { 588 if self.iregions[i].GetId() == id { 589 return self.iregions[i], nil 590 } 591 } 592 return nil, cloudprovider.ErrNotFound 593 } 594 595 func (self *SAliyunClient) invalidateIBuckets() { 596 self.iBuckets = nil 597 } 598 599 func (self *SAliyunClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) { 600 if self.iBuckets == nil { 601 err := self.fetchBuckets() 602 if err != nil { 603 return nil, errors.Wrap(err, "fetchBuckets") 604 } 605 } 606 return self.iBuckets, nil 607 } 608 609 func (self *SAliyunClient) fetchBuckets() error { 610 osscli, err := self.getOssClient(ALIYUN_DEFAULT_REGION) 611 if err != nil { 612 return errors.Wrap(err, "self.getOssClient") 613 } 614 result, err := osscli.ListBuckets() 615 if err != nil { 616 return errors.Wrap(err, "oss.ListBuckets") 617 } 618 619 if len(self.ownerId) == 0 { 620 self.ownerId = result.Owner.ID 621 } 622 623 ret := make([]cloudprovider.ICloudBucket, 0) 624 for _, bInfo := range result.Buckets { 625 regionId := bInfo.Location[4:] 626 region, err := self.getRegionByRegionId(regionId) 627 if err != nil { 628 log.Errorf("cannot find bucket's region %s", regionId) 629 continue 630 } 631 b := SBucket{ 632 region: region.(*SRegion), 633 Name: bInfo.Name, 634 Location: bInfo.Location, 635 CreationDate: bInfo.CreationDate, 636 StorageClass: bInfo.StorageClass, 637 } 638 ret = append(ret, &b) 639 } 640 self.iBuckets = ret 641 return nil 642 } 643 644 func (self *SAliyunClient) GetRegions() []SRegion { 645 regions := make([]SRegion, len(self.iregions)) 646 for i := 0; i < len(regions); i += 1 { 647 region := self.iregions[i].(*SRegion) 648 regions[i] = *region 649 } 650 return regions 651 } 652 653 func (self *SAliyunClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { 654 err := self.fetchRegions() 655 if err != nil { 656 return nil, err 657 } 658 subAccount := cloudprovider.SSubAccount{} 659 subAccount.Name = self.cpcfg.Name 660 subAccount.Account = self.accessKey 661 subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL 662 projects, err := self.GetIProjects() 663 if err != nil { 664 return nil, errors.Wrapf(err, "GetIProject") 665 } 666 for i := range projects { 667 if projects[i].GetName() == "默认资源组" { 668 subAccount.DefaultProjectId = projects[i].GetGlobalId() 669 break 670 } 671 } 672 return []cloudprovider.SSubAccount{subAccount}, nil 673 } 674 675 func (self *SAliyunClient) GetAccountId() string { 676 if len(self.ownerId) > 0 { 677 return self.ownerId 678 } 679 caller, err := self.GetCallerIdentity() 680 if err != nil { 681 return "" 682 } 683 self.ownerId = caller.AccountId 684 return self.ownerId 685 } 686 687 func (self *SAliyunClient) GetIRegions() []cloudprovider.ICloudRegion { 688 return self.iregions 689 } 690 691 func (self *SAliyunClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { 692 for i := 0; i < len(self.iregions); i += 1 { 693 if self.iregions[i].GetGlobalId() == id { 694 return self.iregions[i], nil 695 } 696 } 697 return nil, cloudprovider.ErrNotFound 698 } 699 700 func (self *SAliyunClient) GetRegion(regionId string) *SRegion { 701 if len(regionId) == 0 { 702 regionId = ALIYUN_DEFAULT_REGION 703 } 704 for i := 0; i < len(self.iregions); i += 1 { 705 if self.iregions[i].GetId() == regionId { 706 return self.iregions[i].(*SRegion) 707 } 708 } 709 return nil 710 } 711 712 func (self *SAliyunClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) { 713 for i := 0; i < len(self.iregions); i += 1 { 714 ihost, err := self.iregions[i].GetIHostById(id) 715 if err == nil { 716 return ihost, nil 717 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 718 return nil, err 719 } 720 } 721 return nil, cloudprovider.ErrNotFound 722 } 723 724 func (self *SAliyunClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { 725 for i := 0; i < len(self.iregions); i += 1 { 726 ihost, err := self.iregions[i].GetIVpcById(id) 727 if err == nil { 728 return ihost, nil 729 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 730 return nil, err 731 } 732 } 733 return nil, cloudprovider.ErrNotFound 734 } 735 736 func (self *SAliyunClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { 737 for i := 0; i < len(self.iregions); i += 1 { 738 ihost, err := self.iregions[i].GetIStorageById(id) 739 if err == nil { 740 return ihost, nil 741 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 742 return nil, err 743 } 744 } 745 return nil, cloudprovider.ErrNotFound 746 } 747 748 func (self *SAliyunClient) GetProjects() ([]SResourceGroup, error) { 749 if len(self.resourceGroups) > 0 { 750 return self.resourceGroups, nil 751 } 752 pageSize, pageNumber := 50, 1 753 self.resourceGroups = []SResourceGroup{} 754 for { 755 parts, total, err := self.GetResourceGroups(pageNumber, pageSize) 756 if err != nil { 757 return nil, errors.Wrap(err, "GetResourceGroups") 758 } 759 self.resourceGroups = append(self.resourceGroups, parts...) 760 if len(self.resourceGroups) >= total { 761 break 762 } 763 pageNumber += 1 764 } 765 return self.resourceGroups, nil 766 } 767 768 func (self *SAliyunClient) SetResourceGropuId(params map[string]string) map[string]string { 769 if params == nil { 770 params = map[string]string{} 771 } 772 for _, groupId := range options.Options.AliyunResourceGroups { 773 if utils.IsInStringArray(groupId, self.GetResourceGroupIds()) { 774 params["ResourceGroupId"] = groupId 775 } 776 } 777 return params 778 } 779 780 func (self *SAliyunClient) GetResourceGroupIds() []string { 781 ret := []string{} 782 resourceGroups, err := self.GetProjects() 783 if err != nil { 784 return ret 785 } 786 for i := range resourceGroups { 787 ret = append(ret, resourceGroups[i].Id) 788 } 789 return ret 790 } 791 792 func (self *SAliyunClient) GetIProjects() ([]cloudprovider.ICloudProject, error) { 793 resourceGroups, err := self.GetProjects() 794 if err != nil { 795 return nil, err 796 } 797 ret := []cloudprovider.ICloudProject{} 798 for i := range resourceGroups { 799 ret = append(ret, &resourceGroups[i]) 800 } 801 return ret, nil 802 } 803 804 func (region *SAliyunClient) GetCapabilities() []string { 805 caps := []string{ 806 cloudprovider.CLOUD_CAPABILITY_PROJECT, 807 cloudprovider.CLOUD_CAPABILITY_COMPUTE, 808 cloudprovider.CLOUD_CAPABILITY_NETWORK, 809 cloudprovider.CLOUD_CAPABILITY_EIP, 810 cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, 811 cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, 812 cloudprovider.CLOUD_CAPABILITY_RDS, 813 cloudprovider.CLOUD_CAPABILITY_CACHE, 814 cloudprovider.CLOUD_CAPABILITY_EVENT, 815 cloudprovider.CLOUD_CAPABILITY_CLOUDID, 816 cloudprovider.CLOUD_CAPABILITY_DNSZONE, 817 cloudprovider.CLOUD_CAPABILITY_INTERVPCNETWORK, 818 cloudprovider.CLOUD_CAPABILITY_SAML_AUTH, 819 cloudprovider.CLOUD_CAPABILITY_NAT, 820 cloudprovider.CLOUD_CAPABILITY_NAS, 821 cloudprovider.CLOUD_CAPABILITY_WAF, 822 cloudprovider.CLOUD_CAPABILITY_QUOTA + cloudprovider.READ_ONLY_SUFFIX, 823 cloudprovider.CLOUD_CAPABILITY_MONGO_DB + cloudprovider.READ_ONLY_SUFFIX, 824 cloudprovider.CLOUD_CAPABILITY_ES + cloudprovider.READ_ONLY_SUFFIX, 825 cloudprovider.CLOUD_CAPABILITY_KAFKA + cloudprovider.READ_ONLY_SUFFIX, 826 cloudprovider.CLOUD_CAPABILITY_CDN + cloudprovider.READ_ONLY_SUFFIX, 827 cloudprovider.CLOUD_CAPABILITY_CONTAINER + cloudprovider.READ_ONLY_SUFFIX, 828 cloudprovider.CLOUD_CAPABILITY_TABLESTORE + cloudprovider.READ_ONLY_SUFFIX, 829 } 830 return caps 831 } 832 833 func (self *SAliyunClient) GetAccessEnv() string { 834 switch self.cloudEnv { 835 case ALIYUN_INTERNATIONAL_CLOUDENV: 836 return api.CLOUD_ACCESS_ENV_ALIYUN_GLOBAL 837 case ALIYUN_FINANCE_CLOUDENV: 838 return api.CLOUD_ACCESS_ENV_ALIYUN_FINANCE 839 default: 840 return api.CLOUD_ACCESS_ENV_ALIYUN_GLOBAL 841 } 842 }