cf_deployer 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ChangeLog.md +6 -0
- data/Rakefile +6 -1
- data/cf_deployer.gemspec +2 -0
- data/fixtures/vcr_cassettes/aws_call_count/driver/auto_scaling_group/healthy_instance_count.yml +243 -0
- data/lib/cf_deployer/deployment_strategy/auto_scaling_group_swap.rb +20 -7
- data/lib/cf_deployer/driver/auto_scaling_group.rb +42 -25
- data/lib/cf_deployer/logger.rb +4 -0
- data/lib/cf_deployer/version.rb +1 -1
- data/spec/aws_call_count/driver/auto_scaling_group_spec.rb +19 -0
- data/spec/aws_call_count_spec_helper.rb +38 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/unit/deployment_strategy/auto_scaling_group_swap_spec.rb +308 -31
- data/spec/unit/driver/auto_scaling_group_spec.rb +154 -38
- metadata +35 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b15fbdcf8f040017be2e4f7fd2fca49e99327164
|
|
4
|
+
data.tar.gz: 6138254475f90d7983da1823754ace6134be9d85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 209ff6fd2211ae9b37ef0592aaec30066139ee37742cc36bdb45593ad0bc8f4476393ccc76be3d04204b9021bef07621b703bdd09b65b85580f18827ff5ceb9a
|
|
7
|
+
data.tar.gz: 9d3a7676274ac56d77ef0d545f1dc2441bf9a84ce07bf50cae1aec809df349ff8a5f23202bfdabedf8064d08383ac191d879606c9a80b3586e91792f2819f91b
|
data/ChangeLog.md
CHANGED
|
@@ -57,3 +57,9 @@ version 1.4.0
|
|
|
57
57
|
|
|
58
58
|
version 1.5.0
|
|
59
59
|
- Treat deployments that end in a rollback as a failure
|
|
60
|
+
|
|
61
|
+
version 1.6.0
|
|
62
|
+
- Improve warm up for AutoScalingGroup based deployments (See: https://github.com/manheim/cf_deployer/issues/32)
|
|
63
|
+
- Automatically rollback on failures for AutoScalingGroup based deployments (See: https://github.com/manheim/cf_deployer/issues/39)
|
|
64
|
+
- Improve error message when no stack is exists on ASG-based switch (See: https://github.com/manheim/cf_deployer/issues/50)
|
|
65
|
+
- Reduce AWS calls during healthy_instance_count (See: https://github.com/manheim/cf_deployer/issues/48)
|
data/Rakefile
CHANGED
|
@@ -22,7 +22,12 @@ namespace :spec do
|
|
|
22
22
|
t.pattern = 'spec/functional/**/*_spec.rb'
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
RSpec::Core::RakeTask.new(:aws_call_count) do |t|
|
|
26
|
+
t.rspec_opts = RSPEC_OPTS
|
|
27
|
+
t.pattern = 'spec/aws_call_count/**/*_spec.rb'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
task :all => [:unit, :functional, :aws_call_count]
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
task :default => 'spec:all'
|
data/cf_deployer.gemspec
CHANGED
|
@@ -18,6 +18,8 @@ Gem::Specification.new do |gem|
|
|
|
18
18
|
gem.add_development_dependency 'pry', '~> 0.10.1'
|
|
19
19
|
gem.add_development_dependency 'rspec', '2.14.1'
|
|
20
20
|
gem.add_development_dependency 'rake', '~> 10.3.0'
|
|
21
|
+
gem.add_development_dependency 'webmock', '~> 2.1.0'
|
|
22
|
+
gem.add_development_dependency 'vcr', '~> 2.9.3'
|
|
21
23
|
|
|
22
24
|
gem.files = `git ls-files`.split($\).reject {|f| f =~ /^samples\// }
|
|
23
25
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
data/fixtures/vcr_cassettes/aws_call_count/driver/auto_scaling_group/healthy_instance_count.yml
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
http_interactions:
|
|
3
|
+
- request:
|
|
4
|
+
method: post
|
|
5
|
+
uri: https://autoscaling.us-east-1.amazonaws.com/
|
|
6
|
+
response:
|
|
7
|
+
status:
|
|
8
|
+
code: 200
|
|
9
|
+
message: OK
|
|
10
|
+
body:
|
|
11
|
+
encoding: US-ASCII
|
|
12
|
+
string: ! "<DescribeAutoScalingGroupsResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2011-01-01/\">\n
|
|
13
|
+
\ <DescribeAutoScalingGroupsResult>\n <AutoScalingGroups>\n <member>\n
|
|
14
|
+
\ <HealthCheckType>ELB</HealthCheckType>\n <LoadBalancerNames>\n
|
|
15
|
+
\ <member>myELB</member>\n </LoadBalancerNames>\n
|
|
16
|
+
\ <Instances>\n <member>\n <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
17
|
+
\ <LifecycleState>InService</LifecycleState>\n <InstanceId>instance1</InstanceId>\n
|
|
18
|
+
\ <HealthStatus>Healthy</HealthStatus>\n <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n
|
|
19
|
+
\ <AvailabilityZone>us-east-1d</AvailabilityZone>\n </member>\n
|
|
20
|
+
\ <member>\n <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
21
|
+
\ <LifecycleState>InService</LifecycleState>\n <InstanceId>instance2</InstanceId>\n
|
|
22
|
+
\ <HealthStatus>Healthy</HealthStatus>\n <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n
|
|
23
|
+
\ <AvailabilityZone>us-east-1c</AvailabilityZone>\n </member>\n
|
|
24
|
+
\ <member>\n <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
25
|
+
\ <LifecycleState>InService</LifecycleState>\n <InstanceId>instance3</InstanceId>\n
|
|
26
|
+
\ <HealthStatus>Healthy</HealthStatus>\n <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n
|
|
27
|
+
\ <AvailabilityZone>us-east-1e</AvailabilityZone>\n </member>\n
|
|
28
|
+
\ <member>\n <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
29
|
+
\ <LifecycleState>InService</LifecycleState>\n <InstanceId>instance4</InstanceId>\n
|
|
30
|
+
\ <HealthStatus>Healthy</HealthStatus>\n <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n
|
|
31
|
+
\ <AvailabilityZone>us-east-1e</AvailabilityZone>\n </member>\n
|
|
32
|
+
\ </Instances>\n <TerminationPolicies>\n <member>Default</member>\n
|
|
33
|
+
\ </TerminationPolicies>\n <DefaultCooldown>300</DefaultCooldown>\n
|
|
34
|
+
\ <AutoScalingGroupARN>arn:aws:autoscaling:us-east-1:12345:autoScalingGroup:12345:autoScalingGroupName/myASG</AutoScalingGroupARN>\n
|
|
35
|
+
\ <EnabledMetrics/>\n <MaxSize>5</MaxSize>\n <AvailabilityZones>\n
|
|
36
|
+
\ <member>us-east-1c</member>\n <member>us-east-1d</member>\n
|
|
37
|
+
\ <member>us-east-1e</member>\n </AvailabilityZones>\n <TargetGroupARNs/>\n
|
|
38
|
+
\ <Tags/>\n
|
|
39
|
+
\ <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
40
|
+
\ <AutoScalingGroupName>myASG</AutoScalingGroupName>\n
|
|
41
|
+
\ <HealthCheckGracePeriod>600</HealthCheckGracePeriod>\n <NewInstancesProtectedFromScaleIn>false</NewInstancesProtectedFromScaleIn>\n
|
|
42
|
+
\ <CreatedTime>2016-09-23T03:52:21.733Z</CreatedTime>\n <MinSize>1</MinSize>\n
|
|
43
|
+
\ <SuspendedProcesses/>\n <DesiredCapacity>4</DesiredCapacity>\n
|
|
44
|
+
\ <VPCZoneIdentifier>subnet</VPCZoneIdentifier>\n
|
|
45
|
+
\ </member>\n </AutoScalingGroups>\n </DescribeAutoScalingGroupsResult>\n
|
|
46
|
+
\ <ResponseMetadata>\n <RequestId>12345</RequestId>\n
|
|
47
|
+
\ </ResponseMetadata>\n</DescribeAutoScalingGroupsResponse>\n"
|
|
48
|
+
http_version:
|
|
49
|
+
recorded_at: Tue, 27 Sep 2016 05:47:40 GMT
|
|
50
|
+
- request:
|
|
51
|
+
method: post
|
|
52
|
+
uri: https://autoscaling.us-east-1.amazonaws.com/
|
|
53
|
+
response:
|
|
54
|
+
status:
|
|
55
|
+
code: 200
|
|
56
|
+
message: OK
|
|
57
|
+
body:
|
|
58
|
+
encoding: US-ASCII
|
|
59
|
+
string: ! "<DescribeAutoScalingInstancesResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2011-01-01/\">\n
|
|
60
|
+
\ <DescribeAutoScalingInstancesResult>\n <AutoScalingInstances>\n <member>\n
|
|
61
|
+
\ <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
62
|
+
\ <LifecycleState>InService</LifecycleState>\n <AutoScalingGroupName></AutoScalingGroupName>\n
|
|
63
|
+
\ <InstanceId>instance1</InstanceId>\n <HealthStatus>HEALTHY</HealthStatus>\n
|
|
64
|
+
\ <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n <AvailabilityZone>us-east-1d</AvailabilityZone>\n
|
|
65
|
+
\ </member>\n </AutoScalingInstances>\n </DescribeAutoScalingInstancesResult>\n
|
|
66
|
+
\ <ResponseMetadata>\n <RequestId>12346</RequestId>\n
|
|
67
|
+
\ </ResponseMetadata>\n</DescribeAutoScalingInstancesResponse>\n"
|
|
68
|
+
http_version:
|
|
69
|
+
recorded_at: Tue, 27 Sep 2016 05:47:40 GMT
|
|
70
|
+
- request:
|
|
71
|
+
method: post
|
|
72
|
+
uri: https://elasticloadbalancing.us-east-1.amazonaws.com/
|
|
73
|
+
body:
|
|
74
|
+
encoding: UTF-8
|
|
75
|
+
string: Action=DescribeInstanceHealth&LoadBalancerName=myELB&Timestamp=2016-09-27T05%3A47%3A41Z&Version=2012-06-01
|
|
76
|
+
response:
|
|
77
|
+
status:
|
|
78
|
+
code: 200
|
|
79
|
+
message: OK
|
|
80
|
+
body:
|
|
81
|
+
encoding: US-ASCII
|
|
82
|
+
string: ! "<DescribeInstanceHealthResponse xmlns=\"http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/\">\n
|
|
83
|
+
\ <DescribeInstanceHealthResult>\n <InstanceStates>\n <member>\n <Description>N/A</Description>\n
|
|
84
|
+
\ <InstanceId>instance1</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
85
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
86
|
+
\ <InstanceId>instance2</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
87
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
88
|
+
\ <InstanceId>instance3</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
89
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
90
|
+
\ <InstanceId>instance4</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
91
|
+
\ <State>InService</State>\n </member>\n </InstanceStates>\n
|
|
92
|
+
\ </DescribeInstanceHealthResult>\n <ResponseMetadata>\n <RequestId>12347</RequestId>\n
|
|
93
|
+
\ </ResponseMetadata>\n</DescribeInstanceHealthResponse>\n"
|
|
94
|
+
http_version:
|
|
95
|
+
recorded_at: Tue, 27 Sep 2016 05:47:41 GMT
|
|
96
|
+
- request:
|
|
97
|
+
method: post
|
|
98
|
+
uri: https://autoscaling.us-east-1.amazonaws.com/
|
|
99
|
+
body:
|
|
100
|
+
encoding: UTF-8
|
|
101
|
+
string: Action=DescribeAutoScalingInstances&InstanceIds.member.1=instance2&Timestamp=2016-09-27T05%3A47%3A41Z&Version=2011-01-01
|
|
102
|
+
response:
|
|
103
|
+
status:
|
|
104
|
+
code: 200
|
|
105
|
+
message: OK
|
|
106
|
+
body:
|
|
107
|
+
encoding: US-ASCII
|
|
108
|
+
string: ! "<DescribeAutoScalingInstancesResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2011-01-01/\">\n
|
|
109
|
+
\ <DescribeAutoScalingInstancesResult>\n <AutoScalingInstances>\n <member>\n
|
|
110
|
+
\ <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
111
|
+
\ <LifecycleState>InService</LifecycleState>\n <AutoScalingGroupName>myASG</AutoScalingGroupName>\n
|
|
112
|
+
\ <InstanceId>instance2</InstanceId>\n <HealthStatus>HEALTHY</HealthStatus>\n
|
|
113
|
+
\ <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n <AvailabilityZone>us-east-1c</AvailabilityZone>\n
|
|
114
|
+
\ </member>\n </AutoScalingInstances>\n </DescribeAutoScalingInstancesResult>\n
|
|
115
|
+
\ <ResponseMetadata>\n <RequestId>12348</RequestId>\n
|
|
116
|
+
\ </ResponseMetadata>\n</DescribeAutoScalingInstancesResponse>\n"
|
|
117
|
+
http_version:
|
|
118
|
+
recorded_at: Tue, 27 Sep 2016 05:47:42 GMT
|
|
119
|
+
- request:
|
|
120
|
+
method: post
|
|
121
|
+
uri: https://elasticloadbalancing.us-east-1.amazonaws.com/
|
|
122
|
+
body:
|
|
123
|
+
encoding: UTF-8
|
|
124
|
+
string: Action=DescribeInstanceHealth&LoadBalancerName=myELB&Timestamp=2016-09-27T05%3A47%3A42Z&Version=2012-06-01
|
|
125
|
+
response:
|
|
126
|
+
status:
|
|
127
|
+
code: 200
|
|
128
|
+
message: OK
|
|
129
|
+
body:
|
|
130
|
+
encoding: US-ASCII
|
|
131
|
+
string: ! "<DescribeInstanceHealthResponse xmlns=\"http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/\">\n
|
|
132
|
+
\ <DescribeInstanceHealthResult>\n <InstanceStates>\n <member>\n <Description>N/A</Description>\n
|
|
133
|
+
\ <InstanceId>instance1</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
134
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
135
|
+
\ <InstanceId>instance2</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
136
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
137
|
+
\ <InstanceId>instance3</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
138
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
139
|
+
\ <InstanceId>instance4</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
140
|
+
\ <State>InService</State>\n </member>\n </InstanceStates>\n
|
|
141
|
+
\ </DescribeInstanceHealthResult>\n <ResponseMetadata>\n <RequestId>12349</RequestId>\n
|
|
142
|
+
\ </ResponseMetadata>\n</DescribeInstanceHealthResponse>\n"
|
|
143
|
+
http_version:
|
|
144
|
+
recorded_at: Tue, 27 Sep 2016 05:47:42 GMT
|
|
145
|
+
- request:
|
|
146
|
+
method: post
|
|
147
|
+
uri: https://autoscaling.us-east-1.amazonaws.com/
|
|
148
|
+
body:
|
|
149
|
+
encoding: UTF-8
|
|
150
|
+
string: Action=DescribeAutoScalingInstances&InstanceIds.member.1=instance3&Timestamp=2016-09-27T05%3A47%3A42Z&Version=2011-01-01
|
|
151
|
+
response:
|
|
152
|
+
status:
|
|
153
|
+
code: 200
|
|
154
|
+
message: OK
|
|
155
|
+
body:
|
|
156
|
+
encoding: US-ASCII
|
|
157
|
+
string: ! "<DescribeAutoScalingInstancesResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2011-01-01/\">\n
|
|
158
|
+
\ <DescribeAutoScalingInstancesResult>\n <AutoScalingInstances>\n <member>\n
|
|
159
|
+
\ <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
160
|
+
\ <LifecycleState>InService</LifecycleState>\n <AutoScalingGroupName>myASG</AutoScalingGroupName>\n
|
|
161
|
+
\ <InstanceId>instance3</InstanceId>\n <HealthStatus>HEALTHY</HealthStatus>\n
|
|
162
|
+
\ <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n <AvailabilityZone>us-east-1e</AvailabilityZone>\n
|
|
163
|
+
\ </member>\n </AutoScalingInstances>\n </DescribeAutoScalingInstancesResult>\n
|
|
164
|
+
\ <ResponseMetadata>\n <RequestId>12350</RequestId>\n
|
|
165
|
+
\ </ResponseMetadata>\n</DescribeAutoScalingInstancesResponse>\n"
|
|
166
|
+
http_version:
|
|
167
|
+
recorded_at: Tue, 27 Sep 2016 05:47:42 GMT
|
|
168
|
+
- request:
|
|
169
|
+
method: post
|
|
170
|
+
uri: https://elasticloadbalancing.us-east-1.amazonaws.com/
|
|
171
|
+
body:
|
|
172
|
+
encoding: UTF-8
|
|
173
|
+
string: Action=DescribeInstanceHealth&LoadBalancerName=myELB&Timestamp=2016-09-27T05%3A47%3A42Z&Version=2012-06-01
|
|
174
|
+
response:
|
|
175
|
+
status:
|
|
176
|
+
code: 200
|
|
177
|
+
message: OK
|
|
178
|
+
body:
|
|
179
|
+
encoding: US-ASCII
|
|
180
|
+
string: ! "<DescribeInstanceHealthResponse xmlns=\"http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/\">\n
|
|
181
|
+
\ <DescribeInstanceHealthResult>\n <InstanceStates>\n <member>\n <Description>N/A</Description>\n
|
|
182
|
+
\ <InstanceId>instance1</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
183
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
184
|
+
\ <InstanceId>instance2</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
185
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
186
|
+
\ <InstanceId>instance3</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
187
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
188
|
+
\ <InstanceId>instance4</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
189
|
+
\ <State>InService</State>\n </member>\n </InstanceStates>\n
|
|
190
|
+
\ </DescribeInstanceHealthResult>\n <ResponseMetadata>\n <RequestId>12351</RequestId>\n
|
|
191
|
+
\ </ResponseMetadata>\n</DescribeInstanceHealthResponse>\n"
|
|
192
|
+
http_version:
|
|
193
|
+
recorded_at: Tue, 27 Sep 2016 05:47:42 GMT
|
|
194
|
+
- request:
|
|
195
|
+
method: post
|
|
196
|
+
uri: https://autoscaling.us-east-1.amazonaws.com/
|
|
197
|
+
body:
|
|
198
|
+
encoding: UTF-8
|
|
199
|
+
string: Action=DescribeAutoScalingInstances&InstanceIds.member.1=instance4&Timestamp=2016-09-27T05%3A47%3A42Z&Version=2011-01-01
|
|
200
|
+
response:
|
|
201
|
+
status:
|
|
202
|
+
code: 200
|
|
203
|
+
message: OK
|
|
204
|
+
body:
|
|
205
|
+
encoding: US-ASCII
|
|
206
|
+
string: ! "<DescribeAutoScalingInstancesResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2011-01-01/\">\n
|
|
207
|
+
\ <DescribeAutoScalingInstancesResult>\n <AutoScalingInstances>\n <member>\n
|
|
208
|
+
\ <LaunchConfigurationName>myLaunchConfig</LaunchConfigurationName>\n
|
|
209
|
+
\ <LifecycleState>InService</LifecycleState>\n <AutoScalingGroupName>myASG</AutoScalingGroupName>\n
|
|
210
|
+
\ <InstanceId>instance4</InstanceId>\n <HealthStatus>HEALTHY</HealthStatus>\n
|
|
211
|
+
\ <ProtectedFromScaleIn>false</ProtectedFromScaleIn>\n <AvailabilityZone>us-east-1e</AvailabilityZone>\n
|
|
212
|
+
\ </member>\n </AutoScalingInstances>\n </DescribeAutoScalingInstancesResult>\n
|
|
213
|
+
\ <ResponseMetadata>\n <RequestId>12352</RequestId>\n
|
|
214
|
+
\ </ResponseMetadata>\n</DescribeAutoScalingInstancesResponse>\n"
|
|
215
|
+
http_version:
|
|
216
|
+
recorded_at: Tue, 27 Sep 2016 05:47:42 GMT
|
|
217
|
+
- request:
|
|
218
|
+
method: post
|
|
219
|
+
uri: https://elasticloadbalancing.us-east-1.amazonaws.com/
|
|
220
|
+
body:
|
|
221
|
+
encoding: UTF-8
|
|
222
|
+
string: Action=DescribeInstanceHealth&LoadBalancerName=myELB&Timestamp=2016-09-27T05%3A47%3A42Z&Version=2012-06-01
|
|
223
|
+
response:
|
|
224
|
+
status:
|
|
225
|
+
code: 200
|
|
226
|
+
message: OK
|
|
227
|
+
body:
|
|
228
|
+
encoding: US-ASCII
|
|
229
|
+
string: ! "<DescribeInstanceHealthResponse xmlns=\"http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/\">\n
|
|
230
|
+
\ <DescribeInstanceHealthResult>\n <InstanceStates>\n <member>\n <Description>N/A</Description>\n
|
|
231
|
+
\ <InstanceId>instance1</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
232
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
233
|
+
\ <InstanceId>instance2</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
234
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
235
|
+
\ <InstanceId>instance3</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
236
|
+
\ <State>InService</State>\n </member>\n <member>\n <Description>N/A</Description>\n
|
|
237
|
+
\ <InstanceId>instance4</InstanceId>\n <ReasonCode>N/A</ReasonCode>\n
|
|
238
|
+
\ <State>InService</State>\n </member>\n </InstanceStates>\n
|
|
239
|
+
\ </DescribeInstanceHealthResult>\n <ResponseMetadata>\n <RequestId>12353</RequestId>\n
|
|
240
|
+
\ </ResponseMetadata>\n</DescribeInstanceHealthResponse>\n"
|
|
241
|
+
http_version:
|
|
242
|
+
recorded_at: Tue, 27 Sep 2016 05:47:42 GMT
|
|
243
|
+
recorded_with: VCR 2.9.3
|
|
@@ -2,12 +2,25 @@ module CfDeployer
|
|
|
2
2
|
module DeploymentStrategy
|
|
3
3
|
class AutoScalingGroupSwap < BlueGreen
|
|
4
4
|
|
|
5
|
+
def cool_inactive_on_failure
|
|
6
|
+
yield
|
|
7
|
+
rescue => e
|
|
8
|
+
if both_stacks_active?
|
|
9
|
+
Log.error "Deployment failed - #{e.message} - and both stacks are active. Cooling down failed stack. Look into the failure, and try your deployment again."
|
|
10
|
+
cool_down(inactive_stack)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
raise e
|
|
14
|
+
end
|
|
15
|
+
|
|
5
16
|
def deploy
|
|
6
17
|
check_blue_green_not_both_active 'Deployment'
|
|
7
18
|
Log.info "Found active stack #{active_stack.name}" if active_stack
|
|
8
19
|
delete_stack inactive_stack
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
cool_inactive_on_failure do
|
|
21
|
+
create_inactive_stack
|
|
22
|
+
swap_group
|
|
23
|
+
end
|
|
11
24
|
run_hook(:'after-swap')
|
|
12
25
|
Log.info "Active stack has been set to #{inactive_stack.name}"
|
|
13
26
|
delete_stack(active_stack) if active_stack && !keep_previous_stack
|
|
@@ -22,8 +35,8 @@ module CfDeployer
|
|
|
22
35
|
|
|
23
36
|
def switch
|
|
24
37
|
check_blue_green_not_both_active 'Switch'
|
|
25
|
-
raise ApplicationError.new('
|
|
26
|
-
swap_group true
|
|
38
|
+
raise ApplicationError.new('Both stacks must exist to switch.') unless both_stacks_exist?
|
|
39
|
+
cool_inactive_on_failure { swap_group true }
|
|
27
40
|
end
|
|
28
41
|
|
|
29
42
|
private
|
|
@@ -35,7 +48,7 @@ module CfDeployer
|
|
|
35
48
|
|
|
36
49
|
def swap_group is_switching_to_cooled = false
|
|
37
50
|
is_switching_to_cooled ? warm_up_cooled_stack : warm_up_inactive_stack
|
|
38
|
-
|
|
51
|
+
cool_down(active_stack) if active_stack && (is_switching_to_cooled || keep_previous_stack)
|
|
39
52
|
end
|
|
40
53
|
|
|
41
54
|
def keep_previous_stack
|
|
@@ -56,8 +69,8 @@ module CfDeployer
|
|
|
56
69
|
warm_up_stack(inactive_stack, active_stack, true)
|
|
57
70
|
end
|
|
58
71
|
|
|
59
|
-
def
|
|
60
|
-
get_active_asgs(
|
|
72
|
+
def cool_down stack
|
|
73
|
+
get_active_asgs(stack).each do |id|
|
|
61
74
|
asg_driver(id).cool_down
|
|
62
75
|
end
|
|
63
76
|
end
|
|
@@ -3,7 +3,7 @@ module CfDeployer
|
|
|
3
3
|
class AutoScalingGroup
|
|
4
4
|
extend Forwardable
|
|
5
5
|
|
|
6
|
-
def_delegators :aws_group, :auto_scaling_instances, :ec2_instances, :load_balancers
|
|
6
|
+
def_delegators :aws_group, :auto_scaling_instances, :ec2_instances, :load_balancers, :desired_capacity
|
|
7
7
|
|
|
8
8
|
attr_reader :group_name, :group
|
|
9
9
|
|
|
@@ -23,7 +23,7 @@ module CfDeployer
|
|
|
23
23
|
|
|
24
24
|
CfDeployer::Driver::DryRun.guard "Skipping ASG warmup" do
|
|
25
25
|
aws_group.set_desired_capacity desired
|
|
26
|
-
|
|
26
|
+
wait_for_desired_capacity
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -50,37 +50,54 @@ module CfDeployer
|
|
|
50
50
|
instance_info
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
count = auto_scaling_instances.count do |instance|
|
|
58
|
-
instance.health_status == 'HEALTHY' && (load_balancers.empty? || instance_in_service?( instance.ec2_instance ))
|
|
53
|
+
def wait_for_desired_capacity
|
|
54
|
+
Timeout::timeout(@timeout){
|
|
55
|
+
until desired_capacity_reached?
|
|
56
|
+
sleep 15
|
|
59
57
|
end
|
|
60
|
-
|
|
61
|
-
count
|
|
62
|
-
rescue => e
|
|
63
|
-
Log.info "Unable to determine healthy instance count due to error: #{e.message}"
|
|
64
|
-
-1
|
|
65
|
-
end
|
|
58
|
+
}
|
|
66
59
|
end
|
|
67
60
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
def desired_capacity_reached?
|
|
62
|
+
healthy_instance_count >= desired_capacity
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def healthy_instance_ids
|
|
66
|
+
instances = auto_scaling_instances.select do |instance|
|
|
67
|
+
'HEALTHY'.casecmp(instance.health_status) == 0
|
|
73
68
|
end
|
|
69
|
+
instances.map(&:id)
|
|
74
70
|
end
|
|
75
71
|
|
|
76
|
-
def
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
def in_service_instance_ids
|
|
73
|
+
elbs = load_balancers
|
|
74
|
+
return [] if elbs.empty?
|
|
75
|
+
|
|
76
|
+
ids = elbs.collect(&:instances)
|
|
77
|
+
.collect(&:health)
|
|
78
|
+
.to_a
|
|
79
|
+
.collect { |elb_healths|
|
|
80
|
+
elb_healths.select { |health| health[:state] == 'InService' }
|
|
81
|
+
.map { |health| health[:instance].id }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
ids.inject(:&)
|
|
82
85
|
end
|
|
83
86
|
|
|
87
|
+
def healthy_instance_count
|
|
88
|
+
AWS.memoize do
|
|
89
|
+
instances = healthy_instance_ids
|
|
90
|
+
instances &= in_service_instance_ids unless load_balancers.empty?
|
|
91
|
+
Log.info "Healthy instance count: #{instances.count}"
|
|
92
|
+
instances.count
|
|
93
|
+
end
|
|
94
|
+
rescue => e
|
|
95
|
+
Log.error "Unable to determine healthy instance count due to error: #{e.message}"
|
|
96
|
+
-1
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
84
101
|
def aws_group
|
|
85
102
|
@my_group ||= AWS::AutoScaling.new.groups[group_name]
|
|
86
103
|
end
|
data/lib/cf_deployer/logger.rb
CHANGED
data/lib/cf_deployer/version.rb
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'aws_call_count_spec_helper'
|
|
2
|
+
|
|
3
|
+
describe CfDeployer::Driver::AutoScalingGroup do
|
|
4
|
+
it 'makes the minimum number of calls to AWS when there are 4 instances in the ASG' do
|
|
5
|
+
asg = 'myASG'
|
|
6
|
+
|
|
7
|
+
override_aws_environment(AWS_REGION: 'us-east-1') do
|
|
8
|
+
logs = nil
|
|
9
|
+
allow(CfDeployer::Log).to receive(:error) { |message| logs = message }
|
|
10
|
+
driver = CfDeployer::Driver::AutoScalingGroup.new asg
|
|
11
|
+
VCR.use_cassette("aws_call_count/driver/auto_scaling_group/healthy_instance_count") do
|
|
12
|
+
expect(driver.send(:healthy_instance_count)).to equal(4), "Logs: #{logs}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
expect(WebMock).to have_requested(:post, "https://autoscaling.us-east-1.amazonaws.com/").times(1)
|
|
16
|
+
expect(WebMock).to have_requested(:post, "https://elasticloadbalancing.us-east-1.amazonaws.com/").times(1)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'webmock/rspec'
|
|
3
|
+
require 'vcr'
|
|
4
|
+
|
|
5
|
+
VCR.configure do |config|
|
|
6
|
+
config.cassette_library_dir = "fixtures/vcr_cassettes"
|
|
7
|
+
config.hook_into :webmock
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def override_aws_environment options = {}
|
|
11
|
+
options[:AWS_REGION] ||= 'us-east-1'
|
|
12
|
+
options[:AWS_ACCESS_KEY_ID] ||= 'someId'
|
|
13
|
+
options[:AWS_SECRET_ACCESS_KEY] ||= 'secretKey'
|
|
14
|
+
|
|
15
|
+
override_environment_variables(options) { yield }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def override_environment_variables options = {}
|
|
19
|
+
previous_values = override_previous_values(options)
|
|
20
|
+
|
|
21
|
+
yield
|
|
22
|
+
|
|
23
|
+
restore_values(previous_values)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def override_previous_values options = {}
|
|
27
|
+
previous_values = options.inject([]) do |memo, (key,value)|
|
|
28
|
+
memo << { key: key.to_s, value: value, existed: ENV.has_key?(key.to_s), old_value: ENV[key.to_s] }
|
|
29
|
+
ENV[key.to_s] = value
|
|
30
|
+
memo
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def restore_values previous_values = []
|
|
35
|
+
previous_values.each do |value|
|
|
36
|
+
value[:existed] ? ENV[value[:key]] = value[:old_value] : ENV.delete(value[:key])
|
|
37
|
+
end
|
|
38
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -3,7 +3,20 @@ Dir.glob("#{File.dirname File.absolute_path(__FILE__)}/fakes/*.rb") { |file| req
|
|
|
3
3
|
|
|
4
4
|
CfDeployer::Log.log.outputters = nil
|
|
5
5
|
|
|
6
|
+
RSPEC_LOG = Logger.new(STDOUT)
|
|
7
|
+
RSPEC_LOG.level = Logger::WARN
|
|
8
|
+
|
|
9
|
+
if ENV['DEBUG']
|
|
10
|
+
RSPEC_LOG.level = Logger::DEBUG
|
|
11
|
+
AWS.config :logger => RSPEC_LOG
|
|
12
|
+
end
|
|
13
|
+
|
|
6
14
|
def puts *args
|
|
7
15
|
|
|
8
16
|
end
|
|
9
17
|
|
|
18
|
+
def ignore_errors
|
|
19
|
+
yield
|
|
20
|
+
rescue => e
|
|
21
|
+
RSPEC_LOG.debug "Intentionally ignoring error: #{e.message}"
|
|
22
|
+
end
|
|
@@ -127,6 +127,114 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
|
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
+
context 'on deployment failure' do
|
|
131
|
+
context 'in stack creation' do
|
|
132
|
+
context 'and only one stack is active' do
|
|
133
|
+
it 'should not cool down the only available stack' do
|
|
134
|
+
strategy = create_strategy(blue: :active)
|
|
135
|
+
error = RuntimeError.new("Error before inactive_stack became active")
|
|
136
|
+
|
|
137
|
+
expect(strategy).to receive(:create_inactive_stack).and_raise(error)
|
|
138
|
+
expect(strategy).to_not receive(:cool_down)
|
|
139
|
+
|
|
140
|
+
ignore_errors { strategy.deploy }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'should report the deployment as a failure' do
|
|
144
|
+
strategy = create_strategy(blue: :active)
|
|
145
|
+
error = RuntimeError.new("Error before inactive_stack became active")
|
|
146
|
+
|
|
147
|
+
expect(strategy).to receive(:create_inactive_stack).and_raise(error)
|
|
148
|
+
expect { strategy.deploy }.to raise_error(error)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
context 'and both stacks are active' do
|
|
153
|
+
it 'should cool down inactive stack' do
|
|
154
|
+
strategy = create_strategy(blue: :active)
|
|
155
|
+
inactive_stack = strategy.send(:green_stack)
|
|
156
|
+
|
|
157
|
+
expect(strategy).to receive(:create_inactive_stack) do
|
|
158
|
+
activate_stack(inactive_stack)
|
|
159
|
+
raise RuntimeError.new("Error after inactive_stack became active")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
expect(strategy).to receive(:cool_down).with(inactive_stack)
|
|
163
|
+
ignore_errors { strategy.deploy }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it 'should report the deployment as a failure' do
|
|
167
|
+
strategy = create_strategy(blue: :active)
|
|
168
|
+
inactive_stack = strategy.send(:green_stack)
|
|
169
|
+
error = RuntimeError.new("Error after inactive_stack became active")
|
|
170
|
+
|
|
171
|
+
expect(strategy).to receive(:create_inactive_stack) do
|
|
172
|
+
activate_stack(inactive_stack)
|
|
173
|
+
raise error
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
allow(strategy).to receive(:cool_down)
|
|
177
|
+
expect { strategy.deploy }.to raise_error(error)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
context 'in asg swap' do
|
|
183
|
+
# This shouldn't be possible - a stack normally becomes active at creation
|
|
184
|
+
context 'and only one stack is active' do
|
|
185
|
+
it 'should not cool down the only available stack' do
|
|
186
|
+
strategy = create_strategy(blue: :active)
|
|
187
|
+
error = RuntimeError.new("Error during swap")
|
|
188
|
+
|
|
189
|
+
# The stack would normally be active after create_inactive_stack
|
|
190
|
+
allow(strategy).to receive(:create_inactive_stack)
|
|
191
|
+
expect(strategy).to receive(:swap_group).and_raise(error)
|
|
192
|
+
expect(strategy).to_not receive(:cool_down)
|
|
193
|
+
|
|
194
|
+
ignore_errors { strategy.deploy }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it 'should report the deployment as a failure' do
|
|
198
|
+
strategy = create_strategy(blue: :active)
|
|
199
|
+
error = RuntimeError.new("Error during swap")
|
|
200
|
+
|
|
201
|
+
# The stack would normally be active after create_inactive_stack
|
|
202
|
+
allow(strategy).to receive(:create_inactive_stack)
|
|
203
|
+
expect(strategy).to receive(:swap_group).and_raise(error)
|
|
204
|
+
expect(strategy).to_not receive(:cool_down)
|
|
205
|
+
|
|
206
|
+
expect { strategy.deploy }.to raise_error(error)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
context 'and both stacks are active' do
|
|
211
|
+
it 'should cool down inactive stack' do
|
|
212
|
+
strategy = create_strategy(blue: :active)
|
|
213
|
+
inactive_stack = strategy.send(:green_stack)
|
|
214
|
+
error = RuntimeError.new("Error during swap")
|
|
215
|
+
|
|
216
|
+
allow(strategy).to receive(:create_inactive_stack) { activate_stack(inactive_stack) }
|
|
217
|
+
expect(strategy).to receive(:swap_group).and_raise(error)
|
|
218
|
+
expect(strategy).to receive(:cool_down).with(inactive_stack)
|
|
219
|
+
|
|
220
|
+
ignore_errors { strategy.deploy }
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'should report the deployment as a failure' do
|
|
224
|
+
strategy = create_strategy(blue: :active)
|
|
225
|
+
inactive_stack = strategy.send(:green_stack)
|
|
226
|
+
error = RuntimeError.new("Error during swap")
|
|
227
|
+
|
|
228
|
+
allow(strategy).to receive(:create_inactive_stack) { activate_stack(inactive_stack) }
|
|
229
|
+
expect(strategy).to receive(:swap_group).and_raise(error)
|
|
230
|
+
expect(strategy).to receive(:cool_down).with(inactive_stack)
|
|
231
|
+
|
|
232
|
+
expect { strategy.deploy }.to raise_error(error)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
130
238
|
context 'has active group' do
|
|
131
239
|
it 'should deploy blue stack if green stack is active' do
|
|
132
240
|
blue_stack.live!
|
|
@@ -306,62 +414,169 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
|
|
|
306
414
|
describe '#switch' do
|
|
307
415
|
context 'both stacks are active' do
|
|
308
416
|
it 'should raise an error' do
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
|
|
312
|
-
allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
|
|
313
|
-
allow(green_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
|
|
314
|
-
allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
|
|
417
|
+
strategy = create_strategy(blue: :active, green: :active)
|
|
418
|
+
error = 'Found both auto-scaling-groups, ["greenASG", "blueASG"], in green and blue stacks are active. Switch aborted!'
|
|
315
419
|
|
|
316
|
-
|
|
317
|
-
expect{strategy.switch}.to raise_error 'Found both auto-scaling-groups, ["greenASG", "blueASG"], in green and blue stacks are active. Switch aborted!'
|
|
420
|
+
expect { strategy.switch }.to raise_error(error)
|
|
318
421
|
end
|
|
319
422
|
end
|
|
320
423
|
|
|
321
424
|
context 'both stacks do not exist' do
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
425
|
+
let(:error) { 'Both stacks must exist to switch.' }
|
|
426
|
+
|
|
427
|
+
context '(only green exists)' do
|
|
428
|
+
it 'should raise an error' do
|
|
429
|
+
strategy = create_strategy(green: :active, blue: :dead)
|
|
430
|
+
|
|
431
|
+
expect { strategy.switch }.to raise_error(error)
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
context '(only blue exists)' do
|
|
436
|
+
it 'should raise an error' do
|
|
437
|
+
strategy = create_strategy(green: :dead, blue: :active)
|
|
438
|
+
|
|
439
|
+
expect { strategy.switch }.to raise_error(error)
|
|
440
|
+
end
|
|
441
|
+
end
|
|
328
442
|
|
|
329
|
-
|
|
330
|
-
|
|
443
|
+
context '(no stack exists)' do
|
|
444
|
+
it 'should raise an error' do
|
|
445
|
+
strategy = create_strategy(green: :dead, blue: :dead)
|
|
446
|
+
|
|
447
|
+
expect { strategy.switch }.to raise_error(error)
|
|
448
|
+
end
|
|
331
449
|
end
|
|
332
450
|
end
|
|
333
451
|
|
|
334
452
|
context 'green stack is active' do
|
|
453
|
+
let(:strategy) { create_strategy(green: :active, blue: :inactive) }
|
|
454
|
+
|
|
335
455
|
it 'should warm up blue stack and cool down green stack' do
|
|
336
|
-
green_stack
|
|
337
|
-
blue_stack
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
456
|
+
active_stack = strategy.send(:green_stack)
|
|
457
|
+
inactive_stack = strategy.send(:blue_stack)
|
|
458
|
+
|
|
459
|
+
expect(strategy).to receive(:warm_up_stack).with(inactive_stack, active_stack, true)
|
|
460
|
+
expect(strategy).to receive(:cool_down).with(active_stack)
|
|
461
|
+
|
|
462
|
+
strategy.switch
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
context 'swap fails' do
|
|
466
|
+
context 'before blue stack becomes active' do
|
|
467
|
+
let(:error) { 'Error before inactive stack becomes active' }
|
|
468
|
+
|
|
469
|
+
it 'does not cool down any stack' do
|
|
470
|
+
expect(strategy).to receive(:warm_up_stack).and_raise(error)
|
|
471
|
+
expect(strategy).to_not receive(:cool_down)
|
|
472
|
+
|
|
473
|
+
ignore_errors { strategy.switch }
|
|
474
|
+
end
|
|
343
475
|
|
|
344
|
-
|
|
345
|
-
|
|
476
|
+
it 'reports the switch as a failure' do
|
|
477
|
+
expect(strategy).to receive(:warm_up_stack).and_raise(error)
|
|
478
|
+
allow(strategy).to receive(:cool_down)
|
|
479
|
+
|
|
480
|
+
expect { strategy.switch }.to raise_error(error)
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
context 'after both stacks became active' do
|
|
485
|
+
let(:error) { 'Error after inactive stack becomes active ' }
|
|
486
|
+
|
|
487
|
+
it 'cools down the blue stack' do
|
|
488
|
+
expect(strategy).to receive(:warm_up_stack) do
|
|
489
|
+
expect(strategy).to receive(:both_stacks_active?).and_return(true)
|
|
490
|
+
raise error
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
inactive_stack = strategy.send(:blue_stack)
|
|
494
|
+
expect(strategy).to receive(:cool_down).with(inactive_stack)
|
|
495
|
+
|
|
496
|
+
ignore_errors { strategy.switch }
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
it 'reports the switch as a failure' do
|
|
500
|
+
expect(strategy).to receive(:warm_up_stack) do
|
|
501
|
+
expect(strategy).to receive(:both_stacks_active?).and_return(true)
|
|
502
|
+
raise error
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
expect { strategy.switch }.to raise_error(error)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
context 'blue stack is active' do
|
|
512
|
+
let(:strategy) { create_strategy(green: :inactive, blue: :active) }
|
|
513
|
+
|
|
514
|
+
it 'should warm up green stack and cool down blue stack' do
|
|
515
|
+
active_stack = strategy.send(:blue_stack)
|
|
516
|
+
inactive_stack = strategy.send(:green_stack)
|
|
517
|
+
|
|
518
|
+
expect(strategy).to receive(:warm_up_stack).with(inactive_stack, active_stack, true)
|
|
519
|
+
expect(strategy).to receive(:cool_down).with(active_stack)
|
|
346
520
|
|
|
347
|
-
strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
|
|
348
521
|
strategy.switch
|
|
349
522
|
end
|
|
523
|
+
|
|
524
|
+
context 'swap fails' do
|
|
525
|
+
context 'before green stack becomes active' do
|
|
526
|
+
let(:error) { 'Error before inactive stack becomes active' }
|
|
527
|
+
|
|
528
|
+
it 'does not cool down any stack' do
|
|
529
|
+
expect(strategy).to receive(:warm_up_stack).and_raise(error)
|
|
530
|
+
expect(strategy).to_not receive(:cool_down)
|
|
531
|
+
|
|
532
|
+
ignore_errors { strategy.switch }
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
it 'reports the switch as a failure' do
|
|
536
|
+
expect(strategy).to receive(:warm_up_stack).and_raise(error)
|
|
537
|
+
allow(strategy).to receive(:cool_down)
|
|
538
|
+
|
|
539
|
+
expect { strategy.switch }.to raise_error(error)
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
context 'after both stacks become active' do
|
|
544
|
+
let(:error) { 'Error after inactive stack becomes active ' }
|
|
545
|
+
|
|
546
|
+
it 'cools down the green stack' do
|
|
547
|
+
expect(strategy).to receive(:warm_up_stack) do
|
|
548
|
+
expect(strategy).to receive(:both_stacks_active?).and_return(true)
|
|
549
|
+
raise error
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
inactive_stack = strategy.send(:green_stack)
|
|
553
|
+
expect(strategy).to receive(:cool_down).with(inactive_stack)
|
|
554
|
+
|
|
555
|
+
ignore_errors { strategy.switch }
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
it 'reports the switch as a failure' do
|
|
559
|
+
expect(strategy).to receive(:warm_up_stack) do
|
|
560
|
+
expect(strategy).to receive(:both_stacks_active?).and_return(true)
|
|
561
|
+
raise error
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
expect { strategy.switch }.to raise_error(error)
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
end
|
|
350
568
|
end
|
|
351
569
|
end
|
|
352
570
|
|
|
353
|
-
context '#
|
|
571
|
+
context '#cool_down' do
|
|
354
572
|
it 'should cool down only those ASGs which actually exist' do
|
|
355
573
|
blue_stack.live!
|
|
356
|
-
green_stack.die!
|
|
357
|
-
allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
|
|
358
574
|
allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
|
|
359
|
-
allow(green_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0 } }
|
|
360
575
|
allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
|
|
361
576
|
|
|
362
577
|
strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
|
|
363
578
|
expect(blue_asg_driver).to receive(:cool_down)
|
|
364
|
-
strategy.send(:
|
|
579
|
+
strategy.send(:cool_down, blue_stack)
|
|
365
580
|
end
|
|
366
581
|
end
|
|
367
582
|
|
|
@@ -488,4 +703,66 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
|
|
|
488
703
|
asg_swap.send(:stack_active?, blue_stack).should be(true)
|
|
489
704
|
end
|
|
490
705
|
end
|
|
706
|
+
|
|
707
|
+
def default_options
|
|
708
|
+
{
|
|
709
|
+
app_name: 'app',
|
|
710
|
+
environment: 'environment',
|
|
711
|
+
component: 'component',
|
|
712
|
+
context: {
|
|
713
|
+
:'deployment-strategy' => 'auto-scaling-group-swap',
|
|
714
|
+
:settings => {
|
|
715
|
+
:'auto-scaling-group-name-output' => ['AutoScalingGroupID']
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def create_strategy original_options = {}
|
|
722
|
+
options = default_options.merge(original_options)
|
|
723
|
+
|
|
724
|
+
create_stack(:blue, options.delete(:blue) || :dead, options)
|
|
725
|
+
create_stack(:green, options.delete(:green) || :dead, options)
|
|
726
|
+
|
|
727
|
+
CfDeployer::DeploymentStrategy.create(options[:app_name], options[:environment], options[:component], options[:context])
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
def create_stack color, status = :active, original_options = {}
|
|
731
|
+
options = default_options.merge(original_options)
|
|
732
|
+
|
|
733
|
+
stack = Fakes::Stack.new(name: color.to_s, outputs: {'web-elb-name' => "#{color}-elb"}, parameters: { name: color.to_s})
|
|
734
|
+
|
|
735
|
+
stack_color_name = (color.to_s == 'green' ? 'G' : 'B')
|
|
736
|
+
stack_name = "#{options[:app_name]}-#{options[:environment]}-#{options[:component]}-#{stack_color_name}"
|
|
737
|
+
allow(CfDeployer::Stack).to receive(:new).with(stack_name, options[:component], options[:context]).and_return(stack)
|
|
738
|
+
|
|
739
|
+
stack.tap do
|
|
740
|
+
case status
|
|
741
|
+
when :active; activate_stack(stack)
|
|
742
|
+
when :inactive; activate_stack(stack, { desired: 0, max: 0, min: 0 })
|
|
743
|
+
when :dead; kill_stack(stack)
|
|
744
|
+
else raise "Trying to create stack with unknown status; #{status}"
|
|
745
|
+
end
|
|
746
|
+
end
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
def activate_stack stack, instances = {}
|
|
750
|
+
stack.live!
|
|
751
|
+
|
|
752
|
+
allow(stack).to receive(:output).with('AutoScalingGroupID').and_return("#{stack.name}ASG")
|
|
753
|
+
allow(stack).to receive(:find_output).with('AutoScalingGroupID').and_return("#{stack.name}ASG")
|
|
754
|
+
allow(stack).to receive(:resource_statuses).and_return(asg_ids("#{stack.name}ASG"))
|
|
755
|
+
|
|
756
|
+
asg_driver = double("#{stack.name}_asg_driver")
|
|
757
|
+
instances[:desired] ||= 2
|
|
758
|
+
instances[:min] ||= 1
|
|
759
|
+
instances[:max] ||= 5
|
|
760
|
+
|
|
761
|
+
allow(asg_driver).to receive(:describe).and_return(instances)
|
|
762
|
+
allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with("#{stack.name}ASG") { asg_driver }
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
def kill_stack stack
|
|
766
|
+
stack.die!
|
|
767
|
+
end
|
|
491
768
|
end
|
|
@@ -27,79 +27,152 @@ describe 'Autoscaling group driver' do
|
|
|
27
27
|
|
|
28
28
|
describe '#warm_up' do
|
|
29
29
|
it 'should warm up the group to the desired size' do
|
|
30
|
-
expect(group).to receive(:auto_scaling_instances){[instance1, instance2]}
|
|
31
30
|
expect(group).to receive(:set_desired_capacity).with(2)
|
|
31
|
+
expect(@driver).to receive(:wait_for_desired_capacity)
|
|
32
32
|
@driver.warm_up 2
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it 'should wait for the warm up of the group even if desired is the same as the minimum' do
|
|
36
|
-
expect(group).to receive(:auto_scaling_instances){[instance2]}
|
|
37
36
|
expect(group).to receive(:set_desired_capacity).with(1)
|
|
37
|
+
expect(@driver).to receive(:wait_for_desired_capacity)
|
|
38
38
|
@driver.warm_up 1
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
it 'should ignore warming up if desired number is less than min size of the group' do
|
|
42
42
|
expect(group).not_to receive(:set_desired_capacity)
|
|
43
|
+
expect(@driver).not_to receive(:wait_for_desired_capacity)
|
|
43
44
|
@driver.warm_up 0
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
it 'should warm up to maximum if desired number is greater than maximum size of group' do
|
|
47
|
-
expect(group).to receive(:auto_scaling_instances){[instance1, instance2, instance3, instance4]}
|
|
48
48
|
expect(group).to receive(:set_desired_capacity).with(4)
|
|
49
|
+
expect(@driver).to receive(:wait_for_desired_capacity)
|
|
49
50
|
@driver.warm_up 5
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
53
|
|
|
53
|
-
describe '#
|
|
54
|
-
it '
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
describe '#healthy_instance_ids' do
|
|
55
|
+
it 'returns the ids of all instances that are healthy' do
|
|
56
|
+
instance1 = double('instance1', :health_status => 'HEALTHY', id: 'instance1')
|
|
57
|
+
instance2 = double('instance2', :health_status => 'HEALTHY', id: 'instance2')
|
|
58
|
+
instance3 = double('instance3', :health_status => 'UNHEALTHY', id: 'instance3')
|
|
59
|
+
instance4 = double('instance4', :health_status => 'HEALTHY', id: 'instance4')
|
|
60
|
+
allow(group).to receive(:auto_scaling_instances){[instance1, instance2, instance3, instance4]}
|
|
61
|
+
|
|
62
|
+
expect(@driver.healthy_instance_ids).to eql ['instance1', 'instance2', 'instance4']
|
|
58
63
|
end
|
|
59
64
|
|
|
60
|
-
it '
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
expect(@driver.
|
|
65
|
+
it 'returns the ids of all instances that are healthy (case insensitive)' do
|
|
66
|
+
instance1 = double('instance1', :health_status => 'HealThy', id: 'instance1')
|
|
67
|
+
allow(group).to receive(:auto_scaling_instances){[instance1]}
|
|
68
|
+
|
|
69
|
+
expect(@driver.healthy_instance_ids).to eql ['instance1']
|
|
65
70
|
end
|
|
71
|
+
end
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
allow(group).to receive(:load_balancers) { [load_balancer] }
|
|
72
|
-
allow(group).to receive(:auto_scaling_instances) { [instance1, instance2] }
|
|
73
|
+
describe '#in_service_instance_ids' do
|
|
74
|
+
context 'when there are no load balancers' do
|
|
75
|
+
it 'returns no ids' do
|
|
76
|
+
allow(group).to receive(:load_balancers).and_return([])
|
|
73
77
|
|
|
74
|
-
expect(@driver.
|
|
78
|
+
expect(@driver.in_service_instance_ids).to eq []
|
|
75
79
|
end
|
|
80
|
+
end
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
allow(group).to receive(:load_balancers) { [load_balancer] }
|
|
82
|
+
context 'when there is only 1 elb' do
|
|
83
|
+
it 'returns the ids of all instances that are in service' do
|
|
84
|
+
health1 = { state: 'InService', instance: double('i1', id: 'instance1') }
|
|
85
|
+
health2 = { state: 'OutOfService', instance: double('i2', id: 'instance2') }
|
|
86
|
+
health3 = { state: 'InService', instance: double('i3', id: 'instance3') }
|
|
87
|
+
health4 = { state: 'InService', instance: double('i4', id: 'instance4') }
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
instance_collection = double('instance_collection', health: [health1, health2, health3, health4])
|
|
90
|
+
elb = double('elb', instances: instance_collection)
|
|
91
|
+
allow(group).to receive(:load_balancers).and_return([ elb ])
|
|
92
|
+
|
|
93
|
+
expect(@driver.in_service_instance_ids).to eql ['instance1', 'instance3', 'instance4']
|
|
86
94
|
end
|
|
87
95
|
end
|
|
88
96
|
|
|
89
|
-
context 'when there are multiple elbs
|
|
90
|
-
it '
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
context 'when there are multiple elbs' do
|
|
98
|
+
it 'returns only the ids of instances that are in all ELBs' do
|
|
99
|
+
health1 = { state: 'InService', instance: double('i1', id: 'instance1') }
|
|
100
|
+
health2 = { state: 'InService', instance: double('i2', id: 'instance2') }
|
|
101
|
+
health3 = { state: 'InService', instance: double('i3', id: 'instance3') }
|
|
102
|
+
health4 = { state: 'InService', instance: double('i4', id: 'instance4') }
|
|
103
|
+
health5 = { state: 'InService', instance: double('i5', id: 'instance5') }
|
|
104
|
+
|
|
105
|
+
instance_collection1 = double('instance_collection1', health: [health1, health2, health3])
|
|
106
|
+
instance_collection2 = double('instance_collection2', health: [health2, health3, health4])
|
|
107
|
+
instance_collection3 = double('instance_collection3', health: [health2, health3, health5])
|
|
108
|
+
elb1 = double('elb1', instances: instance_collection1)
|
|
109
|
+
elb2 = double('elb2', instances: instance_collection2)
|
|
110
|
+
elb3 = double('elb3', instances: instance_collection3)
|
|
111
|
+
allow(group).to receive(:load_balancers).and_return([ elb1, elb2, elb3 ])
|
|
112
|
+
|
|
113
|
+
# Only instance 2 and 3 are associated with all ELB's
|
|
114
|
+
expect(@driver.in_service_instance_ids).to eql ['instance2', 'instance3']
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'returns only the ids instances that are InService in all ELBs' do
|
|
118
|
+
health11 = { state: 'OutOfService', instance: double('i1', id: 'instance1') }
|
|
119
|
+
health12 = { state: 'InService', instance: double('i2', id: 'instance2') }
|
|
120
|
+
health13 = { state: 'InService', instance: double('i3', id: 'instance3') }
|
|
121
|
+
|
|
122
|
+
health21 = { state: 'InService', instance: double('i1', id: 'instance1') }
|
|
123
|
+
health22 = { state: 'InService', instance: double('i2', id: 'instance2') }
|
|
124
|
+
health23 = { state: 'OutOfService', instance: double('i3', id: 'instance3') }
|
|
125
|
+
|
|
126
|
+
health31 = { state: 'InService', instance: double('i1', id: 'instance1') }
|
|
127
|
+
health32 = { state: 'InService', instance: double('i2', id: 'instance2') }
|
|
128
|
+
health33 = { state: 'InService', instance: double('i3', id: 'instance3') }
|
|
129
|
+
|
|
130
|
+
instance_collection1 = double('instance_collection1', health: [health11, health12, health13])
|
|
131
|
+
instance_collection2 = double('instance_collection2', health: [health21, health22, health23])
|
|
132
|
+
instance_collection3 = double('instance_collection3', health: [health31, health32, health33])
|
|
97
133
|
|
|
98
|
-
|
|
134
|
+
elb1 = double('elb1', instances: instance_collection1)
|
|
135
|
+
elb2 = double('elb2', instances: instance_collection2)
|
|
136
|
+
elb3 = double('elb3', instances: instance_collection3)
|
|
137
|
+
|
|
138
|
+
allow(group).to receive(:load_balancers).and_return([ elb1, elb2, elb3 ])
|
|
139
|
+
|
|
140
|
+
# Only instance 2 is InService across all ELB's
|
|
141
|
+
expect(@driver.in_service_instance_ids).to eql ['instance2']
|
|
99
142
|
end
|
|
100
143
|
end
|
|
101
144
|
end
|
|
102
145
|
|
|
146
|
+
describe '#healthy_instance_count' do
|
|
147
|
+
context 'when there are no load balancers' do
|
|
148
|
+
it 'should return the number of healthy instances' do
|
|
149
|
+
healthy_instance_ids = ['1', '3', '4', '5']
|
|
150
|
+
allow(@driver).to receive(:load_balancers).and_return([])
|
|
151
|
+
expect(@driver).to receive(:healthy_instance_ids).and_return(healthy_instance_ids)
|
|
152
|
+
|
|
153
|
+
expect(@driver.healthy_instance_count).to eql(healthy_instance_ids.count)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
context 'when load balancers exist' do
|
|
158
|
+
it 'should return the number of instances that are both healthy, and in service' do
|
|
159
|
+
healthy_instance_ids = ['1', '3', '4', '5']
|
|
160
|
+
in_service_instance_ids = ['3', '4']
|
|
161
|
+
allow(@driver).to receive(:load_balancers).and_return(double('elb', empty?: false))
|
|
162
|
+
expect(@driver).to receive(:healthy_instance_ids).and_return(healthy_instance_ids)
|
|
163
|
+
expect(@driver).to receive(:in_service_instance_ids).and_return(in_service_instance_ids)
|
|
164
|
+
|
|
165
|
+
# Only instances 3 and 4 are both healthy and in service
|
|
166
|
+
expect(@driver.healthy_instance_count).to eql(2)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'health check should be resilient against intermittent errors' do
|
|
171
|
+
expect(@driver).to receive(:healthy_instance_ids).and_raise("Some error")
|
|
172
|
+
expect(@driver.healthy_instance_count).to eql -1
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
103
176
|
describe '#cool_down' do
|
|
104
177
|
it 'should cool down group' do
|
|
105
178
|
expect(group).to receive(:update).with({min_size: 0, max_size: 0})
|
|
@@ -113,7 +186,7 @@ describe 'Autoscaling group driver' do
|
|
|
113
186
|
hash = {:max => 5, :min => 2, :desired => 3}
|
|
114
187
|
allow(group).to receive(:auto_scaling_instances){[instance1, instance2, instance3]}
|
|
115
188
|
expect(group).to receive(:update).with({:min_size => 2, :max_size => 5})
|
|
116
|
-
expect(
|
|
189
|
+
expect(@driver).to receive(:warm_up).with(3)
|
|
117
190
|
@driver.warm_up_cooled_group hash
|
|
118
191
|
end
|
|
119
192
|
end
|
|
@@ -131,4 +204,47 @@ describe 'Autoscaling group driver' do
|
|
|
131
204
|
expect(@driver.instance_statuses).to eq( { 'i-abcd1234' => returned_status } )
|
|
132
205
|
end
|
|
133
206
|
end
|
|
207
|
+
|
|
208
|
+
describe '#wait_for_desired_capacity' do
|
|
209
|
+
it 'completes if desired capacity reached' do
|
|
210
|
+
expect(@driver).to receive(:desired_capacity_reached?).and_return(true)
|
|
211
|
+
|
|
212
|
+
@driver.wait_for_desired_capacity
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'times out if desired capacity is not reached' do
|
|
216
|
+
expect(@driver).to receive(:desired_capacity_reached?).and_return(false)
|
|
217
|
+
|
|
218
|
+
expect { @driver.wait_for_desired_capacity }.to raise_error(Timeout::Error)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
describe '#desired_capacity_reached?' do
|
|
223
|
+
it 'returns true if healthy instance count matches desired capacity' do
|
|
224
|
+
expected_number = 5
|
|
225
|
+
|
|
226
|
+
expect(group).to receive(:desired_capacity).and_return(expected_number)
|
|
227
|
+
expect(@driver).to receive(:healthy_instance_count).and_return(expected_number)
|
|
228
|
+
|
|
229
|
+
expect(@driver.desired_capacity_reached?).to be_true
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it 'returns false if healthy instance count is less than desired capacity' do
|
|
233
|
+
expected_number = 5
|
|
234
|
+
|
|
235
|
+
expect(group).to receive(:desired_capacity).and_return(expected_number)
|
|
236
|
+
expect(@driver).to receive(:healthy_instance_count).and_return(expected_number - 1)
|
|
237
|
+
|
|
238
|
+
expect(@driver.desired_capacity_reached?).to be_false
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it 'returns true if healthy instance count is more than desired capacity' do
|
|
242
|
+
expected_number = 5
|
|
243
|
+
|
|
244
|
+
expect(group).to receive(:desired_capacity).and_return(expected_number)
|
|
245
|
+
expect(@driver).to receive(:healthy_instance_count).and_return(expected_number + 1)
|
|
246
|
+
|
|
247
|
+
expect(@driver.desired_capacity_reached?).to be_true
|
|
248
|
+
end
|
|
249
|
+
end
|
|
134
250
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cf_deployer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jame Brechtel
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2016-
|
|
14
|
+
date: 2016-10-04 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: aws-sdk
|
|
@@ -139,6 +139,34 @@ dependencies:
|
|
|
139
139
|
- - ~>
|
|
140
140
|
- !ruby/object:Gem::Version
|
|
141
141
|
version: 10.3.0
|
|
142
|
+
- !ruby/object:Gem::Dependency
|
|
143
|
+
name: webmock
|
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
|
145
|
+
requirements:
|
|
146
|
+
- - ~>
|
|
147
|
+
- !ruby/object:Gem::Version
|
|
148
|
+
version: 2.1.0
|
|
149
|
+
type: :development
|
|
150
|
+
prerelease: false
|
|
151
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
152
|
+
requirements:
|
|
153
|
+
- - ~>
|
|
154
|
+
- !ruby/object:Gem::Version
|
|
155
|
+
version: 2.1.0
|
|
156
|
+
- !ruby/object:Gem::Dependency
|
|
157
|
+
name: vcr
|
|
158
|
+
requirement: !ruby/object:Gem::Requirement
|
|
159
|
+
requirements:
|
|
160
|
+
- - ~>
|
|
161
|
+
- !ruby/object:Gem::Version
|
|
162
|
+
version: 2.9.3
|
|
163
|
+
type: :development
|
|
164
|
+
prerelease: false
|
|
165
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
166
|
+
requirements:
|
|
167
|
+
- - ~>
|
|
168
|
+
- !ruby/object:Gem::Version
|
|
169
|
+
version: 2.9.3
|
|
142
170
|
description: For automatic blue green deployment flow on CloudFormation.
|
|
143
171
|
email:
|
|
144
172
|
- jbrechtel@gmail.com
|
|
@@ -162,6 +190,7 @@ files:
|
|
|
162
190
|
- Rakefile
|
|
163
191
|
- bin/cf_deploy
|
|
164
192
|
- cf_deployer.gemspec
|
|
193
|
+
- fixtures/vcr_cassettes/aws_call_count/driver/auto_scaling_group/healthy_instance_count.yml
|
|
165
194
|
- lib/cf_deployer.rb
|
|
166
195
|
- lib/cf_deployer/application.rb
|
|
167
196
|
- lib/cf_deployer/application_error.rb
|
|
@@ -188,6 +217,8 @@ files:
|
|
|
188
217
|
- lib/cf_deployer/stack.rb
|
|
189
218
|
- lib/cf_deployer/status_presenter.rb
|
|
190
219
|
- lib/cf_deployer/version.rb
|
|
220
|
+
- spec/aws_call_count/driver/auto_scaling_group_spec.rb
|
|
221
|
+
- spec/aws_call_count_spec_helper.rb
|
|
191
222
|
- spec/fakes/instance.rb
|
|
192
223
|
- spec/fakes/route53_client.rb
|
|
193
224
|
- spec/fakes/stack.rb
|
|
@@ -240,6 +271,8 @@ specification_version: 4
|
|
|
240
271
|
summary: Support multiple components deployment using CloudFormation templates with
|
|
241
272
|
multiple blue green strategies.
|
|
242
273
|
test_files:
|
|
274
|
+
- spec/aws_call_count/driver/auto_scaling_group_spec.rb
|
|
275
|
+
- spec/aws_call_count_spec_helper.rb
|
|
243
276
|
- spec/fakes/instance.rb
|
|
244
277
|
- spec/fakes/route53_client.rb
|
|
245
278
|
- spec/fakes/stack.rb
|