yad 0.0.4

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,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