cf_deployer 1.2.8

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.
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