From 5fdc199a8bf405b46bfdc7b6abfebacb83b2735e Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Wed, 23 Mar 2016 20:11:27 -0700 Subject: [PATCH 1/8] Consolidate auto scaling group capacity wait logic. --- .../aws/resource_aws_autoscaling_group.go | 9 +- .../resource_aws_autoscaling_group_waiting.go | 52 +++------ ...urce_aws_autoscaling_group_waiting_test.go | 107 +----------------- 3 files changed, 25 insertions(+), 143 deletions(-) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group.go b/builtin/providers/aws/resource_aws_autoscaling_group.go index fc47756c794b..5fc8568a00a3 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group.go @@ -235,7 +235,7 @@ func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) d.SetId(d.Get("name").(string)) log.Printf("[INFO] AutoScaling Group ID: %s", d.Id()) - if err := waitForASGCapacity(d, meta, capacitySatifiedCreate); err != nil { + if err := waitForASGCapacity(d, meta); err != nil { return err } @@ -298,7 +298,6 @@ func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) e func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).autoscalingconn - shouldWaitForCapacity := false opts := autoscaling.UpdateAutoScalingGroupInput{ AutoScalingGroupName: aws.String(d.Id()), @@ -310,7 +309,6 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) if d.HasChange("desired_capacity") { opts.DesiredCapacity = aws.Int64(int64(d.Get("desired_capacity").(int))) - shouldWaitForCapacity = true } if d.HasChange("launch_configuration") { @@ -319,7 +317,6 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) if d.HasChange("min_size") { opts.MinSize = aws.Int64(int64(d.Get("min_size").(int))) - shouldWaitForCapacity = true } if d.HasChange("max_size") { @@ -409,8 +406,8 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) } } - if shouldWaitForCapacity { - waitForASGCapacity(d, meta, capacitySatifiedUpdate) + if err := waitForASGCapacity(d, meta); err != nil { + return err } if d.HasChange("enabled_metrics") { diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go b/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go index bfafa9b3364f..bc06f846bce3 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go @@ -16,10 +16,7 @@ import ( // the capacitySatisfiedFunc returns true. // // See "Waiting for Capacity" in docs for more discussion of the feature. -func waitForASGCapacity( - d *schema.ResourceData, - meta interface{}, - satisfiedFunc capacitySatisfiedFunc) error { +func waitForASGCapacity(d *schema.ResourceData, meta interface{}) error { wait, err := time.ParseDuration(d.Get("wait_for_capacity_timeout").(string)) if err != nil { return err @@ -77,7 +74,7 @@ func waitForASGCapacity( } } - satisfied, reason := satisfiedFunc(d, haveASG, haveELB) + satisfied, reason := checkCapacitySatisfied(d, haveASG, haveELB) log.Printf("[DEBUG] %q Capacity: %d ASG, %d ELB, satisfied: %t, reason: %q", d.Id(), haveASG, haveELB, satisfied, reason) @@ -91,42 +88,27 @@ func waitForASGCapacity( }) } -type capacitySatisfiedFunc func(*schema.ResourceData, int, int) (bool, string) - -// capacitySatifiedCreate treats all targets as minimums -func capacitySatifiedCreate(d *schema.ResourceData, haveASG, haveELB int) (bool, string) { - minASG := d.Get("min_size").(int) - if wantASG := d.Get("desired_capacity").(int); wantASG > 0 { - minASG = wantASG - } - if haveASG < minASG { +// checkCapacitySatisfied determines if required ASG and ELB targets are met. +func checkCapacitySatisfied(d *schema.ResourceData, haveASG, haveELB int) (bool, string) { + if desiredASG, ok := d.GetOk("desired_capacity"); ok && desiredASG.(int) != haveASG { return false, fmt.Sprintf( - "Need at least %d healthy instances in ASG, have %d", minASG, haveASG) - } - minELB := d.Get("min_elb_capacity").(int) - if wantELB := d.Get("wait_for_elb_capacity").(int); wantELB > 0 { - minELB = wantELB + "Need exactly %d healthy instances in ASG, have %d", desiredASG.(int), haveASG) } - if haveELB < minELB { + + if minASG, ok := d.GetOk("min_size"); ok && minASG.(int) > haveASG { return false, fmt.Sprintf( - "Need at least %d healthy instances in ELB, have %d", minELB, haveELB) + "Need at least %d healthy instances in ASG, have %d", minASG.(int), haveASG) } - return true, "" -} -// capacitySatifiedUpdate only cares about specific targets -func capacitySatifiedUpdate(d *schema.ResourceData, haveASG, haveELB int) (bool, string) { - if wantASG := d.Get("desired_capacity").(int); wantASG > 0 { - if haveASG != wantASG { - return false, fmt.Sprintf( - "Need exactly %d healthy instances in ASG, have %d", wantASG, haveASG) - } + if desiredELB, ok := d.GetOk("wait_for_elb_capacity"); ok && desiredELB.(int) != haveELB { + return false, fmt.Sprintf( + "Need exactly %d healthy instances in ELB, have %d", desiredELB.(int), haveELB) } - if wantELB := d.Get("wait_for_elb_capacity").(int); wantELB > 0 { - if haveELB != wantELB { - return false, fmt.Sprintf( - "Need exactly %d healthy instances in ELB, have %d", wantELB, haveELB) - } + + if minELB, ok := d.GetOk("min_elb_capacity"); ok && minELB.(int) > haveELB { + return false, fmt.Sprintf( + "Need at least %d healthy instances in ELB, have %d", minELB.(int), haveELB) } + return true, "" } diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go b/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go index d7c3895c0e17..67b21cd8f88e 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go @@ -2,7 +2,7 @@ package aws import "testing" -func TestCapacitySatisfiedCreate(t *testing.T) { +func TestCapacitySatisfied(t *testing.T) { cases := map[string]struct { Data map[string]interface{} HaveASG int @@ -38,7 +38,7 @@ func TestCapacitySatisfiedCreate(t *testing.T) { }, HaveASG: 2, ExpectSatisfied: false, - ExpectReason: "Need at least 5 healthy instances in ASG, have 2", + ExpectReason: "Need exactly 5 healthy instances in ASG, have 2", }, "desired_capacity overrides min_size": { Data: map[string]interface{}{ @@ -47,7 +47,7 @@ func TestCapacitySatisfiedCreate(t *testing.T) { }, HaveASG: 2, ExpectSatisfied: false, - ExpectReason: "Need at least 5 healthy instances in ASG, have 2", + ExpectReason: "Need exactly 5 healthy instances in ASG, have 2", }, "desired_capacity, got it": { Data: map[string]interface{}{ @@ -56,14 +56,6 @@ func TestCapacitySatisfiedCreate(t *testing.T) { HaveASG: 5, ExpectSatisfied: true, }, - "desired_capacity, have more": { - Data: map[string]interface{}{ - "desired_capacity": 5, - }, - HaveASG: 10, - ExpectSatisfied: true, - }, - "min_elb_capacity, have less": { Data: map[string]interface{}{ "min_elb_capacity": 5, @@ -92,7 +84,7 @@ func TestCapacitySatisfiedCreate(t *testing.T) { }, HaveELB: 2, ExpectSatisfied: false, - ExpectReason: "Need at least 5 healthy instances in ELB, have 2", + ExpectReason: "Need exactly 5 healthy instances in ELB, have 2", }, "wait_for_elb_capacity, got it": { Data: map[string]interface{}{ @@ -101,13 +93,6 @@ func TestCapacitySatisfiedCreate(t *testing.T) { HaveELB: 5, ExpectSatisfied: true, }, - "wait_for_elb_capacity, have more": { - Data: map[string]interface{}{ - "wait_for_elb_capacity": 5, - }, - HaveELB: 10, - ExpectSatisfied: true, - }, "wait_for_elb_capacity overrides min_elb_capacity": { Data: map[string]interface{}{ "min_elb_capacity": 2, @@ -115,90 +100,8 @@ func TestCapacitySatisfiedCreate(t *testing.T) { }, HaveELB: 2, ExpectSatisfied: false, - ExpectReason: "Need at least 5 healthy instances in ELB, have 2", - }, - } - - r := resourceAwsAutoscalingGroup() - for tn, tc := range cases { - d := r.TestResourceData() - for k, v := range tc.Data { - if err := d.Set(k, v); err != nil { - t.Fatalf("err: %s", err) - } - } - gotSatisfied, gotReason := capacitySatifiedCreate(d, tc.HaveASG, tc.HaveELB) - - if gotSatisfied != tc.ExpectSatisfied { - t.Fatalf("%s: expected satisfied: %t, got: %t (reason: %s)", - tn, tc.ExpectSatisfied, gotSatisfied, gotReason) - } - - if gotReason != tc.ExpectReason { - t.Fatalf("%s: expected reason: %s, got: %s", - tn, tc.ExpectReason, gotReason) - } - } -} - -func TestCapacitySatisfiedUpdate(t *testing.T) { - cases := map[string]struct { - Data map[string]interface{} - HaveASG int - HaveELB int - ExpectSatisfied bool - ExpectReason string - }{ - "default is satisfied": { - Data: map[string]interface{}{}, - ExpectSatisfied: true, - }, - "desired_capacity, have less": { - Data: map[string]interface{}{ - "desired_capacity": 5, - }, - HaveASG: 2, - ExpectSatisfied: false, - ExpectReason: "Need exactly 5 healthy instances in ASG, have 2", - }, - "desired_capacity, got it": { - Data: map[string]interface{}{ - "desired_capacity": 5, - }, - HaveASG: 5, - ExpectSatisfied: true, - }, - "desired_capacity, have more": { - Data: map[string]interface{}{ - "desired_capacity": 5, - }, - HaveASG: 10, - ExpectSatisfied: false, - ExpectReason: "Need exactly 5 healthy instances in ASG, have 10", - }, - "wait_for_elb_capacity, have less": { - Data: map[string]interface{}{ - "wait_for_elb_capacity": 5, - }, - HaveELB: 2, - ExpectSatisfied: false, ExpectReason: "Need exactly 5 healthy instances in ELB, have 2", }, - "wait_for_elb_capacity, got it": { - Data: map[string]interface{}{ - "wait_for_elb_capacity": 5, - }, - HaveELB: 5, - ExpectSatisfied: true, - }, - "wait_for_elb_capacity, have more": { - Data: map[string]interface{}{ - "wait_for_elb_capacity": 5, - }, - HaveELB: 10, - ExpectSatisfied: false, - ExpectReason: "Need exactly 5 healthy instances in ELB, have 10", - }, } r := resourceAwsAutoscalingGroup() @@ -209,7 +112,7 @@ func TestCapacitySatisfiedUpdate(t *testing.T) { t.Fatalf("err: %s", err) } } - gotSatisfied, gotReason := capacitySatifiedUpdate(d, tc.HaveASG, tc.HaveELB) + gotSatisfied, gotReason := checkCapacitySatisfied(d, tc.HaveASG, tc.HaveELB) if gotSatisfied != tc.ExpectSatisfied { t.Fatalf("%s: expected satisfied: %t, got: %t (reason: %s)", From 2ee2b8aca17aa977c4d87366ebe51fd3610d8780 Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Wed, 23 Mar 2016 21:09:05 -0700 Subject: [PATCH 2/8] Wait for auto scaling group capacity to terminate. --- .../resource_aws_autoscaling_group_waiting.go | 5 ++++ ...urce_aws_autoscaling_group_waiting_test.go | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go b/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go index bc06f846bce3..6c6e6384b951 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go @@ -100,6 +100,11 @@ func checkCapacitySatisfied(d *schema.ResourceData, haveASG, haveELB int) (bool, "Need at least %d healthy instances in ASG, have %d", minASG.(int), haveASG) } + if maxASG, ok := d.GetOk("max_size"); ok && maxASG.(int) < haveASG { + return false, fmt.Sprintf( + "Need at most %d healthy instances in ASG, have %d", maxASG.(int), haveASG) + } + if desiredELB, ok := d.GetOk("wait_for_elb_capacity"); ok && desiredELB.(int) != haveELB { return false, fmt.Sprintf( "Need exactly %d healthy instances in ELB, have %d", desiredELB.(int), haveELB) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go b/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go index 67b21cd8f88e..ce6c6af863b0 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go @@ -56,6 +56,36 @@ func TestCapacitySatisfied(t *testing.T) { HaveASG: 5, ExpectSatisfied: true, }, + "max_size, have less": { + Data: map[string]interface{}{ + "max_size": 5, + }, + HaveASG: 2, + ExpectSatisfied: true, + }, + "max_size, have more": { + Data: map[string]interface{}{ + "max_size": 5, + }, + HaveASG: 10, + ExpectSatisfied: false, + ExpectReason: "Need at most 5 healthy instances in ASG, have 10", + }, + "max_size, got it": { + Data: map[string]interface{}{ + "max_size": 5, + }, + HaveASG: 5, + ExpectSatisfied: true, + }, + "min_size, max_size, between": { + Data: map[string]interface{}{ + "min_size": 1, + "max_size": 5, + }, + HaveASG: 2, + ExpectSatisfied: true, + }, "min_elb_capacity, have less": { Data: map[string]interface{}{ "min_elb_capacity": 5, From e289a110604851a2aefe5e96d18c576e1cc58c68 Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Thu, 11 Feb 2016 01:39:56 -0800 Subject: [PATCH 3/8] Support AWS auto scaling group suspended policies. --- .../aws/resource_aws_autoscaling_group.go | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group.go b/builtin/providers/aws/resource_aws_autoscaling_group.go index 5fc8568a00a3..49d81091f922 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group.go @@ -109,6 +109,13 @@ func resourceAwsAutoscalingGroup() *schema.Resource { Set: schema.HashString, }, + "suspended_processes": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "vpc_zone_identifier": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -232,7 +239,12 @@ func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error creating Autoscaling Group: %s", err) } + if err := updateSuspendedProcesses(d, meta); err != nil { + return err + } + d.SetId(d.Get("name").(string)) + log.Printf("[INFO] AutoScaling Group ID: %s", d.Id()) if err := waitForASGCapacity(d, meta); err != nil { @@ -273,6 +285,7 @@ func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) e d.Set("max_size", g.MaxSize) d.Set("placement_group", g.PlacementGroup) d.Set("name", g.AutoScalingGroupName) + d.Set("suspended_processes", flattenSuspendedProcesses(g.SuspendedProcesses)) d.Set("tag", g.Tags) d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ",")) @@ -357,6 +370,10 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) } } + if err := updateSuspendedProcesses(d, meta); err != nil { + return err + } + if err := setAutoscalingTags(conn, d); err != nil { return err } else { @@ -640,3 +657,46 @@ func expandVpcZoneIdentifiers(list []interface{}) *string { } return aws.String(strings.Join(strs, ",")) } + +func flattenSuspendedProcesses(procs []*autoscaling.SuspendedProcess) []string { + names := make([]string, len(procs)) + for i, p := range procs { + names[i] = *p.ProcessName + } + return names +} + +func updateSuspendedProcesses(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).autoscalingconn + + if d.HasChange("suspended_processes") { + old, new := d.GetChange("suspended_processes") + oldSet := old.(*schema.Set) + newSet := new.(*schema.Set) + + added := newSet.Difference(oldSet).List() + if len(added) > 0 { + _, err := conn.SuspendProcesses(&autoscaling.ScalingProcessQuery{ + AutoScalingGroupName: aws.String(d.Get("name").(string)), + ScalingProcesses: expandStringList(added), + }) + if err != nil { + return fmt.Errorf("Failure suspending scaling processes: %s", err) + } + } + + removed := oldSet.Difference(newSet).List() + if len(removed) > 0 { + _, err := conn.ResumeProcesses(&autoscaling.ScalingProcessQuery{ + AutoScalingGroupName: aws.String(d.Get("name").(string)), + ScalingProcesses: expandStringList(removed), + }) + if err != nil { + return fmt.Errorf("Failure resuming scaling processes: %s", err) + } + } + d.SetPartial("suspended_processes") + } + + return nil +} From 145ba78fda5f0980f69467f08c195d8a814482c8 Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Thu, 11 Feb 2016 23:19:43 -0800 Subject: [PATCH 4/8] Add suspended processes acceptance test. --- .../resource_aws_autoscaling_group_test.go | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_test.go b/builtin/providers/aws/resource_aws_autoscaling_group_test.go index ed4d559badb5..8a879760736c 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_test.go @@ -136,6 +136,41 @@ func TestAccAWSAutoScalingGroup_terminationPolicies(t *testing.T) { }) } +func TestAccAWSAutoScalingGroup_suspendedProcceses(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAutoScalingGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSAutoScalingGroupConfig_suspendedProcessesNone, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "aws_autoscaling_group.bar", "suspended_processes.#", "0"), + ), + }, + resource.TestStep{ + Config: testAccAWSAutoScalingGroupConfig_suspendedProcessesUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "aws_autoscaling_group.bar", "suspended_processes.#", "2"), + resource.TestCheckResourceAttr( + "aws_autoscaling_group.bar", "suspended_processes.997436260", "HealthCheck"), + resource.TestCheckResourceAttr( + "aws_autoscaling_group.bar", "suspended_processes.2282213524", "ScheduledActions"), + ), + }, + resource.TestStep{ + Config: testAccAWSAutoScalingGroupConfig_suspendedProcessesNone, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "aws_autoscaling_group.bar", "suspended_processes.#", "0"), + ), + }, + }, + }) +} + func TestAccAWSAutoScalingGroup_tags(t *testing.T) { var group autoscaling.Group @@ -539,6 +574,41 @@ resource "aws_autoscaling_group" "bar" { } ` +const testAccAWSAutoScalingGroupConfig_suspendedProcessesNone = ` +resource "aws_launch_configuration" "foobar" { + image_id = "ami-21f78e11" + instance_type = "t1.micro" +} + +resource "aws_autoscaling_group" "bar" { + availability_zones = ["us-west-2a"] + desired_capacity = 0 + max_size = 0 + min_size = 0 + launch_configuration = "${aws_launch_configuration.foobar.name}" +} +` + +const testAccAWSAutoScalingGroupConfig_suspendedProcessesUpdate = ` +resource "aws_launch_configuration" "foobar" { + image_id = "ami-21f78e11" + instance_type = "t1.micro" +} + +resource "aws_autoscaling_group" "bar" { + availability_zones = ["us-west-2a"] + desired_capacity = 0 + max_size = 0 + min_size = 0 + launch_configuration = "${aws_launch_configuration.foobar.name}" + + suspended_processes = [ + "HealthCheck", + "ScheduledActions", + ] +} +` + const testAccAWSAutoScalingGroupConfig = ` resource "aws_launch_configuration" "foobar" { image_id = "ami-21f78e11" From fcd102e4b15358c63c322149d4dd6b2c1c96a7bd Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Thu, 11 Feb 2016 23:28:37 -0800 Subject: [PATCH 5/8] Add suspended processes website docs. --- .../source/docs/providers/aws/r/autoscaling_group.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/source/docs/providers/aws/r/autoscaling_group.html.markdown b/website/source/docs/providers/aws/r/autoscaling_group.html.markdown index 233fb6cfe475..71a1d047033e 100644 --- a/website/source/docs/providers/aws/r/autoscaling_group.html.markdown +++ b/website/source/docs/providers/aws/r/autoscaling_group.html.markdown @@ -66,6 +66,8 @@ The following arguments are supported: behavior and potentially leaves resources dangling. * `load_balancers` (Optional) A list of load balancer names to add to the autoscaling group names. +* `suspended_processes` (Optional) A list of auto scaling processes to suspend. +(See [Suspending and Resuming Processes](http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/US_SuspendResume.html).) * `vpc_zone_identifier` (Optional) A list of subnet IDs to launch resources in. * `termination_policies` (Optional) A list of policies to decide how the instances in the auto scale group should be terminated. * `tag` (Optional) A list of tag blocks. Tags documented below. From f52c623c93c5ebe03e6992f54ea3ee00cbb33abc Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Thu, 24 Mar 2016 15:19:44 -0700 Subject: [PATCH 6/8] Do not wait when launch or terminate is suspended. --- .../resource_aws_autoscaling_group_waiting.go | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go b/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go index 6c6e6384b951..29f73b6b0f20 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go @@ -90,27 +90,50 @@ func waitForASGCapacity(d *schema.ResourceData, meta interface{}) error { // checkCapacitySatisfied determines if required ASG and ELB targets are met. func checkCapacitySatisfied(d *schema.ResourceData, haveASG, haveELB int) (bool, string) { - if desiredASG, ok := d.GetOk("desired_capacity"); ok && desiredASG.(int) != haveASG { + isLaunchSuspended := false + isTerminateSuspended := false + if procs, ok := d.GetOk("suspended_processes"); ok { + for _, p := range procs.(*schema.Set).List() { + switch p.(string) { + case "Launch": + isLaunchSuspended = true + case "Terminate": + isTerminateSuspended = true + } + } + } + + if desiredASG, ok := d.GetOk("desired_capacity"); ok && desiredASG.(int) > haveASG && !isLaunchSuspended { + return false, fmt.Sprintf( + "Need exactly %d healthy instances in ASG, have %d", desiredASG.(int), haveASG) + } + + if desiredASG, ok := d.GetOk("desired_capacity"); ok && desiredASG.(int) < haveASG && !isTerminateSuspended { return false, fmt.Sprintf( "Need exactly %d healthy instances in ASG, have %d", desiredASG.(int), haveASG) } - if minASG, ok := d.GetOk("min_size"); ok && minASG.(int) > haveASG { + if minASG, ok := d.GetOk("min_size"); ok && minASG.(int) > haveASG && !isLaunchSuspended { return false, fmt.Sprintf( "Need at least %d healthy instances in ASG, have %d", minASG.(int), haveASG) } - if maxASG, ok := d.GetOk("max_size"); ok && maxASG.(int) < haveASG { + if maxASG, ok := d.GetOk("max_size"); ok && maxASG.(int) < haveASG && !isTerminateSuspended { return false, fmt.Sprintf( "Need at most %d healthy instances in ASG, have %d", maxASG.(int), haveASG) } - if desiredELB, ok := d.GetOk("wait_for_elb_capacity"); ok && desiredELB.(int) != haveELB { + if desiredELB, ok := d.GetOk("wait_for_elb_capacity"); ok && desiredELB.(int) > haveELB && !isLaunchSuspended { + return false, fmt.Sprintf( + "Need exactly %d healthy instances in ELB, have %d", desiredELB.(int), haveELB) + } + + if desiredELB, ok := d.GetOk("wait_for_elb_capacity"); ok && desiredELB.(int) < haveELB && !isTerminateSuspended { return false, fmt.Sprintf( "Need exactly %d healthy instances in ELB, have %d", desiredELB.(int), haveELB) } - if minELB, ok := d.GetOk("min_elb_capacity"); ok && minELB.(int) > haveELB { + if minELB, ok := d.GetOk("min_elb_capacity"); ok && minELB.(int) > haveELB && !isLaunchSuspended { return false, fmt.Sprintf( "Need at least %d healthy instances in ELB, have %d", minELB.(int), haveELB) } From d7f7435091a3d03a13af3b854f97097540f9a1eb Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Fri, 25 Mar 2016 10:51:55 -0700 Subject: [PATCH 7/8] Add wait for capacity with suspended processes tests. --- ...urce_aws_autoscaling_group_waiting_test.go | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go b/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go index ce6c6af863b0..2098c11a5c57 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_waiting_test.go @@ -2,6 +2,8 @@ package aws import "testing" +import "github.com/hashicorp/terraform/helper/schema" + func TestCapacitySatisfied(t *testing.T) { cases := map[string]struct { Data map[string]interface{} @@ -18,6 +20,14 @@ func TestCapacitySatisfied(t *testing.T) { ExpectSatisfied: false, ExpectReason: "Need at least 5 healthy instances in ASG, have 2", }, + "min_size, have less, launch suspended": { + Data: map[string]interface{}{ + "min_size": 5, + "suspended_processes": schema.NewSet(schema.HashString, []interface{}{"Launch"}), + }, + HaveASG: 2, + ExpectSatisfied: true, + }, "min_size, got it": { Data: map[string]interface{}{ "min_size": 5, @@ -40,6 +50,23 @@ func TestCapacitySatisfied(t *testing.T) { ExpectSatisfied: false, ExpectReason: "Need exactly 5 healthy instances in ASG, have 2", }, + "desired_capacity, have less, launch suspended": { + Data: map[string]interface{}{ + "desired_capacity": 5, + "suspended_processes": schema.NewSet(schema.HashString, []interface{}{"Launch"}), + }, + HaveASG: 2, + ExpectSatisfied: true, + }, + "desired_capacity, have less, terminate suspended": { + Data: map[string]interface{}{ + "desired_capacity": 5, + "suspended_processes": schema.NewSet(schema.HashString, []interface{}{"Terminate"}), + }, + HaveASG: 2, + ExpectSatisfied: false, + ExpectReason: "Need exactly 5 healthy instances in ASG, have 2", + }, "desired_capacity overrides min_size": { Data: map[string]interface{}{ "min_size": 2, @@ -71,6 +98,14 @@ func TestCapacitySatisfied(t *testing.T) { ExpectSatisfied: false, ExpectReason: "Need at most 5 healthy instances in ASG, have 10", }, + "max_size, have more, terminate suspended": { + Data: map[string]interface{}{ + "max_size": 5, + "suspended_processes": schema.NewSet(schema.HashString, []interface{}{"Terminate"}), + }, + HaveASG: 10, + ExpectSatisfied: true, + }, "max_size, got it": { Data: map[string]interface{}{ "max_size": 5, @@ -94,6 +129,14 @@ func TestCapacitySatisfied(t *testing.T) { ExpectSatisfied: false, ExpectReason: "Need at least 5 healthy instances in ELB, have 2", }, + "min_elb_capacity, have less, launch suspended": { + Data: map[string]interface{}{ + "min_elb_capacity": 5, + "suspended_processes": schema.NewSet(schema.HashString, []interface{}{"Launch"}), + }, + HaveELB: 2, + ExpectSatisfied: true, + }, "min_elb_capacity, got it": { Data: map[string]interface{}{ "min_elb_capacity": 5, @@ -116,6 +159,22 @@ func TestCapacitySatisfied(t *testing.T) { ExpectSatisfied: false, ExpectReason: "Need exactly 5 healthy instances in ELB, have 2", }, + "wait_for_elb_capacity, have less, launch suspended": { + Data: map[string]interface{}{ + "wait_for_elb_capacity": 5, + "suspended_processes": schema.NewSet(schema.HashString, []interface{}{"Launch"}), + }, + HaveELB: 2, + ExpectSatisfied: true, + }, + "wait_for_elb_capacity, have more, terminate suspended": { + Data: map[string]interface{}{ + "wait_for_elb_capacity": 5, + "suspended_processes": schema.NewSet(schema.HashString, []interface{}{"Terminate"}), + }, + HaveELB: 10, + ExpectSatisfied: true, + }, "wait_for_elb_capacity, got it": { Data: map[string]interface{}{ "wait_for_elb_capacity": 5, From be3987276edbf4006e1b11dd38a5942d908f354d Mon Sep 17 00:00:00 2001 From: Trevor Pounds Date: Sun, 27 Mar 2016 14:10:26 -0700 Subject: [PATCH 8/8] Fix golint warning by outdenting unnecessary else. --- builtin/providers/aws/resource_aws_autoscaling_group.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group.go b/builtin/providers/aws/resource_aws_autoscaling_group.go index 49d81091f922..11258b36f9a8 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group.go @@ -376,10 +376,10 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) if err := setAutoscalingTags(conn, d); err != nil { return err - } else { - d.SetPartial("tag") } + d.SetPartial("tag") + log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts) _, err := conn.UpdateAutoScalingGroup(&opts) if err != nil {