servolux 0.11.0 → 0.12.0

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