Merge pull request #22342 from xiaozhu36/master
backend/oss: added support for profile config
This commit is contained in:
commit
e89bed6751
|
@ -2,6 +2,7 @@ package oss
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
|
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
|
||||||
|
@ -9,7 +10,9 @@ import (
|
||||||
"github.com/hashicorp/terraform/backend"
|
"github.com/hashicorp/terraform/backend"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/helper/validation"
|
"github.com/hashicorp/terraform/helper/validation"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
|
||||||
|
@ -134,6 +137,17 @@ func New() backend.Backend {
|
||||||
},
|
},
|
||||||
|
|
||||||
"assume_role": assumeRoleSchema(),
|
"assume_role": assumeRoleSchema(),
|
||||||
|
"shared_credentials_file": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
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.",
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
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.",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_PROFILE", ""),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +173,7 @@ func assumeRoleSchema() *schema.Schema {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Description: "The session name to use when assuming the role.",
|
Description: "The session name to use when assuming the role.",
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_NAME", "terraform"),
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_NAME", ""),
|
||||||
},
|
},
|
||||||
"policy": {
|
"policy": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
|
@ -171,7 +185,6 @@ func assumeRoleSchema() *schema.Schema {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Description: "The time after which the established session for assuming role expires.",
|
Description: "The time after which the established session for assuming role expires.",
|
||||||
ValidateFunc: validation.IntBetween(900, 3600),
|
ValidateFunc: validation.IntBetween(900, 3600),
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION", 3600),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -209,28 +222,68 @@ func (b *Backend) configure(ctx context.Context) error {
|
||||||
b.serverSideEncryption = d.Get("encrypt").(bool)
|
b.serverSideEncryption = d.Get("encrypt").(bool)
|
||||||
b.acl = d.Get("acl").(string)
|
b.acl = d.Get("acl").(string)
|
||||||
|
|
||||||
accessKey := d.Get("access_key").(string)
|
var getBackendConfig = func(str string, key string) string {
|
||||||
secretKey := d.Get("secret_key").(string)
|
if str == "" {
|
||||||
securityToken := d.Get("security_token").(string)
|
value, err := getConfigFromProfile(d, key)
|
||||||
region := d.Get("region").(string)
|
if err == nil && value != nil {
|
||||||
|
str = value.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
accessKey := getBackendConfig(d.Get("access_key").(string), "access_key_id")
|
||||||
|
secretKey := getBackendConfig(d.Get("secret_key").(string), "access_key_secret")
|
||||||
|
securityToken := getBackendConfig(d.Get("security_token").(string), "sts_token")
|
||||||
|
region := getBackendConfig(d.Get("region").(string), "region_id")
|
||||||
|
|
||||||
endpoint := d.Get("endpoint").(string)
|
endpoint := d.Get("endpoint").(string)
|
||||||
schma := "https"
|
schma := "https"
|
||||||
|
|
||||||
|
roleArn := getBackendConfig("", "ram_role_arn")
|
||||||
|
sessionName := getBackendConfig("", "ram_session_name")
|
||||||
|
var policy string
|
||||||
|
var sessionExpiration int
|
||||||
|
expiredSeconds, err := getConfigFromProfile(d, "expired_seconds")
|
||||||
|
if err == nil && expiredSeconds != nil {
|
||||||
|
sessionExpiration = (int)(expiredSeconds.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
if v, ok := d.GetOk("assume_role"); ok {
|
if v, ok := d.GetOk("assume_role"); ok {
|
||||||
for _, v := range v.(*schema.Set).List() {
|
for _, v := range v.(*schema.Set).List() {
|
||||||
assumeRole := v.(map[string]interface{})
|
assumeRole := v.(map[string]interface{})
|
||||||
roleArn := assumeRole["role_arn"].(string)
|
if assumeRole["role_arn"].(string) != "" {
|
||||||
sessionName := assumeRole["session_name"].(string)
|
roleArn = assumeRole["role_arn"].(string)
|
||||||
policy := assumeRole["policy"].(string)
|
}
|
||||||
sessionExpiration := assumeRole["session_expiration"].(int)
|
if assumeRole["session_name"].(string) != "" {
|
||||||
subAccessKeyId, subAccessKeySecret, subSecurityToken, err := getAssumeRoleAK(accessKey, secretKey, region, roleArn, sessionName, policy, sessionExpiration)
|
sessionName = assumeRole["session_name"].(string)
|
||||||
if err != nil {
|
}
|
||||||
return err
|
if sessionName == "" {
|
||||||
|
sessionName = "terraform"
|
||||||
|
}
|
||||||
|
policy = assumeRole["policy"].(string)
|
||||||
|
sessionExpiration = assumeRole["session_expiration"].(int)
|
||||||
|
if sessionExpiration == 0 {
|
||||||
|
if v := os.Getenv("ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION"); v != "" {
|
||||||
|
if expiredSeconds, err := strconv.Atoi(v); err == nil {
|
||||||
|
sessionExpiration = expiredSeconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sessionExpiration == 0 {
|
||||||
|
sessionExpiration = 3600
|
||||||
|
}
|
||||||
}
|
}
|
||||||
accessKey, secretKey, securityToken = subAccessKeyId, subAccessKeySecret, subSecurityToken
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if roleArn != "" {
|
||||||
|
subAccessKeyId, subAccessKeySecret, subSecurityToken, err := getAssumeRoleAK(accessKey, secretKey, region, roleArn, sessionName, policy, sessionExpiration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accessKey, secretKey, securityToken = subAccessKeyId, subAccessKeySecret, subSecurityToken
|
||||||
|
}
|
||||||
|
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
endpointItem, _ := b.getOSSEndpointByRegion(accessKey, secretKey, securityToken, region)
|
endpointItem, _ := b.getOSSEndpointByRegion(accessKey, secretKey, securityToken, region)
|
||||||
if endpointItem != nil && len(endpointItem.Endpoint) > 0 {
|
if endpointItem != nil && len(endpointItem.Endpoint) > 0 {
|
||||||
|
@ -382,3 +435,71 @@ func (a *Invoker) Run(f func() error) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var providerConfig map[string]interface{}
|
||||||
|
|
||||||
|
func getConfigFromProfile(d *schema.ResourceData, ProfileKey string) (interface{}, error) {
|
||||||
|
|
||||||
|
if providerConfig == nil {
|
||||||
|
if v, ok := d.GetOk("profile"); !ok || v.(string) == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
current := d.Get("profile").(string)
|
||||||
|
profilePath := d.Get("shared_credentials_file").(string)
|
||||||
|
if profilePath == "" {
|
||||||
|
profilePath = fmt.Sprintf("%s/.aliyun/config.json", os.Getenv("HOME"))
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
profilePath = fmt.Sprintf("%s/.aliyun/config.json", os.Getenv("USERPROFILE"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
providerConfig = make(map[string]interface{})
|
||||||
|
_, err := os.Stat(profilePath)
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
data, err := ioutil.ReadFile(profilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config := map[string]interface{}{}
|
||||||
|
err = json.Unmarshal(data, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, v := range config["profiles"].([]interface{}) {
|
||||||
|
if current == v.(map[string]interface{})["name"] {
|
||||||
|
providerConfig = v.(map[string]interface{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := ""
|
||||||
|
if v, ok := providerConfig["mode"]; ok {
|
||||||
|
mode = v.(string)
|
||||||
|
} else {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
switch ProfileKey {
|
||||||
|
case "access_key_id", "access_key_secret":
|
||||||
|
if mode == "EcsRamRole" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
case "ram_role_name":
|
||||||
|
if mode != "EcsRamRole" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
case "sts_token":
|
||||||
|
if mode != "StsToken" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
case "ram_role_arn", "ram_session_name":
|
||||||
|
if mode != "RamRoleArn" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
case "expired_seconds":
|
||||||
|
if mode != "RamRoleArn" {
|
||||||
|
return float64(0), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerConfig[ProfileKey], nil
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,41 @@ func TestBackendConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBackendConfigProfile(t *testing.T) {
|
||||||
|
testACC(t)
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"region": "cn-beijing",
|
||||||
|
"bucket": "terraform-backend-oss-test",
|
||||||
|
"prefix": "mystate",
|
||||||
|
"key": "first.tfstate",
|
||||||
|
"tablestore_endpoint": "https://terraformstate.cn-beijing.ots.aliyuncs.com",
|
||||||
|
"tablestore_table": "TableStore",
|
||||||
|
"profile": "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
|
||||||
|
|
||||||
|
if !strings.HasPrefix(b.ossClient.Config.Endpoint, "https://oss-cn-beijing") {
|
||||||
|
t.Fatalf("Incorrect region was provided")
|
||||||
|
}
|
||||||
|
if b.bucketName != "terraform-backend-oss-test" {
|
||||||
|
t.Fatalf("Incorrect bucketName was provided")
|
||||||
|
}
|
||||||
|
if b.statePrefix != "mystate" {
|
||||||
|
t.Fatalf("Incorrect state file path was provided")
|
||||||
|
}
|
||||||
|
if b.stateKey != "first.tfstate" {
|
||||||
|
t.Fatalf("Incorrect keyName was provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.ossClient.Config.AccessKeyID == "" {
|
||||||
|
t.Fatalf("No Access Key Id was provided")
|
||||||
|
}
|
||||||
|
if b.ossClient.Config.AccessKeySecret == "" {
|
||||||
|
t.Fatalf("No Secret Access Key was provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBackendConfig_invalidKey(t *testing.T) {
|
func TestBackendConfig_invalidKey(t *testing.T) {
|
||||||
testACC(t)
|
testACC(t)
|
||||||
cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{
|
cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{
|
||||||
|
|
|
@ -90,9 +90,12 @@ The following configuration options or environment variables are supported:
|
||||||
* `acl` - (Optional) [Object
|
* `acl` - (Optional) [Object
|
||||||
ACL](https://www.alibabacloud.com/help/doc-detail/52284.htm)
|
ACL](https://www.alibabacloud.com/help/doc-detail/52284.htm)
|
||||||
to be applied to the state file.
|
to be applied to the state file.
|
||||||
* `assume_role` - (Optional) If provided with a role ARN, will attempt to assume this role using the supplied credentials.
|
* `shared_credentials_file` - (Optional, Available in 0.12.8+) 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.
|
||||||
|
* `profile` - (Optional, Available in 0.12.8+) 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.
|
||||||
|
* `assume_role` - (Optional, Available in 0.12.6+) If provided with a role ARN, will attempt to assume this role using the supplied credentials.
|
||||||
|
|
||||||
The nested `assume_role` block supports the following:
|
The nested `assume_role` block supports the following:
|
||||||
|
|
||||||
* `role_arn` - (Required) The ARN of the role to assume. If ARN is set to an empty string, it does not perform role switching. It supports environment variable `ALICLOUD_ASSUME_ROLE_ARN`.
|
* `role_arn` - (Required) The ARN of the role to assume. If ARN is set to an empty string, it does not perform role switching. It supports environment variable `ALICLOUD_ASSUME_ROLE_ARN`.
|
||||||
Terraform executes configuration on account with provided credentials.
|
Terraform executes configuration on account with provided credentials.
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ as physical machines, VMs, network switches, containers, and more. Almost any
|
||||||
infrastructure type can be represented as a resource in Terraform.
|
infrastructure type can be represented as a resource in Terraform.
|
||||||
|
|
||||||
A provider is responsible for understanding API interactions and exposing
|
A provider is responsible for understanding API interactions and exposing
|
||||||
resources. Providers generally are an IaaS (e.g. AWS, GCP, Microsoft Azure,
|
resources. Providers generally are an IaaS (e.g. Alibaba Cloud, AWS, GCP, Microsoft Azure,
|
||||||
OpenStack), PaaS (e.g. Heroku), or SaaS services (e.g. Terraform Cloud,
|
OpenStack), PaaS (e.g. Heroku), or SaaS services (e.g. Terraform Cloud,
|
||||||
DNSimple, CloudFlare).
|
DNSimple, CloudFlare).
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ tested by HashiCorp.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
- [AliCloud](/docs/providers/alicloud/index.html)
|
- [Alibaba Cloud](/docs/providers/alicloud/index.html)
|
||||||
- [AWS](/docs/providers/aws/index.html)
|
- [AWS](/docs/providers/aws/index.html)
|
||||||
- [Azure](/docs/providers/azurerm/index.html)
|
- [Azure](/docs/providers/azurerm/index.html)
|
||||||
- [Azure Stack](/docs/providers/azurestack/index.html)
|
- [Azure Stack](/docs/providers/azurestack/index.html)
|
||||||
|
|
Loading…
Reference in New Issue