diff --git a/google-beta/resource_container_node_pool.go b/google-beta/resource_container_node_pool.go index 28f25d669a..fb79a746dd 100644 --- a/google-beta/resource_container_node_pool.go +++ b/google-beta/resource_container_node_pool.go @@ -108,6 +108,27 @@ var schemaNodePool = map[string]*schema.Schema{ Elem: &schema.Schema{Type: schema.TypeString}, }, + "upgrade_settings": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_surge": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + + "max_unavailable": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, + "initial_node_count": { Type: schema.TypeInt, Optional: true, @@ -492,6 +513,19 @@ func expandNodePool(d *schema.ResourceData, prefix string) (*containerBeta.NodeP } } + if v, ok := d.GetOk(prefix + "upgrade_settings"); ok { + upgradeSettingsConfig := v.([]interface{})[0].(map[string]interface{}) + np.UpgradeSettings = &containerBeta.UpgradeSettings{} + + if v, ok := upgradeSettingsConfig["max_surge"]; ok { + np.UpgradeSettings.MaxSurge = int64(v.(int)) + } + + if v, ok := upgradeSettingsConfig["max_unavailable"]; ok { + np.UpgradeSettings.MaxUnavailable = int64(v.(int)) + } + } + return np, nil } @@ -547,6 +581,17 @@ func flattenNodePool(d *schema.ResourceData, config *Config, np *containerBeta.N }, } + if np.UpgradeSettings != nil { + nodePool["upgrade_settings"] = []map[string]interface{}{ + { + "max_surge": np.UpgradeSettings.MaxSurge, + "max_unavailable": np.UpgradeSettings.MaxUnavailable, + }, + } + } else { + delete(nodePool, "upgrade_settings") + } + return nodePool, nil } @@ -766,6 +811,39 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node } } + if d.HasChange(prefix + "upgrade_settings") { + upgradeSettings := &containerBeta.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)) + } + req := &containerBeta.UpdateNodePoolRequest{ + UpgradeSettings: upgradeSettings, + } + updateF := func() error { + op, err := config.clientContainerBeta.Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(name), req).Do() + + if err != nil { + return err + } + + // Wait until it's updated + return containerOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, "updating GKE node pool upgrade settings", timeoutInMinutes) + } + + // Call update serially. + if err := lockedCall(lockKey, updateF); err != nil { + return err + } + + log.Printf("[INFO] Updated upgrade settings in Node Pool %s", name) + + if prefix == "" { + d.SetPartial("upgrade_settings") + } + } + return nil } diff --git a/google-beta/resource_container_node_pool_test.go b/google-beta/resource_container_node_pool_test.go index ef0001159c..0ae44b38e3 100644 --- a/google-beta/resource_container_node_pool_test.go +++ b/google-beta/resource_container_node_pool_test.go @@ -2,6 +2,7 @@ package google import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" @@ -239,6 +240,56 @@ func TestAccContainerNodePool_withSandboxConfig(t *testing.T) { }) } +func TestAccContainerNodePool_withUpgradeSettings(t *testing.T) { + t.Parallel() + + clusterName := acctest.RandString(10) + nodePoolName := acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withUpgradeSettings(clusterName, nodePoolName, 2, 3), + }, + { + ResourceName: "google_container_node_pool.with_upgrade_settings", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerNodePool_withUpgradeSettings(clusterName, nodePoolName, 1, 1), + }, + { + ResourceName: "google_container_node_pool.with_upgrade_settings", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccContainerNodePool_withInvalidUpgradeSettings(t *testing.T) { + t.Parallel() + + clusterName := acctest.RandString(10) + nodePoolName := acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withUpgradeSettings(clusterName, nodePoolName, 0, 0), + ExpectError: regexp.MustCompile(`.?Max_surge and max_unavailable must not be negative and at least one of them must be greater than zero.*`), + }, + }, + }) +} + func TestAccContainerNodePool_withGPU(t *testing.T) { t.Parallel() @@ -1179,6 +1230,32 @@ resource "google_container_node_pool" "with_sandbox_config" { `, acctest.RandString(10), acctest.RandString(10)) } +func testAccContainerNodePool_withUpgradeSettings(clusterName string, nodePoolName string, maxSurge int, maxUnavailable int) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1" { + location = "us-central1" +} + +resource "google_container_cluster" "cluster" { + name = "tf-cluster-nodepool-test-%s" + location = "us-central1" + initial_node_count = 1 + min_master_version = "${data.google_container_engine_versions.central1.latest_master_version}" +} + +resource "google_container_node_pool" "with_upgrade_settings" { + name = "tf-nodepool-test-%s" + location = "us-central1" + cluster = "${google_container_cluster.cluster.name}" + initial_node_count = 1 + upgrade_settings { + max_surge = %d + max_unavailable = %d + } +} +`, clusterName, nodePoolName, maxSurge, maxUnavailable) +} + func testAccContainerNodePool_withGPU() string { return fmt.Sprintf(` data "google_container_engine_versions" "central1c" { diff --git a/website/docs/r/container_node_pool.html.markdown b/website/docs/r/container_node_pool.html.markdown index 931d1b114a..8baf684324 100644 --- a/website/docs/r/container_node_pool.html.markdown +++ b/website/docs/r/container_node_pool.html.markdown @@ -144,6 +144,10 @@ 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, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) 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`. + The maximum number of nodes upgraded simultaneously is limited to 20. + * `version` - (Optional) The Kubernetes version for the nodes in this pool. Note that if this field and `auto_upgrade` are both specified, they will fight each other for what the node version should be, so setting both is highly discouraged. While a fuzzy version can be specified, it's @@ -164,6 +168,18 @@ The `management` block supports: * `auto_upgrade` - (Optional) Whether the nodes will be automatically upgraded. +The `upgrade_settings` block supports: + +* `max_surge` - (Required) 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 + 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. + ## Timeouts