dante 0.0.1

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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dante.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Miso
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,96 @@
1
+ # Dante
2
+
3
+ Turn any process into a daemon with ease.
4
+
5
+ ## Why Dante?
6
+
7
+ Dante is the simplest possible thing that can work to turn arbitrary ruby code into a 'robust' binary that
8
+ can be started normally or as a daemon, and will store a pid file automatically. Dante also allows a process
9
+ to be stopped just as easily using a standardized set of command line options.
10
+
11
+ If you need to create a ruby executable and you want standard daemon start/stop with pid files
12
+ and no hassle, this gem will be a great way to get started.
13
+
14
+ ## Installation
15
+
16
+ Add to your Gemfile:
17
+
18
+ ```
19
+ # Gemfile
20
+
21
+ gem "dante"
22
+ ```
23
+
24
+ ## Usage
25
+
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/mysite`:
27
+
28
+ ```ruby
29
+ #!/usr/bin/env ruby
30
+
31
+ require File.expand_path("../../myapp.rb", __FILE__)
32
+
33
+ Dante.run('myapp') do
34
+ Thin::Server.start('0.0.0.0', port) do
35
+ use Rack::CommonLogger
36
+ use Rack::ShowExceptions
37
+ run MyApp
38
+ end
39
+ end
40
+ ```
41
+
42
+ This gives your binary several useful things for free:
43
+
44
+ ```
45
+ ./bin/myapp
46
+ ```
47
+
48
+ will start the app undaemonized in the terminal, handling trapping and stopping the process.
49
+
50
+ ```
51
+ ./bin/myapp -d -P /var/run/myapp.pid
52
+ ```
53
+
54
+ will daemonize and start the process, storing the pid in the specified pid file.
55
+
56
+ ```
57
+ ./bin/myapp -k -P /var/run/myapp.pid
58
+ ```
59
+
60
+ will stop all daemonized processes for the specified pid file.
61
+
62
+ ```
63
+ ./bin/myapp --help
64
+ ```
65
+
66
+ Will return a useful help banner message explaining the simple usage.
67
+
68
+ ## God
69
+
70
+ Dante can be used well in conjunction with the excellent God process manager. Simply, use Dante to daemonize a process
71
+ and then you can easily use God to monitor:
72
+
73
+ ```ruby
74
+ # /etc/god/myapp.rb
75
+
76
+ God.watch do |w|
77
+ w.name = "myapp"
78
+ w.interval = 30.seconds
79
+ w.start = "ruby /path/to/myapp/bin/myapp -d"
80
+ w.stop = "ruby /path/to/myapp/bin/myapp -k"
81
+ w.start_grace = 15.seconds
82
+ w.restart_grace = 15.seconds
83
+ w.pid_file = "/var/run/myapp.pid"
84
+
85
+ w.behavior(:clean_pid_file)
86
+
87
+ w.start_if do |start|
88
+ start.condition(:process_running) do |c|
89
+ c.interval = 5.seconds
90
+ c.running = false
91
+ end
92
+ end
93
+ end
94
+ ```
95
+
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.
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ # require 'yard'
4
+
5
+ task :test do
6
+ Rake::TestTask.new do |t|
7
+ t.libs.push "lib"
8
+ t.test_files = FileList[File.expand_path('../test/**/*_test.rb', __FILE__)]
9
+ t.verbose = true
10
+ end
11
+ end
12
+
13
+ # task :doc do
14
+ # YARD::CLI::Yardoc.new.run
15
+ # end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dante/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dante"
7
+ s.version = Dante::VERSION
8
+ s.authors = ["Nathan Esquenazi"]
9
+ s.email = ["nesquena@gmail.com"]
10
+ s.homepage = "https://github.com/bazaarlabs/dante"
11
+ s.summary = %q{Turn any process into a demon}
12
+ s.description = %q{Turn any process into a demon.}
13
+
14
+ s.rubyforge_project = "dante"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'minitest'
23
+ end
@@ -0,0 +1,27 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+ require "dante/version"
3
+ require "dante/runner"
4
+
5
+ =begin
6
+
7
+ Dante.run("process-name") do
8
+ begin
9
+ # ...something here
10
+ rescue Abort
11
+ # ...shutdown here
12
+ end
13
+ end
14
+
15
+ =end
16
+
17
+ module Dante
18
+
19
+ # Forks a process and takes some list of params. I don't really know what this does.
20
+ #
21
+ # @example
22
+ # Dante.run("process-name") { Server.run! }
23
+ #
24
+ def self.run(name, options={}, &blk)
25
+ Runner.new(name, options, &blk).execute!
26
+ end
27
+ end
@@ -0,0 +1,153 @@
1
+ require 'fileutils'
2
+ require 'optparse'
3
+ require 'yaml'
4
+ require 'erb'
5
+
6
+ =begin
7
+
8
+ This is a utility for setting up a binary executable for a service.
9
+
10
+ # Dante::Runner.run("buffet", :pid_path => "/var/run/buffet.pid") do
11
+ # ...startup service here...
12
+ # end
13
+
14
+ =end
15
+
16
+ module Dante
17
+ class Runner
18
+ # Signal to application that the process is shutting down
19
+ class Abort < Exception; end
20
+
21
+ attr_accessor :options
22
+
23
+ class << self
24
+ def run(*args, &block)
25
+ self.new(*args, &block)
26
+ end
27
+ end
28
+
29
+ def initialize(name, defaults={}, &block)
30
+ @name = name
31
+ @startup_command = block
32
+ self.options = {
33
+ :host => '0.0.0.0',
34
+ :pid_path => "/var/run/#{@name}.pid"
35
+ }.merge(defaults)
36
+
37
+ parse_options
38
+
39
+ if options.include?(:kill)
40
+ kill_pid(options[:kill] || '*')
41
+ end
42
+
43
+ Process.euid = options[:user] if options[:user]
44
+ Process.egid = options[:group] if options[:group]
45
+ end
46
+
47
+ # Executes the runner based on options
48
+ def execute!
49
+ if !options[:daemonize]
50
+ start
51
+ else
52
+ daemonize
53
+ end
54
+ end
55
+
56
+ def start
57
+ puts "Starting #{@name} service..."
58
+
59
+ trap("INT") {
60
+ stop
61
+ exit
62
+ }
63
+ trap("TERM"){
64
+ stop
65
+ exit
66
+ }
67
+
68
+ @startup_command.call
69
+ end
70
+
71
+ def stop
72
+ raise Abort
73
+ sleep(1)
74
+ end
75
+
76
+ def parse_options
77
+ OptionParser.new do |opts|
78
+ opts.summary_width = 25
79
+ opts.banner = ["#{@name} (#{VERSION})\n\n",
80
+ "Usage: #{@name} [-P file] [-d] [-k port]\n",
81
+ " #{@name} --help\n"].join("")
82
+ opts.separator ""
83
+
84
+ opts.on("-p", "--port PORT", Integer, "Specify port", "(default: #{options[:port]})") do |v|
85
+ options[:port] = v
86
+ end
87
+
88
+ opts.on("-P", "--pid FILE", String, "save PID in FILE when using -d option.", "(default: #{options[:pid_path]})") do |v|
89
+ options[:pid_path] = File.expand_path(v)
90
+ end
91
+
92
+ opts.on("-d", "--daemon", "Daemonize mode") do |v|
93
+ options[:daemonize] = v
94
+ end
95
+
96
+ opts.on("-k", "--kill [PORT]", String, "Kill specified running daemons - leave blank to kill all.") do |v|
97
+ options[:kill] = v
98
+ end
99
+
100
+ opts.on("-u", "--user USER", String, "User to run as") do |user|
101
+ options[:user] = user
102
+ end
103
+
104
+ opts.on("-G", "--group GROUP", String, "Group to run as") do |group|
105
+ options[:group] = group
106
+ end
107
+
108
+ opts.on_tail("-?", "--help", "Display this usage information.") do
109
+ puts "#{opts}\n"
110
+ exit
111
+ end
112
+ end.parse!
113
+ options
114
+ end
115
+
116
+ private
117
+
118
+ def store_pid(pid)
119
+ FileUtils.mkdir_p(File.dirname(options[:pid_path]))
120
+ File.open(options[:pid_path], 'w'){|f| f.write("#{pid}\n")}
121
+ end
122
+
123
+ def kill_pid(k)
124
+ Dir[options[:pid_path]].each do |f|
125
+ begin
126
+ puts f
127
+ pid = IO.read(f).chomp.to_i
128
+ FileUtils.rm f
129
+ Process.kill(9, pid)
130
+ puts "killed PID: #{pid}"
131
+ rescue => e
132
+ puts "Failed to kill! #{k}: #{e}"
133
+ end
134
+ end
135
+ exit
136
+ end
137
+
138
+ def daemonize
139
+ pid = fork do
140
+ exit if fork
141
+ Process.setsid
142
+ exit if fork
143
+ store_pid(Process.pid)
144
+ File.umask 0000
145
+ STDIN.reopen "/dev/null"
146
+ STDOUT.reopen "/dev/null", "a"
147
+ STDERR.reopen STDOUT
148
+ start
149
+ end
150
+ end
151
+
152
+ end
153
+ end
@@ -0,0 +1,3 @@
1
+ module Dante
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe "dante module" do
4
+ before do
5
+ @process = TestingProcess.new('a')
6
+ end
7
+
8
+ it "can run jobs using #run method" do
9
+ capture_stdout do
10
+ Dante.run('test-process') { @process.run_a! }
11
+ end
12
+ @output = File.read(@process.tmp_path)
13
+ assert_match /Started/, @output
14
+ end
15
+
16
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe "dante runner" do
4
+ describe "with no daemonize" do
5
+ before do
6
+ @process = TestingProcess.new('a')
7
+ @runner = Dante::Runner.new('test-process') { @process.run_a! }
8
+ @stdout = capture_stdout { @runner.execute! }
9
+ end
10
+
11
+ it "prints correct stdout" do
12
+ assert_match /Starting test-process/, @stdout
13
+ end
14
+
15
+ it "starts successfully when executed" do
16
+ @output = File.read(@process.tmp_path)
17
+ assert_match /Started/, @output
18
+ end
19
+ end # no daemonize
20
+
21
+ describe "with daemonize flag" do
22
+ before do
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) { @process.run_b! }
26
+ @stdout = capture_stdout { @runner.execute! }
27
+ sleep(1)
28
+ end
29
+
30
+ it "can properly handles aborts and starts / stops on INT" do
31
+ refute_equal 0, @pid = `cat /tmp/dante.pid`.to_i
32
+ Process.kill "INT", @pid
33
+ sleep(1) # Wait to complete
34
+ @output = File.read(@process.tmp_path)
35
+ assert_match /Started!!/, @output
36
+ assert_match /Abort!!/, @output
37
+ assert_match /Closing!!/, @output
38
+ end
39
+
40
+ it "can properly handles aborts and starts / stops on TERM" do
41
+ refute_equal 0, @pid = `cat /tmp/dante.pid`.to_i
42
+ Process.kill "TERM", @pid
43
+ sleep(1) # Wait to complete
44
+ @output = File.read(@process.tmp_path)
45
+ assert_match /Started!!/, @output
46
+ assert_match /Abort!!/, @output
47
+ assert_match /Closing!!/, @output
48
+ end
49
+ end # daemonize
50
+ end
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'tempfile'
3
+ require 'minitest/autorun'
4
+ $:.unshift File.expand_path("../../lib")
5
+ require 'dante'
6
+
7
+ ## Kernel Extensions
8
+ require 'stringio'
9
+
10
+ module Kernel
11
+ # Redirect standard out, standard error and the buffered logger for sprinkle to StringIO
12
+ # capture_stdout { any_commands; you_want } => "all output from the commands"
13
+ def capture_stdout
14
+ return yield if ENV['DEBUG'] # Skip if debug mode
15
+
16
+ out = StringIO.new
17
+ $stdout = out
18
+ $stderr = out
19
+ yield
20
+ return out.string
21
+ ensure
22
+ $stdout = STDOUT
23
+ $stderr = STDERR
24
+ end
25
+ end
26
+
27
+ # Process fixture
28
+ class TestingProcess
29
+ attr_reader :tmp_path
30
+
31
+ def initialize(name)
32
+ @tmp_path = "/tmp/dante-#{name}.log"
33
+ end # initialize
34
+
35
+ def run_a!
36
+ @tmp = File.new(@tmp_path, 'w')
37
+ @tmp.print("Started")
38
+ @tmp.close
39
+ end # run_a!
40
+
41
+ def run_b!
42
+ begin
43
+ @tmp = File.new(@tmp_path, 'w')
44
+ @tmp.print "Started!!"
45
+ sleep(100)
46
+ rescue Dante::Runner::Abort
47
+ @tmp.print "Abort!!"
48
+ exit
49
+ ensure
50
+ @tmp.print "Closing!!"
51
+ @tmp.close
52
+ end
53
+ end # run_b!
54
+ end # TestingProcess
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dante
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Nathan Esquenazi
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-11-21 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rake
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: minitest
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
49
+ description: Turn any process into a demon.
50
+ email:
51
+ - nesquena@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - Gemfile
61
+ - LICENSE
62
+ - README.md
63
+ - Rakefile
64
+ - dante.gemspec
65
+ - lib/dante.rb
66
+ - lib/dante/runner.rb
67
+ - lib/dante/version.rb
68
+ - test/dante_test.rb
69
+ - test/runner_test.rb
70
+ - test/test_helper.rb
71
+ has_rdoc: true
72
+ homepage: https://github.com/bazaarlabs/dante
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project: dante
101
+ rubygems_version: 1.6.2
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Turn any process into a demon
105
+ test_files:
106
+ - test/dante_test.rb
107
+ - test/runner_test.rb
108
+ - test/test_helper.rb