cf-deploy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Made by Made Ltd
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # Rake tasks for deploying to CloudFoundry v6+
2
+
3
+ This gem provides the functionality you need to deploy your rails application to
4
+ a [CloudFoundry][CloudFoundry] provider like [Pivotal][Pivotal].
5
+
6
+ With `cf-deploy` you can:
7
+
8
+ * Define your CloudFoundry connection details in your Rakefile or using
9
+ environment variables
10
+ * Implement blue/green deployment
11
+ * Hook into your existing rake tasks for preparing deploys/syncing assets
12
+
13
+ ## Basics
14
+
15
+ The functionality comes in the shape of generated rake tasks. You require this
16
+ gem in your `Rakefile` and call the `.rake_tasks!` setup method.
17
+
18
+ ``` ruby
19
+ require 'cf-deploy'
20
+ CF::Deploy.rake_tasks!
21
+ ```
22
+
23
+ By default tasks will be created for each manifest in your `manifests/` folder.
24
+ If you have a `staging.yml` and `production.yml` you can now run the following
25
+ commands:
26
+
27
+ ``` sh
28
+ bundle exec rake cf:deploy:staging
29
+ bundle exec rake cf:deploy:production
30
+ ```
31
+
32
+ This however mimics the commands `cf push -f manifests/staging.yml` and
33
+ `cf push -f manifests/production.yml`. Not really anything helpful or new.
34
+ Things start to get more exciting when you define your environments in your
35
+ `Rakefile` along with their task dependencies just like normal rake task syntax.
36
+
37
+ ``` ruby
38
+ require 'cf-deploy'
39
+
40
+ CF::Deploy.rake_tasks! do
41
+ environment :staging => 'assets:precompile'
42
+ environment :production => [:clean, 'assets:precompile']
43
+ end
44
+ ```
45
+
46
+ Now when running `cf:deploy:staging` and `cf:deploy:production` the prerequisite
47
+ tasks will be run first.
48
+
49
+ The next thing to talk about is route mapping. You can define a route in a an
50
+ environment block like so:
51
+
52
+ ``` ruby
53
+ require 'cf-deploy'
54
+
55
+ CF::Deploy.rake_tasks! do
56
+ environment :staging => 'assets:precompile' do
57
+ route 'example.com', 'staging'
58
+ end
59
+
60
+ environment :production => [:clean, 'assets:precompile'] do
61
+ route 'example.com'
62
+ route 'example.com', 'admin'
63
+ end
64
+ end
65
+ ```
66
+
67
+ Any time you deploy an environment with one or more routes defined the routes
68
+ will be mapped to all applications in the environment's manifest.
69
+
70
+ And then things get super interesting when you start talking blue/green.
71
+
72
+ ## What is blue/green deployment?
73
+
74
+ Simply put, blue/green deployment allows you to deploy a new version of your
75
+ app, test it on a private URL and then direct your traffic to the new version
76
+ when you are ready.
77
+
78
+ You have two applications for one environment, say production. One version is
79
+ called green, the other is blue. The first time you deploy your app you either
80
+ go to blue or green. Thereafter, any changes you want to deploy you send to the
81
+ color that doesn't have your production domain pointed at it. You test it on
82
+ a private URL and then when you're happy you flip your domain to point at that.
83
+ If something then goes wrong you can then flip your domain back to the last
84
+ working version.
85
+
86
+ This gem provides rake tasks for you to deploy using this methodology as well
87
+ as the standard single app deployment process on a CloudFoundry provider.
88
+
89
+ For example you might have a straight forward deployment for staging but use
90
+ the blue/green strategy for production. Here is what your Rakefile might look
91
+ like:
92
+
93
+ ``` ruby
94
+ require 'cf-deploy'
95
+
96
+ CF::Deploy.rake_tasks! do
97
+ environment :staging => 'assets:precompile'
98
+
99
+ environment :production => 'assets:precompile' do
100
+ route 'example-app.io'
101
+ end
102
+ end
103
+ ```
104
+
105
+ You should also have `manifests/production_blue.yml` and
106
+ `manifests/production_green.yml` defined.
107
+
108
+ When you run `cf:deploy:production` for the first time (assuming neither
109
+ `production_blue.yml` or `production_green.yml` are deployed) your blue app will
110
+ be deployed and route setup.
111
+
112
+ Running `cf:deploy:production` thereafter will deploy which ever version isn't
113
+ currently deployed. Your route(s) will not be mapped automatically this time.
114
+ Nows your chance to checkout your new deployment using an alternate route. When
115
+ you're happy and want to map your route across run:
116
+
117
+ ``` sh
118
+ bundle exec rake cf:deploy:production:flip
119
+ ```
120
+
121
+ ## Installation
122
+
123
+ You need the `cf` command installed already. Grab the latest release from
124
+ the [CloudFoundry CLI][cli] repo on github.
125
+
126
+ You then need to install this gem in your project's `Gemfile`:
127
+
128
+ ``` ruby
129
+ gem 'cf-deploy', '0.1.0'
130
+ ```
131
+
132
+ ### Defining CloudFoundry details in your Rakefile
133
+
134
+ You can configure some or all of your CloudFoundry details when calling
135
+ `CF::Deploy.rake_tasks!`.
136
+
137
+ ``` ruby
138
+ require 'cf-deploy'
139
+
140
+ CF::Deploy.rake_tasks! do
141
+ api 'api.run.pivotal.io'
142
+ username 'example@example.com'
143
+ password 'SOMETHING'
144
+ organisation 'Made'
145
+ space 'development'
146
+
147
+ environment :staging => 'assets:precompile'
148
+
149
+ environment :production => 'assets:precompile' do
150
+ route 'example-app.io'
151
+ end
152
+ end
153
+ ```
154
+
155
+ All are optional. If you do not provide any you will be prompted when running
156
+ the rake tasks.
157
+
158
+ ### Defining CloudFoundry details using ENV variables
159
+
160
+ Instead of defining your CloudFoundry login details in your Rakefile and
161
+ committing them to your code repository you can instead provide them using
162
+ ENV variables on your command line:
163
+
164
+ ``` sh
165
+ export CF_API=api.run.pivotal.io
166
+ export CF_USERNAME=example@example.com
167
+ export CF_PASSWORD=SOMETHING
168
+ export CF_ORG=Made
169
+ export CF_SPACE=development
170
+ ```
171
+
172
+ Now you can run any of the `cf-deploy` rake tasks providing you have called
173
+ `CF::Deploy.rake_tasks!` in your `Rakefile`.
174
+
175
+ ## Commands
176
+
177
+ ### Deploying an environment
178
+
179
+ If you defined a staging environment in your Rakefile the following task will
180
+ have been created:
181
+
182
+ ```
183
+ bundle exec rake cf:deploy:staging
184
+ ```
185
+
186
+ Run this to deploy out your staging environment.
187
+
188
+ Any environment you define will have a task created named `cf:deploy:#{env}`.
189
+
190
+ ### Deploy the next blue/green environment
191
+
192
+ If you have defined CloudFoundry manifest files matching `manifests/*_blue.yml`
193
+ and `manifests/*_green.yml` you will be able to call `rake cf:deploy:*` without
194
+ the `_blue` or `_green`. For example with `production_blue.yml` and
195
+ `production_green.yml` you can call the following:
196
+
197
+ ```
198
+ bundle exec rake cf:deploy:production
199
+ ```
200
+
201
+ Running the deploy task for an env with blue and green manifests will trigger a
202
+ lookup to see which env is currently deployed. The task will then start
203
+ deploying the other production color, so if green is currently deployed then
204
+ blue will be deployed. If neither is currently deployed, blue will be deployed
205
+ first.
206
+
207
+ Once deployed your routing will still be pointing to the *previous deployment*.
208
+ If you run the same task again, the same environment will be deployed. That is
209
+ if green was deployed, and then you run the task, blue will be deployed, if you
210
+ run the task again, blue will be deployed again. This is because we work out
211
+ the current deployment based on where your routes are pointing and since the
212
+ deploy command for blue green environments doesn't map routes the current
213
+ deployment will not change.
214
+
215
+ #### First time proviso
216
+
217
+ This isn't the case for a first time deploy. The first time you deploy your
218
+ blue environment will be deployed and any defined routes will be mapped to all
219
+ apps defined in your blue manifest.
220
+
221
+ ### Switch routes over to new environment
222
+
223
+ In order to flip your routes from blue to green or vice-versa you need to run
224
+ the following task.
225
+
226
+ ```
227
+ bundle exec rake cf:deploy:production:flip
228
+ ```
229
+
230
+ This will go ahead and map routes to whatever color the routes aren't mapped to
231
+ and then unmap the other color. At this point your new production will be
232
+ deployed and live.
233
+
234
+ ## Credits
235
+
236
+ [![made](https://s3-eu-west-1.amazonaws.com/made-assets/googleapps/google-apps.png)][made]
237
+
238
+ Developed and maintained by [Made][made]. Key contributions:
239
+
240
+ * [Luke Morton](https://github.com/DrPheltRight)
241
+
242
+ ## License
243
+
244
+ Copyright © 2014 Made by Made Ltd. It is free software, and may be
245
+ redistributed under the terms specified in the [MIT-LICENSE][license] file.
246
+
247
+ [CloudFoundry]: http://www.cloudfoundry.org/
248
+ [Pivotal]: https://run.pivotal.io/
249
+ [cli]: https://github.com/cloudfoundry/cli/releases
250
+ [made]: http://www.madetech.co.uk?ref=github&repo=ydtd_frontend
251
+ [license]: https://github.com/madebymade/cf-deploy/blob/master/LICENSE
data/lib/cf-deploy.rb ADDED
@@ -0,0 +1 @@
1
+ require 'cf/deploy'
data/lib/cf/deploy.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'cf/deploy/version'
2
+ require 'cf/deploy/config'
3
+ require 'cf/deploy/env_config'
4
+ require 'cf/deploy/commands'
5
+ require 'rake'
6
+
7
+ module CF
8
+ class Deploy
9
+ class << self
10
+ def rake_tasks!(&block)
11
+ new(Config.new(&block)).tasks
12
+ end
13
+ end
14
+
15
+ include Commands
16
+
17
+ attr_accessor :config
18
+
19
+ def initialize(config)
20
+ @config = config
21
+ end
22
+
23
+ def tasks
24
+ [define_login_task].concat(deploy_tasks)
25
+ end
26
+
27
+ def deploy_tasks
28
+ config[:environments].map { |env| define_deploy_task(env) }
29
+ end
30
+
31
+ def define_login_task
32
+ return Rake::Task['cf:login'] if Rake::Task.task_defined?('cf:login')
33
+
34
+ Rake::Task.define_task('cf:login') do
35
+ login(config)
36
+ end
37
+ end
38
+
39
+ def define_deploy_task(env)
40
+ blue_green_task(env) if env[:deployments].size > 1
41
+
42
+ env[:deployments].each do |deployment|
43
+ Rake::Task.define_task(deployment[:task_name] => env[:deps]) do
44
+ push(deployment[:manifest])
45
+
46
+ env[:routes].each do |route|
47
+ deployment[:app_names].each do |app_name|
48
+ map_route(route, app_name)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def first_domain(env)
56
+ env[:routes].first.values_at(:host, :domain).compact.join('.')
57
+ end
58
+
59
+ def next_production(env)
60
+ current_production(first_domain(env)) != 'blue' ? 'blue' : 'green'
61
+ end
62
+
63
+ def blue_green_task(env)
64
+ Rake::Task.define_task(env[:task_name] => env[:deps]) do
65
+ task_name = EnvConfig.task_name("#{env[:name]}_#{next_production(env)}")
66
+ Rake::Task[task_name].invoke
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,33 @@
1
+ module CF
2
+ class Deploy
3
+ module Commands
4
+ def login(config)
5
+ login_cmd = ['cf login']
6
+
7
+ login_cmd << Config::VALID_CF_KEYS
8
+ .reject { |key| config[key].nil? }
9
+ .map { |key| "-#{key.to_s[0]} #{config[key]}" }
10
+
11
+ Kernel.system(login_cmd.flatten.join(' '))
12
+ end
13
+
14
+ def push(manifest)
15
+ Kernel.system("cf push -f #{manifest}")
16
+ end
17
+
18
+ def map_route(route, app_name)
19
+ map_cmd = "cf map-route #{app_name} #{route[:domain]}"
20
+ map_cmd = "#{map_cmd} -n #{route[:hostname]}" unless route[:hostname].nil?
21
+ Kernel.system(map_cmd)
22
+ end
23
+
24
+ def current_production(domain)
25
+ io = IO.popen("cf routes | grep #{domain}")
26
+ matches = /(blue|green)/.match(io.read)
27
+ io.close
28
+ return if matches.nil?
29
+ matches[1].strip
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,81 @@
1
+ module CF
2
+ class Deploy
3
+ class Config < Hash
4
+ VALID_CF_KEYS = [:api, :username, :password, :organisation, :space]
5
+
6
+ attr_reader :environments_to_be_loaded
7
+
8
+ def initialize(&block)
9
+ @environments_to_be_loaded = []
10
+
11
+ merge!(:manifest_glob => 'manifests/*',
12
+ :api => nil,
13
+ :username => nil,
14
+ :password => nil,
15
+ :organisation => nil,
16
+ :space => nil)
17
+
18
+ instance_eval(&block) if block_given?
19
+
20
+ self[:environments] = environments(manifests_by_env)
21
+ end
22
+
23
+ def [](key)
24
+ from_env(key) || super
25
+ end
26
+
27
+ def from_env(key)
28
+ ENV["CF_#{key.upcase}"] if VALID_CF_KEYS.include?(key)
29
+ end
30
+
31
+ def manifests_by_env
32
+ Dir[self[:manifest_glob]].reduce({}) do |envs, manifest|
33
+ if manifest =~ /_blue.yml$/
34
+ env = File.basename(manifest, '_blue.yml').to_sym
35
+ elsif manifest =~ /_green.yml$/
36
+ env = File.basename(manifest, '_green.yml').to_sym
37
+ else
38
+ env = File.basename(manifest, '.yml').to_sym
39
+ end
40
+
41
+ envs[env] ||= []
42
+ envs[env] << manifest
43
+ envs
44
+ end
45
+ end
46
+
47
+ def environments(manifests_by_env)
48
+ environments = []
49
+
50
+ environments_to_be_loaded.each do |(env, block)|
51
+ if env.is_a?(Hash)
52
+ name, deps = env.first
53
+ deps = (['cf:login'] << deps).flatten
54
+ else
55
+ name = env
56
+ deps = ['cf:login']
57
+ end
58
+
59
+ manifests = manifests_by_env.delete(name) || []
60
+ environments << EnvConfig.new(name, deps, manifests, &block)
61
+ end
62
+
63
+ manifests_by_env.each do |(name, manifests)|
64
+ environments << EnvConfig.new(name, ['cf:login'], manifests)
65
+ end
66
+
67
+ environments
68
+ end
69
+
70
+ # Config setter methods
71
+ #
72
+ def manifest_glob(glob) self[:manifest_glob] = glob end
73
+ def api(api) self[:api] = api end
74
+ def username(username) self[:username] = username end
75
+ def password(password) self[:password] = password end
76
+ def organisation(organisation) self[:organisation] = organisation end
77
+ def space(space) self[:space] = space end
78
+ def environment(env, &block) @environments_to_be_loaded << [env, block] end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,57 @@
1
+ require 'yaml'
2
+
3
+ module CF
4
+ class Deploy
5
+ class EnvConfig < Hash
6
+ def self.task_name(name)
7
+ "cf:deploy:#{name}"
8
+ end
9
+
10
+ def initialize(name, deps, manifests, &block)
11
+ merge!(:name => name,
12
+ :task_name => EnvConfig.task_name(name),
13
+ :deps => deps,
14
+ :routes => [],
15
+ :manifests => manifests)
16
+
17
+ instance_eval(&block) if block_given?
18
+
19
+ self[:deployments] = deployments
20
+ end
21
+
22
+ def deployments
23
+ self[:manifests].map { |manifest| deployment_for_manifest(manifest) }
24
+ end
25
+
26
+ def deployment_for_manifest(manifest)
27
+ if self[:manifests].size > 1
28
+ task_name = EnvConfig.task_name(File.basename(manifest, '.yml').to_sym)
29
+ else
30
+ task_name = self[:task_name]
31
+ end
32
+
33
+ {:task_name => task_name,
34
+ :manifest => manifest,
35
+ :app_names => app_names_for_manifest(manifest)}
36
+ end
37
+
38
+ def app_names_for_manifest(manifest)
39
+ YAML.load_file(manifest)['applications'].map { |a| a['name'] }
40
+ end
41
+
42
+ # Environment config setter methods
43
+ #
44
+ def manifest(manifest)
45
+ self[:manifests] << manifest
46
+ end
47
+
48
+ def manifests(manifests)
49
+ self[:manifests].concat(manifests)
50
+ end
51
+
52
+ def route(domain, hostname = nil)
53
+ self[:routes] << {:domain => domain, :hostname => hostname}
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ module CF
2
+ class Deploy
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'cf-deploy'
3
+ require 'rake'
4
+
5
+ describe CF::Deploy do
6
+ before :each do
7
+ Rake::Task.clear
8
+ end
9
+
10
+ context 'Blue/green deployment task' do
11
+ let :rake_tasks! do
12
+ Dir.chdir('spec/') do
13
+ described_class.rake_tasks! do
14
+ environment :production do
15
+ route 'example.com'
16
+ route 'example.com', '2'
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ it 'should exist if *_blue.yml and *_green.yml manifests exist' do
23
+ rake_tasks!
24
+ expect(Rake::Task['cf:deploy:production']).to be_a(Rake::Task)
25
+ end
26
+
27
+ it 'should deploy blue if not currently deployed' do
28
+ rake_tasks!
29
+ expect(Kernel).to receive(:system).with('cf login').ordered
30
+ expect(IO).to receive(:popen).with('cf routes | grep example.com') { double(:read => '', :close => nil) }
31
+ expect(Kernel).to receive(:system).with('cf push -f manifests/production_blue.yml').ordered
32
+ expect(Kernel).to receive(:system).with('cf map-route production-blue-app example.com').ordered
33
+ expect(Kernel).to receive(:system).with('cf map-route production-blue-app example.com -n 2').ordered
34
+ Rake::Task['cf:deploy:production'].invoke
35
+ end
36
+
37
+ it 'should deploy blue if green currently deployed' do
38
+ rake_tasks!
39
+ expect(Kernel).to receive(:system).with('cf login').ordered
40
+ expect(IO).to receive(:popen).with('cf routes | grep example.com') { double(:read => 'production-green-app', :close => nil) }
41
+ expect(Kernel).to receive(:system).with('cf push -f manifests/production_blue.yml').ordered
42
+ expect(Kernel).to receive(:system).with('cf map-route production-blue-app example.com').ordered
43
+ expect(Kernel).to receive(:system).with('cf map-route production-blue-app example.com -n 2').ordered
44
+ Rake::Task['cf:deploy:production'].invoke
45
+ end
46
+
47
+ it 'should deploy green if blue currently deployed' do
48
+ rake_tasks!
49
+ expect(Kernel).to receive(:system).with('cf login').ordered
50
+ expect(IO).to receive(:popen).with('cf routes | grep example.com') { double(:read => 'production-blue-app', :close => nil) }
51
+ expect(Kernel).to receive(:system).with('cf push -f manifests/production_green.yml').ordered
52
+ expect(Kernel).to receive(:system).with('cf map-route production-green-app example.com').ordered
53
+ expect(Kernel).to receive(:system).with('cf map-route production-green-app example.com -n 2').ordered
54
+ Rake::Task['cf:deploy:production'].invoke
55
+ end
56
+
57
+ xit 'should throw exception if no routes defined for blue/green task' do
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+ require 'cf-deploy'
3
+ require 'rake'
4
+
5
+ describe CF::Deploy do
6
+ before :each do
7
+ Rake::Task.clear
8
+ end
9
+
10
+ context 'Rake::Task[cf:deploy:XX]' do
11
+ it 'should run a manifest' do
12
+ Dir.chdir('spec/') do
13
+ described_class.rake_tasks!
14
+ end
15
+
16
+ expect(Kernel).to receive(:system).with('cf login').ordered
17
+ expect(Kernel).to receive(:system).with('cf push -f manifests/staging.yml').ordered
18
+ Rake::Task['cf:deploy:staging'].invoke
19
+ end
20
+
21
+ it 'should setup a route if defined after pushing manifest' do
22
+ Dir.chdir('spec/') do
23
+ described_class.rake_tasks! do
24
+ environment :test do
25
+ route 'testexample.com'
26
+ end
27
+ end
28
+ end
29
+
30
+ expect(Kernel).to receive(:system).with('cf login').ordered
31
+ expect(Kernel).to receive(:system).with('cf push -f manifests/test.yml').ordered
32
+ expect(Kernel).to receive(:system).with('cf map-route test-app testexample.com').ordered
33
+ Rake::Task['cf:deploy:test'].invoke
34
+ end
35
+
36
+ it 'should setup a route with a hostname if defined' do
37
+ Dir.chdir('spec/') do
38
+ described_class.rake_tasks! do
39
+ environment :test do
40
+ route 'example.com', 'test'
41
+ end
42
+ end
43
+ end
44
+
45
+ expect(Kernel).to receive(:system).with('cf login').ordered
46
+ expect(Kernel).to receive(:system).with('cf push -f manifests/test.yml').ordered
47
+ expect(Kernel).to receive(:system).with('cf map-route test-app example.com -n test')
48
+ Rake::Task['cf:deploy:test'].invoke
49
+ end
50
+
51
+ it 'should setup multiple routes if defined' do
52
+ Dir.chdir('spec/') do
53
+ described_class.rake_tasks! do
54
+ environment :test do
55
+ route 'example.com'
56
+ route 'example.com', '2'
57
+ end
58
+ end
59
+ end
60
+
61
+ expect(Kernel).to receive(:system).with('cf login').ordered
62
+ expect(Kernel).to receive(:system).with('cf push -f manifests/test.yml').ordered
63
+ expect(Kernel).to receive(:system).with('cf map-route test-app example.com').ordered
64
+ expect(Kernel).to receive(:system).with('cf map-route test-app example.com -n 2').ordered
65
+ Rake::Task['cf:deploy:test'].invoke
66
+ end
67
+
68
+ xit 'should not map routes if push fails' do
69
+ end
70
+
71
+ xit 'should throw decent error if manifest does not exist' do
72
+ end
73
+
74
+ xit 'should throw decent error if manifest invalid' do
75
+ end
76
+
77
+ it 'should allow individual manifest to be specified' do
78
+ Dir.chdir('spec/') do
79
+ CF::Deploy.rake_tasks! do
80
+ environment :custom_manifest do
81
+ manifest 'manifests/staging.yml'
82
+ end
83
+ end
84
+ end
85
+
86
+ expect(Kernel).to receive(:system).with('cf login').ordered
87
+ expect(Kernel).to receive(:system).with('cf push -f manifests/staging.yml').ordered
88
+ Rake::Task['cf:deploy:custom_manifest'].invoke
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'cf-deploy'
3
+ require 'rake'
4
+
5
+ describe CF::Deploy do
6
+ before :each do
7
+ Rake::Task.clear
8
+ end
9
+
10
+ context 'Rake::Task[cf:login]' do
11
+ it 'should run `cf login` without arguments if none provided' do
12
+ described_class.rake_tasks!
13
+ expect(Kernel).to receive(:system).with('cf login')
14
+ Rake::Task['cf:login'].invoke
15
+ end
16
+
17
+ it 'should include defined details' do
18
+ described_class.rake_tasks! do
19
+ api 'api.run.pivotal.io'
20
+ end
21
+
22
+ expect(Kernel).to receive(:system).with('cf login -a api.run.pivotal.io')
23
+ Rake::Task['cf:login'].invoke
24
+ end
25
+
26
+ it 'should include all defined details' do
27
+ described_class.rake_tasks! do
28
+ api 'api'
29
+ username 'test'
30
+ password 'pass'
31
+ organisation 'org'
32
+ space 'space'
33
+ end
34
+
35
+ expect(Kernel).to receive(:system).with('cf login -a api -u test -p pass -o org -s space')
36
+ Rake::Task['cf:login'].invoke
37
+ end
38
+
39
+ it 'should include all details provided in ENV' do
40
+ {'CF_API' => 'api',
41
+ 'CF_USERNAME' => 'test',
42
+ 'CF_PASSWORD' => 'pass',
43
+ 'CF_ORGANISATION' => 'org',
44
+ 'CF_SPACE' => 'space'}.each do |(k, v)|
45
+ expect(ENV).to receive(:[]).with(k).and_return(v).at_least(:once)
46
+ end
47
+
48
+ expect(Kernel).to receive(:system).with('cf login -a api -u test -p pass -o org -s space')
49
+ described_class.rake_tasks!
50
+ Rake::Task['cf:login'].invoke
51
+ end
52
+
53
+ it 'should mix and match ENV and defined details with ENV having precedence' do
54
+ {'CF_API' => nil,
55
+ 'CF_USERNAME' => 'test',
56
+ 'CF_PASSWORD' => 'pass',
57
+ 'CF_ORGANISATION' => 'org',
58
+ 'CF_SPACE' => nil}.each do |(k, v)|
59
+ expect(ENV).to receive(:[]).with(k).and_return(v).at_least(:once)
60
+ end
61
+
62
+ expect(Kernel).to receive(:system).with('cf login -a api -u test -p pass -o org')
63
+
64
+ described_class.rake_tasks! do
65
+ api 'api'
66
+ organisation 'will be overridden by ENV[CF_ORGANISATION]'
67
+ end
68
+
69
+ Rake::Task['cf:login'].invoke
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require 'cf-deploy'
3
+ require 'rake'
4
+
5
+ describe CF::Deploy do
6
+ before :each do
7
+ Rake::Task.clear
8
+ end
9
+
10
+ context '.rake_tasks!' do
11
+ it 'should install task for each manifest' do
12
+ Dir.chdir('spec/') do
13
+ described_class.rake_tasks!
14
+ end
15
+
16
+ expect(Rake::Task['cf:deploy:staging']).to be_a(Rake::Task)
17
+ expect(Rake::Task['cf:deploy:test']).to be_a(Rake::Task)
18
+ end
19
+
20
+ it 'should install login task' do
21
+ Dir.chdir('spec/') do
22
+ described_class.rake_tasks!
23
+ end
24
+
25
+ expect(Rake::Task['cf:login']).to be_a(Rake::Task)
26
+ end
27
+
28
+ it 'should install a login task as a prerequisite for deploy tasks' do
29
+ Dir.chdir('spec/') do
30
+ described_class.rake_tasks!
31
+ end
32
+
33
+ expect(Rake::Task['cf:deploy:staging'].prerequisite_tasks[0]).to be(Rake::Task['cf:login'])
34
+ end
35
+
36
+ it 'should install tasks with prerequisites' do
37
+ expected_task_1 = Rake::Task.define_task('asset:precompile')
38
+ expected_task_2 = Rake::Task.define_task(:clean)
39
+
40
+ Dir.chdir('spec/') do
41
+ described_class.rake_tasks! do
42
+ environment :staging => 'asset:precompile'
43
+ environment :test => ['asset:precompile', :clean]
44
+ end
45
+ end
46
+
47
+ expect(Rake::Task['cf:deploy:staging'].prerequisite_tasks[1]).to be(expected_task_1)
48
+ expect(Rake::Task['cf:deploy:test'].prerequisite_tasks[2]).to be(expected_task_2)
49
+ end
50
+
51
+ it 'should have a configurable manifest glob options' do
52
+ Dir.chdir('spec/') do
53
+ described_class.rake_tasks! do
54
+ manifest_glob 'manifests/staging.yml'
55
+ end
56
+ end
57
+
58
+ expect(Rake::Task.tasks.count).to eq(2)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
8
+
9
+ SimpleCov.start
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cf-deploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Luke Morton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-07-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.5'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.5'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 3.0.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.7.1
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.7.1
78
+ - !ruby/object:Gem::Dependency
79
+ name: coveralls
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.7.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.7.0
94
+ description:
95
+ email:
96
+ - luke@madebymade.co.uk
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - lib/cf/deploy/commands.rb
102
+ - lib/cf/deploy/config.rb
103
+ - lib/cf/deploy/env_config.rb
104
+ - lib/cf/deploy/version.rb
105
+ - lib/cf/deploy.rb
106
+ - lib/cf-deploy.rb
107
+ - spec/blue_green_task_spec.rb
108
+ - spec/deploy_task_spec.rb
109
+ - spec/login_task_spec.rb
110
+ - spec/rake_tasks_spec.rb
111
+ - spec/spec_helper.rb
112
+ - LICENSE
113
+ - README.md
114
+ homepage: https://github.com/madebymade/cf-deploy
115
+ licenses:
116
+ - MIT
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ segments:
128
+ - 0
129
+ hash: 3630708785980553311
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ segments:
137
+ - 0
138
+ hash: 3630708785980553311
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 1.8.23
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: Rake tasks for deploying to CloudFoundry v6+
145
+ test_files: []