github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/backend/remote-state/oss/backend.go (about)

     1  package oss
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"net/http"
    10  	"net/url"
    11  	"os"
    12  	"regexp"
    13  	"runtime"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints"
    19  
    20  	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
    21  	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
    22  	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
    23  	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
    24  	"github.com/aliyun/alibaba-cloud-sdk-go/services/location"
    25  	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
    26  	"github.com/aliyun/aliyun-oss-go-sdk/oss"
    27  	"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
    28  	"github.com/hashicorp/go-cleanhttp"
    29  	"github.com/jmespath/go-jmespath"
    30  	"github.com/mitchellh/go-homedir"
    31  
    32  	"github.com/hashicorp/terraform/internal/backend"
    33  	"github.com/hashicorp/terraform/internal/legacy/helper/schema"
    34  	"github.com/hashicorp/terraform/version"
    35  )
    36  
    37  // Deprecated in favor of flattening assume_role_* options
    38  func deprecatedAssumeRoleSchema() *schema.Schema {
    39  	return &schema.Schema{
    40  		Type:       schema.TypeSet,
    41  		Optional:   true,
    42  		MaxItems:   1,
    43  		Deprecated: "use assume_role_* options instead",
    44  		Elem: &schema.Resource{
    45  			Schema: map[string]*schema.Schema{
    46  				"role_arn": {
    47  					Type:        schema.TypeString,
    48  					Required:    true,
    49  					Description: "The ARN of a RAM role to assume prior to making API calls.",
    50  					DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_ARN", ""),
    51  				},
    52  				"session_name": {
    53  					Type:        schema.TypeString,
    54  					Optional:    true,
    55  					Description: "The session name to use when assuming the role.",
    56  					DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_NAME", ""),
    57  				},
    58  				"policy": {
    59  					Type:        schema.TypeString,
    60  					Optional:    true,
    61  					Description: "The permissions applied when assuming a role. You cannot use this policy to grant permissions which exceed those of the role that is being assumed.",
    62  				},
    63  				"session_expiration": {
    64  					Type:        schema.TypeInt,
    65  					Optional:    true,
    66  					Description: "The time after which the established session for assuming role expires.",
    67  					ValidateFunc: func(v interface{}, k string) ([]string, []error) {
    68  						min := 900
    69  						max := 3600
    70  						value, ok := v.(int)
    71  						if !ok {
    72  							return nil, []error{fmt.Errorf("expected type of %s to be int", k)}
    73  						}
    74  
    75  						if value < min || value > max {
    76  							return nil, []error{fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v)}
    77  						}
    78  
    79  						return nil, nil
    80  					},
    81  				},
    82  			},
    83  		},
    84  	}
    85  }
    86  
    87  // New creates a new backend for OSS remote state.
    88  func New() backend.Backend {
    89  	s := &schema.Backend{
    90  		Schema: map[string]*schema.Schema{
    91  			"access_key": {
    92  				Type:        schema.TypeString,
    93  				Optional:    true,
    94  				Description: "Alibaba Cloud Access Key ID",
    95  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ACCESS_KEY", os.Getenv("ALICLOUD_ACCESS_KEY_ID")),
    96  			},
    97  
    98  			"secret_key": {
    99  				Type:        schema.TypeString,
   100  				Optional:    true,
   101  				Description: "Alibaba Cloud Access Secret Key",
   102  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECRET_KEY", os.Getenv("ALICLOUD_ACCESS_KEY_SECRET")),
   103  			},
   104  
   105  			"security_token": {
   106  				Type:        schema.TypeString,
   107  				Optional:    true,
   108  				Description: "Alibaba Cloud Security Token",
   109  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECURITY_TOKEN", ""),
   110  			},
   111  
   112  			"ecs_role_name": {
   113  				Type:        schema.TypeString,
   114  				Optional:    true,
   115  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ECS_ROLE_NAME", os.Getenv("ALICLOUD_ECS_ROLE_NAME")),
   116  				Description: "The RAM Role Name attached on a ECS instance for API operations. You can retrieve this from the 'Access Control' section of the Alibaba Cloud console.",
   117  			},
   118  
   119  			"region": {
   120  				Type:        schema.TypeString,
   121  				Optional:    true,
   122  				Description: "The region of the OSS bucket.",
   123  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", os.Getenv("ALICLOUD_DEFAULT_REGION")),
   124  			},
   125  			"sts_endpoint": {
   126  				Type:        schema.TypeString,
   127  				Optional:    true,
   128  				Description: "A custom endpoint for the STS API",
   129  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_STS_ENDPOINT", ""),
   130  			},
   131  			"tablestore_endpoint": {
   132  				Type:        schema.TypeString,
   133  				Optional:    true,
   134  				Description: "A custom endpoint for the TableStore API",
   135  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_TABLESTORE_ENDPOINT", ""),
   136  			},
   137  			"endpoint": {
   138  				Type:        schema.TypeString,
   139  				Optional:    true,
   140  				Description: "A custom endpoint for the OSS API",
   141  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_OSS_ENDPOINT", os.Getenv("OSS_ENDPOINT")),
   142  			},
   143  
   144  			"bucket": {
   145  				Type:        schema.TypeString,
   146  				Required:    true,
   147  				Description: "The name of the OSS bucket",
   148  			},
   149  
   150  			"prefix": {
   151  				Type:        schema.TypeString,
   152  				Optional:    true,
   153  				Description: "The directory where state files will be saved inside the bucket",
   154  				Default:     "env:",
   155  				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
   156  					prefix := v.(string)
   157  					if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") {
   158  						return nil, []error{fmt.Errorf("workspace_key_prefix must not start with '/' or './'")}
   159  					}
   160  					return nil, nil
   161  				},
   162  			},
   163  
   164  			"key": {
   165  				Type:        schema.TypeString,
   166  				Optional:    true,
   167  				Description: "The path of the state file inside the bucket",
   168  				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
   169  					if strings.HasPrefix(v.(string), "/") || strings.HasSuffix(v.(string), "/") {
   170  						return nil, []error{fmt.Errorf("key can not start and end with '/'")}
   171  					}
   172  					return nil, nil
   173  				},
   174  				Default: "terraform.tfstate",
   175  			},
   176  
   177  			"tablestore_table": {
   178  				Type:        schema.TypeString,
   179  				Optional:    true,
   180  				Description: "TableStore table for state locking and consistency",
   181  				Default:     "",
   182  			},
   183  
   184  			"encrypt": {
   185  				Type:        schema.TypeBool,
   186  				Optional:    true,
   187  				Description: "Whether to enable server side encryption of the state file",
   188  				Default:     false,
   189  			},
   190  
   191  			"acl": {
   192  				Type:        schema.TypeString,
   193  				Optional:    true,
   194  				Description: "Object ACL to be applied to the state file",
   195  				Default:     "",
   196  				ValidateFunc: func(v interface{}, k string) ([]string, []error) {
   197  					if value := v.(string); value != "" {
   198  						acls := oss.ACLType(value)
   199  						if acls != oss.ACLPrivate && acls != oss.ACLPublicRead && acls != oss.ACLPublicReadWrite {
   200  							return nil, []error{fmt.Errorf(
   201  								"%q must be a valid ACL value , expected %s, %s or %s, got %q",
   202  								k, oss.ACLPrivate, oss.ACLPublicRead, oss.ACLPublicReadWrite, acls)}
   203  						}
   204  					}
   205  					return nil, nil
   206  				},
   207  			},
   208  			"shared_credentials_file": {
   209  				Type:        schema.TypeString,
   210  				Optional:    true,
   211  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SHARED_CREDENTIALS_FILE", ""),
   212  				Description: "This is the path to the shared credentials file. If this is not set and a profile is specified, `~/.aliyun/config.json` will be used.",
   213  			},
   214  			"profile": {
   215  				Type:        schema.TypeString,
   216  				Optional:    true,
   217  				Description: "This is the Alibaba Cloud profile name as set in the shared credentials file. It can also be sourced from the `ALICLOUD_PROFILE` environment variable.",
   218  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_PROFILE", ""),
   219  			},
   220  			"assume_role": deprecatedAssumeRoleSchema(),
   221  			"assume_role_role_arn": {
   222  				Type:        schema.TypeString,
   223  				Optional:    true,
   224  				Description: "The ARN of a RAM role to assume prior to making API calls.",
   225  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_ARN", ""),
   226  			},
   227  			"assume_role_session_name": {
   228  				Type:        schema.TypeString,
   229  				Optional:    true,
   230  				Description: "The session name to use when assuming the role.",
   231  				DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_NAME", ""),
   232  			},
   233  			"assume_role_policy": {
   234  				Type:        schema.TypeString,
   235  				Optional:    true,
   236  				Description: "The permissions applied when assuming a role. You cannot use this policy to grant permissions which exceed those of the role that is being assumed.",
   237  			},
   238  			"assume_role_session_expiration": {
   239  				Type:        schema.TypeInt,
   240  				Optional:    true,
   241  				Description: "The time after which the established session for assuming role expires.",
   242  				ValidateFunc: func(v interface{}, k string) ([]string, []error) {
   243  					min := 900
   244  					max := 3600
   245  					value, ok := v.(int)
   246  					if !ok {
   247  						return nil, []error{fmt.Errorf("expected type of %s to be int", k)}
   248  					}
   249  
   250  					if value < min || value > max {
   251  						return nil, []error{fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v)}
   252  					}
   253  
   254  					return nil, nil
   255  				},
   256  			},
   257  		},
   258  	}
   259  
   260  	result := &Backend{Backend: s}
   261  	result.Backend.ConfigureFunc = result.configure
   262  	return result
   263  }
   264  
   265  type Backend struct {
   266  	*schema.Backend
   267  
   268  	// The fields below are set from configure
   269  	ossClient *oss.Client
   270  	otsClient *tablestore.TableStoreClient
   271  
   272  	bucketName           string
   273  	statePrefix          string
   274  	stateKey             string
   275  	serverSideEncryption bool
   276  	acl                  string
   277  	otsEndpoint          string
   278  	otsTable             string
   279  }
   280  
   281  func (b *Backend) configure(ctx context.Context) error {
   282  	if b.ossClient != nil {
   283  		return nil
   284  	}
   285  
   286  	// Grab the resource data
   287  	d := schema.FromContextBackendConfig(ctx)
   288  
   289  	b.bucketName = d.Get("bucket").(string)
   290  	b.statePrefix = strings.TrimPrefix(strings.Trim(d.Get("prefix").(string), "/"), "./")
   291  	b.stateKey = d.Get("key").(string)
   292  	b.serverSideEncryption = d.Get("encrypt").(bool)
   293  	b.acl = d.Get("acl").(string)
   294  
   295  	var getBackendConfig = func(str string, key string) string {
   296  		if str == "" {
   297  			value, err := getConfigFromProfile(d, key)
   298  			if err == nil && value != nil {
   299  				str = value.(string)
   300  			}
   301  		}
   302  		return str
   303  	}
   304  
   305  	accessKey := getBackendConfig(d.Get("access_key").(string), "access_key_id")
   306  	secretKey := getBackendConfig(d.Get("secret_key").(string), "access_key_secret")
   307  	securityToken := getBackendConfig(d.Get("security_token").(string), "sts_token")
   308  	region := getBackendConfig(d.Get("region").(string), "region_id")
   309  
   310  	stsEndpoint := d.Get("sts_endpoint").(string)
   311  	endpoint := d.Get("endpoint").(string)
   312  	schma := "https"
   313  
   314  	roleArn := getBackendConfig("", "ram_role_arn")
   315  	sessionName := getBackendConfig("", "ram_session_name")
   316  	var policy string
   317  	var sessionExpiration int
   318  	expiredSeconds, err := getConfigFromProfile(d, "expired_seconds")
   319  	if err == nil && expiredSeconds != nil {
   320  		sessionExpiration = (int)(expiredSeconds.(float64))
   321  	}
   322  
   323  	if v, ok := d.GetOk("assume_role_role_arn"); ok && v.(string) != "" {
   324  		roleArn = v.(string)
   325  		if v, ok := d.GetOk("assume_role_session_name"); ok {
   326  			sessionName = v.(string)
   327  		}
   328  		if v, ok := d.GetOk("assume_role_policy"); ok {
   329  			policy = v.(string)
   330  		}
   331  		if v, ok := d.GetOk("assume_role_session_expiration"); ok {
   332  			sessionExpiration = v.(int)
   333  		}
   334  	} else if v, ok := d.GetOk("assume_role"); ok {
   335  		// deprecated assume_role block
   336  		for _, v := range v.(*schema.Set).List() {
   337  			assumeRole := v.(map[string]interface{})
   338  			if assumeRole["role_arn"].(string) != "" {
   339  				roleArn = assumeRole["role_arn"].(string)
   340  			}
   341  			if assumeRole["session_name"].(string) != "" {
   342  				sessionName = assumeRole["session_name"].(string)
   343  			}
   344  			policy = assumeRole["policy"].(string)
   345  			sessionExpiration = assumeRole["session_expiration"].(int)
   346  		}
   347  	}
   348  
   349  	if sessionName == "" {
   350  		sessionName = "terraform"
   351  	}
   352  	if sessionExpiration == 0 {
   353  		if v := os.Getenv("ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION"); v != "" {
   354  			if expiredSeconds, err := strconv.Atoi(v); err == nil {
   355  				sessionExpiration = expiredSeconds
   356  			}
   357  		}
   358  		if sessionExpiration == 0 {
   359  			sessionExpiration = 3600
   360  		}
   361  	}
   362  
   363  	if accessKey == "" {
   364  		ecsRoleName := getBackendConfig(d.Get("ecs_role_name").(string), "ram_role_name")
   365  		subAccessKeyId, subAccessKeySecret, subSecurityToken, err := getAuthCredentialByEcsRoleName(ecsRoleName)
   366  		if err != nil {
   367  			return err
   368  		}
   369  		accessKey, secretKey, securityToken = subAccessKeyId, subAccessKeySecret, subSecurityToken
   370  	}
   371  
   372  	if roleArn != "" {
   373  		subAccessKeyId, subAccessKeySecret, subSecurityToken, err := getAssumeRoleAK(accessKey, secretKey, securityToken, region, roleArn, sessionName, policy, stsEndpoint, sessionExpiration)
   374  		if err != nil {
   375  			return err
   376  		}
   377  		accessKey, secretKey, securityToken = subAccessKeyId, subAccessKeySecret, subSecurityToken
   378  	}
   379  
   380  	if endpoint == "" {
   381  		endpointsResponse, err := b.getOSSEndpointByRegion(accessKey, secretKey, securityToken, region)
   382  		if err != nil {
   383  			log.Printf("[WARN] getting oss endpoint failed and using oss-%s.aliyuncs.com instead. Error: %#v.", region, err)
   384  		} else {
   385  			for _, endpointItem := range endpointsResponse.Endpoints.Endpoint {
   386  				if endpointItem.Type == "openAPI" {
   387  					endpoint = endpointItem.Endpoint
   388  					break
   389  				}
   390  			}
   391  		}
   392  		if endpoint == "" {
   393  			endpoint = fmt.Sprintf("oss-%s.aliyuncs.com", region)
   394  		}
   395  	}
   396  	if !strings.HasPrefix(endpoint, "http") {
   397  		endpoint = fmt.Sprintf("%s://%s", schma, endpoint)
   398  	}
   399  	log.Printf("[DEBUG] Instantiate OSS client using endpoint: %#v", endpoint)
   400  	var options []oss.ClientOption
   401  	if securityToken != "" {
   402  		options = append(options, oss.SecurityToken(securityToken))
   403  	}
   404  	options = append(options, oss.UserAgent(fmt.Sprintf("%s/%s", TerraformUA, TerraformVersion)))
   405  
   406  	proxyUrl := getHttpProxyUrl()
   407  	if proxyUrl != nil {
   408  		options = append(options, oss.Proxy(proxyUrl.String()))
   409  	}
   410  
   411  	client, err := oss.New(endpoint, accessKey, secretKey, options...)
   412  	b.ossClient = client
   413  	otsEndpoint := d.Get("tablestore_endpoint").(string)
   414  	if otsEndpoint != "" {
   415  		if !strings.HasPrefix(otsEndpoint, "http") {
   416  			otsEndpoint = fmt.Sprintf("%s://%s", schma, otsEndpoint)
   417  		}
   418  		b.otsEndpoint = otsEndpoint
   419  		parts := strings.Split(strings.TrimPrefix(strings.TrimPrefix(otsEndpoint, "https://"), "http://"), ".")
   420  		b.otsClient = tablestore.NewClientWithConfig(otsEndpoint, parts[0], accessKey, secretKey, securityToken, tablestore.NewDefaultTableStoreConfig())
   421  	}
   422  	b.otsTable = d.Get("tablestore_table").(string)
   423  
   424  	return err
   425  }
   426  
   427  func (b *Backend) getOSSEndpointByRegion(access_key, secret_key, security_token, region string) (*location.DescribeEndpointsResponse, error) {
   428  	args := location.CreateDescribeEndpointsRequest()
   429  	args.ServiceCode = "oss"
   430  	args.Id = region
   431  	args.Domain = "location-readonly.aliyuncs.com"
   432  
   433  	locationClient, err := location.NewClientWithOptions(region, getSdkConfig(), credentials.NewStsTokenCredential(access_key, secret_key, security_token))
   434  	if err != nil {
   435  		return nil, fmt.Errorf("unable to initialize the location client: %#v", err)
   436  
   437  	}
   438  	locationClient.AppendUserAgent(TerraformUA, TerraformVersion)
   439  	endpointsResponse, err := locationClient.DescribeEndpoints(args)
   440  	if err != nil {
   441  		return nil, fmt.Errorf("describe oss endpoint using region: %#v got an error: %#v", region, err)
   442  	}
   443  	return endpointsResponse, nil
   444  }
   445  
   446  func getAssumeRoleAK(accessKey, secretKey, stsToken, region, roleArn, sessionName, policy, stsEndpoint string, sessionExpiration int) (string, string, string, error) {
   447  	request := sts.CreateAssumeRoleRequest()
   448  	request.RoleArn = roleArn
   449  	request.RoleSessionName = sessionName
   450  	request.DurationSeconds = requests.NewInteger(sessionExpiration)
   451  	request.Policy = policy
   452  	request.Scheme = "https"
   453  
   454  	var client *sts.Client
   455  	var err error
   456  	if stsToken == "" {
   457  		client, err = sts.NewClientWithAccessKey(region, accessKey, secretKey)
   458  	} else {
   459  		client, err = sts.NewClientWithStsToken(region, accessKey, secretKey, stsToken)
   460  	}
   461  	if err != nil {
   462  		return "", "", "", err
   463  	}
   464  	if stsEndpoint != "" {
   465  		endpoints.AddEndpointMapping(region, "STS", stsEndpoint)
   466  	}
   467  	response, err := client.AssumeRole(request)
   468  	if err != nil {
   469  		return "", "", "", err
   470  	}
   471  	return response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken, nil
   472  }
   473  
   474  func getSdkConfig() *sdk.Config {
   475  	return sdk.NewConfig().
   476  		WithMaxRetryTime(5).
   477  		WithTimeout(time.Duration(30) * time.Second).
   478  		WithGoRoutinePoolSize(10).
   479  		WithDebug(false).
   480  		WithHttpTransport(getTransport()).
   481  		WithScheme("HTTPS")
   482  }
   483  
   484  func getTransport() *http.Transport {
   485  	handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout"))
   486  	if err != nil {
   487  		handshakeTimeout = 120
   488  	}
   489  	transport := cleanhttp.DefaultTransport()
   490  	transport.TLSHandshakeTimeout = time.Duration(handshakeTimeout) * time.Second
   491  	transport.Proxy = http.ProxyFromEnvironment
   492  	return transport
   493  }
   494  
   495  type Invoker struct {
   496  	catchers []*Catcher
   497  }
   498  
   499  type Catcher struct {
   500  	Reason           string
   501  	RetryCount       int
   502  	RetryWaitSeconds int
   503  }
   504  
   505  const TerraformUA = "HashiCorp-Terraform"
   506  
   507  var TerraformVersion = strings.TrimSuffix(version.String(), "-dev")
   508  var ClientErrorCatcher = Catcher{"AliyunGoClientFailure", 10, 3}
   509  var ServiceBusyCatcher = Catcher{"ServiceUnavailable", 10, 3}
   510  
   511  func NewInvoker() Invoker {
   512  	i := Invoker{}
   513  	i.AddCatcher(ClientErrorCatcher)
   514  	i.AddCatcher(ServiceBusyCatcher)
   515  	return i
   516  }
   517  
   518  func (a *Invoker) AddCatcher(catcher Catcher) {
   519  	a.catchers = append(a.catchers, &catcher)
   520  }
   521  
   522  func (a *Invoker) Run(f func() error) error {
   523  	err := f()
   524  
   525  	if err == nil {
   526  		return nil
   527  	}
   528  
   529  	for _, catcher := range a.catchers {
   530  		if strings.Contains(err.Error(), catcher.Reason) {
   531  			catcher.RetryCount--
   532  
   533  			if catcher.RetryCount <= 0 {
   534  				return fmt.Errorf("retry timeout and got an error: %#v", err)
   535  			} else {
   536  				time.Sleep(time.Duration(catcher.RetryWaitSeconds) * time.Second)
   537  				return a.Run(f)
   538  			}
   539  		}
   540  	}
   541  	return err
   542  }
   543  
   544  var providerConfig map[string]interface{}
   545  
   546  func getConfigFromProfile(d *schema.ResourceData, ProfileKey string) (interface{}, error) {
   547  
   548  	if providerConfig == nil {
   549  		if v, ok := d.GetOk("profile"); !ok || v.(string) == "" {
   550  			return nil, nil
   551  		}
   552  		current := d.Get("profile").(string)
   553  		// Set CredsFilename, expanding home directory
   554  		profilePath, err := homedir.Expand(d.Get("shared_credentials_file").(string))
   555  		if err != nil {
   556  			return nil, err
   557  		}
   558  		if profilePath == "" {
   559  			profilePath = fmt.Sprintf("%s/.aliyun/config.json", os.Getenv("HOME"))
   560  			if runtime.GOOS == "windows" {
   561  				profilePath = fmt.Sprintf("%s/.aliyun/config.json", os.Getenv("USERPROFILE"))
   562  			}
   563  		}
   564  		providerConfig = make(map[string]interface{})
   565  		_, err = os.Stat(profilePath)
   566  		if !os.IsNotExist(err) {
   567  			data, err := ioutil.ReadFile(profilePath)
   568  			if err != nil {
   569  				return nil, err
   570  			}
   571  			config := map[string]interface{}{}
   572  			err = json.Unmarshal(data, &config)
   573  			if err != nil {
   574  				return nil, err
   575  			}
   576  			for _, v := range config["profiles"].([]interface{}) {
   577  				if current == v.(map[string]interface{})["name"] {
   578  					providerConfig = v.(map[string]interface{})
   579  				}
   580  			}
   581  		}
   582  	}
   583  
   584  	mode := ""
   585  	if v, ok := providerConfig["mode"]; ok {
   586  		mode = v.(string)
   587  	} else {
   588  		return v, nil
   589  	}
   590  	switch ProfileKey {
   591  	case "access_key_id", "access_key_secret":
   592  		if mode == "EcsRamRole" {
   593  			return "", nil
   594  		}
   595  	case "ram_role_name":
   596  		if mode != "EcsRamRole" {
   597  			return "", nil
   598  		}
   599  	case "sts_token":
   600  		if mode != "StsToken" {
   601  			return "", nil
   602  		}
   603  	case "ram_role_arn", "ram_session_name":
   604  		if mode != "RamRoleArn" {
   605  			return "", nil
   606  		}
   607  	case "expired_seconds":
   608  		if mode != "RamRoleArn" {
   609  			return float64(0), nil
   610  		}
   611  	}
   612  
   613  	return providerConfig[ProfileKey], nil
   614  }
   615  
   616  var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"
   617  
   618  // getAuthCredentialByEcsRoleName aims to access meta to get sts credential
   619  // Actually, the job should be done by sdk, but currently not all resources and products support alibaba-cloud-sdk-go,
   620  // and their go sdk does support ecs role name.
   621  // This method is a temporary solution and it should be removed after all go sdk support ecs role name
   622  // The related PR: https://github.com/terraform-providers/terraform-provider-alicloud/pull/731
   623  func getAuthCredentialByEcsRoleName(ecsRoleName string) (accessKey, secretKey, token string, err error) {
   624  
   625  	if ecsRoleName == "" {
   626  		return
   627  	}
   628  	requestUrl := securityCredURL + ecsRoleName
   629  	httpRequest, err := http.NewRequest(requests.GET, requestUrl, strings.NewReader(""))
   630  	if err != nil {
   631  		err = fmt.Errorf("build sts requests err: %s", err.Error())
   632  		return
   633  	}
   634  	httpClient := &http.Client{}
   635  	httpResponse, err := httpClient.Do(httpRequest)
   636  	if err != nil {
   637  		err = fmt.Errorf("get Ecs sts token err : %s", err.Error())
   638  		return
   639  	}
   640  
   641  	response := responses.NewCommonResponse()
   642  	err = responses.Unmarshal(response, httpResponse, "")
   643  	if err != nil {
   644  		err = fmt.Errorf("unmarshal Ecs sts token response err : %s", err.Error())
   645  		return
   646  	}
   647  
   648  	if response.GetHttpStatus() != http.StatusOK {
   649  		err = fmt.Errorf("get Ecs sts token err, httpStatus: %d, message = %s", response.GetHttpStatus(), response.GetHttpContentString())
   650  		return
   651  	}
   652  	var data interface{}
   653  	err = json.Unmarshal(response.GetHttpContentBytes(), &data)
   654  	if err != nil {
   655  		err = fmt.Errorf("refresh Ecs sts token err, json.Unmarshal fail: %s", err.Error())
   656  		return
   657  	}
   658  	code, err := jmespath.Search("Code", data)
   659  	if err != nil {
   660  		err = fmt.Errorf("refresh Ecs sts token err, fail to get Code: %s", err.Error())
   661  		return
   662  	}
   663  	if code.(string) != "Success" {
   664  		err = fmt.Errorf("refresh Ecs sts token err, Code is not Success")
   665  		return
   666  	}
   667  	accessKeyId, err := jmespath.Search("AccessKeyId", data)
   668  	if err != nil {
   669  		err = fmt.Errorf("refresh Ecs sts token err, fail to get AccessKeyId: %s", err.Error())
   670  		return
   671  	}
   672  	accessKeySecret, err := jmespath.Search("AccessKeySecret", data)
   673  	if err != nil {
   674  		err = fmt.Errorf("refresh Ecs sts token err, fail to get AccessKeySecret: %s", err.Error())
   675  		return
   676  	}
   677  	securityToken, err := jmespath.Search("SecurityToken", data)
   678  	if err != nil {
   679  		err = fmt.Errorf("refresh Ecs sts token err, fail to get SecurityToken: %s", err.Error())
   680  		return
   681  	}
   682  
   683  	if accessKeyId == nil || accessKeySecret == nil || securityToken == nil {
   684  		err = fmt.Errorf("there is no any available accesskey, secret and security token for Ecs role %s", ecsRoleName)
   685  		return
   686  	}
   687  
   688  	return accessKeyId.(string), accessKeySecret.(string), securityToken.(string), nil
   689  }
   690  
   691  func getHttpProxyUrl() *url.URL {
   692  	for _, v := range []string{"HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"} {
   693  		value := strings.Trim(os.Getenv(v), " ")
   694  		if value != "" {
   695  			if !regexp.MustCompile(`^http(s)?://`).MatchString(value) {
   696  				value = fmt.Sprintf("https://%s", value)
   697  			}
   698  			proxyUrl, err := url.Parse(value)
   699  			if err == nil {
   700  				return proxyUrl
   701  			}
   702  			break
   703  		}
   704  	}
   705  	return nil
   706  }