FooBarWidget-daemon_controller 0.2.0 → 0.2.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.
- data/README.markdown +44 -4
- data/daemon_controller.gemspec +2 -2
- data/lib/daemon_controller.rb +10 -2
- data/lib/daemon_controller/lock_file.rb +30 -1
- data/spec/daemon_controller_spec.rb +14 -3
- metadata +2 -2
data/README.markdown
CHANGED
@@ -24,7 +24,7 @@ some of them.
|
|
24
24
|
### Starting daemons is a hassle
|
25
25
|
|
26
26
|
If you've used similar software, then you might agree that managing these
|
27
|
-
daemons
|
27
|
+
daemons is a hassle. If you're using BackgrounDRb, then the daemon must be
|
28
28
|
running. Starting the daemon is not hard, but it is annoying. It's also
|
29
29
|
possible that the system administrator forgets to start the daemon. While
|
30
30
|
configuring the system to automatically start a daemon at startup is not hard,
|
@@ -209,6 +209,46 @@ daemon_controller's goal is to make daemon management less of a hassle, and as
|
|
209
209
|
automatic and straightforward as possible.
|
210
210
|
|
211
211
|
|
212
|
+
What about Monit/God?
|
213
|
+
=====================
|
214
|
+
|
215
|
+
daemon_controller is not a replacement for [Monit](http://www.tildeslash.com/monit/)
|
216
|
+
or [God](http://god.rubyforge.org/). Rather, it is a solution to the following
|
217
|
+
problem:
|
218
|
+
|
219
|
+
> **Hongli:** hey Ninh, do a 'git pull', I just implemented awesome searching
|
220
|
+
> features in our application!
|
221
|
+
> **Ninh:** cool. *pulls from repository*
|
222
|
+
> **Ninh:** hey Hongli, it doesn't work.
|
223
|
+
> **Hongli:** what do you mean, it doesn't work?
|
224
|
+
> **Ninh:** it says "connection refused", or something
|
225
|
+
> **Hongli:** oh I forgot to mention it, you have to run the Sphinx search
|
226
|
+
> daemon before it works. type "rake sphinx:daemon:start" to do
|
227
|
+
> that
|
228
|
+
> **Ninh:** great. but now I get a different error. something about
|
229
|
+
> BackgrounDRb.
|
230
|
+
> **Hongli:** oops, I forgot to mention this too. you need to start the
|
231
|
+
> BackgrounDRb server with "rake backgroundrb:start_server"
|
232
|
+
> **Ninh:** okay, so every time I want to use this app, I have to type
|
233
|
+
> "rake sphinx:daemon:start", "rake backgroundrb:start_server" and
|
234
|
+
> "./script/server"?
|
235
|
+
> **Hongli:** yep
|
236
|
+
|
237
|
+
Imagine the above conversation becoming just:
|
238
|
+
|
239
|
+
> **Hongli:** hey Ninh, do a 'git pull', I just implemented awesome searching
|
240
|
+
> features in our application!
|
241
|
+
> **Ninh:** cool. *pulls from repository*
|
242
|
+
> **Ninh:** awesome, it works!
|
243
|
+
|
244
|
+
This is not something that can be achieved with Monit/God. Monit/God are for
|
245
|
+
monitoring daemons, auto-restarting them when they use too much resources.
|
246
|
+
daemon_controller's goal is to allow developers to implement daemon
|
247
|
+
starting/stopping and daemon auto-starting code that's robust. daemon_controller
|
248
|
+
is intended to be used to make daemon-dependent applications Just Work(tm)
|
249
|
+
without having to start the daemons manually.
|
250
|
+
|
251
|
+
|
212
252
|
Tutorial #1: controlling Apache
|
213
253
|
===============================
|
214
254
|
|
@@ -271,7 +311,7 @@ raises `Errno::ECONNREFUSED`, then that's also interpreted by DaemonController
|
|
271
311
|
as meaning that the daemon isn't responding yet.
|
272
312
|
|
273
313
|
After `controller.start` has returned, we can continue with the test case. At
|
274
|
-
this point, we know that Apache
|
314
|
+
this point, we know that Apache is done with initializing.
|
275
315
|
When we're done with Apache, we stop it with `controller.stop`. This does not
|
276
316
|
return until Apache has fully stopped.
|
277
317
|
|
@@ -295,7 +335,7 @@ that there's little room for human screw-up, and so we've developed this
|
|
295
335
|
library. Our Sphinx search daemon is completely managed through this library
|
296
336
|
and is automatically started on demand.
|
297
337
|
|
298
|
-
Our Sphinx config file is generated from an ERB template. This ERB
|
338
|
+
Our Sphinx config file is generated from an ERB template. This ERB template
|
299
339
|
writes different values in the config file, depending on whether we're in
|
300
340
|
development, test or production mode. We will want to regenerate this config
|
301
341
|
file every time, just before we start the search daemon.
|
@@ -325,7 +365,7 @@ This can be achieved with the following code:
|
|
325
365
|
:before_start => method(:before_start),
|
326
366
|
:ping_command => lambda { TCPSocket.new('localhost', SEARCH_SERVER_PORT) },
|
327
367
|
:pid_file => 'tmp/pids/sphinx.pid',
|
328
|
-
:log_file => 'log/sphinx.log'
|
368
|
+
:log_file => 'log/sphinx.log')
|
329
369
|
end
|
330
370
|
|
331
371
|
def query(search_terms)
|
data/daemon_controller.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "daemon_controller"
|
3
|
-
s.version = "0.2.
|
4
|
-
s.date = "
|
3
|
+
s.version = "0.2.1"
|
4
|
+
s.date = "2009-02-22"
|
5
5
|
s.summary = "A library for implementing daemon management capabilities"
|
6
6
|
s.email = "hongli@phusion.nl"
|
7
7
|
s.homepage = "http://github.com/FooBarWidget/daemon_controller/tree/master"
|
data/lib/daemon_controller.rb
CHANGED
@@ -21,12 +21,13 @@
|
|
21
21
|
|
22
22
|
require 'tempfile'
|
23
23
|
require 'fcntl'
|
24
|
+
require 'timeout'
|
24
25
|
require File.expand_path(File.dirname(__FILE__) << '/daemon_controller/lock_file')
|
25
26
|
|
26
27
|
# Main daemon controller object. See the README for an introduction and tutorial.
|
27
28
|
class DaemonController
|
28
29
|
ALLOWED_CONNECT_EXCEPTIONS = [Errno::ECONNREFUSED, Errno::ENETUNREACH,
|
29
|
-
Errno::ETIMEDOUT, Errno::ECONNRESET]
|
30
|
+
Errno::ETIMEDOUT, Errno::ECONNRESET, Errno::EINVAL]
|
30
31
|
|
31
32
|
class Error < StandardError
|
32
33
|
end
|
@@ -497,6 +498,11 @@ private
|
|
497
498
|
STDIN.reopen("/dev/null", "r")
|
498
499
|
STDOUT.reopen(tempfile_path, "w")
|
499
500
|
STDERR.reopen(tempfile_path, "w")
|
501
|
+
ObjectSpace.each_object(IO) do |obj|
|
502
|
+
if STDIN != obj && STDOUT != obj && STDERR != obj
|
503
|
+
obj.close rescue nil
|
504
|
+
end
|
505
|
+
end
|
500
506
|
exec(command)
|
501
507
|
end
|
502
508
|
begin
|
@@ -519,7 +525,9 @@ private
|
|
519
525
|
raise StartError, File.read(tempfile_path).strip
|
520
526
|
end
|
521
527
|
else
|
522
|
-
|
528
|
+
cmd = "#{command} >\"#{tempfile_path}\""
|
529
|
+
cmd += " 2>\"#{tempfile_path}\"" unless PLATFORM =~ /mswin/
|
530
|
+
if !system(cmd)
|
523
531
|
raise StartError, File.read(tempfile_path).strip
|
524
532
|
end
|
525
533
|
end
|
@@ -21,6 +21,9 @@
|
|
21
21
|
|
22
22
|
class DaemonController
|
23
23
|
class LockFile
|
24
|
+
class AlreadyLocked < StandardError
|
25
|
+
end
|
26
|
+
|
24
27
|
def initialize(filename)
|
25
28
|
@filename = filename
|
26
29
|
end
|
@@ -44,5 +47,31 @@ class LockFile
|
|
44
47
|
yield
|
45
48
|
end
|
46
49
|
end
|
47
|
-
|
50
|
+
|
51
|
+
def try_shared_lock
|
52
|
+
File.open(@filename, 'w') do |f|
|
53
|
+
if Fcntl.const_defined? :F_SETFD
|
54
|
+
f.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
55
|
+
end
|
56
|
+
if f.flock(File::LOCK_SH | File::LOCK_NB)
|
57
|
+
yield
|
58
|
+
else
|
59
|
+
raise AlreadyLocked
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def try_exclusive_lock
|
65
|
+
File.open(@filename, 'w') do |f|
|
66
|
+
if Fcntl.const_defined? :F_SETFD
|
67
|
+
f.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
68
|
+
end
|
69
|
+
if f.flock(File::LOCK_EX | File::LOCK_NB)
|
70
|
+
yield
|
71
|
+
else
|
72
|
+
raise AlreadyLocked
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # class LockFile
|
48
77
|
end # class DaemonController
|
@@ -165,8 +165,12 @@ describe DaemonController, "#start" do
|
|
165
165
|
@controller.should_receive(:start_timed_out).and_return do
|
166
166
|
end_time = Time.now
|
167
167
|
end
|
168
|
-
|
169
|
-
|
168
|
+
begin
|
169
|
+
lambda { @controller.start }.should raise_error(DaemonController::StartTimeout)
|
170
|
+
(min_start_timeout .. max_start_timeout).should === end_time - start_time
|
171
|
+
ensure
|
172
|
+
@controller.stop
|
173
|
+
end
|
170
174
|
end
|
171
175
|
|
172
176
|
it "kills the daemon with a signal if the daemon doesn't start in time and there's a PID file" do
|
@@ -178,7 +182,14 @@ describe DaemonController, "#start" do
|
|
178
182
|
end
|
179
183
|
pid = @controller.send(:read_pid_file)
|
180
184
|
end
|
181
|
-
|
185
|
+
begin
|
186
|
+
lambda { @controller.start }.should raise_error(DaemonController::StartTimeout)
|
187
|
+
ensure
|
188
|
+
# It's possible that because of a racing condition, the PID
|
189
|
+
# file doesn't get deleted before the next test is run. So
|
190
|
+
# here we ensure that the PID file is gone.
|
191
|
+
File.unlink("echo_server.pid") rescue nil
|
192
|
+
end
|
182
193
|
end
|
183
194
|
|
184
195
|
if DaemonController.send(:fork_supported?)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: FooBarWidget-daemon_controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hongli Lai
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-02-22 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|