daemon-kit 0.1.7 → 0.1.7.4

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.
Files changed (80) hide show
  1. data/Configuration.txt +58 -0
  2. data/History.txt +24 -0
  3. data/Manifest.txt +50 -2
  4. data/PostInstall.txt +1 -1
  5. data/README.rdoc +7 -9
  6. data/Rakefile +2 -4
  7. data/TODO.txt +6 -5
  8. data/app_generators/daemon_kit/daemon_kit_generator.rb +5 -0
  9. data/app_generators/daemon_kit/templates/Rakefile +3 -1
  10. data/app_generators/daemon_kit/templates/bin/daemon.erb +1 -1
  11. data/app_generators/daemon_kit/templates/config/arguments.rb +12 -0
  12. data/app_generators/daemon_kit/templates/config/boot.rb +2 -2
  13. data/app_generators/daemon_kit/templates/script/console +3 -0
  14. data/app_generators/daemon_kit/templates/script/destroy +14 -0
  15. data/app_generators/daemon_kit/templates/script/generate +14 -0
  16. data/daemon_generators/amqp/templates/config/amqp.yml +5 -5
  17. data/daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb +4 -23
  18. data/daemon_generators/deploy_capistrano/templates/USAGE +10 -0
  19. data/daemon_generators/deploy_capistrano/templates/config/deploy.rb +3 -1
  20. data/lib/daemon_kit.rb +33 -5
  21. data/lib/daemon_kit/amqp.rb +2 -1
  22. data/lib/daemon_kit/application.rb +136 -11
  23. data/lib/daemon_kit/arguments.rb +151 -0
  24. data/lib/daemon_kit/commands/console.rb +38 -0
  25. data/lib/daemon_kit/config.rb +1 -0
  26. data/lib/daemon_kit/console_daemon.rb +2 -0
  27. data/lib/daemon_kit/core_ext.rb +1 -0
  28. data/lib/daemon_kit/core_ext/string.rb +22 -0
  29. data/lib/daemon_kit/deployment/capistrano.rb +6 -9
  30. data/lib/daemon_kit/error_handlers/mail.rb +52 -15
  31. data/lib/daemon_kit/initializer.rb +95 -41
  32. data/lib/daemon_kit/pid_file.rb +61 -0
  33. data/lib/daemon_kit/tasks/environment.rake +5 -4
  34. data/lib/daemon_kit/tasks/framework.rake +15 -1
  35. data/lib/daemon_kit/tasks/god.rake +62 -0
  36. data/lib/daemon_kit/tasks/log.rake +8 -0
  37. data/lib/daemon_kit/tasks/monit.rake +29 -0
  38. data/spec/argument_spec.rb +51 -0
  39. data/spec/config_spec.rb +77 -0
  40. data/spec/daemon_kit_spec.rb +2 -2
  41. data/spec/error_handlers_spec.rb +23 -0
  42. data/spec/fixtures/env.yml +15 -0
  43. data/spec/fixtures/noenv.yml +4 -0
  44. data/spec/initializer_spec.rb +4 -3
  45. data/spec/spec_helper.rb +8 -11
  46. data/templates/god/god.erb +69 -0
  47. data/templates/monit/monit.erb +14 -0
  48. data/test/test_daemon-kit_generator.rb +6 -1
  49. data/test/test_deploy_capistrano_generator.rb +1 -2
  50. data/vendor/tmail-1.2.3/tmail.rb +5 -0
  51. data/vendor/tmail-1.2.3/tmail/address.rb +426 -0
  52. data/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
  53. data/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
  54. data/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
  55. data/vendor/tmail-1.2.3/tmail/config.rb +67 -0
  56. data/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
  57. data/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
  58. data/vendor/tmail-1.2.3/tmail/header.rb +960 -0
  59. data/vendor/tmail-1.2.3/tmail/index.rb +9 -0
  60. data/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
  61. data/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
  62. data/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
  63. data/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
  64. data/vendor/tmail-1.2.3/tmail/main.rb +6 -0
  65. data/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
  66. data/vendor/tmail-1.2.3/tmail/net.rb +248 -0
  67. data/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
  68. data/vendor/tmail-1.2.3/tmail/parser.rb +1476 -0
  69. data/vendor/tmail-1.2.3/tmail/port.rb +379 -0
  70. data/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
  71. data/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
  72. data/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
  73. data/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
  74. data/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
  75. data/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
  76. data/vendor/tmail-1.2.3/tmail/version.rb +39 -0
  77. data/vendor/tmail.rb +13 -0
  78. metadata +57 -18
  79. data/daemon_generators/deploy_capistrano/USAGE +0 -5
  80. data/lib/daemon_kit/patches/force_kill_wait.rb +0 -120
@@ -20,6 +20,7 @@ module DaemonKit
20
20
 
21
21
  # Load the +config+.yml file from DAEMON_ROOT/config
22
22
  def load( config )
23
+ config = config.to_s
23
24
  config += '.yml' unless config =~ /\.yml$/
24
25
 
25
26
  path = File.join( DAEMON_ROOT, 'config', config )
@@ -0,0 +1,2 @@
1
+ # Load and configure our daemon process, without daemonizing
2
+ DaemonKit::Application.running!
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/core_ext/string'
@@ -0,0 +1,22 @@
1
+ require 'pathname'
2
+
3
+ class String
4
+
5
+ # Assuming the string is a file or path name, convert it into an
6
+ # absolute path.
7
+ def to_absolute_path
8
+ # Pathname is incompatible with Windows, but Windows doesn't have
9
+ # real symlinks so File.expand_path is safe.
10
+ if RUBY_PLATFORM =~ /(:?mswin|mingw)/
11
+ File.expand_path( self )
12
+
13
+ # Otherwise use Pathname#realpath which respects symlinks.
14
+ else
15
+ begin
16
+ File.expand_path( Pathname.new( self ).realpath.to_s )
17
+ rescue Errno::ENOENT
18
+ File.expand_path( Pathname.new( self ).cleanpath.to_s )
19
+ end
20
+ end
21
+ end
22
+ end
@@ -305,18 +305,15 @@ namespace :deploy do
305
305
  end
306
306
 
307
307
  desc <<-DESC
308
- Restarts your application. This works by calling the bin/:application \
309
- script under the current path with 'restart'
308
+ Restarts your application. This works by calling 'stop' task, \
309
+ followed by the 'start' task.
310
310
 
311
- By default, this will be invoked via sudo as the `app' user. If \
312
- you wish to run it as a different user, set the :runner variable to \
313
- that user. If you are in an environment where you can't use sudo, set \
314
- the :use_sudo variable to false:
315
-
316
- set :use_sudo, false
311
+ See the descriptions for the 'start' and 'stop' tasks for any \
312
+ additional info.
317
313
  DESC
318
314
  task :restart, :except => { :no_release => true } do
319
- try_runner "/usr/bin/env DAEMON_ENV=#{fetch(:daemon_env)} #{current_path}/bin/#{application} restart"
315
+ stop
316
+ start
320
317
  end
321
318
 
322
319
  namespace :rollback do
@@ -1,33 +1,66 @@
1
+ require DaemonKit.framework_root + '/vendor/tmail'
1
2
  require 'net/smtp'
2
3
 
3
4
  module DaemonKit
4
5
  module ErrorHandlers
5
- # Send an email notification of the exception via SMTP
6
+ # Send an email notification of the exception via SMTP.
6
7
  class Mail < Base
7
-
8
+
8
9
  # SMTP hostname
9
10
  @host = 'localhost'
10
- attr_accessor :host
11
-
11
+
12
+ # SMTP port
13
+ @port = 25
14
+
12
15
  # Recipients of the notification
13
16
  @recipients = []
14
- attr_accessor :recipients
15
17
 
16
18
  # Subject prefix
17
19
  @prefix = '[DAEMON-KIT]'
18
- attr_accessor :prefix
19
20
 
20
21
  # Sender address
21
22
  @sender = 'daemon-kit'
22
- attr_accessor :sender
23
+
24
+ # SMTP username
25
+ @username = nil
26
+
27
+ # SMTP password
28
+ @password = nil
29
+
30
+ # Authentication mechanism (:plain, :login, or :cram_md5)
31
+ @authentication = nil
32
+
33
+ # Use TLS?
34
+ @tls = false
35
+
36
+ # Domain used when talking to SMTP server
37
+ @domain = 'localhost.localdomain'
38
+
39
+ class << self
40
+ attr_accessor :host, :port, :recipients, :prefix, :sender, :username,
41
+ :password, :authentication, :tls, :domain
42
+ end
43
+
44
+ [ :host, :port, :recipients, :prefix, :sender, :username, :password,
45
+ :authentication, :tls, :domain ].each do |cm|
46
+ class_eval(<<-EOM, __FILE__, __LINE__)
47
+ def #{cm}=( val )
48
+ self.class.#{cm} = val
49
+ end
50
+ EOM
51
+ end
23
52
 
24
53
  def handle_exception( exception )
25
- email = <<EOF
26
- To: #{self.recipients.map { |r| '<' + r + '>' }.join(', ')}
27
- From: <#{self.sender}>
28
- Subject: #{self.prefix} #{exception.message}
29
- Date: #{Time.now}
30
54
 
55
+ mail = TMail::Mail.new
56
+ mail.to = self.class.recipients
57
+ mail.from = self.class.sender
58
+ mail.subject = "#{self.class.prefix} #{exception.message}"
59
+ mail.set_content_type 'text', 'plain'
60
+ mail.mime_version = '1.0'
61
+ mail.date = Time.now
62
+
63
+ mail.body = <<EOF
31
64
  DaemonKit caught an exception inside #{DaemonKit.configuration.daemon_name}.
32
65
 
33
66
  Message: #{exception.message}
@@ -37,10 +70,14 @@ Backtrace:
37
70
  Environment: #{ENV.inspect}
38
71
  EOF
39
72
  begin
40
- Net::SMTP.start( self.host ) do |smtp|
41
- smtp.send_message( email, self.sender, self.recipients )
73
+ smtp = Net::SMTP.new( self.class.host, self.class.port )
74
+ smtp.enable_starttls_auto if self.class.tls && smtp.respond_to?(:enable_starttls_auto)
75
+ smtp.start( self.class.domain, self.class.username, self.class.password,
76
+ self.class.authentication ) do |smtp|
77
+ smtp.sendmail( mail.to_s, mail.from, mail.to )
42
78
  end
43
- rescue
79
+ rescue => e
80
+ DaemonKit.logger.error "Failed to send exception mail: #{e.message}" if DaemonKit.logger
44
81
  end
45
82
  end
46
83
  end
@@ -3,21 +3,21 @@ require 'pathname'
3
3
 
4
4
  DAEMON_ENV = (ENV['DAEMON_ENV'] || 'development').dup unless defined?(DAEMON_ENV)
5
5
 
6
- $:.unshift File.dirname(__FILE__) + '/..'
6
+ # Absolute paths to the daemon_kit libraries added to $:
7
+ incdir = ( File.dirname(__FILE__) + '/..' )
8
+ absincdir = if RUBY_PLATFORM =~ /(:?mswin|mingw)/
9
+ File.expand_path( incdir )
10
+ else
11
+ File.expand_path( Pathname.new( incdir ).realpath.to_s )
12
+ end
13
+ $:.unshift absincdir unless $:.include?( absincdir )
14
+
7
15
  require 'daemon_kit'
8
16
 
9
17
  module DaemonKit
10
18
 
11
19
  class << self
12
20
 
13
- def logger
14
- @logger
15
- end
16
-
17
- def logger=( logger )
18
- @logger = logger
19
- end
20
-
21
21
  def configuration
22
22
  @configuration
23
23
  end
@@ -26,17 +26,18 @@ module DaemonKit
26
26
  @configuration = configuration
27
27
  end
28
28
 
29
- def trap( *args, &block )
30
- self.configuration.trap( *args, &block )
29
+ def arguments
30
+ @arguments
31
31
  end
32
32
 
33
- def framework_root
34
- File.join( File.dirname(__FILE__), '..', '..' )
33
+ def arguments=( args )
34
+ @arguments = args
35
35
  end
36
36
 
37
- def root
38
- DAEMON_ROOT
37
+ def trap( *args, &block )
38
+ self.configuration.trap( *args, &block )
39
39
  end
40
+
40
41
  end
41
42
 
42
43
 
@@ -46,7 +47,9 @@ module DaemonKit
46
47
 
47
48
  attr_reader :configuration
48
49
 
49
- def self.run( configuration = Configuration.new )
50
+ def self.run
51
+ configuration = DaemonKit.configuration || Configuration.new
52
+
50
53
  yield configuration if block_given?
51
54
  initializer = new configuration
52
55
  initializer.before_daemonize
@@ -59,7 +62,7 @@ module DaemonKit
59
62
  end
60
63
 
61
64
  def self.shutdown
62
- DaemonKit.logger.warn "Shutting down"
65
+ DaemonKit.logger.warn "Shutting down #{DaemonKit.configuration.daemon_name}"
63
66
  exit
64
67
  end
65
68
 
@@ -83,6 +86,10 @@ module DaemonKit
83
86
 
84
87
  include_core_lib
85
88
  load_postdaemonize_configs
89
+
90
+ set_process_name
91
+
92
+ DaemonKit.logger.info( "DaemonKit (#{DaemonKit::VERSION}) booted, now running #{DaemonKit.configuration.daemon_name}" )
86
93
  end
87
94
 
88
95
  def set_load_path
@@ -96,9 +103,7 @@ module DaemonKit
96
103
  end
97
104
 
98
105
  def load_patches
99
- if !!configuration.force_kill_wait
100
- require 'daemon_kit/patches/force_kill_wait'
101
- end
106
+
102
107
  end
103
108
 
104
109
  def load_environment
@@ -136,6 +141,8 @@ module DaemonKit
136
141
 
137
142
  DaemonKit.logger = logger
138
143
 
144
+ DaemonKit.logger.info "DaemonKit (#{DaemonKit::VERSION}) booting in #{DAEMON_ENV} mode"
145
+
139
146
  configuration.trap("USR1") {
140
147
  DaemonKit.logger.level = DaemonKit.logger.debug? ? Logger::INFO : Logger::DEBUG
141
148
  DaemonKit.logger.info "Log level changed to #{DaemonKit.logger.debug? ? 'DEBUG' : 'INFO' }"
@@ -144,8 +151,6 @@ module DaemonKit
144
151
  DaemonKit.logger.level = Logger::DEBUG
145
152
  DaemonKit.logger.info "Log level changed to DEBUG"
146
153
  }
147
-
148
- DaemonKit.logger.info "DaemonKit up and running in #{DAEMON_ENV} mode"
149
154
  end
150
155
 
151
156
  def initialize_signal_traps
@@ -159,6 +164,10 @@ module DaemonKit
159
164
  require core_lib
160
165
  end
161
166
  end
167
+
168
+ def set_process_name
169
+ $0 = configuration.daemon_name
170
+ end
162
171
  end
163
172
 
164
173
  # Holds our various configuration values
@@ -175,21 +184,15 @@ module DaemonKit
175
184
  # Path to the log file, defaults to 'log/<environment>.log'
176
185
  attr_accessor :log_path
177
186
 
178
- # :system,
179
- attr_accessor :dir_mode
180
-
181
- # Path to the log file, defaults to 'log/<environment>.log'
182
- attr_accessor :dir
183
-
184
187
  # Provide a custom logger to use
185
188
  attr_accessor :logger
186
189
 
190
+ # Path to the pid file, defaults to 'log/<daemon_name>.pid'
191
+ attr_accessor :pid_file
192
+
187
193
  # The application name
188
194
  attr_accessor :daemon_name
189
195
 
190
- # Allow multiple copies to run?
191
- attr_accessor :multiple
192
-
193
196
  # Use the force kill patch? Give the number of seconds
194
197
  attr_accessor :force_kill_wait
195
198
 
@@ -200,6 +203,8 @@ module DaemonKit
200
203
  attr_accessor :safety_net
201
204
 
202
205
  def initialize
206
+ parse_arguments!
207
+
203
208
  set_root_path!
204
209
  set_daemon_defaults!
205
210
 
@@ -207,7 +212,6 @@ module DaemonKit
207
212
  self.log_level = default_log_level
208
213
  self.log_path = default_log_path
209
214
 
210
- self.multiple = false
211
215
  self.force_kill_wait = false
212
216
 
213
217
  self.safety_net = DaemonKit::Safety.instance
@@ -240,6 +244,10 @@ module DaemonKit
240
244
  @signal_traps[signal].unshift( proc || block )
241
245
  end
242
246
 
247
+ def pid_file
248
+ @pid_file ||= "#{File.dirname(self.log_path)}/#{self.daemon_name}.pid"
249
+ end
250
+
243
251
  protected
244
252
 
245
253
  def run_traps( signal )
@@ -255,28 +263,57 @@ module DaemonKit
255
263
  Signal.trap( signal, Proc.new { self.run_traps( signal ) } )
256
264
  end
257
265
 
266
+ def parse_arguments!
267
+ return unless own_args?
268
+
269
+ configs = Arguments.configuration( ARGV ).first
270
+ @unused_arguments = {}
271
+
272
+ configs.each do |c|
273
+ k,v = c.split('=')
274
+
275
+ if v.nil?
276
+ error( "#{k} has no value" )
277
+ next
278
+ end
279
+
280
+ begin
281
+ if self.respond_to?( k )
282
+ self.send( "#{k}=", v ) # pid_file = /var/run/foo.pid
283
+ else
284
+ @unused_arguments[ k ] = v
285
+ end
286
+ rescue => e
287
+ error( "Couldn't set `#{k}' to `#{v}': #{e.message}" )
288
+ end
289
+ end
290
+ end
291
+
292
+ # DANGEROUS: Change the value of DAEMON_ENV
293
+ def environment=( env )
294
+ ::DAEMON_ENV.replace( env )
295
+ end
296
+
258
297
  def set_root_path!
259
298
  raise "DAEMON_ROOT is not set" unless defined?(::DAEMON_ROOT)
260
- raise "DAEMON_ROOT is not a directory" unless defined?(::DAEMON_ROOT)
299
+ raise "DAEMON_ROOT is not a directory" unless File.directory?(::DAEMON_ROOT)
261
300
 
262
- @root_path =
301
+ @root_path = ::DAEMON_ROOT.to_absolute_path
263
302
  # Pathname is incompatible with Windows, but Windows doesn't have
264
303
  # real symlinks so File.expand_path is safe.
265
- if RUBY_PLATFORM =~ /(:?mswin|mingw)/
266
- File.expand_path(::DAEMON_ROOT)
304
+ #if RUBY_PLATFORM =~ /(:?mswin|mingw)/
305
+ # File.expand_path(::DAEMON_ROOT)
267
306
 
268
307
  # Otherwise use Pathname#realpath which respects symlinks.
269
- else
270
- Pathname.new(::DAEMON_ROOT).realpath.to_s
271
- end
308
+ #else
309
+ # File.expand_path( Pathname.new(::DAEMON_ROOT).realpath.to_s )
310
+ #end
272
311
 
273
312
  Object.const_set(:RELATIVE_DAEMON_ROOT, ::DAEMON_ROOT.dup) unless defined?(::RELATIVE_DAEMON_ROOT)
274
313
  ::DAEMON_ROOT.replace @root_path
275
314
  end
276
315
 
277
316
  def set_daemon_defaults!
278
- self.dir_mode = :normal
279
- self.dir = File.join( DAEMON_ROOT, 'log' )
280
317
  end
281
318
 
282
319
  def default_load_paths
@@ -290,6 +327,23 @@ module DaemonKit
290
327
  def default_log_level
291
328
  environment == 'production' ? Logger::INFO : Logger::DEBUG
292
329
  end
330
+
331
+ def error( msg )
332
+ msg = "[E] Configuration: #{msg}"
333
+
334
+ if DaemonKit.logger
335
+ DaemonKit.logger.error( msg )
336
+ else
337
+ STDERR.puts msg
338
+ end
339
+ end
340
+
341
+ # If we are executed with any of these commands, don't allow
342
+ # arguments to be parsed cause they will interfere with the
343
+ # script encapsulating DaemonKit, like capistrano
344
+ def own_args?
345
+ ![ 'cap' ].include?( File.basename( $0 ) )
346
+ end
293
347
  end
294
348
 
295
349
 
@@ -0,0 +1,61 @@
1
+ module DaemonKit
2
+
3
+ # Simple pidfile handling for daemon processes
4
+ class PidFile
5
+
6
+ def initialize( path )
7
+ @path = path.to_absolute_path
8
+ end
9
+
10
+ def exists?
11
+ File.exists?( @path )
12
+ end
13
+
14
+ # Returns true if the process is running
15
+ def running?
16
+ return false unless self.exists?
17
+
18
+ # Check if process is in existence
19
+ # The simplest way to do this is to send signal '0'
20
+ # (which is a single system call) that doesn't actually
21
+ # send a signal
22
+ begin
23
+ Process.kill(0, self.pid)
24
+ return true
25
+ rescue Errno::ESRCH
26
+ return false
27
+ rescue ::Exception # for example on EPERM (process exists but does not belong to us)
28
+ return true
29
+ #rescue Errno::EPERM
30
+ # return false
31
+ end
32
+ end
33
+
34
+ # Return the pid contained in the pidfile, or nil
35
+ def pid
36
+ return nil unless self.exists?
37
+
38
+ File.open( @path ) { |f|
39
+ return f.gets.to_i
40
+ }
41
+ end
42
+
43
+ def ensure_stopped!
44
+ if self.running?
45
+ puts "Process already running with id #{self.pid}"
46
+ exit 1
47
+ end
48
+ end
49
+
50
+ def cleanup
51
+ File.delete( @path ) rescue Errno::ENOENT
52
+ end
53
+ alias zap cleanup
54
+
55
+ def write!
56
+ File.open( @path, 'w' ) { |f|
57
+ f.puts Process.pid
58
+ }
59
+ end
60
+ end
61
+ end