github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/builtin/providers/aws/resource_aws_autoscaling_group_waiting.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/service/autoscaling"
    11  	"github.com/hashicorp/errwrap"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  // waitForASGCapacityTimeout gathers the current numbers of healthy instances
    17  // in the ASG and its attached ELBs and yields these numbers to a
    18  // capacitySatifiedFunction. Loops for up to wait_for_capacity_timeout until
    19  // the capacitySatisfiedFunc returns true.
    20  //
    21  // See "Waiting for Capacity" in docs for more discussion of the feature.
    22  func waitForASGCapacity(
    23  	d *schema.ResourceData,
    24  	meta interface{},
    25  	satisfiedFunc capacitySatisfiedFunc) error {
    26  	wait, err := time.ParseDuration(d.Get("wait_for_capacity_timeout").(string))
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	if wait == 0 {
    32  		log.Printf("[DEBUG] Capacity timeout set to 0, skipping capacity waiting.")
    33  		return nil
    34  	}
    35  
    36  	log.Printf("[DEBUG] Waiting on %s for capacity...", d.Id())
    37  
    38  	err = resource.Retry(wait, func() *resource.RetryError {
    39  		g, err := getAwsAutoscalingGroup(d.Id(), meta.(*AWSClient).autoscalingconn)
    40  		if err != nil {
    41  			return resource.NonRetryableError(err)
    42  		}
    43  		if g == nil {
    44  			log.Printf("[INFO] Autoscaling Group %q not found", d.Id())
    45  			d.SetId("")
    46  			return nil
    47  		}
    48  		elbis, err := getELBInstanceStates(g, meta)
    49  		albis, err := getTargetGroupInstanceStates(g, meta)
    50  		if err != nil {
    51  			return resource.NonRetryableError(err)
    52  		}
    53  
    54  		haveASG := 0
    55  		haveELB := 0
    56  
    57  		for _, i := range g.Instances {
    58  			if i.HealthStatus == nil || i.InstanceId == nil || i.LifecycleState == nil {
    59  				continue
    60  			}
    61  
    62  			if !strings.EqualFold(*i.HealthStatus, "Healthy") {
    63  				continue
    64  			}
    65  
    66  			if !strings.EqualFold(*i.LifecycleState, "InService") {
    67  				continue
    68  			}
    69  
    70  			haveASG++
    71  
    72  			inAllLbs := true
    73  			for _, states := range elbis {
    74  				state, ok := states[*i.InstanceId]
    75  				if !ok || !strings.EqualFold(state, "InService") {
    76  					inAllLbs = false
    77  				}
    78  			}
    79  			for _, states := range albis {
    80  				state, ok := states[*i.InstanceId]
    81  				if !ok || !strings.EqualFold(state, "healthy") {
    82  					inAllLbs = false
    83  				}
    84  			}
    85  			if inAllLbs {
    86  				haveELB++
    87  			}
    88  		}
    89  
    90  		satisfied, reason := satisfiedFunc(d, haveASG, haveELB)
    91  
    92  		log.Printf("[DEBUG] %q Capacity: %d ASG, %d ELB/ALB, satisfied: %t, reason: %q",
    93  			d.Id(), haveASG, haveELB, satisfied, reason)
    94  
    95  		if satisfied {
    96  			return nil
    97  		}
    98  
    99  		return resource.RetryableError(
   100  			fmt.Errorf("%q: Waiting up to %s: %s", d.Id(), wait, reason))
   101  	})
   102  
   103  	if err == nil {
   104  		return nil
   105  	}
   106  
   107  	recentStatus := ""
   108  
   109  	conn := meta.(*AWSClient).autoscalingconn
   110  	resp, aErr := conn.DescribeScalingActivities(&autoscaling.DescribeScalingActivitiesInput{
   111  		AutoScalingGroupName: aws.String(d.Id()),
   112  		MaxRecords:           aws.Int64(1),
   113  	})
   114  	if aErr == nil {
   115  		if len(resp.Activities) > 0 {
   116  			recentStatus = fmt.Sprintf("%s", resp.Activities[0])
   117  		} else {
   118  			recentStatus = "(0 activities found)"
   119  		}
   120  	} else {
   121  		recentStatus = fmt.Sprintf("(Failed to describe scaling activities: %s)", aErr)
   122  	}
   123  
   124  	msg := fmt.Sprintf("{{err}}. Most recent activity: %s", recentStatus)
   125  	return errwrap.Wrapf(msg, err)
   126  }
   127  
   128  type capacitySatisfiedFunc func(*schema.ResourceData, int, int) (bool, string)
   129  
   130  // capacitySatisfiedCreate treats all targets as minimums
   131  func capacitySatisfiedCreate(d *schema.ResourceData, haveASG, haveELB int) (bool, string) {
   132  	minASG := d.Get("min_size").(int)
   133  	if wantASG := d.Get("desired_capacity").(int); wantASG > 0 {
   134  		minASG = wantASG
   135  	}
   136  	if haveASG < minASG {
   137  		return false, fmt.Sprintf(
   138  			"Need at least %d healthy instances in ASG, have %d", minASG, haveASG)
   139  	}
   140  	minELB := d.Get("min_elb_capacity").(int)
   141  	if wantELB := d.Get("wait_for_elb_capacity").(int); wantELB > 0 {
   142  		minELB = wantELB
   143  	}
   144  	if haveELB < minELB {
   145  		return false, fmt.Sprintf(
   146  			"Need at least %d healthy instances in ELB, have %d", minELB, haveELB)
   147  	}
   148  	return true, ""
   149  }
   150  
   151  // capacitySatisfiedUpdate only cares about specific targets
   152  func capacitySatisfiedUpdate(d *schema.ResourceData, haveASG, haveELB int) (bool, string) {
   153  	if wantASG := d.Get("desired_capacity").(int); wantASG > 0 {
   154  		if haveASG != wantASG {
   155  			return false, fmt.Sprintf(
   156  				"Need exactly %d healthy instances in ASG, have %d", wantASG, haveASG)
   157  		}
   158  	}
   159  	if wantELB := d.Get("wait_for_elb_capacity").(int); wantELB > 0 {
   160  		if haveELB != wantELB {
   161  			return false, fmt.Sprintf(
   162  				"Need exactly %d healthy instances in ELB, have %d", wantELB, haveELB)
   163  		}
   164  	}
   165  	return true, ""
   166  }