Merge pull request #22342 from xiaozhu36/master

backend/oss: added support for profile config
This commit is contained in:
James Bardin 2019-08-23 19:36:54 -04:00 committed by GitHub
commit e89bed6751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 177 additions and 18 deletions

View File

@ -2,6 +2,7 @@ package oss
import (
"context"
"encoding/json"
"fmt"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
@ -9,7 +10,9 @@ import (
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"io/ioutil"
"os"
"runtime"
"strings"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
@ -134,6 +137,17 @@ func New() backend.Backend {
},
"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,
Optional: true,
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": {
Type: schema.TypeString,
@ -171,7 +185,6 @@ func assumeRoleSchema() *schema.Schema {
Optional: true,
Description: "The time after which the established session for assuming role expires.",
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.acl = d.Get("acl").(string)
accessKey := d.Get("access_key").(string)
secretKey := d.Get("secret_key").(string)
securityToken := d.Get("security_token").(string)
region := d.Get("region").(string)
var getBackendConfig = func(str string, key string) string {
if str == "" {
value, err := getConfigFromProfile(d, key)
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)
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 {
for _, v := range v.(*schema.Set).List() {
assumeRole := v.(map[string]interface{})
roleArn := assumeRole["role_arn"].(string)
sessionName := assumeRole["session_name"].(string)
policy := assumeRole["policy"].(string)
sessionExpiration := assumeRole["session_expiration"].(int)
subAccessKeyId, subAccessKeySecret, subSecurityToken, err := getAssumeRoleAK(accessKey, secretKey, region, roleArn, sessionName, policy, sessionExpiration)
if err != nil {
return err
if assumeRole["role_arn"].(string) != "" {
roleArn = assumeRole["role_arn"].(string)
}
if assumeRole["session_name"].(string) != "" {
sessionName = assumeRole["session_name"].(string)
}
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 == "" {
endpointItem, _ := b.getOSSEndpointByRegion(accessKey, secretKey, securityToken, region)
if endpointItem != nil && len(endpointItem.Endpoint) > 0 {
@ -382,3 +435,71 @@ func (a *Invoker) Run(f func() error) error {
}
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
}

View File

@ -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) {
testACC(t)
cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{

View File

@ -90,9 +90,12 @@ The following configuration options or environment variables are supported:
* `acl` - (Optional) [Object
ACL](https://www.alibabacloud.com/help/doc-detail/52284.htm)
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:
* `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.

View File

@ -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.
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,
DNSimple, CloudFlare).

View File

@ -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)
- [Azure](/docs/providers/azurerm/index.html)
- [Azure Stack](/docs/providers/azurestack/index.html)