Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bigtable: Add support for abandoning GC policy #13066

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/6817.txt
@@ -0,0 +1,3 @@
```release-note:enhancement
bigtable: added support for abandoning GC policy
```
17 changes: 17 additions & 0 deletions google/resource_bigtable_gc_policy.go
Expand Up @@ -164,6 +164,15 @@ func resourceBigtableGCPolicy() *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_policy": {
Type: schema.TypeString,
Optional: true,
Description: `The deletion policy for the GC policy. Setting ABANDON allows the resource
to be abandoned rather than deleted. This is useful for GC policy as it cannot be deleted
in a replicated instance. Possible values are: "ABANDON".`,
ValidateFunc: validation.StringInSlice([]string{"ABANDON", ""}, false),
},
},
UseJSONNumber: true,
}
Expand Down Expand Up @@ -367,6 +376,14 @@ func gcPolicyToGCRuleString(gc bigtable.GCPolicy, topLevel bool) (map[string]int

func resourceBigtableGCPolicyDestroy(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

if deletionPolicy := d.Get("deletion_policy"); deletionPolicy == "ABANDON" {
// Allows for the GC policy to be abandoned without deletion to avoid possible
// deletion failure in a replicated instance.
log.Printf("[WARN] The GC policy is abandoned")
return nil
}

userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
return err
Expand Down
135 changes: 135 additions & 0 deletions google/resource_bigtable_gc_policy_test.go
Expand Up @@ -37,6 +37,39 @@ func TestAccBigtableGCPolicy_basic(t *testing.T) {
})
}

func TestAccBigtableGCPolicy_abandoned(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))
familyName := fmt.Sprintf("tf-test-%s", randString(t, 10))

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBigtableGCPolicyDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccBigtableGCPolicyToBeAbandoned(instanceName, tableName, familyName),
Check: resource.ComposeTestCheckFunc(
testAccBigtableGCPolicyExists(
t, "google_bigtable_gc_policy.policy", false),
),
},
// Verify that the remote infrastructure GC policy still exists after it is removed in the config.
{
Config: testAccBigtableGCPolicyNoPolicy(instanceName, tableName, familyName),
Check: resource.ComposeTestCheckFunc(
testAccBigtableRemoteGCPolicyExists(
t, "google_bigtable_table.table"),
),
},
},
})
}

func TestAccBigtableGCPolicy_swapOffDeprecated(t *testing.T) {
// bigtable instance does not use the shared HTTP client, this test creates an instance
skipIfVcr(t)
Expand Down Expand Up @@ -522,6 +555,46 @@ func testAccBigtableGCPolicyExists(t *testing.T, n string, compareGcRules bool)
}
}

func testAccBigtableRemoteGCPolicyExists(t *testing.T, table_name_space 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"])
}

// We expect a single local column family in the table.
family, ok := rs.Primary.Attributes["column_family.0.family"]
if !ok {
return fmt.Errorf("Error retrieving the local family")
}

for _, familyInfo := range table.FamilyInfos {
if familyInfo.Name == family {
if familyInfo.GCPolicy == "" {
return fmt.Errorf("The remote GC policy is missing in family %s", family)
}
return nil
}
}

return fmt.Errorf("Error retrieving GC policy. Could not find the column family")
}
}

func testAccBigtableCanWriteData(t *testing.T, n string, numberOfRows int) resource.TestCheckFunc {
var ctx = context.Background()
return func(s *terraform.State) error {
Expand Down Expand Up @@ -668,6 +741,68 @@ resource "google_bigtable_gc_policy" "policy" {
`, instanceName, instanceName, tableName, family, family)
}

func testAccBigtableGCPolicyToBeAbandoned(instanceName, tableName, 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.id

column_family {
family = "%s"
}
}

resource "google_bigtable_gc_policy" "policy" {
instance_name = google_bigtable_instance.instance.id
table = google_bigtable_table.table.name
column_family = "%s"

max_age {
duration = "72h"
}

deletion_policy = "ABANDON"
}
`, instanceName, instanceName, tableName, family, family)
}

func testAccBigtableGCPolicyNoPolicy(instanceName, tableName, 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.id

column_family {
family = "%s"
}
}
`, instanceName, instanceName, tableName, family)
}

func testAccBigtableGCPolicyUnion(instanceName, tableName, family string) string {
return fmt.Sprintf(`
resource "google_bigtable_instance" "instance" {
Expand Down
8 changes: 8 additions & 0 deletions website/docs/r/bigtable_gc_policy.html.markdown
Expand Up @@ -45,6 +45,7 @@ resource "google_bigtable_gc_policy" "policy" {
instance_name = google_bigtable_instance.instance.name
table = google_bigtable_table.table.name
column_family = "name"
deletion_policy = "ABANDON"

max_age {
duration = "168h"
Expand All @@ -59,6 +60,7 @@ resource "google_bigtable_gc_policy" "policy" {
instance_name = google_bigtable_instance.instance.name
table = google_bigtable_table.table.name
column_family = "name"
deletion_policy = "ABANDON"

mode = "UNION"

Expand Down Expand Up @@ -101,6 +103,7 @@ resource "google_bigtable_gc_policy" "policy" {
instance_name = google_bigtable_instance.instance.id
table = google_bigtable_table.table.name
column_family = "cf1"
deletion_policy = "ABANDON"

gc_rules = <<EOF
{
Expand Down Expand Up @@ -151,6 +154,11 @@ The following arguments are supported:

* `gc_rules` - (Optional) Serialized JSON object to represent a more complex GC policy. Conflicts with `mode`, `max_age` and `max_version`. Conflicts with `mode`, `max_age` and `max_version`.

* `deletion_policy` - (Optional) The deletion policy for the GC policy.
Setting ABANDON allows the resource to be abandoned rather than deleted. This is useful for GC policy as it cannot be deleted in a replicated instance.

Possible values are: `ABANDON`.

-----

`max_age` supports the following arguments:
Expand Down