diff --git a/mmv1/third_party/terraform/resources/resource_container_node_pool.go.erb b/mmv1/third_party/terraform/resources/resource_container_node_pool.go.erb index c51a66cc8e73..9f2c02d418a8 100644 --- a/mmv1/third_party/terraform/resources/resource_container_node_pool.go.erb +++ b/mmv1/third_party/terraform/resources/resource_container_node_pool.go.erb @@ -89,16 +89,37 @@ var schemaNodePool = map[string]*schema.Schema{ Schema: map[string]*schema.Schema{ "min_node_count": &schema.Schema{ Type: schema.TypeInt, - Required: true, + Optional: true, ValidateFunc: validation.IntAtLeast(0), - Description: `Minimum number of nodes in the NodePool. Must be >=0 and <= max_node_count.`, + Description: `Minimum number of nodes per zone in the node pool. Must be >=0 and <= max_node_count. Cannot be used with total limits.`, }, "max_node_count": &schema.Schema{ Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.IntAtLeast(1), - Description: `Maximum number of nodes in the NodePool. Must be >= min_node_count.`, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + Description: `Maximum number of nodes per zone in the node pool. Must be >= min_node_count. Cannot be used with total limits.`, + }, + + "total_min_node_count": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + Description: `Minimum number of all nodes in the node pool. Must be >=0 and <= total_max_node_count. Cannot be used with per zone limits.`, + }, + + "total_max_node_count": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + Description: `Maximum number of all nodes in the node pool. Must be >= total_min_node_count. Cannot be used with per zone limits.`, + }, + + "location_policy": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"BALANCED", "ANY"}, false), + Description: `Location policy specifies the algorithm used when scaling-up the node pool. "BALANCED" - Is a best effort policy that aims to balance the sizes of available zones. "ANY" - Instructs the cluster autoscaler to prioritize utilization of unused reservations, and reduces preemption risk for Spot VMs.`, }, }, }, @@ -759,10 +780,13 @@ func expandNodePool(d *schema.ResourceData, prefix string) (*container.NodePool, if v, ok := d.GetOk(prefix + "autoscaling"); ok { autoscaling := v.([]interface{})[0].(map[string]interface{}) np.Autoscaling = &container.NodePoolAutoscaling{ - Enabled: true, - MinNodeCount: int64(autoscaling["min_node_count"].(int)), - MaxNodeCount: int64(autoscaling["max_node_count"].(int)), - ForceSendFields: []string{"MinNodeCount"}, + Enabled: true, + MinNodeCount: int64(autoscaling["min_node_count"].(int)), + MaxNodeCount: int64(autoscaling["max_node_count"].(int)), + TotalMinNodeCount: int64(autoscaling["total_min_node_count"].(int)), + TotalMaxNodeCount: int64(autoscaling["total_max_node_count"].(int)), + LocationPolicy: autoscaling["location_policy"].(string), + ForceSendFields: []string{"MinNodeCount", "MaxNodeCount", "TotalMinNodeCount", "TotalMaxNodeCount"}, } } @@ -863,8 +887,11 @@ func flattenNodePool(d *schema.ResourceData, config *Config, np *container.NodeP if np.Autoscaling.Enabled { nodePool["autoscaling"] = []map[string]interface{}{ { - "min_node_count": np.Autoscaling.MinNodeCount, - "max_node_count": np.Autoscaling.MaxNodeCount, + "min_node_count": np.Autoscaling.MinNodeCount, + "max_node_count": np.Autoscaling.MaxNodeCount, + "total_min_node_count": np.Autoscaling.TotalMinNodeCount, + "total_max_node_count": np.Autoscaling.TotalMaxNodeCount, + "location_policy": np.Autoscaling.LocationPolicy, }, } } else { @@ -967,10 +994,13 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node if v, ok := d.GetOk(prefix + "autoscaling"); ok { autoscaling := v.([]interface{})[0].(map[string]interface{}) update.DesiredNodePoolAutoscaling = &container.NodePoolAutoscaling{ - Enabled: true, - MinNodeCount: int64(autoscaling["min_node_count"].(int)), - MaxNodeCount: int64(autoscaling["max_node_count"].(int)), - ForceSendFields: []string{"MinNodeCount"}, + Enabled: true, + MinNodeCount: int64(autoscaling["min_node_count"].(int)), + MaxNodeCount: int64(autoscaling["max_node_count"].(int)), + TotalMinNodeCount: int64(autoscaling["total_min_node_count"].(int)), + TotalMaxNodeCount: int64(autoscaling["total_max_node_count"].(int)), + LocationPolicy: autoscaling["location_policy"].(string), + ForceSendFields: []string{"MinNodeCount", "TotalMinNodeCount"}, } } else { update.DesiredNodePoolAutoscaling = &container.NodePoolAutoscaling{ diff --git a/mmv1/third_party/terraform/tests/resource_container_cluster_test.go.erb b/mmv1/third_party/terraform/tests/resource_container_cluster_test.go.erb index c413fddd1338..d25fff55a7be 100755 --- a/mmv1/third_party/terraform/tests/resource_container_cluster_test.go.erb +++ b/mmv1/third_party/terraform/tests/resource_container_cluster_test.go.erb @@ -1426,6 +1426,68 @@ func TestAccContainerCluster_withNodePoolAutoscaling(t *testing.T) { }) } +func TestAccContainerCluster_withNodePoolCIA(t *testing.T) { + t.Parallel() + + clusterName := fmt.Sprintf("tf-test-cluster-nodepool-%s", randString(t, 10)) + npName := fmt.Sprintf("tf-test-cluster-nodepool-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerNodePoolDestroyProducer(t), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccContainerRegionalCluster_withNodePoolCIA(clusterName, npName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.min_node_count", "0"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.max_node_count", "0"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.total_min_node_count", "3"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.total_max_node_count", "21"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.location_policy", "BALANCED"), + ), + }, + { + ResourceName: "google_container_cluster.with_node_pool", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + resource.TestStep{ + Config: testAccContainerRegionalClusterUpdate_withNodePoolCIA(clusterName, npName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.min_node_count", "0"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.max_node_count", "0"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.total_min_node_count", "4"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.total_max_node_count", "32"), + resource.TestCheckResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.location_policy", "ANY"), + ), + }, + { + ResourceName: "google_container_cluster.with_node_pool", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + resource.TestStep{ + Config: testAccContainerRegionalCluster_withNodePoolBasic(clusterName, npName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.min_node_count"), + resource.TestCheckNoResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.max_node_count"), + resource.TestCheckNoResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.total_min_node_count"), + resource.TestCheckNoResourceAttr("google_container_cluster.with_node_pool", "node_pool.0.autoscaling.0.total_max_node_count"), + ), + }, + { + ResourceName: "google_container_cluster.with_node_pool", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version"}, + }, + }, + }) +} + func TestAccContainerCluster_withNodePoolNamePrefix(t *testing.T) { // Randomness skipIfVcr(t) @@ -4737,6 +4799,61 @@ resource "google_container_cluster" "with_node_pool" { `, cluster, np) } +func testAccContainerRegionalCluster_withNodePoolCIA(cluster, np string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_node_pool" { + name = "%s" + location = "us-central1" + min_master_version = "1.24" + + node_pool { + name = "%s" + initial_node_count = 2 + autoscaling { + total_min_node_count = 3 + total_max_node_count = 21 + location_policy = "BALANCED" + } + } +} +`, cluster, np) +} + +func testAccContainerRegionalClusterUpdate_withNodePoolCIA(cluster, np string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_node_pool" { + name = "%s" + location = "us-central1" + min_master_version = "1.24" + + node_pool { + name = "%s" + initial_node_count = 2 + autoscaling { + total_min_node_count = 4 + total_max_node_count = 32 + location_policy = "ANY" + } + } +} +`, cluster, np) +} + +func testAccContainerRegionalCluster_withNodePoolBasic(cluster, nodePool string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_node_pool" { + name = "%s" + location = "us-central1" + min_master_version = "1.24" + + node_pool { + name = "%s" + initial_node_count = 2 + } +} +`, cluster, nodePool) +} + func testAccContainerCluster_withNodePoolNamePrefix(cluster, npPrefix string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_node_pool_name_prefix" { diff --git a/mmv1/third_party/terraform/tests/resource_container_node_pool_test.go.erb b/mmv1/third_party/terraform/tests/resource_container_node_pool_test.go.erb index 29db3e5ccf04..a640a0b9a9d9 100644 --- a/mmv1/third_party/terraform/tests/resource_container_node_pool_test.go.erb +++ b/mmv1/third_party/terraform/tests/resource_container_node_pool_test.go.erb @@ -691,6 +691,63 @@ func TestAccContainerNodePool_regionalAutoscaling(t *testing.T) { }) } +//This test exists to validate a node pool with total size *and* and update to it. +func TestAccContainerNodePool_totalSize(t *testing.T) { + t.Parallel() + + cluster := fmt.Sprintf("tf-test-cluster-%s", randString(t, 10)) + np := fmt.Sprintf("tf-test-nodepool-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerNodePoolDestroyProducer(t), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccContainerNodePool_totalSize(cluster, np), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_container_node_pool.np", "autoscaling.0.total_min_node_count", "4"), + resource.TestCheckResourceAttr("google_container_node_pool.np", "autoscaling.0.total_max_node_count", "12"), + resource.TestCheckResourceAttr("google_container_node_pool.np", "autoscaling.0.location_policy", "BALANCED"), + ), + }, + resource.TestStep{ + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccContainerNodePool_updateTotalSize(cluster, np), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_container_node_pool.np", "autoscaling.0.total_min_node_count", "2"), + resource.TestCheckResourceAttr("google_container_node_pool.np", "autoscaling.0.total_max_node_count", "22"), + resource.TestCheckResourceAttr("google_container_node_pool.np", "autoscaling.0.location_policy", "ANY"), + ), + }, + resource.TestStep{ + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + Config: testAccContainerNodePool_basicTotalSize(cluster, np), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr("google_container_node_pool.np", "autoscaling.0.min_node_count"), + resource.TestCheckNoResourceAttr("google_container_node_pool.np", "autoscaling.0.max_node_count"), + ), + }, + resource.TestStep{ + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + // autoscaling.# = 0 is equivalent to no autoscaling at all, + // but will still cause an import diff + ImportStateVerifyIgnore: []string{"autoscaling.#"}, + }, + }, + }) +} + func TestAccContainerNodePool_autoscaling(t *testing.T) { t.Parallel() @@ -1399,6 +1456,74 @@ resource "google_container_node_pool" "np" { `, cluster, np) } +func testAccContainerNodePool_totalSize(cluster, np string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "cluster" { + name = "%s" + location = "us-central1" + initial_node_count = 3 + min_master_version = "1.24" +} + +resource "google_container_node_pool" "np" { + name = "%s" + location = "us-central1" + cluster = google_container_cluster.cluster.name + initial_node_count = 2 + autoscaling { + total_min_node_count = 4 + total_max_node_count = 12 + location_policy = "BALANCED" + } +} +`, cluster, np) +} + +func testAccContainerNodePool_updateTotalSize(cluster, np string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "cluster" { + name = "%s" + location = "us-central1" + initial_node_count = 3 + min_master_version = "1.24" +} + +resource "google_container_node_pool" "np" { + name = "%s" + location = "us-central1" + cluster = google_container_cluster.cluster.name + initial_node_count = 2 + autoscaling { + total_min_node_count = 2 + total_max_node_count = 22 + location_policy = "ANY" + } +} +`, cluster, np) +} + + +func testAccContainerNodePool_basicTotalSize(cluster, np string) string { + return fmt.Sprintf(` +provider "google" { + user_project_override = true +} +resource "google_container_cluster" "cluster" { + name = "%s" + location = "us-central1" + initial_node_count = 3 + min_master_version = "1.24" +} + +resource "google_container_node_pool" "np" { + name = "%s" + location = "us-central1" + cluster = google_container_cluster.cluster.name + initial_node_count = 2 +} +`, cluster, np) +} + func testAccContainerNodePool_autoscaling(cluster, np string) string { return fmt.Sprintf(` resource "google_container_cluster" "cluster" { diff --git a/mmv1/third_party/terraform/website/docs/r/container_node_pool.html.markdown b/mmv1/third_party/terraform/website/docs/r/container_node_pool.html.markdown index e9f48766a493..2840f49e3e03 100644 --- a/mmv1/third_party/terraform/website/docs/r/container_node_pool.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/container_node_pool.html.markdown @@ -171,12 +171,24 @@ cluster. * `placement_policy` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) Specifies a custom placement policy for the nodes. -The `autoscaling` block supports: +The `autoscaling` block supports (either total or per zone limits are required): -* `min_node_count` - (Required) Minimum number of nodes in the NodePool. Must be >=0 and - <= `max_node_count`. +* `min_node_count` - (Optional) Minimum number of nodes per zone in the NodePool. + Must be >=0 and <= `max_node_count`. Cannot be used with total limits. -* `max_node_count` - (Required) Maximum number of nodes in the NodePool. Must be >= min_node_count. +* `max_node_count` - (Optional) Maximum number of nodes per zone in the NodePool. + Must be >= min_node_count. Cannot be used with total limits. + +* `total_min_node_count` - (Optional) Total minimum number of nodes in the NodePool. + Must be >=0 and <= `total_max_node_count`. Cannot be used with per zone limits. + +* `total_max_node_count` - (Optional) Total maximum number of nodes in the NodePool. + Must be >= total_min_node_count. Cannot be used with per zone limits. + +* `location_policy` - (Optional) Location policy specifies the algorithm used when scaling-up the node pool. \ + "BALANCED" - Is a best effort policy that aims to balance the sizes of available zones. \ + "ANY" - Instructs the cluster autoscaler to prioritize utilization of unused reservations, + and reduce preemption risk for Spot VMs. The `management` block supports: