Skip to content

Commit

Permalink
Merge pull request #6648 from sunilkumarmohanty/dynamodb-billing
Browse files Browse the repository at this point in the history
Add Support for dynamodb billing
  • Loading branch information
bflad authored Nov 30, 2018
2 parents 291a107 + 0f34622 commit ee1f864
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 11 deletions.
62 changes: 53 additions & 9 deletions aws/resource_aws_dynamodb_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,22 @@ func resourceAwsDynamoDbTable() *schema.Resource {
Optional: true,
ForceNew: true,
},
"billing_mode": {
Type: schema.TypeString,
Optional: true,
Default: dynamodb.BillingModeProvisioned,
ValidateFunc: validation.StringInSlice([]string{
dynamodb.BillingModePayPerRequest,
dynamodb.BillingModeProvisioned,
}, false),
},
"write_capacity": {
Type: schema.TypeInt,
Required: true,
Optional: true,
},
"read_capacity": {
Type: schema.TypeInt,
Required: true,
Optional: true,
},
"attribute": {
Type: schema.TypeSet,
Expand Down Expand Up @@ -279,12 +288,22 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er
log.Printf("[DEBUG] Creating DynamoDB table with key schema: %#v", keySchemaMap)

req := &dynamodb.CreateTableInput{
TableName: aws.String(d.Get("name").(string)),
ProvisionedThroughput: expandDynamoDbProvisionedThroughput(map[string]interface{}{
"read_capacity": d.Get("read_capacity"),
"write_capacity": d.Get("write_capacity"),
}),
KeySchema: expandDynamoDbKeySchema(keySchemaMap),
TableName: aws.String(d.Get("name").(string)),
BillingMode: aws.String(d.Get("billing_mode").(string)),
KeySchema: expandDynamoDbKeySchema(keySchemaMap),
}

if d.Get("billing_mode").(string) == dynamodb.BillingModeProvisioned {
v_read, ok_read := d.GetOk("read_capacity")
v_write, ok_write := d.GetOk("write_capacity")
if !ok_read || !ok_write {
return fmt.Errorf("Read and Write capacity should be set when billing mode is PROVISIONED")
}

req.ProvisionedThroughput = expandDynamoDbProvisionedThroughput(map[string]interface{}{
"read_capacity": v_read,
"write_capacity": v_write,
})
}

if v, ok := d.GetOk("attribute"); ok {
Expand Down Expand Up @@ -360,10 +379,35 @@ func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) er

func resourceAwsDynamoDbTableUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).dynamodbconn
if d.HasChange("billing_mode") && !d.IsNewResource() {
req := &dynamodb.UpdateTableInput{
TableName: aws.String(d.Id()),
BillingMode: aws.String(d.Get("billing_mode").(string)),
}
if d.Get("billing_mode").(string) == dynamodb.BillingModeProvisioned {

v_read, ok_read := d.GetOk("read_capacity")
v_write, ok_write := d.GetOk("write_capacity")
if !ok_read || !ok_write {
return fmt.Errorf("Read and Write capacity should be set when billing mode is PROVISIONED")
}

req.ProvisionedThroughput = expandDynamoDbProvisionedThroughput(map[string]interface{}{
"read_capacity": v_read,
"write_capacity": v_write,
})
}
_, err := conn.UpdateTable(req)
if err != nil {
return fmt.Errorf("Error updating DynamoDB Table (%s) billing mode: %s", d.Id(), err)
}
if err := waitForDynamoDbTableToBeActive(d.Id(), d.Timeout(schema.TimeoutUpdate), conn); err != nil {
return fmt.Errorf("Error waiting for DynamoDB Table update: %s", err)
}
}
// Cannot create or delete index while updating table IOPS
// so we update IOPS separately
if (d.HasChange("read_capacity") || d.HasChange("write_capacity")) && !d.IsNewResource() {
if !d.HasChange("billing_mode") && d.Get("billing_mode").(string) == dynamodb.BillingModeProvisioned && (d.HasChange("read_capacity") || d.HasChange("write_capacity")) && !d.IsNewResource() {
_, err := conn.UpdateTable(&dynamodb.UpdateTableInput{
TableName: aws.String(d.Id()),
ProvisionedThroughput: expandDynamoDbProvisionedThroughput(map[string]interface{}{
Expand Down
79 changes: 79 additions & 0 deletions aws/resource_aws_dynamodb_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,33 @@ func TestAccAWSDynamoDbTable_enablePitr(t *testing.T) {
})
}

func TestAccAWSDynamoDbTable_BillingMode(t *testing.T) {
var conf dynamodb.DescribeTableOutput

rName := acctest.RandomWithPrefix("TerraformTestTable-")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSDynamoDbTableDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSDynamoDbConfigInitialState(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table", &conf),
testAccCheckInitialAWSDynamoDbTableConf("aws_dynamodb_table.basic-dynamodb-table"),
),
},
{
Config: testAccAWSDynamoDbBilling_PayPerRequest(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDynamoDbTableHasBilling_PayPerRequest("aws_dynamodb_table.basic-dynamodb-table"),
),
},
},
})
}

func TestAccAWSDynamoDbTable_streamSpecification(t *testing.T) {
var conf dynamodb.DescribeTableOutput

Expand Down Expand Up @@ -989,6 +1016,10 @@ func testAccCheckInitialAWSDynamoDbTableConf(n string) resource.TestCheckFunc {

log.Printf("[DEBUG] Checking on table %s", rs.Primary.ID)

if table.BillingModeSummary != nil && aws.StringValue(table.BillingModeSummary.BillingMode) != dynamodb.BillingModeProvisioned {
return fmt.Errorf("Billing Mode was %s, not %s!", aws.StringValue(table.BillingModeSummary.BillingMode), dynamodb.BillingModeProvisioned)
}

if *table.ProvisionedThroughput.WriteCapacityUnits != 2 {
return fmt.Errorf("Provisioned write capacity was %d, not 2!", table.ProvisionedThroughput.WriteCapacityUnits)
}
Expand Down Expand Up @@ -1066,6 +1097,39 @@ func testAccCheckDynamoDbTableHasPointInTimeRecoveryEnabled(n string) resource.T
}
}

func testAccCheckDynamoDbTableHasBilling_PayPerRequest(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No DynamoDB table name specified!")
}

conn := testAccProvider.Meta().(*AWSClient).dynamodbconn
params := &dynamodb.DescribeTableInput{
TableName: aws.String(rs.Primary.ID),
}
resp, err := conn.DescribeTable(params)

if err != nil {
return err
}
table := resp.Table

if table.BillingModeSummary == nil {
return fmt.Errorf("Billing Mode summary was empty, expected summary to exist and contain billing mode %s", dynamodb.BillingModePayPerRequest)
} else if aws.StringValue(table.BillingModeSummary.BillingMode) != dynamodb.BillingModePayPerRequest {
return fmt.Errorf("Billing Mode was %s, not %s!", aws.StringValue(table.BillingModeSummary.BillingMode), dynamodb.BillingModePayPerRequest)

}

return nil
}
}

func testAccCheckDynamoDbTableWasUpdated(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down Expand Up @@ -1186,6 +1250,21 @@ resource "aws_dynamodb_table" "basic-dynamodb-table" {
`, rName)
}

func testAccAWSDynamoDbBilling_PayPerRequest(rName string) string {
return fmt.Sprintf(`
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "%s"
billing_mode = "PAY_PER_REQUEST"
hash_key = "TestTableHashKey"
attribute {
name = "TestTableHashKey"
type = "S"
}
}
`, rName)
}

func testAccAWSDynamoDbConfigInitialState(rName string) string {
return fmt.Sprintf(`
resource "aws_dynamodb_table" "basic-dynamodb-table" {
Expand Down
5 changes: 5 additions & 0 deletions aws/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -4154,6 +4154,11 @@ func flattenDynamoDbPitr(pitrDesc *dynamodb.DescribeContinuousBackupsOutput) []i
}

func flattenAwsDynamoDbTableResource(d *schema.ResourceData, table *dynamodb.TableDescription) error {
d.Set("billing_mode", dynamodb.BillingModeProvisioned)
if table.BillingModeSummary != nil {
d.Set("billing_mode", table.BillingModeSummary.BillingMode)
}

d.Set("write_capacity", table.ProvisionedThroughput.WriteCapacityUnits)
d.Set("read_capacity", table.ProvisionedThroughput.ReadCapacityUnits)

Expand Down
6 changes: 4 additions & 2 deletions website/docs/r/dynamodb_table.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ in the [AWS SDK example documentation](https://docs.aws.amazon.com/amazondynamod
```hcl
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "GameScores"
billing_mode = "PROVISIONED"
read_capacity = 20
write_capacity = 20
hash_key = "UserId"
Expand Down Expand Up @@ -83,10 +84,11 @@ The following arguments are supported:

* `name` - (Required) The name of the table, this needs to be unique
within a region.
* `billing_mode` - (Optional) Controls how you are charged for read and write throughput and how you manage capacity. The valid values are `PROVISIONED` and `PAY_PER_REQUEST`. Defaults to `PROVISIONED`.
* `hash_key` - (Required, Forces new resource) The attribute to use as the hash (partition) key. Must also be defined as an `attribute`, see below.
* `range_key` - (Optional, Forces new resource) The attribute to use as the range (sort) key. Must also be defined as an `attribute`, see below.
* `write_capacity` - (Required) The number of write units for this table
* `read_capacity` - (Required) The number of read units for this table
* `write_capacity` - (Optional) The number of write units for this table. If the `billing_mode` is `PROVISIONED`, this field is required.
* `read_capacity` - (Optional) The number of read units for this table. If the `billing_mode` is `PROVISIONED`, this field is required.
* `attribute` - (Required) List of nested attribute definitions. Only required for `hash_key` and `range_key` attributes. Each attribute has two properties:
* `name` - (Required) The name of the attribute
* `type` - (Required) Attribute type, which must be a scalar type: `S`, `N`, or `B` for (S)tring, (N)umber or (B)inary data
Expand Down

0 comments on commit ee1f864

Please sign in to comment.