From 8dff4dadcb01c16da28313f0a510bbca82f10c57 Mon Sep 17 00:00:00 2001 From: wj-chen Date: Tue, 16 Apr 2024 15:40:54 -0700 Subject: [PATCH] Add resource tags to BigQuery Table (#10455) --- mmv1/products/bigquery/Table.yaml | 8 + ...able.go => resource_bigquery_table.go.erb} | 37 +++- ...go => resource_bigquery_table_test.go.erb} | 187 ++++++++++++++++++ 3 files changed, 231 insertions(+), 1 deletion(-) rename mmv1/third_party/terraform/services/bigquery/{resource_bigquery_table.go => resource_bigquery_table.go.erb} (98%) rename mmv1/third_party/terraform/services/bigquery/{resource_bigquery_table_test.go => resource_bigquery_table_test.go.erb} (95%) diff --git a/mmv1/products/bigquery/Table.yaml b/mmv1/products/bigquery/Table.yaml index 6913bf4f2dbc..53403f29a895 100644 --- a/mmv1/products/bigquery/Table.yaml +++ b/mmv1/products/bigquery/Table.yaml @@ -508,3 +508,11 @@ properties: - :INTEGER - :FLOAT - :BOOLEAN + - !ruby/object:Api::Type::KeyValuePairs + name: 'resourceTags' + min_version: beta + description: | + The tags attached to this table. Tag keys are globally unique. Tag key is expected to be + in the namespaced format, for example "123456789012/environment" where 123456789012 is the + ID of the parent organization or project resource for this tag key. Tag value is expected + to be the short name, for example "Production". diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go.erb similarity index 98% rename from mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go rename to mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go.erb index b39be4116a79..a9ebe7804de4 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package bigquery import ( @@ -1305,6 +1306,14 @@ func ResourceBigQueryTable() *schema.Resource { }, }, }, + <% unless version == 'ga' -%> + "resource_tags": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: `The tags attached to this table. Tag keys are globally unique. Tag key is expected to be in the namespaced format, for example "123456789012/environment" where 123456789012 is the ID of the parent organization or project resource for this tag key. Tag value is expected to be the short name, for example "Production".`, + }, + <% end -%> }, UseJSONNumber: true, } @@ -1419,6 +1428,10 @@ func resourceTable(d *schema.ResourceData, meta interface{}) (*bigquery.Table, e table.TableConstraints = tableConstraints } + <% unless version == 'ga' -%> + table.ResourceTags = tpgresource.ExpandStringMap(d, "resource_tags") + + <% end -%> return table, nil } @@ -1692,6 +1705,12 @@ func resourceBigQueryTableRead(d *schema.ResourceData, meta interface{}) error { } } + <% unless version == 'ga' -%> + if err := d.Set("resource_tags", res.ResourceTags); err != nil { + return fmt.Errorf("Error setting resource tags: %s", err) + } + + <% end -%> // TODO: Update when the Get API fields for TableReplicationInfo are available in the client library. url, err := tpgresource.ReplaceVars(d, config, "{{BigQueryBasePath}}projects/{{project}}/datasets/{{dataset_id}}/tables/{{table_id}}") if err != nil { @@ -1772,6 +1791,10 @@ func resourceBigQueryTableColumnDrop(config *transport_tpg.Config, userAgent str return err } + if table.Schema == nil { + return nil + } + newTableFields := map[string]bool{} for _, field := range table.Schema.Fields { newTableFields[field.Name] = true @@ -1807,8 +1830,20 @@ func resourceBigQueryTableColumnDrop(config *transport_tpg.Config, userAgent str func resourceBigQueryTableDelete(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`") + return fmt.Errorf("cannot destroy table %v without setting deletion_protection=false and running `terraform apply`", d.Id()) + } + <% unless version == 'ga' -%> + if v, ok := d.GetOk("resource_tags"); ok { + var resourceTags []string + + for k, v := range v.(map[string]interface{}) { + resourceTags = append(resourceTags, fmt.Sprintf("%s:%s", k, v.(string))) + } + + return fmt.Errorf("cannot destroy table %v without clearing the following resource tags: %v", d.Id(), resourceTags) } + + <% end -%> config := meta.(*transport_tpg.Config) userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) if err != nil { diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go.erb similarity index 95% rename from mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go rename to mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go.erb index 778ecf40b3d5..3a7790cdcd36 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package bigquery_test import ( @@ -1552,6 +1553,58 @@ func TestAccBigQueryTable_TableReplicationInfo_WithReplicationInterval(t *testin }) } +<% unless version == 'ga' -%> +func TestAccBigQueryTable_ResourceTags(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": envvar.GetTestProjectFromEnv(), + "dataset_id": fmt.Sprintf("tf_test_dataset_%s", acctest.RandString(t, 10)), + "table_id" : fmt.Sprintf("tf_test_table_%s", acctest.RandString(t, 10)), + "tag_key_name1": fmt.Sprintf("tf_test_tag_key1_%s", acctest.RandString(t, 10)), + "tag_value_name1": fmt.Sprintf("tf_test_tag_value1_%s", acctest.RandString(t, 10)), + "tag_key_name2": fmt.Sprintf("tf_test_tag_key2_%s", acctest.RandString(t, 10)), + "tag_value_name2": fmt.Sprintf("tf_test_tag_value2_%s", acctest.RandString(t, 10)), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + CheckDestroy: testAccCheckBigQueryTableDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigQueryTableWithResourceTags(context), + }, + { + ResourceName: "google_bigquery_table.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + { + Config: testAccBigQueryTableWithResourceTagsUpdate(context), + }, + { + ResourceName: "google_bigquery_table.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + // testAccBigQueryTableWithResourceTagsDestroy must be called at the end of this test to clear the resource tag bindings of the table before deletion. + { + Config: testAccBigQueryTableWithResourceTagsDestroy(context), + }, + { + ResourceName: "google_bigquery_table.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + +<% end -%> func testAccCheckBigQueryExtData(t *testing.T, expectedQuoteChar string) resource.TestCheckFunc { return func(s *terraform.State) error { for _, rs := range s.RootModule().Resources { @@ -3921,6 +3974,140 @@ resource "time_sleep" "wait_10_seconds_last" { `, sourceDatasetID, sourceTableID, sourceMVJobID, sourceDatasetID, sourceMVID, sourceDatasetID, sourceTableID, projectID, sourceMVID, replicaDatasetID, replicaMVID, projectID, sourceMVID, replicationIntervalExpr, dropMVJobID, sourceDatasetID, sourceMVID) } +<% unless version == 'ga' -%> +func testAccBigQueryTableWithResourceTags(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_tags_tag_key" "key1" { + provider = google-beta + + parent = "projects/%{project_id}" + short_name = "%{tag_key_name1}" +} + +resource "google_tags_tag_value" "value1" { + provider = google-beta + + parent = "tagKeys/${google_tags_tag_key.key1.name}" + short_name = "%{tag_value_name1}" +} + +resource "google_bigquery_dataset" "test" { + provider = google-beta + + dataset_id = "%{dataset_id}" +} + +resource "google_bigquery_table" "test" { + provider = google-beta + + deletion_protection = false + dataset_id = "${google_bigquery_dataset.test.dataset_id}" + table_id = "%{table_id}" + resource_tags = { + "%{project_id}/${google_tags_tag_key.key1.short_name}" = "${google_tags_tag_value.value1.short_name}" + } +} +`, context) +} + +func testAccBigQueryTableWithResourceTagsUpdate(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_tags_tag_key" "key1" { + provider = google-beta + + parent = "projects/%{project_id}" + short_name = "%{tag_key_name1}" +} + +resource "google_tags_tag_value" "value1" { + provider = google-beta + + parent = "tagKeys/${google_tags_tag_key.key1.name}" + short_name = "%{tag_value_name1}" +} + +resource "google_tags_tag_key" "key2" { + provider = google-beta + + parent = "projects/%{project_id}" + short_name = "%{tag_key_name2}" +} + +resource "google_tags_tag_value" "value2" { + provider = google-beta + + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "%{tag_value_name2}" +} + +resource "google_bigquery_dataset" "test" { + provider = google-beta + + dataset_id = "%{dataset_id}" +} + +resource "google_bigquery_table" "test" { + provider = google-beta + + deletion_protection = false + dataset_id = "${google_bigquery_dataset.test.dataset_id}" + table_id = "%{table_id}" + resource_tags = { + "%{project_id}/${google_tags_tag_key.key1.short_name}" = "${google_tags_tag_value.value1.short_name}" + "%{project_id}/${google_tags_tag_key.key2.short_name}" = "${google_tags_tag_value.value2.short_name}" + } +} +`, context) +} + +func testAccBigQueryTableWithResourceTagsDestroy(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_tags_tag_key" "key1" { + provider = google-beta + + parent = "projects/%{project_id}" + short_name = "%{tag_key_name1}" +} + +resource "google_tags_tag_value" "value1" { + provider = google-beta + + parent = "tagKeys/${google_tags_tag_key.key1.name}" + short_name = "%{tag_value_name1}" +} + +resource "google_tags_tag_key" "key2" { + provider = google-beta + + parent = "projects/%{project_id}" + short_name = "%{tag_key_name2}" +} + +resource "google_tags_tag_value" "value2" { + provider = google-beta + + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "%{tag_value_name2}" +} + +resource "google_bigquery_dataset" "test" { + provider = google-beta + + dataset_id = "%{dataset_id}" +} + +resource "google_bigquery_table" "test" { + provider = google-beta + + deletion_protection = false + dataset_id = "${google_bigquery_dataset.test.dataset_id}" + table_id = "%{table_id}" + resource_tags = {} +} +`, context) +} + +<% end -%> var TEST_CSV = `lifelock,LifeLock,,web,Tempe,AZ,1-May-07,6850000,USD,b lifelock,LifeLock,,web,Tempe,AZ,1-Oct-06,6000000,USD,a lifelock,LifeLock,,web,Tempe,AZ,1-Jan-08,25000000,USD,c