provider/aws: wait for ASG capacity on creation
On ASG creation, waits for up to 10m for desired_capacity or min_size healthy nodes to show up in the group before continuing. With CBD and proper HealthCheck tuning, this allows us guarantee safe ASG replacement.
This commit is contained in:
parent
e7ca6cbe9e
commit
063454e9b8
|
@ -174,6 +174,10 @@ func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{})
|
||||||
d.SetId(d.Get("name").(string))
|
d.SetId(d.Get("name").(string))
|
||||||
log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())
|
log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())
|
||||||
|
|
||||||
|
if err := waitForASGCapacity(d, meta); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return resourceAwsAutoscalingGroupRead(d, meta)
|
return resourceAwsAutoscalingGroupRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,3 +363,45 @@ func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{})
|
||||||
return fmt.Errorf("group still has %d instances", len(g.Instances))
|
return fmt.Errorf("group still has %d instances", len(g.Instances))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var waitForASGCapacityTimeout = 10 * time.Minute
|
||||||
|
|
||||||
|
// Waits for a minimum number of healthy instances to show up as healthy in the
|
||||||
|
// ASG before continuing. Waits up to `waitForASGCapacityTimeout` for
|
||||||
|
// "desired_capacity", or "min_size" if desired capacity is not specified.
|
||||||
|
func waitForASGCapacity(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
waitFor := d.Get("min_size").(int)
|
||||||
|
if v := d.Get("desired_capacity").(int); v > 0 {
|
||||||
|
waitFor = v
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for group to have %d healthy instances", waitFor)
|
||||||
|
return resource.Retry(waitForASGCapacityTimeout, func() error {
|
||||||
|
g, err := getAwsAutoscalingGroup(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return resource.RetryError{Err: err}
|
||||||
|
}
|
||||||
|
if g == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
healthy := 0
|
||||||
|
for _, i := range g.Instances {
|
||||||
|
if i.HealthStatus == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.EqualFold(*i.HealthStatus, "Healthy") {
|
||||||
|
healthy++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(
|
||||||
|
"[DEBUG] %q has %d/%d healthy instances", d.Id(), healthy, waitFor)
|
||||||
|
|
||||||
|
if healthy >= waitFor {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Waiting for healthy instances: %d/%d", healthy, waitFor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package aws
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/awslabs/aws-sdk-go/aws"
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
@ -24,6 +25,7 @@ func TestAccAWSAutoScalingGroup_basic(t *testing.T) {
|
||||||
Config: testAccAWSAutoScalingGroupConfig,
|
Config: testAccAWSAutoScalingGroupConfig,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group),
|
testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group),
|
||||||
|
testAccCheckAWSAutoScalingGroupHealthyCapacity(&group, 2),
|
||||||
testAccCheckAWSAutoScalingGroupAttributes(&group),
|
testAccCheckAWSAutoScalingGroupAttributes(&group),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"aws_autoscaling_group.bar", "availability_zones.2487133097", "us-west-2a"),
|
"aws_autoscaling_group.bar", "availability_zones.2487133097", "us-west-2a"),
|
||||||
|
@ -116,6 +118,7 @@ func TestAccAWSAutoScalingGroup_WithLoadBalancer(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccCheckAWSAutoScalingGroupDestroy(s *terraform.State) error {
|
func testAccCheckAWSAutoScalingGroupDestroy(s *terraform.State) error {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).autoscalingconn
|
conn := testAccProvider.Meta().(*AWSClient).autoscalingconn
|
||||||
|
|
||||||
|
@ -261,6 +264,25 @@ func testLaunchConfigurationName(n string, lc *autoscaling.LaunchConfiguration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSAutoScalingGroupHealthyCapacity(
|
||||||
|
g *autoscaling.AutoScalingGroup, exp int) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
healthy := 0
|
||||||
|
for _, i := range g.Instances {
|
||||||
|
if i.HealthStatus == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.EqualFold(*i.HealthStatus, "Healthy") {
|
||||||
|
healthy++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if healthy < exp {
|
||||||
|
return fmt.Errorf("Expected at least %d healthy, got %d.", exp, healthy)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const testAccAWSAutoScalingGroupConfig = `
|
const testAccAWSAutoScalingGroupConfig = `
|
||||||
resource "aws_launch_configuration" "foobar" {
|
resource "aws_launch_configuration" "foobar" {
|
||||||
name = "foobarautoscaling-terraform-test"
|
name = "foobarautoscaling-terraform-test"
|
||||||
|
|
|
@ -43,12 +43,19 @@ The following arguments are supported:
|
||||||
|
|
||||||
* `name` - (Required) The name of the auto scale group.
|
* `name` - (Required) The name of the auto scale group.
|
||||||
* `max_size` - (Required) The maximum size of the auto scale group.
|
* `max_size` - (Required) The maximum size of the auto scale group.
|
||||||
* `min_size` - (Required) The minimum size of the auto scale group.
|
* `min_size` - (Required) The minimum size of the auto scale group. Terraform
|
||||||
|
waits after ASG creation for this number of healthy instances to show up in
|
||||||
|
the ASG before continuing. Currently, it will wait for a maxiumum of 10m, if
|
||||||
|
ASG creation is taking more than a few minutes, it's worth investigating for
|
||||||
|
scaling actvity errors caused by problems with the selected Launch
|
||||||
|
Configuration.
|
||||||
* `availability_zones` - (Required) A list of AZs to launch resources in.
|
* `availability_zones` - (Required) A list of AZs to launch resources in.
|
||||||
* `launch_configuration` - (Required) The ID of the launch configuration to use.
|
* `launch_configuration` - (Required) The ID of the launch configuration to use.
|
||||||
* `health_check_grace_period` - (Optional) Time after instance comes into service before checking health.
|
* `health_check_grace_period` - (Optional) Time after instance comes into service before checking health.
|
||||||
* `health_check_type` - (Optional) "EC2" or "ELB". Controls how health checking is done.
|
* `health_check_type` - (Optional) "EC2" or "ELB". Controls how health checking is done.
|
||||||
* `desired_capacity` - (Optional) The number of Amazon EC2 instances that should be running in the group.
|
* `desired_capacity` - (Optional) The number of Amazon EC2 instances that
|
||||||
|
should be running in the group. (If this is specified, Terraform will wait for
|
||||||
|
this number of healthy instances after ASG creation instead of `min_size`.)
|
||||||
* `force_delete` - (Optional) Allows deleting the autoscaling group without waiting
|
* `force_delete` - (Optional) Allows deleting the autoscaling group without waiting
|
||||||
for all instances in the pool to terminate.
|
for all instances in the pool to terminate.
|
||||||
* `load_balancers` (Optional) A list of load balancer names to add to the autoscaling
|
* `load_balancers` (Optional) A list of load balancer names to add to the autoscaling
|
||||||
|
|
Loading…
Reference in New Issue