Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rolling update support for instance group manager #1137

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 125 additions & 2 deletions google/resource_compute_instance_group_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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),
},
},
},
},
},
}
}
Expand Down Expand Up @@ -184,6 +241,12 @@ func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta inte
return err
}

_, ok := d.GetOk("rolling_update_policy")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Seems like it'd be neater as part of the if block below.


if 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),
Expand Down Expand Up @@ -380,6 +443,11 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte

d.Partial(true)

_, ok := d.GetOk("rolling_update_policy")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment - include in the if statement if possible.

if 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))
Expand Down Expand Up @@ -536,6 +604,23 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
}
}

if d.Get("update_strategy").(string) == "ROLLING_UPDATE" {
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")
}

Expand Down Expand Up @@ -732,6 +817,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 {
Expand Down
193 changes: 193 additions & 0 deletions google/resource_compute_instance_group_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package google
import (
"fmt"
"reflect"
"strconv"
"strings"
"testing"

Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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" {
Expand Down Expand Up @@ -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" {
Expand Down
Loading