Skip to content

Commit

Permalink
provider/aws: wait for ASG capacity on update
Browse files Browse the repository at this point in the history
It's a bit confusing to have Terraform poll until instances come up on
ASG creation but not on update. This changes update to also poll if
min_size or desired_capacity are changed.

This changes the waiting behavior to wait for precisely the desired
number of instances instead of that number as a "minimum". I believe
this shouldn't have any undue side effects, and the behavior can still
be opted out of by setting `wait_for_capacity_timeout` to 0.
  • Loading branch information
phinze committed Nov 17, 2015
1 parent 24ee563 commit 7f93607
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 14 deletions.
30 changes: 24 additions & 6 deletions builtin/providers/aws/resource_aws_autoscaling_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
},

"min_elb_capacity": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Type: schema.TypeInt,
Optional: true,
Deprecated: "Please use 'wait_for_elb_capacity' instead.",
},

"min_size": &schema.Schema{
Expand Down Expand Up @@ -136,6 +137,11 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
},
},

"wait_for_elb_capacity": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},

"tag": autoscalingTagsSchema(),
},
}
Expand Down Expand Up @@ -242,6 +248,7 @@ 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()),
Expand All @@ -253,6 +260,7 @@ 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") {
Expand All @@ -261,6 +269,7 @@ 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") {
Expand Down Expand Up @@ -353,6 +362,10 @@ func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{})
}
}

if shouldWaitForCapacity {
waitForASGCapacity(d, meta)
}

return resourceAwsAutoscalingGroupRead(d, meta)
}

Expand Down Expand Up @@ -490,15 +503,18 @@ func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{})
// ASG before continuing. Waits up to `waitForASGCapacityTimeout` for
// "desired_capacity", or "min_size" if desired capacity is not specified.
//
// If "min_elb_capacity" is specified, will also wait for that number of
// If "wait_for_elb_capacity" is specified, will also wait for that number of
// instances to show up InService in all attached ELBs. See "Waiting for
// Capacity" in docs for more discussion of the feature.
func waitForASGCapacity(d *schema.ResourceData, meta interface{}) error {
wantASG := d.Get("min_size").(int)
if v := d.Get("desired_capacity").(int); v > 0 {
wantASG = v
}
wantELB := d.Get("min_elb_capacity").(int)
wantELB := d.Get("wait_for_elb_capacity").(int)

// Covers deprecated field support
wantELB += d.Get("min_elb_capacity").(int)

wait, err := time.ParseDuration(d.Get("wait_for_capacity_timeout").(string))
if err != nil {
Expand Down Expand Up @@ -561,11 +577,13 @@ func waitForASGCapacity(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] %q Capacity: %d/%d ASG, %d/%d ELB",
d.Id(), haveASG, wantASG, haveELB, wantELB)

if haveASG >= wantASG && haveELB >= wantELB {
if haveASG == wantASG && haveELB == wantELB {
return nil
}

return fmt.Errorf("Still need to wait for more healthy instances. This could mean instances failed to launch. See Scaling History for more information.")
return fmt.Errorf(
"Still waiting for %q instances. Current/Desired: %d/%d ASG, %d/%d ELB",
d.Id(), haveASG, wantASG, haveELB, wantELB)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ resource "aws_autoscaling_group" "bar" {
min_size = 2
health_check_grace_period = 300
health_check_type = "ELB"
min_elb_capacity = 2
wait_for_elb_capacity = 2
force_delete = true
launch_configuration = "${aws_launch_configuration.foobar.name}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ The following arguments are supported:
* `desired_capacity` - (Optional) The number of Amazon EC2 instances that
should be running in the group. (See also [Waiting for
Capacity](#waiting-for-capacity) below.)
* `min_elb_capacity` - (Optional) Setting this will cause Terraform to wait
for this number of healthy instances all attached load balancers.
(See also [Waiting for Capacity](#waiting-for-capacity) below.)
* `force_delete` - (Optional) Allows deleting the autoscaling group without waiting
for all instances in the pool to terminate. You can force an autoscaling group to delete
even if it's in the process of scaling a resource. Normally, Terraform
Expand All @@ -71,6 +68,9 @@ The following arguments are supported:
wait for ASG instances to be healthy before timing out. (See also [Waiting
for Capacity](#waiting-for-capacity) below.) Setting this to "0" causes
Terraform to skip all Capacity Waiting behavior.
* `wait_for_elb_capacity` - (Optional) Setting this will cause Terraform to wait
for this number of healthy instances all attached load balancers.
(See also [Waiting for Capacity](#waiting-for-capacity) below.)

Tags support the following:

Expand All @@ -79,6 +79,10 @@ Tags support the following:
* `propagate_at_launch` - (Required) Enables propagation of the tag to
Amazon EC2 instances launched via this ASG

The following fields are deprecated:

* `min_elb_capacity` - Please use `wait_for_elb_capacity` instead.

## Attributes Reference

The following attributes are exported:
Expand All @@ -96,7 +100,7 @@ The following attributes are exported:
* `vpc_zone_identifier` - The VPC zone identifier
* `load_balancers` (Optional) The load balancer names associated with the
autoscaling group.

~> **NOTE:** When using `ELB` as the health_check_type, `health_check_grace_period` is required.

<a id="waiting-for-capacity"></a>
Expand All @@ -115,6 +119,10 @@ The first is default behavior. Terraform waits after ASG creation for
`min_size` (or `desired_capacity`, if specified) healthy instances to show up
in the ASG before continuing.

If `min_size` or `desired_capacity` are changed in a subsequent update,
Terraform will also wait for the correct number of healthy instances before
continuing.

Terraform considers an instance "healthy" when the ASG reports `HealthStatus:
"Healthy"` and `LifecycleState: "InService"`. See the [AWS AutoScaling
Docs](https://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/AutoScalingGroupLifecycle.html)
Expand All @@ -130,9 +138,9 @@ Setting `wait_for_capacity_timeout` to `"0"` disables ASG Capacity waiting.
#### Waiting for ELB Capacity

The second mechanism is optional, and affects ASGs with attached Load
Balancers. If `min_elb_capacity` is set, Terraform will wait for that number of
Instances to be `"InService"` in all attached `load_balancers`. This can be
used to ensure that service is being provided before Terraform moves on.
Balancers. If `wait_for_elb_capacity` is set, Terraform will wait for that
number of Instances to be `"InService"` in all attached `load_balancers`. This
can be used to ensure that service is being provided before Terraform moves on.

As with ASG Capacity, Terraform will wait for up to `wait_for_capacity_timeout`
(for `"InService"` instances. If ASG creation takes more than a few minutes,
Expand Down

0 comments on commit 7f93607

Please sign in to comment.