From 53b91630f5e0214b08327e5c65a4778c3f322655 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Thu, 15 Jun 2017 10:41:05 -0700 Subject: [PATCH 01/15] Bigtable instance support. --- google/client_factory_bigtable.go | 22 ++ google/config.go | 17 ++ google/provider.go | 1 + google/resource_bigtable_instance.go | 188 ++++++++++++++++++ google/resource_bigtable_instance_test.go | 118 +++++++++++ .../docs/r/bigtable_instance.html.markdown | 49 +++++ 6 files changed, 395 insertions(+) create mode 100644 google/client_factory_bigtable.go create mode 100644 google/resource_bigtable_instance.go create mode 100644 google/resource_bigtable_instance_test.go create mode 100644 website/docs/r/bigtable_instance.html.markdown diff --git a/google/client_factory_bigtable.go b/google/client_factory_bigtable.go new file mode 100644 index 00000000..f616788e --- /dev/null +++ b/google/client_factory_bigtable.go @@ -0,0 +1,22 @@ +package google + +import ( + "context" + + "cloud.google.com/go/bigtable" + "golang.org/x/oauth2" + "google.golang.org/api/option" +) + +type ClientFactoryBigtable struct { + UserAgent string + TokenSource oauth2.TokenSource +} + +func (s *ClientFactoryBigtable) NewInstanceAdminClient(project string) (*bigtable.InstanceAdminClient, error) { + return bigtable.NewInstanceAdminClient(context.Background(), project, option.WithTokenSource(s.TokenSource), option.WithUserAgent(s.UserAgent)) +} + +func (s *ClientFactoryBigtable) NewAdminClient(project, instance string) (*bigtable.AdminClient, error) { + return bigtable.NewAdminClient(context.Background(), project, instance, option.WithTokenSource(s.TokenSource), option.WithUserAgent(s.UserAgent)) +} diff --git a/google/config.go b/google/config.go index 71629644..37897998 100644 --- a/google/config.go +++ b/google/config.go @@ -1,6 +1,7 @@ package google import ( + "context" "encoding/json" "fmt" "log" @@ -11,6 +12,7 @@ import ( "github.com/hashicorp/terraform/helper/logging" "github.com/hashicorp/terraform/helper/pathorcontents" "github.com/hashicorp/terraform/terraform" + "golang.org/x/oauth2" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" @@ -45,6 +47,8 @@ type Config struct { clientIAM *iam.Service clientServiceMan *servicemanagement.APIService clientBigQuery *bigquery.Service + + clientFactoryBigtable *ClientFactoryBigtable } func (c *Config) loadAndValidate() error { @@ -57,6 +61,7 @@ func (c *Config) loadAndValidate() error { } var client *http.Client + var tokenSource oauth2.TokenSource if c.Credentials != "" { contents, _, err := pathorcontents.Read(c.Credentials) @@ -87,6 +92,7 @@ func (c *Config) loadAndValidate() error { // your service account. client = conf.Client(oauth2.NoContext) + tokenSource = conf.TokenSource(context.Background()) } else { log.Printf("[INFO] Authenticating using DefaultClient") err := error(nil) @@ -94,6 +100,11 @@ func (c *Config) loadAndValidate() error { if err != nil { return err } + + tokenSource, err = google.DefaultTokenSource(oauth2.NoContext, clientScopes...) + if err != nil { + return err + } } client.Transport = logging.NewTransport("Google", client.Transport) @@ -181,6 +192,12 @@ func (c *Config) loadAndValidate() error { } c.clientBigQuery.UserAgent = userAgent + log.Printf("[INFO] Instantiating Google Cloud Bigtable Client Factory...") + c.clientFactoryBigtable = &ClientFactoryBigtable{ + UserAgent: userAgent, + TokenSource: tokenSource, + } + return nil } diff --git a/google/provider.go b/google/provider.go index 6c08fd11..806aa2fb 100644 --- a/google/provider.go +++ b/google/provider.go @@ -64,6 +64,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "google_bigquery_dataset": resourceBigQueryDataset(), "google_bigquery_table": resourceBigQueryTable(), + "google_bigtable_instance": resourceBigtableInstance(), "google_compute_autoscaler": resourceComputeAutoscaler(), "google_compute_address": resourceComputeAddress(), "google_compute_backend_bucket": resourceComputeBackendBucket(), diff --git a/google/resource_bigtable_instance.go b/google/resource_bigtable_instance.go new file mode 100644 index 00000000..3fc256c5 --- /dev/null +++ b/google/resource_bigtable_instance.go @@ -0,0 +1,188 @@ +package google + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + + "cloud.google.com/go/bigtable" + "golang.org/x/net/context" +) + +func resourceBigtableInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceBigtableInstanceCreate, + Read: resourceBigtableInstanceRead, + Delete: resourceBigtableInstanceDestroy, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "display_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "cluster_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "num_nodes": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + // 30 is the maximum number of nodes without a quota increase. + ValidateFunc: validation.IntBetween(3, 30), + }, + + "storage_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false), + }, + + "zone": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "project": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceBigtableInstanceCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + ctx := context.Background() + + project, err := getProject(d, config) + if err != nil { + return err + } + + name := d.Get("name").(string) + displayName, ok := d.GetOk("display_name") + if !ok { + displayName = name + } + + clusterId := d.Get("cluster_id").(string) + numNodes := int32(d.Get("num_nodes").(int)) + zone := d.Get("zone").(string) + + var storageType bigtable.StorageType + switch value := d.Get("storage_type"); value { + case "HDD": + storageType = bigtable.HDD + case "SSD": + storageType = bigtable.SSD + } + + instanceConf := &bigtable.InstanceConf{ + InstanceId: name, + DisplayName: displayName.(string), + ClusterId: clusterId, + NumNodes: numNodes, + StorageType: storageType, + Zone: zone, + } + + c, err := config.clientFactoryBigtable.NewInstanceAdminClient(project) + if err != nil { + return fmt.Errorf("Error starting instance admin client. %s", err) + } + + defer c.Close() + + err = c.CreateInstance(ctx, instanceConf) + if err != nil { + return fmt.Errorf("Error creating instance. %s", err) + } + + d.SetId(name) + + return resourceBigtableInstanceRead(d, meta) +} + +func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + ctx := context.Background() + + project, err := getProject(d, config) + if err != nil { + return err + } + + c, err := config.clientFactoryBigtable.NewInstanceAdminClient(project) + if err != nil { + return fmt.Errorf("Error starting instance admin client. %s", err) + } + + defer c.Close() + + name := d.Id() + instances, err := c.Instances(ctx) + if err != nil { + return fmt.Errorf("Error retrieving instances. %s", err) + } + + var instanceInfo *bigtable.InstanceInfo + found := false + for _, i := range instances { + if i.Name == name { + instanceInfo = i + found = true + break + } + } + if !found { + return fmt.Errorf("Error retrieving instance. Could not find %s.", name) + } + + d.Set("name", instanceInfo.Name) + d.Set("display_name", instanceInfo.DisplayName) + + return nil +} + +func resourceBigtableInstanceDestroy(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + ctx := context.Background() + + project, err := getProject(d, config) + if err != nil { + return err + } + + c, err := config.clientFactoryBigtable.NewInstanceAdminClient(project) + if err != nil { + return fmt.Errorf("Error starting instance admin client. %s", err) + } + + defer c.Close() + + name := d.Id() + err = c.DeleteInstance(ctx, name) + if err != nil { + return fmt.Errorf("Error deleting instance. %s", err) + } + + d.SetId("") + + return nil +} diff --git a/google/resource_bigtable_instance_test.go b/google/resource_bigtable_instance_test.go new file mode 100644 index 00000000..4c5ef5e6 --- /dev/null +++ b/google/resource_bigtable_instance_test.go @@ -0,0 +1,118 @@ +package google + +import ( + "fmt" + "testing" + + "context" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccBigtableInstance_basic(t *testing.T) { + instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigtableInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBigtableInstance(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccBigtableInstanceExists( + "google_bigtable_instance.instance"), + ), + }, + }, + }) +} + +func testAccCheckBigtableInstanceDestroy(s *terraform.State) error { + var ctx = context.Background() + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_bigtable_instance" { + continue + } + + config := testAccProvider.Meta().(*Config) + c, err := config.clientFactoryBigtable.NewInstanceAdminClient(config.Project) + if err != nil { + return fmt.Errorf("Error starting instance admin client. %s", err) + } + + instances, err := c.Instances(ctx) + if err != nil { + return fmt.Errorf("Error retrieving instances. %s", err) + } + + found := false + for _, i := range instances { + if i.Name == rs.Primary.Attributes["name"] { + found = true + break + } + } + + if found { + return fmt.Errorf("Instance %s still exists.", rs.Primary.Attributes["name"]) + } + + c.Close() + } + + return nil +} + +func testAccBigtableInstanceExists(n string) resource.TestCheckFunc { + var ctx = context.Background() + 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 ID is set") + } + config := testAccProvider.Meta().(*Config) + c, err := config.clientFactoryBigtable.NewInstanceAdminClient(config.Project) + if err != nil { + return fmt.Errorf("Error starting instance admin client. %s", err) + } + + instances, err := c.Instances(ctx) + if err != nil { + return fmt.Errorf("Error retrieving instances. %s", err) + } + + found := false + for _, i := range instances { + if i.Name == rs.Primary.Attributes["name"] { + found = true + break + } + } + + if !found { + return fmt.Errorf("Error retrieving instance %s.", rs.Primary.Attributes["name"]) + } + + c.Close() + + return nil + } +} + +func testAccBigtableInstance(instanceName string) string { + return fmt.Sprintf(` +resource "google_bigtable_instance" "instance" { + name = "%s" + cluster_id = "%s" + zone = "us-central1-b" + num_nodes = 3 + storage_type = "HDD" +} +`, instanceName, instanceName) +} diff --git a/website/docs/r/bigtable_instance.html.markdown b/website/docs/r/bigtable_instance.html.markdown new file mode 100644 index 00000000..d48723c2 --- /dev/null +++ b/website/docs/r/bigtable_instance.html.markdown @@ -0,0 +1,49 @@ +--- +layout: "google" +page_title: "Google: google_bigtable_instance" +sidebar_current: "docs-google-bigtable_instance" +description: |- + Creates a Google Bigtable instance. +--- + +# google_bigtable_instance + +Creates a Google Bigtable instance. For more information see +[the official documentation](https://cloud.google.com/bigtable/) and +[API](https://cloud.google.com/bigtable/docs/go/reference). + + +## Example Usage + +```hcl +resource "google_bigtable_instance" "instance" { + name = "tf-instance" + cluster_id = "tf-instance-cluster" + zone = "us-central1-b" + num_nodes = 3 + storage_type = "HDD" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Bigtable instance. + +* `cluster_id` - (Required) The name of the Bigtable instance's cluster. + +* `zone` - (Required) The zone to create the Bigtable instance in. Note: Many zones do not support Bigtable instances. + +* `num_nodes` - (Required) The number of nodes in your Bigtable instance. Minimum of 3. + +* `storage_type` - (Required) The storage type to use. One of `"SSD"` or `"HDD"`. + +* `project` - (Optional) The project in which the resource belongs. If it + is not provided, the provider project is used. + +* `display_name` - (Optional) The human-readable display name of the Bigtable instance. Defaults to the instance `name`. + +## Attributes Reference + +Only the arguments listed above are exposed as attributes. From 2c2615317b9528559283544fe347dddc9b4e9ea7 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 26 Jun 2017 13:33:05 -0700 Subject: [PATCH 02/15] Changes based on review including default values in schema and inlining object properties. --- google/config.go | 6 +-- google/resource_bigtable_instance.go | 44 +++++++++++++------ google/resource_bigtable_instance_test.go | 10 ++--- .../docs/r/bigtable_instance.html.markdown | 14 +++--- 4 files changed, 45 insertions(+), 29 deletions(-) diff --git a/google/config.go b/google/config.go index 37897998..22aef767 100644 --- a/google/config.go +++ b/google/config.go @@ -90,18 +90,18 @@ func (c *Config) loadAndValidate() error { // Initiate an http.Client. The following GET request will be // authorized and authenticated on the behalf of // your service account. - client = conf.Client(oauth2.NoContext) + client = conf.Client(context.Background()) tokenSource = conf.TokenSource(context.Background()) } else { log.Printf("[INFO] Authenticating using DefaultClient") err := error(nil) - client, err = google.DefaultClient(oauth2.NoContext, clientScopes...) + client, err = google.DefaultClient(context.Background(), clientScopes...) if err != nil { return err } - tokenSource, err = google.DefaultTokenSource(oauth2.NoContext, clientScopes...) + tokenSource, err = google.DefaultTokenSource(context.Background(), clientScopes...) if err != nil { return err } diff --git a/google/resource_bigtable_instance.go b/google/resource_bigtable_instance.go index 3fc256c5..9e635487 100644 --- a/google/resource_bigtable_instance.go +++ b/google/resource_bigtable_instance.go @@ -37,17 +37,18 @@ func resourceBigtableInstance() *schema.Resource { }, "num_nodes": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - // 30 is the maximum number of nodes without a quota increase. - ValidateFunc: validation.IntBetween(3, 30), + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 3, + ValidateFunc: IntAtLeast(3), }, "storage_type": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, + Default: "SSD", ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false), }, @@ -81,10 +82,6 @@ func resourceBigtableInstanceCreate(d *schema.ResourceData, meta interface{}) er displayName = name } - clusterId := d.Get("cluster_id").(string) - numNodes := int32(d.Get("num_nodes").(int)) - zone := d.Get("zone").(string) - var storageType bigtable.StorageType switch value := d.Get("storage_type"); value { case "HDD": @@ -96,10 +93,10 @@ func resourceBigtableInstanceCreate(d *schema.ResourceData, meta interface{}) er instanceConf := &bigtable.InstanceConf{ InstanceId: name, DisplayName: displayName.(string), - ClusterId: clusterId, - NumNodes: numNodes, + ClusterId: d.Get("cluster_id").(string), + NumNodes: int32(d.Get("num_nodes").(int)), StorageType: storageType, - Zone: zone, + Zone: d.Get("zone").(string), } c, err := config.clientFactoryBigtable.NewInstanceAdminClient(project) @@ -135,13 +132,13 @@ func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) erro defer c.Close() - name := d.Id() instances, err := c.Instances(ctx) if err != nil { return fmt.Errorf("Error retrieving instances. %s", err) } var instanceInfo *bigtable.InstanceInfo + name := d.Id() found := false for _, i := range instances { if i.Name == name { @@ -186,3 +183,22 @@ func resourceBigtableInstanceDestroy(d *schema.ResourceData, meta interface{}) e return nil } + +// IntAtLeast returns a SchemaValidateFunc which tests if the provided value +// is of type int and is above min (inclusive) +func IntAtLeast(min int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(int) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be int", k)) + return + } + + if v < min { + es = append(es, fmt.Errorf("expected %s to be at least %d, got %d", k, min, v)) + return + } + + return + } +} diff --git a/google/resource_bigtable_instance_test.go b/google/resource_bigtable_instance_test.go index 4c5ef5e6..55a8d31f 100644 --- a/google/resource_bigtable_instance_test.go +++ b/google/resource_bigtable_instance_test.go @@ -108,11 +108,11 @@ func testAccBigtableInstanceExists(n string) resource.TestCheckFunc { func testAccBigtableInstance(instanceName string) string { return fmt.Sprintf(` resource "google_bigtable_instance" "instance" { - name = "%s" - cluster_id = "%s" - zone = "us-central1-b" - num_nodes = 3 - storage_type = "HDD" + name = "%s" + cluster_id = "%s" + zone = "us-central1-b" + num_nodes = 3 + storage_type = "HDD" } `, instanceName, instanceName) } diff --git a/website/docs/r/bigtable_instance.html.markdown b/website/docs/r/bigtable_instance.html.markdown index d48723c2..00ee12d1 100644 --- a/website/docs/r/bigtable_instance.html.markdown +++ b/website/docs/r/bigtable_instance.html.markdown @@ -17,10 +17,10 @@ Creates a Google Bigtable instance. For more information see ```hcl resource "google_bigtable_instance" "instance" { - name = "tf-instance" - cluster_id = "tf-instance-cluster" - zone = "us-central1-b" - num_nodes = 3 + name = "tf-instance" + cluster_id = "tf-instance-cluster" + zone = "us-central1-b" + num_nodes = 3 storage_type = "HDD" } ``` @@ -33,11 +33,11 @@ The following arguments are supported: * `cluster_id` - (Required) The name of the Bigtable instance's cluster. -* `zone` - (Required) The zone to create the Bigtable instance in. Note: Many zones do not support Bigtable instances. +* `zone` - (Required) The zone to create the Bigtable instance in. Zones that support Bigtable instances are noted on the [Cloud Locations page](https://cloud.google.com/about/locations/). -* `num_nodes` - (Required) The number of nodes in your Bigtable instance. Minimum of 3. +* `num_nodes` - (Required) The number of nodes in your Bigtable instance. Minimum of `3`. Defaults to `3`. -* `storage_type` - (Required) The storage type to use. One of `"SSD"` or `"HDD"`. +* `storage_type` - (Required) The storage type to use. One of `"SSD"` or `"HDD"`. Defaults to `SSD`. * `project` - (Optional) The project in which the resource belongs. If it is not provided, the provider project is used. From 048bfe5857bdbcbe9935bfc0a5922e45a8375bcd Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 26 Jun 2017 13:34:33 -0700 Subject: [PATCH 03/15] Renamed ClientFactoryBigtable to BigtableClientFactory --- ...lient_factory_bigtable.go => bigtable_client_factory.go} | 6 +++--- google/config.go | 4 ++-- google/resource_bigtable_instance.go | 6 +++--- google/resource_bigtable_instance_test.go | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) rename google/{client_factory_bigtable.go => bigtable_client_factory.go} (77%) diff --git a/google/client_factory_bigtable.go b/google/bigtable_client_factory.go similarity index 77% rename from google/client_factory_bigtable.go rename to google/bigtable_client_factory.go index f616788e..1c159788 100644 --- a/google/client_factory_bigtable.go +++ b/google/bigtable_client_factory.go @@ -8,15 +8,15 @@ import ( "google.golang.org/api/option" ) -type ClientFactoryBigtable struct { +type BigtableClientFactory struct { UserAgent string TokenSource oauth2.TokenSource } -func (s *ClientFactoryBigtable) NewInstanceAdminClient(project string) (*bigtable.InstanceAdminClient, error) { +func (s *BigtableClientFactory) NewInstanceAdminClient(project string) (*bigtable.InstanceAdminClient, error) { return bigtable.NewInstanceAdminClient(context.Background(), project, option.WithTokenSource(s.TokenSource), option.WithUserAgent(s.UserAgent)) } -func (s *ClientFactoryBigtable) NewAdminClient(project, instance string) (*bigtable.AdminClient, error) { +func (s *BigtableClientFactory) NewAdminClient(project, instance string) (*bigtable.AdminClient, error) { return bigtable.NewAdminClient(context.Background(), project, instance, option.WithTokenSource(s.TokenSource), option.WithUserAgent(s.UserAgent)) } diff --git a/google/config.go b/google/config.go index 22aef767..5b534ef2 100644 --- a/google/config.go +++ b/google/config.go @@ -48,7 +48,7 @@ type Config struct { clientServiceMan *servicemanagement.APIService clientBigQuery *bigquery.Service - clientFactoryBigtable *ClientFactoryBigtable + bigtableClientFactory *BigtableClientFactory } func (c *Config) loadAndValidate() error { @@ -193,7 +193,7 @@ func (c *Config) loadAndValidate() error { c.clientBigQuery.UserAgent = userAgent log.Printf("[INFO] Instantiating Google Cloud Bigtable Client Factory...") - c.clientFactoryBigtable = &ClientFactoryBigtable{ + c.bigtableClientFactory = &BigtableClientFactory{ UserAgent: userAgent, TokenSource: tokenSource, } diff --git a/google/resource_bigtable_instance.go b/google/resource_bigtable_instance.go index 9e635487..309dc5a8 100644 --- a/google/resource_bigtable_instance.go +++ b/google/resource_bigtable_instance.go @@ -99,7 +99,7 @@ func resourceBigtableInstanceCreate(d *schema.ResourceData, meta interface{}) er Zone: d.Get("zone").(string), } - c, err := config.clientFactoryBigtable.NewInstanceAdminClient(project) + c, err := config.bigtableClientFactory.NewInstanceAdminClient(project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } @@ -125,7 +125,7 @@ func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) erro return err } - c, err := config.clientFactoryBigtable.NewInstanceAdminClient(project) + c, err := config.bigtableClientFactory.NewInstanceAdminClient(project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } @@ -166,7 +166,7 @@ func resourceBigtableInstanceDestroy(d *schema.ResourceData, meta interface{}) e return err } - c, err := config.clientFactoryBigtable.NewInstanceAdminClient(project) + c, err := config.bigtableClientFactory.NewInstanceAdminClient(project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } diff --git a/google/resource_bigtable_instance_test.go b/google/resource_bigtable_instance_test.go index 55a8d31f..95ad5a1f 100644 --- a/google/resource_bigtable_instance_test.go +++ b/google/resource_bigtable_instance_test.go @@ -37,7 +37,7 @@ func testAccCheckBigtableInstanceDestroy(s *terraform.State) error { } config := testAccProvider.Meta().(*Config) - c, err := config.clientFactoryBigtable.NewInstanceAdminClient(config.Project) + c, err := config.bigtableClientFactory.NewInstanceAdminClient(config.Project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } @@ -77,7 +77,7 @@ func testAccBigtableInstanceExists(n string) resource.TestCheckFunc { return fmt.Errorf("No ID is set") } config := testAccProvider.Meta().(*Config) - c, err := config.clientFactoryBigtable.NewInstanceAdminClient(config.Project) + c, err := config.bigtableClientFactory.NewInstanceAdminClient(config.Project) if err != nil { return fmt.Errorf("Error starting instance admin client. %s", err) } From 5bceaef26f0fd5a1d1722793b13dc981da95a883 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 26 Jun 2017 14:03:35 -0700 Subject: [PATCH 04/15] Moved required fields and updated docs. --- google/resource_bigtable_instance.go | 24 +++++++++---------- .../docs/r/bigtable_instance.html.markdown | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/google/resource_bigtable_instance.go b/google/resource_bigtable_instance.go index 309dc5a8..2ba226e4 100644 --- a/google/resource_bigtable_instance.go +++ b/google/resource_bigtable_instance.go @@ -23,6 +23,18 @@ func resourceBigtableInstance() *schema.Resource { ForceNew: true, }, + "cluster_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "zone": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "display_name": { Type: schema.TypeString, Optional: true, @@ -30,12 +42,6 @@ func resourceBigtableInstance() *schema.Resource { Computed: true, }, - "cluster_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "num_nodes": { Type: schema.TypeInt, Optional: true, @@ -52,12 +58,6 @@ func resourceBigtableInstance() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false), }, - "zone": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "project": { Type: schema.TypeString, Optional: true, diff --git a/website/docs/r/bigtable_instance.html.markdown b/website/docs/r/bigtable_instance.html.markdown index 00ee12d1..e86160ab 100644 --- a/website/docs/r/bigtable_instance.html.markdown +++ b/website/docs/r/bigtable_instance.html.markdown @@ -35,9 +35,9 @@ The following arguments are supported: * `zone` - (Required) The zone to create the Bigtable instance in. Zones that support Bigtable instances are noted on the [Cloud Locations page](https://cloud.google.com/about/locations/). -* `num_nodes` - (Required) The number of nodes in your Bigtable instance. Minimum of `3`. Defaults to `3`. +* `num_nodes` - (Optional) The number of nodes in your Bigtable instance. Minimum of `3`. Defaults to `3`. -* `storage_type` - (Required) The storage type to use. One of `"SSD"` or `"HDD"`. Defaults to `SSD`. +* `storage_type` - (Optional) The storage type to use. One of `"SSD"` or `"HDD"`. Defaults to `SSD`. * `project` - (Optional) The project in which the resource belongs. If it is not provided, the provider project is used. From cfe9e4afb4a24f89a2b1efa0215334dc23d7aabf Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 28 Jun 2017 09:54:26 -0700 Subject: [PATCH 05/15] Vendored updated bigtable client. --- vendor/cloud.google.com/go/bigtable/admin.go | 21 ++++++++++++++++++ .../cloud.google.com/go/bigtable/bigtable.go | 2 +- vendor/cloud.google.com/go/bigtable/filter.go | 22 +++++++++---------- vendor/vendor.json | 6 ++--- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/vendor/cloud.google.com/go/bigtable/admin.go b/vendor/cloud.google.com/go/bigtable/admin.go index 4838d350..a1f5f01c 100644 --- a/vendor/cloud.google.com/go/bigtable/admin.go +++ b/vendor/cloud.google.com/go/bigtable/admin.go @@ -369,3 +369,24 @@ func (iac *InstanceAdminClient) Instances(ctx context.Context) ([]*InstanceInfo, } return is, nil } + +// InstanceInfo returns information about an instance. +func (iac *InstanceAdminClient) InstanceInfo(ctx context.Context, instanceId string) (*InstanceInfo, error) { + ctx = mergeOutgoingMetadata(ctx, iac.md) + req := &btapb.GetInstanceRequest{ + Name: "projects/" + iac.project + "/instances/" + instanceId, + } + res, err := iac.iClient.GetInstance(ctx, req) + if err != nil { + return nil, err + } + + m := instanceNameRegexp.FindStringSubmatch(res.Name) + if m == nil { + return nil, fmt.Errorf("malformed instance name %q", res.Name) + } + return &InstanceInfo{ + Name: m[2], + DisplayName: res.DisplayName, + }, nil +} diff --git a/vendor/cloud.google.com/go/bigtable/bigtable.go b/vendor/cloud.google.com/go/bigtable/bigtable.go index 5c2129cb..316e3d3c 100644 --- a/vendor/cloud.google.com/go/bigtable/bigtable.go +++ b/vendor/cloud.google.com/go/bigtable/bigtable.go @@ -73,7 +73,7 @@ func (c *Client) Close() error { } var ( - idempotentRetryCodes = []codes.Code{codes.DeadlineExceeded, codes.Unavailable, codes.Aborted, codes.Internal} + idempotentRetryCodes = []codes.Code{codes.DeadlineExceeded, codes.Unavailable, codes.Aborted} isIdempotentRetryCode = make(map[codes.Code]bool) retryOptions = []gax.CallOption{ gax.WithDelayTimeoutSettings(100*time.Millisecond, 2000*time.Millisecond, 1.2), diff --git a/vendor/cloud.google.com/go/bigtable/filter.go b/vendor/cloud.google.com/go/bigtable/filter.go index fb85498c..a1cf748a 100644 --- a/vendor/cloud.google.com/go/bigtable/filter.go +++ b/vendor/cloud.google.com/go/bigtable/filter.go @@ -154,7 +154,7 @@ func (stripValueFilter) proto() *btpb.RowFilter { return &btpb.RowFilter{Filter: &btpb.RowFilter_StripValueTransformer{true}} } -// TimestampRangeFilter returns a filter that matches any rows whose timestamp is within the given time bounds. A zero +// TimestampRangeFilter returns a filter that matches any cells whose timestamp is within the given time bounds. A zero // time means no bound. // The timestamp will be truncated to millisecond granularity. func TimestampRangeFilter(startTime time.Time, endTime time.Time) Filter { @@ -168,7 +168,7 @@ func TimestampRangeFilter(startTime time.Time, endTime time.Time) Filter { return trf } -// TimestampRangeFilterMicros returns a filter that matches any rows whose timestamp is within the given time bounds, +// TimestampRangeFilterMicros returns a filter that matches any cells whose timestamp is within the given time bounds, // specified in units of microseconds since 1 January 1970. A zero value for the end time is interpreted as no bound. // The timestamp will be truncated to millisecond granularity. func TimestampRangeFilterMicros(startTime Timestamp, endTime Timestamp) Filter { @@ -187,10 +187,10 @@ func (trf timestampRangeFilter) String() string { func (trf timestampRangeFilter) proto() *btpb.RowFilter { return &btpb.RowFilter{ Filter: &btpb.RowFilter_TimestampRangeFilter{ - &btpb.TimestampRange{ - int64(trf.startTime.TruncateToMilliseconds()), - int64(trf.endTime.TruncateToMilliseconds()), - }, + &btpb.TimestampRange{ + int64(trf.startTime.TruncateToMilliseconds()), + int64(trf.endTime.TruncateToMilliseconds()), + }, }} } @@ -228,8 +228,8 @@ func ValueRangeFilter(start, end []byte) Filter { } type valueRangeFilter struct { - start []byte - end []byte + start []byte + end []byte } func (vrf valueRangeFilter) String() string { @@ -260,8 +260,8 @@ func ConditionFilter(predicateFilter, trueFilter, falseFilter Filter) Filter { type conditionFilter struct { predicateFilter Filter - trueFilter Filter - falseFilter Filter + trueFilter Filter + falseFilter Filter } func (cf conditionFilter) String() string { @@ -282,7 +282,7 @@ func (cf conditionFilter) proto() *btpb.RowFilter { cf.predicateFilter.proto(), tf, ff, - }}} + }}} } // TODO(dsymonds): More filters: sampling diff --git a/vendor/vendor.json b/vendor/vendor.json index f303c341..2a736b49 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -3,10 +3,10 @@ "ignore": "appengine test github.com/hashicorp/nomad/", "package": [ { - "checksumSHA1": "fEobtlzPZ57VXi864qMmblI2gsA=", + "checksumSHA1": "NrTYYg3++pBxH1Z8xjVg6ssQTYY=", "path": "cloud.google.com/go/bigtable", - "revision": "69931d826ffbbcb4f8451b42d5cf7fc2ac6c7443", - "revisionTime": "2017-06-09T14:31:37Z" + "revision": "f6bedb5a8dbce75418580cedf5c2434c78d06cfa", + "revisionTime": "2017-06-27T21:12:08Z" }, { "checksumSHA1": "B1HkIrBavSgy6ntVyGr/eNUy44I=", From ec2f9332529b538cadd30a76abe9fb825ff55682 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 28 Jun 2017 10:00:49 -0700 Subject: [PATCH 06/15] Changed Bigtable instance to use InstanceInfo instead of iterating the list of infos. --- google/resource_bigtable_instance.go | 22 +++-------------- google/resource_bigtable_instance_test.go | 30 +++-------------------- 2 files changed, 7 insertions(+), 45 deletions(-) diff --git a/google/resource_bigtable_instance.go b/google/resource_bigtable_instance.go index 2ba226e4..b3ecc68f 100644 --- a/google/resource_bigtable_instance.go +++ b/google/resource_bigtable_instance.go @@ -132,27 +132,13 @@ func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) erro defer c.Close() - instances, err := c.Instances(ctx) + instance, err := c.InstanceInfo(ctx, d.Id()) if err != nil { - return fmt.Errorf("Error retrieving instances. %s", err) + return fmt.Errorf("Error retrieving instance. Could not find %s.", d.Id()) } - var instanceInfo *bigtable.InstanceInfo - name := d.Id() - found := false - for _, i := range instances { - if i.Name == name { - instanceInfo = i - found = true - break - } - } - if !found { - return fmt.Errorf("Error retrieving instance. Could not find %s.", name) - } - - d.Set("name", instanceInfo.Name) - d.Set("display_name", instanceInfo.DisplayName) + d.Set("name", instance.Name) + d.Set("display_name", instance.DisplayName) return nil } diff --git a/google/resource_bigtable_instance_test.go b/google/resource_bigtable_instance_test.go index 95ad5a1f..7592cba3 100644 --- a/google/resource_bigtable_instance_test.go +++ b/google/resource_bigtable_instance_test.go @@ -42,20 +42,8 @@ func testAccCheckBigtableInstanceDestroy(s *terraform.State) error { return fmt.Errorf("Error starting instance admin client. %s", err) } - instances, err := c.Instances(ctx) - if err != nil { - return fmt.Errorf("Error retrieving instances. %s", err) - } - - found := false - for _, i := range instances { - if i.Name == rs.Primary.Attributes["name"] { - found = true - break - } - } - - if found { + _, err = c.InstanceInfo(ctx, rs.Primary.Attributes["name"]) + if err == nil { return fmt.Errorf("Instance %s still exists.", rs.Primary.Attributes["name"]) } @@ -82,20 +70,8 @@ func testAccBigtableInstanceExists(n string) resource.TestCheckFunc { return fmt.Errorf("Error starting instance admin client. %s", err) } - instances, err := c.Instances(ctx) + _, err = c.InstanceInfo(ctx, rs.Primary.Attributes["name"]) if err != nil { - return fmt.Errorf("Error retrieving instances. %s", err) - } - - found := false - for _, i := range instances { - if i.Name == rs.Primary.Attributes["name"] { - found = true - break - } - } - - if !found { return fmt.Errorf("Error retrieving instance %s.", rs.Primary.Attributes["name"]) } From 28b35fa3dd4f41a22a497f792a0f9d2df3401b7c Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 28 Jun 2017 12:46:06 -0700 Subject: [PATCH 07/15] Clear bigtable instance when gone, pass along error message. --- google/resource_bigtable_instance.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/google/resource_bigtable_instance.go b/google/resource_bigtable_instance.go index b3ecc68f..d2141ffd 100644 --- a/google/resource_bigtable_instance.go +++ b/google/resource_bigtable_instance.go @@ -8,6 +8,7 @@ import ( "cloud.google.com/go/bigtable" "golang.org/x/net/context" + "log" ) func resourceBigtableInstance() *schema.Resource { @@ -134,7 +135,9 @@ func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) erro instance, err := c.InstanceInfo(ctx, d.Id()) if err != nil { - return fmt.Errorf("Error retrieving instance. Could not find %s.", d.Id()) + log.Printf("[WARN] Removing %s because it's gone", d.Id()) + d.SetId("") + return fmt.Errorf("Error retrieving instance. Could not find %s. %s", d.Id(), err) } d.Set("name", instance.Name) From d0d116fa3e22cbb7671c251bf3d6d1dfa10757e2 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 26 Jun 2017 15:40:01 -0700 Subject: [PATCH 08/15] Add support for Bigtable table. --- google/provider.go | 1 + google/resource_bigtable_table.go | 134 ++++++++++++++++++++ google/resource_bigtable_table_test.go | 127 +++++++++++++++++++ website/docs/r/bigtable_table.html.markdown | 49 +++++++ 4 files changed, 311 insertions(+) create mode 100644 google/resource_bigtable_table.go create mode 100644 google/resource_bigtable_table_test.go create mode 100644 website/docs/r/bigtable_table.html.markdown diff --git a/google/provider.go b/google/provider.go index 806aa2fb..6cd1f975 100644 --- a/google/provider.go +++ b/google/provider.go @@ -65,6 +65,7 @@ func Provider() terraform.ResourceProvider { "google_bigquery_dataset": resourceBigQueryDataset(), "google_bigquery_table": resourceBigQueryTable(), "google_bigtable_instance": resourceBigtableInstance(), + "google_bigtable_table": resourceBigtableTable(), "google_compute_autoscaler": resourceComputeAutoscaler(), "google_compute_address": resourceComputeAddress(), "google_compute_backend_bucket": resourceComputeBackendBucket(), diff --git a/google/resource_bigtable_table.go b/google/resource_bigtable_table.go new file mode 100644 index 00000000..b662aa61 --- /dev/null +++ b/google/resource_bigtable_table.go @@ -0,0 +1,134 @@ +package google + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + + "golang.org/x/net/context" +) + +func resourceBigtableTable() *schema.Resource { + return &schema.Resource{ + Create: resourceBigtableTableCreate, + Read: resourceBigtableTableRead, + Delete: resourceBigtableTableDestroy, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "instance_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "split_keys": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "project": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceBigtableTableCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + ctx := context.Background() + + project, err := getProject(d, config) + if err != nil { + return err + } + + instanceName := d.Get("instance_name").(string) + c, err := config.bigtableClientFactory.NewAdminClient(project, instanceName) + if err != nil { + return fmt.Errorf("Error starting admin client. %s", err) + } + + defer c.Close() + + name := d.Get("name").(string) + if v, ok := d.GetOk("split_keys"); ok { + splitKeys := convertSchemaArrayToStringArray(v.([]interface{})) + err = c.CreatePresplitTable(ctx, name, splitKeys) + if err != nil { + return fmt.Errorf("Error creating presplit table. %s", err) + } + } else { + err = c.CreateTable(ctx, name) + if err != nil { + return fmt.Errorf("Error creating table. %s", err) + } + } + + d.SetId(name) + + return resourceBigtableTableRead(d, meta) +} + +func resourceBigtableTableRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + ctx := context.Background() + + project, err := getProject(d, config) + if err != nil { + return err + } + + instanceName := d.Get("instance_name").(string) + c, err := config.bigtableClientFactory.NewAdminClient(project, instanceName) + if err != nil { + return fmt.Errorf("Error starting admin client. %s", err) + } + + defer c.Close() + + name := d.Id() + _, err = c.TableInfo(ctx, name) + if err != nil { + return fmt.Errorf("Error retrieving table. Could not find %s in %s.", name, instanceName) + } + + return nil +} + +func resourceBigtableTableDestroy(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + ctx := context.Background() + + project, err := getProject(d, config) + if err != nil { + return err + } + + instanceName := d.Get("instance_name").(string) + c, err := config.bigtableClientFactory.NewAdminClient(project, instanceName) + if err != nil { + return fmt.Errorf("Error starting admin client. %s", err) + } + + defer c.Close() + + name := d.Get("name").(string) + err = c.DeleteTable(ctx, name) + if err != nil { + return fmt.Errorf("Error deleting instance. %s", err) + } + + d.SetId("") + + return nil +} diff --git a/google/resource_bigtable_table_test.go b/google/resource_bigtable_table_test.go new file mode 100644 index 00000000..216073e8 --- /dev/null +++ b/google/resource_bigtable_table_test.go @@ -0,0 +1,127 @@ +package google + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccBigtableTable_basic(t *testing.T) { + instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + tableName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigtableTableDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBigtableTable(instanceName, tableName), + Check: resource.ComposeTestCheckFunc( + testAccBigtableTableExists( + "google_bigtable_table.table"), + ), + }, + }, + }) +} + +func testAccCheckBigtableTableDestroy(s *terraform.State) error { + var ctx = context.Background() + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_bigtable_table" { + continue + } + + config := testAccProvider.Meta().(*Config) + c, err := config.bigtableClientFactory.NewAdminClient(config.Project, rs.Primary.Attributes["instance_name"]) + if err != nil { + // The instance is already gone + return nil + } + + tables, err := c.Tables(ctx) + if err != nil { + // The instance is already gone. + return nil + } + + found := false + for _, t := range tables { + if t == rs.Primary.Attributes["name"] { + found = true + break + } + } + + if found { + return fmt.Errorf("Table still present. Found %s in %s.", rs.Primary.Attributes["name"], rs.Primary.Attributes["instance_name"]) + } + + c.Close() + } + + return nil +} + +func testAccBigtableTableExists(n string) resource.TestCheckFunc { + var ctx = context.Background() + 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 ID is set") + } + config := testAccProvider.Meta().(*Config) + c, err := config.bigtableClientFactory.NewAdminClient(config.Project, rs.Primary.Attributes["instance_name"]) + if err != nil { + return fmt.Errorf("Error starting admin client. %s", err) + } + + tables, err := c.Tables(ctx) + if err != nil { + return fmt.Errorf("Error starting admin client. %s", err) + } + + found := false + for _, t := range tables { + if t == rs.Primary.Attributes["name"] { + found = true + break + } + } + + if !found { + return fmt.Errorf("Error retrieving table. Could not find %s in %s.", rs.Primary.Attributes["name"], rs.Primary.Attributes["instance_name"]) + } + + c.Close() + + return nil + } +} + +func testAccBigtableTable(instanceName, tableName string) string { + return fmt.Sprintf(` +resource "google_bigtable_instance" "instance" { + name = "%s" + cluster_id = "%s" + zone = "us-central1-b" + num_nodes = 3 + storage_type = "HDD" +} + +resource "google_bigtable_table" "table" { + name = "%s" + instance_name = "${google_bigtable_instance.instance.name}" + split_keys = ["a", "b", "c"] +} +`, instanceName, instanceName, tableName) +} diff --git a/website/docs/r/bigtable_table.html.markdown b/website/docs/r/bigtable_table.html.markdown new file mode 100644 index 00000000..b37b5fb0 --- /dev/null +++ b/website/docs/r/bigtable_table.html.markdown @@ -0,0 +1,49 @@ +--- +layout: "google" +page_title: "Google: google_bigtable_table" +sidebar_current: "docs-google-bigtable_table" +description: |- + Creates a Google Bigtable table inside an instance. +--- + +# google_bigtable_table + +Creates a Google Bigtable table inside an instance. For more information see +[the official documentation](https://cloud.google.com/bigtable/) and +[API](https://cloud.google.com/bigtable/docs/go/reference). + + +## Example Usage + +```hcl +resource "google_bigtable_instance" "instance" { + name = "tf-instance" + cluster_id = "tf-instance-cluster" + zone = "us-central1-b" + num_nodes = 3 + storage_type = "HDD" +} + +resource "google_bigtable_table" "table" { + name = "tf-table" + instance_name = "${google_bigtable_instance.instance.name}" + split_keys = ["a", "b", "c"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the table. + +* `instance_name` - (Required) The name of the Bigtable instance. + +* `split_keys` - (Optional) A list of predefined keys to split the table on. + +* `project` - (Optional) The project in which the resource belongs. If it + is not provided, the provider project is used. + +## Attributes Reference + +Only the arguments listed above are exposed as attributes. From a358f4147f90036dfb96511990e9c6efc7856fe0 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 28 Jun 2017 12:49:12 -0700 Subject: [PATCH 09/15] Clear BigtableTable schema when resource missing, pass along missing error. --- google/resource_bigtable_table.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/google/resource_bigtable_table.go b/google/resource_bigtable_table.go index b662aa61..550f00b8 100644 --- a/google/resource_bigtable_table.go +++ b/google/resource_bigtable_table.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "golang.org/x/net/context" + "log" ) func resourceBigtableTable() *schema.Resource { @@ -99,7 +100,9 @@ func resourceBigtableTableRead(d *schema.ResourceData, meta interface{}) error { name := d.Id() _, err = c.TableInfo(ctx, name) if err != nil { - return fmt.Errorf("Error retrieving table. Could not find %s in %s.", name, instanceName) + log.Printf("[WARN] Removing %s because it's gone", name) + d.SetId("") + return fmt.Errorf("Error retrieving table. Could not find %s in %s. %s", name, instanceName, err) } return nil From e5ff2b7bcd3defbc637fa1b2d4fbd905fbbef647 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 5 Jul 2017 09:59:57 -0700 Subject: [PATCH 10/15] Updated google_bigtable_table tests. --- google/resource_bigtable_table.go | 6 ++- google/resource_bigtable_table_test.go | 68 +++++++++++++++----------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/google/resource_bigtable_table.go b/google/resource_bigtable_table.go index 550f00b8..edf49b7c 100644 --- a/google/resource_bigtable_table.go +++ b/google/resource_bigtable_table.go @@ -64,11 +64,15 @@ func resourceBigtableTableCreate(d *schema.ResourceData, meta interface{}) error name := d.Get("name").(string) if v, ok := d.GetOk("split_keys"); ok { splitKeys := convertSchemaArrayToStringArray(v.([]interface{})) + // This method may return before the table's creation is complete - we may need to wait until + // it exists in the future. err = c.CreatePresplitTable(ctx, name, splitKeys) if err != nil { return fmt.Errorf("Error creating presplit table. %s", err) } } else { + // This method may return before the table's creation is complete - we may need to wait until + // it exists in the future. err = c.CreateTable(ctx, name) if err != nil { return fmt.Errorf("Error creating table. %s", err) @@ -128,7 +132,7 @@ func resourceBigtableTableDestroy(d *schema.ResourceData, meta interface{}) erro name := d.Get("name").(string) err = c.DeleteTable(ctx, name) if err != nil { - return fmt.Errorf("Error deleting instance. %s", err) + return fmt.Errorf("Error deleting table. %s", err) } d.SetId("") diff --git a/google/resource_bigtable_table_test.go b/google/resource_bigtable_table_test.go index 216073e8..ff79ff98 100644 --- a/google/resource_bigtable_table_test.go +++ b/google/resource_bigtable_table_test.go @@ -30,6 +30,26 @@ func TestAccBigtableTable_basic(t *testing.T) { }) } +func TestAccBigtableTable_splitKeys(t *testing.T) { + instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + tableName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigtableTableDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBigtableTable_splitKeys(instanceName, tableName), + Check: resource.ComposeTestCheckFunc( + testAccBigtableTableExists( + "google_bigtable_table.table"), + ), + }, + }, + }) +} + func testAccCheckBigtableTableDestroy(s *terraform.State) error { var ctx = context.Background() for _, rs := range s.RootModule().Resources { @@ -44,21 +64,8 @@ func testAccCheckBigtableTableDestroy(s *terraform.State) error { return nil } - tables, err := c.Tables(ctx) - if err != nil { - // The instance is already gone. - return nil - } - - found := false - for _, t := range tables { - if t == rs.Primary.Attributes["name"] { - found = true - break - } - } - - if found { + _, err = c.TableInfo(ctx, rs.Primary.Attributes["name"]) + if err == nil { return fmt.Errorf("Table still present. Found %s in %s.", rs.Primary.Attributes["name"], rs.Primary.Attributes["instance_name"]) } @@ -85,20 +92,8 @@ func testAccBigtableTableExists(n string) resource.TestCheckFunc { return fmt.Errorf("Error starting admin client. %s", err) } - tables, err := c.Tables(ctx) + _, err = c.TableInfo(ctx, rs.Primary.Attributes["name"]) if err != nil { - return fmt.Errorf("Error starting admin client. %s", err) - } - - found := false - for _, t := range tables { - if t == rs.Primary.Attributes["name"] { - found = true - break - } - } - - if !found { return fmt.Errorf("Error retrieving table. Could not find %s in %s.", rs.Primary.Attributes["name"], rs.Primary.Attributes["instance_name"]) } @@ -118,6 +113,23 @@ resource "google_bigtable_instance" "instance" { storage_type = "HDD" } +resource "google_bigtable_table" "table" { + name = "%s" + instance_name = "${google_bigtable_instance.instance.name}" +} +`, instanceName, instanceName, tableName) +} + +func testAccBigtableTable_splitKeys(instanceName, tableName string) string { + return fmt.Sprintf(` +resource "google_bigtable_instance" "instance" { + name = "%s" + cluster_id = "%s" + zone = "us-central1-b" + num_nodes = 3 + storage_type = "HDD" +} + resource "google_bigtable_table" "table" { name = "%s" instance_name = "${google_bigtable_instance.instance.name}" From fc24b321b96eaea6cbe70ae1a8ea6fd47ac88892 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 5 Jul 2017 10:49:13 -0700 Subject: [PATCH 11/15] Add bigtable sidebar entries. --- website/google.erb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/website/google.erb b/website/google.erb index 71f952da..5d416ee8 100644 --- a/website/google.erb +++ b/website/google.erb @@ -21,6 +21,17 @@ + > + Google Bigtable Resources + + + > Google Cloud Platform Data Sources