forked from minamijoyo/myaws
/
autoscaling_waiter.go
129 lines (116 loc) · 4.73 KB
/
autoscaling_waiter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package myaws
import (
"fmt"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/autoscaling"
)
// WaitUntilAutoScalingGroupStable is a helper function which waits until
// the AutoScaling Group converges to the desired state. We only check the
// status of AutoScaling Group. If the ASG has an ELB, the health check status
// of ELB can link with the health status of ASG, so we don't check the status
// of ELB here.
// Due to the current limitation of the implementation of `request.Waiter`,
// we need to wait it in two steps.
// 1. Wait until the number of instances equals `DesiredCapacity`.
// 2. Wait until all instances are InService.
func (client *Client) WaitUntilAutoScalingGroupStable(asgName string) error {
desiredCapacity, err := client.getAutoScalingGroupDesiredCapacity(asgName)
if err != nil {
return err
}
ctx := aws.BackgroundContext()
input := &autoscaling.DescribeAutoScalingGroupsInput{
AutoScalingGroupNames: []*string{&asgName},
}
// make sure instances are created or terminated.
err = client.waitUntilAutoScalingGroupNumberOfInstancesEqualsDesiredCapacityWithContext(
ctx,
desiredCapacity,
input,
)
if err != nil {
return err
}
// if the desired state is no instance, we just return here.
if desiredCapacity == 0 {
return nil
}
// check all instances are InService state.
return client.waitUntilAutoScalingGroupAllInstancesAreInServiceWithContext(ctx, input)
}
// waitUntilAutoScalingGroupNumberOfInstancesEqualsDesiredCapacityWithContext
// waits the number of instances equals DesiredCapacity.
func (client *Client) waitUntilAutoScalingGroupNumberOfInstancesEqualsDesiredCapacityWithContext(ctx aws.Context, desiredCapacity int64, input *autoscaling.DescribeAutoScalingGroupsInput, opts ...request.WaiterOption) error {
// We implicitly assume that the number of AutoScalingGroup is only one to
// simplify checking desiredCapacity. In our case, multiple AutoScalingGroup
// doesn't pass this function.
// Properties in the response returned by aws-sdk-go are reference types and
// not primitive. Thus we cannot be directly compared on JMESPath.
matcher := fmt.Sprintf("AutoScalingGroups[].[length(Instances) == `%d`][]", desiredCapacity)
w := request.Waiter{
Name: "WaitUntilAutoScalingGroupNumberOfInstancesEqualsDesiredCapacity",
MaxAttempts: 20,
Delay: request.ConstantWaiterDelay(15 * time.Second),
Acceptors: []request.WaiterAcceptor{
{
State: request.SuccessWaiterState,
Matcher: request.PathAllWaiterMatch, Argument: matcher,
Expected: true,
},
},
Logger: client.config.Logger,
NewRequest: func(opts []request.Option) (*request.Request, error) {
var inCpy *autoscaling.DescribeAutoScalingGroupsInput
if input != nil {
tmp := *input
inCpy = &tmp
}
req, _ := client.AutoScaling.DescribeAutoScalingGroupsRequest(inCpy)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return req, nil
},
}
w.ApplyOptions(opts...)
return w.WaitWithContext(ctx)
}
// waitUntilAutoScalingGroupAllInstancesAreInService waits until all instances
// are in service. Since the official `WaitUntilGroupInServiceWithContext` in
// aws-sdk-go checks as follow:
// contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)
// But we found this doesn't work as expected. Properties in the response
// returned by aws-sdk-go are reference type and not primitive. Thus we can not
// be directly compared on JMESPath. So we implement a customized waiter here.
// When the number of desired instances increase or decrease, the affected
// instances are in states other than InService until the operation completes.
// So we should check that all the states of instances are InService.
func (client *Client) waitUntilAutoScalingGroupAllInstancesAreInServiceWithContext(ctx aws.Context, input *autoscaling.DescribeAutoScalingGroupsInput, opts ...request.WaiterOption) error {
w := request.Waiter{
Name: "WaitUntilAutoScalingGroupAllInstancesAreInService",
MaxAttempts: 20,
Delay: request.ConstantWaiterDelay(15 * time.Second),
Acceptors: []request.WaiterAcceptor{
{
State: request.SuccessWaiterState,
Matcher: request.PathAllWaiterMatch, Argument: "AutoScalingGroups[].Instances[].LifecycleState",
Expected: "InService",
},
},
Logger: client.config.Logger,
NewRequest: func(opts []request.Option) (*request.Request, error) {
var inCpy *autoscaling.DescribeAutoScalingGroupsInput
if input != nil {
tmp := *input
inCpy = &tmp
}
req, _ := client.AutoScaling.DescribeAutoScalingGroupsRequest(inCpy)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return req, nil
},
}
w.ApplyOptions(opts...)
return w.WaitWithContext(ctx)
}