diff --git a/aws/resource_aws_autoscaling_group.go b/aws/resource_aws_autoscaling_group.go index 7f0896291c7..df05d33a369 100644 --- a/aws/resource_aws_autoscaling_group.go +++ b/aws/resource_aws_autoscaling_group.go @@ -596,6 +596,9 @@ func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) return nil }) + if isResourceTimeoutError(err) { + _, err = conn.CreateAutoScalingGroup(&createOpts) + } if err != nil { return fmt.Errorf("Error creating AutoScaling Group: %s", err) } @@ -1005,23 +1008,38 @@ func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) // Successful delete return nil }) + if isResourceTimeoutError(err) { + _, err = conn.DeleteAutoScalingGroup(&deleteopts) + if isAWSErr(err, "InvalidGroup.NotFound", "") { + return nil + } + } if err != nil { - return err + return fmt.Errorf("Error deleting autoscaling group: %s", err) } - return resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { - if g, _ = getAwsAutoscalingGroup(d.Id(), conn); g != nil { - return resource.RetryableError( - fmt.Errorf("Auto Scaling Group still exists")) + var group *autoscaling.Group + err = resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { + group, err = getAwsAutoscalingGroup(d.Id(), conn) + + if group != nil { + return resource.RetryableError(fmt.Errorf("Auto Scaling Group still exists")) } return nil }) + if isResourceTimeoutError(err) { + group, err = getAwsAutoscalingGroup(d.Id(), conn) + if group != nil { + return fmt.Errorf("Auto Scaling Group still exists") + } + } + if err != nil { + return fmt.Errorf("Error deleting autoscaling group: %s", err) + } + return nil } -func getAwsAutoscalingGroup( - asgName string, - conn *autoscaling.AutoScaling) (*autoscaling.Group, error) { - +func getAwsAutoscalingGroup(asgName string, conn *autoscaling.AutoScaling) (*autoscaling.Group, error) { describeOpts := autoscaling.DescribeAutoScalingGroupsInput{ AutoScalingGroupNames: []*string{aws.String(asgName)}, } @@ -1069,7 +1087,8 @@ func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) // Next, wait for the autoscale group to drain log.Printf("[DEBUG] Waiting for group to have zero instances") - return resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { + var g *autoscaling.Group + err := resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { g, err := getAwsAutoscalingGroup(d.Id(), conn) if err != nil { return resource.NonRetryableError(err) @@ -1085,8 +1104,21 @@ func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) } return resource.RetryableError( - fmt.Errorf("group still has %d instances", len(g.Instances))) + fmt.Errorf("Group still has %d instances", len(g.Instances))) }) + if isResourceTimeoutError(err) { + g, err = getAwsAutoscalingGroup(d.Id(), conn) + if err != nil { + return fmt.Errorf("Error getting autoscaling group info when draining: %s", err) + } + if g != nil && len(g.Instances) > 0 { + return fmt.Errorf("Group still has %d instances", len(g.Instances)) + } + } + if err != nil { + return fmt.Errorf("Error draining autoscaling group: %s", err) + } + return nil } func enableASGSuspendedProcesses(d *schema.ResourceData, conn *autoscaling.AutoScaling) error { diff --git a/aws/resource_aws_autoscaling_group_waiting.go b/aws/resource_aws_autoscaling_group_waiting.go index 404d845feec..748f58c4761 100644 --- a/aws/resource_aws_autoscaling_group_waiting.go +++ b/aws/resource_aws_autoscaling_group_waiting.go @@ -44,63 +44,32 @@ func waitForASGCapacity( d.SetId("") return nil } - elbis, err := getELBInstanceStates(g, meta) - if err != nil { - return resource.NonRetryableError(err) - } - albis, err := getTargetGroupInstanceStates(g, meta) - if err != nil { - return resource.NonRetryableError(err) - } - - haveASG := 0 - haveELB := 0 - for _, i := range g.Instances { - if i.HealthStatus == nil || i.InstanceId == nil || i.LifecycleState == nil { - continue - } - - if !strings.EqualFold(*i.HealthStatus, "Healthy") { - continue - } - - if !strings.EqualFold(*i.LifecycleState, "InService") { - continue - } + satisfied, reason := isELBCapacitySatisfied(d, meta, g, satisfiedFunc) + if satisfied { + return nil + } - haveASG++ + return resource.RetryableError(fmt.Errorf("%q: Waiting up to %s: %s", d.Id(), wait, reason)) + }) + if isResourceTimeoutError(err) { + g, err := getAwsAutoscalingGroup(d.Id(), meta.(*AWSClient).autoscalingconn) - inAllLbs := true - for _, states := range elbis { - state, ok := states[*i.InstanceId] - if !ok || !strings.EqualFold(state, "InService") { - inAllLbs = false - } - } - for _, states := range albis { - state, ok := states[*i.InstanceId] - if !ok || !strings.EqualFold(state, "healthy") { - inAllLbs = false - } - } - if inAllLbs { - haveELB++ - } + if err != nil { + return fmt.Errorf("Error getting autoscaling group info: %s", err) } - satisfied, reason := satisfiedFunc(d, haveASG, haveELB) - - log.Printf("[DEBUG] %q Capacity: %d ASG, %d ELB/ALB, satisfied: %t, reason: %q", - d.Id(), haveASG, haveELB, satisfied, reason) + if g == nil { + log.Printf("[WARN] Autoscaling Group (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + satisfied, _ := isELBCapacitySatisfied(d, meta, g, satisfiedFunc) if satisfied { return nil } - - return resource.RetryableError( - fmt.Errorf("%q: Waiting up to %s: %s", d.Id(), wait, reason)) - }) + } if err == nil { return nil @@ -126,6 +95,60 @@ func waitForASGCapacity( return fmt.Errorf("%s. Most recent activity: %s", err, recentStatus) } +func isELBCapacitySatisfied(d *schema.ResourceData, meta interface{}, g *autoscaling.Group, satisfiedFunc capacitySatisfiedFunc) (bool, string) { + elbis, err := getELBInstanceStates(g, meta) + if err != nil { + return false, fmt.Sprintf("Error getting ELB instance states: %s", err) + } + albis, err := getTargetGroupInstanceStates(g, meta) + if err != nil { + return false, fmt.Sprintf("Error getting target group instance states: %s", err) + } + + haveASG := 0 + haveELB := 0 + + for _, i := range g.Instances { + if i.HealthStatus == nil || i.InstanceId == nil || i.LifecycleState == nil { + continue + } + + if !strings.EqualFold(*i.HealthStatus, "Healthy") { + continue + } + + if !strings.EqualFold(*i.LifecycleState, "InService") { + continue + } + + haveASG++ + + inAllLbs := true + for _, states := range elbis { + state, ok := states[*i.InstanceId] + if !ok || !strings.EqualFold(state, "InService") { + inAllLbs = false + } + } + for _, states := range albis { + state, ok := states[*i.InstanceId] + if !ok || !strings.EqualFold(state, "healthy") { + inAllLbs = false + } + } + if inAllLbs { + haveELB++ + } + } + + satisfied, reason := satisfiedFunc(d, haveASG, haveELB) + + log.Printf("[DEBUG] %q Capacity: %d ASG, %d ELB/ALB, satisfied: %t, reason: %q", + d.Id(), haveASG, haveELB, satisfied, reason) + + return satisfied, reason +} + type capacitySatisfiedFunc func(*schema.ResourceData, int, int) (bool, string) // capacitySatisfiedCreate treats all targets as minimums diff --git a/aws/resource_aws_autoscaling_lifecycle_hook.go b/aws/resource_aws_autoscaling_lifecycle_hook.go index 7508a9d0f25..87677c54f3b 100644 --- a/aws/resource_aws_autoscaling_lifecycle_hook.go +++ b/aws/resource_aws_autoscaling_lifecycle_hook.go @@ -65,7 +65,7 @@ func resourceAwsAutoscalingLifecycleHook() *schema.Resource { func resourceAwsAutoscalingLifecycleHookPutOp(conn *autoscaling.AutoScaling, params *autoscaling.PutLifecycleHookInput) error { log.Printf("[DEBUG] AutoScaling PutLifecyleHook: %s", params) - return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := conn.PutLifecycleHook(params) if err != nil { @@ -78,6 +78,13 @@ func resourceAwsAutoscalingLifecycleHookPutOp(conn *autoscaling.AutoScaling, par } return nil }) + if isResourceTimeoutError(err) { + _, err = conn.PutLifecycleHook(params) + } + if err != nil { + return fmt.Errorf("Error putting autoscaling lifecycle hook: %s", err) + } + return nil } func resourceAwsAutoscalingLifecycleHookPut(d *schema.ResourceData, meta interface{}) error {