Skip to content

Commit 478374a

Browse files
Breaking change: Rework taint model in GKE (#9011) (#15959)
Signed-off-by: Modular Magician <[email protected]>
1 parent 5f9b077 commit 478374a

7 files changed

+97
-29
lines changed

.changelog/9011.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:breaking-change
2+
container: reworked the `taint` field in `google_container_cluster` and `google_container_node_pool` to only manage a subset of taint keys based on those already in state. Most existing resources are unaffected, unless they use `sandbox_config`- see upgrade guide for details.
3+
```
4+
```release-note:enhancement
5+
container: added the `effective_taints` attribute to `google_container_cluster` and `google_container_node_pool`, outputting all known taint values
6+
```

google/services/container/node_config.go

+70-11
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,9 @@ func schemaNodeConfig() *schema.Schema {
380380
},
381381

382382
"taint": {
383-
Type: schema.TypeList,
384-
Optional: true,
385-
// Computed=true because GKE Sandbox will automatically add taints to nodes that can/cannot run sandboxed pods.
386-
Computed: true,
387-
ForceNew: true,
388-
// Legacy config mode allows explicitly defining an empty taint.
389-
// See https://www.terraform.io/docs/configuration/attr-as-blocks.html
390-
ConfigMode: schema.SchemaConfigModeAttr,
383+
Type: schema.TypeList,
384+
Optional: true,
385+
ForceNew: true,
391386
Description: `List of Kubernetes taints to be applied to each node.`,
392387
Elem: &schema.Resource{
393388
Schema: map[string]*schema.Schema{
@@ -414,6 +409,31 @@ func schemaNodeConfig() *schema.Schema {
414409
},
415410
},
416411

412+
"effective_taints": {
413+
Type: schema.TypeList,
414+
Computed: true,
415+
Description: `List of kubernetes taints applied to each node.`,
416+
Elem: &schema.Resource{
417+
Schema: map[string]*schema.Schema{
418+
"key": {
419+
Type: schema.TypeString,
420+
Computed: true,
421+
Description: `Key for taint.`,
422+
},
423+
"value": {
424+
Type: schema.TypeString,
425+
Computed: true,
426+
Description: `Value for taint.`,
427+
},
428+
"effect": {
429+
Type: schema.TypeString,
430+
Computed: true,
431+
Description: `Effect for taint.`,
432+
},
433+
},
434+
},
435+
},
436+
417437
"workload_metadata_config": {
418438
Computed: true,
419439
Type: schema.TypeList,
@@ -828,8 +848,10 @@ func expandNodeConfig(v interface{}) *container.NodeConfig {
828848
Value: data["value"].(string),
829849
Effect: data["effect"].(string),
830850
}
851+
831852
nodeTaints = append(nodeTaints, taint)
832853
}
854+
833855
nc.Taints = nodeTaints
834856
}
835857

@@ -991,13 +1013,24 @@ func flattenNodeConfigDefaults(c *container.NodeConfigDefaults) []map[string]int
9911013
return result
9921014
}
9931015

994-
func flattenNodeConfig(c *container.NodeConfig) []map[string]interface{} {
1016+
// v == old state of `node_config`
1017+
func flattenNodeConfig(c *container.NodeConfig, v interface{}) []map[string]interface{} {
9951018
config := make([]map[string]interface{}, 0, 1)
9961019

9971020
if c == nil {
9981021
return config
9991022
}
10001023

1024+
// default to no prior taint state if there are any issues
1025+
oldTaints := []interface{}{}
1026+
oldNodeConfigSchemaContainer := v.([]interface{})
1027+
if len(oldNodeConfigSchemaContainer) != 0 {
1028+
oldNodeConfigSchema := oldNodeConfigSchemaContainer[0].(map[string]interface{})
1029+
if vt, ok := oldNodeConfigSchema["taint"]; ok && len(vt.([]interface{})) > 0 {
1030+
oldTaints = vt.([]interface{})
1031+
}
1032+
}
1033+
10011034
config = append(config, map[string]interface{}{
10021035
"machine_type": c.MachineType,
10031036
"disk_size_gb": c.DiskSizeGb,
@@ -1020,7 +1053,8 @@ func flattenNodeConfig(c *container.NodeConfig) []map[string]interface{} {
10201053
"spot": c.Spot,
10211054
"min_cpu_platform": c.MinCpuPlatform,
10221055
"shielded_instance_config": flattenShieldedInstanceConfig(c.ShieldedInstanceConfig),
1023-
"taint": flattenTaints(c.Taints),
1056+
"taint": flattenTaints(c.Taints, oldTaints),
1057+
"effective_taints": flattenEffectiveTaints(c.Taints),
10241058
"workload_metadata_config": flattenWorkloadMetadataConfig(c.WorkloadMetadataConfig),
10251059
"confidential_nodes": flattenConfidentialNodes(c.ConfidentialNodes),
10261060
"boot_disk_kms_key": c.BootDiskKmsKey,
@@ -1148,7 +1182,31 @@ func flattenGKEReservationAffinity(c *container.ReservationAffinity) []map[strin
11481182
return result
11491183
}
11501184

1151-
func flattenTaints(c []*container.NodeTaint) []map[string]interface{} {
1185+
// flattenTaints records the set of taints already present in state.
1186+
func flattenTaints(c []*container.NodeTaint, oldTaints []interface{}) []map[string]interface{} {
1187+
taintKeys := map[string]struct{}{}
1188+
for _, raw := range oldTaints {
1189+
data := raw.(map[string]interface{})
1190+
taintKey := data["key"].(string)
1191+
taintKeys[taintKey] = struct{}{}
1192+
}
1193+
1194+
result := []map[string]interface{}{}
1195+
for _, taint := range c {
1196+
if _, ok := taintKeys[taint.Key]; ok {
1197+
result = append(result, map[string]interface{}{
1198+
"key": taint.Key,
1199+
"value": taint.Value,
1200+
"effect": taint.Effect,
1201+
})
1202+
}
1203+
}
1204+
1205+
return result
1206+
}
1207+
1208+
// flattenEffectiveTaints records the complete set of taints returned from GKE.
1209+
func flattenEffectiveTaints(c []*container.NodeTaint) []map[string]interface{} {
11521210
result := []map[string]interface{}{}
11531211
for _, taint := range c {
11541212
result = append(result, map[string]interface{}{
@@ -1157,6 +1215,7 @@ func flattenTaints(c []*container.NodeTaint) []map[string]interface{} {
11571215
"effect": taint.Effect,
11581216
})
11591217
}
1218+
11601219
return result
11611220
}
11621221

google/services/container/resource_container_cluster.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -2421,7 +2421,7 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro
24212421
return fmt.Errorf("Error setting default_max_pods_per_node: %s", err)
24222422
}
24232423
}
2424-
if err := d.Set("node_config", flattenNodeConfig(cluster.NodeConfig)); err != nil {
2424+
if err := d.Set("node_config", flattenNodeConfig(cluster.NodeConfig, d.Get("node_config"))); err != nil {
24252425
return err
24262426
}
24272427
if err := d.Set("project", project); err != nil {

google/services/container/resource_container_cluster_test.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -983,17 +983,19 @@ func TestAccContainerCluster_withNodeConfig(t *testing.T) {
983983
Config: testAccContainerCluster_withNodeConfig(clusterName),
984984
},
985985
{
986-
ResourceName: "google_container_cluster.with_node_config",
987-
ImportState: true,
988-
ImportStateVerify: true,
986+
ResourceName: "google_container_cluster.with_node_config",
987+
ImportState: true,
988+
ImportStateVerify: true,
989+
ImportStateVerifyIgnore: []string{"node_config.0.taint"},
989990
},
990991
{
991992
Config: testAccContainerCluster_withNodeConfigUpdate(clusterName),
992993
},
993994
{
994-
ResourceName: "google_container_cluster.with_node_config",
995-
ImportState: true,
996-
ImportStateVerify: true,
995+
ResourceName: "google_container_cluster.with_node_config",
996+
ImportState: true,
997+
ImportStateVerify: true,
998+
ImportStateVerifyIgnore: []string{"node_config.0.taint"},
997999
},
9981000
},
9991001
})

google/services/container/resource_container_node_pool.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,7 @@ func flattenNodePool(d *schema.ResourceData, config *transport_tpg.Config, np *c
10551055
"initial_node_count": np.InitialNodeCount,
10561056
"node_locations": schema.NewSet(schema.HashString, tpgresource.ConvertStringArrToInterface(np.Locations)),
10571057
"node_count": nodeCount,
1058-
"node_config": flattenNodeConfig(np.Config),
1058+
"node_config": flattenNodeConfig(np.Config, d.Get(prefix+"node_config")),
10591059
"instance_group_urls": igmUrls,
10601060
"managed_instance_group_urls": managedIgmUrls,
10611061
"version": np.Version,

google/services/container/resource_container_node_pool_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ func TestAccContainerNodePool_withNodeConfig(t *testing.T) {
216216
ImportStateVerify: true,
217217
// autoscaling.# = 0 is equivalent to no autoscaling at all,
218218
// but will still cause an import diff
219-
ImportStateVerifyIgnore: []string{"autoscaling.#"},
219+
ImportStateVerifyIgnore: []string{"autoscaling.#", "node_config.0.taint"},
220220
},
221221
{
222222
Config: testAccContainerNodePool_withNodeConfigUpdate(cluster, nodePool),
@@ -227,7 +227,7 @@ func TestAccContainerNodePool_withNodeConfig(t *testing.T) {
227227
ImportStateVerify: true,
228228
// autoscaling.# = 0 is equivalent to no autoscaling at all,
229229
// but will still cause an import diff
230-
ImportStateVerifyIgnore: []string{"autoscaling.#"},
230+
ImportStateVerifyIgnore: []string{"autoscaling.#", "node_config.0.taint"},
231231
},
232232
},
233233
})

website/docs/r/container_cluster.html.markdown

+9-8
Original file line numberDiff line numberDiff line change
@@ -888,14 +888,13 @@ gvnic {
888888
* `tags` - (Optional) The list of instance tags applied to all nodes. Tags are used to identify
889889
valid sources or targets for network firewalls.
890890

891-
* `taint` - (Optional) A list of [Kubernetes taints](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)
892-
to apply to nodes. GKE's API can only set this field on cluster creation.
893-
However, GKE will add taints to your nodes if you enable certain features such
894-
as GPUs. If this field is set, any diffs on this field will cause Terraform to
895-
recreate the underlying resource. Taint values can be updated safely in
896-
Kubernetes (eg. through `kubectl`), and it's recommended that you do not use
897-
this field to manage taints. If you do, `lifecycle.ignore_changes` is
898-
recommended. Structure is [documented below](#nested_taint).
891+
* `taint` - (Optional) A list of
892+
[Kubernetes taints](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)
893+
to apply to nodes. This field will only report drift on taint keys that are
894+
already managed with Terraform, use `effective_taints` to view the list of
895+
GKE-managed taints on the node pool from all sources. Importing this resource
896+
will not record any taints as being Terraform-managed, and will cause drift with
897+
any configured taints. Structure is [documented below](#nested_taint).
899898

900899
* `workload_metadata_config` - (Optional) Metadata configuration to expose to workloads on the node pool.
901900
Structure is [documented below](#nested_workload_metadata_config).
@@ -1310,6 +1309,8 @@ exported:
13101309

13111310
* `cluster_autoscaling.0.auto_provisioning_defaults.0.management.0.upgrade_options` - Specifies the [Auto Upgrade knobs](https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1beta1/NodeManagement#AutoUpgradeOptions) for the node pool.
13121311

1312+
* `node_config.0.effective_taints` - List of kubernetes taints applied to each node. Structure is [documented above](#nested_taint).
1313+
13131314
## Timeouts
13141315

13151316
This resource provides the following

0 commit comments

Comments
 (0)