yad 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
1
+ module Yad
2
+ module Scm
3
+ class Git
4
+ def self.build_checkout_command(repository, destination_directory, options = {})
5
+ default_options = { :revision => 'origin/master', :enable_submodules => true }
6
+ options = default_options.merge(options)
7
+
8
+ commands = ["([ -d #{destination_directory}/cached-copy/.git ] && echo 'Existing repository found' || git clone #{repository} #{destination_directory}/cached-copy)",
9
+ "cd #{destination_directory}/cached-copy",
10
+ "git fetch",
11
+ "git reset --hard #{options[:revision]}"
12
+ ]
13
+
14
+ if options[:enable_submodules]
15
+ commands << [ "git submodule -q init",
16
+ "git submodule -q update"
17
+ ]
18
+ end
19
+ commands.join(" && ")
20
+ end
21
+
22
+ def self.build_export_command(source_directory, destination_directory)
23
+ "mkdir -p #{destination_directory} && rsync -a -f '- .git' #{source_directory}/cached-copy/ #{destination_directory}"
24
+ end
25
+
26
+ def self.build_inline_revision_identifier_command(scm_directory, options = {})
27
+ default_options = { :revision => 'origin/master' }
28
+ options = default_options.merge(options)
29
+ "`cd #{scm_directory}/cached-copy && git rev-parse #{options[:revision]}`"
30
+ end
31
+
32
+ def self.define_tasks
33
+ return if @tasks_already_defined
34
+ @tasks_already_defined = true
35
+ namespace :yad do
36
+ namespace :scm do
37
+
38
+ desc "Updates the source code on the application servers and exports it as the latest release"
39
+ remote_task :update, :roles => :app do
40
+ begin
41
+ options = Rake::RemoteTask.get_options_hash(:revision, :enable_submodules)
42
+ checkout_command = Yad::Scm::Git.build_checkout_command(repository, scm_path, options)
43
+ export_command = Yad::Scm::Git.build_export_command(scm_path, release_path)
44
+ cmd = Yad::Core.build_update_source_code_command(checkout_command, export_command, release_path)
45
+ run(cmd)
46
+ puts("source code updated on #{target_host}")
47
+ rescue => e
48
+ cmd = Yad::Core.build_remove_directory_command(release_path)
49
+ run(cmd)
50
+ raise e
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end # class Git
60
+
61
+ end # module Scm
62
+ end # module Yad
@@ -0,0 +1,62 @@
1
+ module Yad
2
+ module Web
3
+ class Apache
4
+ def self.build_start_command(options = {})
5
+ default_options = { :apache_command => 'apachectl' }
6
+ options = default_options.merge(options)
7
+ "#{options[:apache_command]} start"
8
+ end
9
+
10
+ def self.build_stop_command(options = {})
11
+ default_options = { :apache_command => 'apachectl' }
12
+ options = default_options.merge(options)
13
+ "#{options[:apache_command]} stop"
14
+ end
15
+
16
+ def self.build_restart_command(options = {})
17
+ default_options = { :apache_command => 'apachectl' }
18
+ options = default_options.merge(options)
19
+ "#{options[:apache_command]} restart"
20
+ end
21
+
22
+ def self.define_tasks
23
+ return if @tasks_already_defined
24
+ @tasks_already_defined = true
25
+ namespace :yad do
26
+ namespace :web do
27
+
28
+ desc "Starts the web server"
29
+ remote_task :start, :roles => :web do
30
+ options = Rake::RemoteTask.get_options_hash(:apache_command)
31
+ cmd = Yad::Web::Apache.build_start_command(options)
32
+ use_sudo_for_apache = Rake::RemoteTask.fetch(:use_sudo_for_apache, true)
33
+ if use_sudo_for_apache
34
+ sudo(cmd)
35
+ else
36
+ run(cmd)
37
+ end
38
+ puts("Apache started on #{target_host}")
39
+ end
40
+
41
+ desc "Stops the web server"
42
+ remote_task :stop, :roles => :web do
43
+ options = Rake::RemoteTask.get_options_hash(:apache_command)
44
+ cmd = Yad::Web::Apache.build_stop_command(options)
45
+ use_sudo_for_apache = Rake::RemoteTask.fetch(:use_sudo_for_apache, true)
46
+ if use_sudo_for_apache
47
+ sudo(cmd)
48
+ else
49
+ run(cmd)
50
+ end
51
+ puts("Apache stopped on #{target_host}")
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end # class Apache
60
+
61
+ end # module Web
62
+ end # module Yad
@@ -0,0 +1,394 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe Rake::RemoteTask do
4
+ def create_example_task(options = {})
5
+ task = Rake::RemoteTask.remote_task(:test_task, options)
6
+ task.commands = []
7
+ task.output = []
8
+ task.error = []
9
+ task.action = nil
10
+ task
11
+ end
12
+
13
+ def capture_output
14
+ require 'stringio'
15
+ orig_stdout = $stdout.dup
16
+ orig_stderr = $stderr.dup
17
+ captured_stdout = StringIO.new
18
+ captured_stderr = StringIO.new
19
+ $stdout = captured_stdout
20
+ $stderr = captured_stderr
21
+ yield
22
+ captured_stdout.rewind
23
+ captured_stderr.rewind
24
+ return captured_stdout, captured_stderr
25
+ ensure
26
+ $stdout = orig_stdout
27
+ $stderr = orig_stderr
28
+ end
29
+
30
+ it "should list all remote hosts" do
31
+ set_example_hosts
32
+ Rake::RemoteTask.all_hosts.should eql(%w[app.example.com db.example.com])
33
+ end
34
+
35
+ it "should fetch variables that have been set" do
36
+ set :foo, 5
37
+ Rake::RemoteTask.fetch(:foo).should eql(5)
38
+ end
39
+
40
+ it "should fetch the given default when a variable is not set" do
41
+ Rake::RemoteTask.fetch(:not_here, 5).should eql(5)
42
+ end
43
+
44
+ it "should assign multiple roles to one remote host" do
45
+ Rake::RemoteTask.host "test.example.com", :app, :db
46
+ Rake::RemoteTask.roles[:app].should == { "test.example.com" => {} }
47
+ Rake::RemoteTask.roles[:db].should == { "test.example.com" => {} }
48
+ end
49
+
50
+ it "should error when the host name is invalid" do
51
+ lambda { Rake::RemoteTask.host nil, :web }.should raise_error(ArgumentError)
52
+ end
53
+
54
+ it "should assign multiple roles to multiple remote hosts" do
55
+ Rake::RemoteTask.host "test.example.com", :app, :db
56
+ Rake::RemoteTask.host "yarr.example.com", :app, :db, :no_release => true
57
+
58
+ expected = {
59
+ "test.example.com" => {},
60
+ "yarr.example.com" => {:no_release => true}
61
+ }
62
+
63
+ Rake::RemoteTask.roles[:app].should == expected
64
+ Rake::RemoteTask.roles[:db].should == expected
65
+ Rake::RemoteTask.roles[:app]["test.example.com"].object_id.should_not eql(Rake::RemoteTask.roles[:db]["test.example.com"].object_id)
66
+ end
67
+
68
+ it "should get remote hosts for an array of roles" do
69
+ set_example_hosts
70
+ Rake::RemoteTask.hosts_for([:app, :db]).should eql(%w[app.example.com db.example.com])
71
+ end
72
+
73
+ it "should get remote hosts for one role" do
74
+ set_example_hosts
75
+ Rake::RemoteTask.host "app2.example.com", :app
76
+ Rake::RemoteTask.hosts_for(:app).should eql(%w[app.example.com app2.example.com])
77
+ end
78
+
79
+ it "should get remote hosts for multiple roles" do
80
+ set_example_hosts
81
+ Rake::RemoteTask.hosts_for(:app, :db).should eql(%w[app.example.com db.example.com])
82
+ end
83
+
84
+ it "should get a unique list of remote hosts for given roles" do
85
+ set_example_hosts
86
+ Rake::RemoteTask.host "app.example.com", :web
87
+ Rake::RemoteTask.hosts_for(:app, :db, :web).should eql(%w[app.example.com db.example.com])
88
+ end
89
+
90
+ it "should not set defaults for required configuration variables" do
91
+ Rake::RemoteTask.set_defaults
92
+ lambda { Rake::RemoteTask.repository }.should raise_error(Rake::RemoteTask::ConfigurationError)
93
+ lambda { Rake::RemoteTask.deploy_to }.should raise_error(Rake::RemoteTask::ConfigurationError)
94
+ lambda { Rake::RemoteTask.domain }.should raise_error(Rake::RemoteTask::ConfigurationError)
95
+ end
96
+
97
+ it "should assign a role for one remote host" do
98
+ Rake::RemoteTask.role :app, "test.example.com"
99
+ Rake::RemoteTask.roles[:app].should == { "test.example.com" => {} }
100
+ end
101
+
102
+ it "should assign a role for multiple remote hosts" do
103
+ Rake::RemoteTask.role :app, "test.example.com", :primary => true
104
+ Rake::RemoteTask.role :db, "yarr.example.com", :no_release => true
105
+ Rake::RemoteTask.roles[:db].should == { "yarr.example.com" => { :no_release => true } }
106
+ Rake::RemoteTask.roles[:app].should == { "test.example.com" => { :primary => true } }
107
+ end
108
+
109
+ it "should increase the number of tasks when created" do
110
+ task = Rake::RemoteTask.remote_task(:test_task) { 5 }
111
+ Rake.application.tasks.size.should eql(@task_count + 1)
112
+ end
113
+
114
+ it "should assign an empty set of roles if none are supplied" do
115
+ task = Rake::RemoteTask.remote_task(:test_task) { 5 }
116
+ task.options.should == { :roles => [] }
117
+ end
118
+
119
+ it "should assign all remote hosts to a task by default" do
120
+ set_example_hosts
121
+ task = Rake::RemoteTask.remote_task(:test_task) { 5 }
122
+ task.target_hosts.should eql(%w[app.example.com db.example.com])
123
+ end
124
+
125
+ it "should override hosts from environment" do
126
+ old_env_hosts = ENV["HOSTS"]
127
+ ENV["HOSTS"] = 'other1.example.com, other2.example.com'
128
+ set_example_hosts
129
+ task = Rake::RemoteTask.remote_task(:test_task) { 5 }
130
+ task.target_hosts.should eql(%w[other1.example.com other2.example.com])
131
+ ENV["HOSTS"] = old_env_hosts
132
+ end
133
+
134
+ it "should have access set variables within task body" do
135
+ set(:some_variable, 5)
136
+ Rake::RemoteTask.host 'www.example.com', :app
137
+ Rake::RemoteTask.remote_task(:some_task) do $some_task_result = some_variable end
138
+ Rake::Task['some_task'].execute nil
139
+ Rake::RemoteTask.fetch(:some_variable).should eql($some_task_result)
140
+ end
141
+
142
+ it "should assign roles when creating a task" do
143
+ task = Rake::RemoteTask.remote_task :test_task, :roles => [:app, :db] do
144
+ fail "should not run"
145
+ end
146
+ task.options.should == { :roles => [:app, :db] }
147
+ end
148
+
149
+ it "should allow hosts to be assigned to a role after a task for a role has been created" do
150
+ task = Rake::RemoteTask.remote_task :test_task, :roles => :web do 5 end
151
+ Rake::RemoteTask.host 'www.example.com', :web
152
+ task.target_hosts.should eql(%w[www.example.com])
153
+ end
154
+
155
+ it "should allow for roles to be overridden" do
156
+ host "db1", :db
157
+ host "db2", :db
158
+ host "db3", :db
159
+ host "master", :master_db
160
+
161
+ remote_task(:migrate_the_db, :roles => [:db]) { fail "bad!" }
162
+ task = Rake::Task["migrate_the_db"]
163
+ task.target_hosts.should eql(%w[db1 db2 db3])
164
+
165
+ task.options[:roles] = :master_db
166
+ task.target_hosts.should eql(%w[master])
167
+
168
+ task.options[:roles] = [:master_db]
169
+ task.target_hosts.should eql(%w[master])
170
+ end
171
+
172
+ it "should set variables" do
173
+ set :test, 5
174
+ Rake::RemoteTask.test.should eql(5)
175
+ end
176
+
177
+ it "should do lazy evaluation of set blocks" do
178
+ set(:test) { fail "lose" }
179
+ lambda { Rake::RemoteTask.test }.should raise_error(RuntimeError)
180
+ end
181
+
182
+ it "should evaluate a set block the first time" do
183
+ x = 1
184
+ set(:test) { x += 2 }
185
+ Rake::RemoteTask.test.should eql(3)
186
+ Rake::RemoteTask.test.should eql(3)
187
+ end
188
+
189
+ it "should have access to set variables within set blocks" do
190
+ Rake::RemoteTask.instance_eval do
191
+ set(:var_one) { var_two }
192
+ set(:var_two) { var_three }
193
+ set(:var_three) { 5 }
194
+ end
195
+
196
+ Rake::RemoteTask.var_one.should eql(5)
197
+ end
198
+
199
+ it "should error when a value and a block is supplied with set" do
200
+ lambda { set(:test, 5) { 6 } }.should raise_error(ArgumentError, "cannot provide both a value and a block")
201
+ end
202
+
203
+ it "should allow a variable to be set to nil" do
204
+ set(:test, nil)
205
+ Rake::RemoteTask.test.should be_nil
206
+ end
207
+
208
+ it "should not allow variables with reserved names to be set" do
209
+ $TESTING = false
210
+ lambda { set(:all_hosts, []) }.should raise_error(ArgumentError, "cannot set reserved name: 'all_hosts'")
211
+ $TESTING = true
212
+ end
213
+
214
+ it "should allow a variable to be set to false" do
215
+ set(:can_set_nil, nil)
216
+ set(:lies_are, false)
217
+
218
+ Rake::RemoteTask.can_set_nil.should be_nil
219
+ Rake::RemoteTask.lies_are.should be_false
220
+ end
221
+
222
+ it "should fetch false as a default for a variable" do
223
+ Rake::RemoteTask.fetch(:unknown, false).should be_false
224
+ end
225
+
226
+ it "should be enhanced with prerequisites and actions when it has been created with a body" do
227
+ set_example_hosts
228
+ body = Proc.new { 5 }
229
+ task = Rake::RemoteTask.remote_task(:some_task => :foo, &body)
230
+ action = Rake::RemoteTask::Action.new(task, body)
231
+ task.remote_actions.should == [action]
232
+ action.task.should eql(task)
233
+ task.prerequisites.should eql(["foo"])
234
+ end
235
+
236
+ it "should not be enhanced with prerequisites or actions when it has been created without a body" do
237
+ set_example_hosts
238
+ task = create_example_task
239
+ task.remote_actions.should be_empty
240
+ task.prerequisites.should be_empty
241
+ end
242
+
243
+ it "should execute on all remote hosts when no role is given" do
244
+ set_example_hosts
245
+ set :some_variable, 1
246
+ x = 5
247
+ task = Rake::RemoteTask.remote_task(:some_task) { x += some_variable }
248
+ task.execute nil
249
+ task.some_variable.should eql(1)
250
+ x.should eql(7)
251
+ end
252
+
253
+ it "should allow variables to be set inside of the body" do
254
+ host "app.example.com", :app
255
+ task = Rake::RemoteTask.remote_task(:target_task) { set(:test_target_host, target_host) }
256
+ task.execute nil
257
+ Rake::RemoteTask.fetch(:test_target_host).should eql("app.example.com")
258
+ end
259
+
260
+ it "should not excute with no remote hosts set" do
261
+ Rake::RemoteTask.host "app.example.com", :app
262
+ task = Rake::RemoteTask.remote_task(:flunk, :roles => :db) { fail "should not have run" }
263
+ lambda { task.execute nil }.should raise_error(Rake::RemoteTask::ConfigurationError,
264
+ "No target hosts specified on task flunk for roles [:db]")
265
+ end
266
+
267
+ it "should not execute with no remote hosts for a given role" do
268
+ task = Rake::RemoteTask.remote_task(:flunk, :roles => :junk) { fail "should not have run" }
269
+ lambda { task.execute nil }.should raise_error(Rake::RemoteTask::ConfigurationError,
270
+ "No target hosts specified on task flunk for roles [:junk]")
271
+ end
272
+
273
+ it "should execute on remote hosts with a given role" do
274
+ set_example_hosts
275
+ set :some_variable, 1
276
+ x = 5
277
+ task = Rake::RemoteTask.remote_task(:some_task, :roles => :db) { x += some_variable }
278
+ task.execute nil
279
+ task.some_variable.should eql(1)
280
+ x.should eql(6)
281
+ end
282
+
283
+ it "should build rsync commands" do
284
+ task = create_example_task
285
+ task.rsync 'localfile', 'app.example.com:remotefile'
286
+ task.commands.size.should eql(1)
287
+ task.commands.first.should eql(%w[rsync -azP --delete localfile app.example.com:remotefile])
288
+ end
289
+
290
+ it "should error when an rsync command fails" do
291
+ task = create_example_task
292
+ task.action = lambda { false }
293
+ lambda { task.rsync 'local', 'app.example.com:remote' }.should raise_error(Rake::RemoteTask::CommandFailedError,
294
+ "execution failed: rsync -azP --delete local app.example.com:remote")
295
+ end
296
+
297
+ it "should build get commands" do
298
+ task = create_example_task
299
+ task.target_host = 'app.example.com'
300
+ lambda { task.get('tmp', 'remote1', 'remote2') }.should_not raise_error
301
+ task.commands.size.should eql(1)
302
+ task.commands.first.should eql(%w[rsync -azP --delete app.example.com:remote1 app.example.com:remote2 tmp])
303
+ end
304
+
305
+ it "should build put commands" do
306
+ task = create_example_task
307
+ task.target_host = 'app.example.com'
308
+ lambda { task.put('dest') { 'whatever' } }.should_not raise_error
309
+ task.commands.size.should eql(1)
310
+ task.commands.first[3] = 'some_temp_file_name'
311
+ task.commands.first.should eql( %w[rsync -azP --delete some_temp_file_name app.example.com:dest])
312
+ end
313
+
314
+ it "should run remote commands" do
315
+ task = create_example_task
316
+ task.output << "file1\nfile2\n"
317
+ task.target_host = "app.example.com"
318
+ result = nil
319
+
320
+ out, err = capture_output do
321
+ result = task.run("ls")
322
+ end
323
+
324
+ task.commands.size.should eql(1)
325
+ task.commands.first.should eql(["ssh", "app.example.com", "ls"])
326
+
327
+ result.should eql("file1\nfile2\n")
328
+ out.read.should eql("file1\nfile2\n")
329
+ err.read.should eql('')
330
+ end
331
+
332
+ it "should error when a remote command fails" do
333
+ set_example_hosts
334
+ task = create_example_task
335
+ task.input = StringIO.new "file1\nfile2\n"
336
+ task.target_host = 'app.example.com'
337
+ task.action = lambda { 1 }
338
+ lambda { task.run("ls") }.should raise_error(Rake::RemoteTask::CommandFailedError,
339
+ "execution failed with status 1: ssh app.example.com ls")
340
+ task.commands.size.should eql(1)
341
+ end
342
+
343
+ it "should build remote sudo commands" do
344
+ task = create_example_task
345
+ task.target_host = "app.example.com"
346
+ task.sudo "ls"
347
+ task.commands.size.should eql(1)
348
+ task.commands.first.should eql(["ssh", "app.example.com", "sudo -p Password: ls"])
349
+ end
350
+
351
+ it "should run remote sudo commands" do
352
+ task = create_example_task
353
+ task.output << "file1\nfile2\n"
354
+ task.error << 'Password:'
355
+ task.target_host = "app.example.com"
356
+ def task.sudo_password() "my password" end # gets defined by set
357
+ result = nil
358
+
359
+ out, err = capture_output do
360
+ result = task.run("sudo ls")
361
+ end
362
+
363
+ task.commands.size.should eql(1)
364
+ task.commands.first.should eql(['ssh', 'app.example.com', 'sudo ls'])
365
+ task.input.string.should eql("my password\n")
366
+
367
+ # WARN: Technically incorrect, the password line should be
368
+ # first... this is an artifact of changes to the IO code in run
369
+ # and the fact that we have a very simplistic (non-blocking)
370
+ # testing model.
371
+ result.should eql("file1\nfile2\nPassword:\n")
372
+ out.read.should eql("file1\nfile2\n")
373
+ err.read.should eql("Password:\n")
374
+ end
375
+
376
+ it "should get an options hash for a single option" do
377
+ set :my_option, 10
378
+ options = Rake::RemoteTask.get_options_hash(:my_option)
379
+ options.should ==({ :my_option => 10 })
380
+ end
381
+
382
+ it "should get an options hash for multiple options" do
383
+ set :my_option, 10
384
+ set :my_other_option, 'my other option value'
385
+ options = Rake::RemoteTask.get_options_hash(:my_option, :my_other_option)
386
+ options.should ==({ :my_option => 10, :my_other_option => 'my other option value' })
387
+ end
388
+
389
+ it "should an empty options hash for an undefined option" do
390
+ options = Rake::RemoteTask.get_options_hash(:my_undefined_option)
391
+ options.should ==({})
392
+ end
393
+
394
+ end