Skip to content

Commit 22ddb34

Browse files
Support for Tags in google_dataproc_metastore_service resource (#14620) (#10854)
[upstream:707ee8f775d48f6b7750044d61a605ec50ca7520] Signed-off-by: Modular Magician <[email protected]>
1 parent df76686 commit 22ddb34

11 files changed

+427
-19
lines changed

.changelog/14620.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
metastore: added `tags` field to `google_dataproc_metastore_service` and 'google_dataproc_metastore_federation' resources to allow setting tags for services and federation at creation time
3+
```

google-beta/services/dataprocmetastore/resource_dataproc_metastore_federation.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,15 @@ Please refer to the field 'effective_labels' for all of the labels present on th
111111
ForceNew: true,
112112
Description: `The location where the metastore federation should reside.`,
113113
},
114+
"tags": {
115+
Type: schema.TypeMap,
116+
Optional: true,
117+
ForceNew: true,
118+
Description: `A map of resource manager tags.
119+
Resource manager tag keys and values have the same definition as resource manager tags.
120+
Keys must be in the format tagKeys/{tag_key_id}, and values are in the format tagValues/{tag_value_id}.`,
121+
Elem: &schema.Schema{Type: schema.TypeString},
122+
},
114123
"create_time": {
115124
Type: schema.TypeString,
116125
Computed: true,
@@ -198,6 +207,12 @@ func resourceDataprocMetastoreFederationCreate(d *schema.ResourceData, meta inte
198207
} else if v, ok := d.GetOkExists("backend_metastores"); !tpgresource.IsEmptyValue(reflect.ValueOf(backendMetastoresProp)) && (ok || !reflect.DeepEqual(v, backendMetastoresProp)) {
199208
obj["backendMetastores"] = backendMetastoresProp
200209
}
210+
tagsProp, err := expandDataprocMetastoreFederationTags(d.Get("tags"), d, config)
211+
if err != nil {
212+
return err
213+
} else if v, ok := d.GetOkExists("tags"); !tpgresource.IsEmptyValue(reflect.ValueOf(tagsProp)) && (ok || !reflect.DeepEqual(v, tagsProp)) {
214+
obj["tags"] = tagsProp
215+
}
201216
effectiveLabelsProp, err := expandDataprocMetastoreFederationEffectiveLabels(d.Get("effective_labels"), d, config)
202217
if err != nil {
203218
return err
@@ -655,6 +670,17 @@ func expandDataprocMetastoreFederationBackendMetastoresMetastoreType(v interface
655670
return v, nil
656671
}
657672

673+
func expandDataprocMetastoreFederationTags(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
674+
if v == nil {
675+
return map[string]string{}, nil
676+
}
677+
m := make(map[string]string)
678+
for k, val := range v.(map[string]interface{}) {
679+
m[k] = val.(string)
680+
}
681+
return m, nil
682+
}
683+
658684
func expandDataprocMetastoreFederationEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
659685
if v == nil {
660686
return map[string]string{}, nil

google-beta/services/dataprocmetastore/resource_dataproc_metastore_federation_generated_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ fields:
2121
- field: 'name'
2222
- field: 'state'
2323
- field: 'state_message'
24+
- field: 'tags'
2425
- field: 'terraform_labels'
2526
provider_only: true
2627
- field: 'uid'

google-beta/services/dataprocmetastore/resource_dataproc_metastore_federation_generated_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestAccDataprocMetastoreFederation_dataprocMetastoreFederationBasicExample(
4949
ResourceName: "google_dataproc_metastore_federation.default",
5050
ImportState: true,
5151
ImportStateVerify: true,
52-
ImportStateVerifyIgnore: []string{"deletion_protection", "federation_id", "labels", "location", "terraform_labels"},
52+
ImportStateVerifyIgnore: []string{"deletion_protection", "federation_id", "labels", "location", "tags", "terraform_labels"},
5353
},
5454
},
5555
})
@@ -103,7 +103,7 @@ func TestAccDataprocMetastoreFederation_dataprocMetastoreFederationBigqueryExamp
103103
ResourceName: "google_dataproc_metastore_federation.default",
104104
ImportState: true,
105105
ImportStateVerify: true,
106-
ImportStateVerifyIgnore: []string{"federation_id", "labels", "location", "terraform_labels"},
106+
ImportStateVerifyIgnore: []string{"federation_id", "labels", "location", "tags", "terraform_labels"},
107107
},
108108
},
109109
})

google-beta/services/dataprocmetastore/resource_dataproc_metastore_federation_test.go

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ package dataprocmetastore_test
2020

2121
import (
2222
"fmt"
23-
"github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest"
23+
"net/url"
2424
"regexp"
2525
"testing"
2626

2727
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
28+
"github.com/hashicorp/terraform-plugin-testing/terraform"
29+
"github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest"
30+
"github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar"
31+
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
32+
"strings"
2833
)
2934

3035
func TestAccMetastoreFederation_deletionprotection(t *testing.T) {
@@ -62,6 +67,37 @@ func TestAccMetastoreFederation_deletionprotection(t *testing.T) {
6267
})
6368
}
6469

70+
func TestAccMetastoreFederation_tags(t *testing.T) {
71+
t.Parallel()
72+
tagKey := acctest.BootstrapSharedTestOrganizationTagKey(t, "metastore-fed-tagkey", map[string]interface{}{})
73+
context := map[string]interface{}{
74+
"random_suffix": acctest.RandString(t, 10),
75+
"org": envvar.GetTestOrgFromEnv(t),
76+
"tagKey": tagKey,
77+
"tagValue": acctest.BootstrapSharedTestOrganizationTagValue(t, "metastore-fed-tagvalue", tagKey),
78+
}
79+
acctest.VcrTest(t, resource.TestCase{
80+
PreCheck: func() { acctest.AccTestPreCheck(t) },
81+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
82+
CheckDestroy: testAccCheckDataprocMetastoreFederationDestroyProducer(t),
83+
Steps: []resource.TestStep{
84+
{
85+
Config: testAccMetastoreFederationTags(context),
86+
Check: resource.ComposeTestCheckFunc(
87+
resource.TestCheckResourceAttrSet("google_dataproc_metastore_federation.default", "tags.%"),
88+
testAccCheckMetastoreFederationHasTagBindings(t),
89+
),
90+
},
91+
{
92+
ResourceName: "google_dataproc_metastore_federation.default",
93+
ImportState: true,
94+
ImportStateVerify: true,
95+
ImportStateVerifyIgnore: []string{"federation_id", "location", "tags"},
96+
},
97+
},
98+
})
99+
}
100+
65101
func testAccMetastoreFederationDeletionProtection(name string, location string) string {
66102

67103
return fmt.Sprintf(`
@@ -113,3 +149,136 @@ func testAccMetastoreFederationDeletionProtectionFalse(name string, location str
113149
}
114150
`, name, name, location)
115151
}
152+
153+
func testAccCheckMetastoreFederationHasTagBindings(t *testing.T) func(s *terraform.State) error {
154+
return func(s *terraform.State) error {
155+
for name, rs := range s.RootModule().Resources {
156+
if rs.Type != "google_dataproc_metastore_federation" {
157+
continue
158+
}
159+
if strings.HasPrefix(name, "data.") {
160+
continue
161+
}
162+
163+
config := acctest.GoogleProviderConfig(t)
164+
165+
var configuredTagValueNamespacedName string
166+
var tagKeyNamespacedName, tagValueShortName string
167+
for key, val := range rs.Primary.Attributes {
168+
if strings.HasPrefix(key, "tags.") && key != "tags.%" {
169+
tagKeyNamespacedName = strings.TrimPrefix(key, "tags.")
170+
tagValueShortName = val
171+
if tagValueShortName != "" {
172+
configuredTagValueNamespacedName = fmt.Sprintf("%s/%s", tagKeyNamespacedName, tagValueShortName)
173+
break
174+
}
175+
}
176+
}
177+
178+
if configuredTagValueNamespacedName == "" {
179+
return fmt.Errorf("could not find a configured tag value in the state for resource %s", rs.Primary.ID)
180+
}
181+
182+
if strings.Contains(configuredTagValueNamespacedName, "%{") {
183+
return fmt.Errorf("tag namespaced name contains unsubstituted variables: %q. Ensure the context map in the test step is populated", configuredTagValueNamespacedName)
184+
}
185+
186+
safeNamespacedName := url.QueryEscape(configuredTagValueNamespacedName)
187+
describeTagValueURL := fmt.Sprintf("https://cloudresourcemanager.googleapis.com/v3/tagValues/namespaced?name=%s", safeNamespacedName)
188+
189+
respDescribe, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
190+
Config: config,
191+
Method: "GET",
192+
RawURL: describeTagValueURL,
193+
UserAgent: config.UserAgent,
194+
})
195+
196+
if err != nil {
197+
return fmt.Errorf("error describing tag value using namespaced name %q: %v", configuredTagValueNamespacedName, err)
198+
}
199+
200+
fullTagValueName, ok := respDescribe["name"].(string)
201+
if !ok || fullTagValueName == "" {
202+
return fmt.Errorf("tag value details (name) not found in response for namespaced name: %q, response: %v", configuredTagValueNamespacedName, respDescribe)
203+
}
204+
205+
parts := strings.Split(rs.Primary.ID, "/")
206+
if len(parts) != 6 {
207+
return fmt.Errorf("invalid resource ID format: %s", rs.Primary.ID)
208+
}
209+
project := parts[1]
210+
location := parts[3]
211+
federation_id := parts[5]
212+
213+
parentURL := fmt.Sprintf("//metastore.googleapis.com/projects/%s/locations/%s/federations/%s", project, location, federation_id)
214+
listBindingsURL := fmt.Sprintf("https://%s-cloudresourcemanager.googleapis.com/v3/tagBindings?parent=%s", location, url.QueryEscape(parentURL))
215+
216+
resp, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
217+
Config: config,
218+
Method: "GET",
219+
RawURL: listBindingsURL,
220+
UserAgent: config.UserAgent,
221+
})
222+
223+
if err != nil {
224+
return fmt.Errorf("error calling TagBindings API: %v", err)
225+
}
226+
227+
tagBindingsVal, exists := resp["tagBindings"]
228+
if !exists {
229+
tagBindingsVal = []interface{}{}
230+
}
231+
232+
tagBindings, ok := tagBindingsVal.([]interface{})
233+
if !ok {
234+
return fmt.Errorf("'tagBindings' is not a slice in response for resource %s. Response: %v", rs.Primary.ID, resp)
235+
}
236+
237+
foundMatch := false
238+
for _, binding := range tagBindings {
239+
bindingMap, ok := binding.(map[string]interface{})
240+
if !ok {
241+
continue
242+
}
243+
if bindingMap["tagValue"] == fullTagValueName {
244+
foundMatch = true
245+
break
246+
}
247+
}
248+
249+
if !foundMatch {
250+
return fmt.Errorf("expected tag value %s (from namespaced %q) not found in tag bindings for resource %s. Bindings: %v", fullTagValueName, configuredTagValueNamespacedName, rs.Primary.ID, tagBindings)
251+
}
252+
253+
t.Logf("Successfully found matching tag binding for %s with tagValue %s", rs.Primary.ID, fullTagValueName)
254+
}
255+
256+
return nil
257+
}
258+
}
259+
260+
func testAccMetastoreFederationTags(context map[string]interface{}) string {
261+
return acctest.Nprintf(`
262+
resource "google_dataproc_metastore_service" "backend" {
263+
service_id = "tf-test-service-%{random_suffix}"
264+
location = "us-east1"
265+
tier = "DEVELOPER"
266+
hive_metastore_config {
267+
version = "3.1.2"
268+
endpoint_protocol = "GRPC"
269+
}
270+
}
271+
resource "google_dataproc_metastore_federation" "default" {
272+
federation_id = "tf-test-federation-%{random_suffix}"
273+
location = "us-east1"
274+
version = "3.1.2"
275+
backend_metastores {
276+
name = google_dataproc_metastore_service.backend.id
277+
metastore_type = "DATAPROC_METASTORE"
278+
rank = "1"
279+
}
280+
tags = {
281+
"%{org}/%{tagKey}" = "%{tagValue}"
282+
}
283+
}`, context)
284+
}

google-beta/services/dataprocmetastore/resource_dataproc_metastore_service.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,15 @@ There must be at least one IP address available in the subnet's primary range. T
421421
},
422422
},
423423
},
424+
"tags": {
425+
Type: schema.TypeMap,
426+
Optional: true,
427+
ForceNew: true,
428+
Description: `A map of resource manager tags.
429+
Resource manager tag keys and values have the same definition as resource manager tags.
430+
Keys must be in the format tagKeys/{tag_key_id}, and values are in the format tagValues/{tag_value_id}.`,
431+
Elem: &schema.Schema{Type: schema.TypeString},
432+
},
424433
"telemetry_config": {
425434
Type: schema.TypeList,
426435
Computed: true,
@@ -603,6 +612,12 @@ func resourceDataprocMetastoreServiceCreate(d *schema.ResourceData, meta interfa
603612
} else if v, ok := d.GetOkExists("telemetry_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(telemetryConfigProp)) && (ok || !reflect.DeepEqual(v, telemetryConfigProp)) {
604613
obj["telemetryConfig"] = telemetryConfigProp
605614
}
615+
tagsProp, err := expandDataprocMetastoreServiceTags(d.Get("tags"), d, config)
616+
if err != nil {
617+
return err
618+
} else if v, ok := d.GetOkExists("tags"); !tpgresource.IsEmptyValue(reflect.ValueOf(tagsProp)) && (ok || !reflect.DeepEqual(v, tagsProp)) {
619+
obj["tags"] = tagsProp
620+
}
606621
effectiveLabelsProp, err := expandDataprocMetastoreServiceEffectiveLabels(d.Get("effective_labels"), d, config)
607622
if err != nil {
608623
return err
@@ -2101,6 +2116,17 @@ func expandDataprocMetastoreServiceTelemetryConfigLogFormat(v interface{}, d tpg
21012116
return v, nil
21022117
}
21032118

2119+
func expandDataprocMetastoreServiceTags(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
2120+
if v == nil {
2121+
return map[string]string{}, nil
2122+
}
2123+
m := make(map[string]string)
2124+
for k, val := range v.(map[string]interface{}) {
2125+
m[k] = val.(string)
2126+
}
2127+
return m, nil
2128+
}
2129+
21042130
func expandDataprocMetastoreServiceEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
21052131
if v == nil {
21062132
return map[string]string{}, nil

google-beta/services/dataprocmetastore/resource_dataproc_metastore_service_generated_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ fields:
4848
provider_only: true
4949
- field: 'state'
5050
- field: 'state_message'
51+
- field: 'tags'
5152
- field: 'telemetry_config.log_format'
5253
- field: 'terraform_labels'
5354
provider_only: true

0 commit comments

Comments
 (0)