Skip to content

Commit

Permalink
Bigtable: table deletion protection support (#6722) (#13232)
Browse files Browse the repository at this point in the history
Co-authored-by: Cameron Thornton <camthornton@google.com>
Co-authored-by: Edward Sun <sunedward@google.com>
Co-authored-by: Luca Prete <lucaprete@google.com>
Co-authored-by: Stephen Lewis (Burrows) <stephen.r.burrows@gmail.com>
Co-authored-by: Aleksandr Averbukh <averbukh@google.com>
Co-authored-by: Edward Sun <42220489+edwardmedia@users.noreply.github.com>
Co-authored-by: Jay Sanghani <111726632+sanghaniJ@users.noreply.github.com>
Co-authored-by: Sarah French <15078782+SarahFrench@users.noreply.github.com>
Co-authored-by: Stephen Lewis (Burrows) <stephenrlewis@google.com>
Co-authored-by: AlfatahB <111489009+AlfatahB@users.noreply.github.com>
Co-authored-by: Kevin Si <kevinsi4508@gmail.com>
Co-authored-by: Zhenhua Li <zhenhuali@google.com>
Co-authored-by: Mikołaj Siedlarek <mikolaj@siedlarek.net>
Co-authored-by: Avi Dave <avi.dave@mail.utoronto.ca>
Co-authored-by: Grigory Solomin <5736535+gsolomin@users.noreply.github.com>
Co-authored-by: Avi Dave <avidave97@gmail.com>
Co-authored-by: Ryan Oaks <ryanoaks@google.com>
Co-authored-by: Haruaki OTAKE <aaharu@hotmail.com>
Co-authored-by: x <xuruicheng96@gmail.com>
Co-authored-by: sakuya9t <4102161+sakuya9t@users.noreply.github.com>
Co-authored-by: Luca Prete <preteluca@gmail.com>
Co-authored-by: aniketkumarj <112697326+aniketkumarj@users.noreply.github.com>
Co-authored-by: Terrence Ryan <tpryan@google.com>
Co-authored-by: Sam Levenick <slevenick@google.com>
Co-authored-by: Shotaro Kohama <khmshtr28@gmail.com>
Co-authored-by: Francis (Feng) Liu <Francis.Liu2012@gmail.com>
Co-authored-by: bohengy <108434983+bohengy@users.noreply.github.com>
Co-authored-by: Søren Hansen <hello@severinus.io>
Co-authored-by: Ilia Lazebnik <Ilia.lazebnik@gmail.com>
Co-authored-by: t-indumathy <30333884+t-indumathy@users.noreply.github.com>
Co-authored-by: AarshDhokai <111489014+AarshDhokai@users.noreply.github.com>
Co-authored-by: Joost Buskermolen <joostbuskermolen@hotmail.com>
Co-authored-by: Benjamin Berriot <40873921+IIBenII@users.noreply.github.com>
Co-authored-by: Scott Suarez <ScottSuarez@google.com>
Co-authored-by: Carl Yeksigian <carl@yeksigian.com>
Co-authored-by: Shuya Ma <87669292+shuyama1@users.noreply.github.com>
Co-authored-by: iperetz-goo <108678927+iperetz-goo@users.noreply.github.com>
Co-authored-by: Riley Karson <rileykarson@google.com>
Co-authored-by: Thomas Rodgers <thomasrodgers@google.com>
Co-authored-by: Daniel Vega-Myhre <105610547+danielvegamyhre@users.noreply.github.com>
Co-authored-by: Neha Vellanki <35039892+neha-vellanki12@users.noreply.github.com>
Co-authored-by: gfxcc <yongcy@google.com>
Co-authored-by: Lingkai Shen <slk49@live.cn>
Co-authored-by: Stenal P Jolly <stenalpjolly@gmail.com>
Co-authored-by: Mohamed Fouad <110571142+mraouffouad@users.noreply.github.com>
Signed-off-by: Modular Magician <magic-modules@google.com>

Signed-off-by: Modular Magician <magic-modules@google.com>
Co-authored-by: Cameron Thornton <camthornton@google.com>
Co-authored-by: Edward Sun <sunedward@google.com>
Co-authored-by: Luca Prete <lucaprete@google.com>
Co-authored-by: Stephen Lewis (Burrows) <stephen.r.burrows@gmail.com>
Co-authored-by: Aleksandr Averbukh <averbukh@google.com>
Co-authored-by: Edward Sun <42220489+edwardmedia@users.noreply.github.com>
Co-authored-by: Jay Sanghani <111726632+sanghaniJ@users.noreply.github.com>
Co-authored-by: Sarah French <15078782+SarahFrench@users.noreply.github.com>
Co-authored-by: Stephen Lewis (Burrows) <stephenrlewis@google.com>
Co-authored-by: AlfatahB <111489009+AlfatahB@users.noreply.github.com>
Co-authored-by: Kevin Si <kevinsi4508@gmail.com>
Co-authored-by: Zhenhua Li <zhenhuali@google.com>
Co-authored-by: Mikołaj Siedlarek <mikolaj@siedlarek.net>
Co-authored-by: Avi Dave <avi.dave@mail.utoronto.ca>
Co-authored-by: Grigory Solomin <5736535+gsolomin@users.noreply.github.com>
Co-authored-by: Avi Dave <avidave97@gmail.com>
Co-authored-by: Ryan Oaks <ryanoaks@google.com>
Co-authored-by: Haruaki OTAKE <aaharu@hotmail.com>
Co-authored-by: x <xuruicheng96@gmail.com>
Co-authored-by: sakuya9t <4102161+sakuya9t@users.noreply.github.com>
Co-authored-by: Luca Prete <preteluca@gmail.com>
Co-authored-by: aniketkumarj <112697326+aniketkumarj@users.noreply.github.com>
Co-authored-by: Terrence Ryan <tpryan@google.com>
Co-authored-by: Sam Levenick <slevenick@google.com>
Co-authored-by: Shotaro Kohama <khmshtr28@gmail.com>
Co-authored-by: Francis (Feng) Liu <Francis.Liu2012@gmail.com>
Co-authored-by: bohengy <108434983+bohengy@users.noreply.github.com>
Co-authored-by: Søren Hansen <hello@severinus.io>
Co-authored-by: Ilia Lazebnik <Ilia.lazebnik@gmail.com>
Co-authored-by: t-indumathy <30333884+t-indumathy@users.noreply.github.com>
Co-authored-by: AarshDhokai <111489014+AarshDhokai@users.noreply.github.com>
Co-authored-by: Joost Buskermolen <joostbuskermolen@hotmail.com>
Co-authored-by: Benjamin Berriot <40873921+IIBenII@users.noreply.github.com>
Co-authored-by: Scott Suarez <ScottSuarez@google.com>
Co-authored-by: Carl Yeksigian <carl@yeksigian.com>
Co-authored-by: Shuya Ma <87669292+shuyama1@users.noreply.github.com>
Co-authored-by: iperetz-goo <108678927+iperetz-goo@users.noreply.github.com>
Co-authored-by: Riley Karson <rileykarson@google.com>
Co-authored-by: Thomas Rodgers <thomasrodgers@google.com>
Co-authored-by: Daniel Vega-Myhre <105610547+danielvegamyhre@users.noreply.github.com>
Co-authored-by: Neha Vellanki <35039892+neha-vellanki12@users.noreply.github.com>
Co-authored-by: gfxcc <yongcy@google.com>
Co-authored-by: Lingkai Shen <slk49@live.cn>
Co-authored-by: Stenal P Jolly <stenalpjolly@gmail.com>
Co-authored-by: Mohamed Fouad <110571142+mraouffouad@users.noreply.github.com>
  • Loading branch information
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .changelog/6722.txt
@@ -0,0 +1,4 @@
```release-note:enhancement
bigtable: supported table deletion protection in terraform

```
44 changes: 44 additions & 0 deletions google/resource_bigtable_table.go
Expand Up @@ -8,6 +8,7 @@ import (

"cloud.google.com/go/bigtable"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceBigtableTable() *schema.Resource {
Expand Down Expand Up @@ -76,6 +77,15 @@ func resourceBigtableTable() *schema.Resource {
ForceNew: true,
Description: `The ID of the project in which the resource belongs. If it is not provided, the provider project is used.`,
},

"deletion_protection": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"PROTECTED", "UNPROTECTED"}, false),
Elem: &schema.Schema{Type: schema.TypeString},
Description: `A field to make the table protected against data loss i.e. when set to PROTECTED, deleting the table, the column families in the table, and the instance containing the table would be prohibited. If not provided, currently deletion protection will be set to UNPROTECTED as it is the API default value.`,
},
},
UseJSONNumber: true,
}
Expand Down Expand Up @@ -109,6 +119,15 @@ func resourceBigtableTableCreate(d *schema.ResourceData, meta interface{}) error
tableId := d.Get("name").(string)
tblConf := bigtable.TableConf{TableID: tableId}

// Check if deletion protection is given
// If not given, currently tblConf.DeletionProtection will be set to false in the API
deletionProtection := d.Get("deletion_protection")
if deletionProtection == "PROTECTED" {
tblConf.DeletionProtection = bigtable.Protected
} else if deletionProtection == "UNPROTECTED" {
tblConf.DeletionProtection = bigtable.Unprotected
}

// Set the split keys if given.
if v, ok := d.GetOk("split_keys"); ok {
tblConf.SplitKeys = convertStringArr(v.([]interface{}))
Expand Down Expand Up @@ -188,6 +207,18 @@ func resourceBigtableTableRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error setting column_family: %s", err)
}

deletionProtection := table.DeletionProtection
if deletionProtection == bigtable.Protected {
if err := d.Set("deletion_protection", "PROTECTED"); err != nil {
return fmt.Errorf("Error setting deletion_protection: %s", err)
}
} else if deletionProtection == bigtable.Unprotected {
if err := d.Set("deletion_protection", "UNPROTECTED"); err != nil {
return fmt.Errorf("Error setting deletion_protection: %s", err)
}
} else {
return fmt.Errorf("Error setting deletion_protection, it should be either PROTECTED or UNPROTECTED")
}
return nil
}

Expand Down Expand Up @@ -240,6 +271,19 @@ func resourceBigtableTableUpdate(d *schema.ResourceData, meta interface{}) error
}
}

if d.HasChange("deletion_protection") {
deletionProtection := d.Get("deletion_protection")
if deletionProtection == "PROTECTED" {
if err := c.UpdateTableWithDeletionProtection(ctx, name, bigtable.Protected); err != nil {
return fmt.Errorf("Error updating deletion protection in table %v: %s", name, err)
}
} else if deletionProtection == "UNPROTECTED" {
if err := c.UpdateTableWithDeletionProtection(ctx, name, bigtable.Unprotected); err != nil {
return fmt.Errorf("Error updating deletion protection in table %v: %s", name, err)
}
}
}

return resourceBigtableTableRead(d, meta)
}

Expand Down
192 changes: 192 additions & 0 deletions google/resource_bigtable_table_test.go
Expand Up @@ -3,6 +3,7 @@ package google
import (
"context"
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
Expand Down Expand Up @@ -86,6 +87,125 @@ func TestAccBigtableTable_family(t *testing.T) {
})
}

func TestAccBigtableTable_deletion_protection_protected(t *testing.T) {
// bigtable instance does not use the shared HTTP client, this test creates an instance
skipIfVcr(t)
t.Parallel()

instanceName := fmt.Sprintf("tf-test-%s", randString(t, 10))
tableName := fmt.Sprintf("tf-test-%s", randString(t, 10))
family := fmt.Sprintf("tf-test-%s", randString(t, 10))

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBigtableTableDestroyProducer(t),
Steps: []resource.TestStep{
// creating a table with a column family and deletion protection equals to protected
{
Config: testAccBigtableTable_deletion_protection(instanceName, tableName, "PROTECTED", family),
},
{
ResourceName: "google_bigtable_table.table",
ImportState: true,
ImportStateVerify: true,
},
// it is not possible to delete column families in the table with deletion protection equals to protected
{
Config: testAccBigtableTable(instanceName, tableName),
ExpectError: regexp.MustCompile(".*deletion protection field is set to true.*"),
},
// it is not possible to delete the table because of deletion protection equals to protected
{
Config: testAccBigtableTable_destroyTable(instanceName),
ExpectError: regexp.MustCompile(".*deletion protection field is set to true.*"),
},
// changing deletion protection field to unprotected without changing the column families
// checking if the table and the column family exists
{
Config: testAccBigtableTable_deletion_protection(instanceName, tableName, "UNPROTECTED", family),
Check: resource.ComposeTestCheckFunc(
testAccBigtableColumnFamilyExists(t, "google_bigtable_table.table", family),
),
},
{
ResourceName: "google_bigtable_table.table",
ImportState: true,
ImportStateVerify: true,
},
// destroying the table is possible when deletion protection is equals to unprotected
{
Config: testAccBigtableTable_destroyTable(instanceName),
},
{
ResourceName: "google_bigtable_instance.instance",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"},
},
},
})
}

func TestAccBigtableTable_deletion_protection_unprotected(t *testing.T) {
// bigtable instance does not use the shared HTTP client, this test creates an instance
skipIfVcr(t)
t.Parallel()

instanceName := fmt.Sprintf("tf-test-%s", randString(t, 10))
tableName := fmt.Sprintf("tf-test-%s", randString(t, 10))
family := fmt.Sprintf("tf-test-%s", randString(t, 10))

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBigtableTableDestroyProducer(t),
Steps: []resource.TestStep{
// creating a table with a column family and deletion protection equals to unprotected
{
Config: testAccBigtableTable_deletion_protection(instanceName, tableName, "UNPROTECTED", family),
},
{
ResourceName: "google_bigtable_table.table",
ImportState: true,
ImportStateVerify: true,
},
// removing the column family is possible because the deletion protection field is unprotected
{
Config: testAccBigtableTable(instanceName, tableName),
},
{
ResourceName: "google_bigtable_table.table",
ImportState: true,
ImportStateVerify: true,
},
// changing the deletion protection field to protected
{
Config: testAccBigtableTable_deletion_protection(instanceName, tableName, "PROTECTED", family),
},
{
ResourceName: "google_bigtable_table.table",
ImportState: true,
ImportStateVerify: true,
},
// it is not possible to delete the table because of deletion protection equals to protected
{
Config: testAccBigtableTable_destroyTable(instanceName),
ExpectError: regexp.MustCompile(".*deletion protection field is set to true.*"),
},
// changing the deletion protection field to unprotected so that the sources can properly be destroyed
{
Config: testAccBigtableTable_deletion_protection(instanceName, tableName, "UNPROTECTED", family),
},
{
ResourceName: "google_bigtable_table.table",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccBigtableTable_familyMany(t *testing.T) {
// bigtable instance does not use the shared HTTP client, this test creates an instance
skipIfVcr(t)
Expand Down Expand Up @@ -173,6 +293,36 @@ func testAccCheckBigtableTableDestroyProducer(t *testing.T) func(s *terraform.St
}
}

func testAccBigtableColumnFamilyExists(t *testing.T, table_name_space, family string) resource.TestCheckFunc {
var ctx = context.Background()
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[table_name_space]
if !ok {
return fmt.Errorf("Table not found: %s", table_name_space)
}

config := googleProviderConfig(t)
c, err := config.BigTableClientFactory(config.userAgent).NewAdminClient(config.Project, rs.Primary.Attributes["instance_name"])
if err != nil {
return fmt.Errorf("Error starting admin client. %s", err)
}

defer c.Close()

table, err := c.TableInfo(ctx, rs.Primary.Attributes["name"])
if err != nil {
return fmt.Errorf("Error retrieving table. Could not find %s in %s.", rs.Primary.Attributes["name"], rs.Primary.Attributes["instance_name"])
}
for _, data := range flattenColumnFamily(table.Families) {
if data["family"] != family {
return fmt.Errorf("Error checking column family. Could not find column family %s in %s.", family, rs.Primary.Attributes["name"])
}
}

return nil
}
}

func testAccBigtableTable(instanceName, tableName string) string {
return fmt.Sprintf(`
resource "google_bigtable_instance" "instance" {
Expand Down Expand Up @@ -239,6 +389,32 @@ resource "google_bigtable_table" "table" {
`, instanceName, instanceName, tableName, family)
}

func testAccBigtableTable_deletion_protection(instanceName, tableName, deletionProtection, family string) string {
return fmt.Sprintf(`
resource "google_bigtable_instance" "instance" {
name = "%s"
cluster {
cluster_id = "%s"
zone = "us-central1-b"
}
instance_type = "DEVELOPMENT"
deletion_protection = false
}
resource "google_bigtable_table" "table" {
name = "%s"
instance_name = google_bigtable_instance.instance.name
deletion_protection = "%s"
column_family {
family = "%s"
}
}
`, instanceName, instanceName, tableName, deletionProtection, family)
}

func testAccBigtableTable_familyMany(instanceName, tableName, family string) string {
return fmt.Sprintf(`
resource "google_bigtable_instance" "instance" {
Expand Down Expand Up @@ -300,3 +476,19 @@ resource "google_bigtable_table" "table" {
}
`, instanceName, instanceName, tableName, family, family, family)
}

func testAccBigtableTable_destroyTable(instanceName string) string {
return fmt.Sprintf(`
resource "google_bigtable_instance" "instance" {
name = "%s"
cluster {
cluster_id = "%s"
zone = "us-central1-b"
}
instance_type = "DEVELOPMENT"
deletion_protection = false
}
`, instanceName, instanceName)
}

0 comments on commit 1120b9f

Please sign in to comment.