Merge pull request #29307 from hayorov/main
Flatten `assume_role` block for OSS backend
This commit is contained in:
commit
2ebdc099ac
|
@ -24,155 +24,22 @@ import (
|
||||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
||||||
"github.com/hashicorp/go-cleanhttp"
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
|
"github.com/jmespath/go-jmespath"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/internal/backend"
|
"github.com/hashicorp/terraform/internal/backend"
|
||||||
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
"github.com/hashicorp/terraform/internal/legacy/helper/schema"
|
||||||
"github.com/hashicorp/terraform/version"
|
"github.com/hashicorp/terraform/version"
|
||||||
"github.com/jmespath/go-jmespath"
|
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates a new backend for OSS remote state.
|
// Deprecated in favor of flattening assume_role_* options
|
||||||
func New() backend.Backend {
|
func deprecatedAssumeRoleSchema() *schema.Schema {
|
||||||
s := &schema.Backend{
|
|
||||||
Schema: map[string]*schema.Schema{
|
|
||||||
"access_key": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Alibaba Cloud Access Key ID",
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ACCESS_KEY", os.Getenv("ALICLOUD_ACCESS_KEY_ID")),
|
|
||||||
},
|
|
||||||
|
|
||||||
"secret_key": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Alibaba Cloud Access Secret Key",
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECRET_KEY", os.Getenv("ALICLOUD_ACCESS_KEY_SECRET")),
|
|
||||||
},
|
|
||||||
|
|
||||||
"security_token": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Alibaba Cloud Security Token",
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECURITY_TOKEN", ""),
|
|
||||||
},
|
|
||||||
|
|
||||||
"ecs_role_name": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ECS_ROLE_NAME", os.Getenv("ALICLOUD_ECS_ROLE_NAME")),
|
|
||||||
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.",
|
|
||||||
},
|
|
||||||
|
|
||||||
"region": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "The region of the OSS bucket.",
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", os.Getenv("ALICLOUD_DEFAULT_REGION")),
|
|
||||||
},
|
|
||||||
"tablestore_endpoint": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "A custom endpoint for the TableStore API",
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_TABLESTORE_ENDPOINT", ""),
|
|
||||||
},
|
|
||||||
"endpoint": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "A custom endpoint for the OSS API",
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_OSS_ENDPOINT", os.Getenv("OSS_ENDPOINT")),
|
|
||||||
},
|
|
||||||
|
|
||||||
"bucket": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Required: true,
|
|
||||||
Description: "The name of the OSS bucket",
|
|
||||||
},
|
|
||||||
|
|
||||||
"prefix": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "The directory where state files will be saved inside the bucket",
|
|
||||||
Default: "env:",
|
|
||||||
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
|
|
||||||
prefix := v.(string)
|
|
||||||
if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") {
|
|
||||||
return nil, []error{fmt.Errorf("workspace_key_prefix must not start with '/' or './'")}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"key": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "The path of the state file inside the bucket",
|
|
||||||
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
|
|
||||||
if strings.HasPrefix(v.(string), "/") || strings.HasSuffix(v.(string), "/") {
|
|
||||||
return nil, []error{fmt.Errorf("key can not start and end with '/'")}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
},
|
|
||||||
Default: "terraform.tfstate",
|
|
||||||
},
|
|
||||||
|
|
||||||
"tablestore_table": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "TableStore table for state locking and consistency",
|
|
||||||
Default: "",
|
|
||||||
},
|
|
||||||
|
|
||||||
"encrypt": &schema.Schema{
|
|
||||||
Type: schema.TypeBool,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Whether to enable server side encryption of the state file",
|
|
||||||
Default: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
"acl": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Description: "Object ACL to be applied to the state file",
|
|
||||||
Default: "",
|
|
||||||
ValidateFunc: func(v interface{}, k string) ([]string, []error) {
|
|
||||||
if value := v.(string); value != "" {
|
|
||||||
acls := oss.ACLType(value)
|
|
||||||
if acls != oss.ACLPrivate && acls != oss.ACLPublicRead && acls != oss.ACLPublicReadWrite {
|
|
||||||
return nil, []error{fmt.Errorf(
|
|
||||||
"%q must be a valid ACL value , expected %s, %s or %s, got %q",
|
|
||||||
k, oss.ACLPrivate, oss.ACLPublicRead, oss.ACLPublicReadWrite, acls)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"assume_role": assumeRoleSchema(),
|
|
||||||
"shared_credentials_file": {
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SHARED_CREDENTIALS_FILE", ""),
|
|
||||||
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", ""),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &Backend{Backend: s}
|
|
||||||
result.Backend.ConfigureFunc = result.configure
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func assumeRoleSchema() *schema.Schema {
|
|
||||||
return &schema.Schema{
|
return &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
MaxItems: 1,
|
ConflictsWith: []string{"assume_role_role_arn", "assume_role_session_name", "assume_role_policy", "assume_role_session_expiration"},
|
||||||
|
MaxItems: 1,
|
||||||
|
Deprecated: "use assume_role_* options instead",
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"role_arn": {
|
"role_arn": {
|
||||||
|
@ -216,6 +83,182 @@ func assumeRoleSchema() *schema.Schema {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New creates a new backend for OSS remote state.
|
||||||
|
func New() backend.Backend {
|
||||||
|
s := &schema.Backend{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"access_key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "Alibaba Cloud Access Key ID",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ACCESS_KEY", os.Getenv("ALICLOUD_ACCESS_KEY_ID")),
|
||||||
|
},
|
||||||
|
|
||||||
|
"secret_key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "Alibaba Cloud Access Secret Key",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECRET_KEY", os.Getenv("ALICLOUD_ACCESS_KEY_SECRET")),
|
||||||
|
},
|
||||||
|
|
||||||
|
"security_token": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "Alibaba Cloud Security Token",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SECURITY_TOKEN", ""),
|
||||||
|
},
|
||||||
|
|
||||||
|
"ecs_role_name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ECS_ROLE_NAME", os.Getenv("ALICLOUD_ECS_ROLE_NAME")),
|
||||||
|
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.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"region": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "The region of the OSS bucket.",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", os.Getenv("ALICLOUD_DEFAULT_REGION")),
|
||||||
|
},
|
||||||
|
"tablestore_endpoint": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "A custom endpoint for the TableStore API",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_TABLESTORE_ENDPOINT", ""),
|
||||||
|
},
|
||||||
|
"endpoint": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "A custom endpoint for the OSS API",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_OSS_ENDPOINT", os.Getenv("OSS_ENDPOINT")),
|
||||||
|
},
|
||||||
|
|
||||||
|
"bucket": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
Description: "The name of the OSS bucket",
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefix": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "The directory where state files will be saved inside the bucket",
|
||||||
|
Default: "env:",
|
||||||
|
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
|
||||||
|
prefix := v.(string)
|
||||||
|
if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") {
|
||||||
|
return nil, []error{fmt.Errorf("workspace_key_prefix must not start with '/' or './'")}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "The path of the state file inside the bucket",
|
||||||
|
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
|
||||||
|
if strings.HasPrefix(v.(string), "/") || strings.HasSuffix(v.(string), "/") {
|
||||||
|
return nil, []error{fmt.Errorf("key can not start and end with '/'")}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
Default: "terraform.tfstate",
|
||||||
|
},
|
||||||
|
|
||||||
|
"tablestore_table": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "TableStore table for state locking and consistency",
|
||||||
|
Default: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
"encrypt": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Description: "Whether to enable server side encryption of the state file",
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"acl": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Description: "Object ACL to be applied to the state file",
|
||||||
|
Default: "",
|
||||||
|
ValidateFunc: func(v interface{}, k string) ([]string, []error) {
|
||||||
|
if value := v.(string); value != "" {
|
||||||
|
acls := oss.ACLType(value)
|
||||||
|
if acls != oss.ACLPrivate && acls != oss.ACLPublicRead && acls != oss.ACLPublicReadWrite {
|
||||||
|
return nil, []error{fmt.Errorf(
|
||||||
|
"%q must be a valid ACL value , expected %s, %s or %s, got %q",
|
||||||
|
k, oss.ACLPrivate, oss.ACLPublicRead, oss.ACLPublicReadWrite, acls)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"shared_credentials_file": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_SHARED_CREDENTIALS_FILE", ""),
|
||||||
|
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", ""),
|
||||||
|
},
|
||||||
|
"assume_role": deprecatedAssumeRoleSchema(),
|
||||||
|
"assume_role_role_arn": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"assume_role"},
|
||||||
|
Description: "The ARN of a RAM role to assume prior to making API calls.",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_ARN", ""),
|
||||||
|
},
|
||||||
|
"assume_role_session_name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"assume_role"},
|
||||||
|
Description: "The session name to use when assuming the role.",
|
||||||
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_NAME", ""),
|
||||||
|
},
|
||||||
|
"assume_role_policy": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"assume_role"},
|
||||||
|
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.",
|
||||||
|
},
|
||||||
|
"assume_role_session_expiration": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
ConflictsWith: []string{"assume_role"},
|
||||||
|
Description: "The time after which the established session for assuming role expires.",
|
||||||
|
ValidateFunc: func(v interface{}, k string) ([]string, []error) {
|
||||||
|
min := 900
|
||||||
|
max := 3600
|
||||||
|
value, ok := v.(int)
|
||||||
|
if !ok {
|
||||||
|
return nil, []error{fmt.Errorf("expected type of %s to be int", k)}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value < min || value > max {
|
||||||
|
return nil, []error{fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &Backend{Backend: s}
|
||||||
|
result.Backend.ConfigureFunc = result.configure
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
*schema.Backend
|
*schema.Backend
|
||||||
|
|
||||||
|
@ -228,7 +271,6 @@ type Backend struct {
|
||||||
stateKey string
|
stateKey string
|
||||||
serverSideEncryption bool
|
serverSideEncryption bool
|
||||||
acl string
|
acl string
|
||||||
endpoint string
|
|
||||||
otsEndpoint string
|
otsEndpoint string
|
||||||
otsTable string
|
otsTable string
|
||||||
}
|
}
|
||||||
|
@ -275,6 +317,7 @@ func (b *Backend) configure(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := d.GetOk("assume_role"); ok {
|
if v, ok := d.GetOk("assume_role"); ok {
|
||||||
|
// deprecated assume_role block
|
||||||
for _, v := range v.(*schema.Set).List() {
|
for _, v := range v.(*schema.Set).List() {
|
||||||
assumeRole := v.(map[string]interface{})
|
assumeRole := v.(map[string]interface{})
|
||||||
if assumeRole["role_arn"].(string) != "" {
|
if assumeRole["role_arn"].(string) != "" {
|
||||||
|
@ -283,22 +326,28 @@ func (b *Backend) configure(ctx context.Context) error {
|
||||||
if assumeRole["session_name"].(string) != "" {
|
if assumeRole["session_name"].(string) != "" {
|
||||||
sessionName = assumeRole["session_name"].(string)
|
sessionName = assumeRole["session_name"].(string)
|
||||||
}
|
}
|
||||||
if sessionName == "" {
|
|
||||||
sessionName = "terraform"
|
|
||||||
}
|
|
||||||
policy = assumeRole["policy"].(string)
|
policy = assumeRole["policy"].(string)
|
||||||
sessionExpiration = assumeRole["session_expiration"].(int)
|
sessionExpiration = assumeRole["session_expiration"].(int)
|
||||||
if sessionExpiration == 0 {
|
}
|
||||||
if v := os.Getenv("ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION"); v != "" {
|
} else {
|
||||||
if expiredSeconds, err := strconv.Atoi(v); err == nil {
|
roleArn = d.Get("assume_role_role_arn").(string)
|
||||||
sessionExpiration = expiredSeconds
|
sessionName = d.Get("assume_role_session_name").(string)
|
||||||
}
|
policy = d.Get("assume_role_policy").(string)
|
||||||
}
|
sessionExpiration = d.Get("assume_role_session_expiration").(int)
|
||||||
if sessionExpiration == 0 {
|
}
|
||||||
sessionExpiration = 3600
|
|
||||||
}
|
if sessionName == "" {
|
||||||
|
sessionName = "terraform"
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if accessKey == "" {
|
if accessKey == "" {
|
||||||
|
@ -372,13 +421,13 @@ func (b *Backend) getOSSEndpointByRegion(access_key, secret_key, security_token,
|
||||||
|
|
||||||
locationClient, err := location.NewClientWithOptions(region, getSdkConfig(), credentials.NewStsTokenCredential(access_key, secret_key, security_token))
|
locationClient, err := location.NewClientWithOptions(region, getSdkConfig(), credentials.NewStsTokenCredential(access_key, secret_key, security_token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Unable to initialize the location client: %#v", err)
|
return nil, fmt.Errorf("unable to initialize the location client: %#v", err)
|
||||||
|
|
||||||
}
|
}
|
||||||
locationClient.AppendUserAgent(TerraformUA, TerraformVersion)
|
locationClient.AppendUserAgent(TerraformUA, TerraformVersion)
|
||||||
endpointsResponse, err := locationClient.DescribeEndpoints(args)
|
endpointsResponse, err := locationClient.DescribeEndpoints(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Describe oss endpoint using region: %#v got an error: %#v.", region, err)
|
return nil, fmt.Errorf("describe oss endpoint using region: %#v got an error: %#v", region, err)
|
||||||
}
|
}
|
||||||
return endpointsResponse, nil
|
return endpointsResponse, nil
|
||||||
}
|
}
|
||||||
|
@ -468,7 +517,7 @@ func (a *Invoker) Run(f func() error) error {
|
||||||
catcher.RetryCount--
|
catcher.RetryCount--
|
||||||
|
|
||||||
if catcher.RetryCount <= 0 {
|
if catcher.RetryCount <= 0 {
|
||||||
return fmt.Errorf("Retry timeout and got an error: %#v.", err)
|
return fmt.Errorf("retry timeout and got an error: %#v", err)
|
||||||
} else {
|
} else {
|
||||||
time.Sleep(time.Duration(catcher.RetryWaitSeconds) * time.Second)
|
time.Sleep(time.Duration(catcher.RetryWaitSeconds) * time.Second)
|
||||||
return a.Run(f)
|
return a.Run(f)
|
||||||
|
@ -578,7 +627,7 @@ func getAuthCredentialByEcsRoleName(ecsRoleName string) (accessKey, secretKey, t
|
||||||
response := responses.NewCommonResponse()
|
response := responses.NewCommonResponse()
|
||||||
err = responses.Unmarshal(response, httpResponse, "")
|
err = responses.Unmarshal(response, httpResponse, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Unmarshal Ecs sts token response err : %s", err.Error())
|
err = fmt.Errorf("unmarshal Ecs sts token response err : %s", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,19 +3,18 @@ package oss
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
|
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/internal/backend"
|
"github.com/hashicorp/terraform/internal/backend"
|
||||||
"github.com/hashicorp/terraform/internal/states"
|
"github.com/hashicorp/terraform/internal/states"
|
||||||
"github.com/hashicorp/terraform/internal/states/remote"
|
"github.com/hashicorp/terraform/internal/states/remote"
|
||||||
"github.com/hashicorp/terraform/internal/states/statemgr"
|
"github.com/hashicorp/terraform/internal/states/statemgr"
|
||||||
|
|
||||||
"log"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -43,7 +42,7 @@ func (b *Backend) remoteClient(name string) (*RemoteClient, error) {
|
||||||
TableName: b.otsTable,
|
TableName: b.otsTable,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return client, fmt.Errorf("Error describing table store %s: %#v", b.otsTable, err)
|
return client, fmt.Errorf("error describing table store %s: %#v", b.otsTable, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ func (b *Backend) remoteClient(name string) (*RemoteClient, error) {
|
||||||
func (b *Backend) Workspaces() ([]string, error) {
|
func (b *Backend) Workspaces() ([]string, error) {
|
||||||
bucket, err := b.ossClient.Bucket(b.bucketName)
|
bucket, err := b.ossClient.Bucket(b.bucketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{""}, fmt.Errorf("Error getting bucket: %#v", err)
|
return []string{""}, fmt.Errorf("error getting bucket: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var options []oss.Option
|
var options []oss.Option
|
||||||
|
@ -135,7 +134,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
|
||||||
lockInfo.Operation = "init"
|
lockInfo.Operation = "init"
|
||||||
lockId, err := client.Lock(lockInfo)
|
lockId, err := client.Lock(lockInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to lock OSS state: %s", err)
|
return nil, fmt.Errorf("failed to lock OSS state: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local helper function so we can call it multiple places
|
// Local helper function so we can call it multiple places
|
||||||
|
|
|
@ -3,22 +3,21 @@ package oss
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"encoding/hex"
|
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
uuid "github.com/hashicorp/go-uuid"
|
uuid "github.com/hashicorp/go-uuid"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/internal/states/remote"
|
"github.com/hashicorp/terraform/internal/states/remote"
|
||||||
"github.com/hashicorp/terraform/internal/states/statemgr"
|
"github.com/hashicorp/terraform/internal/states/statemgr"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -48,8 +47,6 @@ type RemoteClient struct {
|
||||||
lockFile string
|
lockFile string
|
||||||
serverSideEncryption bool
|
serverSideEncryption bool
|
||||||
acl string
|
acl string
|
||||||
info *statemgr.LockInfo
|
|
||||||
mu sync.Mutex
|
|
||||||
otsTable string
|
otsTable string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +96,7 @@ func (c *RemoteClient) Get() (payload *remote.Payload, err error) {
|
||||||
func (c *RemoteClient) Put(data []byte) error {
|
func (c *RemoteClient) Put(data []byte) error {
|
||||||
bucket, err := c.ossClient.Bucket(c.bucketName)
|
bucket, err := c.ossClient.Bucket(c.bucketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting bucket: %#v", err)
|
return fmt.Errorf("error getting bucket: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := bytes.NewReader(data)
|
body := bytes.NewReader(data)
|
||||||
|
@ -116,7 +113,7 @@ func (c *RemoteClient) Put(data []byte) error {
|
||||||
|
|
||||||
if body != nil {
|
if body != nil {
|
||||||
if err := bucket.PutObject(c.stateFile, body, options...); err != nil {
|
if err := bucket.PutObject(c.stateFile, body, options...); err != nil {
|
||||||
return fmt.Errorf("Failed to upload state %s: %#v", c.stateFile, err)
|
return fmt.Errorf("failed to upload state %s: %#v", c.stateFile, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +121,7 @@ func (c *RemoteClient) Put(data []byte) error {
|
||||||
if err := c.putMD5(sum[:]); err != nil {
|
if err := c.putMD5(sum[:]); err != nil {
|
||||||
// if this errors out, we unfortunately have to error out altogether,
|
// if this errors out, we unfortunately have to error out altogether,
|
||||||
// since the next Get will inevitably fail.
|
// since the next Get will inevitably fail.
|
||||||
return fmt.Errorf("Failed to store state MD5: %s", err)
|
return fmt.Errorf("failed to store state MD5: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -132,13 +129,13 @@ func (c *RemoteClient) Put(data []byte) error {
|
||||||
func (c *RemoteClient) Delete() error {
|
func (c *RemoteClient) Delete() error {
|
||||||
bucket, err := c.ossClient.Bucket(c.bucketName)
|
bucket, err := c.ossClient.Bucket(c.bucketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting bucket %s: %#v", c.bucketName, err)
|
return fmt.Errorf("error getting bucket %s: %#v", c.bucketName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Deleting remote state from OSS: %#v", c.stateFile)
|
log.Printf("[DEBUG] Deleting remote state from OSS: %#v", c.stateFile)
|
||||||
|
|
||||||
if err := bucket.DeleteObject(c.stateFile); err != nil {
|
if err := bucket.DeleteObject(c.stateFile); err != nil {
|
||||||
return fmt.Errorf("Error deleting state %s: %#v", c.stateFile, err)
|
return fmt.Errorf("error deleting state %s: %#v", c.stateFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.deleteMD5(); err != nil {
|
if err := c.deleteMD5(); err != nil {
|
||||||
|
@ -413,11 +410,11 @@ func (c *RemoteClient) lockPath() string {
|
||||||
func (c *RemoteClient) getObj() (*remote.Payload, error) {
|
func (c *RemoteClient) getObj() (*remote.Payload, error) {
|
||||||
bucket, err := c.ossClient.Bucket(c.bucketName)
|
bucket, err := c.ossClient.Bucket(c.bucketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error getting bucket %s: %#v", c.bucketName, err)
|
return nil, fmt.Errorf("error getting bucket %s: %#v", c.bucketName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if exist, err := bucket.IsObjectExist(c.stateFile); err != nil {
|
if exist, err := bucket.IsObjectExist(c.stateFile); err != nil {
|
||||||
return nil, fmt.Errorf("Estimating object %s is exist got an error: %#v", c.stateFile, err)
|
return nil, fmt.Errorf("estimating object %s is exist got an error: %#v", c.stateFile, err)
|
||||||
} else if !exist {
|
} else if !exist {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -425,12 +422,12 @@ func (c *RemoteClient) getObj() (*remote.Payload, error) {
|
||||||
var options []oss.Option
|
var options []oss.Option
|
||||||
output, err := bucket.GetObject(c.stateFile, options...)
|
output, err := bucket.GetObject(c.stateFile, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error getting object: %#v", err)
|
return nil, fmt.Errorf("error getting object: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
if _, err := io.Copy(buf, output); err != nil {
|
if _, err := io.Copy(buf, output); err != nil {
|
||||||
return nil, fmt.Errorf("Failed to read remote state: %s", err)
|
return nil, fmt.Errorf("failed to read remote state: %s", err)
|
||||||
}
|
}
|
||||||
sum := md5.Sum(buf.Bytes())
|
sum := md5.Sum(buf.Bytes())
|
||||||
payload := &remote.Payload{
|
payload := &remote.Payload{
|
||||||
|
@ -452,5 +449,4 @@ This may be caused by unusually long delays in OSS processing a previous state
|
||||||
update. Please wait for a minute or two and try again. If this problem
|
update. Please wait for a minute or two and try again. If this problem
|
||||||
persists, and neither OSS nor TableStore are experiencing an outage, you may need
|
persists, and neither OSS nor TableStore are experiencing an outage, you may need
|
||||||
to manually verify the remote state and update the Digest value stored in the
|
to manually verify the remote state and update the Digest value stored in the
|
||||||
TableStore table to the following value: %x
|
TableStore table to the following value: %x`
|
||||||
`
|
|
||||||
|
|
|
@ -77,36 +77,41 @@ data "terraform_remote_state" "network" {
|
||||||
|
|
||||||
The following configuration options or environment variables are supported:
|
The following configuration options or environment variables are supported:
|
||||||
|
|
||||||
* `access_key` - (Optional) Alibaba Cloud access key. It supports environment variables `ALICLOUD_ACCESS_KEY` and `ALICLOUD_ACCESS_KEY_ID`.
|
* `access_key` - (Optional) Alibaba Cloud access key. It supports environment variables `ALICLOUD_ACCESS_KEY` and `ALICLOUD_ACCESS_KEY_ID`.
|
||||||
* `secret_key` - (Optional) Alibaba Cloud secret access key. It supports environment variables `ALICLOUD_SECRET_KEY` and `ALICLOUD_ACCESS_KEY_SECRET`.
|
* `secret_key` - (Optional) Alibaba Cloud secret access key. It supports environment variables `ALICLOUD_SECRET_KEY` and `ALICLOUD_ACCESS_KEY_SECRET`.
|
||||||
* `security_token` - (Optional) STS access token. It supports environment variable `ALICLOUD_SECURITY_TOKEN`.
|
* `security_token` - (Optional) STS access token. It supports environment variable `ALICLOUD_SECURITY_TOKEN`.
|
||||||
* `ecs_role_name` - (Optional, Available in 0.12.14+) 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.
|
* `ecs_role_name` - (Optional, Available in 0.12.14+) 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.
|
||||||
* `region` - (Optional) The region of the OSS bucket. It supports environment variables `ALICLOUD_REGION` and `ALICLOUD_DEFAULT_REGION`.
|
* `region` - (Optional) The region of the OSS bucket. It supports environment variables `ALICLOUD_REGION` and `ALICLOUD_DEFAULT_REGION`.
|
||||||
* `endpoint` - (Optional) A custom endpoint for the OSS API. It supports environment variables `ALICLOUD_OSS_ENDPOINT` and `OSS_ENDPOINT`.
|
* `endpoint` - (Optional) A custom endpoint for the OSS API. It supports environment variables `ALICLOUD_OSS_ENDPOINT` and `OSS_ENDPOINT`.
|
||||||
* `bucket` - (Required) The name of the OSS bucket.
|
* `bucket` - (Required) The name of the OSS bucket.
|
||||||
* `prefix` - (Opeional) The path directory of the state file will be stored. Default to "env:".
|
* `prefix` - (Opeional) The path directory of the state file will be stored. Default to "env:".
|
||||||
* `key` - (Optional) The name of the state file. Defaults to `terraform.tfstate`.
|
* `key` - (Optional) The name of the state file. Defaults to `terraform.tfstate`.
|
||||||
* `tablestore_endpoint` / `ALICLOUD_TABLESTORE_ENDPOINT` - (Optional) A custom endpoint for the TableStore API.
|
* `tablestore_endpoint` / `ALICLOUD_TABLESTORE_ENDPOINT` - (Optional) A custom endpoint for the TableStore API.
|
||||||
* `tablestore_table` - (Optional) A TableStore table for state locking and consistency. The table must have a primary key named `LockID` of type `String`.
|
* `tablestore_table` - (Optional) A TableStore table for state locking and consistency. The table must have a primary key named `LockID` of type `String`.
|
||||||
* `encrypt` - (Optional) Whether to enable server side
|
* `encrypt` - (Optional) Whether to enable server side
|
||||||
encryption of the state file. If it is true, OSS will use 'AES256' encryption algorithm to encrypt state file.
|
encryption of the state file. If it is true, OSS will use 'AES256' encryption algorithm to encrypt state file.
|
||||||
* `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.
|
||||||
* `shared_credentials_file` - (Optional, Available in 0.12.8+) This is the path to the shared credentials file. It can also be sourced from the `ALICLOUD_SHARED_CREDENTIALS_FILE` environment variable. If this is not set and a profile is specified, `~/.aliyun/config.json` will be used.
|
* `shared_credentials_file` - (Optional, Available in 0.12.8+) This is the path to the shared credentials file. It can also be sourced from the `ALICLOUD_SHARED_CREDENTIALS_FILE` environment variable. 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.
|
* `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.
|
* `assume_role_role_arn` - (Optional, Available in 1.1.0+) The ARN of the role to assume. If ARN is set to an empty string, it does not perform role switching. It supports the environment variable `ALICLOUD_ASSUME_ROLE_ARN`.
|
||||||
|
|
||||||
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.
|
Terraform executes configuration on account with provided credentials.
|
||||||
|
* `assume_role_policy` - (Optional, Available in 1.1.0+ A more restrictive policy to apply to the temporary credentials. This gives you a way to further restrict the permissions for the resulting temporary security credentials. You cannot use this policy to grant permissions that exceed those of the role that is being assumed.
|
||||||
|
* `assume_role_session_name` - (Optional, Available in 1.1.0+) The session name to use when assuming the role. If omitted, 'terraform' is passed to the AssumeRole call as session name. It supports environment variable `ALICLOUD_ASSUME_ROLE_SESSION_NAME`.
|
||||||
|
* `assume_role_session_expiration` - (Optional, Available in 1.1.0+ The time after which the established session for assuming role expires. Valid value range: [900-3600] seconds. Default to 3600 (in this case Alibaba Cloud uses its own default value). It supports environment variable `ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION`.
|
||||||
|
|
||||||
* `policy` - (Optional) A more restrictive policy to apply to the temporary credentials. This gives you a way to further restrict the permissions for the resulting temporary
|
* `assume_role` - (**Deprecated as of 1.1.0+**, Available in 0.12.6+) If provided with a role ARN, will attempt to assume this role using the supplied credentials.
|
||||||
security credentials. You cannot use this policy to grant permissions which exceed those of the role that is being assumed.
|
|
||||||
|
|
||||||
* `session_name` - (Optional) The session name to use when assuming the role. If omitted, 'terraform' is passed to the AssumeRole call as session name. It supports environment variable `ALICLOUD_ASSUME_ROLE_SESSION_NAME`.
|
**Deprecated in favor of flattening assume_role_\* options**
|
||||||
|
|
||||||
* `session_expiration` - (Optional) The time after which the established session for assuming role expires. Valid value range: [900-3600] seconds. Default to 3600 (in this case Alibaba Cloud use own default value). It supports environment variable `ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION`.
|
* `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 the environment variable `ALICLOUD_ASSUME_ROLE_ARN`.
|
||||||
|
Terraform executes configuration on account with provided credentials.
|
||||||
|
|
||||||
-> **Note:** If you want to store state in the custom OSS endpoint, you can specify a environment variable `OSS_ENDPOINT`, like "oss-cn-beijing-internal.aliyuncs.com"
|
* `policy` - (Optional) A more restrictive policy to apply to the temporary credentials. This gives you a way to further restrict the permissions for the resulting temporary security credentials. You cannot use this policy to grant permissions that exceed those of the role that is being assumed.
|
||||||
|
|
||||||
|
* `session_name` - (Optional) The session name to use when assuming the role. If omitted, 'terraform' is passed to the AssumeRole call as session name. It supports environment variable `ALICLOUD_ASSUME_ROLE_SESSION_NAME`.
|
||||||
|
|
||||||
|
* `session_expiration` - (Optional) The time after which the established session for assuming role expires. Valid value range: [900-3600] seconds. Default to 3600 (in this case Alibaba Cloud uses its own default value). It supports environment variable `ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION`.
|
||||||
|
|
||||||
|
-> **Note:** If you want to store state in the custom OSS endpoint, you can specify an environment variable `OSS_ENDPOINT`, like "oss-cn-beijing-internal.aliyuncs.com"
|
||||||
|
|
Loading…
Reference in New Issue