daemon-spawn 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +16 -0
- data/.gitignore +2 -0
- data/History.txt +9 -0
- data/Manifest.txt +5 -3
- data/README.txt +10 -1
- data/Rakefile +18 -5
- data/VERSION +1 -0
- data/daemon-spawn.gemspec +58 -0
- data/lib/{daemon-spawn.rb → daemon_spawn.rb} +78 -40
- data/test/daemon_spawn_test.rb +102 -0
- data/test/multi_daemon_spawn_test.rb +111 -0
- data/{examples → test/servers}/echo_server.rb +9 -3
- data/test/servers/simple_server.rb +34 -0
- metadata +36 -34
- data/test/test_daemon-spawn.rb +0 -0
data/.autotest
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Autotest.add_hook(:initialize) do |at|
|
2
|
+
at.clear_mappings
|
3
|
+
|
4
|
+
at.add_mapping(/.*flymake/) do |f, _|
|
5
|
+
[]
|
6
|
+
end
|
7
|
+
|
8
|
+
at.add_mapping(%r[lib/daemon-spawn.rb]) do |f, _|
|
9
|
+
at.files_matching /^test\/.*_test\.rb$/
|
10
|
+
end
|
11
|
+
|
12
|
+
at.add_mapping(/^test\/.*_test\.rb$/) do |filename, _|
|
13
|
+
filename
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/.gitignore
ADDED
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
=== 0.3.0 / 2010-04-18
|
2
|
+
|
3
|
+
* Typo fix and internal refactoring patches from Emmanuel Gomez
|
4
|
+
* Added multi-process spawning and management (inspired by Jontathan Tropper's
|
5
|
+
patches)
|
6
|
+
* Added LSB-compliance patches from Woody Peterson
|
7
|
+
* Added some actual test-coverage :-P
|
8
|
+
* Moved examples from "examples" directory to "test/servers"
|
9
|
+
|
1
10
|
=== 0.2.0 / 2009-04-22
|
2
11
|
|
3
12
|
* Allow specification of args instead of ARGV so that scripts can
|
data/Manifest.txt
CHANGED
@@ -2,6 +2,8 @@ History.txt
|
|
2
2
|
Manifest.txt
|
3
3
|
README.txt
|
4
4
|
Rakefile
|
5
|
-
lib/
|
6
|
-
test/
|
7
|
-
|
5
|
+
lib/daemon_spawn.rb
|
6
|
+
test/daemon_spawn_test.rb
|
7
|
+
test/multi_daemon_spawn_test.rb
|
8
|
+
test/servers/echo_server.rb
|
9
|
+
test/servers/simple_server.rb
|
data/README.txt
CHANGED
@@ -46,7 +46,10 @@ The <tt>spawn!</tt> method takes a hash of symbolized keys. At a minimum you
|
|
46
46
|
_must_ specify the <tt>:working_dir</tt> option. You can also override
|
47
47
|
the default locations for the log and PID files.
|
48
48
|
|
49
|
-
|
49
|
+
If you pass a <tt>:processes</tt> option to the <tt>spawn!</tt>,
|
50
|
+
daemon spawn will start that number of processes.
|
51
|
+
|
52
|
+
See the <tt>test/servers</tt> directory for working examples.
|
50
53
|
|
51
54
|
=== RUNNING A DAEMON:
|
52
55
|
|
@@ -68,6 +71,12 @@ method of an instance of your daemon class.
|
|
68
71
|
|
69
72
|
None!
|
70
73
|
|
74
|
+
== CONTRIBUTIONS:
|
75
|
+
|
76
|
+
Feel free to fork this project and send me pull requests with any
|
77
|
+
changes that you have. Please note that I won't accept any patches
|
78
|
+
with significant formatting changes or ones without tests.
|
79
|
+
|
71
80
|
== INSTALL:
|
72
81
|
|
73
82
|
* sudo gem install daemon-spawn
|
data/Rakefile
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
5
|
-
require './lib/
|
4
|
+
require 'jeweler'
|
5
|
+
require './lib/daemon_spawn.rb'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gemspec|
|
10
|
+
gemspec.name = "daemon-spawn"
|
11
|
+
gemspec.summary = "Daemon launching and management made dead simple"
|
12
|
+
gemspec.description = %Q[With daemon-spawn you can start, stop and restart processes that run
|
13
|
+
in the background. Processed are tracked by a simple PID file written
|
14
|
+
to disk.]
|
15
|
+
gemspec.rubyforge_project = "daemon-spawn"
|
16
|
+
gemspec.email = "alex.vollmer@gmail.com"
|
17
|
+
gemspec.homepage = "http://github.com/alexvollmer/daemon-spawn"
|
18
|
+
gemspec.authors = ["Alex Vollmer"]
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
10
23
|
end
|
11
24
|
|
12
25
|
# vim: syntax=Ruby
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{daemon-spawn}
|
8
|
+
s.version = "0.3.0"
|
9
|
+
|
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}
|
13
|
+
s.description = %q{With daemon-spawn you can start, stop and restart processes that run
|
14
|
+
in the background. Processed are tracked by a simple PID file written
|
15
|
+
to disk.}
|
16
|
+
s.email = %q{alex.vollmer@gmail.com}
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README.txt"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".autotest",
|
22
|
+
".gitignore",
|
23
|
+
"History.txt",
|
24
|
+
"Manifest.txt",
|
25
|
+
"README.txt",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"daemon-spawn.gemspec",
|
29
|
+
"lib/daemon_spawn.rb",
|
30
|
+
"test/daemon_spawn_test.rb",
|
31
|
+
"test/multi_daemon_spawn_test.rb",
|
32
|
+
"test/servers/echo_server.rb",
|
33
|
+
"test/servers/simple_server.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/alexvollmer/daemon-spawn}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubyforge_project = %q{daemon-spawn}
|
39
|
+
s.rubygems_version = %q{1.3.7}
|
40
|
+
s.summary = %q{Daemon launching and management made dead simple}
|
41
|
+
s.test_files = [
|
42
|
+
"test/daemon_spawn_test.rb",
|
43
|
+
"test/multi_daemon_spawn_test.rb",
|
44
|
+
"test/servers/echo_server.rb",
|
45
|
+
"test/servers/simple_server.rb"
|
46
|
+
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
else
|
54
|
+
end
|
55
|
+
else
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -1,9 +1,8 @@
|
|
1
|
-
require
|
1
|
+
require 'fileutils'
|
2
2
|
|
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.2.0'
|
7
6
|
|
8
7
|
def self.usage(msg=nil) #:nodoc:
|
9
8
|
print "#{msg}, " if msg
|
@@ -19,16 +18,16 @@ module DaemonSpawn
|
|
19
18
|
end
|
20
19
|
|
21
20
|
if !File.writable?(File.dirname(daemon.pid_file))
|
22
|
-
STDERR.puts "Unable to write
|
21
|
+
STDERR.puts "Unable to write PID file to #{daemon.pid_file}"
|
23
22
|
exit 1
|
24
23
|
end
|
25
24
|
|
26
25
|
if daemon.alive? && daemon.singleton
|
27
|
-
STDERR.puts "An instance of #{daemon.
|
26
|
+
STDERR.puts "An instance of #{daemon.app_name} is already " +
|
28
27
|
"running (PID #{daemon.pid})"
|
29
|
-
exit
|
28
|
+
exit 0
|
30
29
|
end
|
31
|
-
|
30
|
+
|
32
31
|
fork do
|
33
32
|
Process.setsid
|
34
33
|
exit if fork
|
@@ -43,7 +42,7 @@ module DaemonSpawn
|
|
43
42
|
trap("TERM") {daemon.stop; exit}
|
44
43
|
daemon.start(args)
|
45
44
|
end
|
46
|
-
puts "#{daemon.
|
45
|
+
puts "#{daemon.app_name} started."
|
47
46
|
end
|
48
47
|
|
49
48
|
def self.stop(daemon) #:nodoc:
|
@@ -55,30 +54,30 @@ module DaemonSpawn
|
|
55
54
|
rescue Errno::ECHILD
|
56
55
|
end
|
57
56
|
else
|
58
|
-
puts "
|
59
|
-
exit
|
57
|
+
puts "PID file not found. Is the daemon started?"
|
60
58
|
end
|
61
59
|
rescue Errno::ESRCH
|
62
|
-
puts "
|
60
|
+
puts "PID file found, but process was not running. The daemon may have died."
|
63
61
|
end
|
64
62
|
|
65
63
|
def self.status(daemon) #:nodoc:
|
66
|
-
|
67
|
-
puts "#{daemon.classname} is running (PID #{PidFile.recall(daemon)})"
|
68
|
-
else
|
69
|
-
puts "#{daemon.classname} is NOT running"
|
70
|
-
end
|
64
|
+
puts "#{daemon.app_name} is #{daemon.alive? ? "" : "NOT "}running (PID #{daemon.pid})"
|
71
65
|
end
|
72
66
|
|
73
67
|
class Base
|
74
|
-
attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :singleton
|
68
|
+
attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :app_name, :singleton, :index
|
75
69
|
|
76
|
-
def initialize(opts={
|
77
|
-
raise
|
70
|
+
def initialize(opts = {})
|
71
|
+
raise 'You must specify a :working_dir' unless opts[:working_dir]
|
78
72
|
self.working_dir = opts[:working_dir]
|
79
|
-
|
80
|
-
self.
|
81
|
-
self.
|
73
|
+
self.app_name = opts[:application] || classname
|
74
|
+
self.pid_file = opts[:pid_file] || File.join(working_dir, 'tmp', 'pids', app_name + extension)
|
75
|
+
self.log_file = opts[:log_file] || File.join(working_dir, 'logs', app_name + '.log')
|
76
|
+
self.index = opts[:index] || 0
|
77
|
+
if self.index > 0
|
78
|
+
self.pid_file += ".#{self.index}"
|
79
|
+
self.log_file += ".#{self.index}"
|
80
|
+
end
|
82
81
|
self.sync_log = opts[:sync_log]
|
83
82
|
self.singleton = opts[:singleton] || false
|
84
83
|
end
|
@@ -116,6 +115,22 @@ module DaemonSpawn
|
|
116
115
|
IO.read(self.pid_file).to_i rescue nil
|
117
116
|
end
|
118
117
|
|
118
|
+
def self.build(options)
|
119
|
+
count = options.delete(:processes) || 1
|
120
|
+
daemons = []
|
121
|
+
count.times do |index|
|
122
|
+
daemons << new(options.merge(:index => index))
|
123
|
+
end
|
124
|
+
daemons
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.find(options)
|
128
|
+
pid_file = new(options).pid_file
|
129
|
+
basename = File.basename(pid_file).split('.').first
|
130
|
+
pid_files = Dir.glob(File.join(File.dirname(pid_file), "#{basename}.*pid*"))
|
131
|
+
pid_files.map { |f| new(options.merge(:pid_file => f)) }
|
132
|
+
end
|
133
|
+
|
119
134
|
# Invoke this method to process command-line args and dispatch
|
120
135
|
# appropriately. Valid options include the following _symbols_:
|
121
136
|
# - <tt>:working_dir</tt> -- the working directory (required)
|
@@ -127,25 +142,10 @@ module DaemonSpawn
|
|
127
142
|
# args must begin with 'start', 'stop', 'status', or 'restart'.
|
128
143
|
# The first token will be removed and any remaining arguments
|
129
144
|
# passed to the daemon's start method.
|
130
|
-
def self.spawn!(opts={
|
131
|
-
case args.
|
132
|
-
when 'start'
|
133
|
-
|
134
|
-
DaemonSpawn.start(daemon, args)
|
135
|
-
when 'stop'
|
136
|
-
daemon = self.new(opts)
|
137
|
-
DaemonSpawn.stop(daemon)
|
138
|
-
when 'status'
|
139
|
-
daemon = self.new(opts)
|
140
|
-
if daemon.alive?
|
141
|
-
puts "#{daemon.classname} is running (#{daemon.pid})"
|
142
|
-
else
|
143
|
-
puts "#{daemon.classname} is NOT running"
|
144
|
-
end
|
145
|
-
when 'restart'
|
146
|
-
daemon = self.new(opts)
|
147
|
-
DaemonSpawn.stop(daemon)
|
148
|
-
DaemonSpawn.start(daemon, args)
|
145
|
+
def self.spawn!(opts = {}, args = ARGV)
|
146
|
+
case args.any? and command = args.shift
|
147
|
+
when 'start', 'stop', 'status', 'restart'
|
148
|
+
send(command, opts, args)
|
149
149
|
when '-h', '--help', 'help'
|
150
150
|
DaemonSpawn.usage
|
151
151
|
exit
|
@@ -154,5 +154,43 @@ module DaemonSpawn
|
|
154
154
|
exit 1
|
155
155
|
end
|
156
156
|
end
|
157
|
+
|
158
|
+
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(', ')}"
|
165
|
+
exit 1
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.stop(opts, args)
|
170
|
+
daemons = find(opts)
|
171
|
+
if daemons.empty?
|
172
|
+
puts "No PID files found. Is the daemon started?"
|
173
|
+
exit 1
|
174
|
+
else
|
175
|
+
daemons.each { |d| DaemonSpawn.stop(d) }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.status(opts, args)
|
180
|
+
daemons = find(opts)
|
181
|
+
if daemons.empty?
|
182
|
+
puts 'No PIDs found'
|
183
|
+
else
|
184
|
+
daemons.each { |d| DaemonSpawn.status(d) }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.restart(opts, args)
|
189
|
+
daemons = find(opts)
|
190
|
+
daemons.map do |daemon|
|
191
|
+
DaemonSpawn.stop(daemon)
|
192
|
+
DaemonSpawn.start(daemon, args)
|
193
|
+
end
|
194
|
+
end
|
157
195
|
end
|
158
196
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "test/unit"
|
3
|
+
|
4
|
+
class DaemonSpawnTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
SERVERS = File.join(File.dirname(__FILE__), "servers")
|
7
|
+
|
8
|
+
def with_socket
|
9
|
+
socket = TCPSocket.new('localhost', 5150)
|
10
|
+
socket.setsockopt(Socket::SOL_SOCKET,
|
11
|
+
Socket::SO_RCVTIMEO,
|
12
|
+
[1, 0].pack("l_2"))
|
13
|
+
|
14
|
+
begin
|
15
|
+
yield(socket) if block_given?
|
16
|
+
ensure
|
17
|
+
socket.close
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def echo_server(*args)
|
22
|
+
`./echo_server.rb #{args.join(' ')}`
|
23
|
+
end
|
24
|
+
|
25
|
+
def while_running
|
26
|
+
Dir.chdir(SERVERS) do
|
27
|
+
`./echo_server.rb stop`
|
28
|
+
assert_match(/EchoServer started./, `./echo_server.rb start 5150`)
|
29
|
+
sleep 1
|
30
|
+
begin
|
31
|
+
with_socket
|
32
|
+
ensure
|
33
|
+
assert_match(//, `./echo_server.rb stop`)
|
34
|
+
assert_raises(Errno::ECONNREFUSED) { TCPSocket.new('localhost', 5150) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_daemon_running
|
40
|
+
while_running do |socket|
|
41
|
+
socket << "foobar\n"
|
42
|
+
assert_equal "foobar\n", socket.readline
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_status_running
|
47
|
+
while_running do |socket|
|
48
|
+
assert_match(/EchoServer is running/, `./echo_server.rb status`)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_status_not_running
|
53
|
+
Dir.chdir(SERVERS) do
|
54
|
+
assert_match(/No PIDs found/, `./echo_server.rb status`)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_start_after_started
|
59
|
+
while_running do
|
60
|
+
pid = echo_server("status").match(/PID (\d+)/)[1]
|
61
|
+
assert_match(/Daemons already started! PIDS: #{pid}/,
|
62
|
+
echo_server("start"))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_stop_after_stopped
|
67
|
+
Dir.chdir(SERVERS) do
|
68
|
+
assert_match("No PID files found. Is the daemon started?",
|
69
|
+
`./echo_server.rb stop`)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_restart_after_stopped
|
74
|
+
Dir.chdir(SERVERS) do
|
75
|
+
assert_match(/EchoServer started/, `./echo_server.rb restart 5150`)
|
76
|
+
assert_equal(0, $?.exitstatus)
|
77
|
+
sleep 1
|
78
|
+
with_socket do |socket|
|
79
|
+
socket << "foobar\n"
|
80
|
+
assert_equal "foobar\n", socket.readline
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_restart_after_started
|
86
|
+
Dir.chdir(SERVERS) do
|
87
|
+
assert_match(/EchoServer started/, `./echo_server.rb start 5150`)
|
88
|
+
assert_equal(0, $?.exitstatus)
|
89
|
+
sleep 1
|
90
|
+
|
91
|
+
assert_match(/EchoServer started/, `./echo_server.rb restart 5150`)
|
92
|
+
assert_equal(0, $?.exitstatus)
|
93
|
+
sleep 1
|
94
|
+
|
95
|
+
with_socket do |socket|
|
96
|
+
socket << "foobar\n"
|
97
|
+
assert_equal "foobar\n", socket.readline
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "tempfile"
|
3
|
+
|
4
|
+
class MultiDaemonSpawnTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
SERVERS = File.join(File.dirname(__FILE__), "servers")
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@tmpfile = Tempfile.new("multi_daemon_spawn_test")
|
10
|
+
end
|
11
|
+
|
12
|
+
def tear_down
|
13
|
+
@tmpfile.delete
|
14
|
+
end
|
15
|
+
|
16
|
+
def simple_server(*args)
|
17
|
+
`./simple_server.rb #{args.join(" ")}`
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_pids
|
21
|
+
regexp = /SimpleServer is running \(PID (\d+)\)/
|
22
|
+
pids = simple_server("status").split("\n").map do |line|
|
23
|
+
if m = regexp.match(line)
|
24
|
+
m[1]
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
def while_running
|
32
|
+
Dir.chdir(SERVERS) do
|
33
|
+
simple_server "stop"
|
34
|
+
simple_server "start", @tmpfile.path
|
35
|
+
sleep 1
|
36
|
+
begin
|
37
|
+
yield if block_given?
|
38
|
+
ensure
|
39
|
+
simple_server "stop"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_start_multiple
|
45
|
+
while_running do
|
46
|
+
lines = open(@tmpfile.path).readlines
|
47
|
+
assert_equal 2, lines.size
|
48
|
+
assert lines.member?("SimpleServer (0) started\n")
|
49
|
+
assert lines.member?("SimpleServer (1) started\n")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_status_multiple
|
54
|
+
while_running do
|
55
|
+
lines = simple_server("status").split("\n")
|
56
|
+
lines.each do |line|
|
57
|
+
assert_match /SimpleServer is running/, line
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_stop_multiple
|
63
|
+
while_running
|
64
|
+
Dir.chdir(SERVERS) do
|
65
|
+
assert_match /No PIDs found/, simple_server("status")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_restart_multiple
|
70
|
+
while_running do
|
71
|
+
pids = current_pids
|
72
|
+
simple_server "restart"
|
73
|
+
new_pids = current_pids
|
74
|
+
assert_not_equal pids.sort, new_pids.sort
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_status_with_one_dead_process
|
79
|
+
while_running do
|
80
|
+
pids = current_pids
|
81
|
+
Process.kill(9, pids[0].to_i)
|
82
|
+
|
83
|
+
lines = simple_server("status").split("\n")
|
84
|
+
assert_equal 2, lines.size
|
85
|
+
assert lines.member?("SimpleServer is NOT running (PID #{pids[0]})")
|
86
|
+
assert lines.member?("SimpleServer is running (PID #{pids[1]})")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_restart_with_one_dead_process
|
91
|
+
while_running do
|
92
|
+
pids = current_pids
|
93
|
+
Process.kill(9, pids[0].to_i)
|
94
|
+
|
95
|
+
lines = simple_server("restart").split("\n")
|
96
|
+
assert lines.member?("PID file found, but process was not running. The daemon may have died."), lines.inspect
|
97
|
+
assert_equal 2, lines.select { |l| l == "SimpleServer started." }.size
|
98
|
+
|
99
|
+
new_pids = current_pids
|
100
|
+
assert_not_equal new_pids, pids
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_start_after_started
|
105
|
+
while_running do
|
106
|
+
pids = current_pids
|
107
|
+
assert_match(/Daemons already started! PIDS: #{pids.join(', ')}/,
|
108
|
+
simple_server("start"))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
|
4
|
+
|
5
|
+
require "daemon_spawn"
|
3
6
|
require "socket"
|
4
|
-
require "rubygems"
|
5
|
-
require "daemon-spawn"
|
6
7
|
|
7
8
|
# An echo server using daemon-spawn. It starts up local TCP server
|
8
9
|
# socket and repeats each line it receives on the connection. To fire
|
@@ -21,13 +22,18 @@ class EchoServer < DaemonSpawn::Base
|
|
21
22
|
def start(args)
|
22
23
|
port = args.empty? ? 0 : args.first.to_i
|
23
24
|
self.server_socket = TCPServer.new('127.0.0.1', port)
|
25
|
+
self.server_socket.setsockopt(Socket::SOL_SOCKET,
|
26
|
+
Socket::SO_REUSEADDR,
|
27
|
+
true)
|
24
28
|
port = self.server_socket.addr[1]
|
25
29
|
puts "EchoServer started on port #{port}"
|
26
30
|
loop do
|
27
31
|
begin
|
28
32
|
client = self.server_socket.accept
|
33
|
+
puts "Got a connection from #{client}"
|
29
34
|
while str = client.gets
|
30
35
|
client.write(str)
|
36
|
+
puts "Echoed '#{str}' to #{client}"
|
31
37
|
end
|
32
38
|
rescue Errno::ECONNRESET => e
|
33
39
|
STDERR.puts "Client reset connection"
|
@@ -41,7 +47,7 @@ class EchoServer < DaemonSpawn::Base
|
|
41
47
|
end
|
42
48
|
end
|
43
49
|
|
44
|
-
EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..'),
|
50
|
+
EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
|
45
51
|
:log_file => '/tmp/echo_server.log',
|
46
52
|
:pid_file => '/tmp/echo_server.pid',
|
47
53
|
:sync_log => true,
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
|
4
|
+
|
5
|
+
require "daemon_spawn"
|
6
|
+
|
7
|
+
class SimpleServer < DaemonSpawn::Base
|
8
|
+
|
9
|
+
attr_accessor :outfile
|
10
|
+
|
11
|
+
def start(args)
|
12
|
+
abort "USAGE: phrase_server.rb LOGFILE" if args.empty?
|
13
|
+
@outfile = args.first
|
14
|
+
self.puts "SimpleServer (#{self.index}) started"
|
15
|
+
while true # keep running like a real daemon
|
16
|
+
sleep 5
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def puts(str)
|
21
|
+
open(@outfile, "a") { |f| f.puts str }
|
22
|
+
end
|
23
|
+
|
24
|
+
def stop
|
25
|
+
self.puts "SimpleServer (#{self.index}) stopped"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
SimpleServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
|
31
|
+
:log_file => '/tmp/simple_server.log',
|
32
|
+
:pid_file => '/tmp/simple_server.pid',
|
33
|
+
:sync_log => true,
|
34
|
+
:processes => 2)
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: daemon-spawn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Alex Vollmer
|
@@ -9,75 +15,71 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2010-05-20 00:00:00 -07:00
|
13
19
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
name: hoe
|
17
|
-
type: :development
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 2.3.3
|
24
|
-
version:
|
20
|
+
dependencies: []
|
21
|
+
|
25
22
|
description: |-
|
26
|
-
Daemon launching and management made dead simple.
|
27
|
-
|
28
23
|
With daemon-spawn you can start, stop and restart processes that run
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
In addition, you can choose to either execute ruby in your daemonized
|
33
|
-
process or 'exec' another process altogether (handy for wrapping other
|
34
|
-
services).
|
35
|
-
email:
|
36
|
-
- alex.vollmer@gmail.com
|
24
|
+
in the background. Processed are tracked by a simple PID file written
|
25
|
+
to disk.
|
26
|
+
email: alex.vollmer@gmail.com
|
37
27
|
executables: []
|
38
28
|
|
39
29
|
extensions: []
|
40
30
|
|
41
31
|
extra_rdoc_files:
|
42
|
-
- History.txt
|
43
|
-
- Manifest.txt
|
44
32
|
- README.txt
|
45
33
|
files:
|
34
|
+
- .autotest
|
35
|
+
- .gitignore
|
46
36
|
- History.txt
|
47
37
|
- Manifest.txt
|
48
38
|
- README.txt
|
49
39
|
- Rakefile
|
50
|
-
-
|
51
|
-
-
|
52
|
-
-
|
40
|
+
- VERSION
|
41
|
+
- daemon-spawn.gemspec
|
42
|
+
- lib/daemon_spawn.rb
|
43
|
+
- test/daemon_spawn_test.rb
|
44
|
+
- test/multi_daemon_spawn_test.rb
|
45
|
+
- test/servers/echo_server.rb
|
46
|
+
- test/servers/simple_server.rb
|
53
47
|
has_rdoc: true
|
54
48
|
homepage: http://github.com/alexvollmer/daemon-spawn
|
55
49
|
licenses: []
|
56
50
|
|
57
51
|
post_install_message:
|
58
52
|
rdoc_options:
|
59
|
-
- --
|
60
|
-
- README.txt
|
53
|
+
- --charset=UTF-8
|
61
54
|
require_paths:
|
62
55
|
- lib
|
63
56
|
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
64
58
|
requirements:
|
65
59
|
- - ">="
|
66
60
|
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
67
64
|
version: "0"
|
68
|
-
version:
|
69
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
70
67
|
requirements:
|
71
68
|
- - ">="
|
72
69
|
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
73
|
version: "0"
|
74
|
-
version:
|
75
74
|
requirements: []
|
76
75
|
|
77
76
|
rubyforge_project: daemon-spawn
|
78
|
-
rubygems_version: 1.3.
|
77
|
+
rubygems_version: 1.3.7
|
79
78
|
signing_key:
|
80
79
|
specification_version: 3
|
81
80
|
summary: Daemon launching and management made dead simple
|
82
81
|
test_files:
|
83
|
-
- test/
|
82
|
+
- test/daemon_spawn_test.rb
|
83
|
+
- test/multi_daemon_spawn_test.rb
|
84
|
+
- test/servers/echo_server.rb
|
85
|
+
- test/servers/simple_server.rb
|
data/test/test_daemon-spawn.rb
DELETED
File without changes
|