cf_deployer 1.3.8 → 1.3.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 99837edd085628d600239c517e9384c05dd2186e
4
- data.tar.gz: ff5b6d33be922e3aeaa0f609991f799e0064e623
3
+ metadata.gz: 6d5da90ce16ae1b8de132cba6d1359e3a643d4c9
4
+ data.tar.gz: 8d34ac5950b1ce737da92f384c86dc72a0b8128c
5
5
  SHA512:
6
- metadata.gz: 7ba8da26ac2cfb06a31fa2acff9ff3fce51c2a2328c79dc6c9542d78b9741d84bf8296e10945eb5a50b631014ae9c9ea8463ee13ab0d2ff6a6ccd7fbd1714941
7
- data.tar.gz: 8b18b73a9110ba2d0dbcb953207770e2d83dd92bfcbf72db7a4a7a855f62042ea035141ff7f4a6523871166f8a214c3a594cd8c4d8e8a2f8eb7b9b87ce08308d
6
+ metadata.gz: 05f26ca292c10038313d724d7a24e5b875179aa41f986fa0b757bd7ea16038cd65fa585950bf18282bde4db042dede93dbc7e2d5fe1b6c1f3749bdceab967f12
7
+ data.tar.gz: 843e008b514878929b1a1d2802ae2b42b5fe638acc9898f7ca6bdb0d11af0c52d8db541fad2c55f331988dac1ac2b57cd25ca866ccd322607ea3a284ed885635
data/.gitignore CHANGED
@@ -28,3 +28,5 @@ tags
28
28
  .rbenv-version
29
29
  .ruby-version
30
30
  Gemfile.lock
31
+ .idea
32
+ *.iml
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.7
6
+ - 2.2.3
7
+ - ruby-head
8
+ - jruby-19mode
9
+ - jruby-9.0.1.0
10
+ - rbx-2
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: ruby-head
14
+ - rvm: rbx-2
15
+ - rvm: jruby-9.0.1.0
16
+ fast_finish: true
17
+ script:
18
+ - bundle exec rake
data/ChangeLog.md CHANGED
@@ -46,3 +46,7 @@ version 1.3.7
46
46
  version 1.3.8
47
47
  - Moved dependencies out of Gemfile and into gemspec
48
48
  - Removed Gemfile.lock
49
+
50
+ version 1.3.9
51
+ - Allow new ASGs to be added to template (See: https://github.com/manheim/cf_deployer/issues/31)
52
+
data/README.md CHANGED
@@ -1,3 +1,8 @@
1
+ [![Gem Version](https://badge.fury.io/rb/cf_deployer.svg)](https://badge.fury.io/rb/cf_deployer)
2
+ [![Build Status](https://travis-ci.org/manheim/cf_deployer.svg?branch=master)](https://travis-ci.org/manheim/cf_deployer)
3
+ [![Code Climate](https://codeclimate.com/github/manheim/cf_deployer/badges/gpa.svg)](https://codeclimate.com/github/manheim/cf_deployer)
4
+ [![License](http://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+
1
6
  ##### [README](README.md) - [QUICKSTART](QUICKSTART.md) - [DETAILS](DETAILS.md) - [FAQ](FAQ.md)
2
7
 
3
8
  CFDeployer
@@ -2,7 +2,6 @@ module CfDeployer
2
2
  module DeploymentStrategy
3
3
  class AutoScalingGroupSwap < BlueGreen
4
4
 
5
-
6
5
  def deploy
7
6
  check_blue_green_not_both_active 'Deployment'
8
7
  Log.info "Found active stack #{active_stack.name}" if active_stack
@@ -15,7 +14,6 @@ module CfDeployer
15
14
  Log.info "#{component_name} deployed successfully"
16
15
  end
17
16
 
18
-
19
17
  def kill_inactive
20
18
  check_blue_green_not_both_active 'Kill Inactive'
21
19
  raise ApplicationError.new('Only one color stack exists, cannot kill a non-existant version!') unless both_stacks_exist?
@@ -25,15 +23,13 @@ module CfDeployer
25
23
  def switch
26
24
  check_blue_green_not_both_active 'Switch'
27
25
  raise ApplicationError.new('Only one color stack exists, cannot switch to a non-existent version!') unless both_stacks_exist?
28
- warm_up_cooled = true
29
- swap_group warm_up_cooled
26
+ swap_group true
30
27
  end
31
28
 
32
29
  private
33
30
 
34
-
35
31
  def check_blue_green_not_both_active action
36
- active_stacks = get_active_asg(active_stack) + get_active_asg(inactive_stack)
32
+ active_stacks = get_active_asgs(active_stack) + get_active_asgs(inactive_stack)
37
33
  raise BothStacksActiveError.new("Found both auto-scaling-groups, #{active_stacks}, in green and blue stacks are active. #{action} aborted!") if both_stacks_active?
38
34
  end
39
35
 
@@ -56,32 +52,25 @@ module CfDeployer
56
52
  active_stack && stack_active?(inactive_stack)
57
53
  end
58
54
 
59
-
60
55
  def warm_up_cooled_stack
61
- group_ids(active_stack).each_with_index do |id, index|
62
- min_max_desired = asg_driver(id).describe
63
- asg_driver(group_ids(inactive_stack)[index]).warm_up_cooled_group min_max_desired
64
- end
56
+ warm_up_stack(inactive_stack, active_stack, true)
65
57
  end
66
58
 
67
59
  def cool_down_active_stack
68
- group_ids(active_stack).each do |id|
60
+ get_active_asgs(active_stack).each do |id|
69
61
  asg_driver(id).cool_down
70
62
  end
71
-
72
63
  end
73
64
 
74
65
  def stack_active?(stack)
75
- return false unless stack.exists?
76
- get_active_asg(stack).any?
66
+ stack.exists? && get_active_asgs(stack).any?
77
67
  end
78
68
 
79
-
80
- def get_active_asg stack
81
- return [] unless stack && stack.exists?
82
- group_ids(stack).select do |id|
69
+ def get_active_asgs stack
70
+ return [] unless stack && stack.exists? && stack.resource_statuses[:asg_instances]
71
+ stack.resource_statuses[:asg_instances].keys.select do |id|
83
72
  result = asg_driver(id).describe
84
- result[:min] > 0 && result[:max] > 0 && result[:desired] > 0
73
+ result[:min] > 0 && result[:max] > 0 && result[:desired] > 0
85
74
  end
86
75
  end
87
76
 
@@ -89,7 +78,7 @@ module CfDeployer
89
78
  @auto_scaling_group_drivers[name] ||= CfDeployer::Driver::AutoScalingGroup.new name
90
79
  end
91
80
 
92
- def asg_id_outputs
81
+ def asg_name_outputs
93
82
  @context[:settings][:'auto-scaling-group-name-output']
94
83
  end
95
84
 
@@ -1,4 +1,3 @@
1
-
2
1
  module CfDeployer
3
2
  module DeploymentStrategy
4
3
 
@@ -74,29 +73,38 @@ module CfDeployer
74
73
  end
75
74
 
76
75
  def warm_up_inactive_stack
77
- group_ids(inactive_stack).each_with_index do |id, index|
78
- asg_driver(id).warm_up get_desired(id, index)
79
- end
76
+ warm_up_stack(inactive_stack, active_stack)
80
77
  end
81
78
 
82
- def get_desired(id, index)
83
- group_id = active_stack ? group_ids(active_stack)[index] : id
84
- asg_driver(group_id).describe[:desired]
79
+ def warm_up_stack stack, previous_stack = nil, adjust_min_max = false
80
+ previous_ids = previous_stack ? template_asg_name_to_ids(previous_stack) : {}
81
+ template_asg_name_to_ids(stack).each do |name, id|
82
+ driver = asg_driver(id)
83
+ description = asg_driver(previous_ids[name] || id).describe
84
+ if adjust_min_max
85
+ driver.warm_up_cooled_group(description)
86
+ else
87
+ driver.warm_up(description[:desired])
88
+ end
89
+ end
85
90
  end
86
91
 
87
- def group_ids(stack)
88
- return [] unless asg_id_outputs
89
- asg_id_outputs.map { |id| stack.output id }
92
+ def template_asg_name_to_ids(stack)
93
+ {}.tap do |result|
94
+ (asg_name_outputs || []).each do |name|
95
+ id = stack.find_output(name)
96
+ result[name] = id if id
97
+ end
98
+ end
90
99
  end
91
100
 
92
101
  def asg_driver name
93
102
  @auto_scaling_group_drivers[name] ||= CfDeployer::Driver::AutoScalingGroup.new name
94
103
  end
95
104
 
96
- def asg_id_outputs
105
+ def asg_name_outputs
97
106
  @context[:settings][:'auto-scaling-group-name-output']
98
107
  end
99
-
100
108
  end
101
109
  end
102
110
  end
@@ -67,8 +67,6 @@ module CfDeployer
67
67
  return blue_stack if stack_active?(blue_stack)
68
68
  nil
69
69
  end
70
-
71
-
72
70
  end
73
71
  end
74
72
  end
@@ -29,7 +29,7 @@ module CfDeployer
29
29
 
30
30
  def warm_up_cooled_group options
31
31
  CfDeployer::Driver::DryRun.guard 'Skipping update of ASG min & max instance count' do
32
- aws_group.update :min_size => options[:min], :max_size => options[:max]
32
+ aws_group.update :min_size => options[:min], :max_size => options[:max]
33
33
  end
34
34
  warm_up options[:desired]
35
35
  end
@@ -81,11 +81,9 @@ module CfDeployer
81
81
  }
82
82
  end
83
83
 
84
-
85
84
  def aws_group
86
85
  @my_group ||= AWS::AutoScaling.new.groups[group_name]
87
86
  end
88
-
89
87
  end
90
88
  end
91
89
  end
@@ -38,8 +38,12 @@ module CfDeployer
38
38
  end
39
39
 
40
40
  def output key
41
+ find_output(key) || (raise ApplicationError.new("'#{key}' is empty from stack #{name} output"))
42
+ end
43
+
44
+ def find_output key
41
45
  begin
42
- @cf_driver.query_output(key) || (raise ApplicationError.new("'#{key}' is empty from stack #{name} output"))
46
+ @cf_driver.query_output(key)
43
47
  rescue AWS::CloudFormation::Errors::ValidationError => e
44
48
  raise ResourceNotInReadyState.new("Resource stack not in ready state yet, perhaps you should provision it first?")
45
49
  end
@@ -1,3 +1,3 @@
1
1
  module CfDeployer
2
- VERSION = "1.3.8"
2
+ VERSION = "1.3.9"
3
3
  end
data/spec/fakes/stack.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  module Fakes
2
2
  class Stack
3
3
  attr_reader :outputs, :parameters
4
+ attr_accessor :resource_statuses
5
+
4
6
  def initialize(options)
5
7
  @exists = options[:exists?].nil? ? true : options[:exists?]
6
8
  @outputs = options[:outputs] || {}
7
9
  @parameters = options[:parameters] || {}
8
10
  @name = options[:name] || 'Unnamed'
9
11
  @status = options[:status] || :ready
12
+ @resource_statuses = {}
10
13
  end
11
14
 
12
15
  def inspect
@@ -18,6 +21,7 @@ module Fakes
18
21
  raise 'Stack is dead' unless @exists
19
22
  @outputs[key]
20
23
  end
24
+ alias_method :find_output, :output
21
25
 
22
26
  def set_output(key, value)
23
27
  @outputs[key] = value
@@ -61,6 +61,8 @@ describe 'Deploy' do
61
61
  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-dev-base', 'base', anything) { base_stack }
62
62
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
63
63
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
64
+ allow(blue_stack).to receive(:resource_statuses) { asg_ids 'blueASG' }
65
+ allow(green_stack).to receive(:resource_statuses) { asg_ids 'greenASG' }
64
66
  allow(blue_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
65
67
  allow(green_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
66
68
  expect(blue_asg_driver).to receive(:warm_up).with(2)
@@ -35,23 +35,39 @@ describe 'Kill Inactive' do
35
35
  end
36
36
 
37
37
  context 'asg-swap' do
38
- let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupName' => 'blueASG'}, parameters: {:name => 'blue'}) }
38
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupName' => 'templateBlueASG'}, parameters: {:name => 'blue'}) }
39
39
  let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'ELBName' => 'GREEN-elb', 'AutoScalingGroupName' => 'greenASG'}, parameters: {:name => 'green'}) }
40
- let(:blue_asg_driver) { double('blue_asg_driver') }
40
+ let(:template_blue_asg_driver) { double('template_blue_asg_driver') }
41
+ let(:actual_blue_asg_driver) { double('actual_blue_asg_driver') }
41
42
  let(:green_asg_driver) { double('green_asg_driver') }
42
43
 
43
- it 'should delete the stack that has no active instances' do
44
+ before :each do
44
45
  blue_stack.live!
45
46
  green_stack.live!
46
47
  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-test-web-B', 'web', anything) { blue_stack }
47
48
  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-test-web-G', 'web', anything) { green_stack }
48
49
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
49
- allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
50
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('templateBlueASG') { template_blue_asg_driver }
51
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('actualBlueASG') { actual_blue_asg_driver }
52
+ end
53
+
54
+ it 'should delete the stack that has no active instances' do
55
+ allow(blue_stack).to receive(:resource_statuses) { asg_ids 'actualBlueASG' }
56
+ allow(actual_blue_asg_driver).to receive(:'exists?').and_return(true)
50
57
  allow(green_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0} }
51
- allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 2} }
58
+ allow(actual_blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 2} }
52
59
  CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/simple/cf_deployer.yml'])
53
60
  expect(green_stack).to be_deleted
54
61
  expect(blue_stack).not_to be_deleted
55
62
  end
63
+
64
+ it 'should determine active instances from CF stack' do
65
+ allow(blue_stack).to receive(:resource_statuses) { asg_ids 'actualBlueASG' }
66
+ allow(template_blue_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0} }
67
+ allow(actual_blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 1} }
68
+
69
+ CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/simple/cf_deployer.yml'])
70
+ expect(blue_stack).not_to be_deleted
71
+ end
56
72
  end
57
73
  end
@@ -1,3 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  ARGV.clear
4
+
5
+ def asg_ids(*ids)
6
+ values = Hash[ids.zip([nil] * ids.length)]
7
+ {
8
+ asg_instances: values
9
+ }
10
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require 'functional_spec_helper'
2
2
 
3
3
  describe 'Auto Scaling Group Swap Deployment Strategy' do
4
4
  let(:app) { 'myapp' }
@@ -6,12 +6,12 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
6
6
  let(:component) { 'worker' }
7
7
 
8
8
  let(:context) {
9
- {
10
- :'deployment-strategy' => 'auto-scaling-group-swap',
11
- :settings => {
9
+ {
10
+ :'deployment-strategy' => 'auto-scaling-group-swap',
11
+ :settings => {
12
12
  :'auto-scaling-group-name-output' => ['AutoScalingGroupID']
13
- }
14
13
  }
14
+ }
15
15
  }
16
16
 
17
17
  let(:blue_asg_driver) { double('blue_asg_driver') }
@@ -23,6 +23,10 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
23
23
  before :each do
24
24
  allow(blue_stack).to receive(:output).with('AutoScalingGroupID'){'blueASG'}
25
25
  allow(green_stack).to receive(:output).with('AutoScalingGroupID'){'greenASG'}
26
+ allow(blue_stack).to receive(:find_output).with('AutoScalingGroupID'){'blueASG'}
27
+ allow(green_stack).to receive(:find_output).with('AutoScalingGroupID'){'greenASG'}
28
+ allow(blue_stack).to receive(:resource_statuses) { asg_ids('blueASG') }
29
+ allow(green_stack).to receive(:resource_statuses) { asg_ids('greenASG') }
26
30
  allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-worker-B', 'worker', context) { blue_stack }
27
31
  allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-worker-G', 'worker', context) { green_stack }
28
32
  end
@@ -59,7 +63,7 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
59
63
  CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
60
64
  end
61
65
 
62
- it 'should deploy blue stack if green stack is not active' do
66
+ it 'should deploy blue stack if green stack is not active' do
63
67
  blue_stack.die!
64
68
  green_stack.live!
65
69
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
@@ -137,7 +141,7 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
137
141
  CfDeployer::DeploymentStrategy.create(app, env, component, context).deploy
138
142
  end
139
143
 
140
- it 'should deploy green stack if blue stack is active' do
144
+ it 'should deploy green stack if blue stack is active' do
141
145
  blue_stack.live!
142
146
  green_stack.live!
143
147
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
@@ -196,17 +200,17 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
196
200
 
197
201
  context 'multiple ASG' do
198
202
  let(:context) {
199
- {
200
- :'deployment-strategy' => 'auto-scaling-group-swap',
201
- :settings => {
203
+ {
204
+ :'deployment-strategy' => 'auto-scaling-group-swap',
205
+ :settings => {
202
206
  :'auto-scaling-group-name-output' => ['AutoScalingGroupID', 'AlternateASGID']
203
- }
204
207
  }
208
+ }
205
209
  }
206
210
 
207
211
  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'}
212
+ allow(blue_stack).to receive(:find_output).with('AlternateASGID'){'AltblueASG'}
213
+ allow(green_stack).to receive(:find_output).with('AlternateASGID'){'AltgreenASG'}
210
214
  blue_stack.live!
211
215
  green_stack.live!
212
216
  alt_blue_asg_driver = double('alt_blue_asg_driver')
@@ -215,6 +219,7 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
215
219
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
216
220
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('AltgreenASG') { alt_green_asg_driver }
217
221
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
222
+ allow(blue_stack).to receive(:resource_statuses) { asg_ids('blueASG', 'AltblueASG') }
218
223
  allow(alt_blue_asg_driver).to receive(:describe) {{desired: 1, min: 1, max: 2}}
219
224
  allow(alt_green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
220
225
  allow(blue_asg_driver).to receive(:describe) {{desired: 1, min: 1, max: 2}}
@@ -345,6 +350,21 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
345
350
  end
346
351
  end
347
352
 
353
+ context '#cool_down_active_stack' do
354
+ it 'should cool down only those ASGs which actually exist' do
355
+ blue_stack.live!
356
+ green_stack.die!
357
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
358
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
359
+ allow(green_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0 } }
360
+ allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 3 } }
361
+
362
+ strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
363
+ expect(blue_asg_driver).to receive(:cool_down)
364
+ strategy.send(:cool_down_active_stack)
365
+ end
366
+ end
367
+
348
368
  describe '#asg_driver' do
349
369
  it 'returns the same driver for the same aws_group_name' do
350
370
  strategy = CfDeployer::DeploymentStrategy.create(app, env, component, context)
@@ -358,7 +378,6 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
358
378
  end
359
379
 
360
380
  context '#output_value' do
361
-
362
381
  it 'should get stack output if active stack exists' do
363
382
  blue_stack.live!
364
383
  green_stack.live!
@@ -383,53 +402,90 @@ describe 'Auto Scaling Group Swap Deployment Strategy' do
383
402
  end
384
403
 
385
404
  context '#status' do
386
- before :each do
405
+ before :each do
387
406
  blue_stack.live!
388
407
  green_stack.live!
389
408
  allow(blue_stack).to receive(:status) { 'blue deployed' }
390
409
  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
410
  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}}
411
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
396
412
  allow(blue_asg_driver).to receive(:describe) {{desired: 3, min: 1, max: 5}}
413
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
397
414
  asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
398
415
 
399
416
  end
400
417
 
401
418
  it 'should get status for both green and blue stacks' do
402
419
  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
- }
420
+ expected_result = {
421
+ 'BLUE' => {
422
+ :active => true,
423
+ :status => 'blue deployed'
424
+ },
425
+ 'GREEN' => {
426
+ :active => false,
427
+ :status => 'green deployed'
428
+ }
412
429
  }
413
430
  asg_swap.status.should eq(expected_result)
414
431
  end
415
432
 
416
433
  it 'should get status for both green and blue stacks including resources info' do
417
434
  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
- }
435
+ expected_result = {
436
+ 'BLUE' => {
437
+ :active => true,
438
+ :status => 'blue deployed',
439
+ :resources => {
440
+ :asg_instances => {
441
+ 'blueASG' => nil
442
+ }
443
+ }
444
+ },
445
+ 'GREEN' => {
446
+ :active => false,
447
+ :status => 'green deployed',
448
+ :resources => {
449
+ :asg_instances => {
450
+ 'greenASG' => nil
451
+ }
452
+ }
453
+ }
429
454
  }
430
455
  asg_swap.status(true).should eq(expected_result)
431
456
  end
457
+ end
458
+
459
+ context 'new ASG' do
460
+ let(:foo_asg_driver) { double('foo_asg_driver') }
461
+ let(:bar_asg_driver) { double('bar_asg_driver') }
462
+
463
+ before :each do
464
+ allow(foo_asg_driver).to receive(:describe) { {min: 1, desired: 2, max: 3} }
465
+ allow(bar_asg_driver).to receive(:describe) { {min: 0, desired: 0, max: 0} }
466
+ end
467
+
468
+ it 'should get active ASGs from CF stack' do
469
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('foo') { foo_asg_driver }
470
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('bar') { bar_asg_driver }
471
+ allow(blue_stack).to receive(:resource_statuses) { asg_ids('foo', 'bar') }
472
+
473
+ asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
474
+ asg_swap.send(:get_active_asgs, blue_stack).should eq(['foo'])
475
+ end
476
+ end
432
477
 
478
+ context '#stack_active' do
479
+ it 'should consider a stack active if it has any active ASGs' do
480
+ allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-worker-B', 'worker', context) { blue_stack }
481
+ allow(CfDeployer::Stack).to receive(:new).with('myapp-dev-worker-G', 'worker', context) { green_stack }
482
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
483
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
484
+ allow(blue_asg_driver).to receive(:describe) { {min: 1, desired: 2, max: 3} }
485
+ allow(green_asg_driver).to receive(:describe) { {min: 0, desired: 0, max: 0} }
433
486
 
487
+ asg_swap = CfDeployer::DeploymentStrategy.create(app, env, component, context)
488
+ asg_swap.send(:stack_active?, blue_stack).should be(true)
489
+ end
434
490
  end
435
491
  end
@@ -134,4 +134,78 @@ describe 'Base Deployment Strategy' do
134
134
  strategy.run_hook(:some_hook)
135
135
  end
136
136
  end
137
+
138
+ context '#warm_up_stack' do
139
+ let(:context) {
140
+ {
141
+ :'deployment-strategy' => 'base',
142
+ :settings => {
143
+ :'auto-scaling-group-name-output' => ['ASG1', 'ASG2']
144
+ }
145
+ }
146
+ }
147
+ let(:blue_stack) { double('blue_stack') }
148
+ let(:green_stack) { double('green_stack') }
149
+ let(:blue_asg_driver_1) { double('blue_asg_driver_1') }
150
+ let(:blue_asg_driver_2) { double('blue_asg_driver_2') }
151
+ let(:green_asg_driver_1) { double('green_asg_driver_1') }
152
+ let(:green_asg_driver_2) { double('green_asg_driver_2') }
153
+
154
+ before :each do
155
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blue_asg_driver_1') { blue_asg_driver_1 }
156
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blue_asg_driver_2') { blue_asg_driver_2 }
157
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('green_asg_driver_1') { green_asg_driver_1 }
158
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('green_asg_driver_2') { green_asg_driver_2 }
159
+
160
+ allow(green_stack).to receive(:find_output).with('ASG1') { 'green_asg_driver_1' }
161
+ allow(green_stack).to receive(:find_output).with('ASG2') { 'green_asg_driver_2' }
162
+ end
163
+
164
+ it 'should warm up ASG with previous stack ASG values when present' do
165
+ allow(blue_stack).to receive(:resource_statuses) { { 'blueASG1' => nil, 'blueASG2' => nil } }
166
+ allow(blue_stack).to receive(:find_output).with('ASG1') { 'blue_asg_driver_1' }
167
+ allow(blue_stack).to receive(:find_output).with('ASG2') { 'blue_asg_driver_2' }
168
+ allow(blue_asg_driver_1).to receive(:describe) { {min: 1, desired: 2, max: 3} }
169
+ allow(blue_asg_driver_2).to receive(:describe) { {min: 2, desired: 3, max: 4} }
170
+ strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', context)
171
+
172
+ expect(green_asg_driver_1).to receive(:warm_up).with(2)
173
+ expect(green_asg_driver_2).to receive(:warm_up).with(3)
174
+ strategy.send(:warm_up_stack, green_stack, blue_stack)
175
+ end
176
+
177
+ it 'should warm up ASG with own values when previous stack does not contain ASG' do
178
+ allow(blue_stack).to receive(:find_output).with(anything) { nil }
179
+ allow(green_asg_driver_1).to receive(:describe) { {min: 3, desired: 4, max: 5} }
180
+ allow(green_asg_driver_2).to receive(:describe) { {min: 4, desired: 5, max: 6} }
181
+ strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', context)
182
+
183
+ expect(green_asg_driver_1).to receive(:warm_up).with(4)
184
+ expect(green_asg_driver_2).to receive(:warm_up).with(5)
185
+ strategy.send(:warm_up_stack, green_stack, blue_stack)
186
+ end
187
+ end
188
+
189
+ context '#template_asg_name_to_ids' do
190
+ let(:context) {
191
+ {
192
+ :'deployment-strategy' => 'base',
193
+ :settings => {
194
+ :'auto-scaling-group-name-output' => ['ASG1', 'ASG2']
195
+ }
196
+ }
197
+ }
198
+
199
+ it 'should map names in templates to stack outputs' do
200
+ allow(blue_stack).to receive(:find_output).with('ASG1') { 'blue_asg_driver_1' }
201
+ allow(blue_stack).to receive(:find_output).with('ASG2') { 'blue_asg_driver_2' }
202
+ expected = {
203
+ 'ASG1' => 'blue_asg_driver_1',
204
+ 'ASG2' => 'blue_asg_driver_2',
205
+ }
206
+
207
+ strategy = CfDeployer::DeploymentStrategy.create('myApp', 'uat', 'web', context)
208
+ expect(strategy.send(:template_asg_name_to_ids, blue_stack)).to eq(expected)
209
+ end
210
+ end
137
211
  end
@@ -61,7 +61,7 @@ describe 'CreateOrUpdate Strategy' do
61
61
  context[:settings] = {}
62
62
  context[:settings][:'auto-scaling-group-name-output'] = ['AutoScalingGroupID']
63
63
  @stack.should_receive(:exists?).and_return(false)
64
- allow(@stack).to receive(:output).with('AutoScalingGroupID') { 'asg_name' }
64
+ allow(@stack).to receive(:find_output).with('AutoScalingGroupID') { 'asg_name' }
65
65
  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('asg_name') { asg_driver }
66
66
  allow(asg_driver).to receive(:describe) { {desired:2, min:1, max:3} }
67
67
  allow(@after_create_hook).to receive(:run).with(anything)
@@ -1,10 +1,28 @@
1
1
  require 'spec_helper'
2
+
2
3
  describe 'CloudFormation' do
3
4
  let(:outputs) { [output1, output2] }
4
5
  let(:output1) { double('output1', :key => 'key1', :value => 'value1')}
5
6
  let(:output2) { double('output2', :key => 'key2', :value => 'value2')}
6
7
  let(:parameters) { double('parameters')}
7
- let(:stack) { double('stack', :outputs => outputs, :parameters => parameters) }
8
+ let(:resource_summaries) { [
9
+ {
10
+ :resource_type => 'AWS::AutoScaling::AutoScalingGroup',
11
+ :physical_resource_id => 'asg_1',
12
+ :resource_status => 'STATUS_1'
13
+ },
14
+ {
15
+ :resource_type => 'AWS::AutoScaling::LaunchConfiguration',
16
+ :physical_resource_id => 'launch_config_1',
17
+ :resource_status => 'STATUS_2'
18
+ },
19
+ {
20
+ :resource_type => 'AWS::AutoScaling::AutoScalingGroup',
21
+ :physical_resource_id => 'asg_2',
22
+ :resource_status => 'STATUS_2'
23
+ }
24
+ ] }
25
+ let(:stack) { double('stack', :outputs => outputs, :parameters => parameters, :resource_summaries => resource_summaries) }
8
26
  let(:cloudFormation) {
9
27
  double('cloudFormation',
10
28
  :stacks =>
@@ -25,8 +43,18 @@ describe 'CloudFormation' do
25
43
  end
26
44
 
27
45
  context 'resource_statuses' do
28
- it 'should be tested' do
29
- false
46
+ it 'should get resource statuses' do
47
+ expected = {
48
+ 'AWS::AutoScaling::AutoScalingGroup' => {
49
+ 'asg_1' => 'STATUS_1',
50
+ 'asg_2' => 'STATUS_2'
51
+ },
52
+ 'AWS::AutoScaling::LaunchConfiguration' => {
53
+ 'launch_config_1' => 'STATUS_2'
54
+ }
55
+ }
56
+
57
+ CfDeployer::Driver::CloudFormation.new('testStack').resource_statuses.should eq(expected)
30
58
  end
31
59
  end
32
60
  end
@@ -59,8 +59,31 @@ describe CfDeployer::Stack do
59
59
  end
60
60
  end
61
61
 
62
- context "#ready?" do
62
+ context '#output' do
63
+ it 'should get output value' do
64
+ expect(@cf_driver).to receive(:query_output).with('mykey'){ 'myvalue'}
65
+ @stack.output('mykey').should eq('myvalue')
66
+ end
63
67
 
68
+ it 'should get error if output is empty' do
69
+ expect(@cf_driver).to receive(:query_output).with('mykey'){ nil }
70
+ expect{@stack.output('mykey')}.to raise_error("'mykey' is empty from stack test output")
71
+ end
72
+ end
73
+
74
+ context '#find_output' do
75
+ it 'should get output value' do
76
+ expect(@cf_driver).to receive(:query_output).with('mykey'){ 'myvalue'}
77
+ @stack.find_output('mykey').should eq('myvalue')
78
+ end
79
+
80
+ it 'should return nil for non-existent value' do
81
+ expect(@cf_driver).to receive(:query_output).with('mykey'){ nil }
82
+ @stack.find_output('mykey').should be(nil)
83
+ end
84
+ end
85
+
86
+ context "#ready?" do
64
87
  CfDeployer::Stack::READY_STATS.each do |status|
65
88
  it "should be ready when in #{status} status" do
66
89
  allow(@cf_driver).to receive(:stack_status) { status }
@@ -72,16 +95,6 @@ describe CfDeployer::Stack do
72
95
  allow(@cf_driver).to receive(:stack_status) { :my_fake_status }
73
96
  expect(@stack).not_to be_ready
74
97
  end
75
-
76
- it "should get error if output is empty" do
77
- expect(@cf_driver).to receive(:query_output).with('mykey'){ nil }
78
- expect{@stack.output('mykey')}.to raise_error("'mykey' is empty from stack test output")
79
- end
80
-
81
- it "should get output value" do
82
- expect(@cf_driver).to receive(:query_output).with('mykey'){ 'myvalue'}
83
- @stack.output('mykey').should eq('myvalue')
84
- end
85
98
  end
86
99
 
87
100
  describe '#delete' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cf_deployer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.8
4
+ version: 1.3.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jame Brechtel
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-07-27 00:00:00.000000000 Z
14
+ date: 2015-11-09 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: aws-sdk
@@ -151,6 +151,7 @@ extensions: []
151
151
  extra_rdoc_files: []
152
152
  files:
153
153
  - .gitignore
154
+ - .travis.yml
154
155
  - ChangeLog.md
155
156
  - DETAILS.md
156
157
  - FAQ.md