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