Rolling update support for instance group manager (#1137)

This commit is contained in:
ishashchuk 2018-03-15 14:10:09 -04:00 committed by Nathan McKinley
parent 0a451ed438
commit 14f1431896
3 changed files with 354 additions and 6 deletions

View File

@ -14,7 +14,10 @@ import (
)
var InstanceGroupManagerBaseApiVersion = v1
var InstanceGroupManagerVersionedFeatures = []Feature{Feature{Version: v0beta, Item: "auto_healing_policies"}}
var InstanceGroupManagerVersionedFeatures = []Feature{
Feature{Version: v0beta, Item: "auto_healing_policies"},
Feature{Version: v0beta, Item: "rolling_update_policy"},
}
func resourceComputeInstanceGroupManager() *schema.Resource {
return &schema.Resource{
@ -102,7 +105,7 @@ func resourceComputeInstanceGroupManager() *schema.Resource {
Type: schema.TypeString,
Optional: true,
Default: "RESTART",
ValidateFunc: validation.StringInSlice([]string{"RESTART", "NONE"}, false),
ValidateFunc: validation.StringInSlice([]string{"RESTART", "NONE", "ROLLING_UPDATE"}, false),
},
"target_pools": &schema.Schema{
@ -140,6 +143,60 @@ func resourceComputeInstanceGroupManager() *schema.Resource {
},
},
},
"rolling_update_policy": &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"minimal_action": &schema.Schema{
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"RESTART", "REPLACE"}, false),
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"OPPORTUNISTIC", "PROACTIVE"}, false),
},
"max_surge_fixed": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 1,
ConflictsWith: []string{"rolling_update_policy.0.max_surge_percent"},
},
"max_surge_percent": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ConflictsWith: []string{"rolling_update_policy.0.max_surge_fixed"},
ValidateFunc: validation.IntBetween(0, 100),
},
"max_unavailable_fixed": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 1,
ConflictsWith: []string{"rolling_update_policy.0.max_unavailable_percent"},
},
"max_unavailable_percent": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ConflictsWith: []string{"rolling_update_policy.0.max_unavailable_fixed"},
ValidateFunc: validation.IntBetween(0, 100),
},
"min_ready_sec": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 3600),
},
},
},
},
},
}
}
@ -184,6 +241,10 @@ func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta inte
return err
}
if _, ok := d.GetOk("rolling_update_policy"); d.Get("update_strategy") == "ROLLING_UPDATE" && !ok {
return fmt.Errorf("[rolling_update_policy] must be set when 'update_strategy' is set to 'ROLLING_UPDATE'")
}
// Build the parameter
manager := &computeBeta.InstanceGroupManager{
Name: d.Get("name").(string),
@ -380,6 +441,10 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
d.Partial(true)
if _, ok := d.GetOk("rolling_update_policy"); d.Get("update_strategy") == "ROLLING_UPDATE" && !ok {
return fmt.Errorf("[rolling_update_policy] must be set when 'update_strategy' is set to 'ROLLING_UPDATE'")
}
// If target_pools changes then update
if d.HasChange("target_pools") {
targetPools := convertStringSet(d.Get("target_pools").(*schema.Set))
@ -536,6 +601,28 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
}
}
if d.Get("update_strategy").(string) == "ROLLING_UPDATE" {
// UpdatePolicy is set for InstanceGroupManager on update only, because it is only relevant for `Patch` calls.
// Other tools(gcloud and UI) capable of executing the same `ROLLING UPDATE` call
// expect those values to be provided by user as part of the call
// or provide their own defaults without respecting what was previously set on UpdateManager.
// To follow the same logic, we provide policy values on relevant update change only.
manager := &computeBeta.InstanceGroupManager{
UpdatePolicy: expandUpdatePolicy(d.Get("rolling_update_policy").([]interface{})),
}
op, err = config.clientComputeBeta.InstanceGroupManagers.Patch(
project, zone, d.Id(), manager).Do()
if err != nil {
return fmt.Errorf("Error updating managed group instances: %s", err)
}
err = computeSharedOperationWait(config.clientCompute, op, project, "Updating managed group instances")
if err != nil {
return err
}
}
d.SetPartial("instance_template")
}
@ -732,6 +819,44 @@ func expandAutoHealingPolicies(configured []interface{}) []*computeBeta.Instance
return autoHealingPolicies
}
func expandUpdatePolicy(configured []interface{}) *computeBeta.InstanceGroupManagerUpdatePolicy {
updatePolicy := &computeBeta.InstanceGroupManagerUpdatePolicy{}
for _, raw := range configured {
data := raw.(map[string]interface{})
updatePolicy.MinimalAction = data["minimal_action"].(string)
updatePolicy.Type = data["type"].(string)
// percent and fixed values are conflicting
// when the percent values are set, the fixed values will be ignored
if v := data["max_surge_percent"]; v.(int) > 0 {
updatePolicy.MaxSurge = &computeBeta.FixedOrPercent{
Percent: int64(v.(int)),
}
} else {
updatePolicy.MaxSurge = &computeBeta.FixedOrPercent{
Fixed: int64(data["max_surge_fixed"].(int)),
}
}
if v := data["max_unavailable_percent"]; v.(int) > 0 {
updatePolicy.MaxUnavailable = &computeBeta.FixedOrPercent{
Percent: int64(v.(int)),
}
} else {
updatePolicy.MaxUnavailable = &computeBeta.FixedOrPercent{
Fixed: int64(data["max_unavailable_fixed"].(int)),
}
}
if v, ok := data["min_ready_sec"]; ok {
updatePolicy.MinReadySec = int64(v.(int))
}
}
return updatePolicy
}
func flattenAutoHealingPolicies(autoHealingPolicies []*computeBeta.InstanceGroupManagerAutoHealingPolicy) []map[string]interface{} {
autoHealingPoliciesSchema := make([]map[string]interface{}, 0, len(autoHealingPolicies))
for _, autoHealingPolicy := range autoHealingPolicies {

View File

@ -3,6 +3,7 @@ package google
import (
"fmt"
"reflect"
"strconv"
"strings"
"testing"
@ -186,6 +187,62 @@ func TestAccInstanceGroupManager_updateStrategy(t *testing.T) {
})
}
func TestAccInstanceGroupManager_rollingUpdatePolicy(t *testing.T) {
t.Parallel()
var manager computeBeta.InstanceGroupManager
igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckInstanceGroupManagerDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccInstanceGroupManager_rollingUpdatePolicy(igm),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceGroupManagerBetaExists(
"google_compute_instance_group_manager.igm-rolling-update-policy", &manager),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "update_strategy", "ROLLING_UPDATE"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.type", "PROACTIVE"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.minimal_action", "REPLACE"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_surge_percent", "50"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_unavailable_percent", "50"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.min_ready_sec", "20"),
),
},
resource.TestStep{
Config: testAccInstanceGroupManager_rollingUpdatePolicy2(igm),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceGroupManagerBetaExists(
"google_compute_instance_group_manager.igm-rolling-update-policy", &manager),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "update_strategy", "ROLLING_UPDATE"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.type", "PROACTIVE"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.minimal_action", "REPLACE"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_surge_fixed", "2"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.max_unavailable_fixed", "2"),
resource.TestCheckResourceAttr(
"google_compute_instance_group_manager.igm-rolling-update-policy", "rolling_update_policy.0.min_ready_sec", "20"),
testAccCheckInstanceGroupManagerRollingUpdatePolicy(
&manager, "google_compute_instance_group_manager.igm-rolling-update-policy"),
),
},
},
})
}
func TestAccInstanceGroupManager_separateRegions(t *testing.T) {
t.Parallel()
@ -521,6 +578,50 @@ func testAccCheckInstanceGroupManagerUpdateStrategy(n, strategy string) resource
}
}
func testAccCheckInstanceGroupManagerRollingUpdatePolicy(manager *computeBeta.InstanceGroupManager, resource string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs := s.RootModule().Resources[resource]
updatePolicy := manager.UpdatePolicy
surgeFixed, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_surge_fixed"], 10, 64)
if updatePolicy.MaxSurge.Fixed != surgeFixed {
return fmt.Errorf("Expected update policy MaxSurge to be %d, got %d", surgeFixed, updatePolicy.MaxSurge.Fixed)
}
surgePercent, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_surge_percent"], 10, 64)
if updatePolicy.MaxSurge.Percent != surgePercent {
return fmt.Errorf("Expected update policy MaxSurge to be %d, got %d", surgePercent, updatePolicy.MaxSurge.Percent)
}
unavailableFixed, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_unavailable_fixed"], 10, 64)
if updatePolicy.MaxUnavailable.Fixed != unavailableFixed {
return fmt.Errorf("Expected update policy MaxUnavailable to be %d, got %d", unavailableFixed, updatePolicy.MaxUnavailable.Fixed)
}
unavailablePercent, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.max_unavailable_percent"], 10, 64)
if updatePolicy.MaxUnavailable.Percent != unavailablePercent {
return fmt.Errorf("Expected update policy MaxUnavailable to be %d, got %d", unavailablePercent, updatePolicy.MaxUnavailable.Percent)
}
policyType := rs.Primary.Attributes["rolling_update_policy.0.type"]
if updatePolicy.Type != policyType {
return fmt.Errorf("Expected update policy Type to be \"%s\", got \"%s\"", policyType, updatePolicy.Type)
}
policyAction := rs.Primary.Attributes["rolling_update_policy.0.minimal_action"]
if updatePolicy.MinimalAction != policyAction {
return fmt.Errorf("Expected update policy MinimalAction to be \"%s\", got \"%s\"", policyAction, updatePolicy.MinimalAction)
}
minReadySec, _ := strconv.ParseInt(rs.Primary.Attributes["rolling_update_policy.0.min_ready_sec"], 10, 64)
if updatePolicy.MinReadySec != minReadySec {
return fmt.Errorf("Expected update policy MinReadySec to be %d, got %d", minReadySec, updatePolicy.MinReadySec)
}
return nil
}
}
func testAccInstanceGroupManager_basic(template, target, igm1, igm2 string) string {
return fmt.Sprintf(`
resource "google_compute_instance_template" "igm-basic" {
@ -828,6 +929,98 @@ func testAccInstanceGroupManager_updateStrategy(igm string) string {
}`, igm)
}
func testAccInstanceGroupManager_rollingUpdatePolicy(igm string) string {
return fmt.Sprintf(`
resource "google_compute_instance_template" "igm-rolling-update-policy" {
machine_type = "n1-standard-1"
can_ip_forward = false
tags = ["terraform-testing"]
disk {
source_image = "debian-cloud/debian-8-jessie-v20160803"
auto_delete = true
boot = true
}
network_interface {
network = "default"
}
service_account {
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
}
lifecycle {
create_before_destroy = true
}
}
resource "google_compute_instance_group_manager" "igm-rolling-update-policy" {
description = "Terraform test instance group manager"
name = "%s"
instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}"
base_instance_name = "igm-rolling-update-policy"
zone = "us-central1-c"
target_size = 3
update_strategy = "ROLLING_UPDATE"
rolling_update_policy {
type = "PROACTIVE"
minimal_action = "REPLACE"
max_surge_percent = 50
max_unavailable_percent = 50
min_ready_sec = 20
}
named_port {
name = "customhttp"
port = 8080
}
}`, igm)
}
func testAccInstanceGroupManager_rollingUpdatePolicy2(igm string) string {
return fmt.Sprintf(`
resource "google_compute_instance_template" "igm-rolling-update-policy" {
machine_type = "n1-standard-1"
can_ip_forward = false
tags = ["terraform-testing"]
disk {
source_image = "debian-cloud/debian-8-jessie-v20160803"
auto_delete = true
boot = true
}
network_interface {
network = "default"
}
lifecycle {
create_before_destroy = true
}
}
resource "google_compute_instance_group_manager" "igm-rolling-update-policy" {
description = "Terraform test instance group manager"
name = "%s"
instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}"
base_instance_name = "igm-rolling-update-policy"
zone = "us-central1-c"
target_size = 3
update_strategy = "ROLLING_UPDATE"
rolling_update_policy {
type = "PROACTIVE"
minimal_action = "REPLACE"
max_surge_fixed = 2
max_unavailable_fixed = 2
min_ready_sec = 20
}
named_port {
name = "customhttp"
port = 8080
}
}`, igm)
}
func testAccInstanceGroupManager_separateRegions(igm1, igm2 string) string {
return fmt.Sprintf(`
resource "google_compute_instance_template" "igm-basic" {

View File

@ -90,8 +90,8 @@ The following arguments are supported:
* `update_strategy` - (Optional, Default `"RESTART"`) If the `instance_template`
resource is modified, a value of `"NONE"` will prevent any of the managed
instances from being restarted by Terraform. A value of `"RESTART"` will
restart all of the instances at once. In the future, as the GCE API matures
we will support `"ROLLING_UPDATE"` as well.
restart all of the instances at once. `"ROLLING_UPDATE"` is supported as [Beta feature].
A value of `"ROLLING_UPDATE"` requires `rolling_update_policy` block to be set
* `target_size` - (Optional) The target number of running instances for this managed
instance group. This value should always be explicitly set unless this resource is attached to
@ -106,13 +106,43 @@ The following arguments are supported:
* `auto_healing_policies` - (Optional, [Beta](/docs/providers/google/index.html#beta-features)) The autohealing policies for this managed instance
group. You can specify only one value. Structure is documented below. For more information, see the [official documentation](https://cloud.google.com/compute/docs/instance-groups/creating-groups-of-managed-instances#monitoring_groups).
The `named_port` block supports: (Include a `named_port` block for each named-port required).
* `rolling_update_policy` - (Optional, [Beta](/docs/providers/google/index.html#beta-features)) The update policy for this managed instance group. Structure is documented below. For more information, see the [official documentation](https://cloud.google.com/compute/docs/instance-groups/updating-managed-instance-groups) and [API](https://cloud.google.com/compute/docs/reference/rest/beta/instanceGroupManagers/patch)
The **rolling_update_policy** block supports:
```hcl
rolling_update_policy{
type = "PROACTIVE"
minimal_action = "REPLACE"
max_surge_percent = 20
max_unavailable_fixed = 2
min_ready_sec = 50
}
```
* `minimal_action` - (Required) - Minimal action to be taken on an instance. Valid values are `"RESTART"`, `"REPLACE"`
* `type` - (Required) - The type of update. Valid values are `"OPPORTUNISTIC"`, `"PROACTIVE"`
* `max_surge_fixed` - (Optional), The maximum number of instances that can be created above the specified targetSize during the update process. Conflicts with `max_surge_percent`. If neither is set, defaults to 1
* `max_surge_percent` - (Optional), The maximum number of instances(calculated as percentage) that can be created above the specified targetSize during the update process. Conflicts with `max_surge_fixed`.
* `max_unavailable_fixed` - (Optional), The maximum number of instances that can be unavailable during the update process. Conflicts with `max_unavailable_percent`. If neither is set, defaults to 1
* `max_unavailable_percent` - (Optional), The maximum number of instances(calculated as percentage) that can be unavailable during the update process. Conflicts with `max_unavailable_fixed`.
* `min_ready_sec` - (Optional), Minimum number of seconds to wait for after a newly created instance becomes available. This value must be from range [0, 3600]
- - -
The **named_port** block supports: (Include a `named_port` block for each named-port required).
* `name` - (Required) The name of the port.
* `port` - (Required) The port number.
- - -
The `auto_healing_policies` block supports:
The **auto_healing_policies** block supports:
* `health_check` - (Required) The health check resource that signals autohealing.