From 9d5f1752c851cc15da18098a8dc2eff0dc42cf5a Mon Sep 17 00:00:00 2001 From: Alex Khaerov Date: Tue, 3 Aug 2021 14:21:48 +0800 Subject: [PATCH 1/4] oss backend: flattern assume_role block --- internal/backend/remote-state/oss/backend.go | 123 +++++++----------- .../language/settings/backends/oss.html.md | 15 +-- 2 files changed, 54 insertions(+), 84 deletions(-) diff --git a/internal/backend/remote-state/oss/backend.go b/internal/backend/remote-state/oss/backend.go index de08af37d6..5a2b2880ce 100644 --- a/internal/backend/remote-state/oss/backend.go +++ b/internal/backend/remote-state/oss/backend.go @@ -146,8 +146,6 @@ func New() backend.Backend { return nil, nil }, }, - - "assume_role": assumeRoleSchema(), "shared_credentials_file": { Type: schema.TypeString, Optional: true, @@ -160,60 +158,48 @@ func New() backend.Backend { 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{ - Type: schema.TypeSet, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "role_arn": { - Type: schema.TypeString, - Required: true, - Description: "The ARN of a RAM role to assume prior to making API calls.", - DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_ARN", ""), - }, - "session_name": { - Type: schema.TypeString, - Optional: true, - Description: "The session name to use when assuming the role.", - DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_NAME", ""), - }, - "policy": { - Type: schema.TypeString, - Optional: true, - 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.", - }, - "session_expiration": { - Type: schema.TypeInt, - Optional: true, - 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)} - } + "assume_role_role_arn": { + Type: schema.TypeString, + Required: true, + 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, + 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, + 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, + 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)} - } + 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 - }, + return nil, nil }, }, }, } + + result := &Backend{Backend: s} + result.Backend.ConfigureFunc = result.configure + return result } type Backend struct { @@ -274,31 +260,22 @@ func (b *Backend) configure(ctx context.Context) error { sessionExpiration = (int)(expiredSeconds.(float64)) } - if v, ok := d.GetOk("assume_role"); ok { - for _, v := range v.(*schema.Set).List() { - assumeRole := v.(map[string]interface{}) - 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 - } + roleArn = d.Get("assume_role_role_arn").(string) + sessionName = d.Get("assume_role_session_name").(string) + if sessionName == "" { + sessionName = "terraform" + } + policy = d.Get("assume_role_policy").(string) + sessionExpiration = d.Get("assume_role_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 + } } if accessKey == "" { diff --git a/website/docs/language/settings/backends/oss.html.md b/website/docs/language/settings/backends/oss.html.md index 6acc16af1c..515095ceb5 100644 --- a/website/docs/language/settings/backends/oss.html.md +++ b/website/docs/language/settings/backends/oss.html.md @@ -95,18 +95,11 @@ The following configuration options or environment variables are supported: 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. * `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`. +* `assume_role_role_arn` - (Optional, Available in 0.12.6+) 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. - -* `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_policy` - (Optional, Available in 0.12.6+) 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 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`. - -* `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`. +* `assume_role_session_name` - (Optional, Available in 0.12.6+) 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 0.12.6+) 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`. -> **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" From f32702c5c2369d59f279863cd2815f00add2bd23 Mon Sep 17 00:00:00 2001 From: Alex Khaerov Date: Mon, 18 Oct 2021 12:32:57 +0800 Subject: [PATCH 2/4] Support deprecated assume_role block --- internal/backend/remote-state/oss/backend.go | 140 +++++++++++++----- .../backend/remote-state/oss/backend_state.go | 17 +-- internal/backend/remote-state/oss/client.go | 30 ++-- 3 files changed, 127 insertions(+), 60 deletions(-) diff --git a/internal/backend/remote-state/oss/backend.go b/internal/backend/remote-state/oss/backend.go index 5a2b2880ce..c4131060e8 100644 --- a/internal/backend/remote-state/oss/backend.go +++ b/internal/backend/remote-state/oss/backend.go @@ -24,32 +24,84 @@ import ( "github.com/aliyun/aliyun-oss-go-sdk/oss" "github.com/aliyun/aliyun-tablestore-go-sdk/tablestore" "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/legacy/helper/schema" "github.com/hashicorp/terraform/version" - "github.com/jmespath/go-jmespath" - "github.com/mitchellh/go-homedir" ) +// deprecated in favor to flatten parameters +func deprecatedAssumeRoleSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ConflictsWith: []string{"assume_role_role_arn", "assume_role_session_name", "assume_role_policy", "assume_role_session_expiration"}, + MaxItems: 1, + Deprecated: "use flatten assume_role_* instead", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "role_arn": { + Type: schema.TypeString, + Required: true, + Description: "The ARN of a RAM role to assume prior to making API calls.", + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_ARN", ""), + }, + "session_name": { + Type: schema.TypeString, + Optional: true, + Description: "The session name to use when assuming the role.", + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_ROLE_SESSION_NAME", ""), + }, + "policy": { + Type: schema.TypeString, + Optional: true, + 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.", + }, + "session_expiration": { + Type: schema.TypeInt, + Optional: true, + 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 + }, + }, + }, + }, + } +} + // New creates a new backend for OSS remote state. func New() backend.Backend { s := &schema.Backend{ Schema: map[string]*schema.Schema{ - "access_key": &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": &schema.Schema{ + "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": &schema.Schema{ + "security_token": { Type: schema.TypeString, Optional: true, Description: "Alibaba Cloud Security Token", @@ -63,7 +115,7 @@ func New() backend.Backend { 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{ + "region": { Type: schema.TypeString, Optional: true, Description: "The region of the OSS bucket.", @@ -82,13 +134,13 @@ func New() backend.Backend { DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_OSS_ENDPOINT", os.Getenv("OSS_ENDPOINT")), }, - "bucket": &schema.Schema{ + "bucket": { Type: schema.TypeString, Required: true, Description: "The name of the OSS bucket", }, - "prefix": &schema.Schema{ + "prefix": { Type: schema.TypeString, Optional: true, Description: "The directory where state files will be saved inside the bucket", @@ -102,7 +154,7 @@ func New() backend.Backend { }, }, - "key": &schema.Schema{ + "key": { Type: schema.TypeString, Optional: true, Description: "The path of the state file inside the bucket", @@ -122,14 +174,14 @@ func New() backend.Backend { Default: "", }, - "encrypt": &schema.Schema{ + "encrypt": { Type: schema.TypeBool, Optional: true, Description: "Whether to enable server side encryption of the state file", Default: false, }, - "acl": &schema.Schema{ + "acl": { Type: schema.TypeString, Optional: true, Description: "Object ACL to be applied to the state file", @@ -158,27 +210,32 @@ func New() backend.Backend { 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, - Required: true, - Description: "The ARN of a RAM role to assume prior to making API calls.", - DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_ASSUME_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, - Description: "The session name to use when assuming the role.", - DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_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, - 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.", + 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, - Description: "The time after which the established session for assuming role expires.", + 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 @@ -214,7 +271,6 @@ type Backend struct { stateKey string serverSideEncryption bool acl string - endpoint string otsEndpoint string otsTable string } @@ -260,13 +316,29 @@ func (b *Backend) configure(ctx context.Context) error { sessionExpiration = (int)(expiredSeconds.(float64)) } - roleArn = d.Get("assume_role_role_arn").(string) - sessionName = d.Get("assume_role_session_name").(string) + if v, ok := d.GetOk("assume_role"); ok { + // deprecated assume_role block + for _, v := range v.(*schema.Set).List() { + assumeRole := v.(map[string]interface{}) + if assumeRole["role_arn"].(string) != "" { + roleArn = assumeRole["role_arn"].(string) + } + if assumeRole["session_name"].(string) != "" { + sessionName = assumeRole["session_name"].(string) + } + policy = assumeRole["policy"].(string) + sessionExpiration = assumeRole["session_expiration"].(int) + } + } else { + roleArn = d.Get("assume_role_role_arn").(string) + sessionName = d.Get("assume_role_session_name").(string) + policy = d.Get("assume_role_policy").(string) + sessionExpiration = d.Get("assume_role_session_expiration").(int) + } + if sessionName == "" { sessionName = "terraform" } - policy = d.Get("assume_role_policy").(string) - sessionExpiration = d.Get("assume_role_session_expiration").(int) if sessionExpiration == 0 { if v := os.Getenv("ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION"); v != "" { if expiredSeconds, err := strconv.Atoi(v); err == nil { @@ -346,13 +418,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)) 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) endpointsResponse, err := locationClient.DescribeEndpoints(args) 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 } @@ -442,7 +514,7 @@ func (a *Invoker) Run(f func() error) error { catcher.RetryCount-- 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 { time.Sleep(time.Duration(catcher.RetryWaitSeconds) * time.Second) return a.Run(f) @@ -552,7 +624,7 @@ func getAuthCredentialByEcsRoleName(ecsRoleName string) (accessKey, secretKey, t response := responses.NewCommonResponse() err = responses.Unmarshal(response, httpResponse, "") 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 } diff --git a/internal/backend/remote-state/oss/backend_state.go b/internal/backend/remote-state/oss/backend_state.go index d91ed6c5c9..d08e1d1338 100644 --- a/internal/backend/remote-state/oss/backend_state.go +++ b/internal/backend/remote-state/oss/backend_state.go @@ -3,19 +3,18 @@ package oss import ( "errors" "fmt" + "log" + "path" "sort" "strings" "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/states" "github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/statemgr" - - "log" - "path" - - "github.com/aliyun/aliyun-tablestore-go-sdk/tablestore" ) const ( @@ -43,7 +42,7 @@ func (b *Backend) remoteClient(name string) (*RemoteClient, error) { TableName: b.otsTable, }) 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) { bucket, err := b.ossClient.Bucket(b.bucketName) 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 @@ -85,7 +84,7 @@ func (b *Backend) Workspaces() ([]string, error) { } else { options = append(options, oss.Marker(lastObj)) } - resp, err = bucket.ListObjects(options...) + bucket.ListObjects(options...) } else { break } @@ -135,7 +134,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { lockInfo.Operation = "init" lockId, err := client.Lock(lockInfo) 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 diff --git a/internal/backend/remote-state/oss/client.go b/internal/backend/remote-state/oss/client.go index ccf19576a2..78d835ae13 100644 --- a/internal/backend/remote-state/oss/client.go +++ b/internal/backend/remote-state/oss/client.go @@ -3,22 +3,21 @@ package oss import ( "bytes" "crypto/md5" + "encoding/hex" "encoding/json" "fmt" "io" - - "encoding/hex" "log" - "sync" "time" "github.com/aliyun/aliyun-oss-go-sdk/oss" "github.com/aliyun/aliyun-tablestore-go-sdk/tablestore" "github.com/hashicorp/go-multierror" uuid "github.com/hashicorp/go-uuid" + "github.com/pkg/errors" + "github.com/hashicorp/terraform/internal/states/remote" "github.com/hashicorp/terraform/internal/states/statemgr" - "github.com/pkg/errors" ) const ( @@ -48,8 +47,6 @@ type RemoteClient struct { lockFile string serverSideEncryption bool acl string - info *statemgr.LockInfo - mu sync.Mutex otsTable string } @@ -99,7 +96,7 @@ func (c *RemoteClient) Get() (payload *remote.Payload, err error) { func (c *RemoteClient) Put(data []byte) error { bucket, err := c.ossClient.Bucket(c.bucketName) if err != nil { - return fmt.Errorf("Error getting bucket: %#v", err) + return fmt.Errorf("error getting bucket: %#v", err) } body := bytes.NewReader(data) @@ -116,7 +113,7 @@ func (c *RemoteClient) Put(data []byte) error { if body != 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 this errors out, we unfortunately have to error out altogether, // 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 } @@ -132,13 +129,13 @@ func (c *RemoteClient) Put(data []byte) error { func (c *RemoteClient) Delete() error { bucket, err := c.ossClient.Bucket(c.bucketName) 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) 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 { @@ -413,11 +410,11 @@ func (c *RemoteClient) lockPath() string { func (c *RemoteClient) getObj() (*remote.Payload, error) { bucket, err := c.ossClient.Bucket(c.bucketName) 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 { - 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 { return nil, nil } @@ -425,12 +422,12 @@ func (c *RemoteClient) getObj() (*remote.Payload, error) { var options []oss.Option output, err := bucket.GetObject(c.stateFile, options...) 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) 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()) 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 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 -TableStore table to the following value: %x -` +TableStore table to the following value: %x` From 14f366dbf43131081e40ab156f991a2ac364da3b Mon Sep 17 00:00:00 2001 From: Alex Khaerov Date: Mon, 18 Oct 2021 12:54:40 +0800 Subject: [PATCH 3/4] Update documentation --- internal/backend/remote-state/oss/backend.go | 4 +- .../language/settings/backends/oss.html.md | 60 +++++++++++-------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/internal/backend/remote-state/oss/backend.go b/internal/backend/remote-state/oss/backend.go index c4131060e8..236a5e7857 100644 --- a/internal/backend/remote-state/oss/backend.go +++ b/internal/backend/remote-state/oss/backend.go @@ -32,14 +32,14 @@ import ( "github.com/hashicorp/terraform/version" ) -// deprecated in favor to flatten parameters +// Deprecated in favor of flattening assume_role_* options func deprecatedAssumeRoleSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeSet, Optional: true, ConflictsWith: []string{"assume_role_role_arn", "assume_role_session_name", "assume_role_policy", "assume_role_session_expiration"}, MaxItems: 1, - Deprecated: "use flatten assume_role_* instead", + Deprecated: "use assume_role_* options instead", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "role_arn": { diff --git a/website/docs/language/settings/backends/oss.html.md b/website/docs/language/settings/backends/oss.html.md index 515095ceb5..92e0d3ab05 100644 --- a/website/docs/language/settings/backends/oss.html.md +++ b/website/docs/language/settings/backends/oss.html.md @@ -77,29 +77,41 @@ data "terraform_remote_state" "network" { 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`. - * `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`. - * `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`. - * `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. - * `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`. - * `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`. - * `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. - * `acl` - (Optional) [Object - ACL](https://www.alibabacloud.com/help/doc-detail/52284.htm) - 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. - * `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_role_arn` - (Optional, Available in 0.12.6+) 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`. +* `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`. +* `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. +* `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`. +* `bucket` - (Required) The name of the OSS bucket. +* `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`. +* `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`. +* `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. +* `acl` - (Optional) [Object + ACL](https://www.alibabacloud.com/help/doc-detail/52284.htm) + 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. +* `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_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`. Terraform executes configuration on account with provided credentials. -* `assume_role_policy` - (Optional, Available in 0.12.6+) 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 which exceed those of the role that is being assumed. -* `assume_role_session_name` - (Optional, Available in 0.12.6+) 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 0.12.6+) 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`. +* `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`. --> **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" +* `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. + + **Deprecated in favor of flattening assume_role_\* options** + + * `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. + + * `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" From 6f1eceb0e21bfe75586440d1907b70b7861cbcd7 Mon Sep 17 00:00:00 2001 From: Alex Khaerov Date: Thu, 21 Oct 2021 21:42:13 +0800 Subject: [PATCH 4/4] Revert autoupdate --- internal/backend/remote-state/oss/backend_state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/backend/remote-state/oss/backend_state.go b/internal/backend/remote-state/oss/backend_state.go index d08e1d1338..1d5c1ce46e 100644 --- a/internal/backend/remote-state/oss/backend_state.go +++ b/internal/backend/remote-state/oss/backend_state.go @@ -84,7 +84,7 @@ func (b *Backend) Workspaces() ([]string, error) { } else { options = append(options, oss.Marker(lastObj)) } - bucket.ListObjects(options...) + resp, err = bucket.ListObjects(options...) } else { break }