mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
provider/aws: Add tag support to the dynamodb table resource
Adds tag support to the `aws_dynamodb_table` resource. Also adds a test for the resource, and a test to ensure that the tags are populated correctly from a resource import. ``` $ make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSDynamoDBTable_tags' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/02/01 15:35:00 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSDynamoDBTable_tags -timeout 120m === RUN TestAccAWSDynamoDBTable_tags --- PASS: TestAccAWSDynamoDBTable_tags (28.69s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 28.713s ``` ``` $ make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSDynamoDbTable_importTags' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/02/01 15:39:49 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSDynamoDbTable_importTags -timeout 120m === RUN TestAccAWSDynamoDbTable_importTags --- PASS: TestAccAWSDynamoDbTable_importTags (30.62s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 30.645s ```
This commit is contained in:
parent
b51edd6175
commit
91e0fed333
@ -14,11 +14,32 @@ func TestAccAWSDynamoDbTable_importBasic(t *testing.T) {
|
|||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSDynamoDbConfigInitialState(),
|
Config: testAccAWSDynamoDbConfigInitialState(),
|
||||||
},
|
},
|
||||||
|
|
||||||
resource.TestStep{
|
{
|
||||||
|
ResourceName: resourceName,
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSDynamoDbTable_importTags(t *testing.T) {
|
||||||
|
resourceName := "aws_dynamodb_table.basic-dynamodb-table"
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccAWSDynamoDbConfigTags(),
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
ResourceName: resourceName,
|
ResourceName: resourceName,
|
||||||
ImportState: true,
|
ImportState: true,
|
||||||
ImportStateVerify: true,
|
ImportStateVerify: true,
|
||||||
|
@ -40,43 +40,43 @@ func resourceAwsDynamoDbTable() *schema.Resource {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"arn": &schema.Schema{
|
"arn": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
"name": &schema.Schema{
|
"name": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"hash_key": &schema.Schema{
|
"hash_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"range_key": &schema.Schema{
|
"range_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"write_capacity": &schema.Schema{
|
"write_capacity": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"read_capacity": &schema.Schema{
|
"read_capacity": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"attribute": &schema.Schema{
|
"attribute": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Required: true,
|
Required: true,
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": &schema.Schema{
|
"name": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"type": &schema.Schema{
|
"type": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
@ -89,25 +89,25 @@ func resourceAwsDynamoDbTable() *schema.Resource {
|
|||||||
return hashcode.String(buf.String())
|
return hashcode.String(buf.String())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"local_secondary_index": &schema.Schema{
|
"local_secondary_index": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": &schema.Schema{
|
"name": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"range_key": &schema.Schema{
|
"range_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"projection_type": &schema.Schema{
|
"projection_type": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"non_key_attributes": &schema.Schema{
|
"non_key_attributes": {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
@ -121,36 +121,36 @@ func resourceAwsDynamoDbTable() *schema.Resource {
|
|||||||
return hashcode.String(buf.String())
|
return hashcode.String(buf.String())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"global_secondary_index": &schema.Schema{
|
"global_secondary_index": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": &schema.Schema{
|
"name": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"write_capacity": &schema.Schema{
|
"write_capacity": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"read_capacity": &schema.Schema{
|
"read_capacity": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"hash_key": &schema.Schema{
|
"hash_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"range_key": &schema.Schema{
|
"range_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
"projection_type": &schema.Schema{
|
"projection_type": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
"non_key_attributes": &schema.Schema{
|
"non_key_attributes": {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
@ -167,12 +167,12 @@ func resourceAwsDynamoDbTable() *schema.Resource {
|
|||||||
return hashcode.String(buf.String())
|
return hashcode.String(buf.String())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"stream_enabled": &schema.Schema{
|
"stream_enabled": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
"stream_view_type": &schema.Schema{
|
"stream_view_type": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
@ -182,10 +182,11 @@ func resourceAwsDynamoDbTable() *schema.Resource {
|
|||||||
},
|
},
|
||||||
ValidateFunc: validateStreamViewType,
|
ValidateFunc: validateStreamViewType,
|
||||||
},
|
},
|
||||||
"stream_arn": &schema.Schema{
|
"stream_arn": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
"tags": tagsSchema(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +205,7 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
|
|
||||||
hash_key_name := d.Get("hash_key").(string)
|
hash_key_name := d.Get("hash_key").(string)
|
||||||
keyschema := []*dynamodb.KeySchemaElement{
|
keyschema := []*dynamodb.KeySchemaElement{
|
||||||
&dynamodb.KeySchemaElement{
|
{
|
||||||
AttributeName: aws.String(hash_key_name),
|
AttributeName: aws.String(hash_key_name),
|
||||||
KeyType: aws.String("HASH"),
|
KeyType: aws.String("HASH"),
|
||||||
},
|
},
|
||||||
@ -239,7 +240,7 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if lsidata, ok := d.GetOk("local_secondary_index"); ok {
|
if lsidata, ok := d.GetOk("local_secondary_index"); ok {
|
||||||
fmt.Printf("[DEBUG] Adding LSI data to the table")
|
log.Printf("[DEBUG] Adding LSI data to the table")
|
||||||
|
|
||||||
lsiSet := lsidata.(*schema.Set)
|
lsiSet := lsidata.(*schema.Set)
|
||||||
localSecondaryIndexes := []*dynamodb.LocalSecondaryIndex{}
|
localSecondaryIndexes := []*dynamodb.LocalSecondaryIndex{}
|
||||||
@ -261,11 +262,11 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
localSecondaryIndexes = append(localSecondaryIndexes, &dynamodb.LocalSecondaryIndex{
|
localSecondaryIndexes = append(localSecondaryIndexes, &dynamodb.LocalSecondaryIndex{
|
||||||
IndexName: aws.String(lsi["name"].(string)),
|
IndexName: aws.String(lsi["name"].(string)),
|
||||||
KeySchema: []*dynamodb.KeySchemaElement{
|
KeySchema: []*dynamodb.KeySchemaElement{
|
||||||
&dynamodb.KeySchemaElement{
|
{
|
||||||
AttributeName: aws.String(hash_key_name),
|
AttributeName: aws.String(hash_key_name),
|
||||||
KeyType: aws.String("HASH"),
|
KeyType: aws.String("HASH"),
|
||||||
},
|
},
|
||||||
&dynamodb.KeySchemaElement{
|
{
|
||||||
AttributeName: aws.String(lsi["range_key"].(string)),
|
AttributeName: aws.String(lsi["range_key"].(string)),
|
||||||
KeyType: aws.String("RANGE"),
|
KeyType: aws.String("RANGE"),
|
||||||
},
|
},
|
||||||
@ -276,7 +277,7 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
|
|
||||||
req.LocalSecondaryIndexes = localSecondaryIndexes
|
req.LocalSecondaryIndexes = localSecondaryIndexes
|
||||||
|
|
||||||
fmt.Printf("[DEBUG] Added %d LSI definitions", len(localSecondaryIndexes))
|
log.Printf("[DEBUG] Added %d LSI definitions", len(localSecondaryIndexes))
|
||||||
}
|
}
|
||||||
|
|
||||||
if gsidata, ok := d.GetOk("global_secondary_index"); ok {
|
if gsidata, ok := d.GetOk("global_secondary_index"); ok {
|
||||||
@ -298,9 +299,11 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
StreamViewType: aws.String(d.Get("stream_view_type").(string)),
|
StreamViewType: aws.String(d.Get("stream_view_type").(string)),
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[DEBUG] Adding StreamSpecifications to the table")
|
log.Printf("[DEBUG] Adding StreamSpecifications to the table")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, tagsOk := d.GetOk("tags")
|
||||||
|
|
||||||
attemptCount := 1
|
attemptCount := 1
|
||||||
for attemptCount <= DYNAMODB_MAX_THROTTLE_RETRIES {
|
for attemptCount <= DYNAMODB_MAX_THROTTLE_RETRIES {
|
||||||
output, err := dynamodbconn.CreateTable(req)
|
output, err := dynamodbconn.CreateTable(req)
|
||||||
@ -325,10 +328,16 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er
|
|||||||
} else {
|
} else {
|
||||||
// No error, set ID and return
|
// No error, set ID and return
|
||||||
d.SetId(*output.TableDescription.TableName)
|
d.SetId(*output.TableDescription.TableName)
|
||||||
if err := d.Set("arn", *output.TableDescription.TableArn); err != nil {
|
tableArn := *output.TableDescription.TableArn
|
||||||
|
if err := d.Set("arn", tableArn); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if tagsOk {
|
||||||
|
log.Printf("[DEBUG] Setting DynamoDB Tags on arn: %s", tableArn)
|
||||||
|
if err := createTableTags(d, meta); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return resourceAwsDynamoDbTableRead(d, meta)
|
return resourceAwsDynamoDbTableRead(d, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -581,6 +590,11 @@ func resourceAwsDynamoDbTableUpdate(d *schema.ResourceData, meta interface{}) er
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update tags
|
||||||
|
if err := setTagsDynamoDB(dynamodbconn, d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return resourceAwsDynamoDbTableRead(d, meta)
|
return resourceAwsDynamoDbTableRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,6 +714,14 @@ func resourceAwsDynamoDbTableRead(d *schema.ResourceData, meta interface{}) erro
|
|||||||
|
|
||||||
d.Set("arn", table.TableArn)
|
d.Set("arn", table.TableArn)
|
||||||
|
|
||||||
|
tags, err := readTableTags(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(tags) != 0 {
|
||||||
|
d.Set("tags", tags)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,7 +792,7 @@ func createGSIFromData(data *map[string]interface{}) dynamodb.GlobalSecondaryInd
|
|||||||
readCapacity := (*data)["read_capacity"].(int)
|
readCapacity := (*data)["read_capacity"].(int)
|
||||||
|
|
||||||
key_schema := []*dynamodb.KeySchemaElement{
|
key_schema := []*dynamodb.KeySchemaElement{
|
||||||
&dynamodb.KeySchemaElement{
|
{
|
||||||
AttributeName: aws.String((*data)["hash_key"].(string)),
|
AttributeName: aws.String((*data)["hash_key"].(string)),
|
||||||
KeyType: aws.String("HASH"),
|
KeyType: aws.String("HASH"),
|
||||||
},
|
},
|
||||||
@ -890,3 +912,43 @@ func waitForTableToBeActive(tableName string, meta interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createTableTags(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
// DynamoDB Table has to be in the ACTIVE state in order to tag the resource
|
||||||
|
if err := waitForTableToBeActive(d.Id(), meta); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tags := d.Get("tags").(map[string]interface{})
|
||||||
|
arn := d.Get("arn").(string)
|
||||||
|
dynamodbconn := meta.(*AWSClient).dynamodbconn
|
||||||
|
req := &dynamodb.TagResourceInput{
|
||||||
|
ResourceArn: aws.String(arn),
|
||||||
|
Tags: tagsFromMapDynamoDB(tags),
|
||||||
|
}
|
||||||
|
_, err := dynamodbconn.TagResource(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error tagging dynamodb resource: %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTableTags(d *schema.ResourceData, meta interface{}) (map[string]string, error) {
|
||||||
|
if err := waitForTableToBeActive(d.Id(), meta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
arn := d.Get("arn").(string)
|
||||||
|
//result := make(map[string]string)
|
||||||
|
|
||||||
|
dynamodbconn := meta.(*AWSClient).dynamodbconn
|
||||||
|
req := &dynamodb.ListTagsOfResourceInput{
|
||||||
|
ResourceArn: aws.String(arn),
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := dynamodbconn.ListTagsOfResource(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error reading tags from dynamodb resource: %s", err)
|
||||||
|
}
|
||||||
|
result := tagsToMapDynamoDB(output.Tags)
|
||||||
|
// TODO Read NextToken if avail
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
@ -19,13 +19,13 @@ func TestAccAWSDynamoDbTable_basic(t *testing.T) {
|
|||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSDynamoDbConfigInitialState(),
|
Config: testAccAWSDynamoDbConfigInitialState(),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table"),
|
testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSDynamoDbConfigAddSecondaryGSI,
|
Config: testAccAWSDynamoDbConfigAddSecondaryGSI,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckDynamoDbTableWasUpdated("aws_dynamodb_table.basic-dynamodb-table"),
|
testAccCheckDynamoDbTableWasUpdated("aws_dynamodb_table.basic-dynamodb-table"),
|
||||||
@ -41,7 +41,7 @@ func TestAccAWSDynamoDbTable_streamSpecification(t *testing.T) {
|
|||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSDynamoDbConfigStreamSpecification(),
|
Config: testAccAWSDynamoDbConfigStreamSpecification(),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table"),
|
testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table"),
|
||||||
@ -55,6 +55,24 @@ func TestAccAWSDynamoDbTable_streamSpecification(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSDynamoDBTable_tags(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: testAccAWSDynamoDbConfigTags(),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_dynamodb_table.basic-dynamodb-table", "tags.%", "3"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestResourceAWSDynamoDbTableStreamViewType_validation(t *testing.T) {
|
func TestResourceAWSDynamoDbTableStreamViewType_validation(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Value string
|
Value string
|
||||||
@ -127,7 +145,7 @@ func testAccCheckAWSDynamoDbTableDestroy(s *terraform.State) error {
|
|||||||
|
|
||||||
func testAccCheckInitialAWSDynamoDbTableExists(n string) resource.TestCheckFunc {
|
func testAccCheckInitialAWSDynamoDbTableExists(n string) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
fmt.Printf("[DEBUG] Trying to create initial table state!")
|
log.Printf("[DEBUG] Trying to create initial table state!")
|
||||||
rs, ok := s.RootModule().Resources[n]
|
rs, ok := s.RootModule().Resources[n]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Not found: %s", n)
|
return fmt.Errorf("Not found: %s", n)
|
||||||
@ -146,13 +164,12 @@ func testAccCheckInitialAWSDynamoDbTableExists(n string) resource.TestCheckFunc
|
|||||||
resp, err := conn.DescribeTable(params)
|
resp, err := conn.DescribeTable(params)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("[ERROR] Problem describing table '%s': %s", rs.Primary.ID, err)
|
return fmt.Errorf("[ERROR] Problem describing table '%s': %s", rs.Primary.ID, err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table := resp.Table
|
table := resp.Table
|
||||||
|
|
||||||
fmt.Printf("[DEBUG] Checking on table %s", rs.Primary.ID)
|
log.Printf("[DEBUG] Checking on table %s", rs.Primary.ID)
|
||||||
|
|
||||||
if *table.ProvisionedThroughput.WriteCapacityUnits != 20 {
|
if *table.ProvisionedThroughput.WriteCapacityUnits != 20 {
|
||||||
return fmt.Errorf("Provisioned write capacity was %d, not 20!", table.ProvisionedThroughput.WriteCapacityUnits)
|
return fmt.Errorf("Provisioned write capacity was %d, not 20!", table.ProvisionedThroughput.WriteCapacityUnits)
|
||||||
@ -404,3 +421,49 @@ resource "aws_dynamodb_table" "basic-dynamodb-table" {
|
|||||||
}
|
}
|
||||||
`, acctest.RandInt())
|
`, acctest.RandInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccAWSDynamoDbConfigTags() string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_dynamodb_table" "basic-dynamodb-table" {
|
||||||
|
name = "TerraformTestTable-%d"
|
||||||
|
read_capacity = 10
|
||||||
|
write_capacity = 20
|
||||||
|
hash_key = "TestTableHashKey"
|
||||||
|
range_key = "TestTableRangeKey"
|
||||||
|
attribute {
|
||||||
|
name = "TestTableHashKey"
|
||||||
|
type = "S"
|
||||||
|
}
|
||||||
|
attribute {
|
||||||
|
name = "TestTableRangeKey"
|
||||||
|
type = "S"
|
||||||
|
}
|
||||||
|
attribute {
|
||||||
|
name = "TestLSIRangeKey"
|
||||||
|
type = "N"
|
||||||
|
}
|
||||||
|
attribute {
|
||||||
|
name = "TestGSIRangeKey"
|
||||||
|
type = "S"
|
||||||
|
}
|
||||||
|
local_secondary_index {
|
||||||
|
name = "TestTableLSI"
|
||||||
|
range_key = "TestLSIRangeKey"
|
||||||
|
projection_type = "ALL"
|
||||||
|
}
|
||||||
|
global_secondary_index {
|
||||||
|
name = "InitialTestTableGSI"
|
||||||
|
hash_key = "TestTableHashKey"
|
||||||
|
range_key = "TestGSIRangeKey"
|
||||||
|
write_capacity = 10
|
||||||
|
read_capacity = 10
|
||||||
|
projection_type = "KEYS_ONLY"
|
||||||
|
}
|
||||||
|
tags {
|
||||||
|
Name = "terraform-test-table-%d"
|
||||||
|
AccTest = "yes"
|
||||||
|
Testing = "absolutely"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, acctest.RandInt(), acctest.RandInt())
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/dynamodb"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
@ -247,3 +248,103 @@ func tagIgnoredELBv2(t *elbv2.Tag) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tagsToMapDynamoDB turns the list of tags into a map for dynamoDB
|
||||||
|
func tagsToMapDynamoDB(ts []*dynamodb.Tag) map[string]string {
|
||||||
|
result := make(map[string]string)
|
||||||
|
for _, t := range ts {
|
||||||
|
result[*t.Key] = *t.Value
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagsFromMapDynamoDB returns the tags for a given map
|
||||||
|
func tagsFromMapDynamoDB(m map[string]interface{}) []*dynamodb.Tag {
|
||||||
|
result := make([]*dynamodb.Tag, 0, len(m))
|
||||||
|
for k, v := range m {
|
||||||
|
t := &dynamodb.Tag{
|
||||||
|
Key: aws.String(k),
|
||||||
|
Value: aws.String(v.(string)),
|
||||||
|
}
|
||||||
|
result = append(result, t)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// setTagsDynamoDB is a helper to set the tags for a dynamoDB resource
|
||||||
|
// This is needed because dynamodb requires a completely different set and delete
|
||||||
|
// method from the ec2 tag resource handling. Also the `UntagResource` method
|
||||||
|
// for dynamoDB only requires a list of tag keys, instead of the full map of keys.
|
||||||
|
func setTagsDynamoDB(conn *dynamodb.DynamoDB, d *schema.ResourceData) error {
|
||||||
|
if d.HasChange("tags") {
|
||||||
|
arn := d.Get("arn").(string)
|
||||||
|
oraw, nraw := d.GetChange("tags")
|
||||||
|
o := oraw.(map[string]interface{})
|
||||||
|
n := nraw.(map[string]interface{})
|
||||||
|
create, remove := diffTagsDynamoDB(tagsFromMapDynamoDB(o), tagsFromMapDynamoDB(n))
|
||||||
|
|
||||||
|
// Set tags
|
||||||
|
if len(remove) > 0 {
|
||||||
|
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
|
||||||
|
log.Printf("[DEBUG] Removing tags: %#v from %s", remove, d.Id())
|
||||||
|
_, err := conn.UntagResource(&dynamodb.UntagResourceInput{
|
||||||
|
ResourceArn: aws.String(arn),
|
||||||
|
TagKeys: remove,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec2err, ok := err.(awserr.Error)
|
||||||
|
if ok && strings.Contains(ec2err.Code(), "ResourceNotFoundException") {
|
||||||
|
return resource.RetryableError(err) // retry
|
||||||
|
}
|
||||||
|
return resource.NonRetryableError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(create) > 0 {
|
||||||
|
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
|
||||||
|
log.Printf("[DEBUG] Creating tags: %s for %s", create, d.Id())
|
||||||
|
_, err := conn.TagResource(&dynamodb.TagResourceInput{
|
||||||
|
ResourceArn: aws.String(arn),
|
||||||
|
Tags: create,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec2err, ok := err.(awserr.Error)
|
||||||
|
if ok && strings.Contains(ec2err.Code(), "ResourceNotFoundException") {
|
||||||
|
return resource.RetryableError(err) // retry
|
||||||
|
}
|
||||||
|
return resource.NonRetryableError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffTagsDynamoDB takes a local set of dynamodb tags and the ones found remotely
|
||||||
|
// and returns the set of tags that must be created as a map, and returns a list of tag keys
|
||||||
|
// that must be destroyed.
|
||||||
|
func diffTagsDynamoDB(oldTags, newTags []*dynamodb.Tag) ([]*dynamodb.Tag, []*string) {
|
||||||
|
create := make(map[string]interface{})
|
||||||
|
for _, t := range newTags {
|
||||||
|
create[*t.Key] = *t.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var remove []*string
|
||||||
|
for _, t := range oldTags {
|
||||||
|
// Verify the old tag is not a tag we're currently attempting to create
|
||||||
|
old, ok := create[*t.Key]
|
||||||
|
if !ok || old != *t.Value {
|
||||||
|
remove = append(remove, t.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tagsFromMapDynamoDB(create), remove
|
||||||
|
}
|
||||||
|
@ -43,6 +43,10 @@ resource "aws_dynamodb_table" "basic-dynamodb-table" {
|
|||||||
projection_type = "INCLUDE"
|
projection_type = "INCLUDE"
|
||||||
non_key_attributes = [ "UserId" ]
|
non_key_attributes = [ "UserId" ]
|
||||||
}
|
}
|
||||||
|
tags {
|
||||||
|
Name = "dynamodb-table-1"
|
||||||
|
Environment = "production"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -69,6 +73,7 @@ definition after you have created the resource.
|
|||||||
* `global_secondary_index` - (Optional) Describe a GSO for the table;
|
* `global_secondary_index` - (Optional) Describe a GSO for the table;
|
||||||
subject to the normal limits on the number of GSIs, projected
|
subject to the normal limits on the number of GSIs, projected
|
||||||
attributes, etc.
|
attributes, etc.
|
||||||
|
* `tags` - (Optional) A map of tags to populate on the created table.
|
||||||
|
|
||||||
For both `local_secondary_index` and `global_secondary_index` objects,
|
For both `local_secondary_index` and `global_secondary_index` objects,
|
||||||
the following properties are supported:
|
the following properties are supported:
|
||||||
|
Loading…
Reference in New Issue
Block a user