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