daemon-spawn 0.3.0 → 0.4.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.
data/Rakefile CHANGED
@@ -1,7 +1,6 @@
1
1
  # -*- ruby -*-
2
2
 
3
3
  require 'rubygems'
4
- require 'jeweler'
5
4
  require './lib/daemon_spawn.rb'
6
5
 
7
6
  begin
@@ -15,9 +14,9 @@ begin
15
14
  gemspec.rubyforge_project = "daemon-spawn"
16
15
  gemspec.email = "alex.vollmer@gmail.com"
17
16
  gemspec.homepage = "http://github.com/alexvollmer/daemon-spawn"
18
- gemspec.authors = ["Alex Vollmer"]
19
- Jeweler::GemcutterTasks.new
17
+ gemspec.authors = ["Alex Vollmer", "Seamus Abshere", "Emmanual Gomez", "Seth Falcon", "Woody Peterson"]
20
18
  end
19
+ Jeweler::GemcutterTasks.new
21
20
  rescue LoadError
22
21
  puts "Jeweler not available. Install it with: sudo gem install jeweler"
23
22
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{daemon-spawn}
8
- s.version = "0.3.0"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Alex Vollmer"]
12
- s.date = %q{2010-05-20}
11
+ s.authors = ["Alex Vollmer", "Seamus Abshere", "Emmanual Gomez", "Seth Falcon", "Woody Peterson"]
12
+ s.date = %q{2010-09-05}
13
13
  s.description = %q{With daemon-spawn you can start, stop and restart processes that run
14
14
  in the background. Processed are tracked by a simple PID file written
15
15
  to disk.}
@@ -28,9 +28,13 @@ Gem::Specification.new do |s|
28
28
  "daemon-spawn.gemspec",
29
29
  "lib/daemon_spawn.rb",
30
30
  "test/daemon_spawn_test.rb",
31
+ "test/helper.rb",
31
32
  "test/multi_daemon_spawn_test.rb",
33
+ "test/servers/deaf_server.rb",
32
34
  "test/servers/echo_server.rb",
33
- "test/servers/simple_server.rb"
35
+ "test/servers/simple_server.rb",
36
+ "test/servers/stubborn_server.rb",
37
+ "test/servers/taps_server.rb"
34
38
  ]
35
39
  s.homepage = %q{http://github.com/alexvollmer/daemon-spawn}
36
40
  s.rdoc_options = ["--charset=UTF-8"]
@@ -40,9 +44,13 @@ Gem::Specification.new do |s|
40
44
  s.summary = %q{Daemon launching and management made dead simple}
41
45
  s.test_files = [
42
46
  "test/daemon_spawn_test.rb",
47
+ "test/helper.rb",
43
48
  "test/multi_daemon_spawn_test.rb",
49
+ "test/servers/deaf_server.rb",
44
50
  "test/servers/echo_server.rb",
45
- "test/servers/simple_server.rb"
51
+ "test/servers/simple_server.rb",
52
+ "test/servers/stubborn_server.rb",
53
+ "test/servers/taps_server.rb"
46
54
  ]
47
55
 
48
56
  if s.respond_to? :specification_version then
@@ -3,6 +3,7 @@ require 'fileutils'
3
3
  # Large portions of this were liberally stolen from the
4
4
  # 'simple-daemon' project at http://simple-daemon.rubyforge.org/
5
5
  module DaemonSpawn
6
+ VERSION = '0.3.0'
6
7
 
7
8
  def self.usage(msg=nil) #:nodoc:
8
9
  print "#{msg}, " if msg
@@ -10,6 +11,12 @@ module DaemonSpawn
10
11
  puts "Where <command> is one of start, stop, restart or status"
11
12
  puts "[options] are additional options passed to the underlying process"
12
13
  end
14
+
15
+ def self.alive?(pid)
16
+ Process.kill 0, pid
17
+ rescue Errno::ESRCH
18
+ false
19
+ end
13
20
 
14
21
  def self.start(daemon, args) #:nodoc:
15
22
  if !File.writable?(File.dirname(daemon.log_file))
@@ -48,11 +55,22 @@ module DaemonSpawn
48
55
  def self.stop(daemon) #:nodoc:
49
56
  if pid = daemon.pid
50
57
  FileUtils.rm(daemon.pid_file)
51
- Process.kill("TERM", pid)
58
+ Process.kill(daemon.signal, pid)
52
59
  begin
53
60
  Process.wait(pid)
54
61
  rescue Errno::ECHILD
55
62
  end
63
+ if ticks = daemon.timeout
64
+ while ticks > 0 and alive?(pid) do
65
+ puts "Process is still alive. #{ticks} seconds until I kill -9 it..."
66
+ sleep 1
67
+ ticks -= 1
68
+ end
69
+ if alive?(pid)
70
+ puts "Process didn't quit after timeout of #{daemon.timeout} seconds. Killing..."
71
+ Process.kill 9, pid
72
+ end
73
+ end
56
74
  else
57
75
  puts "PID file not found. Is the daemon started?"
58
76
  end
@@ -65,7 +83,7 @@ module DaemonSpawn
65
83
  end
66
84
 
67
85
  class Base
68
- attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :app_name, :singleton, :index
86
+ attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :app_name, :singleton, :index, :signal, :timeout
69
87
 
70
88
  def initialize(opts = {})
71
89
  raise 'You must specify a :working_dir' unless opts[:working_dir]
@@ -73,6 +91,8 @@ module DaemonSpawn
73
91
  self.app_name = opts[:application] || classname
74
92
  self.pid_file = opts[:pid_file] || File.join(working_dir, 'tmp', 'pids', app_name + extension)
75
93
  self.log_file = opts[:log_file] || File.join(working_dir, 'logs', app_name + '.log')
94
+ self.signal = opts[:signal] || 'TERM'
95
+ self.timeout = opts[:timeout]
76
96
  self.index = opts[:index] || 0
77
97
  if self.index > 0
78
98
  self.pid_file += ".#{self.index}"
@@ -100,12 +120,8 @@ module DaemonSpawn
100
120
  end
101
121
 
102
122
  def alive? #:nodoc:
103
- if File.file?(self.pid_file)
104
- begin
105
- Process.kill(0, self.pid)
106
- rescue Errno::ESRCH, ::Exception
107
- false
108
- end
123
+ if File.file?(pid_file)
124
+ DaemonSpawn.alive? pid
109
125
  else
110
126
  false
111
127
  end
@@ -156,15 +172,14 @@ module DaemonSpawn
156
172
  end
157
173
 
158
174
  def self.start(opts, args)
159
- daemons = find(opts)
160
- if daemons.empty?
161
- daemons = build(opts)
162
- daemons.map { |d| DaemonSpawn.start(d, args) }
163
- else
164
- puts "Daemons already started! PIDS: #{daemons.map {|d| d.pid}.join(', ')}"
175
+ living_daemons = find(opts).select { |d| d.alive? }
176
+ if living_daemons.any?
177
+ puts "Daemons already started! PIDS: #{living_daemons.map {|d| d.pid}.join(', ')}"
165
178
  exit 1
179
+ else
180
+ build(opts).map { |d| DaemonSpawn.start(d, args) }
166
181
  end
167
- end
182
+ end
168
183
 
169
184
  def self.stop(opts, args)
170
185
  daemons = find(opts)
@@ -186,7 +201,7 @@ module DaemonSpawn
186
201
  end
187
202
 
188
203
  def self.restart(opts, args)
189
- daemons = find(opts)
204
+ daemons = build(opts)
190
205
  daemons.map do |daemon|
191
206
  DaemonSpawn.stop(daemon)
192
207
  DaemonSpawn.start(daemon, args)
@@ -1,12 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), "helper")
1
2
  require "socket"
2
- require "test/unit"
3
3
 
4
4
  class DaemonSpawnTest < Test::Unit::TestCase
5
5
 
6
6
  SERVERS = File.join(File.dirname(__FILE__), "servers")
7
7
 
8
+ # Try to make sure no pidfile (or process) is left over from another test.
9
+ def setup
10
+ %w{ echo_server deaf_server stubborn_server }.each do |server|
11
+ begin
12
+ Process.kill 9, possible_pid(server)
13
+ rescue Errno::ESRCH
14
+ # good, no process to kill
15
+ end
16
+ begin
17
+ File.unlink pid_file(server)
18
+ rescue Errno::ENOENT
19
+ # good, no pidfile to clear
20
+ end
21
+ end
22
+ end
23
+
8
24
  def with_socket
9
- socket = TCPSocket.new('localhost', 5150)
25
+ socket = TCPSocket.new('127.0.0.1', 5150)
10
26
  socket.setsockopt(Socket::SOL_SOCKET,
11
27
  Socket::SO_RCVTIMEO,
12
28
  [1, 0].pack("l_2"))
@@ -31,10 +47,25 @@ class DaemonSpawnTest < Test::Unit::TestCase
31
47
  with_socket
32
48
  ensure
33
49
  assert_match(//, `./echo_server.rb stop`)
34
- assert_raises(Errno::ECONNREFUSED) { TCPSocket.new('localhost', 5150) }
50
+ assert_raises(Errno::ECONNREFUSED) { TCPSocket.new('127.0.0.1', 5150) }
35
51
  end
36
52
  end
37
53
  end
54
+
55
+ def after_daemon_dies_leaving_pid_file
56
+ Dir.chdir(SERVERS) do
57
+ `./echo_server.rb stop`
58
+ sleep 1
59
+ `./echo_server.rb start 5150`
60
+ sleep 1
61
+ leftover_pid = IO.read(pid_file('echo_server')).to_i
62
+ Process.kill 9, leftover_pid
63
+ sleep 1
64
+ assert dead?(leftover_pid)
65
+ assert File.exists?(pid_file('echo_server'))
66
+ yield leftover_pid
67
+ end
68
+ end
38
69
 
39
70
  def test_daemon_running
40
71
  while_running do |socket|
@@ -98,5 +129,56 @@ class DaemonSpawnTest < Test::Unit::TestCase
98
129
  end
99
130
  end
100
131
  end
132
+
133
+ def test_start_after_daemon_dies_leaving_pid_file
134
+ after_daemon_dies_leaving_pid_file do |leftover_pid|
135
+ assert_match /EchoServer started/, `./echo_server.rb start 5150`
136
+ sleep 1
137
+ new_pid = IO.read(pid_file('echo_server')).to_i
138
+ assert new_pid != leftover_pid
139
+ assert alive?(new_pid)
140
+ end
141
+ end
101
142
 
143
+ def test_restart_after_daemon_dies_leaving_pid_file
144
+ after_daemon_dies_leaving_pid_file do |leftover_pid|
145
+ assert_match /EchoServer started/, `./echo_server.rb restart 5150`
146
+ sleep 1
147
+ new_pid = reported_pid 'echo_server'
148
+ assert new_pid != leftover_pid
149
+ assert alive?(new_pid)
150
+ end
151
+ end
152
+
153
+ def test_stop_using_custom_signal
154
+ Dir.chdir(SERVERS) do
155
+ `./deaf_server.rb start`
156
+ sleep 1
157
+ pid = reported_pid 'deaf_server'
158
+ assert alive?(pid)
159
+ Process.kill 'TERM', pid
160
+ sleep 1
161
+ assert alive?(pid)
162
+ Process.kill 'INT', pid
163
+ sleep 1
164
+ assert alive?(pid)
165
+ `./deaf_server.rb stop`
166
+ sleep 1
167
+ assert dead?(pid)
168
+ end
169
+ end
170
+
171
+ def test_kill_9_following_timeout
172
+ Dir.chdir(SERVERS) do
173
+ `./stubborn_server.rb start`
174
+ sleep 1
175
+ pid = reported_pid 'stubborn_server'
176
+ assert alive?(pid)
177
+ Process.kill 'TERM', pid
178
+ sleep 1
179
+ assert alive?(pid)
180
+ `./stubborn_server.rb stop`
181
+ assert dead?(pid)
182
+ end
183
+ end
102
184
  end
@@ -0,0 +1,26 @@
1
+ require "test/unit"
2
+ require "tmpdir"
3
+
4
+ class Test::Unit::TestCase
5
+ def pid_file(daemon_name)
6
+ File.join Dir.tmpdir, "#{daemon_name}.pid"
7
+ end
8
+
9
+ def possible_pid(daemon_name)
10
+ `ps x | grep ruby | grep #{daemon_name} | awk '{ print $1 }'`.to_i
11
+ end
12
+
13
+ def reported_pid(daemon_name)
14
+ IO.read(pid_file(daemon_name)).to_i
15
+ end
16
+
17
+ def alive?(pid)
18
+ Process.kill 0, pid
19
+ rescue Errno::ESRCH
20
+ false
21
+ end
22
+
23
+ def dead?(pid)
24
+ not alive? pid
25
+ end
26
+ end
@@ -1,4 +1,4 @@
1
- require "test/unit"
1
+ require File.join(File.dirname(__FILE__), "helper")
2
2
  require "tempfile"
3
3
 
4
4
  class MultiDaemonSpawnTest < Test::Unit::TestCase
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
4
+
5
+ require "daemon_spawn"
6
+ require "tmpdir"
7
+
8
+ # A server that only quits if you send it SIGWINCH
9
+ class DeafServer < DaemonSpawn::Base
10
+ def start(args)
11
+ trap('TERM') { }
12
+ trap('INT') { }
13
+ trap('SIGWINCH') { exit 0 }
14
+ loop do
15
+ sleep 100
16
+ end
17
+ end
18
+
19
+ def stop
20
+ end
21
+ end
22
+
23
+ DeafServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
24
+ :log_file => File.join(Dir.tmpdir, 'deaf_server.log'),
25
+ :pid_file => File.join(Dir.tmpdir, 'deaf_server.pid'),
26
+ :sync_log => true,
27
+ :singleton => true,
28
+ :signal => 'SIGWINCH')
@@ -4,6 +4,7 @@ $:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
4
4
 
5
5
  require "daemon_spawn"
6
6
  require "socket"
7
+ require "tmpdir"
7
8
 
8
9
  # An echo server using daemon-spawn. It starts up local TCP server
9
10
  # socket and repeats each line it receives on the connection. To fire
@@ -48,7 +49,7 @@ class EchoServer < DaemonSpawn::Base
48
49
  end
49
50
 
50
51
  EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
51
- :log_file => '/tmp/echo_server.log',
52
- :pid_file => '/tmp/echo_server.pid',
52
+ :log_file => File.join(Dir.tmpdir, 'echo_server.log'),
53
+ :pid_file => File.join(Dir.tmpdir, 'echo_server.pid'),
53
54
  :sync_log => true,
54
55
  :singleton => true)
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
4
+
5
+ require "daemon_spawn"
6
+ require "tmpdir"
7
+
8
+ # A server that needs to be kill -9'ed
9
+ class StubbornServer < DaemonSpawn::Base
10
+ def start(args)
11
+ trap('TERM') { }
12
+ loop do
13
+ sleep 100
14
+ end
15
+ end
16
+
17
+ def stop
18
+ raise "You should never get to me"
19
+ end
20
+ end
21
+
22
+ StubbornServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
23
+ :log_file => File.join(Dir.tmpdir, 'stubborn_server.log'),
24
+ :pid_file => File.join(Dir.tmpdir, 'stubborn_server.pid'),
25
+ :sync_log => true,
26
+ :singleton => true,
27
+ :timeout => 2)
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
4
+
5
+ require "daemon_spawn"
6
+
7
+ class TapsServer < DaemonSpawn::Base
8
+ def start(args)
9
+ sleep 5 # Sinatra takes its sweet time to shut down
10
+ Kernel.exec "/usr/bin/taps server mysql://dbusername:dbpassword@localhost/data1_production?encoding=utf8 username password"
11
+ end
12
+
13
+ def stop
14
+ end
15
+ end
16
+ TapsServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
17
+ :log_file => File.join(Dir.tmpdir, 'taps_server.log'),
18
+ :pid_file => File.join(Dir.tmpdir, 'taps_server.pid'),
19
+ :sync_log => true,
20
+ :singleton => true,
21
+ :signal => 'INT') # Sinatra ignores TERM
metadata CHANGED
@@ -1,21 +1,25 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: daemon-spawn
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Alex Vollmer
14
+ - Seamus Abshere
15
+ - Emmanual Gomez
16
+ - Seth Falcon
17
+ - Woody Peterson
14
18
  autorequire:
15
19
  bindir: bin
16
20
  cert_chain: []
17
21
 
18
- date: 2010-05-20 00:00:00 -07:00
22
+ date: 2010-09-05 00:00:00 -07:00
19
23
  default_executable:
20
24
  dependencies: []
21
25
 
@@ -41,9 +45,13 @@ files:
41
45
  - daemon-spawn.gemspec
42
46
  - lib/daemon_spawn.rb
43
47
  - test/daemon_spawn_test.rb
48
+ - test/helper.rb
44
49
  - test/multi_daemon_spawn_test.rb
50
+ - test/servers/deaf_server.rb
45
51
  - test/servers/echo_server.rb
46
52
  - test/servers/simple_server.rb
53
+ - test/servers/stubborn_server.rb
54
+ - test/servers/taps_server.rb
47
55
  has_rdoc: true
48
56
  homepage: http://github.com/alexvollmer/daemon-spawn
49
57
  licenses: []
@@ -80,6 +88,10 @@ specification_version: 3
80
88
  summary: Daemon launching and management made dead simple
81
89
  test_files:
82
90
  - test/daemon_spawn_test.rb
91
+ - test/helper.rb
83
92
  - test/multi_daemon_spawn_test.rb
93
+ - test/servers/deaf_server.rb
84
94
  - test/servers/echo_server.rb
85
95
  - test/servers/simple_server.rb
96
+ - test/servers/stubborn_server.rb
97
+ - test/servers/taps_server.rb