diff --git a/.changelog/6618.txt b/.changelog/6618.txt new file mode 100644 index 00000000000..485f68fd018 --- /dev/null +++ b/.changelog/6618.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +container: added field `strategy` to `google_container_node_pool` +``` +```release-note:enhancement +container: added field `blue_green_settings` to `google_container_node_pool` +``` diff --git a/google/resource_container_node_pool.go b/google/resource_container_node_pool.go index 757f1c0fb28..ba5257b3968 100644 --- a/google/resource_container_node_pool.go +++ b/google/resource_container_node_pool.go @@ -74,6 +74,53 @@ func resourceContainerNodePool() *schema.Resource { } } +var schemaBlueGreenSettings = &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "standard_rollout_policy": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: `Standard rollout policy is the default policy for blue-green.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "batch_percentage": { + Type: schema.TypeFloat, + Optional: true, + Computed: true, + Description: `Percentage of the blue pool nodes to drain in a batch.`, + ValidateFunc: validation.FloatBetween(0.0, 1.0), + }, + "batch_node_count": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + Description: `Number of blue nodes to drain in a batch.`, + }, + "batch_soak_duration": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `Soak time after each batch gets drained.`, + }, + }, + }, + }, + "node_pool_soak_duration": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `Time needed after draining entire blue pool. After this period, blue pool will be cleaned up.`, + }, + }, + }, + Description: `Settings for BlueGreen node pool upgrade.`, +} + var schemaNodePool = map[string]*schema.Schema{ "autoscaling": { Type: schema.TypeList, @@ -146,17 +193,29 @@ var schemaNodePool = map[string]*schema.Schema{ Schema: map[string]*schema.Schema{ "max_surge": { Type: schema.TypeInt, - Required: true, + Optional: true, + Computed: true, ValidateFunc: validation.IntAtLeast(0), Description: `The number of additional nodes that can be added to the node pool during an upgrade. Increasing max_surge raises the number of nodes that can be upgraded simultaneously. Can be set to 0 or greater.`, }, "max_unavailable": { Type: schema.TypeInt, - Required: true, + Optional: true, + Computed: true, ValidateFunc: validation.IntAtLeast(0), Description: `The number of nodes that can be simultaneously unavailable during an upgrade. Increasing max_unavailable raises the number of nodes that can be upgraded in parallel. Can be set to 0 or greater.`, }, + + "strategy": { + Type: schema.TypeString, + Optional: true, + Default: "SURGE", + ValidateFunc: validation.StringInSlice([]string{"SURGE", "BLUE_GREEN"}, false), + Description: `Update strategy for the given nodepool.`, + }, + + "blue_green_settings": schemaBlueGreenSettings, }, }, }, @@ -749,18 +808,103 @@ func expandNodePool(d *schema.ResourceData, prefix string) (*container.NodePool, upgradeSettingsConfig := v.([]interface{})[0].(map[string]interface{}) np.UpgradeSettings = &container.UpgradeSettings{} - if v, ok := upgradeSettingsConfig["max_surge"]; ok { - np.UpgradeSettings.MaxSurge = int64(v.(int)) + if v, ok := upgradeSettingsConfig["strategy"]; ok { + np.UpgradeSettings.Strategy = v.(string) } - if v, ok := upgradeSettingsConfig["max_unavailable"]; ok { - np.UpgradeSettings.MaxUnavailable = int64(v.(int)) + if d.HasChange(prefix + "upgrade_settings.0.max_surge") { + if np.UpgradeSettings.Strategy != "SURGE" { + return nil, fmt.Errorf("Surge upgrade settings may not be changed when surge strategy is not enabled") + } + if v, ok := upgradeSettingsConfig["max_surge"]; ok { + np.UpgradeSettings.MaxSurge = int64(v.(int)) + } + } + + if d.HasChange(prefix + "upgrade_settings.0.max_unavailable") { + if np.UpgradeSettings.Strategy != "SURGE" { + return nil, fmt.Errorf("Surge upgrade settings may not be changed when surge strategy is not enabled") + } + if v, ok := upgradeSettingsConfig["max_unavailable"]; ok { + np.UpgradeSettings.MaxUnavailable = int64(v.(int)) + } + } + + if v, ok := upgradeSettingsConfig["blue_green_settings"]; ok && len(v.([]interface{})) > 0 { + blueGreenSettingsConfig := v.([]interface{})[0].(map[string]interface{}) + np.UpgradeSettings.BlueGreenSettings = &container.BlueGreenSettings{} + + if np.UpgradeSettings.Strategy != "BLUE_GREEN" { + return nil, fmt.Errorf("Blue-green upgrade settings may not be changed when blue-green strategy is not enabled") + } + + if v, ok := blueGreenSettingsConfig["node_pool_soak_duration"]; ok { + np.UpgradeSettings.BlueGreenSettings.NodePoolSoakDuration = v.(string) + } + + if v, ok := blueGreenSettingsConfig["standard_rollout_policy"]; ok && len(v.([]interface{})) > 0 { + standardRolloutPolicyConfig := v.([]interface{})[0].(map[string]interface{}) + standardRolloutPolicy := &container.StandardRolloutPolicy{} + + if v, ok := standardRolloutPolicyConfig["batch_soak_duration"]; ok { + standardRolloutPolicy.BatchSoakDuration = v.(string) + } + if v, ok := standardRolloutPolicyConfig["batch_node_count"]; ok { + standardRolloutPolicy.BatchNodeCount = int64(v.(int)) + } + if v, ok := standardRolloutPolicyConfig["batch_percentage"]; ok { + standardRolloutPolicy.BatchPercentage = v.(float64) + } + + np.UpgradeSettings.BlueGreenSettings.StandardRolloutPolicy = standardRolloutPolicy + } } } return np, nil } +func flattenNodePoolStandardRolloutPolicy(rp *container.StandardRolloutPolicy) []map[string]interface{} { + if rp == nil { + return nil + } + + return []map[string]interface{}{ + { + "batch_node_count": rp.BatchNodeCount, + "batch_percentage": rp.BatchPercentage, + "batch_soak_duration": rp.BatchSoakDuration, + }, + } +} + +func flattenNodePoolBlueGreenSettings(bg *container.BlueGreenSettings) []map[string]interface{} { + if bg == nil { + return nil + } + return []map[string]interface{}{ + { + "node_pool_soak_duration": bg.NodePoolSoakDuration, + "standard_rollout_policy": flattenNodePoolStandardRolloutPolicy(bg.StandardRolloutPolicy), + }, + } +} + +func flattenNodePoolUpgradeSettings(us *container.UpgradeSettings) []map[string]interface{} { + if us == nil { + return nil + } + + upgradeSettings := make(map[string]interface{}) + + upgradeSettings["blue_green_settings"] = flattenNodePoolBlueGreenSettings(us.BlueGreenSettings) + upgradeSettings["max_surge"] = us.MaxSurge + upgradeSettings["max_unavailable"] = us.MaxUnavailable + + upgradeSettings["strategy"] = us.Strategy + return []map[string]interface{}{upgradeSettings} +} + func flattenNodePool(d *schema.ResourceData, config *Config, np *container.NodePool, prefix string) (map[string]interface{}, error) { userAgent, err := generateUserAgentString(d, config.userAgent) if err != nil { @@ -835,12 +979,7 @@ func flattenNodePool(d *schema.ResourceData, config *Config, np *container.NodeP } if np.UpgradeSettings != nil { - nodePool["upgrade_settings"] = []map[string]interface{}{ - { - "max_surge": np.UpgradeSettings.MaxSurge, - "max_unavailable": np.UpgradeSettings.MaxUnavailable, - }, - } + nodePool["upgrade_settings"] = flattenNodePoolUpgradeSettings(np.UpgradeSettings) } else { delete(nodePool, "upgrade_settings") } @@ -1165,8 +1304,49 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node upgradeSettings := &container.UpgradeSettings{} if v, ok := d.GetOk(prefix + "upgrade_settings"); ok { upgradeSettingsConfig := v.([]interface{})[0].(map[string]interface{}) - upgradeSettings.MaxSurge = int64(upgradeSettingsConfig["max_surge"].(int)) - upgradeSettings.MaxUnavailable = int64(upgradeSettingsConfig["max_unavailable"].(int)) + upgradeSettings.Strategy = upgradeSettingsConfig["strategy"].(string) + + if d.HasChange(prefix + "upgrade_settings.0.max_surge") { + if upgradeSettings.Strategy != "SURGE" { + return fmt.Errorf("Surge upgrade settings may not be changed when surge strategy is not enabled") + } + if v, ok := upgradeSettingsConfig["max_surge"]; ok { + upgradeSettings.MaxSurge = int64(v.(int)) + } + } + + if d.HasChange(prefix + "upgrade_settings.0.max_unavailable") { + if upgradeSettings.Strategy != "SURGE" { + return fmt.Errorf("Surge upgrade settings may not be changed when surge strategy is not enabled") + } + if v, ok := upgradeSettingsConfig["max_unavailable"]; ok { + upgradeSettings.MaxUnavailable = int64(v.(int)) + } + } + + if d.HasChange(prefix + "upgrade_settings.0.blue_green_settings") { + if upgradeSettings.Strategy != "BLUE_GREEN" { + return fmt.Errorf("Blue-green upgrade settings may not be changed when blue-green strategy is not enabled") + } + + blueGreenSettings := &container.BlueGreenSettings{} + blueGreenSettingsConfig := upgradeSettingsConfig["blue_green_settings"].([]interface{})[0].(map[string]interface{}) + blueGreenSettings.NodePoolSoakDuration = blueGreenSettingsConfig["node_pool_soak_duration"].(string) + + if v, ok := blueGreenSettingsConfig["standard_rollout_policy"]; ok && len(v.([]interface{})) > 0 { + standardRolloutPolicy := &container.StandardRolloutPolicy{} + standardRolloutPolicyConfig := v.([]interface{})[0].(map[string]interface{}) + standardRolloutPolicy.BatchSoakDuration = standardRolloutPolicyConfig["batch_soak_duration"].(string) + if v, ok := standardRolloutPolicyConfig["batch_node_count"]; ok { + standardRolloutPolicy.BatchNodeCount = int64(v.(int)) + } + if v, ok := standardRolloutPolicyConfig["batch_percentage"]; ok { + standardRolloutPolicy.BatchPercentage = v.(float64) + } + blueGreenSettings.StandardRolloutPolicy = standardRolloutPolicy + } + upgradeSettings.BlueGreenSettings = blueGreenSettings + } } req := &container.UpdateNodePoolRequest{ UpgradeSettings: upgradeSettings, diff --git a/google/resource_container_node_pool_test.go b/google/resource_container_node_pool_test.go index 544569789a4..cf4c32720d3 100644 --- a/google/resource_container_node_pool_test.go +++ b/google/resource_container_node_pool_test.go @@ -306,7 +306,7 @@ func TestAccContainerNodePool_withUpgradeSettings(t *testing.T) { CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 2, 3), + Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 2, 3, "SURGE", "", 0, 0.0, ""), }, { ResourceName: "google_container_node_pool.with_upgrade_settings", @@ -314,7 +314,23 @@ func TestAccContainerNodePool_withUpgradeSettings(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 1, 1), + Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 1, 1, "SURGE", "", 0, 0.0, ""), + }, + { + ResourceName: "google_container_node_pool.with_upgrade_settings", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 0, 0, "BLUE_GREEN", "100s", 1, 0.0, "0s"), + }, + { + ResourceName: "google_container_node_pool.with_upgrade_settings", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 0, 0, "BLUE_GREEN", "100s", 0, 0.5, "1s"), }, { ResourceName: "google_container_node_pool.with_upgrade_settings", @@ -1572,7 +1588,32 @@ resource "google_container_node_pool" "with_workload_metadata_config" { `, projectID, cluster, np) } -func testAccContainerNodePool_withUpgradeSettings(clusterName string, nodePoolName string, maxSurge int, maxUnavailable int) string { +func makeUpgradeSettings(maxSurge int, maxUnavailable int, strategy string, nodePoolSoakDuration string, batchNodeCount int, batchPercentage float64, batchSoakDuration string) string { + if strategy == "BLUE_GREEN" { + return fmt.Sprintf(` +upgrade_settings { + strategy = "%s" + blue_green_settings { + node_pool_soak_duration = "%s" + standard_rollout_policy { + batch_node_count = %d + batch_percentage = %f + batch_soak_duration = "%s" + } + } +} +`, strategy, nodePoolSoakDuration, batchNodeCount, batchPercentage, batchSoakDuration) + } + return fmt.Sprintf(` +upgrade_settings { + max_surge = %d + max_unavailable = %d + strategy = "%s" +} +`, maxSurge, maxUnavailable, strategy) +} + +func testAccContainerNodePool_withUpgradeSettings(clusterName string, nodePoolName string, maxSurge int, maxUnavailable int, strategy string, nodePoolSoakDuration string, batchNodeCount int, batchPercentage float64, batchSoakDuration string) string { return fmt.Sprintf(` data "google_container_engine_versions" "central1" { location = "us-central1" @@ -1590,12 +1631,9 @@ resource "google_container_node_pool" "with_upgrade_settings" { location = "us-central1" cluster = "${google_container_cluster.cluster.name}" initial_node_count = 1 - upgrade_settings { - max_surge = %d - max_unavailable = %d - } + %s } -`, clusterName, nodePoolName, maxSurge, maxUnavailable) +`, clusterName, nodePoolName, makeUpgradeSettings(maxSurge, maxUnavailable, strategy, nodePoolSoakDuration, batchNodeCount, batchPercentage, batchSoakDuration)) } func testAccContainerNodePool_withGPU(cluster, np string) string { diff --git a/website/docs/r/container_node_pool.html.markdown b/website/docs/r/container_node_pool.html.markdown index e7b3b5d26ab..3987b62a24b 100644 --- a/website/docs/r/container_node_pool.html.markdown +++ b/website/docs/r/container_node_pool.html.markdown @@ -157,8 +157,7 @@ cluster. * `project` - (Optional) The ID of the project in which to create the node pool. If blank, the provider-configured project will be used. -* `upgrade_settings` (Optional) Specify node upgrade settings to change how many nodes GKE attempts to - upgrade at once. The number of nodes upgraded simultaneously is the sum of `max_surge` and `max_unavailable`. +* `upgrade_settings` (Optional) Specify node upgrade settings to change how GKE upgrades nodes. The maximum number of nodes upgraded simultaneously is limited to 20. Structure is [documented below](#nested_upgrade_settings). * `version` - (Optional) The Kubernetes version for the nodes in this pool. Note that if this field @@ -201,16 +200,31 @@ cluster. The `upgrade_settings` block supports: -* `max_surge` - (Required) The number of additional nodes that can be added to the node pool during +* `max_surge` - (Optional) The number of additional nodes that can be added to the node pool during an upgrade. Increasing `max_surge` raises the number of nodes that can be upgraded simultaneously. Can be set to 0 or greater. -* `max_unavailable` - (Required) The number of nodes that can be simultaneously unavailable during +* `max_unavailable` - (Optional) The number of nodes that can be simultaneously unavailable during an upgrade. Increasing `max_unavailable` raises the number of nodes that can be upgraded in parallel. Can be set to 0 or greater. `max_surge` and `max_unavailable` must not be negative and at least one of them must be greater than zero. +* `strategy` - (Default `SURGE`) The upgrade stragey to be used for upgrading the nodes. + +* `blue_green_settings` - (Optional) The settings to adjust [blue green upgrades](https://cloud.google.com/kubernetes-engine/docs/concepts/node-pool-upgrade-strategies#blue-green-upgrade-strategy). + Structure is [documented below](#nested_blue_green_settings) + +The `blue_green_settings` block supports: + +* `standard_rollout_policy` - (Required) Specifies the standard policy settings for blue-green upgrades. + * `batch_percentage` - (Optional) Percentage of the blue pool nodes to drain in a batch. + * `batch_node_count` - (Optional) Number of blue nodes to drain in a batch. + * `batch_soak_duration` - (Optionial) Soak time after each batch gets drained. + +* `node_pool_soak_duration` - (Optional) Time needed after draining the entire blue pool. + After this period, the blue pool will be cleaned up. + The `placement_policy` block supports: * `type` - (Required) The type of the policy. Supports a single value: COMPACT.