dante 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +71 -11
- data/dante.gemspec +1 -0
- data/lib/dante.rb +2 -2
- data/lib/dante/runner.rb +25 -14
- data/lib/dante/version.rb +1 -1
- data/test/runner_test.rb +68 -6
- data/test/test_helper.rb +6 -4
- metadata +18 -4
data/README.md
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# Dante
|
2
2
|
|
3
|
-
Turn any
|
3
|
+
Turn any ruby into a daemon.
|
4
4
|
|
5
|
-
##
|
5
|
+
## Description
|
6
6
|
|
7
|
-
Dante is the simplest possible thing that
|
8
|
-
can be started
|
9
|
-
to be stopped just as easily using a standardized set of command line options.
|
7
|
+
Dante is the simplest possible thing that will work to turn arbitrary ruby code into an executable that
|
8
|
+
can be started via command line or start/stop a daemon, and will store a pid file for you.
|
10
9
|
|
11
10
|
If you need to create a ruby executable and you want standard daemon start/stop with pid files
|
12
11
|
and no hassle, this gem will be a great way to get started.
|
@@ -15,23 +14,34 @@ and no hassle, this gem will be a great way to get started.
|
|
15
14
|
|
16
15
|
Add to your Gemfile:
|
17
16
|
|
18
|
-
```
|
17
|
+
```ruby
|
19
18
|
# Gemfile
|
20
19
|
|
21
20
|
gem "dante"
|
22
21
|
```
|
23
22
|
|
23
|
+
or to your gemspec:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# mygem.gemspec
|
27
|
+
|
28
|
+
Gem::Specification.new do |s|
|
29
|
+
s.add_dependency "dante"
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
24
33
|
## Usage
|
25
34
|
|
26
|
-
Dante is meant to be used from any "bin" executable. For instance, to create a binary for a web server, create a file in `bin/
|
35
|
+
Dante is meant to be used from any "bin" executable. For instance, to create a binary for a web server, create a file in `bin/myapp`:
|
27
36
|
|
28
37
|
```ruby
|
29
38
|
#!/usr/bin/env ruby
|
30
39
|
|
31
40
|
require File.expand_path("../../myapp.rb", __FILE__)
|
32
41
|
|
33
|
-
Dante.run('myapp') do
|
34
|
-
|
42
|
+
Dante.run('myapp') do |opts|
|
43
|
+
# opts: host, pid_path, port, daemonize, user, group
|
44
|
+
Thin::Server.start('0.0.0.0', opts[:port]) do
|
35
45
|
use Rack::CommonLogger
|
36
46
|
use Rack::ShowExceptions
|
37
47
|
run MyApp
|
@@ -39,6 +49,12 @@ Dante.run('myapp') do
|
|
39
49
|
end
|
40
50
|
```
|
41
51
|
|
52
|
+
Be sure to properly make your bin executable:
|
53
|
+
|
54
|
+
```
|
55
|
+
chmod +x bin/myapp
|
56
|
+
```
|
57
|
+
|
42
58
|
This gives your binary several useful things for free:
|
43
59
|
|
44
60
|
```
|
@@ -48,7 +64,7 @@ This gives your binary several useful things for free:
|
|
48
64
|
will start the app undaemonized in the terminal, handling trapping and stopping the process.
|
49
65
|
|
50
66
|
```
|
51
|
-
./bin/myapp -d -P /var/run/myapp.pid
|
67
|
+
./bin/myapp -p 8080 -d -P /var/run/myapp.pid
|
52
68
|
```
|
53
69
|
|
54
70
|
will daemonize and start the process, storing the pid in the specified pid file.
|
@@ -65,6 +81,46 @@ will stop all daemonized processes for the specified pid file.
|
|
65
81
|
|
66
82
|
Will return a useful help banner message explaining the simple usage.
|
67
83
|
|
84
|
+
## Customization
|
85
|
+
|
86
|
+
In many cases, you will need to add custom flags/options or a custom description to your executable. You can do this
|
87
|
+
easily by using the `Dante::Runner` directly:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
#!/usr/bin/env ruby
|
91
|
+
|
92
|
+
require File.expand_path("../../myapp.rb", __FILE__)
|
93
|
+
|
94
|
+
# Set default port to 8080
|
95
|
+
runner = Dante::Runner.new('myapp', :port => 8080)
|
96
|
+
# Sets the description in 'help'
|
97
|
+
runner.description = "This is myapp"
|
98
|
+
runner.with_options do |opts|
|
99
|
+
opts.on("-t", "--test TEST", String, "Test this thing") do |test|
|
100
|
+
options[:test] = test
|
101
|
+
end
|
102
|
+
end
|
103
|
+
# Parse command-line options and execute the process
|
104
|
+
runner.execute do |opts|
|
105
|
+
# opts: host, pid_path, port, daemonize, user, group
|
106
|
+
Thin::Server.start('0.0.0.0', opts[:port]) do
|
107
|
+
puts opts[:test] # Referencing my custom option
|
108
|
+
use Rack::CommonLogger
|
109
|
+
use Rack::ShowExceptions
|
110
|
+
run MyApp
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
Now you would be able to do:
|
116
|
+
|
117
|
+
```
|
118
|
+
./bin/myapp -t custom
|
119
|
+
```
|
120
|
+
|
121
|
+
and the `opts` would contain the `:test` option for use in your script. In addition, help will now contain
|
122
|
+
your customized description in the banner.
|
123
|
+
|
68
124
|
## God
|
69
125
|
|
70
126
|
Dante can be used well in conjunction with the excellent God process manager. Simply, use Dante to daemonize a process
|
@@ -93,4 +149,8 @@ God.watch do |w|
|
|
93
149
|
end
|
94
150
|
```
|
95
151
|
|
96
|
-
and that's all. Of course now you can also easily daemonize as well as start/stop the process on the command line as well.
|
152
|
+
and that's all. Of course now you can also easily daemonize as well as start/stop the process on the command line as well.
|
153
|
+
|
154
|
+
## Copyright
|
155
|
+
|
156
|
+
Copyright © 2011 Nathan Esquenazi. See [LICENSE](https://github.com/bazaarlabs/dante/blob/master/LICENSE) for details.
|
data/dante.gemspec
CHANGED
data/lib/dante.rb
CHANGED
@@ -16,12 +16,12 @@ require "dante/runner"
|
|
16
16
|
|
17
17
|
module Dante
|
18
18
|
|
19
|
-
# Forks a process and
|
19
|
+
# Forks a process and handles option parsing and start/stopping.
|
20
20
|
#
|
21
21
|
# @example
|
22
22
|
# Dante.run("process-name") { Server.run! }
|
23
23
|
#
|
24
24
|
def self.run(name, options={}, &blk)
|
25
|
-
Runner.new(name, options, &blk).execute
|
25
|
+
Runner.new(name, options, &blk).execute
|
26
26
|
end
|
27
27
|
end
|
data/lib/dante/runner.rb
CHANGED
@@ -7,7 +7,7 @@ require 'erb'
|
|
7
7
|
|
8
8
|
This is a utility for setting up a binary executable for a service.
|
9
9
|
|
10
|
-
# Dante::Runner.
|
10
|
+
# Dante::Runner.new("buffet", :pid_path => "/var/run/buffet.pid") do
|
11
11
|
# ...startup service here...
|
12
12
|
# end
|
13
13
|
|
@@ -18,7 +18,7 @@ module Dante
|
|
18
18
|
# Signal to application that the process is shutting down
|
19
19
|
class Abort < Exception; end
|
20
20
|
|
21
|
-
attr_accessor :options
|
21
|
+
attr_accessor :options, :name, :description
|
22
22
|
|
23
23
|
class << self
|
24
24
|
def run(*args, &block)
|
@@ -29,23 +29,30 @@ module Dante
|
|
29
29
|
def initialize(name, defaults={}, &block)
|
30
30
|
@name = name
|
31
31
|
@startup_command = block
|
32
|
-
|
32
|
+
@options = {
|
33
33
|
:host => '0.0.0.0',
|
34
34
|
:pid_path => "/var/run/#{@name}.pid"
|
35
35
|
}.merge(defaults)
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
+
# Accepts options for the process
|
39
|
+
# @runner.with_options { |opts| opts.on(...) }
|
40
|
+
def with_options(&block)
|
41
|
+
@with_options = block
|
42
|
+
end
|
38
43
|
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
# Executes the runner based on options
|
45
|
+
# @runner.execute
|
46
|
+
# @runner.execute { ... }
|
47
|
+
def execute(&block)
|
48
|
+
parse_options
|
49
|
+
kill_pid(options[:kill] || '*') if options.include?(:kill)
|
42
50
|
|
43
51
|
Process.euid = options[:user] if options[:user]
|
44
52
|
Process.egid = options[:group] if options[:group]
|
45
|
-
end
|
46
53
|
|
47
|
-
|
48
|
-
|
54
|
+
@startup_command = block if block_given?
|
55
|
+
|
49
56
|
if !options[:daemonize]
|
50
57
|
start
|
51
58
|
else
|
@@ -65,7 +72,7 @@ module Dante
|
|
65
72
|
exit
|
66
73
|
}
|
67
74
|
|
68
|
-
@startup_command.call
|
75
|
+
@startup_command.call(self.options) if @startup_command
|
69
76
|
end
|
70
77
|
|
71
78
|
def stop
|
@@ -74,11 +81,12 @@ module Dante
|
|
74
81
|
end
|
75
82
|
|
76
83
|
def parse_options
|
84
|
+
headline = [@name, @description].compact.join(" - ")
|
77
85
|
OptionParser.new do |opts|
|
78
86
|
opts.summary_width = 25
|
79
|
-
opts.banner = ["
|
80
|
-
"Usage: #{@name} [-P file] [-d] [-k
|
81
|
-
" #{@name} --help\n"].join("")
|
87
|
+
opts.banner = [headline, "\n\n",
|
88
|
+
"Usage: #{@name} [-p port] [-P file] [-d] [-k]\n",
|
89
|
+
" #{@name} --help\n"].compact.join("")
|
82
90
|
opts.separator ""
|
83
91
|
|
84
92
|
opts.on("-p", "--port PORT", Integer, "Specify port", "(default: #{options[:port]})") do |v|
|
@@ -109,6 +117,9 @@ module Dante
|
|
109
117
|
puts "#{opts}\n"
|
110
118
|
exit
|
111
119
|
end
|
120
|
+
|
121
|
+
# Load options specified through 'with_options'
|
122
|
+
instance_exec(opts, &@with_options) if @with_options
|
112
123
|
end.parse!
|
113
124
|
options
|
114
125
|
end
|
data/lib/dante/version.rb
CHANGED
data/test/runner_test.rb
CHANGED
@@ -5,7 +5,7 @@ describe "dante runner" do
|
|
5
5
|
before do
|
6
6
|
@process = TestingProcess.new('a')
|
7
7
|
@runner = Dante::Runner.new('test-process') { @process.run_a! }
|
8
|
-
@stdout = capture_stdout { @runner.execute
|
8
|
+
@stdout = capture_stdout { @runner.execute }
|
9
9
|
end
|
10
10
|
|
11
11
|
it "prints correct stdout" do
|
@@ -21,9 +21,10 @@ describe "dante runner" do
|
|
21
21
|
describe "with daemonize flag" do
|
22
22
|
before do
|
23
23
|
@process = TestingProcess.new('b')
|
24
|
-
@run_options = { :daemonize => true, :pid_path => "/tmp/dante.pid" }
|
25
|
-
@runner = Dante::Runner.new('test-process-2', @run_options) {
|
26
|
-
|
24
|
+
@run_options = { :daemonize => true, :pid_path => "/tmp/dante.pid", :port => 8080 }
|
25
|
+
@runner = Dante::Runner.new('test-process-2', @run_options) { |opts|
|
26
|
+
@process.run_b!(opts[:port]) }
|
27
|
+
@stdout = capture_stdout { @runner.execute }
|
27
28
|
sleep(1)
|
28
29
|
end
|
29
30
|
|
@@ -32,7 +33,7 @@ describe "dante runner" do
|
|
32
33
|
Process.kill "INT", @pid
|
33
34
|
sleep(1) # Wait to complete
|
34
35
|
@output = File.read(@process.tmp_path)
|
35
|
-
assert_match /Started!!/, @output
|
36
|
+
assert_match /Started on 8080!!/, @output
|
36
37
|
assert_match /Abort!!/, @output
|
37
38
|
assert_match /Closing!!/, @output
|
38
39
|
end
|
@@ -42,9 +43,70 @@ describe "dante runner" do
|
|
42
43
|
Process.kill "TERM", @pid
|
43
44
|
sleep(1) # Wait to complete
|
44
45
|
@output = File.read(@process.tmp_path)
|
45
|
-
assert_match /Started!!/, @output
|
46
|
+
assert_match /Started on 8080!!/, @output
|
46
47
|
assert_match /Abort!!/, @output
|
47
48
|
assert_match /Closing!!/, @output
|
48
49
|
end
|
49
50
|
end # daemonize
|
51
|
+
|
52
|
+
describe "with execute accepting block" do
|
53
|
+
before do
|
54
|
+
@process = TestingProcess.new('b')
|
55
|
+
@run_options = { :daemonize => true, :pid_path => "/tmp/dante.pid", :port => 8080 }
|
56
|
+
@runner = Dante::Runner.new('test-process-2', @run_options)
|
57
|
+
@stdout = capture_stdout { @runner.execute { |opts| @process.run_b!(opts[:port]) } }
|
58
|
+
sleep(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "can properly handles aborts and starts / stops on INT" do
|
62
|
+
refute_equal 0, @pid = `cat /tmp/dante.pid`.to_i
|
63
|
+
Process.kill "INT", @pid
|
64
|
+
sleep(1) # Wait to complete
|
65
|
+
@output = File.read(@process.tmp_path)
|
66
|
+
assert_match /Started on 8080!!/, @output
|
67
|
+
assert_match /Abort!!/, @output
|
68
|
+
assert_match /Closing!!/, @output
|
69
|
+
end
|
70
|
+
end # execute with block
|
71
|
+
|
72
|
+
describe "with parsing options" do
|
73
|
+
before do
|
74
|
+
Object.send(:remove_const, 'ARGV'); ARGV = ['-t test_text']
|
75
|
+
@process = TestingProcess.new('a')
|
76
|
+
@runner = Dante::Runner.new('test-process')
|
77
|
+
@runner.with_options do |opts|
|
78
|
+
opts.on("-t", "--test TEST", String, "Test this thing") { |test| options[:test] = test }
|
79
|
+
end
|
80
|
+
@stdout = capture_stdout { @runner.execute { |opts| @process.run_a!(opts[:test]) } }
|
81
|
+
end
|
82
|
+
|
83
|
+
it "prints correct stdout" do
|
84
|
+
assert_match /Starting test-process/, @stdout
|
85
|
+
end
|
86
|
+
|
87
|
+
it "prints correct data" do
|
88
|
+
@output = File.read(@process.tmp_path)
|
89
|
+
assert_match /test_text/, @output
|
90
|
+
end
|
91
|
+
|
92
|
+
it "starts successfully when executed" do
|
93
|
+
@output = File.read(@process.tmp_path)
|
94
|
+
assert_match /Started/, @output
|
95
|
+
end
|
96
|
+
end # options parsing
|
97
|
+
|
98
|
+
describe "with help command" do
|
99
|
+
before do
|
100
|
+
Object.send(:remove_const, 'ARGV'); ARGV = ['--help']
|
101
|
+
@process = TestingProcess.new('a')
|
102
|
+
@runner = Dante::Runner.new('test-process')
|
103
|
+
@runner.description = "Test process banana"
|
104
|
+
@runner.expects(:exit).once.returns(true)
|
105
|
+
@stdout = capture_stdout { @runner.execute { @process.run_a! } }
|
106
|
+
end
|
107
|
+
|
108
|
+
it "prints correct stdout" do
|
109
|
+
assert_match /test-process - Test process banana/, @stdout
|
110
|
+
end
|
111
|
+
end # help
|
50
112
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'tempfile'
|
3
3
|
require 'minitest/autorun'
|
4
|
+
require 'mocha'
|
4
5
|
$:.unshift File.expand_path("../../lib")
|
5
6
|
require 'dante'
|
6
7
|
|
@@ -32,16 +33,17 @@ class TestingProcess
|
|
32
33
|
@tmp_path = "/tmp/dante-#{name}.log"
|
33
34
|
end # initialize
|
34
35
|
|
35
|
-
def run_a!
|
36
|
+
def run_a!(data=nil)
|
36
37
|
@tmp = File.new(@tmp_path, 'w')
|
37
|
-
@tmp.
|
38
|
+
@tmp.puts("Started")
|
39
|
+
@tmp.puts "Data is: #{data}" if data
|
38
40
|
@tmp.close
|
39
41
|
end # run_a!
|
40
42
|
|
41
|
-
def run_b!
|
43
|
+
def run_b!(port=9090)
|
42
44
|
begin
|
43
45
|
@tmp = File.new(@tmp_path, 'w')
|
44
|
-
@tmp.print "Started!!"
|
46
|
+
@tmp.print "Started on #{port}!!"
|
45
47
|
sleep(100)
|
46
48
|
rescue Dante::Runner::Abort
|
47
49
|
@tmp.print "Abort!!"
|
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
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nathan Esquenazi
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-28 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -46,6 +46,20 @@ dependencies:
|
|
46
46
|
version: "0"
|
47
47
|
type: :development
|
48
48
|
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: mocha
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
49
63
|
description: Turn any process into a demon.
|
50
64
|
email:
|
51
65
|
- nesquena@gmail.com
|