Skip to content

Commit 94ee0fa

Browse files
GKE: Conditionally send the cpu_cfs_quota field based on presence in config (#15268) (#10823)
[upstream:606f5f2041c291dc7bd864cd7170eea55001461c] Signed-off-by: Modular Magician <[email protected]>
1 parent 945c68f commit 94ee0fa

File tree

5 files changed

+143
-5
lines changed

5 files changed

+143
-5
lines changed

.changelog/15268.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
```release-note:breaking-change
2+
container: `node_config` blocks that had set `kubelet_config` without explicitly setting `cpu_cfs_quota` implicitly set `cfu_cfs_quota` to `false` when unset. From this version onwards, an unset `cpu_cfs_quota` will instead match the API default of true `true`. Resources that are recreated will receive the new value; old resources are unaffected, and may change values by explicitly setting the intended one.
3+
```
4+
5+
```release-note:bug
6+
container: Fixed the default for `node_config.kubelet_config.cpu_cfs_quota` on `google_container_cluster`, `google_container_node_pool`, `google_container_cluster.node_pool` to align with the API. Terraform will now send a `true` value when the field is unset on creation, and preserve any previously set value when unset. Explicitly set values will work as defined in configuration.
7+
```

google-beta/services/container/node_config.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
package container
1818

1919
import (
20+
"fmt"
2021
"log"
22+
"strconv"
2123
"strings"
2224
"time"
2325

26+
"github.com/hashicorp/go-cty/cty"
2427
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2528
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
2629

@@ -665,6 +668,7 @@ func schemaNodeConfig() *schema.Schema {
665668
},
666669
"cpu_cfs_quota": {
667670
Type: schema.TypeBool,
671+
Computed: true,
668672
Optional: true,
669673
Description: `Enable CPU CFS quota enforcement for containers that specify CPU limits.`,
670674
},
@@ -1218,7 +1222,7 @@ func expandNodeConfigDefaults(configured interface{}) *container.NodeConfigDefau
12181222
return nodeConfigDefaults
12191223
}
12201224

1221-
func expandNodeConfig(v interface{}) *container.NodeConfig {
1225+
func expandNodeConfig(d *schema.ResourceData, prefix string, v interface{}) *container.NodeConfig {
12221226
nodeConfigs := v.([]interface{})
12231227
nc := &container.NodeConfig{
12241228
// Defaults can't be set on a list/set in the schema, so set the default on create here.
@@ -1497,6 +1501,34 @@ func expandNodeConfig(v interface{}) *container.NodeConfig {
14971501

14981502
if v, ok := nodeConfig["kubelet_config"]; ok {
14991503
nc.KubeletConfig = expandKubeletConfig(v)
1504+
1505+
// start cpu_cfs_quota fix https://github.com/hashicorp/terraform-provider-google/issues/15767
1506+
// this makes the field conditional on appearance in configuration. This allows the API `true` default
1507+
// to override null, where currently we force-send null as false, which is wrong.
1508+
rawConfigNPRoot := d.GetRawConfig()
1509+
// if we have a prefix, we're in `node_pool.N.` in GKE Cluster. Traverse the RawConfig object to reach that
1510+
// root, at which point local references work going forwards.
1511+
if prefix != "" {
1512+
parts := strings.Split(prefix, ".") // "node_pool.N." -> ["node_pool" "N", ""]
1513+
npIndex, err := strconv.Atoi(parts[1])
1514+
if err != nil { // no error return from expander
1515+
panic(fmt.Errorf("unexpected format for node pool path prefix: %w. value: %v", err, prefix))
1516+
}
1517+
1518+
rawConfigNPRoot = rawConfigNPRoot.GetAttr("node_pool").Index(cty.NumberIntVal(int64(npIndex)))
1519+
}
1520+
1521+
if vNC := rawConfigNPRoot.GetAttr("node_config"); vNC.LengthInt() > 0 {
1522+
if vKC := vNC.Index(cty.NumberIntVal(0)).GetAttr("kubelet_config"); vKC.LengthInt() > 0 {
1523+
v := vKC.Index(cty.NumberIntVal(0)).GetAttr("cpu_cfs_quota")
1524+
if v == cty.NullVal(cty.Bool) {
1525+
nc.KubeletConfig.CpuCfsQuota = true
1526+
} else if v.False() { // force-send explicit false to API
1527+
nc.KubeletConfig.ForceSendFields = append(nc.KubeletConfig.ForceSendFields, "CpuCfsQuota")
1528+
}
1529+
}
1530+
}
1531+
// end cpu_cfs_quota fix
15001532
}
15011533

15021534
if v, ok := nodeConfig["linux_node_config"]; ok {
@@ -1639,7 +1671,6 @@ func expandKubeletConfig(v interface{}) *container.NodeKubeletConfig {
16391671
}
16401672
if cpuCfsQuota, ok := cfg["cpu_cfs_quota"]; ok {
16411673
kConfig.CpuCfsQuota = cpuCfsQuota.(bool)
1642-
kConfig.ForceSendFields = append(kConfig.ForceSendFields, "CpuCfsQuota")
16431674
}
16441675
if cpuCfsQuotaPeriod, ok := cfg["cpu_cfs_quota_period"]; ok {
16451676
kConfig.CpuCfsQuotaPeriod = cpuCfsQuotaPeriod.(string)

google-beta/services/container/resource_container_cluster.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,15 +2848,15 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
28482848
} else {
28492849
// Node Configs have default values that are set in the expand function,
28502850
// but can only be set if node pools are unspecified.
2851-
cluster.NodeConfig = expandNodeConfig([]interface{}{})
2851+
cluster.NodeConfig = expandNodeConfig(d, "", []interface{}{})
28522852
}
28532853

28542854
if v, ok := d.GetOk("node_pool_defaults"); ok {
28552855
cluster.NodePoolDefaults = expandNodePoolDefaults(v)
28562856
}
28572857

28582858
if v, ok := d.GetOk("node_config"); ok {
2859-
cluster.NodeConfig = expandNodeConfig(v)
2859+
cluster.NodeConfig = expandNodeConfig(d, "", v)
28602860
}
28612861

28622862
if v, ok := d.GetOk("authenticator_groups_config"); ok {

google-beta/services/container/resource_container_cluster_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6797,6 +6797,42 @@ func TestAccContainerCluster_additional_pod_ranges_config_on_update(t *testing.T
67976797
})
67986798
}
67996799

6800+
func TestAccContainerCluster_withCpuCfsQuotaPool(t *testing.T) {
6801+
t.Parallel()
6802+
6803+
clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10))
6804+
npName := fmt.Sprintf("tf-test-cluster-nodepool-%s", acctest.RandString(t, 10))
6805+
networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster")
6806+
subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName)
6807+
6808+
acctest.VcrTest(t, resource.TestCase{
6809+
PreCheck: func() { acctest.AccTestPreCheck(t) },
6810+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
6811+
CheckDestroy: testAccCheckContainerClusterDestroyProducer(t),
6812+
Steps: []resource.TestStep{
6813+
{
6814+
Config: testAccContainerCluster_withCpuCfsQuotaPool(clusterName, npName, networkName, subnetworkName),
6815+
},
6816+
{
6817+
ResourceName: "google_container_cluster.with_kubelet_config",
6818+
ImportState: true,
6819+
ImportStateVerify: true,
6820+
ImportStateVerifyIgnore: []string{"deletion_protection"},
6821+
},
6822+
{
6823+
Config: testAccContainerCluster_withCpuCfsQuotaPool2(clusterName, npName, networkName, subnetworkName),
6824+
PlanOnly: true,
6825+
},
6826+
{
6827+
ResourceName: "google_container_cluster.with_kubelet_config",
6828+
ImportState: true,
6829+
ImportStateVerify: true,
6830+
ImportStateVerifyIgnore: []string{"deletion_protection"},
6831+
},
6832+
},
6833+
})
6834+
}
6835+
68006836
func testAccContainerCluster_masterAuthorizedNetworksDisabled(t *testing.T, resource_name string) resource.TestCheckFunc {
68016837
return func(s *terraform.State) error {
68026838
rs, ok := s.RootModule().Resources[resource_name]
@@ -14935,3 +14971,67 @@ resource "google_container_cluster" "with_kubelet_config" {
1493514971
}
1493614972
`, clusterName, networkName, subnetworkName, cpuManagerPolicy, memoryManagerPolicy, topologyManagerPolicy, topologyManagerScope)
1493714973
}
14974+
14975+
func testAccContainerCluster_withCpuCfsQuotaPool(clusterName, npName, networkName, subnetworkName string) string {
14976+
return fmt.Sprintf(`
14977+
resource "google_container_cluster" "with_kubelet_config" {
14978+
name = %q
14979+
location = "us-central1-a"
14980+
network = %q
14981+
subnetwork = %q
14982+
deletion_protection = false
14983+
14984+
node_pool {
14985+
name = "%s-1"
14986+
initial_node_count = 1
14987+
node_config {
14988+
kubelet_config {
14989+
# cpu_cfs_quota = true
14990+
}
14991+
}
14992+
}
14993+
14994+
node_pool {
14995+
name = "%s-2"
14996+
initial_node_count = 1
14997+
node_config {
14998+
kubelet_config {
14999+
cpu_cfs_quota = false
15000+
}
15001+
}
15002+
}
15003+
}
15004+
`, clusterName, networkName, subnetworkName, npName, npName)
15005+
}
15006+
15007+
func testAccContainerCluster_withCpuCfsQuotaPool2(clusterName, npName, networkName, subnetworkName string) string {
15008+
return fmt.Sprintf(`
15009+
resource "google_container_cluster" "with_kubelet_config" {
15010+
name = %q
15011+
location = "us-central1-a"
15012+
network = %q
15013+
subnetwork = %q
15014+
deletion_protection = false
15015+
15016+
node_pool {
15017+
name = "%s-1"
15018+
initial_node_count = 1
15019+
node_config {
15020+
kubelet_config {
15021+
cpu_cfs_quota = true
15022+
}
15023+
}
15024+
}
15025+
15026+
node_pool {
15027+
name = "%s-2"
15028+
initial_node_count = 1
15029+
node_config {
15030+
kubelet_config {
15031+
cpu_cfs_quota = false
15032+
}
15033+
}
15034+
}
15035+
}
15036+
`, clusterName, networkName, subnetworkName, npName, npName)
15037+
}

google-beta/services/container/resource_container_node_pool.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@ func expandNodePool(d *schema.ResourceData, prefix string) (*container.NodePool,
11101110
np := &container.NodePool{
11111111
Name: name,
11121112
InitialNodeCount: int64(nodeCount),
1113-
Config: expandNodeConfig(d.Get(prefix + "node_config")),
1113+
Config: expandNodeConfig(d, prefix, d.Get(prefix+"node_config")),
11141114
Locations: locations,
11151115
Version: d.Get(prefix + "version").(string),
11161116
NetworkConfig: expandNodeNetworkConfig(d.Get(prefix + "network_config")),

0 commit comments

Comments
 (0)