cf_deployer 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/ChangeLog.md +16 -0
  4. data/DETAILS.md +268 -0
  5. data/FAQ.md +61 -0
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +51 -0
  8. data/LICENSE +22 -0
  9. data/QUICKSTART.md +96 -0
  10. data/README.md +36 -0
  11. data/Rakefile +32 -0
  12. data/bin/cf_deploy +10 -0
  13. data/cf_deployer.gemspec +23 -0
  14. data/lib/cf_deployer/application.rb +74 -0
  15. data/lib/cf_deployer/application_error.rb +4 -0
  16. data/lib/cf_deployer/aws_constants.rb +3 -0
  17. data/lib/cf_deployer/cli.rb +111 -0
  18. data/lib/cf_deployer/component.rb +103 -0
  19. data/lib/cf_deployer/config_loader.rb +189 -0
  20. data/lib/cf_deployer/config_validation.rb +138 -0
  21. data/lib/cf_deployer/defaults.rb +10 -0
  22. data/lib/cf_deployer/deployment_strategy/auto_scaling_group_swap.rb +102 -0
  23. data/lib/cf_deployer/deployment_strategy/base.rb +88 -0
  24. data/lib/cf_deployer/deployment_strategy/blue_green.rb +70 -0
  25. data/lib/cf_deployer/deployment_strategy/cname_swap.rb +108 -0
  26. data/lib/cf_deployer/deployment_strategy/create_or_update.rb +57 -0
  27. data/lib/cf_deployer/driver/auto_scaling_group.rb +86 -0
  28. data/lib/cf_deployer/driver/cloud_formation_driver.rb +85 -0
  29. data/lib/cf_deployer/driver/dry_run.rb +27 -0
  30. data/lib/cf_deployer/driver/elb_driver.rb +17 -0
  31. data/lib/cf_deployer/driver/instance.rb +29 -0
  32. data/lib/cf_deployer/driver/route53_driver.rb +79 -0
  33. data/lib/cf_deployer/driver/verisign_driver.rb +21 -0
  34. data/lib/cf_deployer/hook.rb +32 -0
  35. data/lib/cf_deployer/logger.rb +34 -0
  36. data/lib/cf_deployer/stack.rb +154 -0
  37. data/lib/cf_deployer/status_presenter.rb +195 -0
  38. data/lib/cf_deployer/version.rb +3 -0
  39. data/lib/cf_deployer.rb +97 -0
  40. data/spec/fakes/instance.rb +32 -0
  41. data/spec/fakes/route53_client.rb +23 -0
  42. data/spec/fakes/stack.rb +65 -0
  43. data/spec/functional/deploy_spec.rb +73 -0
  44. data/spec/functional/kill_inactive_spec.rb +57 -0
  45. data/spec/functional_spec_helper.rb +3 -0
  46. data/spec/spec_helper.rb +8 -0
  47. data/spec/unit/application_spec.rb +191 -0
  48. data/spec/unit/component_spec.rb +142 -0
  49. data/spec/unit/config_loader_spec.rb +356 -0
  50. data/spec/unit/config_validation_spec.rb +480 -0
  51. data/spec/unit/deployment_strategy/auto_scaling_group_swap_spec.rb +435 -0
  52. data/spec/unit/deployment_strategy/base_spec.rb +44 -0
  53. data/spec/unit/deployment_strategy/cname_swap_spec.rb +294 -0
  54. data/spec/unit/deployment_strategy/create_or_update_spec.rb +113 -0
  55. data/spec/unit/deployment_strategy/deployment_strategy_spec.rb +29 -0
  56. data/spec/unit/driver/auto_scaling_group_spec.rb +127 -0
  57. data/spec/unit/driver/cloud_formation_spec.rb +32 -0
  58. data/spec/unit/driver/elb_spec.rb +11 -0
  59. data/spec/unit/driver/instance_spec.rb +30 -0
  60. data/spec/unit/driver/route53_spec.rb +85 -0
  61. data/spec/unit/driver/verisign_spec.rb +18 -0
  62. data/spec/unit/hook_spec.rb +64 -0
  63. data/spec/unit/stack_spec.rb +150 -0
  64. data/spec/unit/status_presenter_spec.rb +108 -0
  65. metadata +197 -0
@@ -0,0 +1,435 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Auto Scaling Group Swap Deployment Strategy' do
4
+ let(:app) { 'myapp' }
5
+ let(:env) { 'dev' }
6
+ let(:component) { 'worker' }
7
+
8
+ let(:context) {
9
+ {
10
+ :'deployment-strategy' => 'auto-scaling-group-swap',
11
+ :settings => {
12
+ :'auto-scaling-group-name-output' => ['AutoScalingGroupID']
13
+ }
14
+ }
15
+ }
16
+
17
+ let(:blue_asg_driver) { double('blue_asg_driver') }
18
+ let(:green_asg_driver) { double('green_asg_driver') }
19
+
20
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'web-elb-name' => 'BLUE-elb'}, parameters: { name: 'blue'}) }
21
+ let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'web-elb-name' => 'GREEN-elb'}, parameters: { name: 'green'}) }
22
+
23
+ before :each do
24
+ allow(blue_stack).to receive(:output).with('AutoScalingGroupID'){'blueASG'}
25
+ allow(green_stack).to receive(:output).with('AutoScalingGroupID'){'greenASG'}
26
+ allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-worker-B', 'worker', context) { blue_stack }
27
+ allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-worker-G', 'worker', context) { green_stack }
28
+ end
29
+
30
+ context 'component exists?' do
31
+ it 'no if no G and B stacks exist' do
32
+ blue_stack.die!
33
+ green_stack.die!
34
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).exists?.should be_false
35
+ end
36
+
37
+ it 'yes if B stacks exist' do
38
+ blue_stack.live!
39
+ green_stack.die!
40
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).exists?.should be_true
41
+ end
42
+ it 'yes if G stacks exist' do
43
+ blue_stack.die!
44
+ green_stack.live!
45
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).exists?.should be_true
46
+ end
47
+
48
+ end
49
+
50
+ context 'has no active group' do
51
+ it 'should deploy blue stack and warm up if green stack does not exist' do
52
+ blue_stack.live!
53
+ green_stack.die!
54
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
55
+ allow(blue_asg_driver).to receive(:describe).and_return({desired: 0, min: 0, max: 0}, {desired: 1, min: 1, max: 1})
56
+ expect(blue_asg_driver).to receive(:warm_up)
57
+ expect(blue_stack).to receive(:delete)
58
+ expect(blue_stack).to receive(:deploy)
59
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
60
+ end
61
+
62
+ it 'should deploy blue stack if green stack is not active' do
63
+ blue_stack.die!
64
+ green_stack.live!
65
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
66
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
67
+ allow(green_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0} }
68
+ allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 2} }
69
+ expect(blue_stack).to receive(:deploy)
70
+ allow(blue_asg_driver).to receive(:warm_up)
71
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
72
+ end
73
+ end
74
+
75
+ context 'hooks' do
76
+ let(:before_destroy_hook) { double('before_destroy_hook') }
77
+ let(:after_create_hook) { double('after_create_hook') }
78
+ let(:after_swap_hook) { double('after_swap_hook') }
79
+
80
+ before :each do
81
+ allow(CfDeployer::Hook).to receive(:new).with(:'before-destroy', 'before-destroy'){ before_destroy_hook }
82
+ allow(CfDeployer::Hook).to receive(:new).with(:'after-create', 'after-create'){ after_create_hook }
83
+ allow(CfDeployer::Hook).to receive(:new).with(:'after-swap', 'after-swap'){ after_swap_hook }
84
+ context[:'before-destroy'] = 'before-destroy'
85
+ context[:'after-create'] = 'after-create'
86
+ context[:'after-swap'] = 'after-swap'
87
+ end
88
+
89
+ it 'should call hooks when deploying' do
90
+ blue_stack.live!
91
+ green_stack.live!
92
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
93
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
94
+ allow(blue_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
95
+ allow(green_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
96
+ allow(blue_stack).to receive(:delete)
97
+ allow(blue_stack).to receive(:deploy)
98
+ allow(blue_asg_driver).to receive(:warm_up).with(2)
99
+ allow(green_asg_driver).to receive(:cool_down)
100
+ expect(before_destroy_hook).to receive(:run).with(context).twice
101
+ expect(after_create_hook).to receive(:run).with(context)
102
+ expect(after_swap_hook).to receive(:run).with(context)
103
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
104
+ expect(context[:parameters]).to eq({ name: 'green'})
105
+ expect(context[:outputs]).to eq({ "web-elb-name" => 'GREEN-elb'})
106
+ end
107
+
108
+ it 'should call hooks when destroying' do
109
+ @log = ''
110
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
111
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
112
+ allow(blue_asg_driver).to receive(:describe) {{desired: 1, min: 1, max: 2}}
113
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
114
+ green_stack.live!
115
+ blue_stack.live!
116
+ allow(green_stack).to receive(:delete)
117
+ allow(blue_stack).to receive(:delete)
118
+ allow(before_destroy_hook).to receive(:run) do |arg|
119
+ @log += "#{arg[:parameters][:name]} deleted."
120
+ end
121
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).destroy
122
+ @log.should eq('green deleted.blue deleted.')
123
+ end
124
+ end
125
+
126
+ context 'has active group' do
127
+ it 'should deploy blue stack if green stack is active' do
128
+ blue_stack.live!
129
+ green_stack.live!
130
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
131
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
132
+ allow(blue_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
133
+ allow(green_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
134
+ expect(blue_stack).to receive(:delete)
135
+ expect(blue_stack).to receive(:deploy)
136
+ expect(blue_asg_driver).to receive(:warm_up).with(2)
137
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
138
+ end
139
+
140
+ it 'should deploy green stack if blue stack is active' do
141
+ blue_stack.live!
142
+ green_stack.live!
143
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
144
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
145
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
146
+ allow(blue_asg_driver).to receive(:describe) {{desired: 3, min: 1, max: 5}}
147
+ expect(green_stack).to receive(:delete)
148
+ expect(green_stack).to receive(:deploy)
149
+ expect(green_asg_driver).to receive(:warm_up)
150
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
151
+ end
152
+
153
+ it 'should delete blue stack after deploying green' do
154
+ blue_stack.live!
155
+ green_stack.live!
156
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
157
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
158
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
159
+ allow(blue_asg_driver).to receive(:describe) {{desired: 3, min: 1, max: 5}}
160
+ allow(green_asg_driver).to receive(:warm_up)
161
+ expect(blue_asg_driver).not_to receive(:cool_down)
162
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
163
+
164
+ expect(blue_stack).to be_deleted
165
+ end
166
+
167
+ it 'should not delete blue stack after deploying green if keep-previous-stack is specified' do
168
+ blue_stack.live!
169
+ green_stack.live!
170
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
171
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
172
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
173
+ allow(blue_asg_driver).to receive(:describe) {{desired: 3, min: 1, max: 5}}
174
+ allow(green_asg_driver).to receive(:warm_up)
175
+ expect(blue_asg_driver).to receive(:cool_down)
176
+ context[:settings][:'keep-previous-stack'] = true
177
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
178
+
179
+ expect(blue_stack).not_to be_deleted
180
+ end
181
+
182
+
183
+ it 'should get error if both blue and green stacks are active' do
184
+ blue_stack.live!
185
+ green_stack.live!
186
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
187
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
188
+ allow(green_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 3}}
189
+ allow(blue_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
190
+ expect(blue_stack).not_to receive(:delete)
191
+ expect(green_stack).not_to receive(:delete)
192
+ expect(blue_stack).not_to receive(:deploy)
193
+ expect(green_stack).not_to receive(:deploy)
194
+ expect{ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy}.to raise_error("Found both auto-scaling-groups, [\"greenASG\", \"blueASG\"], in green and blue stacks are active. Deployment aborted!")
195
+ end
196
+
197
+ context 'multiple ASG' do
198
+ let(:context) {
199
+ {
200
+ :'deployment-strategy' => 'auto-scaling-group-swap',
201
+ :settings => {
202
+ :'auto-scaling-group-name-output' => ['AutoScalingGroupID', 'AlternateASGID']
203
+ }
204
+ }
205
+ }
206
+
207
+ it 'should get error containing only "active" ASG if both blue and green stacks are active' do
208
+ allow(blue_stack).to receive(:output).with('AlternateASGID'){'AltblueASG'}
209
+ allow(green_stack).to receive(:output).with('AlternateASGID'){'AltgreenASG'}
210
+ blue_stack.live!
211
+ green_stack.live!
212
+ alt_blue_asg_driver = double('alt_blue_asg_driver')
213
+ alt_green_asg_driver = double('alt_green_asg_driver')
214
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('AltblueASG') { alt_blue_asg_driver }
215
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
216
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('AltgreenASG') { alt_green_asg_driver }
217
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
218
+ allow(alt_blue_asg_driver).to receive(:describe) {{desired: 1, min: 1, max: 2}}
219
+ allow(alt_green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
220
+ allow(blue_asg_driver).to receive(:describe) {{desired: 1, min: 1, max: 2}}
221
+ allow(green_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
222
+ expect(blue_stack).not_to receive(:delete)
223
+ expect(green_stack).not_to receive(:delete)
224
+ expect(blue_stack).not_to receive(:deploy)
225
+ expect(green_stack).not_to receive(:deploy)
226
+ expect{ CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy}.to raise_error(CfDeployer::DeploymentStrategy::AutoScalingGroupSwap::BothStacksActiveError, "Found both auto-scaling-groups, [\"greenASG\", \"blueASG\", \"AltblueASG\"], in green and blue stacks are active. Deployment aborted!")
227
+ end
228
+ end
229
+ end
230
+
231
+ it 'should delete stacks' do
232
+ green_stack.live!
233
+ blue_stack.live!
234
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
235
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
236
+ allow(green_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0 } }
237
+ allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
238
+ expect(green_stack).to receive(:delete)
239
+ expect(blue_stack).to receive(:delete)
240
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).destroy
241
+ end
242
+
243
+ describe '#kill_inactive' do
244
+ context 'when blue stack is active' do
245
+ it 'should kill the green stack' do
246
+ green_stack.live!
247
+ blue_stack.live!
248
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
249
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
250
+ allow(green_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0 } }
251
+ allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
252
+ expect(green_stack).to receive(:delete)
253
+ expect(blue_stack).not_to receive(:delete)
254
+
255
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).kill_inactive
256
+ end
257
+ end
258
+
259
+ context 'when green stack is active' do
260
+ it 'should kill the blue stack' do
261
+ green_stack.live!
262
+ blue_stack.live!
263
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
264
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
265
+ allow(blue_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0 } }
266
+ allow(green_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
267
+ expect(blue_stack).to receive(:delete)
268
+ expect(green_stack).not_to receive(:delete)
269
+
270
+ CfDeployer::DeploymentStrategy.create(app, env, component, context).kill_inactive
271
+ end
272
+ end
273
+
274
+ context 'when only one stack exists' do
275
+ it 'should raise an error' do
276
+ green_stack.live!
277
+ blue_stack.die!
278
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
279
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
280
+ allow(blue_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0 } }
281
+ allow(green_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
282
+
283
+ expect {CfDeployer::DeploymentStrategy.create(app, env, component, context).kill_inactive }.to raise_error CfDeployer::ApplicationError
284
+ end
285
+ end
286
+
287
+ context 'when both stacks are active' do
288
+ it 'should raise an error' do
289
+ green_stack.live!
290
+ blue_stack.live!
291
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
292
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
293
+ allow(blue_asg_driver).to receive(:describe) { {desired: 2, min: 1, max: 3 } }
294
+ allow(green_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
295
+
296
+ expect {CfDeployer::DeploymentStrategy.create(app, env, component, context).kill_inactive }.to raise_error CfDeployer::DeploymentStrategy::AutoScalingGroupSwap::BothStacksActiveError
297
+ end
298
+ end
299
+ end
300
+
301
+ describe '#switch' do
302
+ context 'both stacks are active' do
303
+ it 'should raise an error' do
304
+ green_stack.live!
305
+ blue_stack.live!
306
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
307
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
308
+ allow(green_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
309
+ allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
310
+
311
+ strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
312
+ expect{strategy.switch}.to raise_error 'Found both auto-scaling-groups, ["greenASG", "blueASG"], in green and blue stacks are active. Switch aborted!'
313
+ end
314
+ end
315
+
316
+ context 'both stacks do not exist' do
317
+ it 'should raise an error' do
318
+ green_stack.live!
319
+ blue_stack.die!
320
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
321
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
322
+ allow(green_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
323
+
324
+ strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
325
+ expect{ strategy.switch }.to raise_error 'Only one color stack exists, cannot switch to a non-existent version!'
326
+ end
327
+ end
328
+
329
+ context 'green stack is active' do
330
+ it 'should warm up blue stack and cool down green stack' do
331
+ green_stack.live!
332
+ blue_stack.live!
333
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
334
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
335
+ options = {desired: 5, min: 3, max: 7}
336
+ allow(green_asg_driver).to receive(:describe) {options}
337
+ allow(blue_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
338
+
339
+ expect(blue_asg_driver).to receive(:warm_up_cooled_group).with(options)
340
+ expect(green_asg_driver).to receive(:cool_down)
341
+
342
+ strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
343
+ strategy.switch
344
+ end
345
+ end
346
+ end
347
+
348
+ describe '#asg_driver' do
349
+ it 'returns the same driver for the same aws_group_name' do
350
+ strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
351
+ expect(strategy.send(:asg_driver, 'myAsg')).to eql(strategy.send(:asg_driver, 'myAsg'))
352
+ end
353
+
354
+ it 'returns a different driver for a different aws_group_name' do
355
+ strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
356
+ expect(strategy.send(:asg_driver, 'myAsg')).not_to eql(strategy.send(:asg_driver, 'different'))
357
+ end
358
+ end
359
+
360
+ context '#output_value' do
361
+
362
+ it 'should get stack output if active stack exists' do
363
+ blue_stack.live!
364
+ green_stack.live!
365
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
366
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
367
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
368
+ allow(blue_asg_driver).to receive(:describe) {{desired: 3, min: 1, max: 5}}
369
+ asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
370
+ asg_swap.output_value("AutoScalingGroupID").should eq("blueASG")
371
+ end
372
+
373
+ it 'should get the information where the value comes from if the active stack does not exist' do
374
+ blue_stack.live!
375
+ green_stack.live!
376
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
377
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
378
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
379
+ allow(blue_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
380
+ asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
381
+ asg_swap.output_value(:a_key).should eq("The value will be referenced from the output a_key of undeployed component worker")
382
+ end
383
+ end
384
+
385
+ context '#status' do
386
+ before :each do
387
+ blue_stack.live!
388
+ green_stack.live!
389
+ allow(blue_stack).to receive(:status) { 'blue deployed' }
390
+ allow(green_stack).to receive(:status) { 'green deployed' }
391
+ allow(blue_stack).to receive(:resource_statuses) { 'blue resources' }
392
+ allow(green_stack).to receive(:resource_statuses) { 'green resources' }
393
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
394
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
395
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
396
+ allow(blue_asg_driver).to receive(:describe) {{desired: 3, min: 1, max: 5}}
397
+ asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
398
+
399
+ end
400
+
401
+ it 'should get status for both green and blue stacks' do
402
+ asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
403
+ expected_result = {
404
+ 'BLUE' => {
405
+ :active => true,
406
+ :status => 'blue deployed'
407
+ },
408
+ 'GREEN' => {
409
+ :active => false,
410
+ :status => 'green deployed'
411
+ }
412
+ }
413
+ asg_swap.status.should eq(expected_result)
414
+ end
415
+
416
+ it 'should get status for both green and blue stacks including resources info' do
417
+ asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
418
+ expected_result = {
419
+ 'BLUE' => {
420
+ :active => true,
421
+ :status => 'blue deployed',
422
+ :resources => 'blue resources'
423
+ },
424
+ 'GREEN' => {
425
+ :active => false,
426
+ :status => 'green deployed',
427
+ :resources => 'green resources'
428
+ }
429
+ }
430
+ asg_swap.status(true).should eq(expected_result)
431
+ end
432
+
433
+
434
+ end
435
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Base Deployment Strategy' do
4
+ let(:app) { 'myapp' }
5
+ let(:env) { 'dev' }
6
+ let(:component) { 'worker' }
7
+
8
+ let(:context) {
9
+ {
10
+ :'deployment-strategy' => 'base',
11
+ :settings => {
12
+ :'auto-scaling-group-name-output' => ['AutoScalingGroupID']
13
+ }
14
+ }
15
+ }
16
+
17
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'web-elb-name' => 'BLUE-elb'}, status: :ready) }
18
+ let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'web-elb-name' => 'GREEN-elb'}, status: :does_not_exist, exists?: false) }
19
+ let(:white_stack) { Fakes::Stack.new(name: 'WHITE', outputs: {'web-elb-name' => 'WHITE-elb'}, status: :exists) }
20
+
21
+ before do
22
+ @strategy = CfDeployer::DeploymentStrategy::Base.new app, env, component, context
23
+ allow(@strategy).to receive(:blue_stack) { blue_stack }
24
+ allow(@strategy).to receive(:green_stack) { green_stack }
25
+ allow(@strategy).to receive(:stack_active?).with(blue_stack) { true }
26
+ allow(@strategy).to receive(:stack_active?).with(green_stack) { false }
27
+ end
28
+
29
+
30
+ describe '.create' do
31
+ it "should pass back a new strategy object" do
32
+ new_context = context.merge( { :'deployment-strategy' => 'cname-swap' } )
33
+ expect(CfDeployer::DeploymentStrategy::CnameSwap).to receive(:new)
34
+ my_strategy = CfDeployer::DeploymentStrategy.create app, env, component, new_context
35
+ end
36
+
37
+ it "should raise if the specified strategy doesn't exist" do
38
+ new_context = context.merge( { :'deployment-strategy' => 'invade-russia' } )
39
+ expect {
40
+ CfDeployer::DeploymentStrategy.create app, env, component, new_context
41
+ }.to raise_error(CfDeployer::ApplicationError)
42
+ end
43
+ end
44
+ end