Skip to content

Commit 71a9b7a

Browse files
Add Resource Manager Tags support to 'google_container_cluster' (#9531) (#17346)
* resourceManagerTags added to Cluster Node Config schema * update beta tag * add cluster and node proto tests * add expand and flatten proto * removed beta tag * added to documentation * added resource manager tags to auto pilot * migrating resourceManagerTags tests * migrating node_pools test * migrating additional tests * minor fixes * fixing tests * add in-place update support * fixed tests * fixed annotations * validated clusters and node pools tests. Isolated node pool auto config * isolated resource manager tags from docs * fixed permission issue * fixed spaces * fixed non determinism on tag keys * removed auto_pilot rmts * fixed time_sleep * add depends_on to IAM policies [upstream:343ff46df5008cc457392c3baafd0acc6dc97633] Signed-off-by: Modular Magician <[email protected]>
1 parent a381c88 commit 71a9b7a

8 files changed

+615
-7
lines changed

.changelog/9531.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:enhancement
2+
container: added `node_config.resource_manager_tags` field to `google_container_cluster` resource
3+
```
4+
```release-note:enhancement
5+
container: added `node_config.resource_manager_tags` field to `google_container_node_pool` resource
6+
```

google/services/compute/resource_compute_instance.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ func ResourceComputeInstance() *schema.Resource {
633633
Optional: true,
634634
Elem: &schema.Schema{Type: schema.TypeString},
635635
Description: `A set of key/value label pairs assigned to the instance.
636-
636+
637637
**Note**: This field is non-authoritative, and will only manage the labels present in your configuration.
638638
Please refer to the field 'effective_labels' for all of the labels present on the resource.`,
639639
},

google/services/container/node_config.go

+36
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,11 @@ func schemaNodeConfig() *schema.Schema {
625625
},
626626
},
627627
},
628+
"resource_manager_tags": {
629+
Type: schema.TypeMap,
630+
Optional: true,
631+
Description: `A map of resource manager tags. Resource manager tag keys and values have the same definition as resource manager tags. Keys must be in the format tagKeys/{tag_key_id}, and values are in the format tagValues/456. The field is ignored (both PUT & PATCH) when empty.`,
632+
},
628633
},
629634
},
630635
}
@@ -810,6 +815,10 @@ func expandNodeConfig(v interface{}) *container.NodeConfig {
810815
nc.ResourceLabels = m
811816
}
812817

818+
if v, ok := nodeConfig["resource_manager_tags"]; ok && len(v.(map[string]interface{})) > 0 {
819+
nc.ResourceManagerTags = expandResourceManagerTags(v)
820+
}
821+
813822
if v, ok := nodeConfig["tags"]; ok {
814823
tagsList := v.([]interface{})
815824
tags := []string{}
@@ -894,6 +903,19 @@ func expandNodeConfig(v interface{}) *container.NodeConfig {
894903
return nc
895904
}
896905

906+
func expandResourceManagerTags(v interface{}) *container.ResourceManagerTags {
907+
rmts := make(map[string]string)
908+
909+
if v != nil {
910+
rmts = tpgresource.ConvertStringMap(v.(map[string]interface{}))
911+
}
912+
913+
return &container.ResourceManagerTags{
914+
Tags: rmts,
915+
ForceSendFields: []string{"Tags"},
916+
}
917+
}
918+
897919
func expandWorkloadMetadataConfig(v interface{}) *container.WorkloadMetadataConfig {
898920
if v == nil {
899921
return nil
@@ -1090,6 +1112,7 @@ func flattenNodeConfig(c *container.NodeConfig, v interface{}) []map[string]inte
10901112
"advanced_machine_features": flattenAdvancedMachineFeaturesConfig(c.AdvancedMachineFeatures),
10911113
"sole_tenant_config": flattenSoleTenantConfig(c.SoleTenantConfig),
10921114
"fast_socket": flattenFastSocket(c.FastSocket),
1115+
"resource_manager_tags": flattenResourceManagerTags(c.ResourceManagerTags),
10931116
})
10941117

10951118
if len(c.OauthScopes) > 0 {
@@ -1099,6 +1122,19 @@ func flattenNodeConfig(c *container.NodeConfig, v interface{}) []map[string]inte
10991122
return config
11001123
}
11011124

1125+
func flattenResourceManagerTags(c *container.ResourceManagerTags) map[string]interface{} {
1126+
rmt := make(map[string]interface{})
1127+
1128+
if c != nil {
1129+
for k, v := range c.Tags {
1130+
rmt[k] = v
1131+
}
1132+
1133+
}
1134+
1135+
return rmt
1136+
}
1137+
11021138
func flattenAdvancedMachineFeaturesConfig(c *container.AdvancedMachineFeatures) []map[string]interface{} {
11031139
result := []map[string]interface{}{}
11041140
if c != nil {

google/services/container/resource_container_cluster.go

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ var (
9090
forceNewClusterNodeConfigFields = []string{
9191
"labels",
9292
"workload_metadata_config",
93+
"resource_manager_tags",
9394
}
9495

9596
suppressDiffForAutopilot = schema.SchemaDiffSuppressFunc(func(k, oldValue, newValue string, d *schema.ResourceData) bool {
@@ -4901,6 +4902,7 @@ func expandNodePoolAutoConfig(configured interface{}) *container.NodePoolAutoCon
49014902
if v, ok := config["network_tags"]; ok && len(v.([]interface{})) > 0 {
49024903
npac.NetworkTags = expandNodePoolAutoConfigNetworkTags(v)
49034904
}
4905+
49044906
return npac
49054907
}
49064908

google/services/container/resource_container_cluster_test.go

+124-4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,43 @@ func TestAccContainerCluster_basic(t *testing.T) {
5757
})
5858
}
5959

60+
func TestAccContainerCluster_resourceManagerTags(t *testing.T) {
61+
t.Parallel()
62+
63+
pid := envvar.GetTestProjectFromEnv()
64+
65+
randomSuffix := acctest.RandString(t, 10)
66+
clusterName := fmt.Sprintf("tf-test-cluster-%s", randomSuffix)
67+
68+
networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster")
69+
subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName)
70+
71+
acctest.VcrTest(t, resource.TestCase{
72+
PreCheck: func() { acctest.AccTestPreCheck(t) },
73+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
74+
ExternalProviders: map[string]resource.ExternalProvider{
75+
"time": {},
76+
},
77+
CheckDestroy: testAccCheckContainerClusterDestroyProducer(t),
78+
Steps: []resource.TestStep{
79+
{
80+
Config: testAccContainerCluster_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix),
81+
Check: resource.ComposeTestCheckFunc(
82+
resource.TestCheckResourceAttrSet("google_container_cluster.primary", "self_link"),
83+
resource.TestCheckResourceAttrSet("google_container_cluster.primary", "node_config.0.resource_manager_tags.%"),
84+
),
85+
},
86+
{
87+
ResourceName: "google_container_cluster.primary",
88+
ImportStateId: fmt.Sprintf("us-central1-a/%s", clusterName),
89+
ImportState: true,
90+
ImportStateVerify: true,
91+
ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"},
92+
},
93+
},
94+
})
95+
}
96+
6097
func TestAccContainerCluster_networkingModeRoutes(t *testing.T) {
6198
t.Parallel()
6299

@@ -3805,11 +3842,11 @@ func testAccContainerCluster_withIncompatibleMasterVersionNodeVersion(name strin
38053842
resource "google_container_cluster" "gke_cluster" {
38063843
name = "%s"
38073844
location = "us-central1"
3808-
3845+
38093846
min_master_version = "1.10.9-gke.5"
38103847
node_version = "1.10.6-gke.11"
38113848
initial_node_count = 1
3812-
3849+
38133850
}
38143851
`, name)
38153852
}
@@ -5824,7 +5861,7 @@ resource "google_container_cluster" "with_autoprovisioning" {
58245861
min_master_version = data.google_container_engine_versions.central1a.latest_master_version
58255862
initial_node_count = 1
58265863
deletion_protection = false
5827-
5864+
58285865
network = "%s"
58295866
subnetwork = "%s"
58305867
@@ -8095,7 +8132,7 @@ resource "google_compute_resource_policy" "policy" {
80958132
resource "google_container_cluster" "cluster" {
80968133
name = "%s"
80978134
location = "us-central1-a"
8098-
8135+
80998136
node_pool {
81008137
name = "%s"
81018138
initial_node_count = 2
@@ -8196,3 +8233,86 @@ func testAccContainerCluster_additional_pod_ranges_config(name string, nameCount
81968233
}
81978234
`, name, name, name, aprc)
81988235
}
8236+
8237+
func testAccContainerCluster_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string {
8238+
return fmt.Sprintf(`
8239+
data "google_project" "project" {
8240+
project_id = "%[1]s"
8241+
}
8242+
8243+
resource "google_project_iam_binding" "tagHoldAdmin" {
8244+
project = "%[1]s"
8245+
role = "roles/resourcemanager.tagHoldAdmin"
8246+
members = [
8247+
"serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com",
8248+
]
8249+
}
8250+
8251+
resource "google_project_iam_binding" "tagUser" {
8252+
project = "%[1]s"
8253+
role = "roles/resourcemanager.tagUser"
8254+
members = [
8255+
"serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com",
8256+
"serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com",
8257+
]
8258+
8259+
depends_on = [google_project_iam_binding.tagHoldAdmin]
8260+
}
8261+
8262+
resource "time_sleep" "wait_120_seconds" {
8263+
create_duration = "120s"
8264+
8265+
depends_on = [
8266+
google_project_iam_binding.tagHoldAdmin,
8267+
google_project_iam_binding.tagUser
8268+
]
8269+
}
8270+
8271+
resource "google_tags_tag_key" "key" {
8272+
parent = "projects/%[1]s"
8273+
short_name = "foobarbaz-%[2]s"
8274+
description = "For foo/bar resources"
8275+
purpose = "GCE_FIREWALL"
8276+
purpose_data = {
8277+
network = "%[1]s/%[4]s"
8278+
}
8279+
}
8280+
8281+
resource "google_tags_tag_value" "value" {
8282+
parent = "tagKeys/${google_tags_tag_key.key.name}"
8283+
short_name = "foo-%[2]s"
8284+
description = "For foo resources"
8285+
}
8286+
8287+
data "google_container_engine_versions" "uscentral1a" {
8288+
location = "us-central1-a"
8289+
}
8290+
8291+
resource "google_container_cluster" "primary" {
8292+
name = "%[3]s"
8293+
location = "us-central1-a"
8294+
min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"]
8295+
initial_node_count = 1
8296+
8297+
node_config {
8298+
machine_type = "n1-standard-1" // can't be e2 because of local-ssd
8299+
disk_size_gb = 15
8300+
8301+
resource_manager_tags = {
8302+
"tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}"
8303+
}
8304+
}
8305+
8306+
deletion_protection = false
8307+
network = "%[4]s"
8308+
subnetwork = "%[5]s"
8309+
8310+
timeouts {
8311+
create = "30m"
8312+
update = "40m"
8313+
}
8314+
8315+
depends_on = [time_sleep.wait_120_seconds]
8316+
}
8317+
`, projectID, randomSuffix, clusterName, networkName, subnetworkName)
8318+
}

google/services/container/resource_container_node_pool.go

+42
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,48 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node
14361436
log.Printf("[INFO] Updated tags for node pool %s", name)
14371437
}
14381438

1439+
if d.HasChange(prefix + "node_config.0.resource_manager_tags") {
1440+
req := &container.UpdateNodePoolRequest{
1441+
Name: name,
1442+
}
1443+
if v, ok := d.GetOk(prefix + "node_config.0.resource_manager_tags"); ok {
1444+
req.ResourceManagerTags = expandResourceManagerTags(v)
1445+
}
1446+
1447+
// sets resource manager tags to the empty list when user removes a previously defined list of tags entriely
1448+
// aka the node pool goes from having tags to no longer having any
1449+
if req.ResourceManagerTags == nil {
1450+
tags := make(map[string]string)
1451+
rmTags := &container.ResourceManagerTags{
1452+
Tags: tags,
1453+
}
1454+
req.ResourceManagerTags = rmTags
1455+
}
1456+
1457+
updateF := func() error {
1458+
clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(name), req)
1459+
if config.UserProjectOverride {
1460+
clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project)
1461+
}
1462+
op, err := clusterNodePoolsUpdateCall.Do()
1463+
if err != nil {
1464+
return err
1465+
}
1466+
1467+
// Wait until it's updated
1468+
return ContainerOperationWait(config, op,
1469+
nodePoolInfo.project,
1470+
nodePoolInfo.location,
1471+
"updating GKE node pool resource manager tags", userAgent,
1472+
timeout)
1473+
}
1474+
1475+
if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil {
1476+
return err
1477+
}
1478+
log.Printf("[INFO] Updated resource manager tags for node pool %s", name)
1479+
}
1480+
14391481
if d.HasChange(prefix + "node_config.0.resource_labels") {
14401482
req := &container.UpdateNodePoolRequest{
14411483
Name: name,

0 commit comments

Comments
 (0)