daemon-kit 0.1.7.9 → 0.1.7.10
Sign up to get free protection for your applications and to get access to all the features.
- data/Configuration.txt +43 -0
- data/History.txt +8 -0
- data/Manifest.txt +14 -0
- data/README.rdoc +26 -7
- data/Rakefile +9 -2
- data/RuoteParticipants.txt +113 -0
- data/app_generators/daemon_kit/daemon_kit_generator.rb +5 -5
- data/daemon_generators/ruote/USAGE +5 -0
- data/daemon_generators/ruote/ruote_generator.rb +67 -0
- data/daemon_generators/ruote/templates/config/amqp.yml +30 -0
- data/daemon_generators/ruote/templates/config/initializers/ruote.rb +13 -0
- data/daemon_generators/ruote/templates/config/ruote.yml +23 -0
- data/daemon_generators/ruote/templates/lib/daemon.rb +4 -0
- data/daemon_generators/ruote/templates/lib/sample.rb +26 -0
- data/daemon_generators/ruote/templates/libexec/daemon.rb +33 -0
- data/lib/daemon_kit.rb +9 -5
- data/lib/daemon_kit/application.rb +20 -0
- data/lib/daemon_kit/arguments.rb +4 -0
- data/lib/daemon_kit/config.rb +44 -6
- data/lib/daemon_kit/exceptions.rb +8 -0
- data/lib/daemon_kit/initializer.rb +47 -1
- data/lib/daemon_kit/ruote_participants.rb +119 -0
- data/lib/daemon_kit/ruote_pseudo_participant.rb +68 -0
- data/lib/daemon_kit/ruote_workitem.rb +169 -0
- data/spec/argument_spec.rb +19 -0
- data/spec/config_spec.rb +8 -6
- data/spec/initializer_spec.rb +3 -9
- data/test/test_ruote_generator.rb +45 -0
- metadata +40 -12
@@ -0,0 +1,13 @@
|
|
1
|
+
begin
|
2
|
+
require 'json'
|
3
|
+
rescue LoadError
|
4
|
+
$stderr.puts "Missing json gem. Please run 'gem install json'"
|
5
|
+
exit 1
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'amqp'
|
10
|
+
require 'mq'
|
11
|
+
rescue LoadError
|
12
|
+
$stderr.puts "Missing amqp gem. Please run 'gem install amqp' if you wish to use the AMQP participant/listener pair in ruote"
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Sample configuration file for a remote participant daemon
|
2
|
+
|
3
|
+
# If your using the AMQP listener/participant pair in ruote, you only
|
4
|
+
# need to specify the names of the queues to subscribe to.
|
5
|
+
|
6
|
+
defaults: &defaults
|
7
|
+
amqp:
|
8
|
+
queues:
|
9
|
+
- work1
|
10
|
+
#- work2
|
11
|
+
#- work3
|
12
|
+
|
13
|
+
development:
|
14
|
+
<<: *defaults
|
15
|
+
|
16
|
+
test:
|
17
|
+
<<: *defaults
|
18
|
+
|
19
|
+
staging:
|
20
|
+
<<: *defaults
|
21
|
+
|
22
|
+
production:
|
23
|
+
<<: *defaults
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
# Sample pseudo participant
|
4
|
+
#
|
5
|
+
# See http://gist.github.com/144861 for a test engine
|
6
|
+
class Sample < DaemonKit::RuotePseudoParticipant
|
7
|
+
|
8
|
+
on_exception :dammit
|
9
|
+
|
10
|
+
on_complete do |workitem|
|
11
|
+
workitem['success'] = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def quote
|
15
|
+
workitem["quote"] = open("http://www.iheartquotes.com/api/v1/random").read
|
16
|
+
end
|
17
|
+
|
18
|
+
def err
|
19
|
+
raise ArgumentError, "Does not compute"
|
20
|
+
end
|
21
|
+
|
22
|
+
def dammit( exception )
|
23
|
+
workitem["error"] = exception.message
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Generated remote participant for the ruote workflow engine
|
2
|
+
# (http://openwferu.rubyforge.org)
|
3
|
+
|
4
|
+
# Do your post daemonization configuration here
|
5
|
+
# At minimum you need just the first line (without the block), or a lot
|
6
|
+
# of strange things might start happening...
|
7
|
+
DaemonKit::Application.running! do |config|
|
8
|
+
# Trap signals with blocks or procs
|
9
|
+
# config.trap( 'INT' ) do
|
10
|
+
# # do something clever
|
11
|
+
# end
|
12
|
+
# config.trap( 'TERM', Proc.new { puts 'Going down' } )
|
13
|
+
end
|
14
|
+
|
15
|
+
# IMPORTANT CONFIGURATION NOTE
|
16
|
+
#
|
17
|
+
# Please review and update 'config/amqp.yml' accordingly if you wish to use
|
18
|
+
# AMQP as a transport mechanism for workitems sent between ruote and this
|
19
|
+
# daemon.
|
20
|
+
|
21
|
+
# Configuration of the remote participant shell
|
22
|
+
DaemonKit::RuoteParticipants.configure do |config|
|
23
|
+
# Use AMQP as a workitem transport mechanism
|
24
|
+
config.use :amqp
|
25
|
+
|
26
|
+
# Register your classes as pseudo-participants, with work being delegated
|
27
|
+
# according to the 'command' parameter passed in the process definition
|
28
|
+
config.register Sample
|
29
|
+
end
|
30
|
+
|
31
|
+
DaemonKit::RuoteParticipants.run do
|
32
|
+
# Place any additional daemon-specific code in here...
|
33
|
+
end
|
data/lib/daemon_kit.rb
CHANGED
@@ -4,12 +4,13 @@ require 'rubygems'
|
|
4
4
|
require 'eventmachine'
|
5
5
|
|
6
6
|
require File.dirname(__FILE__) + '/daemon_kit/core_ext'
|
7
|
+
require File.dirname(__FILE__) + '/daemon_kit/exceptions'
|
7
8
|
|
8
9
|
$:.unshift( File.dirname(__FILE__).to_absolute_path ) unless
|
9
10
|
$:.include?( File.dirname(__FILE__).to_absolute_path )
|
10
11
|
|
11
12
|
module DaemonKit
|
12
|
-
VERSION = '0.1.7.
|
13
|
+
VERSION = '0.1.7.10'
|
13
14
|
|
14
15
|
autoload :Initializer, 'daemon_kit/initializer'
|
15
16
|
autoload :Application, 'daemon_kit/application'
|
@@ -21,10 +22,13 @@ module DaemonKit
|
|
21
22
|
autoload :EM, 'daemon_kit/em'
|
22
23
|
autoload :Configurable, 'daemon_kit/core_ext/configurable'
|
23
24
|
|
24
|
-
autoload :Cron,
|
25
|
-
autoload :Jabber,
|
26
|
-
autoload :AMQP,
|
27
|
-
autoload :Nanite,
|
25
|
+
autoload :Cron, 'daemon_kit/cron'
|
26
|
+
autoload :Jabber, 'daemon_kit/jabber'
|
27
|
+
autoload :AMQP, 'daemon_kit/amqp'
|
28
|
+
autoload :Nanite, 'daemon_kit/nanite'
|
29
|
+
autoload :RuoteParticipants, 'daemon_kit/ruote_participants'
|
30
|
+
autoload :RuoteWorkitem, 'daemon_kit/ruote_workitem'
|
31
|
+
autoload :RuotePseudoParticipant, 'daemon_kit/ruote_pseudo_participant'
|
28
32
|
|
29
33
|
class << self
|
30
34
|
def logger
|
@@ -40,6 +40,7 @@ module DaemonKit
|
|
40
40
|
|
41
41
|
# Run our file properly
|
42
42
|
def start( file )
|
43
|
+
self.drop_privileges
|
43
44
|
self.daemonize
|
44
45
|
self.chroot
|
45
46
|
self.clean_fd
|
@@ -161,6 +162,25 @@ module DaemonKit
|
|
161
162
|
STDERR.reopen '/dev/null', 'a'
|
162
163
|
end
|
163
164
|
end
|
165
|
+
|
166
|
+
def drop_privileges
|
167
|
+
if DaemonKit.configuration.group
|
168
|
+
begin
|
169
|
+
group = Etc.getgrnam( DaemonKit.configuration.group )
|
170
|
+
Process::Sys.setgid( group.gid.to_i )
|
171
|
+
rescue => e
|
172
|
+
$stderr.puts "Caught exception while trying to drop group privileges: #{e.message}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
if DaemonKit.configuration.user
|
176
|
+
begin
|
177
|
+
user = Etc.getpwnam( DaemonKit.configuration.user )
|
178
|
+
Process::Sys.setuid( user.uid.to_i )
|
179
|
+
rescue => e
|
180
|
+
$stderr.puts "Caught exception while trying to drop user privileges: #{e.message}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
164
184
|
end
|
165
185
|
|
166
186
|
end
|
data/lib/daemon_kit/arguments.rb
CHANGED
@@ -68,21 +68,25 @@ module DaemonKit
|
|
68
68
|
if argv[i] == "--config"
|
69
69
|
argv.delete_at( i )
|
70
70
|
configs << argv.delete_at(i)
|
71
|
+
next
|
71
72
|
end
|
72
73
|
|
73
74
|
if argv[i] == "-e" || argv[i] == "--env"
|
74
75
|
argv.delete_at( i )
|
75
76
|
configs << "environment=#{argv.delete_at(i)}"
|
77
|
+
next
|
76
78
|
end
|
77
79
|
|
78
80
|
if argv[i] == "-l" || argv[i] == "--log"
|
79
81
|
argv.delete_at( i )
|
80
82
|
configs << "log_path=#{argv.delete_at(i)}"
|
83
|
+
next
|
81
84
|
end
|
82
85
|
|
83
86
|
if argv[i] == "--pid"
|
84
87
|
argv.delete_at( i )
|
85
88
|
configs << "pid_file=#{argv.delete_at(i)}"
|
89
|
+
next
|
86
90
|
end
|
87
91
|
|
88
92
|
i += 1
|
data/lib/daemon_kit/config.rb
CHANGED
@@ -40,9 +40,9 @@ module DaemonKit
|
|
40
40
|
# Expects a hash, looks for DAEMON_ENV key
|
41
41
|
def initialize( config_data ) #:nodoc:
|
42
42
|
if config_data.has_key?( DAEMON_ENV )
|
43
|
-
|
43
|
+
self.data = config_data[ DAEMON_ENV ]
|
44
44
|
else
|
45
|
-
|
45
|
+
self.data = config_data
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -54,17 +54,55 @@ module DaemonKit
|
|
54
54
|
# Return the internal hash structure used, optionally symbolizing
|
55
55
|
# the first level of keys in the hash
|
56
56
|
def to_h( symbolize = false )
|
57
|
-
symbolize ? @data.
|
57
|
+
symbolize ? @data.symbolize_keys : @data
|
58
58
|
end
|
59
59
|
|
60
60
|
def method_missing( method_name, *args ) #:nodoc:
|
61
|
-
# don't match setters
|
62
61
|
unless method_name.to_s =~ /[\w_]+=$/
|
63
|
-
|
64
|
-
|
62
|
+
if @data.keys.include?( method_name.to_s )
|
63
|
+
return @data.send( method_name.to_s )
|
64
|
+
end
|
65
65
|
end
|
66
66
|
|
67
67
|
super
|
68
68
|
end
|
69
|
+
|
70
|
+
def data=( hash )
|
71
|
+
@data = hash
|
72
|
+
class << @data
|
73
|
+
def symbolize_keys( hash = self )
|
74
|
+
hash.inject({}) { |result, (key, value)|
|
75
|
+
new_key = case key
|
76
|
+
when String then key.to_sym
|
77
|
+
else key
|
78
|
+
end
|
79
|
+
new_value = case value
|
80
|
+
when Hash then symbolize_keys(value)
|
81
|
+
else value
|
82
|
+
end
|
83
|
+
result[new_key] = new_value
|
84
|
+
result
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
extend_hash( @data )
|
90
|
+
end
|
91
|
+
|
92
|
+
def extend_hash( hash )
|
93
|
+
hash.keys.each do |k|
|
94
|
+
hash.instance_eval <<-KEY
|
95
|
+
def #{k}
|
96
|
+
fetch("#{k}")
|
97
|
+
end
|
98
|
+
KEY
|
99
|
+
end
|
100
|
+
|
101
|
+
hash.each do |(key, value)|
|
102
|
+
case value
|
103
|
+
when Hash then extend_hash( value )
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
69
107
|
end
|
70
108
|
end
|
@@ -37,6 +37,10 @@ module DaemonKit
|
|
37
37
|
self.configuration.trap( *args, &block )
|
38
38
|
end
|
39
39
|
|
40
|
+
def at_shutdown( &block )
|
41
|
+
self.configuration.at_shutdown( &block )
|
42
|
+
end
|
43
|
+
|
40
44
|
end
|
41
45
|
|
42
46
|
|
@@ -63,9 +67,17 @@ module DaemonKit
|
|
63
67
|
def self.shutdown( clean = false )
|
64
68
|
return unless $daemon_kit_shutdown_hooks_ran.nil?
|
65
69
|
$daemon_kit_shutdown_hooks_ran = true
|
66
|
-
|
70
|
+
|
67
71
|
DaemonKit.logger.info "Running shutdown hooks"
|
68
72
|
|
73
|
+
DaemonKit.configuration.shutdown_hooks.each do |hook|
|
74
|
+
begin
|
75
|
+
hook.call
|
76
|
+
rescue => e
|
77
|
+
DaemonKit.logger.exception( e )
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
69
81
|
log_exceptions if DaemonKit.configuration.backtraces && !clean
|
70
82
|
|
71
83
|
DaemonKit.logger.warn "Shutting down #{DaemonKit.configuration.daemon_name}"
|
@@ -87,6 +99,8 @@ module DaemonKit
|
|
87
99
|
end
|
88
100
|
|
89
101
|
def after_daemonize
|
102
|
+
set_umask
|
103
|
+
|
90
104
|
initialize_logger
|
91
105
|
initialize_signal_traps
|
92
106
|
|
@@ -97,6 +111,14 @@ module DaemonKit
|
|
97
111
|
set_process_name
|
98
112
|
|
99
113
|
DaemonKit.logger.info( "DaemonKit (#{DaemonKit::VERSION}) booted, now running #{DaemonKit.configuration.daemon_name}" )
|
114
|
+
|
115
|
+
if DaemonKit.configuration.user || DaemonKit.configuration.group
|
116
|
+
euid = Process.euid
|
117
|
+
egid = Process.egid
|
118
|
+
uid = Process.uid
|
119
|
+
gid = Process.gid
|
120
|
+
DaemonKit.logger.info( "DaemonKit dropped privileges to: #{euid} (EUID), #{egid} (EGID), #{uid} (UID), #{gid} (GID)" )
|
121
|
+
end
|
100
122
|
end
|
101
123
|
|
102
124
|
def set_load_path
|
@@ -138,6 +160,10 @@ module DaemonKit
|
|
138
160
|
end
|
139
161
|
end
|
140
162
|
|
163
|
+
def set_umask
|
164
|
+
File.umask configuration.umask
|
165
|
+
end
|
166
|
+
|
141
167
|
def initialize_logger
|
142
168
|
return if DaemonKit.logger
|
143
169
|
|
@@ -246,11 +272,23 @@ module DaemonKit
|
|
246
272
|
# Should be log backtraces
|
247
273
|
configurable :backtraces, false
|
248
274
|
|
275
|
+
# Configurable umask
|
276
|
+
configurable :umask, 0022
|
277
|
+
|
278
|
+
# Configurable user
|
279
|
+
configurable :user, :locked => true
|
280
|
+
|
281
|
+
# Confgiruable group
|
282
|
+
configurable :group, :locked => true
|
283
|
+
|
249
284
|
# Collection of signal traps
|
250
285
|
attr_reader :signal_traps
|
251
286
|
|
252
287
|
# Our safety net (#Safety) instance
|
253
288
|
attr_accessor :safety_net
|
289
|
+
|
290
|
+
# :nodoc: Shutdown hooks
|
291
|
+
attr_reader :shutdown_hooks
|
254
292
|
|
255
293
|
def initialize
|
256
294
|
parse_arguments!
|
@@ -267,6 +305,7 @@ module DaemonKit
|
|
267
305
|
self.safety_net = DaemonKit::Safety.instance
|
268
306
|
|
269
307
|
@signal_traps = {}
|
308
|
+
@shutdown_hooks = []
|
270
309
|
end
|
271
310
|
|
272
311
|
def environment
|
@@ -294,6 +333,13 @@ module DaemonKit
|
|
294
333
|
@signal_traps[signal].unshift( proc || block )
|
295
334
|
end
|
296
335
|
|
336
|
+
# Add a block or proc to be called during shutdown
|
337
|
+
def at_shutdown( proc = nil, &block )
|
338
|
+
return if proc.nil? && !block_given?
|
339
|
+
|
340
|
+
@shutdown_hooks << ( proc || block )
|
341
|
+
end
|
342
|
+
|
297
343
|
def pid_file
|
298
344
|
@pid_file ||= "#{File.dirname(self.log_path)}/#{self.daemon_name}.pid"
|
299
345
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module DaemonKit
|
2
|
+
# Class that cleanly abstracts away the different remote participants in
|
3
|
+
# ruote and allows daemon writers to just worry about processing workitems
|
4
|
+
# without worrying over the transport mechanism or anything else...
|
5
|
+
class RuoteParticipants
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# Configure this daemon as a remote participant to ruote.
|
10
|
+
def configure(&block)
|
11
|
+
instance.configure(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Activate and run the remote participant code, calling the optional
|
15
|
+
# block for additional daemon logic.
|
16
|
+
def run(&block)
|
17
|
+
instance.run(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
private :new
|
21
|
+
|
22
|
+
def instance
|
23
|
+
@instance ||= new
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def instance=( obj )
|
29
|
+
@instance = obj
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :participants
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@transports = []
|
37
|
+
@participants = {}
|
38
|
+
|
39
|
+
@configuration = Config.load('ruote')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Yields +self+ and configures the remote participants
|
43
|
+
def configure(&block)
|
44
|
+
block.call( self )
|
45
|
+
|
46
|
+
@transports.freeze
|
47
|
+
@participants.freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
# Enable the use of a specific transport for workitems. Can be :amqp to use
|
51
|
+
# the AMQPParticipant/AMQPListener pair in ruote.
|
52
|
+
def use( transport )
|
53
|
+
@transports << transport
|
54
|
+
end
|
55
|
+
|
56
|
+
# Register classes as pseudo-participants. Two styles of registration are
|
57
|
+
# supported:
|
58
|
+
#
|
59
|
+
# register( Foo )
|
60
|
+
# register( 'short', ShortParticipant )
|
61
|
+
#
|
62
|
+
# The first format uses the class name (downcased and underscored) as the
|
63
|
+
# key for identifying the pseudo-participant, the second uses the the
|
64
|
+
# provided key.
|
65
|
+
#
|
66
|
+
# Pseudo-participant classes are instantiated when registered, and the
|
67
|
+
# instances are re-used.
|
68
|
+
def register( *args )
|
69
|
+
key, klass = if args.size == 1
|
70
|
+
[ underscore( args.first.to_s ), args.first ]
|
71
|
+
else
|
72
|
+
[ args[0].to_s, args[1] ]
|
73
|
+
end
|
74
|
+
|
75
|
+
@participants[ key ] = klass.new
|
76
|
+
end
|
77
|
+
|
78
|
+
# Run the participants
|
79
|
+
def run(&block)
|
80
|
+
run_amqp! if @transports.include?( :amqp )
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def run_amqp!
|
86
|
+
AMQP.run do
|
87
|
+
mq = ::MQ.new
|
88
|
+
queues = @configuration['amqp']['queues'].to_a
|
89
|
+
|
90
|
+
queues.each do |q|
|
91
|
+
DaemonKit.logger.debug("Subscribing to #{q} for workitems")
|
92
|
+
|
93
|
+
cmdq = mq.queue( q, :durable => true )
|
94
|
+
cmdq.subscribe( :ack => true ) do |header, message|
|
95
|
+
safely do
|
96
|
+
DaemonKit.logger.debug("Received workitem: #{message.inspect}")
|
97
|
+
|
98
|
+
RuoteWorkitem.process( :amqp, message )
|
99
|
+
|
100
|
+
DaemonKit.logger.debug("Processed workitem.")
|
101
|
+
|
102
|
+
header.ack
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Shamelessly lifted from the ActiveSupport inflector
|
110
|
+
def underscore(camel_cased_word)
|
111
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
112
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
113
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
114
|
+
tr("-", "_").
|
115
|
+
downcase
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|