dante 0.0.4 → 0.1.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/README.md +16 -1
- data/lib/dante.rb +1 -1
- data/lib/dante/runner.rb +85 -26
- data/lib/dante/version.rb +1 -1
- data/test/runner_test.rb +3 -3
- data/test/test_helper.rb +2 -2
- metadata +19 -17
data/README.md
CHANGED
@@ -55,6 +55,8 @@ Be sure to properly make your bin executable:
|
|
55
55
|
chmod +x bin/myapp
|
56
56
|
```
|
57
57
|
|
58
|
+
### CLI
|
59
|
+
|
58
60
|
This gives your binary several useful things for free:
|
59
61
|
|
60
62
|
```
|
@@ -81,7 +83,7 @@ will stop all daemonized processes for the specified pid file.
|
|
81
83
|
|
82
84
|
Will return a useful help banner message explaining the simple usage.
|
83
85
|
|
84
|
-
|
86
|
+
### Advanced
|
85
87
|
|
86
88
|
In many cases, you will need to add custom flags/options or a custom description to your executable. You can do this
|
87
89
|
easily by using `Dante::Runner` more explicitly:
|
@@ -122,6 +124,19 @@ Now you would be able to do:
|
|
122
124
|
and the `opts` would contain the `:test` option for use in your script. In addition, help will now contain
|
123
125
|
your customized description in the banner.
|
124
126
|
|
127
|
+
You can also use dante programmatically to start, stop and restart arbitrary code:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
# daemon start
|
131
|
+
Dante::Runner.new('gitdocs').execute(:daemonize => true, :pid_path => @pid) { something! }
|
132
|
+
# daemon stop
|
133
|
+
Dante::Runner.new('gitdocs').execute(:kill => true, :pid_path => @pid)
|
134
|
+
# daemon restart
|
135
|
+
Dante::Runner.new('gitdocs').execute(:daemonize => true, :restart => true, :pid_path => @pid) { something! }
|
136
|
+
```
|
137
|
+
|
138
|
+
so you can use dante as part of a more complex CLI executable.
|
139
|
+
|
125
140
|
## God
|
126
141
|
|
127
142
|
Dante can be used well in conjunction with the excellent God process manager. Simply, use Dante to daemonize a process
|
data/lib/dante.rb
CHANGED
data/lib/dante/runner.rb
CHANGED
@@ -15,8 +15,7 @@ This is a utility for setting up a binary executable for a service.
|
|
15
15
|
|
16
16
|
module Dante
|
17
17
|
class Runner
|
18
|
-
|
19
|
-
class Abort < Exception; end
|
18
|
+
MAX_START_TRIES = 5
|
20
19
|
|
21
20
|
attr_accessor :options, :name, :description
|
22
21
|
|
@@ -29,6 +28,7 @@ module Dante
|
|
29
28
|
def initialize(name, defaults={}, &block)
|
30
29
|
@name = name
|
31
30
|
@startup_command = block
|
31
|
+
@debug = defaults.delete(:debug) || true
|
32
32
|
@options = {
|
33
33
|
:host => '0.0.0.0',
|
34
34
|
:pid_path => "/var/run/#{@name}.pid"
|
@@ -44,12 +44,14 @@ module Dante
|
|
44
44
|
# Executes the runner based on options
|
45
45
|
# @runner.execute
|
46
46
|
# @runner.execute { ... }
|
47
|
-
def execute(&block)
|
47
|
+
def execute(opts={}, &block)
|
48
48
|
parse_options
|
49
|
+
self.options.merge!(opts)
|
49
50
|
|
50
51
|
if options.include?(:kill)
|
51
|
-
|
52
|
+
self.stop
|
52
53
|
else # create process
|
54
|
+
self.stop if options.include?(:restart)
|
53
55
|
Process.euid = options[:user] if options[:user]
|
54
56
|
Process.egid = options[:group] if options[:group]
|
55
57
|
@startup_command = block if block_given?
|
@@ -57,23 +59,64 @@ module Dante
|
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
62
|
+
def daemonize
|
63
|
+
return log("Process is already started") if self.daemon_running? # daemon already started
|
64
|
+
|
65
|
+
# Start process
|
66
|
+
pid = fork do
|
67
|
+
exit if fork
|
68
|
+
Process.setsid
|
69
|
+
exit if fork
|
70
|
+
store_pid(Process.pid)
|
71
|
+
File.umask 0000
|
72
|
+
STDIN.reopen "/dev/null"
|
73
|
+
STDOUT.reopen "/dev/null", "a"
|
74
|
+
STDERR.reopen STDOUT
|
75
|
+
start
|
76
|
+
end
|
77
|
+
# Ensure process is running
|
78
|
+
if until_true(MAX_START_TRIES) { self.daemon_running? }
|
79
|
+
log "Daemon has started successfully"
|
80
|
+
true
|
81
|
+
else # Failed to start
|
82
|
+
log "Daemonized process couldn't be started"
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
60
87
|
def start
|
61
|
-
|
88
|
+
log "Starting #{@name} service..."
|
62
89
|
|
63
90
|
trap("INT") {
|
64
|
-
|
91
|
+
interrupt
|
65
92
|
exit
|
66
93
|
}
|
67
94
|
trap("TERM"){
|
68
|
-
|
95
|
+
interrupt
|
69
96
|
exit
|
70
97
|
}
|
71
98
|
|
72
99
|
@startup_command.call(self.options) if @startup_command
|
73
100
|
end
|
74
101
|
|
75
|
-
|
76
|
-
|
102
|
+
# Stops a daemonized process
|
103
|
+
def stop(kill_arg=nil)
|
104
|
+
if self.daemon_running?
|
105
|
+
kill_pid(kill_arg || options[:kill])
|
106
|
+
until_true(MAX_START_TRIES) { self.daemon_stopped? }
|
107
|
+
else # not running
|
108
|
+
log "No #{@name} processes are running"
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def restart
|
114
|
+
self.stop
|
115
|
+
self.start
|
116
|
+
end
|
117
|
+
|
118
|
+
def interrupt
|
119
|
+
raise Interrupt
|
77
120
|
sleep(1)
|
78
121
|
end
|
79
122
|
|
@@ -121,38 +164,54 @@ module Dante
|
|
121
164
|
options
|
122
165
|
end
|
123
166
|
|
124
|
-
|
167
|
+
protected
|
125
168
|
|
126
169
|
def store_pid(pid)
|
127
|
-
|
128
|
-
|
170
|
+
FileUtils.mkdir_p(File.dirname(options[:pid_path]))
|
171
|
+
File.open(options[:pid_path], 'w'){|f| f.write("#{pid}\n")}
|
129
172
|
end
|
130
173
|
|
131
|
-
def kill_pid(k)
|
174
|
+
def kill_pid(k='*')
|
132
175
|
Dir[options[:pid_path]].each do |f|
|
133
176
|
begin
|
134
177
|
pid = IO.read(f).chomp.to_i
|
135
178
|
FileUtils.rm f
|
136
179
|
Process.kill('INT', pid)
|
137
|
-
|
180
|
+
log "Stopped PID: #{pid} at #{f}"
|
138
181
|
rescue => e
|
139
|
-
|
182
|
+
log "Failed to stop! #{k}: #{e}"
|
140
183
|
end
|
141
184
|
end
|
142
185
|
end
|
143
186
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
STDIN.reopen "/dev/null"
|
152
|
-
STDOUT.reopen "/dev/null", "a"
|
153
|
-
STDERR.reopen STDOUT
|
154
|
-
start
|
187
|
+
# Runs until the block condition is met or the timeout_seconds is exceeded
|
188
|
+
# until_true(10) { ...return_condition... }
|
189
|
+
def until_true(timeout_seconds, interval=1, &block)
|
190
|
+
elapsed_seconds = 0
|
191
|
+
while elapsed_seconds < timeout_seconds && block.call != true
|
192
|
+
elapsed_seconds += interval
|
193
|
+
sleep(interval)
|
155
194
|
end
|
195
|
+
elapsed_seconds < timeout_seconds
|
196
|
+
end
|
197
|
+
|
198
|
+
# Returns true if process is not running
|
199
|
+
def daemon_stopped?
|
200
|
+
! self.daemon_running?
|
201
|
+
end
|
202
|
+
|
203
|
+
# Returns running for the daemonized process
|
204
|
+
# self.daemon_running?
|
205
|
+
def daemon_running?
|
206
|
+
return false unless File.exist?(options[:pid_path])
|
207
|
+
Process.kill 0, File.read(options[:pid_path]).to_i
|
208
|
+
true
|
209
|
+
rescue Errno::ESRCH
|
210
|
+
false
|
211
|
+
end
|
212
|
+
|
213
|
+
def log(message)
|
214
|
+
puts message if @debug
|
156
215
|
end
|
157
216
|
|
158
217
|
end
|
data/lib/dante/version.rb
CHANGED
data/test/runner_test.rb
CHANGED
@@ -34,7 +34,7 @@ describe "dante runner" do
|
|
34
34
|
sleep(1) # Wait to complete
|
35
35
|
@output = File.read(@process.tmp_path)
|
36
36
|
assert_match /Started on 8080!!/, @output
|
37
|
-
assert_match /
|
37
|
+
assert_match /Interrupt!!/, @output
|
38
38
|
assert_match /Closing!!/, @output
|
39
39
|
end
|
40
40
|
|
@@ -44,7 +44,7 @@ describe "dante runner" do
|
|
44
44
|
sleep(1) # Wait to complete
|
45
45
|
@output = File.read(@process.tmp_path)
|
46
46
|
assert_match /Started on 8080!!/, @output
|
47
|
-
assert_match /
|
47
|
+
assert_match /Interrupt!!/, @output
|
48
48
|
assert_match /Closing!!/, @output
|
49
49
|
end
|
50
50
|
end # daemonize
|
@@ -64,7 +64,7 @@ describe "dante runner" do
|
|
64
64
|
sleep(1) # Wait to complete
|
65
65
|
@output = File.read(@process.tmp_path)
|
66
66
|
assert_match /Started on 8080!!/, @output
|
67
|
-
assert_match /
|
67
|
+
assert_match /Interrupt!!/, @output
|
68
68
|
assert_match /Closing!!/, @output
|
69
69
|
end
|
70
70
|
end # execute with block
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dante
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.4
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nathan Esquenazi
|
@@ -15,10 +15,13 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-12-
|
18
|
+
date: 2011-12-06 00:00:00 -08:00
|
19
|
+
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
|
-
|
22
|
+
name: rake
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
22
25
|
none: false
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
@@ -27,12 +30,12 @@ dependencies:
|
|
27
30
|
segments:
|
28
31
|
- 0
|
29
32
|
version: "0"
|
30
|
-
requirement: *id001
|
31
33
|
type: :development
|
32
|
-
|
33
|
-
name: rake
|
34
|
+
version_requirements: *id001
|
34
35
|
- !ruby/object:Gem::Dependency
|
35
|
-
|
36
|
+
name: minitest
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
39
|
none: false
|
37
40
|
requirements:
|
38
41
|
- - ">="
|
@@ -41,12 +44,12 @@ dependencies:
|
|
41
44
|
segments:
|
42
45
|
- 0
|
43
46
|
version: "0"
|
44
|
-
requirement: *id002
|
45
47
|
type: :development
|
46
|
-
|
47
|
-
name: minitest
|
48
|
+
version_requirements: *id002
|
48
49
|
- !ruby/object:Gem::Dependency
|
49
|
-
|
50
|
+
name: mocha
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
53
|
none: false
|
51
54
|
requirements:
|
52
55
|
- - ">="
|
@@ -55,10 +58,8 @@ dependencies:
|
|
55
58
|
segments:
|
56
59
|
- 0
|
57
60
|
version: "0"
|
58
|
-
requirement: *id003
|
59
61
|
type: :development
|
60
|
-
|
61
|
-
name: mocha
|
62
|
+
version_requirements: *id003
|
62
63
|
description: Turn any process into a demon.
|
63
64
|
email:
|
64
65
|
- nesquena@gmail.com
|
@@ -81,6 +82,7 @@ files:
|
|
81
82
|
- test/dante_test.rb
|
82
83
|
- test/runner_test.rb
|
83
84
|
- test/test_helper.rb
|
85
|
+
has_rdoc: true
|
84
86
|
homepage: https://github.com/bazaarlabs/dante
|
85
87
|
licenses: []
|
86
88
|
|
@@ -110,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
112
|
requirements: []
|
111
113
|
|
112
114
|
rubyforge_project: dante
|
113
|
-
rubygems_version: 1.
|
115
|
+
rubygems_version: 1.6.2
|
114
116
|
signing_key:
|
115
117
|
specification_version: 3
|
116
118
|
summary: Turn any process into a demon
|