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 }