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,65 @@
1
+ module Fakes
2
+ class Stack
3
+ attr_reader :outputs, :parameters
4
+ def initialize(options)
5
+ @exists = options[:exists?].nil? ? true : options[:exists?]
6
+ @outputs = options[:outputs] || {}
7
+ @parameters = options[:parameters] || {}
8
+ @name = options[:name] || 'Unnamed'
9
+ @status = options[:status] || :ready
10
+ end
11
+
12
+ def inspect
13
+ "#{self.class}<#{@name}>"
14
+ end
15
+ alias_method :to_s, :inspect
16
+
17
+ def output(key)
18
+ raise 'Stack is dead' unless @exists
19
+ @outputs[key]
20
+ end
21
+
22
+ def set_output(key, value)
23
+ @outputs[key] = value
24
+ end
25
+
26
+ def live!
27
+ @exists = true
28
+ end
29
+
30
+ def die!
31
+ @exists = false
32
+ end
33
+
34
+ def exists?
35
+ @exists
36
+ end
37
+
38
+ def delete
39
+ @exists = false
40
+ @deployed = false
41
+ @deleted = true
42
+ end
43
+
44
+ def deploy
45
+ @exists = true
46
+ @deployed = true
47
+ end
48
+
49
+ def deployed?
50
+ @deployed
51
+ end
52
+
53
+ def deleted?
54
+ @deleted
55
+ end
56
+
57
+ def name
58
+ @name
59
+ end
60
+
61
+ def status
62
+ @status
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,73 @@
1
+ require 'functional_spec_helper'
2
+
3
+ describe 'Deploy' do
4
+ context 'create-or-update' do
5
+
6
+ let(:asg_driver) { double('asg_driver') }
7
+ let(:stack) { Fakes::Stack.new(name: 'stack', outputs: {'AutoScalingGroupID' => 'myASG'})}
8
+
9
+ it 'should create a stack in Cloud Formation' do
10
+ stack.die!
11
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-create-or-update-test-web', 'web', anything()) { stack }
12
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('myASG') { asg_driver }
13
+ allow(asg_driver).to receive(:describe) {{desired: 1, min: 1, max: 2}}
14
+ allow(asg_driver).to receive(:warm_up).with(1)
15
+ CfDeployer::CLI.start(['deploy', 'test', 'web', '-f', 'samples/create-or-update/cf_deployer.yml'])
16
+ expect(stack).to be_deployed
17
+ end
18
+ end
19
+
20
+ context 'cname-swap' do
21
+ let(:blue_asg_driver) { double('blue_asg_driver') }
22
+ let(:green_asg_driver) { double('green_asg_driver') }
23
+ let(:dns_driver) { double('route53') }
24
+ let(:elb_driver) { double('elb') }
25
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupName' => 'blueASG'}, parameters: {:name => 'blue'}) }
26
+ let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'ELBName' => 'GREEN-elb', 'AutoScalingGroupName' => 'greenASG'}, parameters: {:name => 'green'}) }
27
+
28
+ before :each do
29
+ allow(Kernel).to receive(:sleep)
30
+ end
31
+ it 'should recreate inactive stack and set CNAME map to its ELB dns' do
32
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-dev-web-B', 'web', anything()) { blue_stack }
33
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-dev-web-G', 'web', anything()) { green_stack }
34
+ allow(CfDeployer::Driver::Elb).to receive(:new) { elb_driver }
35
+ allow(CfDeployer::Driver::Route53).to receive(:new) { dns_driver }
36
+ allow(dns_driver).to receive(:find_alias_target).with('zhao.com', 'test1.zhao.com'){ 'BLUE-elb.zhao.com' }
37
+ allow(elb_driver).to receive(:find_dns_and_zone_id).with('BLUE-elb') { {:dns_name => 'blue-elb.zhao.com', :canonical_hosted_zone_name_id => 'BLUE111'}}
38
+ allow(elb_driver).to receive(:find_dns_and_zone_id).with('GREEN-elb') { {:dns_name => 'green-elb.zhao.com', :canonical_hosted_zone_name_id => 'GREEN111'}}
39
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
40
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
41
+ allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
42
+ allow(blue_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
43
+ allow(dns_driver).to receive(:set_alias_target).with('zhao.com', 'test1.zhao.com', 'GREEN111', 'green-elb.zhao.com')
44
+ expect(green_asg_driver).to receive(:warm_up).with(2)
45
+ CfDeployer::CLI.start(['deploy', 'dev', 'web', '-f', 'samples/cname-swap/cf_deployer.yml'])
46
+ expect(green_stack).to be_deleted
47
+ expect(green_stack).to be_deployed
48
+ end
49
+ end
50
+
51
+ context 'autoscaling-swap' do
52
+ let(:blue_asg_driver) { double('blue_asg_driver') }
53
+ let(:green_asg_driver) { double('green_asg_driver') }
54
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'web-elb-name' => 'BLUE-elb', 'AutoScalingGroupName' => 'blueASG'}, parameters: { name: 'blue'}) }
55
+ let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'web-elb-name' => 'GREEN-elb', 'AutoScalingGroupName' => 'greenASG'}, parameters: { name: 'green'}) }
56
+ let(:base_stack) { Fakes::Stack.new(name: 'base') }
57
+
58
+ it 'should re-create and warm up inactive stack and cool down the active stack' do
59
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-dev-web-B', 'web', anything) { blue_stack }
60
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-dev-web-G', 'web', anything) { green_stack }
61
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-dev-base', 'base', anything) { base_stack }
62
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
63
+ allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
64
+ allow(blue_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
65
+ allow(green_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
66
+ expect(blue_asg_driver).to receive(:warm_up).with(2)
67
+
68
+ CfDeployer::CLI.start(['deploy', 'dev', 'web', '-f', 'samples/simple/cf_deployer.yml'])
69
+ expect(blue_stack).to be_deleted
70
+ expect(blue_stack).to be_deployed
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,57 @@
1
+ require 'functional_spec_helper'
2
+
3
+ describe 'Kill Inactive' do
4
+ context 'create-or-update' do
5
+ let(:stack) { Fakes::Stack.new(name: 'stack', outputs: {'AutoScalingGroupID' => 'myASG'})}
6
+
7
+ it 'should raise an error that there is no inactive for create-or-update staks' do
8
+ stack.die!
9
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-create-or-update-test-web', 'web', anything()) { stack }
10
+ expect { CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/create-or-update/cf_deployer.yml']) }.to raise_error CfDeployer::ApplicationError, 'There is no inactive version to kill for Create or Update Deployments.'
11
+ end
12
+ end
13
+
14
+ context 'cname-swap' do
15
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupID' => 'blueASG'}, parameters: {:name => 'blue'}) }
16
+ let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'ELBName' => 'GREEN-elb', 'AutoScalingGroupID' => 'greenASG'}, parameters: {:name => 'green'}) }
17
+
18
+ it 'should delete the stack that is not being pointed to by dns' do
19
+ blue_stack.live!
20
+ green_stack.live!
21
+ elb_driver = double('elb_driver')
22
+ allow(CfDeployer::Driver::Elb).to receive(:new) { elb_driver }
23
+ 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'}}
24
+ 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'}}
25
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-test-web-B', 'web', anything) { blue_stack }
26
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-test-web-G', 'web', anything) { green_stack }
27
+ dns_driver = double('route53 driver')
28
+ allow(CfDeployer::Driver::Route53).to receive(:new) { dns_driver }
29
+ allow(dns_driver).to receive(:find_alias_target).with('zhao.com', 'test1.zhao.com'){ 'BLUE-elb.aws.amazon.com' }
30
+
31
+ CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/cname-swap/cf_deployer.yml'])
32
+ expect(green_stack).to be_deleted
33
+ expect(blue_stack).not_to be_deleted
34
+ end
35
+ end
36
+
37
+ context 'asg-swap' do
38
+ let(:blue_stack) { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupName' => 'blueASG'}, parameters: {:name => 'blue'}) }
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') }
41
+ let(:green_asg_driver) { double('green_asg_driver') }
42
+
43
+ it 'should delete the stack that has no active instances' do
44
+ blue_stack.live!
45
+ green_stack.live!
46
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-test-web-B', 'web', anything) { blue_stack }
47
+ allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-test-web-G', 'web', anything) { green_stack }
48
+ 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(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} }
52
+ CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/simple/cf_deployer.yml'])
53
+ expect(green_stack).to be_deleted
54
+ expect(blue_stack).not_to be_deleted
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,3 @@
1
+ require 'spec_helper'
2
+
3
+ ARGV.clear
@@ -0,0 +1,8 @@
1
+ require_relative '../lib/cf_deployer'
2
+ Dir.glob("#{File.dirname File.absolute_path(__FILE__)}/fakes/*.rb") { |file| require file }
3
+
4
+ CfDeployer::Log.log.outputters = nil
5
+
6
+ def puts *args
7
+
8
+ end
@@ -0,0 +1,191 @@
1
+ require 'spec_helper'
2
+
3
+ describe "application" do
4
+ before :each do
5
+ @context = {
6
+ :application => 'myApp',
7
+ :environment => 'dev',
8
+ :targets => ['queue', 'web', 'db', 'base'],
9
+ :components => {
10
+ :queue => {
11
+ :'depends-on' => [ 'base', 'db' ]
12
+ },
13
+ :web => {
14
+ :'depends-on' => [ 'db', 'queue' ]
15
+ },
16
+ :db => {
17
+ :'depends-on' => [ 'base']
18
+ },
19
+ :base => {
20
+ }
21
+ }
22
+ }
23
+ @app = CfDeployer::Application.new(@context)
24
+ @base = @app.components.find{ |c| c.name == 'base' }
25
+ @db = @app.components.find{ |c| c.name == 'db' }
26
+ @queue = @app.components.find{ |c| c.name == 'queue' }
27
+ @web = @app.components.find{ |c| c.name == 'web' }
28
+ end
29
+
30
+ it "application should get all components" do
31
+ @app.components.length.should eq(4)
32
+ @base.should_not be_nil
33
+ @db.should_not be_nil
34
+ @queue.should_not be_nil
35
+ @web.should_not be_nil
36
+ end
37
+
38
+ context "order components by dependencies" do
39
+ it "should get components ordered by dependencies" do
40
+ expect(@app.components).to eq([@base, @db, @queue, @web])
41
+ end
42
+ end
43
+
44
+ context 'destroy' do
45
+ it 'should destroy components starting with components without dependents' do
46
+ log = ""
47
+ allow(@base).to receive(:destroy) { log += "base "}
48
+ allow(@db).to receive(:destroy) { log += "db "}
49
+ allow(@queue).to receive(:destroy) { log += "queue "}
50
+ allow(@web).to receive(:destroy) { log += "web "}
51
+ @app.destroy
52
+ expect(log).to eql("web queue db base ")
53
+ end
54
+
55
+ it 'should destroy specified components' do
56
+ log = ""
57
+ allow(@base).to receive(:destroy) { log += "base "}
58
+ allow(@db).to receive(:destroy) { log += "db "}
59
+ allow(@queue).to receive(:destroy) { log += "queue "}
60
+ allow(@web).to receive(:destroy) { log += "web "}
61
+ @context[:targets] = ['db', 'web', 'queue']
62
+ @app.destroy
63
+ expect(log).to eql("web queue db ")
64
+ end
65
+ end
66
+
67
+ it "application should get components with their dependencies" do
68
+ expect(@base.dependencies).to match_array([])
69
+ expect(@db.dependencies).to match_array([@base])
70
+ expect(@queue.dependencies).to match_array([@base, @db])
71
+ expect(@web.dependencies).to match_array([@db, @queue])
72
+ end
73
+
74
+ it 'should get add components with their children' do
75
+ expect(@base.children).to match_array([@db, @queue])
76
+ expect(@db.children).to match_array([@web, @queue])
77
+ expect(@queue.children).to match_array([@web])
78
+ expect(@web.children).to match_array([])
79
+ end
80
+
81
+ describe '#switch' do
82
+
83
+ let(:app) { CfDeployer::Application.new(@context.merge(:targets => ['base'])) }
84
+
85
+ it 'should switch the specified component' do
86
+ base = app.components.find { |c| c.name == 'base' }
87
+ expect(base).to receive :switch
88
+ app.switch
89
+ end
90
+
91
+ it 'should not switch components not specified' do
92
+ db = app.components.find { |c| c.name == 'db' }
93
+ queue = app.components.find { |c| c.name == 'queue' }
94
+ web = app.components.find { |c| c.name == 'web' }
95
+ base = app.components.find { |c| c.name == 'base' }
96
+ allow(base).to receive :switch
97
+ expect(db).not_to receive :switch
98
+ expect(queue).not_to receive :switch
99
+ expect(web).not_to receive :switch
100
+ app.switch
101
+ end
102
+ end
103
+
104
+ describe '#kill_inactive' do
105
+ it 'should kill the inactive piece of a component' do
106
+ @context[:targets] = ['base']
107
+ app = CfDeployer::Application.new(@context)
108
+ base = app.components.find{ |c| c.name == 'base' }
109
+ expect(base).to receive(:kill_inactive)
110
+ app.kill_inactive
111
+ end
112
+ end
113
+
114
+ describe '#status' do
115
+ it "should get each component's status" do
116
+ expect(@base).to receive(:status)
117
+ expect(@db).to receive(:status)
118
+ expect(@queue).to receive(:status)
119
+ expect(@web).to receive(:status)
120
+
121
+ @app.status nil, 'all'
122
+ end
123
+
124
+ it 'should pass the get_resource_statuses flag down to the components' do
125
+ expect(@base).to receive(:status).with(true)
126
+ expect(@db).to receive(:status).with(true)
127
+ expect(@queue).to receive(:status).with(true)
128
+ expect(@web).to receive(:status).with(true)
129
+
130
+ @app.status nil, 'all'
131
+ end
132
+
133
+ it 'should filter by component if specified' do
134
+ expect(@base).not_to receive(:status)
135
+ expect(@db).not_to receive(:status)
136
+ expect(@queue).not_to receive(:status)
137
+
138
+ expect(@web).to receive(:status).with(true)
139
+
140
+ @app.status 'web', 'all'
141
+ end
142
+ end
143
+
144
+ context "deploy components" do
145
+ before :each do
146
+ @log = ""
147
+ allow(@base).to receive(:deploy) { @log += "base "}
148
+ allow(@db).to receive(:deploy) { @log += "db "}
149
+ allow(@queue).to receive(:deploy) { @log += "queue "}
150
+ allow(@web).to receive(:deploy) { @log += "web "}
151
+ end
152
+
153
+
154
+ context "deploy all components" do
155
+ it "should deploy all components if no component specified" do
156
+ @app.deploy
157
+ @log.should eq("base db queue web ")
158
+ end
159
+ end
160
+
161
+ context "deploy some components" do
162
+ it "should deploy specified components" do
163
+ @context[:targets] = ['web', 'db']
164
+ @app.deploy
165
+ @log.should eq("db web ")
166
+ end
167
+ end
168
+ end
169
+
170
+ context '#json' do
171
+
172
+ before :each do
173
+ @log = ''
174
+ allow(@base).to receive(:json) { @log += "base " }
175
+ allow(@db).to receive(:json) { @log += "db " }
176
+ allow(@queue).to receive(:json) { @log += "queue " }
177
+ allow(@web).to receive(:json) { @log += "web " }
178
+ end
179
+
180
+ it 'should get json templates for all components' do
181
+ @app.json
182
+ @log.should eq("base db queue web ")
183
+ end
184
+
185
+ it 'should get json templates for components specified' do
186
+ @context[:targets] = ['web', 'db']
187
+ @app.json
188
+ @log.should eq('db web ')
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+
3
+ describe "component" do
4
+ before :each do
5
+ @strategy = double('deployment_strategy')
6
+
7
+ allow(CfDeployer::DeploymentStrategy).to receive(:create).and_return(@strategy)
8
+
9
+ @context = {
10
+ :'deployment-strategy' => 'create-or-update',
11
+ :inputs => {
12
+ :'vpc-subnets' => {
13
+ :component => 'base',
14
+ :'output-key' => 'subnets'
15
+ }
16
+ }
17
+ }
18
+ @base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
19
+ @db = CfDeployer::Component.new('myApp', 'uat', 'db', {})
20
+
21
+ @web = CfDeployer::Component.new('myApp', 'uat', 'web', @context)
22
+ @web.dependencies << @base
23
+ @web.dependencies << @db
24
+ @base.children << @web
25
+ @db.children << @web
26
+ end
27
+
28
+ context 'json' do
29
+
30
+ it 'should revolve settings from parent components if parent components has been deployed' do
31
+ allow(@base).to receive(:exists?){ true }
32
+ allow(@base).to receive(:output_value).with('subnets') { 'abcd1234, edfas1234' }
33
+ expect(CfDeployer::ConfigLoader).to receive(:component_json).with('web', @context)
34
+ @web.json
35
+
36
+ expect(@context[:inputs][:'vpc-subnets']).to eq('abcd1234, edfas1234')
37
+ end
38
+ end
39
+
40
+
41
+ it "should destroy component" do
42
+ expect(@strategy).to receive(:destroy)
43
+ @web.destroy
44
+ end
45
+
46
+ it 'should not destroy a component that is depended on' do
47
+ allow(@web).to receive(:exists?){ true }
48
+ expect(@strategy).not_to receive(:destroy)
49
+ expect{ @base.destroy }.to raise_error("Unable to destroy #{@base.name}, it is depended on by other components")
50
+ end
51
+
52
+ it "should get output value" do
53
+ expect(@strategy).to receive(:output_value).with('key1'){ 'value1' }
54
+ @web.output_value('key1')
55
+ end
56
+
57
+ it "deployment should only deploy depends-on if the depends-on do not exists" do
58
+ expect(@base).to receive(:exists?){ false }
59
+ expect(@db).to receive(:exists?) { true }
60
+ expect(@base).to receive(:deploy)
61
+ expect(@db).to_not receive(:deploy)
62
+ expect(@strategy).to receive(:deploy)
63
+ expect(@base).to receive(:output_value).with('subnets') { 'abcd1234, edfas1234' }
64
+ @web.deploy
65
+
66
+ expect(@context[:inputs][:'vpc-subnets']).to eq('abcd1234, edfas1234')
67
+ end
68
+
69
+ it "should ask strategy if component exists" do
70
+ expect(@strategy).to receive(:exists?){ true }
71
+ @web.exists?.should eq(true)
72
+ end
73
+
74
+ it "should find direct dependencies" do
75
+ web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
76
+ base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
77
+ web.dependencies << base
78
+
79
+ web.depends_on?(base).should eq(true)
80
+ end
81
+
82
+ it "should find transitive dependencies" do
83
+ web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
84
+ haproxy = CfDeployer::Component.new('myApp', 'uat', 'haproxy', {})
85
+ base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
86
+
87
+ haproxy.dependencies << base
88
+ web.dependencies << haproxy
89
+
90
+ web.depends_on?(base).should eq(true)
91
+ end
92
+
93
+ it "should find cyclic dependency" do
94
+ web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
95
+ haproxy = CfDeployer::Component.new('myApp', 'uat', 'haproxy', {})
96
+ base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
97
+ foo = CfDeployer::Component.new('myApp', 'uat', 'foo', {})
98
+
99
+ haproxy.dependencies << base
100
+ web.dependencies << haproxy
101
+ base.dependencies << web
102
+
103
+ expect{haproxy.depends_on? foo}.to raise_error("Cyclic dependency")
104
+ end
105
+
106
+ describe '#status' do
107
+ it "should ask strategy for status" do
108
+ expect(@strategy).to receive(:status){ true }
109
+ @web.status false
110
+ end
111
+
112
+ it "should pass get_resource_statuses down to the strategy" do
113
+ expect(@strategy).to receive(:status).with(true)
114
+ @web.status(true)
115
+ end
116
+ end
117
+
118
+ describe '#kill_inactive' do
119
+ it 'should tell the strategy to kill the inactive piece' do
120
+ expect(@strategy).to receive(:kill_inactive)
121
+ @web.kill_inactive
122
+ end
123
+ end
124
+
125
+ describe '#switch' do
126
+ context 'if no stack exists' do
127
+ it 'should raise an error that there is no stack for the component' do
128
+ allow(@strategy).to receive(:exists?) { false }
129
+ expect(@strategy).not_to receive(:switch)
130
+ expect { @web.switch }.to raise_error 'No stack exists for component: web'
131
+ end
132
+ end
133
+
134
+ context 'a stack exists' do
135
+ it 'should use the deployment strategy to switch' do
136
+ allow(@strategy).to receive(:exists?) { true }
137
+ expect(@strategy).to receive(:switch)
138
+ @web.switch
139
+ end
140
+ end
141
+ end
142
+ end