github.com/anuaimi/terraform@v0.6.4-0.20150904235404-2bf9aec61da8/builtin/providers/aws/resource_aws_dynamodb_table.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/schema" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/aws/awserr" 13 "github.com/aws/aws-sdk-go/service/dynamodb" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 ) 16 17 // Number of times to retry if a throttling-related exception occurs 18 const DYNAMODB_MAX_THROTTLE_RETRIES = 5 19 20 // How long to sleep when a throttle-event happens 21 const DYNAMODB_THROTTLE_SLEEP = 5 * time.Second 22 23 // How long to sleep if a limit-exceeded event happens 24 const DYNAMODB_LIMIT_EXCEEDED_SLEEP = 10 * time.Second 25 26 // A number of these are marked as computed because if you don't 27 // provide a value, DynamoDB will provide you with defaults (which are the 28 // default values specified below) 29 func resourceAwsDynamoDbTable() *schema.Resource { 30 return &schema.Resource{ 31 Create: resourceAwsDynamoDbTableCreate, 32 Read: resourceAwsDynamoDbTableRead, 33 Update: resourceAwsDynamoDbTableUpdate, 34 Delete: resourceAwsDynamoDbTableDelete, 35 36 Schema: map[string]*schema.Schema{ 37 "arn": &schema.Schema{ 38 Type: schema.TypeString, 39 Computed: true, 40 }, 41 "name": &schema.Schema{ 42 Type: schema.TypeString, 43 Required: true, 44 ForceNew: true, 45 }, 46 "hash_key": &schema.Schema{ 47 Type: schema.TypeString, 48 Required: true, 49 }, 50 "range_key": &schema.Schema{ 51 Type: schema.TypeString, 52 Optional: true, 53 }, 54 "write_capacity": &schema.Schema{ 55 Type: schema.TypeInt, 56 Required: true, 57 }, 58 "read_capacity": &schema.Schema{ 59 Type: schema.TypeInt, 60 Required: true, 61 }, 62 "attribute": &schema.Schema{ 63 Type: schema.TypeSet, 64 Required: true, 65 Elem: &schema.Resource{ 66 Schema: map[string]*schema.Schema{ 67 "name": &schema.Schema{ 68 Type: schema.TypeString, 69 Required: true, 70 }, 71 "type": &schema.Schema{ 72 Type: schema.TypeString, 73 Required: true, 74 }, 75 }, 76 }, 77 Set: func(v interface{}) int { 78 var buf bytes.Buffer 79 m := v.(map[string]interface{}) 80 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 81 return hashcode.String(buf.String()) 82 }, 83 }, 84 "local_secondary_index": &schema.Schema{ 85 Type: schema.TypeSet, 86 Optional: true, 87 Elem: &schema.Resource{ 88 Schema: map[string]*schema.Schema{ 89 "name": &schema.Schema{ 90 Type: schema.TypeString, 91 Required: true, 92 }, 93 "range_key": &schema.Schema{ 94 Type: schema.TypeString, 95 Required: true, 96 }, 97 "projection_type": &schema.Schema{ 98 Type: schema.TypeString, 99 Required: true, 100 }, 101 "non_key_attributes": &schema.Schema{ 102 Type: schema.TypeList, 103 Optional: true, 104 Elem: &schema.Schema{Type: schema.TypeString}, 105 }, 106 }, 107 }, 108 Set: func(v interface{}) int { 109 var buf bytes.Buffer 110 m := v.(map[string]interface{}) 111 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 112 return hashcode.String(buf.String()) 113 }, 114 }, 115 "global_secondary_index": &schema.Schema{ 116 Type: schema.TypeSet, 117 Optional: true, 118 Elem: &schema.Resource{ 119 Schema: map[string]*schema.Schema{ 120 "name": &schema.Schema{ 121 Type: schema.TypeString, 122 Required: true, 123 }, 124 "write_capacity": &schema.Schema{ 125 Type: schema.TypeInt, 126 Required: true, 127 }, 128 "read_capacity": &schema.Schema{ 129 Type: schema.TypeInt, 130 Required: true, 131 }, 132 "hash_key": &schema.Schema{ 133 Type: schema.TypeString, 134 Required: true, 135 }, 136 "range_key": &schema.Schema{ 137 Type: schema.TypeString, 138 Optional: true, 139 }, 140 "projection_type": &schema.Schema{ 141 Type: schema.TypeString, 142 Required: true, 143 }, 144 "non_key_attributes": &schema.Schema{ 145 Type: schema.TypeList, 146 Optional: true, 147 Elem: &schema.Schema{Type: schema.TypeString}, 148 }, 149 }, 150 }, 151 // GSI names are the uniqueness constraint 152 Set: func(v interface{}) int { 153 var buf bytes.Buffer 154 m := v.(map[string]interface{}) 155 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 156 buf.WriteString(fmt.Sprintf("%d-", m["write_capacity"].(int))) 157 buf.WriteString(fmt.Sprintf("%d-", m["read_capacity"].(int))) 158 return hashcode.String(buf.String()) 159 }, 160 }, 161 }, 162 } 163 } 164 165 func resourceAwsDynamoDbTableCreate(d *schema.ResourceData, meta interface{}) error { 166 dynamodbconn := meta.(*AWSClient).dynamodbconn 167 168 name := d.Get("name").(string) 169 170 log.Printf("[DEBUG] DynamoDB table create: %s", name) 171 172 throughput := &dynamodb.ProvisionedThroughput{ 173 ReadCapacityUnits: aws.Int64(int64(d.Get("read_capacity").(int))), 174 WriteCapacityUnits: aws.Int64(int64(d.Get("write_capacity").(int))), 175 } 176 177 hash_key_name := d.Get("hash_key").(string) 178 keyschema := []*dynamodb.KeySchemaElement{ 179 &dynamodb.KeySchemaElement{ 180 AttributeName: aws.String(hash_key_name), 181 KeyType: aws.String("HASH"), 182 }, 183 } 184 185 if range_key, ok := d.GetOk("range_key"); ok { 186 range_schema_element := &dynamodb.KeySchemaElement{ 187 AttributeName: aws.String(range_key.(string)), 188 KeyType: aws.String("RANGE"), 189 } 190 keyschema = append(keyschema, range_schema_element) 191 } 192 193 req := &dynamodb.CreateTableInput{ 194 TableName: aws.String(name), 195 ProvisionedThroughput: throughput, 196 KeySchema: keyschema, 197 } 198 199 if attributedata, ok := d.GetOk("attribute"); ok { 200 attributes := []*dynamodb.AttributeDefinition{} 201 attributeSet := attributedata.(*schema.Set) 202 for _, attribute := range attributeSet.List() { 203 attr := attribute.(map[string]interface{}) 204 attributes = append(attributes, &dynamodb.AttributeDefinition{ 205 AttributeName: aws.String(attr["name"].(string)), 206 AttributeType: aws.String(attr["type"].(string)), 207 }) 208 } 209 210 req.AttributeDefinitions = attributes 211 } 212 213 if lsidata, ok := d.GetOk("local_secondary_index"); ok { 214 fmt.Printf("[DEBUG] Adding LSI data to the table") 215 216 lsiSet := lsidata.(*schema.Set) 217 localSecondaryIndexes := []*dynamodb.LocalSecondaryIndex{} 218 for _, lsiObject := range lsiSet.List() { 219 lsi := lsiObject.(map[string]interface{}) 220 221 projection := &dynamodb.Projection{ 222 ProjectionType: aws.String(lsi["projection_type"].(string)), 223 } 224 225 if lsi["projection_type"] == "INCLUDE" { 226 non_key_attributes := []*string{} 227 for _, attr := range lsi["non_key_attributes"].([]interface{}) { 228 non_key_attributes = append(non_key_attributes, aws.String(attr.(string))) 229 } 230 projection.NonKeyAttributes = non_key_attributes 231 } 232 233 localSecondaryIndexes = append(localSecondaryIndexes, &dynamodb.LocalSecondaryIndex{ 234 IndexName: aws.String(lsi["name"].(string)), 235 KeySchema: []*dynamodb.KeySchemaElement{ 236 &dynamodb.KeySchemaElement{ 237 AttributeName: aws.String(hash_key_name), 238 KeyType: aws.String("HASH"), 239 }, 240 &dynamodb.KeySchemaElement{ 241 AttributeName: aws.String(lsi["range_key"].(string)), 242 KeyType: aws.String("RANGE"), 243 }, 244 }, 245 Projection: projection, 246 }) 247 } 248 249 req.LocalSecondaryIndexes = localSecondaryIndexes 250 251 fmt.Printf("[DEBUG] Added %d LSI definitions", len(localSecondaryIndexes)) 252 } 253 254 if gsidata, ok := d.GetOk("global_secondary_index"); ok { 255 globalSecondaryIndexes := []*dynamodb.GlobalSecondaryIndex{} 256 257 gsiSet := gsidata.(*schema.Set) 258 for _, gsiObject := range gsiSet.List() { 259 gsi := gsiObject.(map[string]interface{}) 260 gsiObject := createGSIFromData(&gsi) 261 globalSecondaryIndexes = append(globalSecondaryIndexes, &gsiObject) 262 } 263 req.GlobalSecondaryIndexes = globalSecondaryIndexes 264 } 265 266 attemptCount := 1 267 for attemptCount <= DYNAMODB_MAX_THROTTLE_RETRIES { 268 output, err := dynamodbconn.CreateTable(req) 269 if err != nil { 270 if awsErr, ok := err.(awserr.Error); ok { 271 if awsErr.Code() == "ThrottlingException" { 272 log.Printf("[DEBUG] Attempt %d/%d: Sleeping for a bit to throttle back create request", attemptCount, DYNAMODB_MAX_THROTTLE_RETRIES) 273 time.Sleep(DYNAMODB_THROTTLE_SLEEP) 274 attemptCount += 1 275 } else if awsErr.Code() == "LimitExceededException" { 276 log.Printf("[DEBUG] Limit on concurrent table creations hit, sleeping for a bit") 277 time.Sleep(DYNAMODB_LIMIT_EXCEEDED_SLEEP) 278 attemptCount += 1 279 } else { 280 // Some other non-retryable exception occurred 281 return fmt.Errorf("AWS Error creating DynamoDB table: %s", err) 282 } 283 } else { 284 // Non-AWS exception occurred, give up 285 return fmt.Errorf("Error creating DynamoDB table: %s", err) 286 } 287 } else { 288 // No error, set ID and return 289 d.SetId(*output.TableDescription.TableName) 290 return nil 291 } 292 } 293 294 // Too many throttling events occurred, give up 295 return fmt.Errorf("Unable to create DynamoDB table '%s' after %d attempts", name, attemptCount) 296 } 297 298 func resourceAwsDynamoDbTableUpdate(d *schema.ResourceData, meta interface{}) error { 299 300 log.Printf("[DEBUG] Updating DynamoDB table %s", d.Id()) 301 dynamodbconn := meta.(*AWSClient).dynamodbconn 302 303 // Ensure table is active before trying to update 304 waitForTableToBeActive(d.Id(), meta) 305 306 // LSI can only be done at create-time, abort if it's been changed 307 if d.HasChange("local_secondary_index") { 308 return fmt.Errorf("Local secondary indexes can only be built at creation, you cannot update them!") 309 } 310 311 if d.HasChange("hash_key") { 312 return fmt.Errorf("Hash key can only be specified at creation, you cannot modify it.") 313 } 314 315 if d.HasChange("range_key") { 316 return fmt.Errorf("Range key can only be specified at creation, you cannot modify it.") 317 } 318 319 if d.HasChange("read_capacity") || d.HasChange("write_capacity") { 320 req := &dynamodb.UpdateTableInput{ 321 TableName: aws.String(d.Id()), 322 } 323 324 throughput := &dynamodb.ProvisionedThroughput{ 325 ReadCapacityUnits: aws.Int64(int64(d.Get("read_capacity").(int))), 326 WriteCapacityUnits: aws.Int64(int64(d.Get("write_capacity").(int))), 327 } 328 req.ProvisionedThroughput = throughput 329 330 _, err := dynamodbconn.UpdateTable(req) 331 332 if err != nil { 333 return err 334 } 335 336 waitForTableToBeActive(d.Id(), meta) 337 } 338 339 if d.HasChange("global_secondary_index") { 340 log.Printf("[DEBUG] Changed GSI data") 341 req := &dynamodb.UpdateTableInput{ 342 TableName: aws.String(d.Id()), 343 } 344 345 o, n := d.GetChange("global_secondary_index") 346 347 oldSet := o.(*schema.Set) 348 newSet := n.(*schema.Set) 349 350 // Track old names so we can know which ones we need to just update based on 351 // capacity changes, terraform appears to only diff on the set hash, not the 352 // contents so we need to make sure we don't delete any indexes that we 353 // just want to update the capacity for 354 oldGsiNameSet := make(map[string]bool) 355 newGsiNameSet := make(map[string]bool) 356 357 for _, gsidata := range oldSet.List() { 358 gsiName := gsidata.(map[string]interface{})["name"].(string) 359 oldGsiNameSet[gsiName] = true 360 } 361 362 for _, gsidata := range newSet.List() { 363 gsiName := gsidata.(map[string]interface{})["name"].(string) 364 newGsiNameSet[gsiName] = true 365 } 366 367 // First determine what's new 368 for _, newgsidata := range newSet.List() { 369 updates := []*dynamodb.GlobalSecondaryIndexUpdate{} 370 newGsiName := newgsidata.(map[string]interface{})["name"].(string) 371 if _, exists := oldGsiNameSet[newGsiName]; !exists { 372 attributes := []*dynamodb.AttributeDefinition{} 373 gsidata := newgsidata.(map[string]interface{}) 374 gsi := createGSIFromData(&gsidata) 375 log.Printf("[DEBUG] Adding GSI %s", *gsi.IndexName) 376 update := &dynamodb.GlobalSecondaryIndexUpdate{ 377 Create: &dynamodb.CreateGlobalSecondaryIndexAction{ 378 IndexName: gsi.IndexName, 379 KeySchema: gsi.KeySchema, 380 ProvisionedThroughput: gsi.ProvisionedThroughput, 381 Projection: gsi.Projection, 382 }, 383 } 384 updates = append(updates, update) 385 386 // Hash key is required, range key isn't 387 hashkey_type, err := getAttributeType(d, *(gsi.KeySchema[0].AttributeName)) 388 if err != nil { 389 return err 390 } 391 392 attributes = append(attributes, &dynamodb.AttributeDefinition{ 393 AttributeName: gsi.KeySchema[0].AttributeName, 394 AttributeType: aws.String(hashkey_type), 395 }) 396 397 // If there's a range key, there will be 2 elements in KeySchema 398 if len(gsi.KeySchema) == 2 { 399 rangekey_type, err := getAttributeType(d, *(gsi.KeySchema[1].AttributeName)) 400 if err != nil { 401 return err 402 } 403 404 attributes = append(attributes, &dynamodb.AttributeDefinition{ 405 AttributeName: gsi.KeySchema[1].AttributeName, 406 AttributeType: aws.String(rangekey_type), 407 }) 408 } 409 410 req.AttributeDefinitions = attributes 411 req.GlobalSecondaryIndexUpdates = updates 412 _, err = dynamodbconn.UpdateTable(req) 413 414 if err != nil { 415 return err 416 } 417 418 waitForTableToBeActive(d.Id(), meta) 419 waitForGSIToBeActive(d.Id(), *gsi.IndexName, meta) 420 421 } 422 } 423 424 for _, oldgsidata := range oldSet.List() { 425 updates := []*dynamodb.GlobalSecondaryIndexUpdate{} 426 oldGsiName := oldgsidata.(map[string]interface{})["name"].(string) 427 if _, exists := newGsiNameSet[oldGsiName]; !exists { 428 gsidata := oldgsidata.(map[string]interface{}) 429 log.Printf("[DEBUG] Deleting GSI %s", gsidata["name"].(string)) 430 update := &dynamodb.GlobalSecondaryIndexUpdate{ 431 Delete: &dynamodb.DeleteGlobalSecondaryIndexAction{ 432 IndexName: aws.String(gsidata["name"].(string)), 433 }, 434 } 435 updates = append(updates, update) 436 437 req.GlobalSecondaryIndexUpdates = updates 438 _, err := dynamodbconn.UpdateTable(req) 439 440 if err != nil { 441 return err 442 } 443 444 waitForTableToBeActive(d.Id(), meta) 445 } 446 } 447 } 448 449 // Update any out-of-date read / write capacity 450 if gsiObjects, ok := d.GetOk("global_secondary_index"); ok { 451 gsiSet := gsiObjects.(*schema.Set) 452 if len(gsiSet.List()) > 0 { 453 log.Printf("Updating capacity as needed!") 454 455 // We can only change throughput, but we need to make sure it's actually changed 456 tableDescription, err := dynamodbconn.DescribeTable(&dynamodb.DescribeTableInput{ 457 TableName: aws.String(d.Id()), 458 }) 459 460 if err != nil { 461 return err 462 } 463 464 table := tableDescription.Table 465 466 updates := []*dynamodb.GlobalSecondaryIndexUpdate{} 467 468 for _, updatedgsidata := range gsiSet.List() { 469 gsidata := updatedgsidata.(map[string]interface{}) 470 gsiName := gsidata["name"].(string) 471 gsiWriteCapacity := gsidata["write_capacity"].(int) 472 gsiReadCapacity := gsidata["read_capacity"].(int) 473 474 log.Printf("[DEBUG] Updating GSI %s", gsiName) 475 gsi, err := getGlobalSecondaryIndex(gsiName, table.GlobalSecondaryIndexes) 476 477 if err != nil { 478 return err 479 } 480 481 capacityUpdated := false 482 483 if int64(gsiReadCapacity) != *(gsi.ProvisionedThroughput.ReadCapacityUnits) || 484 int64(gsiWriteCapacity) != *(gsi.ProvisionedThroughput.WriteCapacityUnits) { 485 capacityUpdated = true 486 } 487 488 if capacityUpdated { 489 update := &dynamodb.GlobalSecondaryIndexUpdate{ 490 Update: &dynamodb.UpdateGlobalSecondaryIndexAction{ 491 IndexName: aws.String(gsidata["name"].(string)), 492 ProvisionedThroughput: &dynamodb.ProvisionedThroughput{ 493 WriteCapacityUnits: aws.Int64(int64(gsiWriteCapacity)), 494 ReadCapacityUnits: aws.Int64(int64(gsiReadCapacity)), 495 }, 496 }, 497 } 498 updates = append(updates, update) 499 500 } 501 502 if len(updates) > 0 { 503 504 req := &dynamodb.UpdateTableInput{ 505 TableName: aws.String(d.Id()), 506 } 507 508 req.GlobalSecondaryIndexUpdates = updates 509 510 log.Printf("[DEBUG] Updating GSI read / write capacity on %s", d.Id()) 511 _, err := dynamodbconn.UpdateTable(req) 512 513 if err != nil { 514 log.Printf("[DEBUG] Error updating table: %s", err) 515 return err 516 } 517 } 518 } 519 } 520 521 } 522 523 return resourceAwsDynamoDbTableRead(d, meta) 524 } 525 526 func resourceAwsDynamoDbTableRead(d *schema.ResourceData, meta interface{}) error { 527 dynamodbconn := meta.(*AWSClient).dynamodbconn 528 log.Printf("[DEBUG] Loading data for DynamoDB table '%s'", d.Id()) 529 req := &dynamodb.DescribeTableInput{ 530 TableName: aws.String(d.Id()), 531 } 532 533 result, err := dynamodbconn.DescribeTable(req) 534 535 if err != nil { 536 return err 537 } 538 539 table := result.Table 540 541 d.Set("write_capacity", table.ProvisionedThroughput.WriteCapacityUnits) 542 d.Set("read_capacity", table.ProvisionedThroughput.ReadCapacityUnits) 543 544 attributes := []interface{}{} 545 for _, attrdef := range table.AttributeDefinitions { 546 attribute := map[string]string{ 547 "name": *(attrdef.AttributeName), 548 "type": *(attrdef.AttributeType), 549 } 550 attributes = append(attributes, attribute) 551 log.Printf("[DEBUG] Added Attribute: %s", attribute["name"]) 552 } 553 554 d.Set("attribute", attributes) 555 556 gsiList := make([]map[string]interface{}, 0, len(table.GlobalSecondaryIndexes)) 557 for _, gsiObject := range table.GlobalSecondaryIndexes { 558 gsi := map[string]interface{}{ 559 "write_capacity": *(gsiObject.ProvisionedThroughput.WriteCapacityUnits), 560 "read_capacity": *(gsiObject.ProvisionedThroughput.ReadCapacityUnits), 561 "name": *(gsiObject.IndexName), 562 } 563 564 for _, attribute := range gsiObject.KeySchema { 565 if *attribute.KeyType == "HASH" { 566 gsi["hash_key"] = *attribute.AttributeName 567 } 568 569 if *attribute.KeyType == "RANGE" { 570 gsi["range_key"] = *attribute.AttributeName 571 } 572 } 573 574 gsi["projection_type"] = *(gsiObject.Projection.ProjectionType) 575 gsi["non_key_attributes"] = gsiObject.Projection.NonKeyAttributes 576 577 gsiList = append(gsiList, gsi) 578 log.Printf("[DEBUG] Added GSI: %s - Read: %d / Write: %d", gsi["name"], gsi["read_capacity"], gsi["write_capacity"]) 579 } 580 581 d.Set("global_secondary_index", gsiList) 582 d.Set("arn", table.TableArn) 583 584 return nil 585 } 586 587 func resourceAwsDynamoDbTableDelete(d *schema.ResourceData, meta interface{}) error { 588 dynamodbconn := meta.(*AWSClient).dynamodbconn 589 590 waitForTableToBeActive(d.Id(), meta) 591 592 log.Printf("[DEBUG] DynamoDB delete table: %s", d.Id()) 593 594 _, err := dynamodbconn.DeleteTable(&dynamodb.DeleteTableInput{ 595 TableName: aws.String(d.Id()), 596 }) 597 if err != nil { 598 return err 599 } 600 return nil 601 } 602 603 func createGSIFromData(data *map[string]interface{}) dynamodb.GlobalSecondaryIndex { 604 605 projection := &dynamodb.Projection{ 606 ProjectionType: aws.String((*data)["projection_type"].(string)), 607 } 608 609 if (*data)["projection_type"] == "INCLUDE" { 610 non_key_attributes := []*string{} 611 for _, attr := range (*data)["non_key_attributes"].([]interface{}) { 612 non_key_attributes = append(non_key_attributes, aws.String(attr.(string))) 613 } 614 projection.NonKeyAttributes = non_key_attributes 615 } 616 617 writeCapacity := (*data)["write_capacity"].(int) 618 readCapacity := (*data)["read_capacity"].(int) 619 620 key_schema := []*dynamodb.KeySchemaElement{ 621 &dynamodb.KeySchemaElement{ 622 AttributeName: aws.String((*data)["hash_key"].(string)), 623 KeyType: aws.String("HASH"), 624 }, 625 } 626 627 range_key_name := (*data)["range_key"] 628 if range_key_name != "" { 629 range_key_element := &dynamodb.KeySchemaElement{ 630 AttributeName: aws.String(range_key_name.(string)), 631 KeyType: aws.String("RANGE"), 632 } 633 634 key_schema = append(key_schema, range_key_element) 635 } 636 637 return dynamodb.GlobalSecondaryIndex{ 638 IndexName: aws.String((*data)["name"].(string)), 639 KeySchema: key_schema, 640 Projection: projection, 641 ProvisionedThroughput: &dynamodb.ProvisionedThroughput{ 642 WriteCapacityUnits: aws.Int64(int64(writeCapacity)), 643 ReadCapacityUnits: aws.Int64(int64(readCapacity)), 644 }, 645 } 646 } 647 648 func getGlobalSecondaryIndex(indexName string, indexList []*dynamodb.GlobalSecondaryIndexDescription) (*dynamodb.GlobalSecondaryIndexDescription, error) { 649 for _, gsi := range indexList { 650 if *(gsi.IndexName) == indexName { 651 return gsi, nil 652 } 653 } 654 655 return &dynamodb.GlobalSecondaryIndexDescription{}, fmt.Errorf("Can't find a GSI by that name...") 656 } 657 658 func getAttributeType(d *schema.ResourceData, attributeName string) (string, error) { 659 if attributedata, ok := d.GetOk("attribute"); ok { 660 attributeSet := attributedata.(*schema.Set) 661 for _, attribute := range attributeSet.List() { 662 attr := attribute.(map[string]interface{}) 663 if attr["name"] == attributeName { 664 return attr["type"].(string), nil 665 } 666 } 667 } 668 669 return "", fmt.Errorf("Unable to find an attribute named %s", attributeName) 670 } 671 672 func waitForGSIToBeActive(tableName string, gsiName string, meta interface{}) error { 673 dynamodbconn := meta.(*AWSClient).dynamodbconn 674 req := &dynamodb.DescribeTableInput{ 675 TableName: aws.String(tableName), 676 } 677 678 activeIndex := false 679 680 for activeIndex == false { 681 682 result, err := dynamodbconn.DescribeTable(req) 683 684 if err != nil { 685 return err 686 } 687 688 table := result.Table 689 var targetGSI *dynamodb.GlobalSecondaryIndexDescription = nil 690 691 for _, gsi := range table.GlobalSecondaryIndexes { 692 if *gsi.IndexName == gsiName { 693 targetGSI = gsi 694 } 695 } 696 697 if targetGSI != nil { 698 activeIndex = *targetGSI.IndexStatus == "ACTIVE" 699 700 if !activeIndex { 701 log.Printf("[DEBUG] Sleeping for 5 seconds for %s GSI to become active", gsiName) 702 time.Sleep(5 * time.Second) 703 } 704 } else { 705 log.Printf("[DEBUG] GSI %s did not exist, giving up", gsiName) 706 break 707 } 708 } 709 710 return nil 711 712 } 713 714 func waitForTableToBeActive(tableName string, meta interface{}) error { 715 dynamodbconn := meta.(*AWSClient).dynamodbconn 716 req := &dynamodb.DescribeTableInput{ 717 TableName: aws.String(tableName), 718 } 719 720 activeState := false 721 722 for activeState == false { 723 result, err := dynamodbconn.DescribeTable(req) 724 725 if err != nil { 726 return err 727 } 728 729 activeState = *(result.Table.TableStatus) == "ACTIVE" 730 731 // Wait for a few seconds 732 if !activeState { 733 log.Printf("[DEBUG] Sleeping for 5 seconds for table to become active") 734 time.Sleep(5 * time.Second) 735 } 736 } 737 738 return nil 739 740 }