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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c744b0af533fabea800e187f1008917cf2c7aacf
4
- data.tar.gz: e46bfba16ebbcfac37aa4e4d0fa44bb6e090e403
3
+ metadata.gz: 581567ae92a495cdeb92737c979d52e6bd8cf996
4
+ data.tar.gz: 0f887e81fc0a8b78e1f340578f2183b460a27ca7
5
5
  SHA512:
6
- metadata.gz: 1f8e41c57443b49a4a4ba6766daa95933e7e6a9827e759aee2284828589e29c13536041ba70e196ac945fa5cc1ee96c61124cfd0726881990634c4b4f9b32372
7
- data.tar.gz: 555e8ad6764063743afca6635515f445e8e8e04b509a4143fbda4026cc9074b98d9310f36da0f37c59083f127cba8a10df44738dda4b1590d52ea9a76d83af9c
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 both 1.8 and 1.9
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
@@ -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, addr = acceptor.accept
38
+ socket,_ = acceptor.accept
39
39
  socket.write "child #$$ echo> "
40
40
  socket.flush
41
41
  message = socket.gets
@@ -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
-
@@ -77,7 +77,7 @@ class Servolux::Daemon
77
77
 
78
78
  attr_accessor :name
79
79
  attr_accessor :logger
80
- attr_accessor :pid_file
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, nil)
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
- @name = val.name
202
- @logger = val.logger
203
- @pid_file = val.pid_file
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
- pid = retrieve_pid
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
- signal = Signal.list.invert[signal] if signal.is_a?(Integer)
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 : Integer(File.read(pid_file).strip)
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
@@ -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
- attr_writer :pid_file
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 = opts.fetch(:logger, nil)
157
- self.pid_file = opts.fetch(:pid_file, nil)
158
- self.pid_file_mode = opts.fetch(:pid_file_mode, DEFAULT_PID_FILE_MODE)
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
- create_pid_file
190
+ pid_file.write
195
191
  start
196
192
  join
197
193
  wait_for_shutdown if wait
198
194
  ensure
199
- delete_pid_file
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
- # Returns the PID file name used by the server. If none was given, then
244
- # the server name is used to create a PID file name.
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
- def pid_file
247
- @pid_file ||= name.downcase.tr(' ','_') + '.pid'
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
@@ -1,5 +1,5 @@
1
1
  module Servolux
2
- VERSION = "0.11.0".freeze
2
+ VERSION = "0.12.0".freeze
3
3
 
4
4
  # Returns the version string for the library.
5
5
  def self.version
@@ -1,5 +1,4 @@
1
1
  require File.expand_path('../spec_helper', __FILE__)
2
- require 'logger'
3
2
  require 'fileutils'
4
3
 
5
4
  if Servolux.fork?
@@ -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
@@ -9,61 +9,67 @@ describe Servolux::Server do
9
9
  end
10
10
 
11
11
  base = Class.new(Servolux::Server) do
12
- def initialize( &block )
13
- super('Test Server', :logger => Logging.logger['Servolux'], &block)
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
- File.delete @server.pid_file if test(?f, @server.pid_file)
20
+ @server.pid_file.delete
21
21
  end
22
22
 
23
23
  after :each do
24
24
  @server.shutdown
25
- File.delete @server.pid_file if test(?f, @server.pid_file)
25
+ @server.wait_for_shutdown
26
26
  end
27
27
 
28
28
  it 'generates a PID file' do
29
- expect(test(?e, @server.pid_file)).to be false
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(test(?e, @server.pid_file)).to be true
33
+ expect(@server.pid_file).to exist
34
34
 
35
35
  @server.shutdown
36
36
  wait_until { t.status == false }
37
- expect(test(?e, @server.pid_file)).to be false
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(test(?e, @server.pid_file)).to be true
43
+ expect(@server.pid_file).to exist
44
44
 
45
- expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux : Server "Test Server" creating pid file "test_server.pid"))
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
- expect(File.stat(@server.pid_file).mode & 0777).to eq(0640)
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(test(?e, @server.pid_file)).to be false
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.pid_file_mode = 0400
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(test(?e, @server.pid_file)).to be true
61
+ expect(@server.pid_file).to exist
59
62
 
60
- expect(@log_output.readline.chomp).to eq(%q(DEBUG Servolux : Server "Test Server" creating pid file "test_server.pid"))
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
- expect(File.stat(@server.pid_file).mode & 0777).to eq(0400)
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(test(?e, @server.pid_file)).to be false
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.11.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-05-30 00:00:00.000000000 Z
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 both 1.8 and 1.9
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