ktheory-vlad 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ # $GEM_HOME/gems/vlad-1.2.0/lib/vlad/thin.rb
2
+ # Thin tasks for Vlad the Deployer
3
+ # By cnantais
4
+ require 'vlad'
5
+
6
+ namespace :vlad do
7
+ ##
8
+ # Thin app server
9
+
10
+ set :thin_address, "127.0.0.1"
11
+ set :thin_command, 'thin'
12
+ set(:thin_conf) { "#{shared_path}/thin_cluster.conf" }
13
+ set :thin_environment, "production"
14
+ set :thin_group, nil
15
+ set :thin_log_file, nil
16
+ set :thin_pid_file, nil
17
+ set :thin_port, nil
18
+ set :thin_socket, "/tmp/thin.sock"
19
+ set :thin_prefix, nil
20
+ set :thin_servers, 2
21
+ set :thin_user, nil
22
+ set :thin_rackup, nil
23
+
24
+ desc "Prepares application servers for deployment. thin
25
+ configuration is set via the thin_* variables.".cleanup
26
+
27
+ remote_task :setup_app, :roles => :app do
28
+ cmd = [
29
+ "#{thin_command} config",
30
+ "-s #{thin_servers}",
31
+ "-S #{thin_socket}",
32
+ "-e #{thin_environment}",
33
+ "-c #{current_path}",
34
+ "-C #{thin_conf}",
35
+ ("-a #{thin_address}" if thin_address),
36
+ ("-R #{thin_rackup}" if thin_rackup),
37
+ ("-P #{thin_pid_file}" if thin_pid_file),
38
+ ("-l #{thin_log_file}" if thin_log_file),
39
+ ("--user #{thin_user}" if thin_user),
40
+ ("--group #{thin_group}" if thin_group),
41
+ ("--prefix #{thin_prefix}" if thin_prefix),
42
+ ("-p #{thin_port}" if thin_port),
43
+ ].compact.join ' '
44
+
45
+ run cmd
46
+ end
47
+
48
+ def thin(cmd) # :nodoc:
49
+ "#{thin_command} #{cmd} -C #{thin_conf}"
50
+ end
51
+
52
+ desc "Restart the app servers"
53
+
54
+ remote_task :start_app, :roles => :app do
55
+ run thin("restart -s #{thin_servers}")
56
+ end
57
+
58
+ desc "Stop the app servers"
59
+
60
+ remote_task :stop_app, :roles => :app do
61
+ run thin("stop -s #{thin_servers}")
62
+ end
63
+ end
@@ -0,0 +1,73 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'stringio'
4
+ require 'vlad'
5
+
6
+ class StringIO
7
+ def readpartial(size) read end # suck!
8
+ end
9
+
10
+ module Process
11
+ def self.expected status
12
+ @@expected ||= []
13
+ @@expected << status
14
+ end
15
+
16
+ class << self
17
+ alias :waitpid2_old :waitpid2
18
+
19
+ def waitpid2(pid)
20
+ [ @@expected.shift ]
21
+ end
22
+ end
23
+ end
24
+
25
+ class Rake::RemoteTask
26
+ attr_accessor :commands, :action, :input, :output, :error
27
+
28
+ Status = Struct.new :exitstatus
29
+
30
+ class Status
31
+ def success?() exitstatus == 0 end
32
+ end
33
+
34
+ def system *command
35
+ @commands << command
36
+ self.action ? self.action[command.join(' ')] : true
37
+ end
38
+
39
+ def popen4 *command
40
+ @commands << command
41
+
42
+ @input = StringIO.new
43
+ out = StringIO.new @output.shift.to_s
44
+ err = StringIO.new @error.shift.to_s
45
+
46
+ raise if block_given?
47
+
48
+ status = self.action ? self.action[command.join(' ')] : 0
49
+ Process.expected Status.new(status)
50
+
51
+ return 42, @input, out, err
52
+ end
53
+
54
+ def select reads, writes, errs, timeout
55
+ [reads, writes, errs]
56
+ end
57
+
58
+ end
59
+
60
+ class VladTestCase < MiniTest::Unit::TestCase
61
+ def setup
62
+ @vlad = Rake::RemoteTask
63
+ @vlad.reset
64
+ Rake.application.clear
65
+ @task_count = Rake.application.tasks.size
66
+ @vlad.set :domain, "example.com"
67
+ end
68
+
69
+ def util_set_hosts
70
+ @vlad.host "app.example.com", :app
71
+ @vlad.host "db.example.com", :db
72
+ end
73
+ end
@@ -0,0 +1,279 @@
1
+ require 'vlad_test_case'
2
+ require 'vlad'
3
+
4
+ class TestRakeRemoteTask < VladTestCase
5
+ # TODO: move to minitest
6
+ def assert_silent
7
+ out, err = capture_io do
8
+ yield
9
+ end
10
+
11
+ assert_empty err
12
+ assert_empty out
13
+ end
14
+
15
+ def test_enhance
16
+ util_set_hosts
17
+ body = Proc.new { 5 }
18
+ task = @vlad.remote_task(:some_task => :foo, &body)
19
+ action = Rake::RemoteTask::Action.new(task, body)
20
+ assert_equal [action], task.remote_actions
21
+ assert_equal task, action.task
22
+ assert_equal ["foo"], task.prerequisites
23
+ end
24
+
25
+ def test_enhance_with_no_task_body
26
+ util_set_hosts
27
+ util_setup_task
28
+ assert_equal [], @task.remote_actions
29
+ assert_equal [], @task.prerequisites
30
+ end
31
+
32
+ def test_execute
33
+ util_set_hosts
34
+ set :some_variable, 1
35
+ set :can_set_nil, nil
36
+ set :lies_are, false
37
+ x = 5
38
+ task = @vlad.remote_task(:some_task) { x += some_variable }
39
+ task.execute nil
40
+ assert_equal 1, task.some_variable
41
+ assert_equal 7, x
42
+ assert task.can_set_nil.nil?
43
+ assert_equal false, task.lies_are
44
+ end
45
+
46
+ def test_set_false
47
+ set :can_set_nil, nil
48
+ set :lies_are, false
49
+
50
+ assert_equal nil, task.can_set_nil
51
+
52
+ assert_equal false, task.lies_are
53
+ assert_equal false, Rake::RemoteTask.fetch(:lies_are)
54
+ end
55
+
56
+
57
+ def test_fetch_false
58
+ assert_equal false, Rake::RemoteTask.fetch(:unknown, false)
59
+ end
60
+
61
+ def test_execute_exposes_target_host
62
+ host "app.example.com", :app
63
+ task = remote_task(:target_task) { set(:test_target_host, target_host) }
64
+ task.execute nil
65
+ assert_equal "app.example.com", Rake::RemoteTask.fetch(:test_target_host)
66
+ end
67
+
68
+ def test_execute_with_no_hosts
69
+ @vlad.host "app.example.com", :app
70
+ t = @vlad.remote_task(:flunk, :roles => :db) { flunk "should not have run" }
71
+ e = assert_raises(Vlad::ConfigurationError) { t.execute nil }
72
+ assert_equal "No target hosts specified on task flunk for roles [:db]",
73
+ e.message
74
+ end
75
+
76
+ def test_execute_with_no_roles
77
+ t = @vlad.remote_task(:flunk, :roles => :junk) { flunk "should not have run" }
78
+ e = assert_raises(Vlad::ConfigurationError) { t.execute nil }
79
+ assert_equal "No target hosts specified on task flunk for roles [:junk]",
80
+ e.message
81
+ end
82
+
83
+ def test_execute_with_roles
84
+ util_set_hosts
85
+ set :some_variable, 1
86
+ x = 5
87
+ task = @vlad.remote_task(:some_task, :roles => :db) { x += some_variable }
88
+ task.execute nil
89
+ assert_equal 1, task.some_variable
90
+ assert_equal 6, x
91
+ end
92
+
93
+ def test_rsync
94
+ util_setup_task
95
+ @task.target_host = "app.example.com"
96
+
97
+ assert_silent do
98
+ @task.rsync 'localfile', 'host:remotefile'
99
+ end
100
+
101
+ commands = @task.commands
102
+
103
+ assert_equal 1, commands.size, 'not enough commands'
104
+ assert_equal(%w[rsync -azP --delete localfile host:remotefile],
105
+ commands.first)
106
+ end
107
+
108
+ def test_rsync_fail
109
+ util_setup_task
110
+ @task.target_host = "app.example.com"
111
+ @task.action = lambda { false }
112
+
113
+ e = assert_raises Vlad::CommandFailedError do
114
+ assert_silent do
115
+ @task.rsync 'local', 'host:remote'
116
+ end
117
+ end
118
+ exp = "execution failed: rsync -azP --delete local host:remote"
119
+ assert_equal exp, e.message
120
+ end
121
+
122
+ def test_rsync_deprecation
123
+ util_setup_task
124
+ @task.target_host = "app.example.com"
125
+
126
+ out, err = capture_io do
127
+ @task.rsync 'localfile', 'remotefile'
128
+ end
129
+
130
+ commands = @task.commands
131
+
132
+ assert_equal 1, commands.size, 'not enough commands'
133
+ assert_equal(%w[rsync -azP --delete localfile app.example.com:remotefile],
134
+ commands.first)
135
+
136
+ assert_equal("rsync deprecation: pass target_host:remote_path explicitly\n",
137
+ err)
138
+ assert_empty out
139
+ # flunk "not yet"
140
+ end
141
+
142
+ def test_get
143
+ util_setup_task
144
+ @task.target_host = "app.example.com"
145
+
146
+ assert_silent do
147
+ @task.get 'tmp', "remote1", "remote2"
148
+ end
149
+
150
+ commands = @task.commands
151
+
152
+ expected = %w[rsync -azP --delete app.example.com:remote1 app.example.com:remote2 tmp]
153
+
154
+ assert_equal 1, commands.size
155
+ assert_equal expected, commands.first
156
+ end
157
+
158
+ def test_put
159
+ util_setup_task
160
+ @task.target_host = "app.example.com"
161
+
162
+ assert_silent do
163
+ @task.put 'dest' do
164
+ "whatever"
165
+ end
166
+ end
167
+
168
+ commands = @task.commands
169
+
170
+ expected = %w[rsync -azP --delete HAPPY app.example.com:dest]
171
+ commands.first[3] = 'HAPPY'
172
+
173
+ assert_equal 1, commands.size
174
+ assert_equal expected, commands.first
175
+ end
176
+
177
+ def test_run
178
+ util_setup_task
179
+ @task.output << "file1\nfile2\n"
180
+ @task.target_host = "app.example.com"
181
+ result = nil
182
+
183
+ out, err = capture_io do
184
+ result = @task.run("ls")
185
+ end
186
+
187
+ commands = @task.commands
188
+
189
+ assert_equal 1, commands.size, 'not enough commands'
190
+ assert_equal ["ssh", "app.example.com", "ls"],
191
+ commands.first, 'app'
192
+ assert_equal "file1\nfile2\n", result
193
+
194
+ assert_equal "file1\nfile2\n", out
195
+ assert_equal '', err
196
+ end
197
+
198
+ def test_run_failing_command
199
+ util_set_hosts
200
+ util_setup_task
201
+ @task.input = StringIO.new "file1\nfile2\n"
202
+ @task.target_host = 'app.example.com'
203
+ @task.action = lambda { 1 }
204
+
205
+ e = assert_raises(Vlad::CommandFailedError) { @task.run("ls") }
206
+ assert_equal "execution failed with status 1: ssh app.example.com ls", e.message
207
+
208
+ assert_equal 1, @task.commands.size
209
+ end
210
+
211
+ def test_run_with_prefix_output
212
+ util_setup_task
213
+ @task.output << "file1\nfile2\n"
214
+ @task.target_host = "app.example.com"
215
+ result = nil
216
+
217
+ set :prefix_output, true
218
+
219
+ out, err = capture_io do
220
+ result = @task.run("ls")
221
+ end
222
+
223
+ assert_equal "app.example.com: file1\napp.example.com: file2\n", result
224
+ assert_equal "app.example.com: file1\napp.example.com: file2\n", out
225
+ # Reset to false
226
+ set :prefix_output, false
227
+ end
228
+
229
+ def test_run_sudo
230
+ util_setup_task
231
+ @task.output << "file1\nfile2\n"
232
+ @task.error << 'Password:'
233
+ @task.target_host = "app.example.com"
234
+ def @task.sudo_password() "my password" end # gets defined by set
235
+ result = nil
236
+
237
+ out, err = capture_io do
238
+ result = @task.run("sudo ls")
239
+ end
240
+
241
+ commands = @task.commands
242
+
243
+ assert_equal 1, commands.size, 'not enough commands'
244
+ assert_equal ['ssh', 'app.example.com', 'sudo ls'],
245
+ commands.first
246
+
247
+ assert_equal "my password\n", @task.input.string
248
+
249
+ # WARN: Technically incorrect, the password line should be
250
+ # first... this is an artifact of changes to the IO code in run
251
+ # and the fact that we have a very simplistic (non-blocking)
252
+ # testing model.
253
+ assert_equal "file1\nfile2\nPassword:\n", result
254
+
255
+ assert_equal "file1\nfile2\n", out
256
+ assert_equal "Password:\n", err
257
+ end
258
+
259
+ def test_sudo
260
+ util_setup_task
261
+ @task.target_host = "app.example.com"
262
+ @task.sudo "ls"
263
+
264
+ commands = @task.commands
265
+
266
+ assert_equal 1, commands.size, 'wrong number of commands'
267
+ assert_equal ["ssh", "app.example.com", "sudo -p Password: ls"],
268
+ commands.first, 'app'
269
+ end
270
+
271
+ def util_setup_task(options = {})
272
+ @task = @vlad.remote_task :test_task, options
273
+ @task.commands = []
274
+ @task.output = []
275
+ @task.error = []
276
+ @task.action = nil
277
+ @task
278
+ end
279
+ end
@@ -0,0 +1,210 @@
1
+ require 'vlad_test_case'
2
+ require 'vlad'
3
+
4
+ $TESTING = true
5
+
6
+ class TestVlad < VladTestCase
7
+ def test_all_hosts
8
+ util_set_hosts
9
+ assert_equal %w[app.example.com db.example.com], @vlad.all_hosts
10
+ end
11
+
12
+ def test_fetch
13
+ set :foo, 5
14
+ assert_equal 5, @vlad.fetch(:foo)
15
+ end
16
+
17
+ def test_fetch_with_default
18
+ assert_equal 5, @vlad.fetch(:not_here, 5)
19
+ end
20
+
21
+ def test_host
22
+ @vlad.host "test.example.com", :app, :db
23
+ expected = {"test.example.com" => {}}
24
+ assert_equal expected, @vlad.roles[:app]
25
+ assert_equal expected, @vlad.roles[:db]
26
+ end
27
+
28
+ def test_host_invalid
29
+ assert_raises(ArgumentError) { @vlad.host nil, :web }
30
+ end
31
+
32
+ def test_host_multiple_hosts
33
+ @vlad.host "test.example.com", :app, :db
34
+ @vlad.host "yarr.example.com", :app, :db, :no_release => true
35
+
36
+ expected = {
37
+ "test.example.com" => {},
38
+ "yarr.example.com" => {:no_release => true}
39
+ }
40
+
41
+ assert_equal expected, @vlad.roles[:app]
42
+ assert_equal expected, @vlad.roles[:db]
43
+ refute_equal(@vlad.roles[:db]["test.example.com"].object_id,
44
+ @vlad.roles[:app]["test.example.com"].object_id)
45
+ end
46
+
47
+ def test_hosts_for_array_of_roles
48
+ util_set_hosts
49
+ assert_equal %w[app.example.com db.example.com], @vlad.hosts_for([:app, :db])
50
+ end
51
+
52
+ def test_hosts_for_one_role
53
+ util_set_hosts
54
+ @vlad.host "app2.example.com", :app
55
+ assert_equal %w[app.example.com app2.example.com], @vlad.hosts_for(:app)
56
+ end
57
+
58
+ def test_hosts_for_multiple_roles
59
+ util_set_hosts
60
+ assert_equal %w[app.example.com db.example.com], @vlad.hosts_for(:app, :db)
61
+ end
62
+
63
+ def test_hosts_for_unique
64
+ util_set_hosts
65
+ @vlad.host "app.example.com", :web
66
+ assert_equal %w[app.example.com db.example.com], @vlad.hosts_for(:app, :db, :web)
67
+ end
68
+
69
+ def test_initialize
70
+ @vlad.set_defaults # ensure these three are virginal
71
+ assert_raises(Vlad::ConfigurationError) { @vlad.repository }
72
+ assert_raises(Vlad::ConfigurationError) { @vlad.deploy_to }
73
+ assert_raises(Vlad::ConfigurationError) { @vlad.domain }
74
+ end
75
+
76
+ def test_role
77
+ @vlad.role :app, "test.example.com"
78
+ expected = {"test.example.com" => {}}
79
+ assert_equal expected, @vlad.roles[:app]
80
+ end
81
+
82
+ def test_role_multiple_hosts
83
+ @vlad.role :app, "test.example.com"
84
+ @vlad.role :app, "yarr.example.com", :no_release => true
85
+ expected = {
86
+ "test.example.com" => {},
87
+ "yarr.example.com" => {:no_release => true}
88
+ }
89
+ assert_equal expected, @vlad.roles[:app]
90
+ end
91
+
92
+ def test_role_multiple_roles
93
+ @vlad.role :app, "test.example.com", :primary => true
94
+ @vlad.role :db, "yarr.example.com", :no_release => true
95
+ expected_db = { "yarr.example.com" => {:no_release => true} }
96
+ assert_equal expected_db, @vlad.roles[:db]
97
+ expected_app = { "test.example.com" => {:primary => true} }
98
+ assert_equal expected_app, @vlad.roles[:app]
99
+ end
100
+
101
+ def test_remote_task
102
+ t = @vlad.remote_task(:test_task) { 5 }
103
+ assert_equal @task_count + 1, Rake.application.tasks.size
104
+ assert_equal({ :roles => [] }, t.options)
105
+ end
106
+
107
+ def test_remote_task_all_hosts_by_default
108
+ util_set_hosts
109
+ t = @vlad.remote_task(:test_task) { 5 }
110
+ assert_equal %w[app.example.com db.example.com], t.target_hosts
111
+ end
112
+
113
+ def test_remote_task_environment_override
114
+ old_env_hosts = ENV["HOSTS"]
115
+ ENV["HOSTS"] = 'other1.example.com, other2.example.com'
116
+ util_set_hosts
117
+ t = @vlad.remote_task(:test_task) { 5 }
118
+ assert_equal %w[other1.example.com other2.example.com], t.target_hosts
119
+ ensure
120
+ ENV["HOSTS"] = old_env_hosts
121
+ end
122
+
123
+ def test_remote_task_body_set
124
+ set(:some_variable, 5)
125
+ @vlad.host 'www.example.com', :app
126
+ @vlad.remote_task(:some_task) do $some_task_result = some_variable end
127
+
128
+ Rake::Task['some_task'].execute nil
129
+ assert_equal @vlad.fetch(:some_variable), $some_task_result
130
+ end
131
+
132
+ def test_remote_task_with_options
133
+ t = @vlad.remote_task :test_task, :roles => [:app, :db] do
134
+ fail "should not run"
135
+ end
136
+ assert_equal({:roles => [:app, :db]}, t.options)
137
+ end
138
+
139
+ def test_remote_task_before_host_declaration
140
+ t = @vlad.remote_task :test_task, :roles => :web do 5 end
141
+ @vlad.host 'www.example.com', :web
142
+ assert_equal %w[www.example.com], t.target_hosts
143
+ end
144
+
145
+ def test_remote_task_role_override
146
+ host "db1", :db
147
+ host "db2", :db
148
+ host "db3", :db
149
+ host "master", :master_db
150
+
151
+ remote_task(:migrate_the_db, :roles => [:db]) { flunk "bad!" }
152
+ task = Rake::Task["migrate_the_db"]
153
+ assert_equal %w[db1 db2 db3], task.target_hosts
154
+
155
+ task.options[:roles] = :master_db
156
+ assert_equal %w[master], task.target_hosts
157
+
158
+ task.options[:roles] = [:master_db]
159
+ assert_equal %w[master], task.target_hosts
160
+ end
161
+
162
+ def test_set
163
+ set :win, 5
164
+ assert_equal 5, @vlad.win
165
+ end
166
+
167
+ def test_set_lazy_block_evaluation
168
+ set(:lose) { raise "lose" }
169
+ assert_raises(RuntimeError) { @vlad.lose }
170
+ end
171
+
172
+ def test_set_with_block
173
+ x = 1
174
+ set(:win) { x += 2 }
175
+
176
+ assert_equal 3, @vlad.win
177
+ assert_equal 3, @vlad.win
178
+ end
179
+
180
+ def test_set_with_reference
181
+ @vlad.instance_eval do
182
+ set(:var_one) { var_two }
183
+ set(:var_two) { var_three }
184
+ set(:var_three) { 5 }
185
+ end
186
+
187
+ assert_equal 5, @vlad.var_one
188
+ end
189
+
190
+ def test_set_with_block_and_value
191
+ e = assert_raises(ArgumentError) do
192
+ set(:loser, 5) { 6 }
193
+ end
194
+ assert_equal "cannot provide both a value and a block", e.message
195
+ end
196
+
197
+ def test_set_with_nil
198
+ set(:win, nil)
199
+ assert_equal nil, @vlad.win
200
+ end
201
+
202
+ def test_set_with_reserved_name
203
+ $TESTING = false
204
+ e = assert_raises(ArgumentError) { set(:all_hosts, []) }
205
+ assert_equal "cannot set reserved name: 'all_hosts'", e.message
206
+ ensure
207
+ $TESTING = true
208
+ end
209
+ end
210
+