cf_deployer 1.3.8 → 1.3.9

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