servolux 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/examples/echo.rb +1 -1
- data/lib/servolux.rb +1 -3
- data/lib/servolux/daemon.rb +32 -46
- data/lib/servolux/null_logger.rb +19 -0
- data/lib/servolux/pid_file.rb +172 -0
- data/lib/servolux/server.rb +24 -29
- data/lib/servolux/version.rb +1 -1
- data/spec/daemon_spec.rb +0 -1
- data/spec/pid_file_spec.rb +133 -0
- data/spec/server_spec.rb +22 -16
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 581567ae92a495cdeb92737c979d52e6bd8cf996
|
4
|
+
data.tar.gz: 0f887e81fc0a8b78e1f340578f2183b460a27ca7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b67919d827ef8ca1e956918d631bbb2a1ffebcedf3334c4197664c674f340910c0156b6973e476f62c1030069a49332752c7847fab262ee219f633260d971e3f
|
7
|
+
data.tar.gz: 4cb69e76d39fece3215048a9032bfa3904888061dc1676b818500bca238503f0659e67ee583d98efd9286caf2abc30ccf0e27b283c8deccc62b5e1ec5fe3c558
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ by Tim Pease [![](https://secure.travis-ci.org/TwP/servolux.png)](http://travis-
|
|
8
8
|
|
9
9
|
Serv-O-Lux is a collection of Ruby classes that are useful for daemon and
|
10
10
|
process management, and for writing your own Ruby services. The code is well
|
11
|
-
documented and tested. It works with Ruby and JRuby supporting
|
11
|
+
documented and tested. It works with Ruby and JRuby supporting 1.9 and 2.0
|
12
12
|
interpreters.
|
13
13
|
|
14
14
|
### Features
|
@@ -42,6 +42,9 @@ process to die if it does not exit in a given amount of time.
|
|
42
42
|
-- provides a pre-forking worker pool for executing tasks in parallel using
|
43
43
|
multiple processes.
|
44
44
|
|
45
|
+
[Servolux::PidFile](http://www.rubydoc.info/github/TwP/servolux/Servolux/PidFile)
|
46
|
+
-- provides PID file management and process signaling and liveness checks.
|
47
|
+
|
45
48
|
All the documentation is available online at http://rdoc.info/projects/TwP/servolux
|
46
49
|
|
47
50
|
### Install
|
data/examples/echo.rb
CHANGED
@@ -35,7 +35,7 @@ trap('EXIT') { acceptor.close }
|
|
35
35
|
# Create the worker pool passing in the code to execute in each child
|
36
36
|
# process.
|
37
37
|
pool = Servolux::Prefork.new {
|
38
|
-
socket,
|
38
|
+
socket,_ = acceptor.accept
|
39
39
|
socket.write "child #$$ echo> "
|
40
40
|
socket.flush
|
41
41
|
message = socket.gets
|
data/lib/servolux.rb
CHANGED
@@ -35,10 +35,8 @@ module Servolux
|
|
35
35
|
def self.fork?
|
36
36
|
RUBY_PLATFORM != 'java' and test(?e, '/dev/null')
|
37
37
|
end
|
38
|
-
|
39
38
|
end
|
40
39
|
|
41
|
-
%w[version threaded server piper daemon child prefork].each do |lib|
|
40
|
+
%w[version null_logger threaded pid_file server piper daemon child prefork].each do |lib|
|
42
41
|
require Servolux.libpath('servolux', lib)
|
43
42
|
end
|
44
|
-
|
data/lib/servolux/daemon.rb
CHANGED
@@ -77,7 +77,7 @@ class Servolux::Daemon
|
|
77
77
|
|
78
78
|
attr_accessor :name
|
79
79
|
attr_accessor :logger
|
80
|
-
|
80
|
+
attr_reader :pid_file
|
81
81
|
attr_reader :startup_command
|
82
82
|
attr_accessor :shutdown_command
|
83
83
|
attr_accessor :timeout
|
@@ -153,10 +153,10 @@ class Servolux::Daemon
|
|
153
153
|
def initialize( opts = {} )
|
154
154
|
@piper = nil
|
155
155
|
@logfile_reader = nil
|
156
|
+
@pid_file = nil
|
156
157
|
|
157
158
|
self.name = opts.fetch(:name, nil)
|
158
|
-
self.logger = opts.fetch(:logger,
|
159
|
-
self.pid_file = opts.fetch(:pid_file, nil)
|
159
|
+
self.logger = opts.fetch(:logger, Servolux::NullLogger())
|
160
160
|
self.startup_command = opts.fetch(:server, nil) || opts.fetch(:startup_command, nil)
|
161
161
|
self.shutdown_command = opts.fetch(:shutdown_command, nil)
|
162
162
|
self.timeout = opts.fetch(:timeout, 30)
|
@@ -167,6 +167,8 @@ class Servolux::Daemon
|
|
167
167
|
self.after_fork = opts.fetch(:after_fork, nil)
|
168
168
|
self.before_exec = opts.fetch(:before_exec, nil)
|
169
169
|
|
170
|
+
self.pid_file = opts.fetch(:pid_file, name) if pid_file.nil?
|
171
|
+
|
170
172
|
yield self if block_given?
|
171
173
|
|
172
174
|
ary = %w[name logger pid_file startup_command].map { |var|
|
@@ -198,14 +200,35 @@ class Servolux::Daemon
|
|
198
200
|
@startup_command = val
|
199
201
|
return unless val.is_a?(::Servolux::Server)
|
200
202
|
|
201
|
-
|
202
|
-
|
203
|
-
|
203
|
+
self.name = val.name
|
204
|
+
self.logger = val.logger
|
205
|
+
self.pid_file = val.pid_file
|
204
206
|
@shutdown_command = nil
|
205
207
|
end
|
206
208
|
alias :server= :startup_command=
|
207
209
|
alias :server :startup_command
|
208
210
|
|
211
|
+
# Set the PID file to the given `value`. If a PidFile instance is given, then
|
212
|
+
# it is used. If a name is given, then that name is used to create a PifFile
|
213
|
+
# instance.
|
214
|
+
#
|
215
|
+
# value - The PID file name or a PidFile instance.
|
216
|
+
#
|
217
|
+
# Raises an ArgumentError if the `value` cannot be used as a PID file.
|
218
|
+
def pid_file=( value )
|
219
|
+
@pid_file =
|
220
|
+
case value
|
221
|
+
when Servolux::PidFile
|
222
|
+
value
|
223
|
+
when String
|
224
|
+
path = File.dirname(value)
|
225
|
+
fn = File.basename(value, ".pid")
|
226
|
+
Servolux::PidFile.new(:name => fn, :path => path, :logger => logger)
|
227
|
+
else
|
228
|
+
raise ArgumentError, "#{value.inspect} cannot be used as a PID file"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
209
232
|
# Assign the log file name. This log file will be monitored to determine
|
210
233
|
# if the daemon process is running.
|
211
234
|
#
|
@@ -289,15 +312,7 @@ class Servolux::Daemon
|
|
289
312
|
# @return [Boolean]
|
290
313
|
#
|
291
314
|
def alive?
|
292
|
-
|
293
|
-
Process.kill(0, pid)
|
294
|
-
true
|
295
|
-
rescue Errno::ESRCH, Errno::ENOENT
|
296
|
-
false
|
297
|
-
rescue Errno::EACCES => err
|
298
|
-
logger.error "You do not have access to the PID file at " \
|
299
|
-
"#{pid_file.inspect}: #{err.message}"
|
300
|
-
false
|
315
|
+
pid_file.alive?
|
301
316
|
end
|
302
317
|
|
303
318
|
# Send a signal to the daemon process identified by the PID file. The
|
@@ -309,32 +324,9 @@ class Servolux::Daemon
|
|
309
324
|
# @return [Daemon] self
|
310
325
|
#
|
311
326
|
def kill( signal = 'INT' )
|
312
|
-
|
313
|
-
pid = retrieve_pid
|
314
|
-
logger.info "Killing PID #{pid} with #{signal}"
|
315
|
-
Process.kill(signal, pid)
|
316
|
-
self
|
317
|
-
rescue Errno::EINVAL
|
318
|
-
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
319
|
-
"'#{signal}' is an invalid or unsupported signal number."
|
320
|
-
rescue Errno::EPERM
|
321
|
-
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
322
|
-
"Insufficient permissions."
|
323
|
-
rescue Errno::ESRCH
|
324
|
-
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
325
|
-
"Process is deceased or zombie."
|
326
|
-
rescue Errno::EACCES => err
|
327
|
-
logger.error err.message
|
328
|
-
rescue Errno::ENOENT => err
|
329
|
-
logger.error "Could not find a PID file at #{pid_file.inspect}. " \
|
330
|
-
"Most likely the process is no longer running."
|
331
|
-
rescue Exception => err
|
332
|
-
unless err.is_a?(SystemExit)
|
333
|
-
logger.error "Failed to kill PID #{pid} with #{signal}: #{err.message}"
|
334
|
-
end
|
327
|
+
pid_file.kill signal
|
335
328
|
end
|
336
329
|
|
337
|
-
|
338
330
|
private
|
339
331
|
|
340
332
|
def run_startup_command
|
@@ -373,11 +365,7 @@ private
|
|
373
365
|
end
|
374
366
|
|
375
367
|
def retrieve_pid
|
376
|
-
@piper ? @piper.pid :
|
377
|
-
rescue TypeError
|
378
|
-
raise Error, "A PID file was not specified."
|
379
|
-
rescue ArgumentError
|
380
|
-
raise Error, "#{pid_file.inspect} does not contain a valid PID."
|
368
|
+
@piper ? @piper.pid : pid_file.pid
|
381
369
|
end
|
382
370
|
|
383
371
|
def started?
|
@@ -475,6 +463,4 @@ private
|
|
475
463
|
end
|
476
464
|
end
|
477
465
|
# :startdoc:
|
478
|
-
|
479
466
|
end
|
480
|
-
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "singleton"
|
2
|
+
require "logger"
|
3
|
+
|
4
|
+
module Servolux
|
5
|
+
# A "do nothing" implementation of the standard Ruby Logger class.
|
6
|
+
class NullLogger < Logger
|
7
|
+
include Singleton
|
8
|
+
def initialize(*args); end
|
9
|
+
def add(*args, &block); end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Syntactic sugar for getting the null logger instance.
|
13
|
+
#
|
14
|
+
# Servolux::NullLogger() #=> NullLogger singleton instance
|
15
|
+
#
|
16
|
+
def self.NullLogger
|
17
|
+
NullLogger.instance
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# == Synopsis
|
2
|
+
# The PidFile manages the lifecycle of a PID file.
|
3
|
+
#
|
4
|
+
# == Details
|
5
|
+
# A PID file contains the process ID of a given program. This file can be used
|
6
|
+
# by the program to indicate that it started successfully. The file can be used
|
7
|
+
# programmatically to look up the process ID and send signals to the program.
|
8
|
+
# The file can be used to ensure two instances of the same program are not
|
9
|
+
# started at the same time.
|
10
|
+
#
|
11
|
+
# The PidFile class supports creating and deleting PID files. Methods are
|
12
|
+
# provided to check if the program associated with the PID is `alive?`. Signals
|
13
|
+
# can be sent to the program using the `kill` method.
|
14
|
+
#
|
15
|
+
# == Examples
|
16
|
+
#
|
17
|
+
# Here is a simple example creating a PID file in the "/var/run" directory.
|
18
|
+
#
|
19
|
+
# pid_file = Servolux::PidFile.new(:name => "test", :path => "/var/run")
|
20
|
+
# pid_file.filename #=> "/var/run/test.pid"
|
21
|
+
# pid_file.write
|
22
|
+
#
|
23
|
+
# From another process we can access this PID file and send a `HUP` signal to
|
24
|
+
# the program.
|
25
|
+
#
|
26
|
+
# pid_file = Servolux::PidFile.new(:name => "test", :path => "/var/run")
|
27
|
+
# pid_file.kill("HUP") if pid_file.alive?
|
28
|
+
#
|
29
|
+
class Servolux::PidFile
|
30
|
+
|
31
|
+
DEFAULT_MODE = 0640
|
32
|
+
|
33
|
+
attr_accessor :name # the process name
|
34
|
+
attr_accessor :path # the path to the PID file
|
35
|
+
attr_accessor :mode # PID file permissions mode
|
36
|
+
attr_accessor :logger # logger for outputting messages
|
37
|
+
|
38
|
+
# Create a new PID file instance.
|
39
|
+
#
|
40
|
+
# opts - The options Hash
|
41
|
+
# :name - the name of the program
|
42
|
+
# :path - path to the PID file location
|
43
|
+
# :mode - file permissions mode
|
44
|
+
# :logger - logger for outputting messages
|
45
|
+
#
|
46
|
+
def initialize( opts = {} )
|
47
|
+
@name = opts.fetch(:name, $0)
|
48
|
+
@path = opts.fetch(:path, ".")
|
49
|
+
@mode = opts.fetch(:mode, DEFAULT_MODE)
|
50
|
+
@logger = opts.fetch(:logger, Servolux::NullLogger())
|
51
|
+
|
52
|
+
yield self if block_given?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the full name of the PID file including path and extension.
|
56
|
+
def filename
|
57
|
+
fn = name.to_s.downcase.tr(" ","_") + ".pid"
|
58
|
+
fn = File.join(path, fn) unless path.nil?
|
59
|
+
fn
|
60
|
+
end
|
61
|
+
|
62
|
+
# Writes the given `pid` to the PID file. The `pid` defaults to the current
|
63
|
+
# process ID.
|
64
|
+
#
|
65
|
+
# pid - The process ID to write to the file
|
66
|
+
#
|
67
|
+
# Returns the filename of PID file.
|
68
|
+
# Raises Errno::EACCESS if you do not have permission to write the file.
|
69
|
+
def write( pid = Process.pid )
|
70
|
+
fn = filename
|
71
|
+
logger.debug "Writing pid file #{fn.inspect}"
|
72
|
+
File.open(fn, 'w', mode) { |fd| fd.write(pid.to_s) }
|
73
|
+
fn
|
74
|
+
end
|
75
|
+
|
76
|
+
# Delete the PID file if it exists. This method first checks that the current
|
77
|
+
# process PID is the same as the PID stored in the file. If the PIDs do not
|
78
|
+
# match, then this method returns `nil` without taking any action.
|
79
|
+
#
|
80
|
+
# Returns the filename of the deleted file or `nil` if no action was taken.
|
81
|
+
# Raises Errno::EACCESS if you do not have permission to delete the file.
|
82
|
+
def delete
|
83
|
+
return unless pid == Process.pid
|
84
|
+
fn = filename
|
85
|
+
logger.debug "Deleting pid file #{fn.inspect}"
|
86
|
+
File.delete fn
|
87
|
+
fn
|
88
|
+
end
|
89
|
+
|
90
|
+
# Forcibly delete the PID file if it exists. This method does NOT check that
|
91
|
+
# the current process PID against the PID stored in the file.
|
92
|
+
#
|
93
|
+
# Returns the filename of the deleted file or `nil` if no action was taken.
|
94
|
+
# Raises Errno::EACCESS if you do not have permission to delete the file.
|
95
|
+
def delete!
|
96
|
+
return unless exist?
|
97
|
+
fn = filename
|
98
|
+
logger.debug "Deleting pid file #{fn.inspect}"
|
99
|
+
File.delete fn
|
100
|
+
fn
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns `true` if the PID file exists. Returns `false` otherwise.
|
104
|
+
def exist?
|
105
|
+
File.exist? filename
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the numeric PID read from the file or `nil` if the file does not
|
109
|
+
# exist. If you do not have permission to access the file `nil` is returned.
|
110
|
+
def pid
|
111
|
+
fn = filename
|
112
|
+
Integer(File.read(fn).strip) if File.exist?(fn)
|
113
|
+
rescue Errno::EACCES => err
|
114
|
+
logger.error "You do not have access to the PID file at " \
|
115
|
+
"#{fn.inspect}: #{err.message}"
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns `true` if the process is currently running. Returns `false` if this
|
120
|
+
# is not the case. The status of the process is determined by sending signal 0
|
121
|
+
# to the process.
|
122
|
+
def alive?
|
123
|
+
pid = self.pid
|
124
|
+
return if pid.nil?
|
125
|
+
|
126
|
+
Process.kill(0, pid)
|
127
|
+
true
|
128
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
# Send a signal the process identified by the PID file. The default signal to
|
133
|
+
# send is 'INT' (2). The signal can be given either as a string or a signal
|
134
|
+
# number.
|
135
|
+
#
|
136
|
+
# signal - The signal to send to the process (String or Integer)
|
137
|
+
#
|
138
|
+
# Returns an Integer or `nil` if an error was encountered.
|
139
|
+
def kill( signal = 'INT' )
|
140
|
+
pid = self.pid
|
141
|
+
return if pid.nil?
|
142
|
+
|
143
|
+
signal = Signal.list.invert[signal] if signal.is_a?(Integer)
|
144
|
+
logger.info "Killing PID #{pid} with #{signal}"
|
145
|
+
Process.kill(signal, pid)
|
146
|
+
|
147
|
+
rescue Errno::EINVAL
|
148
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
149
|
+
"'#{signal}' is an invalid or unsupported signal number."
|
150
|
+
nil
|
151
|
+
rescue Errno::EPERM
|
152
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
153
|
+
"Insufficient permissions."
|
154
|
+
nil
|
155
|
+
rescue Errno::ESRCH
|
156
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: " \
|
157
|
+
"Process is deceased or zombie."
|
158
|
+
nil
|
159
|
+
rescue Errno::EACCES => err
|
160
|
+
logger.error err.message
|
161
|
+
nil
|
162
|
+
rescue Errno::ENOENT => err
|
163
|
+
logger.error "Could not find a PID file at #{pid_file.inspect}. " \
|
164
|
+
"Most likely the process is no longer running."
|
165
|
+
nil
|
166
|
+
rescue Exception => err
|
167
|
+
unless err.is_a?(SystemExit)
|
168
|
+
logger.error "Failed to kill PID #{pid} with #{signal}: #{err.message}"
|
169
|
+
end
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
end
|
data/lib/servolux/server.rb
CHANGED
@@ -129,12 +129,9 @@ class Servolux::Server
|
|
129
129
|
|
130
130
|
Error = Class.new(::Servolux::Error)
|
131
131
|
|
132
|
-
DEFAULT_PID_FILE_MODE = 0640
|
133
|
-
|
134
132
|
attr_reader :name
|
135
133
|
attr_accessor :logger
|
136
|
-
|
137
|
-
attr_accessor :pid_file_mode
|
134
|
+
attr_reader :pid_file
|
138
135
|
|
139
136
|
# call-seq:
|
140
137
|
# Server.new( name, options = {} ) { block }
|
@@ -153,10 +150,9 @@ class Servolux::Server
|
|
153
150
|
@mutex = Mutex.new
|
154
151
|
@shutdown = nil
|
155
152
|
|
156
|
-
self.logger
|
157
|
-
self.
|
158
|
-
self.
|
159
|
-
self.interval = opts.fetch(:interval, 0)
|
153
|
+
self.logger = opts.fetch(:logger, Servolux::NullLogger())
|
154
|
+
self.interval = opts.fetch(:interval, 0)
|
155
|
+
self.pid_file = opts.fetch(:pid_file, name)
|
160
156
|
|
161
157
|
if block
|
162
158
|
eg = class << self; self; end
|
@@ -191,12 +187,12 @@ class Servolux::Server
|
|
191
187
|
|
192
188
|
begin
|
193
189
|
trap_signals
|
194
|
-
|
190
|
+
pid_file.write
|
195
191
|
start
|
196
192
|
join
|
197
193
|
wait_for_shutdown if wait
|
198
194
|
ensure
|
199
|
-
|
195
|
+
pid_file.delete
|
200
196
|
end
|
201
197
|
return self
|
202
198
|
end
|
@@ -240,30 +236,29 @@ class Servolux::Server
|
|
240
236
|
alias :term :shutdown # handles the TERM signal
|
241
237
|
private :start, :stop
|
242
238
|
|
243
|
-
#
|
244
|
-
#
|
239
|
+
# Set the PID file to the given `value`. If a PidFile instance is given, then
|
240
|
+
# it is used. If a name is given, then that name is used to create a PifFile
|
241
|
+
# instance.
|
242
|
+
#
|
243
|
+
# value - The PID file name or a PidFile instance.
|
245
244
|
#
|
246
|
-
|
247
|
-
|
245
|
+
# Raises an ArgumentError if the `value` cannot be used as a PID file.
|
246
|
+
def pid_file=( value )
|
247
|
+
@pid_file =
|
248
|
+
case value
|
249
|
+
when Servolux::PidFile
|
250
|
+
value
|
251
|
+
when String
|
252
|
+
path = File.dirname(value)
|
253
|
+
fn = File.basename(value, ".pid")
|
254
|
+
Servolux::PidFile.new(:name => fn, :path => path, :logger => logger)
|
255
|
+
else
|
256
|
+
raise ArgumentError, "#{value.inspect} cannot be used as a PID file"
|
257
|
+
end
|
248
258
|
end
|
249
259
|
|
250
260
|
private
|
251
261
|
|
252
|
-
def create_pid_file
|
253
|
-
logger.debug "Server #{name.inspect} creating pid file #{pid_file.inspect}"
|
254
|
-
File.open(pid_file, 'w', pid_file_mode) {|fd| fd.write(Process.pid.to_s)}
|
255
|
-
end
|
256
|
-
|
257
|
-
def delete_pid_file
|
258
|
-
if test(?f, pid_file)
|
259
|
-
pid = Integer(File.read(pid_file).strip)
|
260
|
-
return unless pid == Process.pid
|
261
|
-
|
262
|
-
logger.debug "Server #{name.inspect} removing pid file #{pid_file.inspect}"
|
263
|
-
File.delete(pid_file)
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
262
|
def trap_signals
|
268
263
|
SIGNALS.each do |sig|
|
269
264
|
method = sig.downcase.to_sym
|
data/lib/servolux/version.rb
CHANGED
data/spec/daemon_spec.rb
CHANGED
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Servolux::PidFile do
|
4
|
+
before :all do
|
5
|
+
tmp = Tempfile.new "servolux-pid-file"
|
6
|
+
@path = tmp.path; tmp.unlink
|
7
|
+
FileUtils.mkdir @path
|
8
|
+
end
|
9
|
+
|
10
|
+
after :all do
|
11
|
+
FileUtils.rm_rf @path
|
12
|
+
end
|
13
|
+
|
14
|
+
before :each do
|
15
|
+
FileUtils.rm_f Dir.glob("#@path/*.pid")
|
16
|
+
@pid_file = Servolux::PidFile.new \
|
17
|
+
:name => "test",
|
18
|
+
:path => @path,
|
19
|
+
:logger => Logging.logger['Servolux']
|
20
|
+
|
21
|
+
@filename = @pid_file.filename
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "filename" do
|
25
|
+
it "normalizes the process name" do
|
26
|
+
pid = Servolux::PidFile.new :name => "Test Server"
|
27
|
+
expect(pid.filename).to eq("./test_server.pid")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "includes the path" do
|
31
|
+
pid = Servolux::PidFile.new :name => "Test Server", :path => @path
|
32
|
+
expect(pid.filename).to eq("#@path/test_server.pid")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "creating" do
|
37
|
+
it "writes a PID file" do
|
38
|
+
expect(test(?e, @filename)).to be false
|
39
|
+
|
40
|
+
@pid_file.write(123456)
|
41
|
+
expect(test(?e, @filename)).to be true
|
42
|
+
expect(@log_output.readline.chomp).to \
|
43
|
+
eq(%Q{DEBUG Servolux : Writing pid file "#@path/test.pid"})
|
44
|
+
|
45
|
+
pid = Integer(File.read(@filename).strip)
|
46
|
+
expect(pid).to eq(123456)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "uses mode rw-r----- by default" do
|
50
|
+
expect(test(?e, @filename)).to be false
|
51
|
+
|
52
|
+
@pid_file.write
|
53
|
+
expect(test(?e, @filename)).to be true
|
54
|
+
expect(@log_output.readline.chomp).to \
|
55
|
+
eq(%Q{DEBUG Servolux : Writing pid file "#@path/test.pid"})
|
56
|
+
|
57
|
+
mode = File.stat(@filename).mode & 0777
|
58
|
+
expect(mode).to eq(0640)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "uses the given mode" do
|
62
|
+
@pid_file.mode = 0400
|
63
|
+
expect(test(?e, @filename)).to be false
|
64
|
+
|
65
|
+
@pid_file.write
|
66
|
+
expect(test(?e, @filename)).to be true
|
67
|
+
expect(@log_output.readline.chomp).to \
|
68
|
+
eq(%Q{DEBUG Servolux : Writing pid file "#@path/test.pid"})
|
69
|
+
|
70
|
+
mode = File.stat(@filename).mode & 0777
|
71
|
+
expect(mode).to eq(0400)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "deleting" do
|
76
|
+
it "removes a PID file" do
|
77
|
+
expect(test(?e, @filename)).to be false
|
78
|
+
expect { @pid_file.delete }.not_to raise_error
|
79
|
+
|
80
|
+
@pid_file.write
|
81
|
+
expect(test(?e, @filename)).to be true
|
82
|
+
expect(@log_output.readline.chomp).to \
|
83
|
+
eq(%Q{DEBUG Servolux : Writing pid file "#@path/test.pid"})
|
84
|
+
|
85
|
+
@pid_file.delete
|
86
|
+
expect(test(?e, @filename)).to be false
|
87
|
+
expect(@log_output.readline.chomp).to \
|
88
|
+
eq(%Q{DEBUG Servolux : Deleting pid file "#@path/test.pid"})
|
89
|
+
end
|
90
|
+
|
91
|
+
it "removes the PID file only from the same process" do
|
92
|
+
@pid_file.write(654321)
|
93
|
+
expect(test(?e, @filename)).to be true
|
94
|
+
expect(@log_output.readline.chomp).to \
|
95
|
+
eq(%Q{DEBUG Servolux : Writing pid file "#@path/test.pid"})
|
96
|
+
|
97
|
+
@pid_file.delete
|
98
|
+
expect(test(?e, @filename)).to be true
|
99
|
+
expect(@log_output.readline).to be_nil
|
100
|
+
end
|
101
|
+
|
102
|
+
it "can forcibly remove a PID file" do
|
103
|
+
@pid_file.write(135790)
|
104
|
+
expect(test(?e, @filename)).to be true
|
105
|
+
expect(@log_output.readline.chomp).to \
|
106
|
+
eq(%Q{DEBUG Servolux : Writing pid file "#@path/test.pid"})
|
107
|
+
|
108
|
+
@pid_file.delete!
|
109
|
+
expect(test(?e, @filename)).to be false
|
110
|
+
expect(@log_output.readline.chomp).to \
|
111
|
+
eq(%Q{DEBUG Servolux : Deleting pid file "#@path/test.pid"})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it "returns the PID from the file" do
|
116
|
+
expect(@pid_file.pid).to be_nil
|
117
|
+
|
118
|
+
File.open(@filename, 'w') { |fd| fd.write(314159) }
|
119
|
+
expect(@pid_file.pid).to eq(314159)
|
120
|
+
|
121
|
+
File.delete(@filename)
|
122
|
+
expect(@pid_file.pid).to be_nil
|
123
|
+
end
|
124
|
+
|
125
|
+
it "reports if the process is alive" do
|
126
|
+
expect(@pid_file.pid).to be_nil # there is no PID file yet
|
127
|
+
expect(@pid_file).not_to be_alive # and so we cannot determine
|
128
|
+
# if the process is alive
|
129
|
+
@pid_file.write
|
130
|
+
expect(@pid_file.pid).not_to be_nil
|
131
|
+
expect(@pid_file).to be_alive
|
132
|
+
end
|
133
|
+
end
|
data/spec/server_spec.rb
CHANGED
@@ -9,61 +9,67 @@ describe Servolux::Server do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
base = Class.new(Servolux::Server) do
|
12
|
-
def initialize
|
13
|
-
super('Test Server', :logger => Logging.logger['Servolux']
|
12
|
+
def initialize
|
13
|
+
super('Test Server', :logger => Logging.logger['Servolux'])
|
14
14
|
end
|
15
15
|
def run() sleep; end
|
16
16
|
end
|
17
17
|
|
18
18
|
before :each do
|
19
19
|
@server = base.new
|
20
|
-
|
20
|
+
@server.pid_file.delete
|
21
21
|
end
|
22
22
|
|
23
23
|
after :each do
|
24
24
|
@server.shutdown
|
25
|
-
|
25
|
+
@server.wait_for_shutdown
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'generates a PID file' do
|
29
|
-
expect(
|
29
|
+
expect(@server.pid_file).to_not exist
|
30
30
|
|
31
31
|
t = Thread.new {@server.startup}
|
32
32
|
wait_until { @server.running? and t.status == 'sleep' }
|
33
|
-
expect(
|
33
|
+
expect(@server.pid_file).to exist
|
34
34
|
|
35
35
|
@server.shutdown
|
36
36
|
wait_until { t.status == false }
|
37
|
-
expect(
|
37
|
+
expect(@server.pid_file).to_not exist
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'generates a PID file with mode rw-r----- by default' do
|
41
41
|
t = Thread.new {@server.startup}
|
42
42
|
wait_until { @server.running? and t.status == 'sleep' }
|
43
|
-
expect(
|
43
|
+
expect(@server.pid_file).to exist
|
44
44
|
|
45
|
-
expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux :
|
45
|
+
expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux : Writing pid file "./test_server.pid"))
|
46
46
|
expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux : Starting))
|
47
|
-
|
47
|
+
|
48
|
+
filename = @server.pid_file.filename
|
49
|
+
mode = File.stat(filename).mode & 0777
|
50
|
+
expect(mode).to eq(0640)
|
48
51
|
|
49
52
|
@server.shutdown
|
50
53
|
wait_until { t.status == false }
|
51
|
-
expect(
|
54
|
+
expect(@server.pid_file).to_not exist
|
52
55
|
end
|
53
56
|
|
54
57
|
it 'generates PID file with the specified permissions' do
|
55
|
-
@server.
|
58
|
+
@server.pid_file.mode = 0400
|
56
59
|
t = Thread.new {@server.startup}
|
57
60
|
wait_until { @server.running? and t.status == 'sleep' }
|
58
|
-
expect(
|
61
|
+
expect(@server.pid_file).to exist
|
59
62
|
|
60
|
-
expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux :
|
63
|
+
expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux : Writing pid file "./test_server.pid"))
|
61
64
|
expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux : Starting))
|
62
|
-
|
65
|
+
|
66
|
+
filename = @server.pid_file.filename
|
67
|
+
mode = File.stat(filename).mode & 0777
|
68
|
+
expect(mode).to eq(0400)
|
63
69
|
|
64
70
|
@server.shutdown
|
65
71
|
wait_until { t.status == false }
|
66
|
-
expect(
|
72
|
+
expect(@server.pid_file).to_not exist
|
67
73
|
end
|
68
74
|
|
69
75
|
it 'shuts down gracefully when signaled' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: servolux
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Pease
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bones-rspec
|
@@ -69,7 +69,7 @@ dependencies:
|
|
69
69
|
description: |-
|
70
70
|
Serv-O-Lux is a collection of Ruby classes that are useful for daemon and
|
71
71
|
process management, and for writing your own Ruby services. The code is well
|
72
|
-
documented and tested. It works with Ruby and JRuby supporting
|
72
|
+
documented and tested. It works with Ruby and JRuby supporting 1.9 and 2.0
|
73
73
|
interpreters.
|
74
74
|
email: tim.pease@gmail.com
|
75
75
|
executables: []
|
@@ -89,6 +89,8 @@ files:
|
|
89
89
|
- lib/servolux.rb
|
90
90
|
- lib/servolux/child.rb
|
91
91
|
- lib/servolux/daemon.rb
|
92
|
+
- lib/servolux/null_logger.rb
|
93
|
+
- lib/servolux/pid_file.rb
|
92
94
|
- lib/servolux/piper.rb
|
93
95
|
- lib/servolux/prefork.rb
|
94
96
|
- lib/servolux/server.rb
|
@@ -97,6 +99,7 @@ files:
|
|
97
99
|
- script/bootstrap
|
98
100
|
- spec/child_spec.rb
|
99
101
|
- spec/daemon_spec.rb
|
102
|
+
- spec/pid_file_spec.rb
|
100
103
|
- spec/piper_spec.rb
|
101
104
|
- spec/prefork_spec.rb
|
102
105
|
- spec/server_spec.rb
|