dante 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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