ripta-daemon-kit 0.1.0.2
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/History.txt +7 -0
- data/Manifest.txt +58 -0
- data/PostInstall.txt +6 -0
- data/README.textile +77 -0
- data/Rakefile +30 -0
- data/TODO.txt +24 -0
- data/app_generators/daemon_kit/USAGE +7 -0
- data/app_generators/daemon_kit/daemon_kit_generator.rb +121 -0
- data/app_generators/daemon_kit/templates/README +48 -0
- data/app_generators/daemon_kit/templates/Rakefile +4 -0
- data/app_generators/daemon_kit/templates/bin/daemon.erb +7 -0
- data/app_generators/daemon_kit/templates/config/boot.rb +52 -0
- data/app_generators/daemon_kit/templates/config/environment.rb +19 -0
- data/app_generators/daemon_kit/templates/config/environments/development.rb +0 -0
- data/app_generators/daemon_kit/templates/config/environments/production.rb +0 -0
- data/app_generators/daemon_kit/templates/config/environments/test.rb +0 -0
- data/app_generators/daemon_kit/templates/config/initializers/readme +11 -0
- data/app_generators/daemon_kit/templates/libexec/daemon.erb +18 -0
- data/bin/daemon_kit +19 -0
- data/daemon_generators/jabber/USAGE +5 -0
- data/daemon_generators/jabber/jabber_generator.rb +65 -0
- data/daemon_generators/jabber/templates/config/initializers/jabber.rb +8 -0
- data/daemon_generators/jabber/templates/config/jabber.yml +26 -0
- data/daemon_generators/jabber/templates/libexec/daemon.rb +27 -0
- data/lib/daemon_kit/application.rb +32 -0
- data/lib/daemon_kit/initializer.rb +249 -0
- data/lib/daemon_kit/jabber.rb +172 -0
- data/lib/daemon_kit/patches/force_kill_wait.rb +120 -0
- data/lib/daemon_kit/tasks/framework.rake +75 -0
- data/lib/daemon_kit/tasks.rb +2 -0
- data/lib/daemon_kit.rb +11 -0
- data/rubygems_generators/install_rspec/USAGE +5 -0
- data/rubygems_generators/install_rspec/install_rspec_generator.rb +57 -0
- data/rubygems_generators/install_rspec/templates/spec/spec.opts +1 -0
- data/rubygems_generators/install_rspec/templates/spec/spec_helper.rb +10 -0
- data/rubygems_generators/install_rspec/templates/spec.rb +11 -0
- data/rubygems_generators/install_rspec/templates/tasks/rspec.rake +21 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +71 -0
- data/spec/daemon_kit_spec.rb +7 -0
- data/spec/initializer_spec.rb +31 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +30 -0
- data/tasks/rspec.rake +21 -0
- data/test/test_daemon-kit_generator.rb +67 -0
- data/test/test_generator_helper.rb +29 -0
- data/test/test_jabber_generator.rb +49 -0
- metadata +150 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'daemons'
|
2
|
+
|
3
|
+
module DaemonKit
|
4
|
+
|
5
|
+
# Class responsible for making the daemons run and keep them running.
|
6
|
+
class Application
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Run the file as a daemon
|
11
|
+
def run( file )
|
12
|
+
raise DaemonNotFound.new( file ) unless File.exist?( file )
|
13
|
+
|
14
|
+
app_name = DaemonKit.configuration.daemon_name || File.basename( file )
|
15
|
+
options = { :backtrace => true, :log_output => true, :app_name => app_name, :dir_mode => :normal, :dir => "log" }
|
16
|
+
|
17
|
+
options[:multiple] = DaemonKit.configuration.multiple
|
18
|
+
options[:force_kill_wait] = DaemonKit.configuration.force_kill_wait if DaemonKit.configuration.force_kill_wait
|
19
|
+
|
20
|
+
Daemons.run( file, options )
|
21
|
+
end
|
22
|
+
|
23
|
+
# Call this from inside a daemonized process to complete the
|
24
|
+
# initialization process
|
25
|
+
def running!
|
26
|
+
DaemonKit::Initializer.continue!
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
DAEMON_ENV = (ENV['DAEMON_ENV'] || 'development').dup unless defined?(DAEMON_ENV)
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__) + '/..'
|
7
|
+
require 'daemon_kit'
|
8
|
+
|
9
|
+
module DaemonKit
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def logger
|
14
|
+
@logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def logger=( logger )
|
18
|
+
@logger = logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def configuration
|
22
|
+
@configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
def configuration=( configuration )
|
26
|
+
@configuration = configuration
|
27
|
+
end
|
28
|
+
|
29
|
+
def trap( *args, &block )
|
30
|
+
self.configuration.trap( *args, &block )
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# This class does all the nightmare work of setting up a working
|
37
|
+
# environment for your daemon.
|
38
|
+
class Initializer
|
39
|
+
|
40
|
+
attr_reader :configuration
|
41
|
+
|
42
|
+
def self.run( configuration = Configuration.new )
|
43
|
+
yield configuration if block_given?
|
44
|
+
initializer = new configuration
|
45
|
+
initializer.before_daemonize
|
46
|
+
initializer
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.continue!
|
50
|
+
initializer = new DaemonKit.configuration
|
51
|
+
initializer.after_daemonize
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.shutdown
|
55
|
+
DaemonKit.logger.warn "Shutting down"
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize( configuration )
|
60
|
+
@configuration = configuration
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_daemonize
|
64
|
+
DaemonKit.configuration = @configuration
|
65
|
+
|
66
|
+
set_load_path
|
67
|
+
load_gems
|
68
|
+
load_patches
|
69
|
+
load_environment
|
70
|
+
end
|
71
|
+
|
72
|
+
def after_daemonize
|
73
|
+
initialize_logger
|
74
|
+
initialize_signal_traps
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_load_path
|
78
|
+
configuration.load_paths.each do |d|
|
79
|
+
$:.unshift( "#{DAEMON_ROOT}/#{d}" ) if File.directory?( "#{DAEMON_ROOT}/#{d}" )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_gems
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def load_patches
|
88
|
+
if !!configuration.force_kill_wait
|
89
|
+
require 'daemon_kit/patches/force_kill_wait'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def load_environment
|
94
|
+
return if @environment_loaded
|
95
|
+
@environment_loaded = true
|
96
|
+
|
97
|
+
config = configuration
|
98
|
+
|
99
|
+
eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
|
100
|
+
|
101
|
+
eval(IO.read(configuration.daemon_initializer), binding, configuration.daemon_initializer) if File.exist?( configuration.daemon_initializer )
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize_logger
|
105
|
+
return if DaemonKit.logger
|
106
|
+
|
107
|
+
unless logger = configuration.logger
|
108
|
+
logger = Logger.new( configuration.log_path )
|
109
|
+
logger.level = configuration.log_level
|
110
|
+
end
|
111
|
+
|
112
|
+
DaemonKit.logger = logger
|
113
|
+
|
114
|
+
configuration.trap("USR1") {
|
115
|
+
DaemonKit.logger.level = DaemonKit.logger.debug? ? Logger::INFO : Logger::DEBUG
|
116
|
+
DaemonKit.logger.info "Log level changed to #{DaemonKit.logger.debug? ? 'DEBUG' : 'INFO' }"
|
117
|
+
}
|
118
|
+
configuration.trap("USR2") {
|
119
|
+
DaemonKit.logger.level = Logger::DEBUG
|
120
|
+
DaemonKit.logger.info "Log level changed to DEBUG"
|
121
|
+
}
|
122
|
+
|
123
|
+
DaemonKit.logger.info "DaemonKit up and running in #{DAEMON_ENV} mode"
|
124
|
+
end
|
125
|
+
|
126
|
+
def initialize_signal_traps
|
127
|
+
term_proc = Proc.new { DaemonKit::Initializer.shutdown }
|
128
|
+
configuration.trap( 'INT', term_proc )
|
129
|
+
configuration.trap( 'TERM', term_proc )
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
# Holds our various configuration values
|
135
|
+
class Configuration
|
136
|
+
# Root to the daemon
|
137
|
+
attr_reader :root_path
|
138
|
+
|
139
|
+
# List of load paths
|
140
|
+
attr_accessor :load_paths
|
141
|
+
|
142
|
+
# The log level to use, defaults to DEBUG
|
143
|
+
attr_accessor :log_level
|
144
|
+
|
145
|
+
# Path to the log file, defaults to 'log/<environment>.log'
|
146
|
+
attr_accessor :log_path
|
147
|
+
|
148
|
+
# Provide a custom logger to use
|
149
|
+
attr_accessor :logger
|
150
|
+
|
151
|
+
# The application name
|
152
|
+
attr_accessor :daemon_name
|
153
|
+
|
154
|
+
# Allow multiple copies to run?
|
155
|
+
attr_accessor :multiple
|
156
|
+
|
157
|
+
# Use the force kill patch? Give the number of seconds
|
158
|
+
attr_accessor :force_kill_wait
|
159
|
+
|
160
|
+
# Collection of signal traps
|
161
|
+
attr_reader :signal_traps
|
162
|
+
|
163
|
+
def initialize
|
164
|
+
set_root_path!
|
165
|
+
|
166
|
+
self.load_paths = default_load_paths
|
167
|
+
self.log_level = default_log_level
|
168
|
+
self.log_path = default_log_path
|
169
|
+
|
170
|
+
self.multiple = false
|
171
|
+
self.force_kill_wait = false
|
172
|
+
|
173
|
+
@signal_traps = {}
|
174
|
+
end
|
175
|
+
|
176
|
+
def environment
|
177
|
+
::DAEMON_ENV
|
178
|
+
end
|
179
|
+
|
180
|
+
# The path to the current environment's file (<tt>development.rb</tt>, etc.). By
|
181
|
+
# default the file is at <tt>config/environments/#{environment}.rb</tt>.
|
182
|
+
def environment_path
|
183
|
+
"#{root_path}/config/environments/#{environment}.rb"
|
184
|
+
end
|
185
|
+
|
186
|
+
def daemon_initializer
|
187
|
+
"#{root_path}/config/initializers/#{self.daemon_name}.rb"
|
188
|
+
end
|
189
|
+
|
190
|
+
# Add a trap for the specified signal, can be code block or a proc
|
191
|
+
def trap( signal, proc = nil, &block )
|
192
|
+
return if proc.nil? && !block_given?
|
193
|
+
|
194
|
+
unless @signal_traps.has_key?( signal )
|
195
|
+
set_trap( signal )
|
196
|
+
end
|
197
|
+
|
198
|
+
@signal_traps[signal].unshift( proc || block )
|
199
|
+
end
|
200
|
+
|
201
|
+
protected
|
202
|
+
|
203
|
+
def run_traps( signal )
|
204
|
+
DaemonKit.logger.info "Running signal traps for #{signal}"
|
205
|
+
self.signal_traps[ signal ].each { |trap| trap.call }
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def set_trap( signal )
|
211
|
+
DaemonKit.logger.info "Setting up trap for #{signal}"
|
212
|
+
@signal_traps[ signal ] = []
|
213
|
+
Signal.trap( signal, Proc.new { self.run_traps( signal ) } )
|
214
|
+
end
|
215
|
+
|
216
|
+
def set_root_path!
|
217
|
+
raise "DAEMON_ROOT is not set" unless defined?(::DAEMON_ROOT)
|
218
|
+
raise "DAEMON_ROOT is not a directory" unless defined?(::DAEMON_ROOT)
|
219
|
+
|
220
|
+
@root_path =
|
221
|
+
# Pathname is incompatible with Windows, but Windows doesn't have
|
222
|
+
# real symlinks so File.expand_path is safe.
|
223
|
+
if RUBY_PLATFORM =~ /(:?mswin|mingw)/
|
224
|
+
File.expand_path(::DAEMON_ROOT)
|
225
|
+
|
226
|
+
# Otherwise use Pathname#realpath which respects symlinks.
|
227
|
+
else
|
228
|
+
Pathname.new(::DAEMON_ROOT).realpath.to_s
|
229
|
+
end
|
230
|
+
|
231
|
+
Object.const_set(:RELATIVE_DAEMON_ROOT, ::DAEMON_ROOT.dup) unless defined?(::RELATIVE_DAEMON_ROOT)
|
232
|
+
::DAEMON_ROOT.replace @root_path
|
233
|
+
end
|
234
|
+
|
235
|
+
def default_load_paths
|
236
|
+
[ 'lib' ]
|
237
|
+
end
|
238
|
+
|
239
|
+
def default_log_path
|
240
|
+
File.join(root_path, 'log', "#{environment}.log")
|
241
|
+
end
|
242
|
+
|
243
|
+
def default_log_level
|
244
|
+
environment == 'production' ? Logger::INFO : Logger::DEBUG
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module DaemonKit
|
4
|
+
# Thin wrapper around xmpp4r-simple, specifically designed to ease
|
5
|
+
# configuration of a jabber daemon and provide some added simplicity.
|
6
|
+
class Jabber
|
7
|
+
|
8
|
+
# Jabber connection
|
9
|
+
attr_reader :connection
|
10
|
+
|
11
|
+
@@instance = nil
|
12
|
+
@@message_handler = nil
|
13
|
+
@@presence_handler = nil
|
14
|
+
@@subscription_handler = nil
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# Deliver a message to the specified jid.
|
19
|
+
def deliver( jid, message )
|
20
|
+
instance.connection.deliver( jid, message )
|
21
|
+
end
|
22
|
+
|
23
|
+
# Use this instead of initializing, keeps it singleton
|
24
|
+
def instance
|
25
|
+
@instance ||= (
|
26
|
+
config = YAML.load_file( "#{DAEMON_ROOT}/config/jabber.yml" )[DAEMON_ENV]
|
27
|
+
raise ArgumentError, "Missing Jabber configuration for #{DAEMON_ENV} environment" if config.nil?
|
28
|
+
new( config )
|
29
|
+
)
|
30
|
+
@instance.startup!
|
31
|
+
end
|
32
|
+
private :new
|
33
|
+
|
34
|
+
def run
|
35
|
+
DaemonKit.logger.info "Starting jabber loop"
|
36
|
+
|
37
|
+
loop do
|
38
|
+
process_messages
|
39
|
+
process_updates
|
40
|
+
process_subscriptions
|
41
|
+
|
42
|
+
begin
|
43
|
+
sleep 1
|
44
|
+
rescue Interrupt
|
45
|
+
DaemonKit.logger.warn "Jabber loop interrupted"
|
46
|
+
break
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def process_messages
|
52
|
+
@message_handler ||= Proc.new { |m| DaemonKit.logger.info "Received message from #{m.from}: #{m.body}" }
|
53
|
+
|
54
|
+
instance.valid_messages { |m| @message_handler.call(m) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def process_updates
|
58
|
+
@presence_handler ||= Proc.new { |friend, old_presence, new_presence|
|
59
|
+
DaemonKit.logger.debug "Received presence update: #{friend} went from #{old_presence} to #{new_presence}"
|
60
|
+
}
|
61
|
+
|
62
|
+
instance.connection.presence_updates { |friend, old_presence, new_presence|
|
63
|
+
@presence_handler.call(friend, old_presence, new_presence)
|
64
|
+
}
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_subscriptions
|
69
|
+
@subscription_handler ||= Proc.new { |friend,presence| DaemonKit.logger.debug "Received presence update from #{friend}: #{presence}" }
|
70
|
+
|
71
|
+
instance.connection.subscription_requests { |friend,presence| @subscription_handler.call(friend,presence) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def received_messages(&block)
|
75
|
+
@message_handler = block
|
76
|
+
end
|
77
|
+
|
78
|
+
def presence_updates(&block)
|
79
|
+
@presence_handler = block
|
80
|
+
end
|
81
|
+
|
82
|
+
def subscription_requests(&block)
|
83
|
+
@subscription_handler = block
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
def initialize( options = {} )
|
89
|
+
@jabber_id = options.delete("jabber_id")
|
90
|
+
@password = options.delete("password")
|
91
|
+
@resource = options.delete("resource") || 'daemon_kit'
|
92
|
+
@masters = options.delete("masters") || []
|
93
|
+
@supporters = options.delete("supporters") || []
|
94
|
+
|
95
|
+
raise ArgumentError if [ @jabber_id, @password ].any? { |a| a.nil? }
|
96
|
+
end
|
97
|
+
|
98
|
+
def startup!
|
99
|
+
return self if @booted
|
100
|
+
|
101
|
+
connect!
|
102
|
+
setup_roster!
|
103
|
+
|
104
|
+
DaemonKit.trap( 'INT', Proc.new { self.shutdown! } )
|
105
|
+
DaemonKit.trap( 'TERM', Proc.new { self.shutdown! } )
|
106
|
+
|
107
|
+
@booted = true
|
108
|
+
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def shutdown!
|
113
|
+
DaemonKit.logger.warn "Disconnecting jabber connection"
|
114
|
+
self.connection.disconnect
|
115
|
+
end
|
116
|
+
|
117
|
+
def contacts
|
118
|
+
@masters + @supporters
|
119
|
+
end
|
120
|
+
|
121
|
+
def valid_messages(&block)
|
122
|
+
self.connection.received_messages.each do |message|
|
123
|
+
next unless valid_master?( message.from )
|
124
|
+
|
125
|
+
busy do
|
126
|
+
block.call message
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def valid_master?( jid )
|
132
|
+
@masters.include?( jid.strip.to_s )
|
133
|
+
end
|
134
|
+
|
135
|
+
def busy(&block)
|
136
|
+
self.connection.status(:dnd, "Working...")
|
137
|
+
yield
|
138
|
+
self.connection.status(:chat, self.status_line )
|
139
|
+
end
|
140
|
+
|
141
|
+
def status_line
|
142
|
+
"#{DaemonKit.configuration.daemon_name} ready for instructions"
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def connect!
|
148
|
+
jid = @jabber_id + '/' + @resource
|
149
|
+
|
150
|
+
@connection = ::Jabber::Simple.new( jid, @password, nil, self.status_line )
|
151
|
+
end
|
152
|
+
|
153
|
+
def setup_roster!
|
154
|
+
# cleanup the roster
|
155
|
+
self.connection.roster.items.each_pair do |jid, roster_item|
|
156
|
+
jid = jid.strip.to_s
|
157
|
+
unless self.contacts.include?( jid )
|
158
|
+
self.connection.remove( jid )
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# add missing contacts
|
163
|
+
self.contacts.each do |jid|
|
164
|
+
unless self.connection.subscribed_to?( jid )
|
165
|
+
self.connection.add( jid )
|
166
|
+
#self.connection.accept_subscription( jid )
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# Shamelessly taken from http://blog.rapleaf.com/dev/?p=19
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'daemons'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
module Daemons
|
8
|
+
|
9
|
+
class ApplicationGroup
|
10
|
+
|
11
|
+
# We want to redefine find_applications to not rely on
|
12
|
+
# pidfiles (e.g. find application if pidfile is gone)
|
13
|
+
# We recreate the pid files if they're not there.
|
14
|
+
def find_applications(dir)
|
15
|
+
# Find pid_files, like original implementation
|
16
|
+
pid_files = PidFile.find_files(dir, app_name)
|
17
|
+
@monitor = Monitor.find(dir, app_name + '_monitor')
|
18
|
+
pid_files.reject! {|f| f =~ /_monitor.pid$/}
|
19
|
+
|
20
|
+
# Find the missing pids based on the UNIX pids
|
21
|
+
pidfile_pids = pid_files.map {|pf| PidFile.existing(pf).pid}
|
22
|
+
missing_pids = unix_pids - pidfile_pids
|
23
|
+
|
24
|
+
# Create pidfiles that are gone
|
25
|
+
if missing_pids.size > 0
|
26
|
+
puts "[daemons_ext]: #{missing_pids.size} missing pidfiles: " +
|
27
|
+
"#{missing_pids.inspect}... creating pid file(s)."
|
28
|
+
missing_pids.each do |pid|
|
29
|
+
pidfile = PidFile.new(dir, app_name, multiple)
|
30
|
+
pidfile.pid = pid # Doesn't seem to matter if it's a string or Fixnum
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Now get all the pid file again
|
35
|
+
pid_files = PidFile.find_files(dir, app_name)
|
36
|
+
|
37
|
+
return pid_files.map {|f|
|
38
|
+
app = Application.new(self, {}, PidFile.existing(f))
|
39
|
+
setup_app(app)
|
40
|
+
app
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
# Specify :force_kill_wait => (seconds to wait) and this method will
|
45
|
+
# block until the process is dead. It first sends a TERM signal, then
|
46
|
+
# a KILL signal (-9) if the process hasn't died after the wait time.
|
47
|
+
def stop_all(force = false)
|
48
|
+
@monitor.stop if @monitor
|
49
|
+
|
50
|
+
wait = options[:force_kill_wait].to_i
|
51
|
+
if wait > 0
|
52
|
+
puts "[daemons_ext]: Killing #{app_name} with force after #{wait} secs."
|
53
|
+
|
54
|
+
# Send term first, don't delete PID files.
|
55
|
+
@applications.each {|a| a.send_sig('TERM')}
|
56
|
+
|
57
|
+
begin
|
58
|
+
started_at = Time.now
|
59
|
+
Timeout::timeout(wait) do
|
60
|
+
num_pids = unix_pids.size
|
61
|
+
while num_pids > 0
|
62
|
+
time_left = wait - (Time.now - started_at)
|
63
|
+
puts "[daemons_ext]: Waiting #{time_left.round} secs on " +
|
64
|
+
"#{num_pids} #{app_name}(s)..."
|
65
|
+
sleep 1
|
66
|
+
num_pids = unix_pids.size
|
67
|
+
end
|
68
|
+
end
|
69
|
+
rescue Timeout::Error
|
70
|
+
@applications.each {|a| a.send_sig('KILL')}
|
71
|
+
ensure
|
72
|
+
# Delete Pidfiles
|
73
|
+
@applications.each {|a| a.zap!}
|
74
|
+
end
|
75
|
+
|
76
|
+
puts "[daemons_ext]: All #{app_name}(s) dead."
|
77
|
+
else
|
78
|
+
@applications.each {|a|
|
79
|
+
if force
|
80
|
+
begin; a.stop; rescue ::Exception; end
|
81
|
+
else
|
82
|
+
a.stop
|
83
|
+
end
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Find UNIX pids based on app_name. CAUTION: This has only been tested on
|
91
|
+
# Mac OS X and CentOS.
|
92
|
+
def unix_pids
|
93
|
+
pids = []
|
94
|
+
x = `ps auxw | grep -v grep | awk '{print $2, $11}' | grep #{app_name}`
|
95
|
+
if x && x.chomp!
|
96
|
+
processes = x.split(/\n/).compact
|
97
|
+
processes = processes.delete_if do |p|
|
98
|
+
pid, name = p.split(/\s/)
|
99
|
+
# We want to make sure that the first part of the process name matches
|
100
|
+
# so that app_name matches app_name_22
|
101
|
+
app_name != name[0..(app_name.length - 1)]
|
102
|
+
end
|
103
|
+
pids = processes.map {|p| p.split(/\s/)[0].to_i}
|
104
|
+
end
|
105
|
+
|
106
|
+
pids
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
class Application
|
112
|
+
|
113
|
+
# Send signal to the process, rescue if process deson't exist
|
114
|
+
def send_sig(sig)
|
115
|
+
Process.kill(sig, @pid.pid) rescue Errno::ESRCH
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
namespace :daemon_kit do
|
2
|
+
namespace :freeze do
|
3
|
+
desc "Lock this application to the current gem (by unpacking it into vendor/daemon_kit)"
|
4
|
+
task :gems do
|
5
|
+
deps = %w()
|
6
|
+
require 'rubygems'
|
7
|
+
require 'rubygems/gem_runner'
|
8
|
+
|
9
|
+
kit = (version = ENV['VERSION']) ?
|
10
|
+
Gem.cache.find_name('daemon-kit', "= #{version}").first :
|
11
|
+
Gem.cache.find_name('daemon-kit').sort_by { |g| g.version }.last
|
12
|
+
|
13
|
+
version ||= kit.version
|
14
|
+
|
15
|
+
unless kit
|
16
|
+
puts "No daemon_kit gem #{version} is installed. Do 'gem list daemon_kit' to see what you have available."
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
puts "Freezing the gem for DaemonKit #{kit.version}"
|
21
|
+
rm_rf "vendor/daemon_kit"
|
22
|
+
mkdir_p "vendor/daemon_kit"
|
23
|
+
|
24
|
+
begin
|
25
|
+
chdir("vendor/daemon_kit") do
|
26
|
+
kit.dependencies.select { |g| deps.include? g.name }.each do |g|
|
27
|
+
Gem::GemRunner.new.run(["unpack", g.name, "--version", g.version_requirements.to_s])
|
28
|
+
mv(Dir.glob("#{g.name}*").first, g.name)
|
29
|
+
end
|
30
|
+
|
31
|
+
Gem::GemRunner.new.run(["unpack", "daemon-kit", "--version", "=#{version}"])
|
32
|
+
FileUtils.mv(Dir.glob("daemon-kit*").first, "daemon-kit")
|
33
|
+
end
|
34
|
+
rescue Exception
|
35
|
+
rm_rf "vendor/daemon_kit"
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'Lock to latest edge daemon_kit'
|
41
|
+
task :edge do
|
42
|
+
require 'open-uri'
|
43
|
+
#version = ENV["RELEASE"] || "edge"
|
44
|
+
commits = "http://github.com/api/v1/yaml/kennethkalmer/daemon-kit/commits/master"
|
45
|
+
url = "http://github.com/kennethkalmer/daemon-kit/zipball/master"
|
46
|
+
|
47
|
+
rm_rf "vendor/daemon_kit"
|
48
|
+
mkdir_p "vendor/daemon_kit"
|
49
|
+
|
50
|
+
chdir 'vendor/daemon_kit' do
|
51
|
+
latest_revision = YAML.load(open(commits))["commits"].first["id"]
|
52
|
+
|
53
|
+
puts "Downloading DaemonKit from #{url}"
|
54
|
+
File.open('daemon-kit.zip', 'wb') do |dst|
|
55
|
+
open url do |src|
|
56
|
+
while chunk = src.read(4096)
|
57
|
+
dst << chunk
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
puts 'Unpacking DaemonKit'
|
63
|
+
rm_rf 'daemon-kit'
|
64
|
+
`unzip daemon-kit.zip`
|
65
|
+
FileUtils.mv(Dir.glob("kennethkalmer-daemon-kit*").first, "daemon-kit")
|
66
|
+
%w(daemon-kit.zip).each do |goner|
|
67
|
+
rm_f goner
|
68
|
+
end
|
69
|
+
|
70
|
+
touch "REVISION_#{latest_revision}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
data/lib/daemon_kit.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
require 'daemon_kit/initializer'
|
7
|
+
require 'daemon_kit/application'
|
8
|
+
|
9
|
+
module DaemonKit
|
10
|
+
VERSION = '0.1.0.2'
|
11
|
+
end
|