github.com/hugorut/terraform@v1.1.3/src/backend/remote-state/oss/backend.go (about)

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