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