daemon-kit 0.1.7 → 0.1.7.4

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