Skip to content

Commit

Permalink
improve autoscaling_policy support in google_compute_node_group (#9044)…
Browse files Browse the repository at this point in the history
… (#16006)

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Sep 26, 2023
1 parent 9ce1711 commit 872a14c
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 74 deletions.
9 changes: 9 additions & 0 deletions .changelog/9044.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
```release-note:breaking-change
compute: `size` in `google_compute_node_group` is now an output only field.
```
```release-note:enhancement
compute: `google_compute_node_group` made mutable
```
```release-note:note
compute: `google_compute_node_group` made to require one of `initial_size` or `autoscaling_policy` fields configured upon resource creation
```
8 changes: 4 additions & 4 deletions google/services/compute/resource_compute_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5905,7 +5905,7 @@ resource "google_compute_node_group" "nodes" {
name = "%s"
zone = "us-central1-a"
size = 1
initial_size = 1
node_template = google_compute_node_template.nodetmpl.self_link
}
`, instance, nodeTemplate, nodeGroup)
Expand Down Expand Up @@ -5974,7 +5974,7 @@ resource "google_compute_node_group" "nodes" {
name = "%s"
zone = "us-central1-a"
size = 1
initial_size = 1
node_template = google_compute_node_template.nodetmpl.self_link
}
`, instance, nodeTemplate, nodeGroup)
Expand Down Expand Up @@ -6043,7 +6043,7 @@ resource "google_compute_node_group" "nodes" {
name = "%s"
zone = "us-central1-a"
size = 1
initial_size = 1
node_template = google_compute_node_template.nodetmpl.self_link
}
`, instance, nodeTemplate, nodeGroup)
Expand Down Expand Up @@ -6106,7 +6106,7 @@ resource "google_compute_node_group" "nodes" {
name = "%s"
zone = "us-central1-a"
size = 1
initial_size = 1
node_template = google_compute_node_template.nodetmpl.self_link
}
`, instance, nodeTemplate, nodeGroup)
Expand Down
179 changes: 137 additions & 42 deletions google/services/compute/resource_compute_node_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
package compute

import (
"errors"
"fmt"
"log"
"reflect"
"regexp"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
Expand Down Expand Up @@ -64,25 +66,24 @@ func ResourceComputeNodeGroup() *schema.Resource {
Type: schema.TypeList,
Computed: true,
Optional: true,
ForceNew: true,
Description: `If you use sole-tenant nodes for your workloads, you can use the node
group autoscaler to automatically manage the sizes of your node groups.`,
group autoscaler to automatically manage the sizes of your node groups.
One of 'initial_size' or 'autoscaling_policy' must be configured on resource creation.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"max_nodes": {
Type: schema.TypeInt,
Computed: true,
Optional: true,
ForceNew: true,
Description: `Maximum size of the node group. Set to a value less than or equal
to 100 and greater than or equal to min-nodes.`,
},
"mode": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
ValidateFunc: verify.ValidateEnum([]string{"OFF", "ON", "ONLY_SCALE_OUT"}),
Description: `The autoscaling mode. Set to one of the following:
- OFF: Disables the autoscaler.
Expand All @@ -95,7 +96,6 @@ to 100 and greater than or equal to min-nodes.`,
Type: schema.TypeInt,
Computed: true,
Optional: true,
ForceNew: true,
Description: `Minimum size of the node group. Must be less
than or equal to max-nodes. The default value is 0.`,
},
Expand All @@ -105,35 +105,29 @@ than or equal to max-nodes. The default value is 0.`,
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: `An optional textual description of the resource.`,
},
"initial_size": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Description: `The initial number of nodes in the node group. One of 'initial_size' or 'size' must be specified.`,
ExactlyOneOf: []string{"size", "initial_size"},
Type: schema.TypeInt,
Optional: true,
Description: `The initial number of nodes in the node group. One of 'initial_size' or 'autoscaling_policy' must be configured on resource creation.`,
},
"maintenance_policy": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: `Specifies how to handle instances when a node in the group undergoes maintenance. Set to one of: DEFAULT, RESTART_IN_PLACE, or MIGRATE_WITHIN_NODE_GROUP. The default value is DEFAULT.`,
Default: "DEFAULT",
},
"maintenance_window": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Description: `contains properties for the timeframe of maintenance`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"start_time": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `instances.start time of the window. This must be in UTC format that resolves to one of 00:00, 04:00, 08:00, 12:00, 16:00, or 20:00. For example, both 13:00-5 and 08:00 are valid.`,
},
},
Expand All @@ -142,41 +136,35 @@ than or equal to max-nodes. The default value is 0.`,
"name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: `Name of the resource.`,
},
"share_settings": {
Type: schema.TypeList,
Computed: true,
Optional: true,
ForceNew: true,
Description: `Share settings for the node group.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"share_type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: verify.ValidateEnum([]string{"ORGANIZATION", "SPECIFIC_PROJECTS", "LOCAL"}),
Description: `Node group sharing type. Possible values: ["ORGANIZATION", "SPECIFIC_PROJECTS", "LOCAL"]`,
},
"project_map": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Description: `A map of project id and project config. This is only valid when shareType's value is SPECIFIC_PROJECTS.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"project_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `The project id/number should be the same as the key of this project config in the project map.`,
},
},
Expand All @@ -185,19 +173,10 @@ than or equal to max-nodes. The default value is 0.`,
},
},
},
"size": {
Type: schema.TypeInt,
Computed: true,
Optional: true,
ForceNew: true,
Description: `The total number of nodes in the node group. One of 'initial_size' or 'size' must be specified.`,
ExactlyOneOf: []string{"size", "initial_size"},
},
"zone": {
Type: schema.TypeString,
Computed: true,
Optional: true,
ForceNew: true,
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
Description: `Zone where this node group is located`,
},
Expand All @@ -206,6 +185,11 @@ than or equal to max-nodes. The default value is 0.`,
Computed: true,
Description: `Creation timestamp in RFC3339 text format.`,
},
"size": {
Type: schema.TypeInt,
Computed: true,
Description: `The total number of nodes in the node group.`,
},
"project": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -247,12 +231,6 @@ func resourceComputeNodeGroupCreate(d *schema.ResourceData, meta interface{}) er
} else if v, ok := d.GetOkExists("node_template"); !tpgresource.IsEmptyValue(reflect.ValueOf(nodeTemplateProp)) && (ok || !reflect.DeepEqual(v, nodeTemplateProp)) {
obj["nodeTemplate"] = nodeTemplateProp
}
sizeProp, err := expandComputeNodeGroupSize(d.Get("size"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("size"); ok || !reflect.DeepEqual(v, sizeProp) {
obj["size"] = sizeProp
}
maintenancePolicyProp, err := expandComputeNodeGroupMaintenancePolicy(d.Get("maintenance_policy"), d, config)
if err != nil {
return err
Expand Down Expand Up @@ -304,10 +282,14 @@ func resourceComputeNodeGroupCreate(d *schema.ResourceData, meta interface{}) er
}

var sizeParam string
if v, ok := d.GetOkExists("size"); ok {
sizeParam = fmt.Sprintf("%v", v)
} else if v, ok := d.GetOkExists("initial_size"); ok {
if v, ok := d.GetOkExists("initial_size"); ok {
sizeParam = fmt.Sprintf("%v", v)
} else {
if _, ok := d.GetOkExists("autoscaling_policy"); ok {
sizeParam = fmt.Sprintf("%v", d.Get("autoscaling_policy.min_nodes"))
} else {
return errors.New("An initial_size or autoscaling_policy must be configured on node group creation.")
}
}

url = regexp.MustCompile("PRE_CREATE_REPLACE_ME").ReplaceAllLiteralString(url, sizeParam)
Expand Down Expand Up @@ -438,6 +420,123 @@ func resourceComputeNodeGroupUpdate(d *schema.ResourceData, meta interface{}) er
}
billingProject = project

obj := make(map[string]interface{})
descriptionProp, err := expandComputeNodeGroupDescription(d.Get("description"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("description"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
obj["description"] = descriptionProp
}
nameProp, err := expandComputeNodeGroupName(d.Get("name"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, nameProp)) {
obj["name"] = nameProp
}
maintenancePolicyProp, err := expandComputeNodeGroupMaintenancePolicy(d.Get("maintenance_policy"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("maintenance_policy"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, maintenancePolicyProp)) {
obj["maintenancePolicy"] = maintenancePolicyProp
}
maintenanceWindowProp, err := expandComputeNodeGroupMaintenanceWindow(d.Get("maintenance_window"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("maintenance_window"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, maintenanceWindowProp)) {
obj["maintenanceWindow"] = maintenanceWindowProp
}
autoscalingPolicyProp, err := expandComputeNodeGroupAutoscalingPolicy(d.Get("autoscaling_policy"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("autoscaling_policy"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, autoscalingPolicyProp)) {
obj["autoscalingPolicy"] = autoscalingPolicyProp
}
shareSettingsProp, err := expandComputeNodeGroupShareSettings(d.Get("share_settings"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("share_settings"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, shareSettingsProp)) {
obj["shareSettings"] = shareSettingsProp
}
zoneProp, err := expandComputeNodeGroupZone(d.Get("zone"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("zone"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, zoneProp)) {
obj["zone"] = zoneProp
}

url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/zones/{{zone}}/nodeGroups/{{name}}")
if err != nil {
return err
}

log.Printf("[DEBUG] Updating NodeGroup %q: %#v", d.Id(), obj)
updateMask := []string{}

if d.HasChange("description") {
updateMask = append(updateMask, "description")
}

if d.HasChange("name") {
updateMask = append(updateMask, "name")
}

if d.HasChange("maintenance_policy") {
updateMask = append(updateMask, "maintenancePolicy")
}

if d.HasChange("maintenance_window") {
updateMask = append(updateMask, "maintenanceWindow")
}

if d.HasChange("autoscaling_policy") {
updateMask = append(updateMask, "autoscalingPolicy")
}

if d.HasChange("share_settings") {
updateMask = append(updateMask, "shareSettings")
}

if d.HasChange("zone") {
updateMask = append(updateMask, "zone")
}
// updateMask is a URL parameter but not present in the schema, so ReplaceVars
// won't set it
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
if err != nil {
return err
}

// err == nil indicates that the billing_project value was found
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
billingProject = bp
}

// if updateMask is empty we are not updating anything so skip the post
if len(updateMask) > 0 {
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "PATCH",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Body: obj,
Timeout: d.Timeout(schema.TimeoutUpdate),
})

if err != nil {
return fmt.Errorf("Error updating NodeGroup %q: %s", d.Id(), err)
} else {
log.Printf("[DEBUG] Finished updating NodeGroup %q: %#v", d.Id(), res)
}

err = ComputeOperationWaitTime(
config, res, project, "Updating NodeGroup", userAgent,
d.Timeout(schema.TimeoutUpdate))

if err != nil {
return err
}
}
d.Partial(true)

if d.HasChange("node_template") {
Expand Down Expand Up @@ -735,10 +834,6 @@ func expandComputeNodeGroupNodeTemplate(v interface{}, d tpgresource.TerraformRe
return f.RelativeLink(), nil
}

func expandComputeNodeGroupSize(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}

func expandComputeNodeGroupMaintenancePolicy(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ resource "google_compute_node_group" "nodes" {
zone = "us-central1-f"
description = "example google_compute_node_group for Terraform Google Provider"
size = 1
initial_size = 1
node_template = google_compute_node_template.soletenant-tmpl.id
}
`, context)
Expand Down Expand Up @@ -172,7 +172,7 @@ resource "google_compute_node_group" "nodes" {
zone = "us-central1-f"
description = "example google_compute_node_group for Terraform Google Provider"
size = 1
initial_size = 1
node_template = google_compute_node_template.soletenant-tmpl.id
share_settings {
Expand Down
Loading

0 comments on commit 872a14c

Please sign in to comment.