| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package aliyun
- import (
- "bytes"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "sync"
- "time"
- "github.com/aliyun/alibaba-cloud-sdk-go/sdk"
- "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
- "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers"
- alierr "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors"
- "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
- "github.com/aliyun/aliyun-oss-go-sdk/oss"
- "github.com/pkg/errors"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/gotypes"
- "yunion.io/x/pkg/util/httputils"
- v "yunion.io/x/pkg/util/version"
- "yunion.io/x/pkg/utils"
- api "yunion.io/x/cloudmux/pkg/apis/compute"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- )
- const (
- ALIYUN_INTERNATIONAL_CLOUDENV = "InternationalCloud"
- ALIYUN_FINANCE_CLOUDENV = "FinanceCloud"
- CLOUD_PROVIDER_ALIYUN = api.CLOUD_PROVIDER_ALIYUN
- CLOUD_PROVIDER_ALIYUN_CN = "阿里云"
- CLOUD_PROVIDER_ALIYUN_EN = "Aliyun"
- ALIYUN_DEFAULT_REGION = "cn-hangzhou"
- ALIYUN_API_VERSION = "2014-05-26"
- ALIYUN_API_VERSION_VPC = "2016-04-28"
- ALIYUN_API_VERSION_LB = "2014-05-15"
- ALIYUN_API_VERSION_KVS = "2015-01-01"
- ALIYUN_API_VERSION_TRIAL = "2020-07-06"
- ALIYUN_BSS_API_VERSION = "2017-12-14"
- ALIYUN_RAM_API_VERSION = "2015-05-01"
- ALIYUN_RDS_API_VERSION = "2014-08-15"
- ALIYUN_RM_API_VERSION = "2020-03-31"
- ALIYUN_STS_API_VERSION = "2015-04-01"
- ALIYUN_PVTZ_API_VERSION = "2018-01-01"
- ALIYUN_ALIDNS_API_VERSION = "2015-01-09"
- ALIYUN_CBN_API_VERSION = "2017-09-12"
- ALIYUN_CDN_API_VERSION = "2018-05-10"
- ALIYUN_IMS_API_VERSION = "2019-08-15"
- ALIYUN_NAS_API_VERSION = "2017-06-26"
- ALIYUN_WAF_API_VERSION = "2019-09-10"
- ALIYUN_WAF_V2_API_VERSION = "2021-10-01"
- ALIYUN_MONGO_DB_API_VERSION = "2015-12-01"
- ALIYUN_ES_API_VERSION = "2017-06-13"
- ALIYUN_KAFKA_API_VERSION = "2019-09-16"
- ALIYUN_K8S_API_VERSION = "2015-12-15"
- ALIYUN_OTS_API_VERSION = "2016-06-20"
- ALIYUN_RD_API_VERSION = "2022-04-19"
- ALIYUN_CAS_API_VERSION = "2018-07-13"
- ALIYUN_SERVICE_ECS = "ecs"
- ALIYUN_SERVICE_VPC = "vpc"
- ALIYUN_SERVICE_RDS = "rds"
- ALIYUN_SERVICE_ES = "es"
- ALIYUN_SERVICE_KAFKA = "kafka"
- ALIYUN_SERVICE_SLB = "slb"
- ALIYUN_SERVICE_KVS = "kvs"
- ALIYUN_SERVICE_NAS = "nas"
- ALIYUN_SERVICE_CDN = "cdn"
- ALIYUN_SERVICE_MONGO_DB = "mongodb"
- DefaultAssumeRoleName = "ResourceDirectoryAccountAccessRole"
- ALIYUN_API_VERSION_ECS = "2014-05-26"
- ALIYUN_API_VERSION_EIP = "2016-04-28"
- ALIYUN_API_VERSION_ELB = "2016-04-28"
- ALIYUN_API_VERSION_VGW = "2016-04-28"
- ALIYUN_API_VERSION_NAS = "2017-06-26"
- ALIYUN_API_VERSION_CON = "2018-12-01"
- ALIYUN_API_VERSION_VHH = "2019-06-01"
- ALIYUN_API_VERSION_BSS = "2017-12-14"
- ALIYUN_API_VERSION_FC = "2021-04-06"
- ALIYUN_API_VERSION_NLB = "2022-04-30"
- ALIYUN_API_VERSION_ALB = "2020-06-16"
- ALIYUN_BSS_BILLING_METHOD_PREPAID = "Subscription"
- ALIYUN_BSS_BILLING_METHOD_POSTPAID = "PayAsYouGo"
- ALIYUN_API_INTERVAL = 5 * time.Second
- DEFAULT_SESSION_DURATION_SECONDS = 3600
- ALIYUN_SERVICE_ALB = "alb"
- ALIYUN_SERVICE_NLB = "nlb"
- ALIYUN_SERVICE_OSS = "oss"
- ALIYUN_SERVICE_EIP = "eip"
- )
- var (
- // https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.18.675f2b8cu8CN5K#concept-zt4-cvy-5db
- OSS_FINANCE_REGION_MAP = map[string]string{
- "cn-hzfinance": "cn-hangzhou",
- "cn-shanghai-finance-1-pub": "cn-shanghai-finance-1",
- "cn-szfinance": "cn-shenzhen-finance-1",
- "cn-hzjbp": "cn-hangzhou",
- "cn-shanghai-finance-1": "cn-shanghai-finance-1",
- "cn-shenzhen-finance-1": "cn-shenzhen-finance-1",
- }
- )
- type AliyunClientConfig struct {
- cpcfg cloudprovider.ProviderConfig
- cloudEnv string // 服务区域 InternationalCloud | FinanceCloud
- accessKey string
- accessSecret string
- accountId string
- debug bool
- }
- func NewAliyunClientConfig(cloudEnv, accessKey, accessSecret string) *AliyunClientConfig {
- cfg := &AliyunClientConfig{
- cloudEnv: cloudEnv,
- accessKey: accessKey,
- accessSecret: accessSecret,
- }
- return cfg
- }
- func (cfg *AliyunClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *AliyunClientConfig {
- cfg.cpcfg = cpcfg
- return cfg
- }
- func (cfg *AliyunClientConfig) AccountId(id string) *AliyunClientConfig {
- cfg.accountId = id
- return cfg
- }
- func (cfg *AliyunClientConfig) Debug(debug bool) *AliyunClientConfig {
- cfg.debug = debug
- return cfg
- }
- func (cfg AliyunClientConfig) Copy() AliyunClientConfig {
- return cfg
- }
- type SAliyunClient struct {
- *AliyunClientConfig
- clients map[string]*sdk.Client
- clientLock sync.Mutex
- ownerId string
- arn string
- nasEndpoints map[string]string
- vpcEndpoints map[string]string
- hbaseEndpoint map[string]string
- iregions []cloudprovider.ICloudRegion
- iBuckets []cloudprovider.ICloudBucket
- }
- func NewAliyunClient(cfg *AliyunClientConfig) (*SAliyunClient, error) {
- client := SAliyunClient{
- AliyunClientConfig: cfg,
- nasEndpoints: map[string]string{},
- vpcEndpoints: map[string]string{},
- }
- return &client, client.fetchRegions()
- }
- func jsonRequest(client *sdk.Client, domain, apiVersion, apiName string, params map[string]string, debug bool) (jsonutils.JSONObject, error) {
- return doRequest(client, domain, apiVersion, apiName, params, nil, debug)
- }
- func doRequest(client *sdk.Client, domain, apiVersion, apiName string, params map[string]string, body interface{}, debug bool) (jsonutils.JSONObject, error) {
- if debug {
- if !gotypes.IsNil(body) {
- log.Debugf("request %s %s %s %s", domain, apiVersion, apiName, jsonutils.Marshal(body))
- } else {
- log.Debugf("request %s %s %s %s", domain, apiVersion, apiName, params)
- }
- }
- var resp jsonutils.JSONObject
- var err error
- for i := 1; i < 4; i++ {
- resp, err = _jsonRequest(client, domain, apiVersion, apiName, params, body)
- retry := false
- if err != nil {
- for _, code := range []string{
- "ErrorClusterNotFound",
- "ErrorNodePoolNotFound",
- } {
- if strings.Contains(err.Error(), code) {
- return nil, errors.Wrap(cloudprovider.ErrNotFound, err.Error())
- }
- }
- if e, ok := errors.Cause(err).(*alierr.ServerError); ok {
- code := e.ErrorCode()
- switch code {
- case "InternalError":
- if apiName != "QueryAccountBalance" {
- return nil, err
- }
- if i != 1 {
- return nil, err
- }
- // 国际版阿里云需要请求新加坡
- domain = "business.ap-southeast-1.aliyuncs.com"
- retry = true
- case "InvalidAccessKeyId.NotFound",
- "InvalidAccessKeyId",
- "NoEnabledAccessKey",
- "InvalidAccessKeyId.Inactive",
- "Forbidden.AccessKeyDisabled",
- "Forbidden.AccessKey":
- return nil, errors.Wrapf(cloudprovider.ErrInvalidAccessKey, "%s", err.Error())
- case "Forbidden.RAM":
- return nil, errors.Wrapf(cloudprovider.ErrForbidden, "%s", err.Error())
- case "404 Not Found", "InstanceNotFound":
- return nil, errors.Wrap(cloudprovider.ErrNotFound, err.Error())
- case "OperationDenied.NoStock":
- return nil, errors.Wrapf(err, "所请求的套餐在指定的区域内已售罄;尝试其他套餐或选择其他区域和可用区。")
- case "InvalidInstance.NotSupported",
- "SignatureNonceUsed", // SignatureNonce 重复。每次请求的 SignatureNonce 在 15 分钟内不能重复。
- "BackendServer.configuring", // 负载均衡的前一个配置项正在配置中,请稍后再试。
- "Operation.Conflict", // 您当前的操作可能与其他人的操作产生了冲突,请稍后重试。
- "OperationDenied.ResourceControl", // 指定的区域处于资源控制中,请稍后再试。
- "ServiceIsStopping", // 监听正在停止,请稍后重试。
- "ProcessingSameRequest", // 正在处理相同的请求。请稍后再试。
- "ResourceInOperating", // 当前资源正在操作中,请求稍后重试。
- "InvalidFileSystemStatus.Ordering", // Message: The filesystem is ordering now, please check it later.
- "OperationUnsupported.EipNatBWPCheck": // create nat snat
- retry = true
- default:
- if strings.HasPrefix(code, "EntityNotExist.") || strings.HasSuffix(code, ".NotFound") || strings.HasSuffix(code, "NotExist") {
- if strings.HasPrefix(apiName, "Delete") {
- return jsonutils.NewDict(), nil
- }
- return nil, errors.Wrap(cloudprovider.ErrNotFound, err.Error())
- }
- return nil, err
- }
- } else {
- for _, code := range []string{
- "EOF",
- "i/o timeout",
- "TLS handshake timeout",
- "Client.Timeout exceeded while awaiting headers",
- "connection reset by peer",
- "context deadline exceeded",
- "server misbehaving",
- "try later",
- "Another operation is being performed", // Another operation is being performed on the DB instance or the DB instance is faulty(赋予RDS账号权限)
- } {
- if strings.Contains(err.Error(), code) {
- retry = true
- break
- }
- }
- }
- }
- if retry {
- if debug {
- log.Debugf("Retry %d...", i)
- }
- if apiName != "QueryAccountBalance" {
- time.Sleep(time.Second * time.Duration(i*10))
- }
- continue
- }
- if debug {
- log.Debugf("Response: %s", resp)
- }
- return resp, err
- }
- return resp, err
- }
- func _jsonRequest(client *sdk.Client, domain string, version string, apiName string, params map[string]string, body interface{}) (jsonutils.JSONObject, error) {
- req := requests.NewCommonRequest()
- req.Domain = domain
- req.Version = version
- req.ApiName = apiName
- if params != nil {
- for k, v := range params {
- req.QueryParams[k] = v
- }
- }
- if body != nil {
- req.Content = []byte(jsonutils.Marshal(body).String())
- req.GetHeaders()["Content-Type"] = "application/json"
- }
- req.Scheme = "https"
- req.GetHeaders()["User-Agent"] = "vendor/yunion-OneCloud@" + v.Get().GitVersion
- method := requests.POST
- for prefix, _method := range map[string]string{
- "Get": requests.GET,
- "Describe": requests.GET,
- "List": requests.GET,
- "Delete": requests.DELETE,
- } {
- if strings.HasPrefix(apiName, prefix) && !strings.HasPrefix(domain, "mongodb") {
- method = _method
- break
- }
- }
- if strings.HasPrefix(domain, "elasticsearch") {
- if strings.HasPrefix(apiName, "UntagResources") {
- method = requests.DELETE
- }
- req.Product = "elasticsearch"
- req.ServiceCode = "elasticsearch"
- pathPattern, ok := params["PathPattern"]
- if !ok {
- return nil, errors.Errorf("Roa request missing pathPattern")
- }
- delete(params, "PathPattern")
- req.PathPattern = pathPattern
- req.Method = method
- } else if strings.HasPrefix(domain, "alikafka") { //alikafka DeleteInstance必须显式指定Method
- req.Method = requests.POST
- } else if strings.HasPrefix(domain, "cs") { //容器
- pathPattern, ok := params["PathPattern"]
- if !ok {
- return nil, errors.Errorf("Roa request missing pathPattern")
- }
- delete(params, "PathPattern")
- req.PathPattern = pathPattern
- req.Method = method
- req.GetHeaders()["Content-Type"] = "application/json"
- } else if strings.HasPrefix(domain, "rocketmq") {
- pathPattern, ok := params["PathPattern"]
- if !ok {
- return nil, errors.Errorf("Roa request missing pathPattern")
- }
- delete(params, "PathPattern")
- req.PathPattern = pathPattern
- req.Method = method
- req.GetHeaders()["Content-Type"] = "application/json"
- } else if strings.HasPrefix(domain, "fc") {
- pathPattern, ok := params["PathPattern"]
- if !ok {
- return nil, errors.Errorf("Roa request missing pathPattern")
- }
- delete(params, "PathPattern")
- req.PathPattern = fmt.Sprintf("/%s/%s", req.Version, strings.TrimPrefix(pathPattern, "/"))
- req.Method = method
- req.GetHeaders()["Content-Type"] = "application/json"
- } else if strings.HasPrefix(domain, "mongodb") {
- req.Method = requests.POST
- req.GetHeaders()["Content-Type"] = "application/json"
- }
- resp, err := processCommonRequest(client, req)
- if err != nil {
- return nil, errors.Wrapf(err, "processCommonRequest with params %s", params)
- }
- respBody, err := jsonutils.Parse(resp.GetHttpContentBytes())
- if err != nil {
- return nil, errors.Wrapf(err, "jsonutils.Parse")
- }
- //{"Code":"InvalidInstanceType.ValueNotSupported","HostId":"ecs.aliyuncs.com","Message":"The specified instanceType beyond the permitted range.","RequestId":"0042EE30-0EDF-48A7-A414-56229D4AD532"}
- //{"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}
- //{"Code":"Success","Data":{"CashCoupon":[]},"Message":"Successful!","RequestId":"87AD7E9A-3F8F-460F-9934-FFFE502325EE","Success":true}
- if respBody.Contains("Code") {
- code, _ := respBody.GetString("Code")
- if len(code) > 0 && !utils.IsInStringArray(code, []string{"200", "Success"}) {
- return nil, fmt.Errorf("%s", respBody.String())
- }
- }
- return respBody, nil
- }
- func (self *SAliyunClient) getNasEndpoint(regionId string) string {
- err := self.fetchNasEndpoints()
- if err != nil {
- return "nas.aliyuncs.com"
- }
- ep, ok := self.nasEndpoints[regionId]
- if ok && len(ep) > 0 {
- return ep
- }
- return "nas.aliyuncs.com"
- }
- func (self *SAliyunClient) fetchNasEndpoints() error {
- if len(self.nasEndpoints) > 0 {
- return nil
- }
- client, err := self.getDefaultClient()
- if err != nil {
- return errors.Wrapf(err, "getDefaultClient")
- }
- resp, err := jsonRequest(client, "nas.aliyuncs.com", ALIYUN_NAS_API_VERSION, "DescribeRegions", nil, self.debug)
- if err != nil {
- return errors.Wrapf(err, "DescribeRegions")
- }
- regions := []SRegion{}
- err = resp.Unmarshal(®ions, "Regions", "Region")
- if err != nil {
- return errors.Wrapf(err, "resp.Unmarshal")
- }
- for _, region := range regions {
- self.nasEndpoints[region.RegionId] = region.RegionEndpoint
- }
- return nil
- }
- func (self *SAliyunClient) getDefaultClient() (*sdk.Client, error) {
- client, err := self.getSdkClient(ALIYUN_DEFAULT_REGION)
- return client, err
- }
- func (self *SAliyunClient) getVpcEndpoint(regionId string) string {
- err := self.fetchVpcEndpoints()
- if err != nil {
- return "vpc.aliyuncs.com"
- }
- ep, ok := self.vpcEndpoints[regionId]
- if ok && len(ep) > 0 {
- return ep
- }
- return "vpc.aliyuncs.com"
- }
- func (self *SAliyunClient) fetchVpcEndpoints() error {
- if len(self.vpcEndpoints) > 0 {
- return nil
- }
- client, err := self.getDefaultClient()
- if err != nil {
- return errors.Wrapf(err, "getDefaultClient")
- }
- resp, err := jsonRequest(client, "vpc.aliyuncs.com", ALIYUN_API_VERSION_VPC, "DescribeRegions", nil, self.debug)
- if err != nil {
- return errors.Wrapf(err, "DescribeRegions")
- }
- regions := []SRegion{}
- err = resp.Unmarshal(®ions, "Regions", "Region")
- if err != nil {
- return errors.Wrapf(err, "resp.Unmarshal")
- }
- for _, region := range regions {
- self.vpcEndpoints[region.RegionId] = region.RegionEndpoint
- }
- return nil
- }
- func (self *SAliyunClient) _getSdkClient(regionId string) (*sdk.Client, error) {
- transport := httputils.GetAdaptiveTransport(true)
- transport.Proxy = self.cpcfg.ProxyFunc
- ts := cloudprovider.GetCheckTransport(transport, func(req *http.Request) (func(resp *http.Response) error, error) {
- params, err := url.ParseQuery(req.URL.RawQuery)
- if err != nil {
- return nil, errors.Wrapf(err, "ParseQuery(%s)", req.URL.RawQuery)
- }
- service := strings.Split(req.URL.Host, ".")[0]
- action := params.Get("Action")
- respCheck := func(resp *http.Response) error {
- if self.cpcfg.UpdatePermission != nil && resp.StatusCode >= 400 && resp.ContentLength > 0 {
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil
- }
- resp.Body = io.NopCloser(bytes.NewBuffer(body))
- obj, err := jsonutils.Parse(body)
- if err != nil {
- return nil
- }
- ret := struct{ Code string }{}
- obj.Unmarshal(&ret)
- if utils.IsInStringArray(ret.Code, []string{
- "NoPermission",
- "SubAccountNoPermission",
- }) || utils.HasPrefix(ret.Code, "Forbidden") ||
- action == "QueryAccountBalance" && ret.Code == "InternalError" {
- self.cpcfg.UpdatePermission(service, action)
- }
- }
- return nil
- }
- for _, prefix := range []string{"Get", "List", "Describe", "Query"} {
- if strings.HasPrefix(action, prefix) {
- return respCheck, nil
- }
- }
- if self.cpcfg.ReadOnly {
- return respCheck, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s", action)
- }
- return respCheck, nil
- })
- client, err := sdk.NewClientWithOptions(
- regionId,
- &sdk.Config{
- HttpTransport: transport,
- Transport: ts,
- },
- credentials.NewBaseCredential(self.accessKey, self.accessSecret),
- )
- if len(self.accountId) > 0 {
- arn := fmt.Sprintf("acs:ram::%s:role/%s", self.accountId, DefaultAssumeRoleName)
- client, err = sdk.NewClientWithOptions(
- regionId,
- &sdk.Config{
- HttpTransport: transport,
- Transport: ts,
- },
- credentials.NewRamRoleArnCredential(self.accessKey, self.accessSecret, arn, self.accountId, 0),
- )
- }
- if err != nil {
- return nil, errors.Wrapf(err, "NewClient")
- }
- return client, nil
- }
- func (self *SAliyunClient) getSdkClient(regionId string) (*sdk.Client, error) {
- self.clientLock.Lock()
- defer self.clientLock.Unlock()
- if gotypes.IsNil(self.clients) {
- self.clients = map[string]*sdk.Client{}
- }
- client, ok := self.clients[regionId]
- if ok {
- return client, nil
- }
- client, err := self._getSdkClient(regionId)
- if err != nil {
- return nil, errors.Wrapf(err, "NewClient")
- }
- self.clients[regionId] = client
- return client, nil
- }
- func (self *SAliyunClient) imsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- cli, err := self.getDefaultClient()
- if err != nil {
- return nil, err
- }
- params = self.SetResourceGropuId(params)
- return jsonRequest(cli, "ims.aliyuncs.com", ALIYUN_IMS_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) rmRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- cli, err := self._getSdkClient(ALIYUN_DEFAULT_REGION)
- if err != nil {
- return nil, err
- }
- return jsonRequest(cli, "resourcemanager.aliyuncs.com", ALIYUN_RM_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) ecsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- cli, err := self.getDefaultClient()
- if err != nil {
- return nil, err
- }
- params = self.SetResourceGropuId(params)
- return jsonRequest(cli, "ecs.aliyuncs.com", ALIYUN_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) pvtzRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- cli, err := self.getDefaultClient()
- if err != nil {
- return nil, err
- }
- params = self.SetResourceGropuId(params)
- return jsonRequest(cli, "pvtz.aliyuncs.com", ALIYUN_PVTZ_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) alidnsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- cli, err := self.getDefaultClient()
- if err != nil {
- return nil, err
- }
- params = self.SetResourceGropuId(params)
- return jsonRequest(cli, "alidns.aliyuncs.com", ALIYUN_ALIDNS_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) cbnRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- cli, err := self.getDefaultClient()
- if err != nil {
- return nil, err
- }
- params = self.SetResourceGropuId(params)
- return jsonRequest(cli, "cbn.aliyuncs.com", ALIYUN_CBN_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) cdnRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- cli, err := self.getDefaultClient()
- if err != nil {
- return nil, err
- }
- params = self.SetResourceGropuId(params)
- return jsonRequest(cli, "cdn.aliyuncs.com", ALIYUN_CDN_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) fetchRegions() error {
- if len(self.iregions) > 0 {
- return nil
- }
- body, err := self.ecsRequest("DescribeRegions", map[string]string{"AcceptLanguage": "zh-CN"})
- if err != nil {
- return errors.Wrapf(err, "DescribeRegions")
- }
- regions := make([]SRegion, 0)
- err = body.Unmarshal(®ions, "Regions", "Region")
- if err != nil {
- return errors.Wrapf(err, "resp.Unmarshal")
- }
- self.iregions = make([]cloudprovider.ICloudRegion, len(regions))
- for i := 0; i < len(regions); i += 1 {
- regions[i].client = self
- self.iregions[i] = ®ions[i]
- }
- return nil
- }
- // oss endpoint
- // https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.6.6E8ZkO
- func getOSSExternalDomain(regionId string) string {
- return fmt.Sprintf("oss-%s.aliyuncs.com", regionId)
- }
- func getOSSInternalDomain(regionId string) string {
- return fmt.Sprintf("oss-%s-internal.aliyuncs.com", regionId)
- }
- func (self *sCred) GetAccessKeyID() string {
- return self.AccessKeyId
- }
- func (self *sCred) GetAccessKeySecret() string {
- return self.AccessKeySecret
- }
- func (self *sCred) GetSecurityToken() string {
- return self.StsToken
- }
- type sCred struct {
- signers.SessionCredential
- }
- func (self *SAliyunClient) GetCredentials() oss.Credentials {
- ret := &sCred{}
- if len(self.accountId) == 0 {
- ret.AccessKeyId = self.accessKey
- ret.AccessKeySecret = self.accessSecret
- return ret
- }
- client, err := self.getDefaultClient()
- if err != nil {
- return ret
- }
- signer := client.GetSigner()
- arnSigner, ok := signer.(*signers.RamRoleArnSigner)
- if ok {
- ret.SessionCredential = *arnSigner.GetSessionCredential()
- }
- return ret
- }
- // https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.6.XqEgD1
- func (client *SAliyunClient) getOssClientByEndpoint(endpoint string) (*oss.Client, error) {
- // NOTE
- //
- // oss package as of version 20181116160301-c6838fdc33ed does not
- // respect http.ProxyFromEnvironment.
- //
- // The ClientOption Proxy, AuthProxy lacks the feature NO_PROXY has
- // which can be used to whitelist ips, domains from http_proxy,
- // https_proxy setting
- // oss use no timeout client so as to send/download large files
- httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
- transport, _ := httpClient.Transport.(*http.Transport)
- httpClient.Transport = cloudprovider.GetCheckTransport(transport, func(req *http.Request) (func(resp *http.Response) error, error) {
- path, method := req.URL.Path, req.Method
- respCheck := func(resp *http.Response) error {
- if client.cpcfg.UpdatePermission != nil && resp.StatusCode == 403 {
- client.cpcfg.UpdatePermission("oss", fmt.Sprintf("%s %s", method, path))
- }
- return nil
- }
- if client.cpcfg.ReadOnly {
- if req.Method == "GET" || req.Method == "HEAD" {
- return respCheck, nil
- }
- return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.RawPath)
- }
- return respCheck, nil
- })
- cliOpts := []oss.ClientOption{
- oss.HTTPClient(httpClient),
- oss.SetCredentialsProvider(client),
- }
- if !strings.HasPrefix(endpoint, "http") {
- endpoint = "https://" + endpoint
- }
- cli, err := oss.New(endpoint, client.accessKey, client.accessSecret, cliOpts...)
- if err != nil {
- return nil, errors.Wrap(err, "oss.New")
- }
- return cli, nil
- }
- func (client *SAliyunClient) getOssClient(regionId string) (*oss.Client, error) {
- ep := getOSSExternalDomain(regionId)
- return client.getOssClientByEndpoint(ep)
- }
- func (self *SAliyunClient) getRegionByRegionId(id string) (cloudprovider.ICloudRegion, error) {
- _id, ok := OSS_FINANCE_REGION_MAP[id]
- if ok {
- id = _id
- }
- for i := 0; i < len(self.iregions); i += 1 {
- if self.iregions[i].GetId() == id {
- return self.iregions[i], nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (self *SAliyunClient) invalidateIBuckets() {
- self.iBuckets = nil
- }
- func (self *SAliyunClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) {
- if self.iBuckets == nil {
- err := self.fetchBuckets()
- if err != nil {
- return nil, errors.Wrap(err, "fetchBuckets")
- }
- }
- return self.iBuckets, nil
- }
- func (self *SAliyunClient) fetchBuckets() error {
- osscli, err := self.getOssClient(ALIYUN_DEFAULT_REGION)
- if err != nil {
- return errors.Wrap(err, "self.getOssClient")
- }
- ret := make([]cloudprovider.ICloudBucket, 0)
- marker := ""
- for {
- opts := []oss.Option{oss.MaxKeys(100)}
- if len(marker) > 0 {
- opts = append(opts, oss.Marker(marker))
- }
- result, err := osscli.ListBuckets(opts...)
- if err != nil {
- return errors.Wrap(err, "oss.ListBuckets")
- }
- if len(self.ownerId) == 0 {
- self.ownerId = result.Owner.ID
- }
- for _, bInfo := range result.Buckets {
- regionId := bInfo.Location[4:]
- region, err := self.getRegionByRegionId(regionId)
- if err != nil {
- log.Errorf("cannot find bucket's region %s", regionId)
- continue
- }
- b := SBucket{
- region: region.(*SRegion),
- Name: bInfo.Name,
- Location: bInfo.Location,
- CreationDate: bInfo.CreationDate,
- StorageClass: bInfo.StorageClass,
- }
- ret = append(ret, &b)
- }
- if !result.IsTruncated {
- break
- }
- marker = result.NextMarker
- }
- self.iBuckets = ret
- return nil
- }
- func (self *SAliyunClient) GetRegions() []SRegion {
- regions := make([]SRegion, len(self.iregions))
- for i := 0; i < len(regions); i += 1 {
- region := self.iregions[i].(*SRegion)
- regions[i] = *region
- }
- return regions
- }
- func (self *SAliyunClient) getSubAccount() ([]cloudprovider.SSubAccount, error) {
- subAccount := cloudprovider.SSubAccount{}
- subAccount.Id = self.GetAccountId()
- subAccount.Name = self.cpcfg.Name
- subAccount.Account = self.accessKey
- subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
- return []cloudprovider.SSubAccount{subAccount}, nil
- }
- func (self *SAliyunClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
- err := self.fetchRegions()
- if err != nil {
- return nil, err
- }
- ret, err := self.getSubAccount()
- if err != nil {
- return nil, errors.Wrapf(err, "GetSubAccount")
- }
- accountId := self.GetAccountId()
- if strings.HasSuffix(self.arn, ":root") {
- return ret, nil
- }
- accounts, err := self.ListAccounts()
- if err != nil {
- if e, ok := errors.Cause(err).(*alierr.ServerError); ok && (e.ErrorCode() == "EntityNotExists.ResourceDirectory" || e.ErrorCode() == "NoPermission") {
- return ret, nil
- }
- return nil, errors.Wrapf(err, "ListAccounts")
- }
- for i := range accounts {
- account := cloudprovider.SSubAccount{}
- account.Name = fmt.Sprintf("%s/%s", accounts[i].DisplayName, self.cpcfg.Name)
- account.Account = self.accessKey
- account.Id = accountId
- account.HealthStatus = api.CLOUD_PROVIDER_HEALTH_SUSPENDED
- if strings.HasSuffix(accounts[i].Status, "Success") {
- account.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
- }
- if accounts[i].AccountId != accountId {
- account.Name = fmt.Sprintf("%s/%s", accounts[i].DisplayName, accounts[i].AccountId)
- account.Account = fmt.Sprintf("%s/%s", self.accessKey, accounts[i].AccountId)
- account.Id = accounts[i].AccountId
- }
- ret = append(ret, account)
- }
- return ret, nil
- }
- func (self *SAliyunClient) GetAccountId() string {
- if len(self.ownerId) > 0 {
- return self.ownerId
- }
- caller, err := self.GetCallerIdentity()
- if err != nil {
- log.Errorf("GetCallerIdentity fail %s", err)
- return ""
- }
- self.ownerId = caller.AccountId
- self.arn = caller.Arn
- return self.ownerId
- }
- func (self *SAliyunClient) GetIRegions() ([]cloudprovider.ICloudRegion, error) {
- return self.iregions, nil
- }
- func (self *SAliyunClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
- for i := 0; i < len(self.iregions); i += 1 {
- if self.iregions[i].GetGlobalId() == id {
- return self.iregions[i], nil
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (self *SAliyunClient) GetRegion(regionId string) *SRegion {
- if len(regionId) == 0 {
- regionId = ALIYUN_DEFAULT_REGION
- }
- for i := 0; i < len(self.iregions); i += 1 {
- if self.iregions[i].GetId() == regionId {
- return self.iregions[i].(*SRegion)
- }
- }
- return nil
- }
- func (self *SAliyunClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
- for i := 0; i < len(self.iregions); i += 1 {
- ihost, err := self.iregions[i].GetIHostById(id)
- if err == nil {
- return ihost, nil
- } else if errors.Cause(err) != cloudprovider.ErrNotFound {
- return nil, err
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (self *SAliyunClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
- for i := 0; i < len(self.iregions); i += 1 {
- ihost, err := self.iregions[i].GetIVpcById(id)
- if err == nil {
- return ihost, nil
- } else if errors.Cause(err) != cloudprovider.ErrNotFound {
- return nil, err
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (self *SAliyunClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
- for i := 0; i < len(self.iregions); i += 1 {
- ihost, err := self.iregions[i].GetIStorageById(id)
- if err == nil {
- return ihost, nil
- } else if errors.Cause(err) != cloudprovider.ErrNotFound {
- return nil, err
- }
- }
- return nil, cloudprovider.ErrNotFound
- }
- func (self *SAliyunClient) GetProjects() ([]SResourceGroup, error) {
- pageSize, pageNumber := 50, 1
- resourceGroups := []SResourceGroup{}
- for {
- parts, total, err := self.GetResourceGroups(pageNumber, pageSize)
- if err != nil {
- return nil, errors.Wrap(err, "GetResourceGroups")
- }
- resourceGroups = append(resourceGroups, parts...)
- if len(resourceGroups) >= total {
- break
- }
- pageNumber += 1
- }
- return resourceGroups, nil
- }
- func (self *SAliyunClient) SetResourceGropuId(params map[string]string) map[string]string {
- if params == nil {
- params = map[string]string{}
- }
- for _, groupId := range self.cpcfg.AliyunResourceGroupIds {
- if utils.IsInStringArray(groupId, self.GetResourceGroupIds()) {
- params["ResourceGroupId"] = groupId
- }
- }
- return params
- }
- func (self *SAliyunClient) GetResourceGroupIds() []string {
- ret := []string{}
- resourceGroups, err := self.GetProjects()
- if err != nil {
- return ret
- }
- for i := range resourceGroups {
- ret = append(ret, resourceGroups[i].Id)
- }
- return ret
- }
- func (self *SAliyunClient) GetIProjects() ([]cloudprovider.ICloudProject, error) {
- ret := []cloudprovider.ICloudProject{}
- resourceGroups, err := self.GetProjects()
- if err != nil {
- return nil, err
- }
- for i := range resourceGroups {
- resourceGroups[i].client = self
- ret = append(ret, &resourceGroups[i])
- }
- return ret, nil
- }
- func (self *SAliyunClient) scRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
- client, err := self.getSdkClient(ALIYUN_DEFAULT_REGION)
- if err != nil {
- return nil, err
- }
- domain := "cas.aliyuncs.com"
- return jsonRequest(client, domain, ALIYUN_CAS_API_VERSION, apiName, params, self.debug)
- }
- func (self *SAliyunClient) GetISSLCertificates() ([]cloudprovider.ICloudSSLCertificate, error) {
- ret := make([]SSSLCertificate, 0)
- for {
- part, total, err := self.GetSSLCertificates(100, len(ret)/100+1)
- if err != nil {
- return nil, errors.Wrapf(err, "GetSSLCertificates")
- }
- ret = append(ret, part...)
- if len(ret) >= total {
- break
- }
- }
- result := make([]cloudprovider.ICloudSSLCertificate, 0)
- for i := range ret {
- if !ret[i].BuyInAliyun {
- continue
- }
- ret[i].client = self
- result = append(result, &ret[i])
- }
- return result, nil
- }
- func (self *SAliyunClient) GetISSLCertificate(certId string) (cloudprovider.ICloudSSLCertificate, error) {
- var res cloudprovider.ICloudSSLCertificate
- res, err := self.GetSSLCertificate(certId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetSSLCertificate")
- }
- return res, nil
- }
- func (region *SAliyunClient) GetCapabilities() []string {
- caps := []string{
- cloudprovider.CLOUD_CAPABILITY_PROJECT,
- cloudprovider.CLOUD_CAPABILITY_COMPUTE,
- cloudprovider.CLOUD_CAPABILITY_NETWORK,
- cloudprovider.CLOUD_CAPABILITY_SECURITY_GROUP,
- cloudprovider.CLOUD_CAPABILITY_EIP,
- cloudprovider.CLOUD_CAPABILITY_LOADBALANCER,
- cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE,
- cloudprovider.CLOUD_CAPABILITY_RDS,
- cloudprovider.CLOUD_CAPABILITY_CACHE,
- cloudprovider.CLOUD_CAPABILITY_EVENT,
- cloudprovider.CLOUD_CAPABILITY_CLOUDID,
- cloudprovider.CLOUD_CAPABILITY_DNSZONE,
- cloudprovider.CLOUD_CAPABILITY_INTERVPCNETWORK,
- cloudprovider.CLOUD_CAPABILITY_SAML_AUTH,
- cloudprovider.CLOUD_CAPABILITY_NAT,
- cloudprovider.CLOUD_CAPABILITY_NAS,
- cloudprovider.CLOUD_CAPABILITY_WAF,
- cloudprovider.CLOUD_CAPABILITY_QUOTA + cloudprovider.READ_ONLY_SUFFIX,
- cloudprovider.CLOUD_CAPABILITY_MONGO_DB + cloudprovider.READ_ONLY_SUFFIX,
- cloudprovider.CLOUD_CAPABILITY_ES + cloudprovider.READ_ONLY_SUFFIX,
- cloudprovider.CLOUD_CAPABILITY_KAFKA + cloudprovider.READ_ONLY_SUFFIX,
- cloudprovider.CLOUD_CAPABILITY_CDN + cloudprovider.READ_ONLY_SUFFIX,
- cloudprovider.CLOUD_CAPABILITY_CONTAINER + cloudprovider.READ_ONLY_SUFFIX,
- cloudprovider.CLOUD_CAPABILITY_TABLESTORE + cloudprovider.READ_ONLY_SUFFIX,
- cloudprovider.CLOUD_CAPABILITY_CERT,
- cloudprovider.CLOUD_CAPABILITY_SNAPSHOT_POLICY,
- }
- return caps
- }
- func (self *SAliyunClient) GetAccessEnv() string {
- switch self.cloudEnv {
- case ALIYUN_INTERNATIONAL_CLOUDENV:
- return api.CLOUD_ACCESS_ENV_ALIYUN_GLOBAL
- case ALIYUN_FINANCE_CLOUDENV:
- return api.CLOUD_ACCESS_ENV_ALIYUN_FINANCE
- default:
- return api.CLOUD_ACCESS_ENV_ALIYUN_GLOBAL
- }
- }
|