diff --git a/.changelog/3252.txt b/.changelog/3252.txt new file mode 100644 index 00000000000..e155446c253 --- /dev/null +++ b/.changelog/3252.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +container: added `node_pool.upgrade_settings` (TPG only) +``` +```release-note:enhancement +container: updated `node_pool.upgrade_settings` to read defaults from API (TPGB only) +``` diff --git a/google/resource_container_node_pool.go b/google/resource_container_node_pool.go index d8a6c493b9f..677adb7dc21 100644 --- a/google/resource_container_node_pool.go +++ b/google/resource_container_node_pool.go @@ -103,6 +103,28 @@ var schemaNodePool = map[string]*schema.Schema{ Computed: true, }, + "upgrade_settings": { + Type: schema.TypeList, + Optional: true, + Computed: 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, @@ -515,6 +537,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 } @@ -569,6 +604,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 } @@ -761,6 +807,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/resource_container_node_pool_test.go b/google/resource_container_node_pool_test.go index fa5a7381ac1..69073a8ff38 100644 --- a/google/resource_container_node_pool_test.go +++ b/google/resource_container_node_pool_test.go @@ -2,6 +2,7 @@ package google import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" @@ -138,6 +139,56 @@ func TestAccContainerNodePool_withNodeConfig(t *testing.T) { }) } +func TestAccContainerNodePool_withUpgradeSettings(t *testing.T) { + t.Parallel() + + cluster := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(10)) + np := fmt.Sprintf("tf-test-np-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 2, 3), + }, + { + ResourceName: "google_container_node_pool.with_upgrade_settings", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 1, 1), + }, + { + ResourceName: "google_container_node_pool.with_upgrade_settings", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccContainerNodePool_withInvalidUpgradeSettings(t *testing.T) { + t.Parallel() + + cluster := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(10)) + np := fmt.Sprintf("tf-test-np-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withUpgradeSettings(cluster, np, 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() @@ -922,6 +973,32 @@ resource "google_container_node_pool" "np_with_node_config" { `, cluster, nodePool) } +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 = "%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 = "%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(cluster, np string) string { return fmt.Sprintf(` data "google_container_engine_versions" "central1c" {