cf_deployer 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/ChangeLog.md +16 -0
  4. data/DETAILS.md +268 -0
  5. data/FAQ.md +61 -0
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +51 -0
  8. data/LICENSE +22 -0
  9. data/QUICKSTART.md +96 -0
  10. data/README.md +36 -0
  11. data/Rakefile +32 -0
  12. data/bin/cf_deploy +10 -0
  13. data/cf_deployer.gemspec +23 -0
  14. data/lib/cf_deployer/application.rb +74 -0
  15. data/lib/cf_deployer/application_error.rb +4 -0
  16. data/lib/cf_deployer/aws_constants.rb +3 -0
  17. data/lib/cf_deployer/cli.rb +111 -0
  18. data/lib/cf_deployer/component.rb +103 -0
  19. data/lib/cf_deployer/config_loader.rb +189 -0
  20. data/lib/cf_deployer/config_validation.rb +138 -0
  21. data/lib/cf_deployer/defaults.rb +10 -0
  22. data/lib/cf_deployer/deployment_strategy/auto_scaling_group_swap.rb +102 -0
  23. data/lib/cf_deployer/deployment_strategy/base.rb +88 -0
  24. data/lib/cf_deployer/deployment_strategy/blue_green.rb +70 -0
  25. data/lib/cf_deployer/deployment_strategy/cname_swap.rb +108 -0
  26. data/lib/cf_deployer/deployment_strategy/create_or_update.rb +57 -0
  27. data/lib/cf_deployer/driver/auto_scaling_group.rb +86 -0
  28. data/lib/cf_deployer/driver/cloud_formation_driver.rb +85 -0
  29. data/lib/cf_deployer/driver/dry_run.rb +27 -0
  30. data/lib/cf_deployer/driver/elb_driver.rb +17 -0
  31. data/lib/cf_deployer/driver/instance.rb +29 -0
  32. data/lib/cf_deployer/driver/route53_driver.rb +79 -0
  33. data/lib/cf_deployer/driver/verisign_driver.rb +21 -0
  34. data/lib/cf_deployer/hook.rb +32 -0
  35. data/lib/cf_deployer/logger.rb +34 -0
  36. data/lib/cf_deployer/stack.rb +154 -0
  37. data/lib/cf_deployer/status_presenter.rb +195 -0
  38. data/lib/cf_deployer/version.rb +3 -0
  39. data/lib/cf_deployer.rb +97 -0
  40. data/spec/fakes/instance.rb +32 -0
  41. data/spec/fakes/route53_client.rb +23 -0
  42. data/spec/fakes/stack.rb +65 -0
  43. data/spec/functional/deploy_spec.rb +73 -0
  44. data/spec/functional/kill_inactive_spec.rb +57 -0
  45. data/spec/functional_spec_helper.rb +3 -0
  46. data/spec/spec_helper.rb +8 -0
  47. data/spec/unit/application_spec.rb +191 -0
  48. data/spec/unit/component_spec.rb +142 -0
  49. data/spec/unit/config_loader_spec.rb +356 -0
  50. data/spec/unit/config_validation_spec.rb +480 -0
  51. data/spec/unit/deployment_strategy/auto_scaling_group_swap_spec.rb +435 -0
  52. data/spec/unit/deployment_strategy/base_spec.rb +44 -0
  53. data/spec/unit/deployment_strategy/cname_swap_spec.rb +294 -0
  54. data/spec/unit/deployment_strategy/create_or_update_spec.rb +113 -0
  55. data/spec/unit/deployment_strategy/deployment_strategy_spec.rb +29 -0
  56. data/spec/unit/driver/auto_scaling_group_spec.rb +127 -0
  57. data/spec/unit/driver/cloud_formation_spec.rb +32 -0
  58. data/spec/unit/driver/elb_spec.rb +11 -0
  59. data/spec/unit/driver/instance_spec.rb +30 -0
  60. data/spec/unit/driver/route53_spec.rb +85 -0
  61. data/spec/unit/driver/verisign_spec.rb +18 -0
  62. data/spec/unit/hook_spec.rb +64 -0
  63. data/spec/unit/stack_spec.rb +150 -0
  64. data/spec/unit/status_presenter_spec.rb +108 -0
  65. metadata +197 -0
@@ -0,0 +1,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