Support deprecated assume_role block

This commit is contained in:
Alex Khaerov 2021-10-18 12:32:57 +08:00
parent de8810cdd9
commit f32702c5c2
No known key found for this signature in database
GPG Key ID: 67B5BC574DECAF54
3 changed files with 127 additions and 60 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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`