github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_dynamodb_table_test.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "testing" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/dynamodb" 11 "github.com/hashicorp/terraform/helper/acctest" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/terraform" 14 ) 15 16 func TestAccAWSDynamoDbTable_basic(t *testing.T) { 17 var conf dynamodb.DescribeTableOutput 18 19 rName := acctest.RandomWithPrefix("TerraformTestTable-") 20 21 resource.Test(t, resource.TestCase{ 22 PreCheck: func() { testAccPreCheck(t) }, 23 Providers: testAccProviders, 24 CheckDestroy: testAccCheckAWSDynamoDbTableDestroy, 25 Steps: []resource.TestStep{ 26 { 27 Config: testAccAWSDynamoDbConfigInitialState(rName), 28 Check: resource.ComposeTestCheckFunc( 29 testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table", &conf), 30 testAccCheckInitialAWSDynamoDbTableConf("aws_dynamodb_table.basic-dynamodb-table"), 31 ), 32 }, 33 { 34 Config: testAccAWSDynamoDbConfigAddSecondaryGSI(rName), 35 Check: resource.ComposeTestCheckFunc( 36 testAccCheckDynamoDbTableWasUpdated("aws_dynamodb_table.basic-dynamodb-table"), 37 ), 38 }, 39 }, 40 }) 41 } 42 43 func TestAccAWSDynamoDbTable_streamSpecification(t *testing.T) { 44 var conf dynamodb.DescribeTableOutput 45 46 resource.Test(t, resource.TestCase{ 47 PreCheck: func() { testAccPreCheck(t) }, 48 Providers: testAccProviders, 49 CheckDestroy: testAccCheckAWSDynamoDbTableDestroy, 50 Steps: []resource.TestStep{ 51 { 52 Config: testAccAWSDynamoDbConfigStreamSpecification(), 53 Check: resource.ComposeTestCheckFunc( 54 testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table", &conf), 55 testAccCheckInitialAWSDynamoDbTableConf("aws_dynamodb_table.basic-dynamodb-table"), 56 resource.TestCheckResourceAttr( 57 "aws_dynamodb_table.basic-dynamodb-table", "stream_enabled", "true"), 58 resource.TestCheckResourceAttr( 59 "aws_dynamodb_table.basic-dynamodb-table", "stream_view_type", "KEYS_ONLY"), 60 ), 61 }, 62 }, 63 }) 64 } 65 66 func TestAccAWSDynamoDbTable_tags(t *testing.T) { 67 var conf dynamodb.DescribeTableOutput 68 69 resource.Test(t, resource.TestCase{ 70 PreCheck: func() { testAccPreCheck(t) }, 71 Providers: testAccProviders, 72 CheckDestroy: testAccCheckAWSDynamoDbTableDestroy, 73 Steps: []resource.TestStep{ 74 { 75 Config: testAccAWSDynamoDbConfigTags(), 76 Check: resource.ComposeTestCheckFunc( 77 testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.basic-dynamodb-table", &conf), 78 testAccCheckInitialAWSDynamoDbTableConf("aws_dynamodb_table.basic-dynamodb-table"), 79 resource.TestCheckResourceAttr( 80 "aws_dynamodb_table.basic-dynamodb-table", "tags.%", "3"), 81 ), 82 }, 83 }, 84 }) 85 } 86 87 // https://github.com/hashicorp/terraform/issues/13243 88 func TestAccAWSDynamoDbTable_gsiUpdate(t *testing.T) { 89 var conf dynamodb.DescribeTableOutput 90 name := acctest.RandString(10) 91 92 resource.Test(t, resource.TestCase{ 93 PreCheck: func() { testAccPreCheck(t) }, 94 Providers: testAccProviders, 95 CheckDestroy: testAccCheckAWSDynamoDbTableDestroy, 96 Steps: []resource.TestStep{ 97 { 98 Config: testAccAWSDynamoDbConfigGsiUpdate(name), 99 Check: resource.ComposeTestCheckFunc( 100 testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.test", &conf), 101 ), 102 }, 103 { 104 Config: testAccAWSDynamoDbConfigGsiUpdated(name), 105 Check: resource.ComposeTestCheckFunc( 106 testAccCheckInitialAWSDynamoDbTableExists("aws_dynamodb_table.test", &conf), 107 ), 108 }, 109 }, 110 }) 111 } 112 113 func TestResourceAWSDynamoDbTableStreamViewType_validation(t *testing.T) { 114 cases := []struct { 115 Value string 116 ErrCount int 117 }{ 118 { 119 Value: "KEYS-ONLY", 120 ErrCount: 1, 121 }, 122 { 123 Value: "RANDOM-STRING", 124 ErrCount: 1, 125 }, 126 { 127 Value: "KEYS_ONLY", 128 ErrCount: 0, 129 }, 130 { 131 Value: "NEW_AND_OLD_IMAGES", 132 ErrCount: 0, 133 }, 134 { 135 Value: "NEW_IMAGE", 136 ErrCount: 0, 137 }, 138 { 139 Value: "OLD_IMAGE", 140 ErrCount: 0, 141 }, 142 } 143 144 for _, tc := range cases { 145 _, errors := validateStreamViewType(tc.Value, "aws_dynamodb_table_stream_view_type") 146 147 if len(errors) != tc.ErrCount { 148 t.Fatalf("Expected the DynamoDB stream_view_type to trigger a validation error") 149 } 150 } 151 } 152 153 func testAccCheckAWSDynamoDbTableDestroy(s *terraform.State) error { 154 conn := testAccProvider.Meta().(*AWSClient).dynamodbconn 155 156 for _, rs := range s.RootModule().Resources { 157 if rs.Type != "aws_dynamodb_table" { 158 continue 159 } 160 161 log.Printf("[DEBUG] Checking if DynamoDB table %s exists", rs.Primary.ID) 162 // Check if queue exists by checking for its attributes 163 params := &dynamodb.DescribeTableInput{ 164 TableName: aws.String(rs.Primary.ID), 165 } 166 167 _, err := conn.DescribeTable(params) 168 if err == nil { 169 return fmt.Errorf("DynamoDB table %s still exists. Failing!", rs.Primary.ID) 170 } 171 172 // Verify the error is what we want 173 if dbErr, ok := err.(awserr.Error); ok && dbErr.Code() == "ResourceNotFoundException" { 174 return nil 175 } 176 177 return err 178 } 179 180 return nil 181 } 182 183 func testAccCheckInitialAWSDynamoDbTableExists(n string, table *dynamodb.DescribeTableOutput) resource.TestCheckFunc { 184 return func(s *terraform.State) error { 185 log.Printf("[DEBUG] Trying to create initial table state!") 186 rs, ok := s.RootModule().Resources[n] 187 if !ok { 188 return fmt.Errorf("Not found: %s", n) 189 } 190 191 if rs.Primary.ID == "" { 192 return fmt.Errorf("No DynamoDB table name specified!") 193 } 194 195 conn := testAccProvider.Meta().(*AWSClient).dynamodbconn 196 197 params := &dynamodb.DescribeTableInput{ 198 TableName: aws.String(rs.Primary.ID), 199 } 200 201 resp, err := conn.DescribeTable(params) 202 203 if err != nil { 204 return fmt.Errorf("[ERROR] Problem describing table '%s': %s", rs.Primary.ID, err) 205 } 206 207 *table = *resp 208 209 return nil 210 } 211 } 212 213 func testAccCheckInitialAWSDynamoDbTableConf(n string) resource.TestCheckFunc { 214 return func(s *terraform.State) error { 215 log.Printf("[DEBUG] Trying to create initial table state!") 216 rs, ok := s.RootModule().Resources[n] 217 if !ok { 218 return fmt.Errorf("Not found: %s", n) 219 } 220 221 if rs.Primary.ID == "" { 222 return fmt.Errorf("No DynamoDB table name specified!") 223 } 224 225 conn := testAccProvider.Meta().(*AWSClient).dynamodbconn 226 227 params := &dynamodb.DescribeTableInput{ 228 TableName: aws.String(rs.Primary.ID), 229 } 230 231 resp, err := conn.DescribeTable(params) 232 233 if err != nil { 234 return fmt.Errorf("[ERROR] Problem describing table '%s': %s", rs.Primary.ID, err) 235 } 236 237 table := resp.Table 238 239 log.Printf("[DEBUG] Checking on table %s", rs.Primary.ID) 240 241 if *table.ProvisionedThroughput.WriteCapacityUnits != 20 { 242 return fmt.Errorf("Provisioned write capacity was %d, not 20!", table.ProvisionedThroughput.WriteCapacityUnits) 243 } 244 245 if *table.ProvisionedThroughput.ReadCapacityUnits != 10 { 246 return fmt.Errorf("Provisioned read capacity was %d, not 10!", table.ProvisionedThroughput.ReadCapacityUnits) 247 } 248 249 attrCount := len(table.AttributeDefinitions) 250 gsiCount := len(table.GlobalSecondaryIndexes) 251 lsiCount := len(table.LocalSecondaryIndexes) 252 253 if attrCount != 4 { 254 return fmt.Errorf("There were %d attributes, not 4 like there should have been!", attrCount) 255 } 256 257 if gsiCount != 1 { 258 return fmt.Errorf("There were %d GSIs, not 1 like there should have been!", gsiCount) 259 } 260 261 if lsiCount != 1 { 262 return fmt.Errorf("There were %d LSIs, not 1 like there should have been!", lsiCount) 263 } 264 265 attrmap := dynamoDbAttributesToMap(&table.AttributeDefinitions) 266 if attrmap["TestTableHashKey"] != "S" { 267 return fmt.Errorf("Test table hash key was of type %s instead of S!", attrmap["TestTableHashKey"]) 268 } 269 if attrmap["TestTableRangeKey"] != "S" { 270 return fmt.Errorf("Test table range key was of type %s instead of S!", attrmap["TestTableRangeKey"]) 271 } 272 if attrmap["TestLSIRangeKey"] != "N" { 273 return fmt.Errorf("Test table LSI range key was of type %s instead of N!", attrmap["TestLSIRangeKey"]) 274 } 275 if attrmap["TestGSIRangeKey"] != "S" { 276 return fmt.Errorf("Test table GSI range key was of type %s instead of S!", attrmap["TestGSIRangeKey"]) 277 } 278 279 return nil 280 } 281 } 282 283 func testAccCheckDynamoDbTableWasUpdated(n string) resource.TestCheckFunc { 284 return func(s *terraform.State) error { 285 rs, ok := s.RootModule().Resources[n] 286 if !ok { 287 return fmt.Errorf("Not found: %s", n) 288 } 289 290 if rs.Primary.ID == "" { 291 return fmt.Errorf("No DynamoDB table name specified!") 292 } 293 294 conn := testAccProvider.Meta().(*AWSClient).dynamodbconn 295 296 params := &dynamodb.DescribeTableInput{ 297 TableName: aws.String(rs.Primary.ID), 298 } 299 resp, err := conn.DescribeTable(params) 300 table := resp.Table 301 302 if err != nil { 303 return err 304 } 305 306 attrCount := len(table.AttributeDefinitions) 307 gsiCount := len(table.GlobalSecondaryIndexes) 308 lsiCount := len(table.LocalSecondaryIndexes) 309 310 if attrCount != 4 { 311 return fmt.Errorf("There were %d attributes, not 4 like there should have been!", attrCount) 312 } 313 314 if gsiCount != 1 { 315 return fmt.Errorf("There were %d GSIs, not 1 like there should have been!", gsiCount) 316 } 317 318 if lsiCount != 1 { 319 return fmt.Errorf("There were %d LSIs, not 1 like there should have been!", lsiCount) 320 } 321 322 if dynamoDbGetGSIIndex(&table.GlobalSecondaryIndexes, "ReplacementTestTableGSI") == -1 { 323 return fmt.Errorf("Could not find GSI named 'ReplacementTestTableGSI' in the table!") 324 } 325 326 if dynamoDbGetGSIIndex(&table.GlobalSecondaryIndexes, "InitialTestTableGSI") != -1 { 327 return fmt.Errorf("Should have removed 'InitialTestTableGSI' but it still exists!") 328 } 329 330 attrmap := dynamoDbAttributesToMap(&table.AttributeDefinitions) 331 if attrmap["TestTableHashKey"] != "S" { 332 return fmt.Errorf("Test table hash key was of type %s instead of S!", attrmap["TestTableHashKey"]) 333 } 334 if attrmap["TestTableRangeKey"] != "S" { 335 return fmt.Errorf("Test table range key was of type %s instead of S!", attrmap["TestTableRangeKey"]) 336 } 337 if attrmap["TestLSIRangeKey"] != "N" { 338 return fmt.Errorf("Test table LSI range key was of type %s instead of N!", attrmap["TestLSIRangeKey"]) 339 } 340 if attrmap["ReplacementGSIRangeKey"] != "N" { 341 return fmt.Errorf("Test table replacement GSI range key was of type %s instead of N!", attrmap["ReplacementGSIRangeKey"]) 342 } 343 344 return nil 345 } 346 } 347 348 func dynamoDbGetGSIIndex(gsiList *[]*dynamodb.GlobalSecondaryIndexDescription, target string) int { 349 for idx, gsiObject := range *gsiList { 350 if *gsiObject.IndexName == target { 351 return idx 352 } 353 } 354 355 return -1 356 } 357 358 func dynamoDbAttributesToMap(attributes *[]*dynamodb.AttributeDefinition) map[string]string { 359 attrmap := make(map[string]string) 360 361 for _, attrdef := range *attributes { 362 attrmap[*attrdef.AttributeName] = *attrdef.AttributeType 363 } 364 365 return attrmap 366 } 367 368 func testAccAWSDynamoDbConfigInitialState(rName string) string { 369 return fmt.Sprintf(` 370 resource "aws_dynamodb_table" "basic-dynamodb-table" { 371 name = "%s" 372 read_capacity = 10 373 write_capacity = 20 374 hash_key = "TestTableHashKey" 375 range_key = "TestTableRangeKey" 376 377 attribute { 378 name = "TestTableHashKey" 379 type = "S" 380 } 381 382 attribute { 383 name = "TestTableRangeKey" 384 type = "S" 385 } 386 387 attribute { 388 name = "TestLSIRangeKey" 389 type = "N" 390 } 391 392 attribute { 393 name = "TestGSIRangeKey" 394 type = "S" 395 } 396 397 local_secondary_index { 398 name = "TestTableLSI" 399 range_key = "TestLSIRangeKey" 400 projection_type = "ALL" 401 } 402 403 global_secondary_index { 404 name = "InitialTestTableGSI" 405 hash_key = "TestTableHashKey" 406 range_key = "TestGSIRangeKey" 407 write_capacity = 10 408 read_capacity = 10 409 projection_type = "KEYS_ONLY" 410 } 411 } 412 `, rName) 413 } 414 415 func testAccAWSDynamoDbConfigAddSecondaryGSI(rName string) string { 416 return fmt.Sprintf(` 417 resource "aws_dynamodb_table" "basic-dynamodb-table" { 418 name = "%s" 419 read_capacity = 20 420 write_capacity = 20 421 hash_key = "TestTableHashKey" 422 range_key = "TestTableRangeKey" 423 424 attribute { 425 name = "TestTableHashKey" 426 type = "S" 427 } 428 429 attribute { 430 name = "TestTableRangeKey" 431 type = "S" 432 } 433 434 attribute { 435 name = "TestLSIRangeKey" 436 type = "N" 437 } 438 439 attribute { 440 name = "ReplacementGSIRangeKey" 441 type = "N" 442 } 443 444 local_secondary_index { 445 name = "TestTableLSI" 446 range_key = "TestLSIRangeKey" 447 projection_type = "ALL" 448 } 449 450 global_secondary_index { 451 name = "ReplacementTestTableGSI" 452 hash_key = "TestTableHashKey" 453 range_key = "ReplacementGSIRangeKey" 454 write_capacity = 5 455 read_capacity = 5 456 projection_type = "INCLUDE" 457 non_key_attributes = ["TestNonKeyAttribute"] 458 } 459 }`, rName) 460 } 461 462 func testAccAWSDynamoDbConfigStreamSpecification() string { 463 return fmt.Sprintf(` 464 resource "aws_dynamodb_table" "basic-dynamodb-table" { 465 name = "TerraformTestStreamTable-%d" 466 read_capacity = 10 467 write_capacity = 20 468 hash_key = "TestTableHashKey" 469 range_key = "TestTableRangeKey" 470 471 attribute { 472 name = "TestTableHashKey" 473 type = "S" 474 } 475 476 attribute { 477 name = "TestTableRangeKey" 478 type = "S" 479 } 480 481 attribute { 482 name = "TestLSIRangeKey" 483 type = "N" 484 } 485 486 attribute { 487 name = "TestGSIRangeKey" 488 type = "S" 489 } 490 491 local_secondary_index { 492 name = "TestTableLSI" 493 range_key = "TestLSIRangeKey" 494 projection_type = "ALL" 495 } 496 497 global_secondary_index { 498 name = "InitialTestTableGSI" 499 hash_key = "TestTableHashKey" 500 range_key = "TestGSIRangeKey" 501 write_capacity = 10 502 read_capacity = 10 503 projection_type = "KEYS_ONLY" 504 } 505 stream_enabled = true 506 stream_view_type = "KEYS_ONLY" 507 } 508 `, acctest.RandInt()) 509 } 510 511 func testAccAWSDynamoDbConfigTags() string { 512 return fmt.Sprintf(` 513 resource "aws_dynamodb_table" "basic-dynamodb-table" { 514 name = "TerraformTestTable-%d" 515 read_capacity = 10 516 write_capacity = 20 517 hash_key = "TestTableHashKey" 518 range_key = "TestTableRangeKey" 519 520 attribute { 521 name = "TestTableHashKey" 522 type = "S" 523 } 524 525 attribute { 526 name = "TestTableRangeKey" 527 type = "S" 528 } 529 530 attribute { 531 name = "TestLSIRangeKey" 532 type = "N" 533 } 534 535 attribute { 536 name = "TestGSIRangeKey" 537 type = "S" 538 } 539 540 local_secondary_index { 541 name = "TestTableLSI" 542 range_key = "TestLSIRangeKey" 543 projection_type = "ALL" 544 } 545 546 global_secondary_index { 547 name = "InitialTestTableGSI" 548 hash_key = "TestTableHashKey" 549 range_key = "TestGSIRangeKey" 550 write_capacity = 10 551 read_capacity = 10 552 projection_type = "KEYS_ONLY" 553 } 554 555 tags { 556 Name = "terraform-test-table-%d" 557 AccTest = "yes" 558 Testing = "absolutely" 559 } 560 } 561 `, acctest.RandInt(), acctest.RandInt()) 562 } 563 564 func testAccAWSDynamoDbConfigGsiUpdate(name string) string { 565 return fmt.Sprintf(` 566 variable "capacity" { 567 default = 10 568 } 569 570 resource "aws_dynamodb_table" "test" { 571 name = "tf-acc-test-%s" 572 read_capacity = "${var.capacity}" 573 write_capacity = "${var.capacity}" 574 hash_key = "id" 575 576 attribute { 577 name = "id" 578 type = "S" 579 } 580 581 attribute { 582 name = "att1" 583 type = "S" 584 } 585 586 attribute { 587 name = "att2" 588 type = "S" 589 } 590 591 attribute { 592 name = "att3" 593 type = "S" 594 } 595 596 global_secondary_index { 597 name = "att1-index" 598 hash_key = "att1" 599 write_capacity = "${var.capacity}" 600 read_capacity = "${var.capacity}" 601 projection_type = "ALL" 602 } 603 604 global_secondary_index { 605 name = "att2-index" 606 hash_key = "att2" 607 write_capacity = "${var.capacity}" 608 read_capacity = "${var.capacity}" 609 projection_type = "ALL" 610 } 611 612 global_secondary_index { 613 name = "att3-index" 614 hash_key = "att3" 615 write_capacity = "${var.capacity}" 616 read_capacity = "${var.capacity}" 617 projection_type = "ALL" 618 } 619 } 620 `, name) 621 } 622 623 func testAccAWSDynamoDbConfigGsiUpdated(name string) string { 624 return fmt.Sprintf(` 625 variable "capacity" { 626 default = 20 627 } 628 629 resource "aws_dynamodb_table" "test" { 630 name = "tf-acc-test-%s" 631 read_capacity = "${var.capacity}" 632 write_capacity = "${var.capacity}" 633 hash_key = "id" 634 635 attribute { 636 name = "id" 637 type = "S" 638 } 639 640 attribute { 641 name = "att1" 642 type = "S" 643 } 644 645 attribute { 646 name = "att2" 647 type = "S" 648 } 649 650 attribute { 651 name = "att3" 652 type = "S" 653 } 654 655 global_secondary_index { 656 name = "att1-index" 657 hash_key = "att1" 658 write_capacity = "${var.capacity}" 659 read_capacity = "${var.capacity}" 660 projection_type = "ALL" 661 } 662 663 global_secondary_index { 664 name = "att2-index" 665 hash_key = "att2" 666 write_capacity = "${var.capacity}" 667 read_capacity = "${var.capacity}" 668 projection_type = "ALL" 669 } 670 671 global_secondary_index { 672 name = "att3-index" 673 hash_key = "att3" 674 write_capacity = "${var.capacity}" 675 read_capacity = "${var.capacity}" 676 projection_type = "ALL" 677 } 678 } 679 `, name) 680 }