kennethkalmer-daemon-kit 0.1.6 → 0.1.7.3
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/Configuration.txt +58 -0
- data/History.txt +16 -0
- data/Manifest.txt +29 -2
- data/PostInstall.txt +1 -1
- data/{README.textile → README.rdoc} +31 -19
- data/Rakefile +2 -4
- data/TODO.txt +6 -5
- data/app_generators/daemon_kit/daemon_kit_generator.rb +29 -0
- data/app_generators/daemon_kit/templates/Rakefile +3 -1
- data/app_generators/daemon_kit/templates/config/arguments.rb +12 -0
- data/app_generators/daemon_kit/templates/config/boot.rb +2 -2
- data/app_generators/daemon_kit/templates/script/console +3 -0
- data/app_generators/daemon_kit/templates/script/destroy +14 -0
- data/app_generators/daemon_kit/templates/script/generate +14 -0
- data/daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb +35 -0
- data/daemon_generators/deploy_capistrano/templates/Capfile +10 -0
- data/daemon_generators/deploy_capistrano/templates/USAGE +10 -0
- data/daemon_generators/deploy_capistrano/templates/config/deploy/production.rb +6 -0
- data/daemon_generators/deploy_capistrano/templates/config/deploy/staging.rb +6 -0
- data/daemon_generators/deploy_capistrano/templates/config/deploy.rb +51 -0
- data/daemon_generators/deploy_capistrano/templates/config/environments/staging.rb +0 -0
- data/lib/daemon_kit/application.rb +135 -11
- data/lib/daemon_kit/arguments.rb +151 -0
- data/lib/daemon_kit/commands/console.rb +38 -0
- data/lib/daemon_kit/config.rb +1 -0
- data/lib/daemon_kit/console_daemon.rb +2 -0
- data/lib/daemon_kit/core_ext/string.rb +22 -0
- data/lib/daemon_kit/core_ext.rb +1 -0
- data/lib/daemon_kit/deployment/capistrano.rb +485 -0
- data/lib/daemon_kit/initializer.rb +87 -25
- data/lib/daemon_kit/pid_file.rb +61 -0
- data/lib/daemon_kit/tasks/environment.rake +5 -4
- data/lib/daemon_kit/tasks/framework.rake +15 -1
- data/lib/daemon_kit/tasks/god.rake +62 -0
- data/lib/daemon_kit/tasks/monit.rake +29 -0
- data/lib/daemon_kit.rb +11 -5
- data/rubygems_generators/install_rspec/templates/spec/spec_helper.rb +1 -1
- data/spec/argument_spec.rb +51 -0
- data/spec/config_spec.rb +77 -0
- data/spec/daemon_kit_spec.rb +2 -2
- data/spec/fixtures/env.yml +15 -0
- data/spec/fixtures/noenv.yml +4 -0
- data/spec/initializer_spec.rb +4 -3
- data/spec/spec_helper.rb +8 -11
- data/templates/god/god.erb +69 -0
- data/templates/monit/monit.erb +14 -0
- data/test/test_daemon-kit_generator.rb +15 -1
- data/test/test_deploy_capistrano_generator.rb +48 -0
- metadata +41 -21
- data/lib/daemon_kit/patches/force_kill_wait.rb +0 -120
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:daemon, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:daemon, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class DeployCapistranoGenerator < RubiGen::Base
|
2
|
+
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(runtime_args, runtime_options = {})
|
6
|
+
super
|
7
|
+
usage if args.empty?
|
8
|
+
@name = args.shift
|
9
|
+
end
|
10
|
+
|
11
|
+
def manifest
|
12
|
+
record do |m|
|
13
|
+
|
14
|
+
m.file "Capfile", "Capfile"
|
15
|
+
m.directory "config"
|
16
|
+
m.template "config/deploy.rb", "config/deploy.rb"
|
17
|
+
m.directory "config/deploy"
|
18
|
+
m.template "config/deploy/staging.rb", "config/deploy/staging.rb"
|
19
|
+
m.template "config/deploy/production.rb", "config/deploy/production.rb"
|
20
|
+
m.directory "config/environments"
|
21
|
+
m.file "config/environments/staging.rb", "config/environments/staging.rb", :collision => :skip
|
22
|
+
m.readme "USAGE"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
def banner
|
28
|
+
<<-EOS
|
29
|
+
Creates the required capistrano configurations for deploying your daemon code
|
30
|
+
to remote servers.
|
31
|
+
|
32
|
+
USAGE: #{$0} #{spec.name} daemon-name
|
33
|
+
EOS
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
unless respond_to?(:namespace) # cap2 differentiator
|
2
|
+
$stderr.puts "Requires capistrano version 2"
|
3
|
+
exit 1
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'config/boot'
|
7
|
+
load DaemonKit.framework_root + '/lib/daemon_kit/deployment/capistrano.rb'
|
8
|
+
|
9
|
+
Dir['config/deploy/recipes/*.rb'].each { |plugin| load(plugin) }
|
10
|
+
load 'config/deploy.rb'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
Capistrano deployment configuration generator completed
|
3
|
+
|
4
|
+
Usage:
|
5
|
+
Review the configuration files in config/deploy.rb and
|
6
|
+
config/deploy/*.rb
|
7
|
+
|
8
|
+
Once you're happy with the configurations, you can review
|
9
|
+
a list of available capistrano commands by running `cap -T'
|
10
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Modified capistrano recipe, based on the standard 'deploy' recipe
|
2
|
+
# provided by capistrano but without the Rails-specific dependencies
|
3
|
+
|
4
|
+
set :stages, %w(staging production)
|
5
|
+
set :default_stage, "staging"
|
6
|
+
require "capistrano/ext/multistage"
|
7
|
+
|
8
|
+
# Set some globals
|
9
|
+
default_run_options[:pty] = true
|
10
|
+
set :application, "<%= name %>"
|
11
|
+
|
12
|
+
# Deployment
|
13
|
+
set :deploy_to, "/svc/#{application}"
|
14
|
+
#set :user, 'someone'
|
15
|
+
|
16
|
+
# Get repo configuration
|
17
|
+
set :repository, "git@github.com:yourname/#{application}.git"
|
18
|
+
set :scm, "git"
|
19
|
+
set :branch, "master"
|
20
|
+
set :deploy_via, :remote_cache
|
21
|
+
set :git_enable_submodules, 1
|
22
|
+
|
23
|
+
# No sudo
|
24
|
+
set :use_sudo, false
|
25
|
+
|
26
|
+
# File list in the config_files setting will be copied from the
|
27
|
+
# 'deploy_to' directory into config, overwriting files from the repo
|
28
|
+
# with the same name
|
29
|
+
set :config_files, %w{}
|
30
|
+
|
31
|
+
# List any work directories here that you need persisted between
|
32
|
+
# deployments. They are created in 'deploy_to'/shared and symlinked
|
33
|
+
# into the root directory of the deployment.
|
34
|
+
set :shared_children, %w{log tmp}
|
35
|
+
|
36
|
+
# Record our dependencies
|
37
|
+
depend :remote, :gem, "daemon-kit", ">=0.0.0"
|
38
|
+
|
39
|
+
# Hook into capistrano's events
|
40
|
+
before "deploy:update_code", "deploy:check"
|
41
|
+
|
42
|
+
# Create some tasks related to deployment
|
43
|
+
namespace :deploy do
|
44
|
+
|
45
|
+
desc "Get the current revision of the deployed code"
|
46
|
+
task :get_current_version do
|
47
|
+
run "cat #{current_path}/REVISION" do |ch, stream, out|
|
48
|
+
puts "Current revision: " + out.chomp
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
File without changes
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'timeout'
|
2
2
|
|
3
3
|
module DaemonKit
|
4
4
|
|
@@ -7,29 +7,153 @@ module DaemonKit
|
|
7
7
|
|
8
8
|
class << self
|
9
9
|
|
10
|
-
# Run the file as a daemon
|
11
|
-
def
|
10
|
+
# Run the specified file as a daemon process.
|
11
|
+
def exec( file )
|
12
12
|
raise DaemonNotFound.new( file ) unless File.exist?( file )
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
DaemonKit.configuration.daemon_name ||= File.basename( file )
|
15
|
+
|
16
|
+
command, configs, args = Arguments.parse( ARGV )
|
17
|
+
|
18
|
+
case command
|
19
|
+
when :run
|
20
|
+
parse_arguments( args )
|
21
|
+
run( file )
|
22
|
+
when :start
|
23
|
+
parse_arguments( args )
|
24
|
+
start( file )
|
25
|
+
when :stop
|
26
|
+
stop
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Run the daemon in the foreground without daemonizing
|
31
|
+
def run( file )
|
32
|
+
self.chroot
|
33
|
+
self.clean_fd
|
34
|
+
self.redirect_io( true )
|
35
|
+
|
36
|
+
require file
|
37
|
+
end
|
38
|
+
|
39
|
+
# Run our file properly
|
40
|
+
def start( file )
|
41
|
+
self.daemonize
|
42
|
+
self.chroot
|
43
|
+
self.clean_fd
|
44
|
+
self.redirect_io
|
45
|
+
|
46
|
+
require file
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
@pid_file = PidFile.new( DaemonKit.configuration.pid_file )
|
51
|
+
|
52
|
+
unless @pid_file.running?
|
53
|
+
puts "Nothing to stop"
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
|
57
|
+
target_pid = @pid_file.pid
|
58
|
+
|
59
|
+
puts "Sending TERM to #{target_pid}"
|
60
|
+
Process.kill( 'TERM', target_pid )
|
61
|
+
|
62
|
+
if seconds = DaemonKit.configuration.force_kill_wait
|
63
|
+
begin
|
64
|
+
Timeout::timeout( seconds ) do
|
65
|
+
loop do
|
66
|
+
puts "Waiting #{seconds} seconds for #{target_pid} before sending KILL"
|
67
|
+
|
68
|
+
break unless @pid_file.running?
|
16
69
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
70
|
+
seconds -= 1
|
71
|
+
sleep 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
rescue Timeout::Error
|
75
|
+
Process.kill( 'KILL', target_pid )
|
76
|
+
end
|
77
|
+
end
|
21
78
|
|
22
|
-
|
79
|
+
@pid_file.cleanup
|
23
80
|
end
|
24
81
|
|
25
82
|
# Call this from inside a daemonized process to complete the
|
26
83
|
# initialization process
|
27
84
|
def running!
|
28
|
-
|
85
|
+
Initializer.continue!
|
29
86
|
|
30
87
|
yield DaemonKit.configuration if block_given?
|
31
88
|
end
|
32
89
|
|
90
|
+
# Exit the daemon
|
91
|
+
# TODO: Make configurable callback chain
|
92
|
+
# TODO: Hook into at_exit()
|
93
|
+
def exit!( code = 0 )
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
|
98
|
+
def parse_arguments( args )
|
99
|
+
DaemonKit.arguments = Arguments.new
|
100
|
+
DaemonKit.arguments.parse( args )
|
101
|
+
end
|
102
|
+
|
103
|
+
# Daemonize the process
|
104
|
+
def daemonize
|
105
|
+
@pid_file = PidFile.new( DaemonKit.configuration.pid_file )
|
106
|
+
@pid_file.ensure_stopped!
|
107
|
+
|
108
|
+
if RUBY_VERSION < "1.9"
|
109
|
+
exit if fork
|
110
|
+
Process.setsid
|
111
|
+
exit if fork
|
112
|
+
else
|
113
|
+
Process.daemon( true, true )
|
114
|
+
end
|
115
|
+
|
116
|
+
@pid_file.write!
|
117
|
+
|
118
|
+
# TODO: Convert into shutdown hook
|
119
|
+
at_exit { @pid_file.cleanup }
|
120
|
+
end
|
121
|
+
|
122
|
+
# Release the old working directory and insure a sensible umask
|
123
|
+
# TODO: Make chroot directory configurable
|
124
|
+
def chroot
|
125
|
+
Dir.chdir '/'
|
126
|
+
File.umask 0000
|
127
|
+
end
|
128
|
+
|
129
|
+
# Make sure all file descriptors are closed (with the exception
|
130
|
+
# of STDIN, STDOUT & STDERR)
|
131
|
+
def clean_fd
|
132
|
+
ObjectSpace.each_object(IO) do |io|
|
133
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
134
|
+
begin
|
135
|
+
unless io.closed?
|
136
|
+
io.close
|
137
|
+
end
|
138
|
+
rescue ::Exception
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Redirect our IO
|
145
|
+
# TODO: make this configurable
|
146
|
+
def redirect_io( simulate = false )
|
147
|
+
begin
|
148
|
+
STDIN.reopen '/dev/null'
|
149
|
+
rescue ::Exception
|
150
|
+
end
|
151
|
+
|
152
|
+
unless simulate
|
153
|
+
STDOUT.reopen '/dev/null', 'a'
|
154
|
+
STDERR.reopen '/dev/null', 'a'
|
155
|
+
end
|
156
|
+
end
|
33
157
|
end
|
34
158
|
|
35
159
|
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module DaemonKit
|
4
|
+
|
5
|
+
# A wrapper around OptParse for setting up arguments to the daemon
|
6
|
+
# process.
|
7
|
+
#
|
8
|
+
# TODO: Set rules for basic options that go for all daemons
|
9
|
+
# TODO: Load options from config/arguments.rb
|
10
|
+
class Arguments
|
11
|
+
|
12
|
+
# Default command
|
13
|
+
@default_command = 'run'
|
14
|
+
|
15
|
+
# Valid commands
|
16
|
+
@commands = [
|
17
|
+
'start',
|
18
|
+
'stop',
|
19
|
+
'run'
|
20
|
+
]
|
21
|
+
|
22
|
+
class << self
|
23
|
+
|
24
|
+
attr_reader :default_command, :commands
|
25
|
+
|
26
|
+
# Parse the argument values and return an array with the command
|
27
|
+
# name, config values and argument values
|
28
|
+
def parse( argv )
|
29
|
+
cmd, argv = self.command( argv )
|
30
|
+
|
31
|
+
return cmd, *self.configuration( argv )
|
32
|
+
end
|
33
|
+
|
34
|
+
# Parse the provided argument array for a given command, or
|
35
|
+
# return the default command and the remaining arguments
|
36
|
+
def command( argv )
|
37
|
+
# extract command or set default
|
38
|
+
cmd = self.commands.include?( argv[0] ) ? argv.shift : self.default_command
|
39
|
+
|
40
|
+
return cmd.to_sym, argv
|
41
|
+
end
|
42
|
+
|
43
|
+
# Extracts any values for arguments matching '--config' as well
|
44
|
+
# as some implication arguments like '-e'. Returns an array with
|
45
|
+
# the configs as the first value and the remaing args as the
|
46
|
+
# last value.
|
47
|
+
#
|
48
|
+
# To set a value on the default #Configuration instance, use the
|
49
|
+
# following notation:
|
50
|
+
#
|
51
|
+
# --config attribute=value
|
52
|
+
#
|
53
|
+
# The above notation can be used several times to set different
|
54
|
+
# values.
|
55
|
+
#
|
56
|
+
# Special, or 'normal' arguments that are mapped to the default
|
57
|
+
# #Configuration instance are listed below:
|
58
|
+
#
|
59
|
+
# -e value or --env value => environment
|
60
|
+
# --pid pidfile => pid_file
|
61
|
+
#
|
62
|
+
def configuration( argv )
|
63
|
+
configs = []
|
64
|
+
|
65
|
+
i = 0
|
66
|
+
while i < argv.size
|
67
|
+
if argv[i] == "--config"
|
68
|
+
argv.delete_at( i )
|
69
|
+
configs << argv.delete_at(i)
|
70
|
+
end
|
71
|
+
|
72
|
+
if argv[i] == "-e" || argv[i] == "--env"
|
73
|
+
argv.delete_at( i )
|
74
|
+
configs << "environment=#{argv.delete_at(i)}"
|
75
|
+
end
|
76
|
+
|
77
|
+
if argv[i] == "--pid"
|
78
|
+
argv.delete_at( i )
|
79
|
+
configs << "pid_file=#{argv.delete_at(i)}"
|
80
|
+
end
|
81
|
+
|
82
|
+
i += 1
|
83
|
+
end
|
84
|
+
|
85
|
+
return configs, argv
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return the arguments remaining after running through #configuration
|
89
|
+
def arguments( argv )
|
90
|
+
self.configuration( argv ).last
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_reader :options
|
95
|
+
|
96
|
+
def initialize
|
97
|
+
@options = {}
|
98
|
+
|
99
|
+
@parser = OptionParser.new do |opts|
|
100
|
+
opts.banner = "Usage: #{File.basename($0)} [command] [options]"
|
101
|
+
|
102
|
+
opts.separator ""
|
103
|
+
|
104
|
+
opts.separator "Command is one of the following:"
|
105
|
+
opts.separator " run - Run the daemon without forking (default)"
|
106
|
+
opts.separator " start - Run the daemon"
|
107
|
+
opts.separator " stop - Stop the running daemon"
|
108
|
+
|
109
|
+
opts.separator ""
|
110
|
+
|
111
|
+
opts.separator "Options can be:"
|
112
|
+
|
113
|
+
arg_file = File.join( DaemonKit.root, 'config', 'arguments.rb' )
|
114
|
+
eval(IO.read(arg_file), binding, arg_file) if File.exists?( arg_file )
|
115
|
+
|
116
|
+
opts.on("-e", "--env ENVIRONMENT", "Environment for the process", "Defaults to development") do
|
117
|
+
# Nothing, just here for show
|
118
|
+
end
|
119
|
+
|
120
|
+
opts.on("--pidfile PATH", "Path to the pidfile", "Defaults to log/#{DaemonKit.configuration.daemon_name}.pid") do
|
121
|
+
# Nothing, just here for show
|
122
|
+
end
|
123
|
+
|
124
|
+
opts.separator ""
|
125
|
+
opts.separator "Advanced configurations:"
|
126
|
+
opts.on("--config ATTRIBUTE=VALUE",
|
127
|
+
"Change values of the daemon-kit Configuration instance",
|
128
|
+
"Example: log_dir=/path/to/log-directory") do
|
129
|
+
# Nothing, just here for show
|
130
|
+
end
|
131
|
+
|
132
|
+
opts.separator ""
|
133
|
+
|
134
|
+
opts.separator "Common options:"
|
135
|
+
opts.on("-v", "--version", "Show version information and exit") do
|
136
|
+
puts "daemon-kit #{DaemonKit::VERSION} (http://github.com/kennethkalmer/daemon-kit)"
|
137
|
+
exit
|
138
|
+
end
|
139
|
+
|
140
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
141
|
+
puts opts
|
142
|
+
exit
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def parse( argv )
|
148
|
+
@parser.parse!( argv )
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|