diff --git a/products/bigtable/terraform.yaml b/products/bigtable/terraform.yaml index be9ec743dc8e..307199112a19 100644 --- a/products/bigtable/terraform.yaml +++ b/products/bigtable/terraform.yaml @@ -28,6 +28,11 @@ overrides: !ruby/object:Overrides::ResourceOverrides vars: instance_name: "bt-instance" app_profile_name: "bt-profile" + deletion_protection: "true" + test_vars_overrides: + deletion_protection: "false" + oics_vars_overrides: + deletion_protection: "false" ignore_read_extra: - "ignore_warnings" - !ruby/object:Provider::Terraform::Examples @@ -36,6 +41,11 @@ overrides: !ruby/object:Overrides::ResourceOverrides vars: instance_name: "bt-instance" app_profile_name: "bt-profile" + deletion_protection: "true" + test_vars_overrides: + deletion_protection: "false" + oics_vars_overrides: + deletion_protection: "false" ignore_read_extra: - "ignore_warnings" properties: diff --git a/provider/terraform/examples.rb b/provider/terraform/examples.rb index 83c0283e0f43..73b1fc36b17a 100644 --- a/provider/terraform/examples.rb +++ b/provider/terraform/examples.rb @@ -81,6 +81,10 @@ class Examples < Api::Object # } attr_reader :test_vars_overrides + # Hash to provider custom override values for generating oics config + # See test_vars_overrides for more details + attr_reader :oics_vars_overrides + # The version name of of the example's version if it's different than the # resource version, eg. `beta` # @@ -193,10 +197,14 @@ def config_test_body def config_example @vars ||= [] + @oics_vars_overrides ||= {} + + rand_vars = vars.map { |k, str| [k, "#{str}-${local.name_suffix}"] }.to_h + # Examples with test_env_vars are skipped elsewhere body = lines(compile_file( { - vars: vars.map { |k, str| [k, "#{str}-${local.name_suffix}"] }.to_h, + vars: rand_vars.merge(oics_vars_overrides), primary_resource_id: primary_resource_id }, config_path diff --git a/templates/terraform/examples/bigtable_app_profile_multicluster.tf.erb b/templates/terraform/examples/bigtable_app_profile_multicluster.tf.erb index 52748eeb377b..4314b7728b5e 100644 --- a/templates/terraform/examples/bigtable_app_profile_multicluster.tf.erb +++ b/templates/terraform/examples/bigtable_app_profile_multicluster.tf.erb @@ -6,6 +6,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = 3 storage_type = "HDD" } + + deletion_protection = "<%= ctx[:vars]['deletion_protection'] %>" } resource "google_bigtable_app_profile" "ap" { diff --git a/templates/terraform/examples/bigtable_app_profile_singlecluster.tf.erb b/templates/terraform/examples/bigtable_app_profile_singlecluster.tf.erb index 3aee678a14fe..3e76ca678e37 100644 --- a/templates/terraform/examples/bigtable_app_profile_singlecluster.tf.erb +++ b/templates/terraform/examples/bigtable_app_profile_singlecluster.tf.erb @@ -6,6 +6,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = 3 storage_type = "HDD" } + + deletion_protection = "<%= ctx[:vars]['deletion_protection'] %>" } resource "google_bigtable_app_profile" "ap" { diff --git a/third_party/terraform/resources/resource_bigtable_instance.go b/third_party/terraform/resources/resource_bigtable_instance.go index 33e129aaa3e6..ece530e46b58 100644 --- a/third_party/terraform/resources/resource_bigtable_instance.go +++ b/third_party/terraform/resources/resource_bigtable_instance.go @@ -27,6 +27,15 @@ func resourceBigtableInstance() *schema.Resource { resourceBigtableInstanceClusterReorderTypeList, ), + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Type: resourceBigtableInstanceResourceV0().CoreConfigSchema().ImpliedType(), + Upgrade: resourceBigtableInstanceUpgradeV0, + Version: 0, + }, + }, + // ---------------------------------------------------------------------- // IMPORTANT: Do not add any additional ForceNew fields to this resource. // Destroying/recreating instances can lead to data loss for users. @@ -82,6 +91,12 @@ func resourceBigtableInstance() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"DEVELOPMENT", "PRODUCTION"}, false), }, + "deletion_protection": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "project": { Type: schema.TypeString, Optional: true, @@ -235,6 +250,9 @@ func resourceBigtableInstanceUpdate(d *schema.ResourceData, meta interface{}) er } func resourceBigtableInstanceDestroy(d *schema.ResourceData, meta interface{}) error { + if d.Get("deletion_protection").(bool) { + return fmt.Errorf("cannot destroy instance without setting deletion_protection=false and running `terraform apply`") + } config := meta.(*Config) ctx := context.Background() diff --git a/third_party/terraform/resources/resource_bigtable_instance_migrate.go b/third_party/terraform/resources/resource_bigtable_instance_migrate.go new file mode 100644 index 000000000000..ebaaf741be54 --- /dev/null +++ b/third_party/terraform/resources/resource_bigtable_instance_migrate.go @@ -0,0 +1,80 @@ +package google + +import ( + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceBigtableInstanceResourceV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "cluster": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cluster_id": { + Type: schema.TypeString, + Required: true, + }, + "zone": { + Type: schema.TypeString, + Required: true, + }, + "num_nodes": { + Type: schema.TypeInt, + Optional: true, + // DEVELOPMENT instances could get returned with either zero or one node, + // so mark as computed. + Computed: true, + ValidateFunc: validation.IntAtLeast(1), + }, + "storage_type": { + Type: schema.TypeString, + Optional: true, + Default: "SSD", + ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false), + }, + }, + }, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "instance_type": { + Type: schema.TypeString, + Optional: true, + Default: "PRODUCTION", + ValidateFunc: validation.StringInSlice([]string{"DEVELOPMENT", "PRODUCTION"}, false), + }, + + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceBigtableInstanceUpgradeV0(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + log.Printf("[DEBUG] Attributes before migration: %#v", rawState) + + rawState["deletion_protection"] = true + + log.Printf("[DEBUG] Attributes after migration: %#v", rawState) + return rawState, nil +} diff --git a/third_party/terraform/tests/resource_bigtable_app_profile_test.go b/third_party/terraform/tests/resource_bigtable_app_profile_test.go index f13de63f0d1c..665095e5de98 100644 --- a/third_party/terraform/tests/resource_bigtable_app_profile_test.go +++ b/third_party/terraform/tests/resource_bigtable_app_profile_test.go @@ -49,6 +49,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = 3 storage_type = "HDD" } + + deletion_protection = false } resource "google_bigtable_app_profile" "ap" { @@ -71,6 +73,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = 3 storage_type = "HDD" } + + deletion_protection = false } resource "google_bigtable_app_profile" "ap" { diff --git a/third_party/terraform/tests/resource_bigtable_gc_policy_test.go b/third_party/terraform/tests/resource_bigtable_gc_policy_test.go index fc1596f77aa7..367d2e94e1fa 100644 --- a/third_party/terraform/tests/resource_bigtable_gc_policy_test.go +++ b/third_party/terraform/tests/resource_bigtable_gc_policy_test.go @@ -136,6 +136,7 @@ resource "google_bigtable_instance" "instance" { } instance_type = "DEVELOPMENT" + deletion_protection = false } resource "google_bigtable_table" "table" { @@ -170,6 +171,7 @@ resource "google_bigtable_instance" "instance" { } instance_type = "DEVELOPMENT" + deletion_protection = false } resource "google_bigtable_table" "table" { diff --git a/third_party/terraform/tests/resource_bigtable_instance_iam_test.go b/third_party/terraform/tests/resource_bigtable_instance_iam_test.go index 3e4eb089ddde..b5ae8846f1ee 100644 --- a/third_party/terraform/tests/resource_bigtable_instance_iam_test.go +++ b/third_party/terraform/tests/resource_bigtable_instance_iam_test.go @@ -209,5 +209,7 @@ resource "google_bigtable_instance" "instance" { zone = "us-central1-b" storage_type = "HDD" } + + deletion_protection = false } ` diff --git a/third_party/terraform/tests/resource_bigtable_instance_test.go b/third_party/terraform/tests/resource_bigtable_instance_test.go index e90e1442f15e..377fcbb3c932 100644 --- a/third_party/terraform/tests/resource_bigtable_instance_test.go +++ b/third_party/terraform/tests/resource_bigtable_instance_test.go @@ -31,7 +31,7 @@ func TestAccBigtableInstance_basic(t *testing.T) { ResourceName: "google_bigtable_instance.instance", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"instance_type"}, // we don't read instance type back + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back }, { Config: testAccBigtableInstance(instanceName, 4), @@ -40,7 +40,7 @@ func TestAccBigtableInstance_basic(t *testing.T) { ResourceName: "google_bigtable_instance.instance", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"instance_type"}, // we don't read instance type back + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back }, }, }) @@ -67,7 +67,7 @@ func TestAccBigtableInstance_cluster(t *testing.T) { ResourceName: "google_bigtable_instance.instance", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"instance_type"}, // we don't read instance type back + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back }, { Config: testAccBigtableInstance_clusterReordered(instanceName, 5), @@ -76,7 +76,7 @@ func TestAccBigtableInstance_cluster(t *testing.T) { ResourceName: "google_bigtable_instance.instance", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"instance_type"}, // we don't read instance type back + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back }, { Config: testAccBigtableInstance_clusterModified(instanceName, 5), @@ -85,7 +85,7 @@ func TestAccBigtableInstance_cluster(t *testing.T) { ResourceName: "google_bigtable_instance.instance", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"instance_type"}, // we don't read instance type back + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back }, { Config: testAccBigtableInstance_clusterReordered(instanceName, 5), @@ -94,7 +94,7 @@ func TestAccBigtableInstance_cluster(t *testing.T) { ResourceName: "google_bigtable_instance.instance", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"instance_type"}, // we don't read instance type back + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back }, }, }) @@ -117,7 +117,38 @@ func TestAccBigtableInstance_development(t *testing.T) { ResourceName: "google_bigtable_instance.instance", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"instance_type"}, // we don't read instance type back + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back + }, + }, + }) +} + +func TestAccBigtableInstance_allowDestroy(t *testing.T) { + t.Parallel() + + instanceName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigtableInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigtableInstance_noAllowDestroy(instanceName, 3), + }, + { + ResourceName: "google_bigtable_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection", "instance_type"}, // we don't read instance type back + }, + { + Config: testAccBigtableInstance_noAllowDestroy(instanceName, 3), + Destroy: true, + ExpectError: regexp.MustCompile("deletion_protection"), + }, + { + Config: testAccBigtableInstance(instanceName, 3), }, }, }) @@ -159,6 +190,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = %d storage_type = "HDD" } + + deletion_protection = false } `, instanceName, instanceName, numNodes) } @@ -199,6 +232,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = %d storage_type = "HDD" } + + deletion_protection = false } `, instanceName, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes) } @@ -237,6 +272,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = 3 storage_type = "HDD" } + + deletion_protection = false } `, instanceName, instanceName, instanceName, instanceName, instanceName, instanceName) } @@ -269,6 +306,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = %d storage_type = "HDD" } + + deletion_protection = false } `, instanceName, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes) } @@ -295,6 +334,8 @@ resource "google_bigtable_instance" "instance" { num_nodes = %d storage_type = "HDD" } + + deletion_protection = false } `, instanceName, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes) } @@ -308,6 +349,22 @@ resource "google_bigtable_instance" "instance" { zone = "us-central1-b" } instance_type = "DEVELOPMENT" + + deletion_protection = false } `, instanceName, instanceName) } + +func testAccBigtableInstance_noAllowDestroy(instanceName string, numNodes int) string { + return fmt.Sprintf(` +resource "google_bigtable_instance" "instance" { + name = "%s" + cluster { + cluster_id = "%s" + zone = "us-central1-b" + num_nodes = %d + storage_type = "HDD" + } +} +`, instanceName, instanceName, numNodes) +} diff --git a/third_party/terraform/tests/resource_bigtable_table_test.go b/third_party/terraform/tests/resource_bigtable_table_test.go index ccb8cba00c12..0fbf709f856b 100644 --- a/third_party/terraform/tests/resource_bigtable_table_test.go +++ b/third_party/terraform/tests/resource_bigtable_table_test.go @@ -172,6 +172,8 @@ resource "google_bigtable_instance" "instance" { cluster_id = "%s" zone = "us-central1-b" } + + deletion_protection = false } resource "google_bigtable_table" "table" { @@ -190,6 +192,8 @@ resource "google_bigtable_instance" "instance" { cluster_id = "%s" zone = "us-central1-b" } + + deletion_protection = false } resource "google_bigtable_table" "table" { @@ -211,6 +215,7 @@ resource "google_bigtable_instance" "instance" { } instance_type = "DEVELOPMENT" + deletion_protection = false } resource "google_bigtable_table" "table" { @@ -235,6 +240,7 @@ resource "google_bigtable_instance" "instance" { } instance_type = "DEVELOPMENT" + deletion_protection = false } resource "google_bigtable_table" "table" { @@ -263,6 +269,7 @@ resource "google_bigtable_instance" "instance" { } instance_type = "DEVELOPMENT" + deletion_protection = false } resource "google_bigtable_table" "table" { diff --git a/third_party/terraform/website/docs/r/bigtable_instance.html.markdown b/third_party/terraform/website/docs/r/bigtable_instance.html.markdown index 724989800831..2e975f41f455 100644 --- a/third_party/terraform/website/docs/r/bigtable_instance.html.markdown +++ b/third_party/terraform/website/docs/r/bigtable_instance.html.markdown @@ -18,6 +18,10 @@ on instances in order to prevent accidental data loss. See [Terraform docs](https://www.terraform.io/docs/configuration/resources.html#prevent_destroy) for more information on lifecycle parameters. +-> **Note**: On newer versions of the provider, you must explicitly set `deletion_protection=false` +(and run `terraform apply` to write the field to state) in order to destroy an instance. +It is recommended to not set this field (or set it to true) until you're ready to destroy. + ## Example Usage - Production Instance @@ -50,10 +54,6 @@ resource "google_bigtable_instance" "development-instance" { zone = "us-central1-b" storage_type = "HDD" } - - lifecycle { - prevent_destroy = true - } } ``` @@ -74,6 +74,9 @@ The following arguments are supported: * `display_name` - (Optional) The human-readable display name of the Bigtable instance. Defaults to the instance `name`. +* `deletion_protection` - (Optional) Whether or not to allow Terraform to destroy the instance. Unless this field is set to false +in Terraform state, a `terraform destroy` or `terraform apply` that would delete the instance will fail. + -----