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,294 @@
1
+ require 'spec_helper'
2
+
3
+ describe CfDeployer::DeploymentStrategy::CnameSwap do
4
+
5
+ let(:dns_driver) { double('route53') }
6
+ let(:elb_driver) { double('elb') }
7
+ let(:blue_asg_driver) { double('blue_asg_driver') }
8
+ let(:green_asg_driver) { double('green_asg_driver') }
9
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'web-elb-name' => 'BLUE-elb', 'AutoScalingGroupID' => 'blueASG'}, parameters: {:name => 'blue'}) }
10
+ let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'web-elb-name' => 'GREEN-elb', 'AutoScalingGroupID' => 'greenASG'}, parameters: {:name => 'green'}) }
11
+
12
+ before do
13
+ allow(Kernel).to receive(:sleep)
14
+ @context =
15
+ {
16
+ :'deployment-strategy' => 'cname-swap',
17
+ :dns_driver => dns_driver,
18
+ :elb_driver => elb_driver,
19
+ :settings => {
20
+ :'dns-fqdn' => 'test.foobar.com',
21
+ :'dns-zone' => 'foobar.com',
22
+ :'elb-name-output' => 'web-elb-name',
23
+ :'dns-driver' => CfDeployer::Defaults::DNSDriver
24
+ }
25
+ }
26
+
27
+ allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-web-B', 'web', anything()) { blue_stack }
28
+ allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-web-G', 'web', anything()) { green_stack }
29
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
30
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
31
+ allow(elb_driver).to receive(:find_dns_and_zone_id).with('BLUE-elb') { {:dns_name => 'blue-elb.aws.amazon.com', :canonical_hosted_zone_name_id => 'BLUE111'}}
32
+ allow(elb_driver).to receive(:find_dns_and_zone_id).with('GREEN-elb') { {:dns_name => 'green-elb.aws.amazon.com', :canonical_hosted_zone_name_id => 'GREEN111'}}
33
+ end
34
+
35
+ context "hooks" do
36
+ let(:before_destroy_hook) { double('before_destroy_hook') }
37
+ let(:after_create_hook) { double('after_create_hook') }
38
+ let(:after_swap_hook) { double('after_swap_hook') }
39
+
40
+ before :each do
41
+ allow(CfDeployer::Hook).to receive(:new).with(:'before-destroy', 'before-destroy'){ before_destroy_hook }
42
+ allow(CfDeployer::Hook).to receive(:new).with(:'after-create', 'after-create'){ after_create_hook }
43
+ allow(CfDeployer::Hook).to receive(:new).with(:'after-swap', 'after-swap'){ after_swap_hook }
44
+ allow(dns_driver).to receive(:set_alias_target)
45
+ @context[:'before-destroy'] = 'before-destroy'
46
+ @context[:'after-create'] = 'after-create'
47
+ @context[:'after-swap'] = 'after-swap'
48
+ end
49
+
50
+ it "should call hooks" do
51
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
52
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
53
+ expect(before_destroy_hook).to receive(:run).with(@context).twice
54
+ expect(after_create_hook).to receive(:run).with(@context)
55
+ expect(after_swap_hook).to receive(:run).with(@context)
56
+ cname_swap.deploy
57
+ end
58
+
59
+ it 'should call hooks when destroying green and blue stacks' do
60
+ @log = ''
61
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
62
+ allow(blue_stack).to receive(:delete)
63
+ allow(green_stack).to receive(:delete)
64
+ allow(before_destroy_hook).to receive(:run) do |arg|
65
+ @log += "#{arg[:parameters][:name]} deleted."
66
+ end
67
+ cname_swap.destroy
68
+ @log.should eq('green deleted.blue deleted.')
69
+ end
70
+ end
71
+
72
+ context "deploy" do
73
+
74
+ it "deploys green when blue is active" do
75
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
76
+ expect(dns_driver).to receive(:set_alias_target).with('foobar.com', 'test.foobar.com', 'GREEN111', 'green-elb.aws.amazon.com')
77
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
78
+ cname_swap.deploy
79
+
80
+ expect(green_stack).to be_deployed
81
+ expect(blue_stack).to_not be_deployed
82
+ end
83
+
84
+ it "deletes blue after deploying green" do
85
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
86
+ allow(dns_driver).to receive(:set_alias_target).with('foobar.com', 'test.foobar.com', 'GREEN111', 'green-elb.aws.amazon.com')
87
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
88
+ cname_swap.deploy
89
+
90
+ expect(blue_stack).to be_deleted
91
+ end
92
+
93
+ it "should not delete blue after deploying green if keep-previous-stack is specified" do
94
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
95
+ allow(dns_driver).to receive(:set_alias_target).with('foobar.com', 'test.foobar.com', 'GREEN111', 'green-elb.aws.amazon.com')
96
+ @context[:settings][:'keep-previous-stack'] = true
97
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
98
+ cname_swap.deploy
99
+
100
+ expect(blue_stack).to_not be_deleted
101
+ end
102
+
103
+ it "deploys blue when green is active" do
104
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'GREEN-elb.aws.amazon.com' }
105
+ expect(dns_driver).to receive(:set_alias_target).with('foobar.com', 'test.foobar.com', 'BLUE111', 'blue-elb.aws.amazon.com')
106
+
107
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
108
+
109
+ cname_swap.deploy
110
+
111
+ expect(blue_stack).to be_deployed
112
+ expect(green_stack).to_not be_deployed
113
+ end
114
+
115
+ it "deletes the inactive stack before deployment" do
116
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
117
+ allow(dns_driver).to receive(:set_alias_target).with('foobar.com', 'test.foobar.com', 'GREEN111', 'green-elb.aws.amazon.com')
118
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
119
+ cname_swap.deploy
120
+
121
+ expect(green_stack).to be_deleted
122
+ expect(green_stack).to be_deployed
123
+ end
124
+
125
+ it "does not delete the green-inactive stack before deployment if that stack does not exist" do
126
+ green_stack.die!
127
+
128
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
129
+ allow(dns_driver).to receive(:set_alias_target)
130
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
131
+ cname_swap.deploy
132
+
133
+ expect(green_stack).to_not be_deleted
134
+ end
135
+
136
+ it "does not delete the blue-inactive stack before deployment if that stack does not exist" do
137
+ blue_stack.die!
138
+
139
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'GREEN-elb.aws.amazon.com' }
140
+ allow(dns_driver).to receive(:set_alias_target)
141
+
142
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
143
+ cname_swap.deploy
144
+
145
+ expect(blue_stack).to_not be_deleted
146
+ end
147
+
148
+ it 'should warm up any auto scaling groups to previous colors levels' do
149
+ blue_stack.die!
150
+ green_stack.live!
151
+
152
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'GREEN-elb.aws.amazon.com' }
153
+ allow(dns_driver).to receive(:set_alias_target)
154
+ allow(green_asg_driver).to receive(:describe) { { desired: 3, min: 1, max: 5 } }
155
+ @context[:settings][:'auto-scaling-group-name-output'] = ['AutoScalingGroupID']
156
+ expect(blue_asg_driver).to receive(:warm_up).with 3
157
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
158
+ cname_swap.deploy
159
+ end
160
+
161
+ it 'should warm up any auto scaling groups to desired number when no previous color exists' do
162
+ blue_stack.die!
163
+ green_stack.die!
164
+
165
+ allow(dns_driver).to receive(:set_alias_target)
166
+ allow(blue_asg_driver).to receive(:describe) { { desired: 2, min: 1, max: 5 } }
167
+ @context[:settings][:'auto-scaling-group-name-output'] = ['AutoScalingGroupID']
168
+ expect(blue_asg_driver).to receive(:warm_up).with 2
169
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
170
+ cname_swap.deploy
171
+ end
172
+
173
+ it 'should not warm up if there are no auto scaling groups given' do
174
+ blue_stack.die!
175
+ green_stack.die!
176
+
177
+ allow(dns_driver).to receive(:set_alias_target)
178
+ allow(blue_asg_driver).to receive(:describe) { { desired: 2, min: 1, max: 5 } }
179
+ expect(blue_asg_driver).not_to receive(:warm_up)
180
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
181
+ cname_swap.deploy
182
+ end
183
+ end
184
+
185
+ context 'exists?' do
186
+ it 'no, if green stack and blue stack do not exist' do
187
+ blue_stack.die!
188
+ green_stack.die!
189
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
190
+ cname_swap.exists?.should be_false
191
+ end
192
+
193
+ it 'yes, if green stack exists and blue stack does not' do
194
+ blue_stack.die!
195
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
196
+ cname_swap.exists?.should be_true
197
+ end
198
+
199
+ it 'yes, if blue stack exists and green stack does not' do
200
+ green_stack.die!
201
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
202
+ cname_swap.exists?.should be_true
203
+ end
204
+ end
205
+
206
+ context '#destroy' do
207
+ it 'should destroy green and blue stacks' do
208
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
209
+ expect(blue_stack).to receive(:delete)
210
+ expect(green_stack).to receive(:delete)
211
+ cname_swap.destroy
212
+ end
213
+ end
214
+
215
+ context 'dns_driver' do
216
+ it 'should use a different driver class if the dns-driver setting is used' do
217
+ my_context = @context.clone
218
+ my_context.delete :dns_driver
219
+ my_context[:settings][:'dns-driver'] = 'CfDeployer::Driver::Verisign'
220
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', my_context)
221
+ cname_swap.send(:dns_driver).class.to_s.should eq(my_context[:settings][:'dns-driver'])
222
+ end
223
+ end
224
+
225
+ describe '#kill_inactive' do
226
+ let(:cname_swap) { CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context) }
227
+
228
+ context 'when blue stack is active' do
229
+ it 'should destroy the green stack' do
230
+ green_stack.live!
231
+ blue_stack.live!
232
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
233
+ expect(green_stack).to receive(:delete)
234
+ cname_swap.kill_inactive
235
+ end
236
+ end
237
+
238
+ context 'when green stack is active' do
239
+ it 'should destroy the blue stack' do
240
+ green_stack.live!
241
+ blue_stack.live!
242
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'GREEN-elb.aws.amazon.com' }
243
+ expect(blue_stack).to receive(:delete)
244
+ cname_swap.kill_inactive
245
+ end
246
+ end
247
+
248
+ context 'when green stack is active and blue stack does not exist' do
249
+ it 'should raise an error' do
250
+ green_stack.live!
251
+ blue_stack.die!
252
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'GREEN-elb.aws.amazon.com' }
253
+ expect(blue_stack).not_to receive(:delete)
254
+ expect { cname_swap.kill_inactive }.to raise_error CfDeployer::ApplicationError
255
+ end
256
+ end
257
+ end
258
+
259
+ describe '#switch' do
260
+ let(:cname_swap) { CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context) }
261
+ context 'if no inactive version exists' do
262
+ it 'should raise an error' do
263
+ green_stack.die!
264
+ blue_stack.live!
265
+ expect { cname_swap.switch }.to raise_error 'There is only one color stack active, you cannot switch back to a non-existent version'
266
+ end
267
+ end
268
+
269
+ context 'if an inactive version exists' do
270
+ it 'should swap the cname to the inactive version' do
271
+ green_stack.live!
272
+ blue_stack.live!
273
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
274
+ expect(dns_driver).to receive(:set_alias_target).with('foobar.com', 'test.foobar.com', 'GREEN111', 'green-elb.aws.amazon.com')
275
+ cname_swap.switch
276
+ end
277
+ end
278
+ end
279
+
280
+ context '#output_value' do
281
+
282
+ it 'should get stack output if active stack exists' do
283
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ 'BLUE-elb.aws.amazon.com' }
284
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
285
+ cname_swap.output_value("AutoScalingGroupID").should eq("blueASG")
286
+ end
287
+
288
+ it 'should get the information where the value comes from if the active stack does not exist' do
289
+ allow(dns_driver).to receive(:find_alias_target).with('foobar.com', 'test.foobar.com'){ '' }
290
+ cname_swap = CfDeployer::DeploymentStrategy.create('myapp', 'dev', 'web', @context)
291
+ cname_swap.output_value(:a_key).should eq("The value will be referenced from the output a_key of undeployed component web")
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'CreateOrUpdate Strategy' do
4
+ before :each do
5
+ @after_create_hook = double('after_create_hook')
6
+ @before_destroy_hook = double('before_destroy_hook')
7
+ allow(CfDeployer::Hook).to receive(:new).with(:'after-create', 'after-create'){ @after_create_hook }
8
+ allow(CfDeployer::Hook).to receive(:new).with(:'before-destroy', 'before-destroy') { @before_destroy_hook }
9
+ @context = {
10
+ :application => 'myApp',
11
+ :environment => 'uat',
12
+ :components =>
13
+ { :base => {
14
+ :settings => {},
15
+ :'deployment-strategy' => 'create-or-update',
16
+ :'after-create' => 'after-create',
17
+ :'before-destroy' => 'before-destroy'
18
+ }
19
+ }
20
+ }
21
+ @stack = double('stack')
22
+ @create_or_update = CfDeployer::DeploymentStrategy.create(@context[:application], @context[:environment], 'base', @context[:components][:base])
23
+ allow(CfDeployer::Stack).to receive(:new).with('myApp-uat-base','base', @context[:components][:base]){ @stack}
24
+ allow(@stack).to receive(:parameters){ {'vpc' => 'myvpc'}}
25
+ allow(@stack).to receive(:outputs){ {'ELBName' => 'myelb'}}
26
+ end
27
+
28
+ it 'should deploy stack' do
29
+ hook_context = nil
30
+ expect(@after_create_hook).to receive(:run) do |given_context|
31
+ hook_context = given_context
32
+ end
33
+ expect(@stack).to receive(:deploy)
34
+ @create_or_update.deploy
35
+ expect(hook_context[:parameters]).to eq( {'vpc' => 'myvpc'} )
36
+ expect(hook_context[:outputs]).to eq( {'ELBName' => 'myelb'} )
37
+
38
+ end
39
+
40
+ context 'warm up auto scaling group' do
41
+
42
+ let(:asg_driver) { double('asg_driver') }
43
+
44
+ it 'should warm up the stack if any auto-scaling groups are given' do
45
+ context = @context[:components][:base]
46
+ context[:settings] = {}
47
+ context[:settings][:'auto-scaling-group-name-output'] = ['AutoScalingGroupID']
48
+ allow(@stack).to receive(:output).with('AutoScalingGroupID') { 'asg_name' }
49
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('asg_name') { asg_driver }
50
+ allow(asg_driver).to receive(:describe) { {desired:2, min:1, max:3} }
51
+ allow(@after_create_hook).to receive(:run).with(anything)
52
+ allow(@stack).to receive(:deploy)
53
+ create_or_update = CfDeployer::DeploymentStrategy.create(@context[:application], @context[:environment], 'base', context)
54
+ expect(asg_driver).to receive(:warm_up).with 2
55
+ create_or_update.deploy
56
+ end
57
+ end
58
+
59
+ it 'should tell if stack exists' do
60
+ expect(@stack).to receive(:exists?){true}
61
+ @create_or_update.exists?.should eq(true)
62
+ end
63
+
64
+ it 'should get stack output' do
65
+ allow(@stack).to receive(:exists?){true}
66
+ expect(@stack).to receive(:output).with(:a_key){ "output_value" }
67
+ @create_or_update.output_value(:a_key).should eq("output_value")
68
+ end
69
+
70
+ it 'should get the information where the value comes from if the stack does not exist' do
71
+ allow(@stack).to receive(:exists?){false}
72
+ expect(@stack).not_to receive(:output).with(anything)
73
+ @create_or_update.output_value(:a_key).should eq("The value will be referenced from the output a_key of undeployed component base")
74
+ end
75
+
76
+ context '#destroy' do
77
+ it 'should destroy stack' do
78
+ allow(@stack).to receive(:exists?){ true}
79
+ allow(@stack).to receive(:parameters) { {}}
80
+ allow(@stack).to receive(:outputs) {{}}
81
+ expect(@before_destroy_hook).to receive(:run).with(anything)
82
+ expect(@stack).to receive(:delete)
83
+ @create_or_update.destroy
84
+ end
85
+ end
86
+
87
+ describe '#kill_inactive' do
88
+ it 'should raise an error' do
89
+ expect { @create_or_update.kill_inactive }.to raise_error CfDeployer::ApplicationError
90
+ end
91
+ end
92
+
93
+ context '#switch' do
94
+ it 'should raise an error' do
95
+ expect{ @create_or_update.switch }.to raise_error 'There is no inactive version to switch to for Create or Update Deployments. Redeploy the version you want'
96
+ end
97
+ end
98
+
99
+ context '#status' do
100
+ before :each do
101
+ allow(@stack).to receive(:status) { 'deployed' }
102
+ allow(@stack).to receive(:name) { 'base-uat' }
103
+ allow(@stack).to receive(:exists?) { true }
104
+ end
105
+ it 'should get status from stack' do
106
+ @create_or_update.status.should eq({ 'base-uat' => {status: 'deployed'}})
107
+ end
108
+ it 'should get status from stack including resource info' do
109
+ allow(@stack).to receive(:resource_statuses) { 'resource1' }
110
+ @create_or_update.status(true).should eq({ 'base-uat' => {status: 'deployed', resources: 'resource1'}})
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Deployment Strategy' do
4
+ before :each do
5
+ @context = {
6
+ application: 'myApp',
7
+ environment: 'uat',
8
+ components:
9
+ { base: {:'deployment-strategy' => 'create-or-update'},
10
+ db: {:'deployment-strategy' => 'auto-scaling-group-swap'},
11
+ web: { :'deployment-strategy' => 'cname-swap' }
12
+ }
13
+ }
14
+ end
15
+
16
+ it "should create Create-Or-Update strategy" do
17
+ expect(CfDeployer::DeploymentStrategy::CreateOrUpdate).to receive(:new)
18
+ CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'base', @context[:components][:base])
19
+ end
20
+
21
+ it "should create Auto-Scaling-Group-Swap strategy" do
22
+ expect(CfDeployer::DeploymentStrategy::AutoScalingGroupSwap).to receive(:new)
23
+ CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'db', @context[:components][:db])
24
+ end
25
+ it "should create Cname-Swap strategy" do
26
+ expect(CfDeployer::DeploymentStrategy::CnameSwap).to receive(:new)
27
+ CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', @context[:components][:web])
28
+ end
29
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Autoscaling group driver' do
4
+ let(:group) { double('group', :desired_capacity => 2, :min_size => 1, :max_size => 4)}
5
+ let(:scaling) { double('scaling', :groups => { 'myAsg' => group}) }
6
+ let(:ec2_instance1) { double('ec2_instance1') }
7
+ let(:ec2_instance2) { double('ec2_instance2') }
8
+ let(:ec2_instance3) { double('ec2_instance3') }
9
+ let(:ec2_instance4) { double('ec2_instance4') }
10
+ let(:instance1) { double('instance1', :health_status => 'HEALTHY', :ec2_instance => ec2_instance1)}
11
+ let(:instance2) { double('instance2', :health_status => 'HEALTHY', :ec2_instance => ec2_instance2)}
12
+ let(:instance3) { double('instance3', :health_status => 'HEALTHY', :ec2_instance => ec2_instance3)}
13
+ let(:instance4) { double('instance4', :health_status => 'HEALTHY', :ec2_instance => ec2_instance4)}
14
+ let(:load_balancer) { double('load_balancer', :instances => [instance1, instance2, instance3, instance4]) }
15
+
16
+ before :each do
17
+ allow(AWS::AutoScaling).to receive(:new) { scaling }
18
+ allow(group).to receive(:load_balancers) { [] }
19
+ allow(group).to receive(:auto_scaling_instances) { [] }
20
+ allow(group).to receive(:ec2_instances) { [] }
21
+ @driver = CfDeployer::Driver::AutoScalingGroup.new('myAsg', 1)
22
+ end
23
+
24
+ it 'should describe group' do
25
+ @driver.describe.should eq({ min: 1, max: 4, desired: 2})
26
+ end
27
+
28
+ describe '#warm_up' do
29
+ it 'should warm up the group to the desired size' do
30
+ expect(group).to receive(:auto_scaling_instances){[instance1, instance2]}
31
+ expect(group).to receive(:set_desired_capacity).with(2)
32
+ @driver.warm_up 2
33
+ end
34
+
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
+ expect(group).to receive(:set_desired_capacity).with(1)
38
+ @driver.warm_up 1
39
+ end
40
+
41
+ it 'should ignore warming up if desired number is less than min size of the group' do
42
+ expect(group).not_to receive(:set_desired_capacity)
43
+ @driver.warm_up 0
44
+ end
45
+
46
+ 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
+ expect(group).to receive(:set_desired_capacity).with(4)
49
+ @driver.warm_up 5
50
+ end
51
+ end
52
+
53
+ describe '#healthy_instance_count' do
54
+ it 'should respond with the number of instances that are HEALTHY' do
55
+ instance5 = double('instance1', :health_status => 'UNHEALTHY')
56
+ allow(group).to receive(:auto_scaling_instances){[instance1, instance2, instance3, instance4, instance5]}
57
+ expect(@driver.send(:healthy_instance_count)).to eql 4
58
+ end
59
+
60
+ context 'when an elb is associated with the auto scaling group' do
61
+ it 'should not include instances that are HEALTHY but not associated with the elb' do
62
+ instance_collection = double('instance_collection', :health => [{:instance => ec2_instance1, :state => 'InService'}])
63
+ load_balancer = double('load_balancer', :instances => instance_collection)
64
+ allow(group).to receive(:load_balancers) { [load_balancer] }
65
+ allow(group).to receive(:auto_scaling_instances) { [instance1, instance2] }
66
+
67
+ expect(@driver.send(:healthy_instance_count)).to eql 1
68
+ end
69
+
70
+ it 'should only include instances registered with an elb that are InService' do
71
+ allow(group).to receive(:auto_scaling_instances) { [instance1, instance2, instance3] }
72
+ instance_collection = double('instance_collection', :health => [{:instance => ec2_instance1, :state => 'InService'},
73
+ {:instance => ec2_instance2, :state => 'OutOfService'},
74
+ {:instance => ec2_instance3, :state => 'OutOfService'}])
75
+ load_balancer = double('load_balancer', :instances => instance_collection)
76
+ allow(group).to receive(:load_balancers) { [load_balancer] }
77
+
78
+ expect(@driver.send(:healthy_instance_count)).to eql 1
79
+ end
80
+ end
81
+
82
+ context 'when there are multiple elbs for an auto scaling group' do
83
+ it 'should not include instances that are not registered with all load balancers' do
84
+ instance_collection1 = double('instance_collection1', :health => [{:instance => ec2_instance1, :state => 'InService'}])
85
+ instance_collection2 = double('instance_collection2', :health => [])
86
+ load_balancer1 = double('load_balancer1', :instances => instance_collection1)
87
+ load_balancer2 = double('load_balancer2', :instances => instance_collection2)
88
+ allow(group).to receive(:load_balancers) { [load_balancer1, load_balancer2] }
89
+ allow(group).to receive(:auto_scaling_instances) { [instance1] }
90
+
91
+ expect(@driver.send(:healthy_instance_count)).to eql 0
92
+ end
93
+ end
94
+ end
95
+
96
+ describe '#cool_down' do
97
+ it 'should cool down group' do
98
+ expect(group).to receive(:update).with({min_size: 0, max_size: 0})
99
+ expect(group).to receive(:set_desired_capacity).with(0)
100
+ @driver.cool_down
101
+ end
102
+ end
103
+
104
+ describe '#warm_up_cooled_group' do
105
+ it 'should set min, max, and desired from a hash' do
106
+ hash = {:max => 5, :min => 2, :desired => 3}
107
+ allow(group).to receive(:auto_scaling_instances){[instance1, instance2, instance3]}
108
+ expect(group).to receive(:update).with({:min_size => 2, :max_size => 5})
109
+ expect(group).to receive(:set_desired_capacity).with(3)
110
+ @driver.warm_up_cooled_group hash
111
+ end
112
+ end
113
+
114
+ describe '#instance_statuses' do
115
+ it 'should get the status for any EC2 instances' do
116
+ aws_instance = double AWS::EC2::Instance
117
+ expect(aws_instance).to receive(:id) { 'i-abcd1234' }
118
+ allow(@driver).to receive(:ec2_instances) { [ aws_instance ] }
119
+
120
+ returned_status = { :status => :some_status }
121
+ cfd_instance = double CfDeployer::Driver::Instance
122
+ expect(CfDeployer::Driver::Instance).to receive(:new).with(aws_instance) { cfd_instance }
123
+ expect(cfd_instance).to receive(:status) { returned_status }
124
+ expect(@driver.instance_statuses).to eq( { 'i-abcd1234' => returned_status } )
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ describe 'CloudFormation' do
3
+ let(:outputs) { [output1, output2] }
4
+ let(:output1) { double('output1', :key => 'key1', :value => 'value1')}
5
+ let(:output2) { double('output2', :key => 'key2', :value => 'value2')}
6
+ let(:parameters) { double('parameters')}
7
+ let(:stack) { double('stack', :outputs => outputs, :parameters => parameters) }
8
+ let(:cloudFormation) {
9
+ double('cloudFormation',
10
+ :stacks =>
11
+ {'testStack' => stack
12
+ })
13
+ }
14
+
15
+ before(:each) do
16
+ allow(AWS::CloudFormation).to receive(:new) { cloudFormation }
17
+ end
18
+
19
+ it 'should get outputs of stack' do
20
+ CfDeployer::Driver::CloudFormation.new('testStack').outputs.should eq({'key1' => 'value1', 'key2' => 'value2'})
21
+ end
22
+
23
+ it 'should get parameters of stack' do
24
+ CfDeployer::Driver::CloudFormation.new('testStack').parameters.should eq(parameters)
25
+ end
26
+
27
+ context 'resource_statuses' do
28
+ it 'should be tested' do
29
+ false
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe CfDeployer::Driver::Elb do
4
+ it 'should get dns name and hosted zone id' do
5
+ elb = double('elb', :dns_name => 'mydns', :canonical_hosted_zone_name_id => 'zone_id')
6
+ aws = double('aws', :load_balancers => {'myelb' => elb})
7
+ elb_name = 'myelb'
8
+ expect(AWS::ELB).to receive(:new){aws}
9
+ CfDeployer::Driver::Elb.new.find_dns_and_zone_id(elb_name).should eq({:dns_name => 'mydns', :canonical_hosted_zone_name_id => 'zone_id'})
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe CfDeployer::Driver::Instance do
4
+ context '#status' do
5
+ it 'should build the right hash of instance info' do
6
+ expected = { :status => :pending,
7
+ :public_ip_address => '4.3.2.1',
8
+ :private_ip_address => '192.168.1.10',
9
+ :image_id => 'ami-testami',
10
+ :key_pair => 'test_pair'
11
+ }
12
+
13
+
14
+ aws = double AWS::EC2
15
+ expect(AWS::EC2).to receive(:new) { aws }
16
+
17
+ instance_collection = double AWS::EC2::InstanceCollection
18
+ expect(aws).to receive(:instances) { instance_collection }
19
+
20
+ instance = Fakes::Instance.new expected.merge( { :id => 'i-wxyz1234' } )
21
+ expect(instance_collection).to receive(:[]).with(instance.id) { instance }
22
+
23
+ instance_status = CfDeployer::Driver::Instance.new('i-wxyz1234').status
24
+
25
+ expected.each do |key, val|
26
+ expect(instance_status[key]).to eq(val)
27
+ end
28
+ end
29
+ end
30
+ end