yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aws/aws.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 aws
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"net/http"
    22  	"net/url"
    23  	"strings"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/aws/aws-sdk-go/aws"
    28  	sdk "github.com/aws/aws-sdk-go/aws"
    29  	"github.com/aws/aws-sdk-go/aws/awserr"
    30  	"github.com/aws/aws-sdk-go/aws/credentials"
    31  	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
    32  	"github.com/aws/aws-sdk-go/aws/session"
    33  	"github.com/aws/aws-sdk-go/service/ec2"
    34  	"github.com/aws/aws-sdk-go/service/elasticache"
    35  	"github.com/aws/aws-sdk-go/service/s3"
    36  
    37  	"yunion.io/x/log"
    38  	"yunion.io/x/pkg/errors"
    39  
    40  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    41  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    42  	"yunion.io/x/onecloud/pkg/httperrors"
    43  )
    44  
    45  const (
    46  	CLOUD_PROVIDER_AWS    = api.CLOUD_PROVIDER_AWS
    47  	CLOUD_PROVIDER_AWS_CN = "AWS"
    48  	CLOUD_PROVIDER_AWS_EN = "AWS"
    49  
    50  	AWS_INTERNATIONAL_CLOUDENV = "InternationalCloud"
    51  	AWS_CHINA_CLOUDENV         = "ChinaCloud"
    52  
    53  	AWS_INTERNATIONAL_DEFAULT_REGION = "us-west-1"
    54  	AWS_CHINA_DEFAULT_REGION         = "cn-north-1"
    55  	AWS_API_VERSION                  = "2018-10-10"
    56  
    57  	AWS_GLOBAL_ARN_PREFIX = "arn:aws:iam::aws:policy/"
    58  	AWS_CHINA_ARN_PREFIX  = "arn:aws-cn:iam::aws:policy/"
    59  
    60  	DEFAULT_S3_REGION_ID = "us-east-1"
    61  
    62  	DefaultAssumeRoleName = "OrganizationAccountAccessRole"
    63  )
    64  
    65  var (
    66  	DEBUG = false
    67  )
    68  
    69  type AwsClientConfig struct {
    70  	cpcfg cloudprovider.ProviderConfig
    71  
    72  	accessUrl    string // 服务区域 ChinaCloud | InternationalCloud
    73  	accessKey    string
    74  	accessSecret string
    75  	accountId    string
    76  
    77  	debug bool
    78  
    79  	assumeRoleName string
    80  }
    81  
    82  func NewAwsClientConfig(accessUrl, accessKey, accessSecret, accountId string) *AwsClientConfig {
    83  	cfg := &AwsClientConfig{
    84  		accessUrl:    accessUrl,
    85  		accessKey:    accessKey,
    86  		accessSecret: accessSecret,
    87  		accountId:    accountId,
    88  	}
    89  	return cfg
    90  }
    91  
    92  func (cfg *AwsClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *AwsClientConfig {
    93  	cfg.cpcfg = cpcfg
    94  	return cfg
    95  }
    96  
    97  func (cfg *AwsClientConfig) Debug(debug bool) *AwsClientConfig {
    98  	cfg.debug = debug
    99  	DEBUG = debug
   100  	return cfg
   101  }
   102  
   103  func (cfg *AwsClientConfig) SetAssumeRole(roleName string) *AwsClientConfig {
   104  	cfg.assumeRoleName = roleName
   105  	return cfg
   106  }
   107  
   108  func (cfg *AwsClientConfig) getAssumeRoleName() string {
   109  	if len(cfg.assumeRoleName) > 0 {
   110  		return cfg.assumeRoleName
   111  	}
   112  	return DefaultAssumeRoleName
   113  }
   114  
   115  type SAwsClient struct {
   116  	*AwsClientConfig
   117  
   118  	ownerId   string
   119  	ownerName string
   120  
   121  	iregions []cloudprovider.ICloudRegion
   122  	iBuckets []cloudprovider.ICloudBucket
   123  
   124  	//sessions map[string]map[bool]*session.Session
   125  	sessions sync.Map // map[string]map[bool]*session.Session
   126  }
   127  
   128  func NewAwsClient(cfg *AwsClientConfig) (*SAwsClient, error) {
   129  	client := SAwsClient{
   130  		AwsClientConfig: cfg,
   131  	}
   132  	_, err := client.fetchRegions()
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	err = client.fetchOwnerId()
   137  	if err != nil {
   138  		return nil, errors.Wrap(err, "fetchOwnerId")
   139  	}
   140  	if client.debug {
   141  		log.Debugf("ownerId: %s ownerName: %s", client.ownerId, client.ownerName)
   142  	}
   143  	return &client, nil
   144  }
   145  
   146  func (cli *SAwsClient) getIamArn(arn string) string {
   147  	switch cli.GetAccessEnv() {
   148  	case api.CLOUD_ACCESS_ENV_AWS_GLOBAL:
   149  		return AWS_GLOBAL_ARN_PREFIX + arn
   150  	default:
   151  		return AWS_CHINA_ARN_PREFIX + arn
   152  	}
   153  }
   154  
   155  func (cli *SAwsClient) getIamCommonArn(arn string) string {
   156  	switch cli.GetAccessEnv() {
   157  	case api.CLOUD_ACCESS_ENV_AWS_GLOBAL:
   158  		return strings.TrimPrefix(arn, AWS_GLOBAL_ARN_PREFIX)
   159  	default:
   160  		return strings.TrimPrefix(arn, AWS_CHINA_ARN_PREFIX)
   161  	}
   162  }
   163  
   164  func GetDefaultRegionId(accessUrl string) string {
   165  	defaultRegion := AWS_INTERNATIONAL_DEFAULT_REGION
   166  	switch accessUrl {
   167  	case AWS_INTERNATIONAL_CLOUDENV:
   168  		defaultRegion = AWS_INTERNATIONAL_DEFAULT_REGION
   169  	case AWS_CHINA_CLOUDENV:
   170  		defaultRegion = AWS_CHINA_DEFAULT_REGION
   171  	}
   172  
   173  	return defaultRegion
   174  }
   175  
   176  func (self *SAwsClient) getDefaultRegionId() string {
   177  	return GetDefaultRegionId(self.accessUrl)
   178  }
   179  
   180  func (client *SAwsClient) getDefaultSession(assumeRole bool) (*session.Session, error) {
   181  	return client.getAwsSession(client.getDefaultRegionId(), assumeRole)
   182  }
   183  
   184  func (client *SAwsClient) GetAccountId() string {
   185  	return client.ownerId
   186  }
   187  
   188  var (
   189  	// cache for describeRegions
   190  	describeRegionResult        sync.Map //map[string]*ec2.DescribeRegionsOutput = map[string]*ec2.DescribeRegionsOutput{}
   191  	describeRegionResultCacheAt sync.Map // map[string]time.Time                  = map[string]time.Time{}
   192  )
   193  
   194  const (
   195  	describeRegionExpireHours = 2
   196  )
   197  
   198  // 用于初始化region信息
   199  func (self *SAwsClient) fetchRegions() ([]SRegion, error) {
   200  	cacheTime, _ := describeRegionResultCacheAt.Load(self.accessUrl)
   201  	if _, ok := describeRegionResult.Load(self.accessUrl); !ok || cacheTime.(time.Time).IsZero() || time.Now().After(cacheTime.(time.Time).Add(time.Hour*describeRegionExpireHours)) {
   202  		s, err := self.getDefaultSession(false)
   203  		if err != nil {
   204  			return nil, errors.Wrap(err, "getDefaultSession")
   205  		}
   206  		svc := ec2.New(s)
   207  		// https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#EC2.DescribeRegions
   208  		input := &ec2.DescribeRegionsInput{}
   209  		input.SetAllRegions(true)
   210  		result, err := svc.DescribeRegions(input)
   211  		if err != nil {
   212  			if e, ok := err.(awserr.Error); ok && e.Code() == "AuthFailure" {
   213  				return nil, errors.Wrap(httperrors.ErrInvalidAccessKey, err.Error())
   214  			}
   215  			return nil, errors.Wrap(err, "DescribeRegions")
   216  		}
   217  		describeRegionResult.Store(self.accessUrl, result)
   218  		describeRegionResultCacheAt.Store(self.accessUrl, time.Now())
   219  	}
   220  
   221  	self.iregions = []cloudprovider.ICloudRegion{}
   222  	regions := make([]SRegion, 0)
   223  	descRegions, _ := describeRegionResult.Load(self.accessUrl)
   224  	for _, region := range descRegions.(*ec2.DescribeRegionsOutput).Regions {
   225  		name := *region.RegionName
   226  		endpoint := *region.Endpoint
   227  		sregion := SRegion{client: self, RegionId: name, RegionEndpoint: endpoint}
   228  		// 初始化region client
   229  		// sregion.getEc2Client()
   230  		regions = append(regions, sregion)
   231  		self.iregions = append(self.iregions, &sregion)
   232  	}
   233  
   234  	return regions, nil
   235  }
   236  
   237  func (client *SAwsClient) getAwsSession(regionId string, assumeRole bool) (*session.Session, error) {
   238  	client.sessions.LoadOrStore(regionId, map[bool]*session.Session{})
   239  	if sess, ok := client.sessions.Load(regionId); ok {
   240  		if ret, ok := sess.(map[bool]*session.Session)[assumeRole]; ok {
   241  			return ret, nil
   242  		}
   243  	}
   244  	httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
   245  	transport, _ := httpClient.Transport.(*http.Transport)
   246  	httpClient.Transport = cloudprovider.GetCheckTransport(transport, func(req *http.Request) (func(resp *http.Response), error) {
   247  		var action string
   248  		if req.ContentLength > 0 {
   249  			body, err := ioutil.ReadAll(req.Body)
   250  			if err != nil {
   251  				return nil, errors.Wrapf(err, "ioutil.ReadAll")
   252  			}
   253  			req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
   254  			params, err := url.ParseQuery(string(body))
   255  			if err != nil {
   256  				return nil, errors.Wrapf(err, "ParseQuery(%s)", string(body))
   257  			}
   258  			action = params.Get("Action")
   259  		}
   260  
   261  		service := strings.Split(req.URL.Host, ".")[0]
   262  		method, path := req.Method, req.URL.Path
   263  		respCheck := func(resp *http.Response) {
   264  			if resp.StatusCode == 403 {
   265  				if client.cpcfg.UpdatePermission != nil {
   266  					if len(action) > 0 {
   267  						client.cpcfg.UpdatePermission(service, action)
   268  					} else { // s3
   269  						client.cpcfg.UpdatePermission(service, fmt.Sprintf("%s %s", method, path))
   270  					}
   271  				}
   272  			}
   273  		}
   274  
   275  		if client.cpcfg.ReadOnly {
   276  			if len(action) > 0 {
   277  				for _, prefix := range []string{"Get", "List", "Describe"} {
   278  					if strings.HasPrefix(action, prefix) {
   279  						return respCheck, nil
   280  					}
   281  				}
   282  				return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, action)
   283  			}
   284  			// s3
   285  			if req.Method == "GET" || req.Method == "HEAD" {
   286  				return respCheck, nil
   287  			}
   288  			return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
   289  		}
   290  		return respCheck, nil
   291  	})
   292  	s, err := session.NewSession(&sdk.Config{
   293  		Region: sdk.String(regionId),
   294  		Credentials: credentials.NewStaticCredentials(
   295  			client.accessKey, client.accessSecret, "",
   296  		),
   297  		HTTPClient:                    httpClient,
   298  		DisableParamValidation:        sdk.Bool(true),
   299  		CredentialsChainVerboseErrors: sdk.Bool(true),
   300  	})
   301  	if err != nil {
   302  		return nil, errors.Wrap(err, "getAwsSession.NewSession")
   303  	}
   304  	if assumeRole && len(client.accountId) > 0 {
   305  		// need to assumeRole
   306  		var env string
   307  		switch client.GetAccessEnv() {
   308  		case api.CLOUD_ACCESS_ENV_AWS_GLOBAL:
   309  			env = "aws"
   310  		default:
   311  			env = "aws-cn"
   312  		}
   313  		roleARN := fmt.Sprintf("arn:%s:iam::%s:role/%s", env, client.accountId, client.getAssumeRoleName())
   314  		creds := stscreds.NewCredentials(s, roleARN)
   315  		s = s.Copy(&aws.Config{Credentials: creds})
   316  	}
   317  	if client.debug {
   318  		logLevel := aws.LogLevelType(uint(aws.LogDebugWithRequestErrors) + uint(aws.LogDebugWithHTTPBody) + uint(aws.LogDebugWithSigning))
   319  		s.Config.LogLevel = &logLevel
   320  	}
   321  
   322  	client.sessions.Store(regionId, map[bool]*session.Session{assumeRole: s})
   323  	return s, nil
   324  }
   325  
   326  func (region *SRegion) getAwsElasticacheClient() (*elasticache.ElastiCache, error) {
   327  	session, err := region.getAwsSession()
   328  	if err != nil {
   329  		return nil, errors.Wrap(err, "client.getDefaultSession")
   330  	}
   331  	session.ClientConfig(ELASTICACHE_SERVICE_NAME)
   332  	return elasticache.New(session), nil
   333  }
   334  
   335  func (client *SAwsClient) getAwsRoute53Session() (*session.Session, error) {
   336  	session, err := client.getDefaultSession(true)
   337  	if err != nil {
   338  		return nil, errors.Wrap(err, "client.getDefaultSession()")
   339  	}
   340  	session.ClientConfig(ROUTE53_SERVICE_NAME)
   341  	return session, nil
   342  }
   343  
   344  func (self *SAwsClient) invalidateIBuckets() {
   345  	self.iBuckets = nil
   346  }
   347  
   348  func (self *SAwsClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) {
   349  	if self.iBuckets == nil {
   350  		err := self.fetchBuckets()
   351  		if err != nil {
   352  			return nil, errors.Wrap(err, "fetchBuckets")
   353  		}
   354  	}
   355  	return self.iBuckets, nil
   356  }
   357  
   358  func (client *SAwsClient) fetchOwnerId() error {
   359  	ident, err := client.GetCallerIdentity()
   360  	if err != nil {
   361  		return errors.Wrap(err, "GetCallerIdentity")
   362  	}
   363  	client.ownerId = ident.Account
   364  	return nil
   365  }
   366  
   367  func (client *SAwsClient) fetchBuckets() error {
   368  	s, err := client.getDefaultSession(true)
   369  	if err != nil {
   370  		return errors.Wrap(err, "getDefaultSession")
   371  	}
   372  	s3cli := s3.New(s)
   373  	output, err := s3cli.ListBuckets(&s3.ListBucketsInput{})
   374  	if err != nil {
   375  		return errors.Wrap(err, "ListBuckets")
   376  	}
   377  
   378  	ret := make([]cloudprovider.ICloudBucket, 0)
   379  	for _, bInfo := range output.Buckets {
   380  		if err := FillZero(bInfo); err != nil {
   381  			log.Errorf("s3cli.Binfo.FillZero error %s", err)
   382  			continue
   383  		}
   384  
   385  		input := &s3.GetBucketLocationInput{}
   386  		input.Bucket = bInfo.Name
   387  		output, err := s3cli.GetBucketLocation(input)
   388  		if err != nil {
   389  			log.Errorf("s3cli.GetBucketLocation error %s", err)
   390  			continue
   391  		}
   392  
   393  		if err := FillZero(output); err != nil {
   394  			log.Errorf("s3cli.GetBucketLocation.FillZero error %s", err)
   395  			continue
   396  		}
   397  
   398  		location := *output.LocationConstraint
   399  		if len(location) == 0 {
   400  			// https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html
   401  			// Buckets in Region us-east-1 have a LocationConstraint of null.
   402  			location = DEFAULT_S3_REGION_ID
   403  		}
   404  		region, err := client.getIRegionByRegionId(location)
   405  		if err != nil {
   406  			log.Errorf("client.getIRegionByRegionId %s fail %s", location, err)
   407  			continue
   408  		}
   409  		b := SBucket{
   410  			region:       region.(*SRegion),
   411  			Name:         *bInfo.Name,
   412  			Location:     location,
   413  			CreationDate: *bInfo.CreationDate,
   414  		}
   415  		ret = append(ret, &b)
   416  	}
   417  
   418  	client.iBuckets = ret
   419  
   420  	return nil
   421  }
   422  
   423  // 只是使用fetchRegions初始化好的self.iregions. 本身并不从云服务器厂商拉取region信息
   424  func (self *SAwsClient) GetRegions() ([]SRegion, error) {
   425  	return self.fetchRegions()
   426  }
   427  
   428  func (self *SAwsClient) GetIRegions() []cloudprovider.ICloudRegion {
   429  	return self.iregions
   430  }
   431  
   432  func (self *SAwsClient) GetRegion(regionId string) (*SRegion, error) {
   433  	regions, err := self.fetchRegions()
   434  	if err != nil {
   435  		return nil, errors.Wrapf(err, "fetchRegions")
   436  	}
   437  
   438  	if len(regionId) == 0 {
   439  		regionId = AWS_INTERNATIONAL_DEFAULT_REGION
   440  		switch self.accessUrl {
   441  		case AWS_INTERNATIONAL_CLOUDENV:
   442  			regionId = AWS_INTERNATIONAL_DEFAULT_REGION
   443  		case AWS_CHINA_CLOUDENV:
   444  			regionId = AWS_CHINA_DEFAULT_REGION
   445  		}
   446  	}
   447  	for i := 0; i < len(regions); i += 1 {
   448  		if regions[i].GetId() == regionId {
   449  			return &regions[i], nil
   450  		}
   451  	}
   452  	return nil, errors.Wrap(cloudprovider.ErrNotFound, regionId)
   453  }
   454  
   455  func (self *SAwsClient) getDefaultRegion() (*SRegion, error) {
   456  	return self.GetRegion("")
   457  }
   458  
   459  func (self *SAwsClient) getIRegionByRegionId(id string) (cloudprovider.ICloudRegion, error) {
   460  	for i := 0; i < len(self.iregions); i += 1 {
   461  		if self.iregions[i].GetId() == id {
   462  			return self.iregions[i], nil
   463  		}
   464  	}
   465  	return nil, errors.Wrap(cloudprovider.ErrNotFound, "getIRegionByRegionId")
   466  }
   467  
   468  func (self *SAwsClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
   469  	for i := 0; i < len(self.iregions); i += 1 {
   470  		if self.iregions[i].GetGlobalId() == id {
   471  			return self.iregions[i], nil
   472  		}
   473  	}
   474  	return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetIRegionById")
   475  }
   476  
   477  func (self *SAwsClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
   478  	for i := 0; i < len(self.iregions); i += 1 {
   479  		ihost, err := self.iregions[i].GetIHostById(id)
   480  		if err == nil {
   481  			return ihost, nil
   482  		} else if errors.Cause(err) != cloudprovider.ErrNotFound {
   483  			log.Errorf("GetIHostById %s: %s", id, err)
   484  			return nil, errors.Wrap(err, "GetIHostById")
   485  		}
   486  	}
   487  	return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetIHostById")
   488  }
   489  
   490  func (self *SAwsClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
   491  	for i := 0; i < len(self.iregions); i += 1 {
   492  		ihost, err := self.iregions[i].GetIVpcById(id)
   493  		if err == nil {
   494  			return ihost, nil
   495  		} else if errors.Cause(err) != cloudprovider.ErrNotFound {
   496  			log.Errorf("GetIVpcById %s: %s", id, err)
   497  			return nil, errors.Wrap(err, "GetIVpcById")
   498  		}
   499  	}
   500  	return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetIVpcById")
   501  }
   502  
   503  func (self *SAwsClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
   504  	for i := 0; i < len(self.iregions); i += 1 {
   505  		ihost, err := self.iregions[i].GetIStorageById(id)
   506  		if err == nil {
   507  			return ihost, nil
   508  		} else if errors.Cause(err) != cloudprovider.ErrNotFound {
   509  			log.Errorf("GetIStorageById %s: %s", id, err)
   510  			return nil, errors.Wrap(err, "GetIStorageById")
   511  		}
   512  	}
   513  	return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetIStorageById")
   514  }
   515  
   516  type SAccountBalance struct {
   517  	AvailableAmount     float64
   518  	AvailableCashAmount float64
   519  	CreditAmount        float64
   520  	MybankCreditAmount  float64
   521  	Currency            string
   522  }
   523  
   524  func (self *SAwsClient) QueryAccountBalance() (*SAccountBalance, error) {
   525  	return nil, cloudprovider.ErrNotSupported
   526  }
   527  
   528  func (self *SAwsClient) GetIProjects() ([]cloudprovider.ICloudProject, error) {
   529  	return nil, cloudprovider.ErrNotImplemented
   530  }
   531  
   532  func (self *SAwsClient) GetAccessEnv() string {
   533  	switch self.accessUrl {
   534  	case AWS_INTERNATIONAL_CLOUDENV:
   535  		return api.CLOUD_ACCESS_ENV_AWS_GLOBAL
   536  	case AWS_CHINA_CLOUDENV:
   537  		return api.CLOUD_ACCESS_ENV_AWS_CHINA
   538  	default:
   539  		return api.CLOUD_ACCESS_ENV_AWS_GLOBAL
   540  	}
   541  }
   542  
   543  func (self *SAwsClient) iamRequest(apiName string, params map[string]string, retval interface{}) error {
   544  	return self.request("", IAM_SERVICE_NAME, IAM_SERVICE_ID, "2010-05-08", apiName, params, retval, true)
   545  }
   546  
   547  func (self *SAwsClient) stsRequest(apiName string, params map[string]string, retval interface{}) error {
   548  	return self.request("", STS_SERVICE_NAME, STS_SERVICE_ID, "2011-06-15", apiName, params, retval, false)
   549  }
   550  
   551  func (self *SAwsClient) GetCapabilities() []string {
   552  	caps := []string{
   553  		// cloudprovider.CLOUD_CAPABILITY_PROJECT,
   554  		cloudprovider.CLOUD_CAPABILITY_COMPUTE,
   555  		cloudprovider.CLOUD_CAPABILITY_NETWORK,
   556  		cloudprovider.CLOUD_CAPABILITY_EIP,
   557  		cloudprovider.CLOUD_CAPABILITY_LOADBALANCER,
   558  		cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE,
   559  		cloudprovider.CLOUD_CAPABILITY_RDS,
   560  		cloudprovider.CLOUD_CAPABILITY_CACHE + cloudprovider.READ_ONLY_SUFFIX,
   561  		cloudprovider.CLOUD_CAPABILITY_NAT + cloudprovider.READ_ONLY_SUFFIX,
   562  		cloudprovider.CLOUD_CAPABILITY_EVENT,
   563  		cloudprovider.CLOUD_CAPABILITY_CLOUDID,
   564  		cloudprovider.CLOUD_CAPABILITY_DNSZONE,
   565  		cloudprovider.CLOUD_CAPABILITY_SAML_AUTH,
   566  		cloudprovider.CLOUD_CAPABILITY_WAF,
   567  	}
   568  	return caps
   569  }
   570  
   571  func (client *SAwsClient) GetIamLoginUrl() string {
   572  	identity, err := client.GetCallerIdentity()
   573  	if err != nil {
   574  		log.Errorf("failed to get caller identity error: %v", err)
   575  		return ""
   576  	}
   577  
   578  	switch client.GetAccessEnv() {
   579  	case api.CLOUD_ACCESS_ENV_AWS_CHINA:
   580  		return fmt.Sprintf("https://%s.signin.amazonaws.cn/console/", identity.Account)
   581  	default:
   582  		return fmt.Sprintf("https://%s.signin.aws.amazon.com/console/", identity.Account)
   583  	}
   584  }
   585  
   586  func (client *SAwsClient) GetBucketCannedAcls() []string {
   587  	switch client.GetAccessEnv() {
   588  	case api.CLOUD_ACCESS_ENV_AWS_CHINA:
   589  		return []string{
   590  			string(cloudprovider.ACLPrivate),
   591  		}
   592  	default:
   593  		return []string{
   594  			string(cloudprovider.ACLPrivate),
   595  			string(cloudprovider.ACLAuthRead),
   596  			string(cloudprovider.ACLPublicRead),
   597  			string(cloudprovider.ACLPublicReadWrite),
   598  		}
   599  	}
   600  }
   601  
   602  func (client *SAwsClient) GetObjectCannedAcls() []string {
   603  	switch client.GetAccessEnv() {
   604  	case api.CLOUD_ACCESS_ENV_AWS_CHINA:
   605  		return []string{
   606  			string(cloudprovider.ACLPrivate),
   607  		}
   608  	default:
   609  		return []string{
   610  			string(cloudprovider.ACLPrivate),
   611  			string(cloudprovider.ACLAuthRead),
   612  			string(cloudprovider.ACLPublicRead),
   613  			string(cloudprovider.ACLPublicReadWrite),
   614  		}
   615  	}
   616  }
   617  
   618  func (client *SAwsClient) GetSamlEntityId() string {
   619  	switch client.accessUrl {
   620  	case AWS_CHINA_CLOUDENV:
   621  		return cloudprovider.SAML_ENTITY_ID_AWS_CN
   622  	default:
   623  		return cloudprovider.SAML_ENTITY_ID_AWS
   624  	}
   625  }