cloudfoundry_blue_green_deploy 0.0.1

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.
@@ -0,0 +1,105 @@
1
+ module CloudfoundryBlueGreenDeploy
2
+ class CloudfoundryFake
3
+ def self.init_route_table(domain, web_app_name, hot_url, current_hot_color)
4
+ @@web_app_name = web_app_name
5
+ @@current_hot_color = current_hot_color
6
+ @@hot_url = hot_url
7
+ @@cf_route_table = [
8
+ Route.new("#{web_app_name}-blue", domain, "#{web_app_name}-blue"),
9
+ Route.new("#{web_app_name}-green", domain, "#{web_app_name}-green"),
10
+ Route.new(hot_url, domain, "#{web_app_name}-#{current_hot_color}")
11
+ ]
12
+ end
13
+
14
+ def self.clear_route_table
15
+ @@cf_route_table = []
16
+ end
17
+
18
+ def self.init_app_list_with_workers_for(app_name)
19
+ @@cf_app_list = [
20
+ App.new(name: "#{app_name}-worker-green", state: 'stopped'),
21
+ App.new(name: "#{app_name}-worker-blue", state: 'stopped')
22
+ ]
23
+ end
24
+
25
+ def self.init_app_list_from_names(app_names)
26
+ @@cf_app_list = []
27
+ app_names.each do |app|
28
+ ['blue', 'green'].each do |color|
29
+ @@cf_app_list << App.new(name: "#{app}-#{color}", state: 'stopped')
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.mark_app_as_started(full_worker_app_name)
35
+ @@cf_app_list.find { |app| app.name == full_worker_app_name }.state = 'started'
36
+ end
37
+
38
+ def self.mark_workers_as_started(worker_app_names, target_color)
39
+ @@cf_app_list.each do |app|
40
+ worker_app_names.each do |worker_name|
41
+ if app.name == "#{worker_name}-#{target_color}"
42
+ app.state = 'started'
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.init_app_list(apps)
49
+ @@cf_app_list = apps
50
+ end
51
+
52
+ def self.clear_app_list
53
+ @@cf_app_list = []
54
+ end
55
+
56
+ # App List Helpers
57
+
58
+ def self.replace_app(new_app)
59
+ app_index = @@cf_app_list.find_index { |existing_app| existing_app.name == new_app.name }
60
+ @@cf_app_list[app_index] = new_app
61
+ end
62
+
63
+ def self.apps
64
+ @@cf_app_list
65
+ end
66
+
67
+
68
+ def self.started_apps
69
+ @@cf_app_list.select { |app| app.state == 'started' }
70
+ end
71
+
72
+ # Route Table Helpers
73
+ def self.find_route(host)
74
+ @@cf_route_table.find { |route| route.host == host }
75
+ end
76
+
77
+ def self.add_route(route)
78
+ @@cf_route_table << route
79
+ end
80
+
81
+ def self.remove_route(host)
82
+ @@cf_route_table.delete_if { |route| route.host == host }
83
+ end
84
+
85
+ # Cloudfoundry fakes
86
+ def self.push(app)
87
+ end
88
+
89
+ def self.stop(app)
90
+ end
91
+
92
+ def self.routes
93
+ @@cf_route_table
94
+ end
95
+
96
+ def self.unmap_route(app, domain, host)
97
+ @@cf_route_table.delete_if { |route| route.app == "#{@@web_app_name}-#{@@current_hot_color}" && route.host == @@hot_url }
98
+ end
99
+
100
+ def self.map_route(app, domain, host)
101
+ @@cf_route_table.delete_if { |route| route.host == host }
102
+ @@cf_route_table.push(Route.new(host, domain, app))
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ module CloudfoundryBlueGreenDeploy
4
+ describe Cloudfoundry do
5
+ describe '#push' do
6
+ subject { Cloudfoundry.push(app) }
7
+ let(:app) { 'app_name-blue' }
8
+
9
+ it 'invokes the Cloudfoundry CLI command "push"' do
10
+ expect(CommandLine).to receive(:system).with("cf push #{app}").and_return(true)
11
+ subject
12
+ end
13
+
14
+ context 'when the Cloudfoundry push fails' do
15
+ before do
16
+ allow(CommandLine).to receive(:system).and_return(false)
17
+ end
18
+ it 'throws a CloudfoundryCliError' do
19
+ expect{ subject }.to raise_error(CloudfoundryCliError)
20
+ end
21
+ end
22
+ end
23
+
24
+ describe '#stop' do
25
+ subject { Cloudfoundry.stop(app) }
26
+ let(:app) { 'app_name-blue' }
27
+
28
+ it 'invokes the Cloudfoundry CLI command "stop"' do
29
+ expect(CommandLine).to receive(:system).with("cf stop #{app}").and_return(true)
30
+ subject
31
+ end
32
+
33
+ context 'when the Cloudfoundry push fails' do
34
+ before do
35
+ allow(CommandLine).to receive(:system).and_return(false)
36
+ end
37
+ it 'throws a CloudfoundryCliError' do
38
+ expect{ subject }.to raise_error(CloudfoundryCliError)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#apps' do
44
+ subject { Cloudfoundry.apps }
45
+ before do
46
+ allow(CommandLine).to receive(:backtick).with('cf apps').and_return(cli_apps_output)
47
+ end
48
+
49
+ context 'there is a list of apps defined in the current organization' do
50
+ let(:cli_apps_output) {
51
+ <<-CLI
52
+ Getting apps in org LA-Ping-Pong-Ding-Dongs / space carrot-soup as pivot-pong-developers@googlegroups.com...
53
+ OK
54
+
55
+ name requested state instances memory disk urls
56
+ carrot-soup-blue stopped 0/1 1G 1G carrot-soup-blue.cfapps.io
57
+ carrot-soup-green started 1/1 1G 1G la-pong.cfapps.io, carrot-soup-green.cfapps.io
58
+ red started 1/1 1G 1G relish.cfapps.io
59
+ CLI
60
+ }
61
+
62
+ it 'parses the CLI "apps" output into a collection of App objects, one for each app' do
63
+ expect(subject).to be_kind_of(Array)
64
+ expect(subject.length).to eq 3
65
+ expect(subject[0].name).to eq 'carrot-soup-blue'
66
+ expect(subject[0].state).to eq 'stopped'
67
+ expect(subject[1].name).to eq 'carrot-soup-green'
68
+ expect(subject[1].state).to eq 'started'
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ describe '#routes' do
75
+ subject { Cloudfoundry.routes }
76
+ before do
77
+ allow(CommandLine).to receive(:backtick).with('cf routes').and_return(cli_routes_output)
78
+ end
79
+
80
+ context 'there is a route defined in the current organization' do
81
+ let(:cli_routes_output) {
82
+ <<-CLI
83
+ Getting routes as pivot-pong-developers@googlegroups.com ...
84
+
85
+ host domain apps
86
+ pivot-pong-blue cfapps.io pivot-pong-staging-blue
87
+ pivot-pong-green cfapps.io pivot-pong-staging-green
88
+ la-pong cfapps.io pivot-pong-staging-green
89
+ CLI
90
+ }
91
+
92
+
93
+ it 'parses the CLI "routes" output into a collection of Route objects, one for each route' do
94
+ expect(subject).to be_kind_of(Array)
95
+ expect(subject.length).to eq 3
96
+ expect(subject[0].host).to eq 'pivot-pong-blue'
97
+ expect(subject[0].domain).to eq 'cfapps.io'
98
+ expect(subject[0].app).to eq 'pivot-pong-staging-blue'
99
+ end
100
+ end
101
+
102
+ context '"cf routes" fails with an error' do
103
+ let(:cli_routes_output) {
104
+ <<-CLI
105
+ Getting routes as pivot-pong-developers@googlegroups.com ...
106
+
107
+ FAILED
108
+ Failed fetching routes.
109
+ CLI
110
+ }
111
+ it 'throws a CloudfoundryCliError' do
112
+ expect{ subject }.to raise_error(CloudfoundryCliError)
113
+ end
114
+ end
115
+ end
116
+
117
+ describe '#map_route' do
118
+ let(:app) { 'the-app' }
119
+ let(:domain) { 'the-domain' }
120
+ let(:host) { 'the-host' }
121
+
122
+ subject { Cloudfoundry.map_route(app, domain, host) }
123
+
124
+ it 'invokes the Cloudfoundry CLI command "map-route" with the proper set of parameters' do
125
+ expect(CommandLine).to receive(:system).with("cf map-route #{app} #{domain} -n #{host}").and_return(true)
126
+ subject
127
+ end
128
+
129
+ context 'when the Cloudfoundry map-route fails' do
130
+ before do
131
+ allow(CommandLine).to receive(:system).and_return(false)
132
+ end
133
+ it 'throws a CloudfoundryCliError' do
134
+ expect{ subject }.to raise_error(CloudfoundryCliError)
135
+ end
136
+ end
137
+ end
138
+
139
+ describe '#unmap_route' do
140
+ let(:app) { 'the-app' }
141
+ let(:domain) { 'the-domain' }
142
+ let(:host) { 'the-host' }
143
+
144
+ subject { Cloudfoundry.unmap_route(app, domain, host) }
145
+
146
+ it 'invokes the Cloudfoundry CLI command "unmap-route" with the proper set of parameters' do
147
+ expect(CommandLine).to receive(:system).with("cf unmap-route #{app} #{domain} -n #{host}").and_return(true)
148
+ subject
149
+ end
150
+
151
+ context 'when the Cloudfoundry unmap-route fails' do
152
+ before do
153
+ allow(CommandLine).to receive(:system).and_return(false)
154
+ end
155
+ it 'throws a CloudfoundryCliError' do
156
+ expect{ subject }.to raise_error(CloudfoundryCliError)
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module CloudfoundryBlueGreenDeploy
4
+ describe CommandLine do
5
+ describe 'backtick' do
6
+ it 'runs the supplied command and returns the stdout string' do
7
+ expect(CommandLine.backtick('echo "sports"')).to eq "sports\n"
8
+ end
9
+ it 'ensures that cf output omits ANSI color sequences' do
10
+ expect(CommandLine.backtick('echo $CF_COLOR')).to eq "false\n"
11
+ end
12
+ end
13
+
14
+ describe 'system' do
15
+ it 'runs the supplied command using Kernel.system' do
16
+ expect(Kernel).to receive(:system)
17
+ CommandLine.system('echo "sports"')
18
+ end
19
+ end
20
+ end
21
+ end
data/spec/manifest.yml ADDED
@@ -0,0 +1,51 @@
1
+ ---
2
+ applications:
3
+
4
+ - name: the-web-app-worker-green
5
+ host: the-web-app-worker-green
6
+ size: 1GB
7
+ command: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails s -p $PORT -e $RAILS_ENV
8
+ services:
9
+ - creme-fraiche
10
+
11
+ - name: hard-worker-green
12
+ host: hard-worker-green
13
+ size: 1GB
14
+ command: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails s -p $PORT -e $RAILS_ENV
15
+ services:
16
+ - creme-fraiche
17
+
18
+ - name: the-web-app-green
19
+ host: the-web-url-green
20
+ domain: cfapps.io
21
+ size: 1GB
22
+ command: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails s -p $PORT -e $RAILS_ENV
23
+ services:
24
+ - oyster-cracker
25
+
26
+ - name: the-web-app-blue
27
+ host: the-web-url-blue
28
+ domain: cfapps.io
29
+ size: 1GB
30
+ command: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails s -p $PORT -e $RAILS_ENV
31
+ services:
32
+ - oyster-cracker
33
+
34
+ - name: the-web-app-worker-blue
35
+ host: the-web-app-worker-blue
36
+ size: 1GB
37
+ command: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails s -p $PORT -e $RAILS_ENV
38
+ services:
39
+ - creme-fraiche
40
+
41
+ - name: hard-worker-blue
42
+ host: hard-worker-blue
43
+ size: 1GB
44
+ command: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails s -p $PORT -e $RAILS_ENV
45
+ services:
46
+ - creme-fraiche
47
+
48
+ - name: the-web-app-shutter
49
+ host: the-web-url-shutter
50
+ domain: cfapps.io
51
+ command: bundle exec rackup config.ru -p $PORT -E $RACK_ENV
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ module CloudfoundryBlueGreenDeploy
4
+ describe Route do
5
+ describe '.initialize' do
6
+ context 'when supplied an empty "app"' do
7
+ it 'reports "app" as nil' do
8
+ expect(Route.new('blah', 'blah', '').app).to be_nil
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ Dir['support/**/*.rb'].each { |file| require_relative file }
2
+ require_relative '../lib/cloudfoundry_blue_green_deploy'
3
+ require 'yaml'
4
+
5
+ RSpec.configure do |config|
6
+ config.order = 'random'
7
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudfoundry_blue_green_deploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Ryan and Mariana Lenetis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Blue-green deployment tool for Cloud Foundry. Please see readme.
70
+ email:
71
+ - jryan@pivotallabs.com
72
+ - mlenetis@pivotallabs.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - cloudfoundry_blue_green_deploy.gemspec
83
+ - lib/cloudfoundry_blue_green_deploy.rb
84
+ - lib/cloudfoundry_blue_green_deploy/app.rb
85
+ - lib/cloudfoundry_blue_green_deploy/blue_green_deploy.rb
86
+ - lib/cloudfoundry_blue_green_deploy/blue_green_deploy_config.rb
87
+ - lib/cloudfoundry_blue_green_deploy/blue_green_deploy_error.rb
88
+ - lib/cloudfoundry_blue_green_deploy/cloudfoundry.rb
89
+ - lib/cloudfoundry_blue_green_deploy/command_line.rb
90
+ - lib/cloudfoundry_blue_green_deploy/railtie.rb
91
+ - lib/cloudfoundry_blue_green_deploy/route.rb
92
+ - lib/cloudfoundry_blue_green_deploy/tasks/cf.rake
93
+ - lib/cloudfoundry_blue_green_deploy/version.rb
94
+ - spec/blue_green_deploy_config_spec.rb
95
+ - spec/blue_green_deploy_spec.rb
96
+ - spec/cloudfoundry_fake.rb
97
+ - spec/cloudfoundry_spec.rb
98
+ - spec/command_line_spec.rb
99
+ - spec/manifest.yml
100
+ - spec/route_spec.rb
101
+ - spec/spec_helper.rb
102
+ homepage: ''
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.2.0
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Blue-green deployment tool for Cloud Foundry.
126
+ test_files:
127
+ - spec/blue_green_deploy_config_spec.rb
128
+ - spec/blue_green_deploy_spec.rb
129
+ - spec/cloudfoundry_fake.rb
130
+ - spec/cloudfoundry_spec.rb
131
+ - spec/command_line_spec.rb
132
+ - spec/manifest.yml
133
+ - spec/route_spec.rb
134
+ - spec/spec_helper.rb