apphunkd 0.9.0
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/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +78 -0
- data/TODO +1 -0
- data/VERSION +1 -0
- data/apphunkd.gemspec +258 -0
- data/bin/apphunkd +4 -0
- data/config/arguments.rb +12 -0
- data/config/boot.rb +68 -0
- data/config/environment.rb +23 -0
- data/config/environments/development.rb +2 -0
- data/config/environments/production.rb +2 -0
- data/config/environments/test.rb +2 -0
- data/config/post-daemonize/readme +5 -0
- data/config/pre-daemonize/readme +12 -0
- data/config/pre-daemonize/requires.rb +2 -0
- data/lib/apphunkd.rb +25 -0
- data/lib/apphunkd/api.rb +5 -0
- data/lib/apphunkd/api/service.rb +27 -0
- data/lib/apphunkd/queue.rb +96 -0
- data/lib/apphunkd/remote.rb +29 -0
- data/lib/apphunkd/remote/result.rb +15 -0
- data/libexec/apphunkd-daemon.rb +13 -0
- data/script/console +3 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/lib/apphunkd/api/service_spec.rb +47 -0
- data/spec/lib/apphunkd/queue_spec.rb +129 -0
- data/spec/lib/apphunkd/remote_spec.rb +61 -0
- data/spec/lib/apphunkd_spec.rb +50 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +13 -0
- data/support/apphunkd.initd +47 -0
- data/support/apphunkd.monitrc +3 -0
- data/tasks/rspec.rake +21 -0
- data/vendor/daemon-kit/Configuration.txt +102 -0
- data/vendor/daemon-kit/Deployment.txt +113 -0
- data/vendor/daemon-kit/History.txt +97 -0
- data/vendor/daemon-kit/Logging.txt +92 -0
- data/vendor/daemon-kit/Manifest.txt +166 -0
- data/vendor/daemon-kit/PostInstall.txt +6 -0
- data/vendor/daemon-kit/README.rdoc +130 -0
- data/vendor/daemon-kit/Rakefile +37 -0
- data/vendor/daemon-kit/RuoteParticipants.txt +113 -0
- data/vendor/daemon-kit/TODO.txt +37 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/USAGE +7 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/daemon_kit_generator.rb +161 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/README +48 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/Rakefile +6 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/bin/daemon.erb +7 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/arguments.rb +12 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/boot.rb +68 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/environment.rb +23 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/environments/development.rb +2 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/environments/production.rb +2 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/environments/test.rb +2 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/post-daemonize/readme +5 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/config/pre-daemonize/readme +12 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/lib/daemon.rb +2 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/libexec/daemon.erb +18 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/script/console +3 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/script/destroy +14 -0
- data/vendor/daemon-kit/app_generators/daemon_kit/templates/script/generate +14 -0
- data/vendor/daemon-kit/bin/daemon_kit +18 -0
- data/vendor/daemon-kit/daemon_generators/amqp/USAGE +5 -0
- data/vendor/daemon-kit/daemon_generators/amqp/amqp_generator.rb +65 -0
- data/vendor/daemon-kit/daemon_generators/amqp/templates/config/amqp.yml +28 -0
- data/vendor/daemon-kit/daemon_generators/amqp/templates/config/initializers/amqp.rb +7 -0
- data/vendor/daemon-kit/daemon_generators/amqp/templates/libexec/daemon.rb +37 -0
- data/vendor/daemon-kit/daemon_generators/cron/USAGE +5 -0
- data/vendor/daemon-kit/daemon_generators/cron/cron_generator.rb +64 -0
- data/vendor/daemon-kit/daemon_generators/cron/templates/config/initializers/cron.rb +11 -0
- data/vendor/daemon-kit/daemon_generators/cron/templates/libexec/daemon.rb +43 -0
- data/vendor/daemon-kit/daemon_generators/cucumber/USAGE +11 -0
- data/vendor/daemon-kit/daemon_generators/cucumber/cucumber_generator.rb +38 -0
- data/vendor/daemon-kit/daemon_generators/cucumber/templates/cucumber +8 -0
- data/vendor/daemon-kit/daemon_generators/cucumber/templates/cucumber.rake +13 -0
- data/vendor/daemon-kit/daemon_generators/cucumber/templates/cucumber_environment.rb +2 -0
- data/vendor/daemon-kit/daemon_generators/cucumber/templates/env.rb +7 -0
- data/vendor/daemon-kit/daemon_generators/deploy_capistrano/deploy_capistrano_generator.rb +35 -0
- data/vendor/daemon-kit/daemon_generators/deploy_capistrano/templates/Capfile +10 -0
- data/vendor/daemon-kit/daemon_generators/deploy_capistrano/templates/USAGE +10 -0
- data/vendor/daemon-kit/daemon_generators/deploy_capistrano/templates/config/deploy.rb +53 -0
- data/vendor/daemon-kit/daemon_generators/deploy_capistrano/templates/config/deploy/production.rb +6 -0
- data/vendor/daemon-kit/daemon_generators/deploy_capistrano/templates/config/deploy/staging.rb +6 -0
- data/vendor/daemon-kit/daemon_generators/deploy_capistrano/templates/config/environments/staging.rb +0 -0
- data/vendor/daemon-kit/daemon_generators/jabber/USAGE +5 -0
- data/vendor/daemon-kit/daemon_generators/jabber/jabber_generator.rb +65 -0
- data/vendor/daemon-kit/daemon_generators/jabber/templates/config/initializers/jabber.rb +7 -0
- data/vendor/daemon-kit/daemon_generators/jabber/templates/config/jabber.yml +26 -0
- data/vendor/daemon-kit/daemon_generators/jabber/templates/libexec/daemon.rb +27 -0
- data/vendor/daemon-kit/daemon_generators/nanite_agent/USAGE +5 -0
- data/vendor/daemon-kit/daemon_generators/nanite_agent/nanite_agent_generator.rb +68 -0
- data/vendor/daemon-kit/daemon_generators/nanite_agent/templates/config/initializers/nanite_agent.rb +6 -0
- data/vendor/daemon-kit/daemon_generators/nanite_agent/templates/config/nanite.yml +35 -0
- data/vendor/daemon-kit/daemon_generators/nanite_agent/templates/lib/actors/sample.rb +11 -0
- data/vendor/daemon-kit/daemon_generators/nanite_agent/templates/libexec/daemon.rb +31 -0
- data/vendor/daemon-kit/daemon_generators/rspec/USAGE +5 -0
- data/vendor/daemon-kit/daemon_generators/rspec/rspec_generator.rb +55 -0
- data/vendor/daemon-kit/daemon_generators/rspec/templates/spec.rb +11 -0
- data/vendor/daemon-kit/daemon_generators/rspec/templates/spec/spec.opts +1 -0
- data/vendor/daemon-kit/daemon_generators/rspec/templates/spec/spec_helper.rb +21 -0
- data/vendor/daemon-kit/daemon_generators/rspec/templates/tasks/rspec.rake +21 -0
- data/vendor/daemon-kit/daemon_generators/ruote/USAGE +5 -0
- data/vendor/daemon-kit/daemon_generators/ruote/ruote_generator.rb +67 -0
- data/vendor/daemon-kit/daemon_generators/ruote/templates/config/amqp.yml +30 -0
- data/vendor/daemon-kit/daemon_generators/ruote/templates/config/initializers/ruote.rb +13 -0
- data/vendor/daemon-kit/daemon_generators/ruote/templates/config/ruote.yml +23 -0
- data/vendor/daemon-kit/daemon_generators/ruote/templates/lib/daemon.rb +4 -0
- data/vendor/daemon-kit/daemon_generators/ruote/templates/lib/sample.rb +26 -0
- data/vendor/daemon-kit/daemon_generators/ruote/templates/libexec/daemon.rb +33 -0
- data/vendor/daemon-kit/lib/daemon_kit.rb +54 -0
- data/vendor/daemon-kit/lib/daemon_kit/abstract_logger.rb +235 -0
- data/vendor/daemon-kit/lib/daemon_kit/amqp.rb +38 -0
- data/vendor/daemon-kit/lib/daemon_kit/application.rb +187 -0
- data/vendor/daemon-kit/lib/daemon_kit/arguments.rb +165 -0
- data/vendor/daemon-kit/lib/daemon_kit/commands/console.rb +38 -0
- data/vendor/daemon-kit/lib/daemon_kit/config.rb +108 -0
- data/vendor/daemon-kit/lib/daemon_kit/console_daemon.rb +2 -0
- data/vendor/daemon-kit/lib/daemon_kit/core_ext.rb +1 -0
- data/vendor/daemon-kit/lib/daemon_kit/core_ext/configurable.rb +96 -0
- data/vendor/daemon-kit/lib/daemon_kit/core_ext/string.rb +22 -0
- data/vendor/daemon-kit/lib/daemon_kit/cron.rb +48 -0
- data/vendor/daemon-kit/lib/daemon_kit/cucumber/world.rb +38 -0
- data/vendor/daemon-kit/lib/daemon_kit/deployment/capistrano.rb +482 -0
- data/vendor/daemon-kit/lib/daemon_kit/em.rb +43 -0
- data/vendor/daemon-kit/lib/daemon_kit/error_handlers/base.rb +32 -0
- data/vendor/daemon-kit/lib/daemon_kit/error_handlers/hoptoad.rb +61 -0
- data/vendor/daemon-kit/lib/daemon_kit/error_handlers/mail.rb +85 -0
- data/vendor/daemon-kit/lib/daemon_kit/exceptions.rb +8 -0
- data/vendor/daemon-kit/lib/daemon_kit/initializer.rb +438 -0
- data/vendor/daemon-kit/lib/daemon_kit/jabber.rb +170 -0
- data/vendor/daemon-kit/lib/daemon_kit/nanite.rb +7 -0
- data/vendor/daemon-kit/lib/daemon_kit/nanite/agent.rb +56 -0
- data/vendor/daemon-kit/lib/daemon_kit/pid_file.rb +61 -0
- data/vendor/daemon-kit/lib/daemon_kit/ruote_participants.rb +119 -0
- data/vendor/daemon-kit/lib/daemon_kit/ruote_pseudo_participant.rb +68 -0
- data/vendor/daemon-kit/lib/daemon_kit/ruote_workitem.rb +169 -0
- data/vendor/daemon-kit/lib/daemon_kit/safety.rb +85 -0
- data/vendor/daemon-kit/lib/daemon_kit/tasks.rb +2 -0
- data/vendor/daemon-kit/lib/daemon_kit/tasks/environment.rake +10 -0
- data/vendor/daemon-kit/lib/daemon_kit/tasks/framework.rake +120 -0
- data/vendor/daemon-kit/lib/daemon_kit/tasks/god.rake +62 -0
- data/vendor/daemon-kit/lib/daemon_kit/tasks/log.rake +8 -0
- data/vendor/daemon-kit/lib/daemon_kit/tasks/monit.rake +29 -0
- data/vendor/daemon-kit/script/console +10 -0
- data/vendor/daemon-kit/script/destroy +14 -0
- data/vendor/daemon-kit/script/generate +14 -0
- data/vendor/daemon-kit/script/txt2html +71 -0
- data/vendor/daemon-kit/spec/abstract_logger_spec.rb +126 -0
- data/vendor/daemon-kit/spec/argument_spec.rb +70 -0
- data/vendor/daemon-kit/spec/config_spec.rb +79 -0
- data/vendor/daemon-kit/spec/configurable_spec.rb +56 -0
- data/vendor/daemon-kit/spec/daemon_kit_spec.rb +7 -0
- data/vendor/daemon-kit/spec/error_handlers_spec.rb +23 -0
- data/vendor/daemon-kit/spec/fixtures/env.yml +15 -0
- data/vendor/daemon-kit/spec/fixtures/noenv.yml +4 -0
- data/vendor/daemon-kit/spec/initializer_spec.rb +26 -0
- data/vendor/daemon-kit/spec/spec.opts +1 -0
- data/vendor/daemon-kit/spec/spec_helper.rb +27 -0
- data/vendor/daemon-kit/tasks/rspec.rake +21 -0
- data/vendor/daemon-kit/templates/god/god.erb +69 -0
- data/vendor/daemon-kit/templates/monit/monit.erb +14 -0
- data/vendor/daemon-kit/test/test_amqp_generator.rb +48 -0
- data/vendor/daemon-kit/test/test_cron_generator.rb +45 -0
- data/vendor/daemon-kit/test/test_daemon-kit_generator.rb +84 -0
- data/vendor/daemon-kit/test/test_daemon_kit_config.rb +28 -0
- data/vendor/daemon-kit/test/test_deploy_capistrano_generator.rb +48 -0
- data/vendor/daemon-kit/test/test_generator_helper.rb +29 -0
- data/vendor/daemon-kit/test/test_helper.rb +7 -0
- data/vendor/daemon-kit/test/test_jabber_generator.rb +49 -0
- data/vendor/daemon-kit/test/test_nanite_agent_generator.rb +49 -0
- data/vendor/daemon-kit/test/test_ruote_generator.rb +45 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail.rb +5 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/address.rb +426 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/config.rb +67 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/header.rb +960 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/index.rb +9 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/main.rb +6 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/net.rb +248 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/parser.rb +1476 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/port.rb +379 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
- data/vendor/daemon-kit/vendor/tmail-1.2.3/tmail/version.rb +39 -0
- data/vendor/daemon-kit/vendor/tmail.rb +13 -0
- metadata +281 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module DaemonKit
|
|
2
|
+
|
|
3
|
+
# EventMachine forms a critical part of the daemon-kit toolset, and
|
|
4
|
+
# especially of daemon process developers.
|
|
5
|
+
#
|
|
6
|
+
# This class abstracts away the difficulties of managing multiple
|
|
7
|
+
# libraries that all utilize the event reactor.
|
|
8
|
+
class EM
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
|
|
12
|
+
# Start a reactor, just like classical EM.run. If the block is
|
|
13
|
+
# provided, the method will block and call the provided block
|
|
14
|
+
# argument inside the running reactor. If the block argument is
|
|
15
|
+
# not provided the reactor will be started in a separate thread
|
|
16
|
+
# and the program will continue to run after the method. All the
|
|
17
|
+
# signal traps are configured to shutdown the reactor when the
|
|
18
|
+
# daemon exists.
|
|
19
|
+
def run(&block)
|
|
20
|
+
if ::EM.reactor_running?
|
|
21
|
+
DaemonKit.logger.warn "EventMachine reactor already running"
|
|
22
|
+
block.call if block_given?
|
|
23
|
+
|
|
24
|
+
else
|
|
25
|
+
if block_given?
|
|
26
|
+
::EM.run { block.call }
|
|
27
|
+
else
|
|
28
|
+
Thread.main[:_dk_reactor] = Thread.new { EM.run {} }
|
|
29
|
+
DaemonKit.trap( 'INT' ) { DaemonKit::EM.stop }
|
|
30
|
+
DaemonKit.trap( 'TERM' ) { DaemonKit::EM.stop }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Stop the reactor
|
|
36
|
+
def stop
|
|
37
|
+
::EM.stop_event_loop if ::EM.reactor_running?
|
|
38
|
+
Thread.main[:_dk_reactor].join
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module DaemonKit
|
|
2
|
+
module ErrorHandlers
|
|
3
|
+
# Error handlers in DaemonKit are used by the #Safety class. Any
|
|
4
|
+
# error handler has to support the interface provided by this
|
|
5
|
+
# class. It's also required that safety handlers implement a
|
|
6
|
+
# singleton approach (handled by default by #Base).
|
|
7
|
+
class Base
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
|
|
11
|
+
@instance = nil
|
|
12
|
+
|
|
13
|
+
def instance
|
|
14
|
+
@instance ||= new
|
|
15
|
+
end
|
|
16
|
+
private :new
|
|
17
|
+
|
|
18
|
+
# When we're inherited, immediately register the handler with
|
|
19
|
+
# the safety net
|
|
20
|
+
def inherited( child ) #:nodoc:
|
|
21
|
+
Safety.register_error_handler( child )
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Error handlers should overwrite this method and implement
|
|
26
|
+
# their own reporting method.
|
|
27
|
+
def handle_exception( exception )
|
|
28
|
+
raise NoMethodError, "Error handler doesn't support #handle_exception"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
|
|
3
|
+
module DaemonKit
|
|
4
|
+
module ErrorHandlers
|
|
5
|
+
# Error reporting via Hoptoad.
|
|
6
|
+
class Hoptoad < Base
|
|
7
|
+
|
|
8
|
+
# Your hoptoad API key
|
|
9
|
+
@api_key = nil
|
|
10
|
+
attr_accessor :api_key
|
|
11
|
+
|
|
12
|
+
def handle_exception( exception )
|
|
13
|
+
headers = {
|
|
14
|
+
'Content-type' => 'application/x-yaml',
|
|
15
|
+
'Accept' => 'text/xml, application/xml'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
http = Net::HTTP.new( url.host, url.port )
|
|
19
|
+
data = clean_exception( exception )
|
|
20
|
+
|
|
21
|
+
response = begin
|
|
22
|
+
http.post( url.path, data.to_yaml, headers )
|
|
23
|
+
rescue TimoutError => e
|
|
24
|
+
DaemonKit.logger.error("Timeout while contacting the Hoptoad server.")
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
case response
|
|
28
|
+
when Net::HTTPSuccess then
|
|
29
|
+
DaemonKit.logger.info "Hoptoad Success: #{response.class}"
|
|
30
|
+
else
|
|
31
|
+
DaemonKit.logger.error "Hoptoad Failure: #{response.class}\n#{response.body if response.respond_to? :body}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def url
|
|
36
|
+
URI.parse("http://hoptoadapp.com/notices/")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def clean_exception( exception )
|
|
40
|
+
data = {
|
|
41
|
+
:api_key => self.api_key,
|
|
42
|
+
:error_class => exception.class.name,
|
|
43
|
+
:error_message => "#{exception.class.name}: #{exception.message}",
|
|
44
|
+
:backtrace => exception.backtrace,
|
|
45
|
+
:environment => ENV.to_hash,
|
|
46
|
+
:request => [],
|
|
47
|
+
:session => []
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
stringify_keys( data )
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def stringify_keys(hash) #:nodoc:
|
|
54
|
+
hash.inject({}) do |h, pair|
|
|
55
|
+
h[pair.first.to_s] = pair.last.is_a?(Hash) ? stringify_keys(pair.last) : pair.last
|
|
56
|
+
h
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require DaemonKit.framework_root + '/vendor/tmail'
|
|
2
|
+
require 'net/smtp'
|
|
3
|
+
|
|
4
|
+
module DaemonKit
|
|
5
|
+
module ErrorHandlers
|
|
6
|
+
# Send an email notification of the exception via SMTP.
|
|
7
|
+
class Mail < Base
|
|
8
|
+
|
|
9
|
+
# SMTP hostname
|
|
10
|
+
@host = 'localhost'
|
|
11
|
+
|
|
12
|
+
# SMTP port
|
|
13
|
+
@port = 25
|
|
14
|
+
|
|
15
|
+
# Recipients of the notification
|
|
16
|
+
@recipients = []
|
|
17
|
+
|
|
18
|
+
# Subject prefix
|
|
19
|
+
@prefix = '[DAEMON-KIT]'
|
|
20
|
+
|
|
21
|
+
# Sender address
|
|
22
|
+
@sender = 'daemon-kit'
|
|
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
|
|
52
|
+
|
|
53
|
+
def handle_exception( exception )
|
|
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
|
|
64
|
+
DaemonKit caught an exception inside #{DaemonKit.configuration.daemon_name}.
|
|
65
|
+
|
|
66
|
+
Message: #{exception.message}
|
|
67
|
+
Backtrace:
|
|
68
|
+
#{exception.backtrace.join("\n ")}
|
|
69
|
+
|
|
70
|
+
Environment: #{ENV.inspect}
|
|
71
|
+
EOF
|
|
72
|
+
begin
|
|
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 )
|
|
78
|
+
end
|
|
79
|
+
rescue => e
|
|
80
|
+
DaemonKit.logger.error "Failed to send exception mail: #{e.message}" if DaemonKit.logger
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
DAEMON_ENV = (ENV['DAEMON_ENV'] || 'development').dup unless defined?(DAEMON_ENV)
|
|
4
|
+
|
|
5
|
+
# Absolute paths to the daemon_kit libraries added to $:
|
|
6
|
+
incdir = ( File.dirname(__FILE__) + '/..' )
|
|
7
|
+
absincdir = if RUBY_PLATFORM =~ /(:?mswin|mingw)/
|
|
8
|
+
File.expand_path( incdir )
|
|
9
|
+
else
|
|
10
|
+
File.expand_path( Pathname.new( incdir ).realpath.to_s )
|
|
11
|
+
end
|
|
12
|
+
$:.unshift absincdir unless $:.include?( absincdir )
|
|
13
|
+
|
|
14
|
+
require 'daemon_kit'
|
|
15
|
+
|
|
16
|
+
module DaemonKit
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
|
|
20
|
+
def configuration
|
|
21
|
+
@configuration
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def configuration=( configuration )
|
|
25
|
+
@configuration = configuration
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def arguments
|
|
29
|
+
@arguments
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def arguments=( args )
|
|
33
|
+
@arguments = args
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def trap( *args, &block )
|
|
37
|
+
self.configuration.trap( *args, &block )
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def at_shutdown( &block )
|
|
41
|
+
self.configuration.at_shutdown( &block )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# This class does all the nightmare work of setting up a working
|
|
48
|
+
# environment for your daemon.
|
|
49
|
+
class Initializer
|
|
50
|
+
|
|
51
|
+
attr_reader :configuration
|
|
52
|
+
|
|
53
|
+
def self.run
|
|
54
|
+
configuration = DaemonKit.configuration || Configuration.new
|
|
55
|
+
|
|
56
|
+
yield configuration if block_given?
|
|
57
|
+
initializer = new configuration
|
|
58
|
+
initializer.before_daemonize
|
|
59
|
+
initializer
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.continue!
|
|
63
|
+
initializer = new DaemonKit.configuration
|
|
64
|
+
initializer.after_daemonize
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.shutdown( clean = false )
|
|
68
|
+
return unless $daemon_kit_shutdown_hooks_ran.nil?
|
|
69
|
+
$daemon_kit_shutdown_hooks_ran = true
|
|
70
|
+
|
|
71
|
+
DaemonKit.logger.info "Running shutdown hooks"
|
|
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
|
+
|
|
81
|
+
log_exceptions if DaemonKit.configuration.backtraces && !clean
|
|
82
|
+
|
|
83
|
+
DaemonKit.logger.warn "Shutting down #{DaemonKit.configuration.daemon_name}"
|
|
84
|
+
exit
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def initialize( configuration )
|
|
88
|
+
@configuration = configuration
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def before_daemonize
|
|
92
|
+
DaemonKit.configuration = @configuration
|
|
93
|
+
|
|
94
|
+
set_load_path
|
|
95
|
+
load_gems
|
|
96
|
+
load_patches
|
|
97
|
+
load_environment
|
|
98
|
+
load_predaemonize_configs
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def after_daemonize
|
|
102
|
+
set_umask
|
|
103
|
+
|
|
104
|
+
initialize_logger
|
|
105
|
+
initialize_signal_traps
|
|
106
|
+
|
|
107
|
+
include_core_lib
|
|
108
|
+
load_postdaemonize_configs
|
|
109
|
+
configure_backtraces
|
|
110
|
+
|
|
111
|
+
set_process_name
|
|
112
|
+
|
|
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
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def set_load_path
|
|
125
|
+
configuration.load_paths.each do |d|
|
|
126
|
+
$:.unshift( "#{DAEMON_ROOT}/#{d}" ) if File.directory?( "#{DAEMON_ROOT}/#{d}" )
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def load_gems
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def load_patches
|
|
135
|
+
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def load_environment
|
|
139
|
+
return if @environment_loaded
|
|
140
|
+
@environment_loaded = true
|
|
141
|
+
|
|
142
|
+
config = configuration
|
|
143
|
+
|
|
144
|
+
eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
|
|
145
|
+
|
|
146
|
+
eval(IO.read(configuration.daemon_initializer), binding, configuration.daemon_initializer) if File.exist?( configuration.daemon_initializer )
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def load_predaemonize_configs
|
|
150
|
+
Dir[ File.join( DAEMON_ROOT, 'config', 'pre-daemonize', '*.rb' ) ].each do |f|
|
|
151
|
+
next if File.basename( f ) == File.basename( configuration.daemon_initializer )
|
|
152
|
+
|
|
153
|
+
require f
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def load_postdaemonize_configs
|
|
158
|
+
Dir[ File.join( DAEMON_ROOT, 'config', 'post-daemonize', '*.rb' ) ].each do |f|
|
|
159
|
+
require f
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def set_umask
|
|
164
|
+
File.umask configuration.umask
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def initialize_logger
|
|
168
|
+
return if DaemonKit.logger
|
|
169
|
+
|
|
170
|
+
unless logger = configuration.logger
|
|
171
|
+
logger = AbstractLogger.new( configuration.log_path )
|
|
172
|
+
logger.level = configuration.log_level
|
|
173
|
+
logger.copy_to_stdout = configuration.log_stdout
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
DaemonKit.logger = logger
|
|
177
|
+
|
|
178
|
+
DaemonKit.logger.info "DaemonKit (#{DaemonKit::VERSION}) booting in #{DAEMON_ENV} mode"
|
|
179
|
+
|
|
180
|
+
configuration.trap("USR1") {
|
|
181
|
+
DaemonKit.logger.level = DaemonKit.logger.debug? ? :info : :debug
|
|
182
|
+
DaemonKit.logger.info "Log level changed to #{DaemonKit.logger.debug? ? 'DEBUG' : 'INFO' }"
|
|
183
|
+
}
|
|
184
|
+
configuration.trap("USR2") {
|
|
185
|
+
DaemonKit.logger.level = :debug
|
|
186
|
+
DaemonKit.logger.info "Log level changed to DEBUG"
|
|
187
|
+
}
|
|
188
|
+
configuration.trap("HUP") {
|
|
189
|
+
DaemonKit.logger.close
|
|
190
|
+
}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def initialize_signal_traps
|
|
194
|
+
term_proc = Proc.new { DaemonKit::Initializer.shutdown( true ) }
|
|
195
|
+
configuration.trap( 'INT', term_proc )
|
|
196
|
+
configuration.trap( 'TERM', term_proc )
|
|
197
|
+
at_exit { DaemonKit::Initializer.shutdown }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def include_core_lib
|
|
201
|
+
if File.exists?( core_lib = File.join( DAEMON_ROOT, 'lib', configuration.daemon_name + '.rb' ) )
|
|
202
|
+
require core_lib
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def configure_backtraces
|
|
207
|
+
Thread.abort_on_exception = configuration.backtraces
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def set_process_name
|
|
211
|
+
$0 = configuration.daemon_name
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def self.log_exceptions
|
|
215
|
+
trace_file = File.join( DaemonKit.root, "backtrace-#{Time.now.strftime('%Y%m%d%H%M%S')}-#{Process.pid}.log" )
|
|
216
|
+
trace_log = Logger.new( trace_file )
|
|
217
|
+
|
|
218
|
+
# Find the last exception
|
|
219
|
+
e = nil
|
|
220
|
+
ObjectSpace.each_object {|o|
|
|
221
|
+
if ::Exception === o
|
|
222
|
+
e = o
|
|
223
|
+
end
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
trace_log.info "*** Below you'll find the most recent exception thrown, this will likely (but not certainly) be the exception that made #{DaemonKit.configuration.daemon_name} exit abnormally ***"
|
|
227
|
+
trace_log.error e
|
|
228
|
+
|
|
229
|
+
trace_log.info "*** Below you'll find all the exception objects in memory, some of them may have been thrown in your application, others may just be in memory because they are standard exceptions ***"
|
|
230
|
+
ObjectSpace.each_object {|o|
|
|
231
|
+
if ::Exception === o
|
|
232
|
+
trace_log.error o
|
|
233
|
+
end
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
trace_log.close
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Holds our various configuration values
|
|
241
|
+
class Configuration
|
|
242
|
+
|
|
243
|
+
include Configurable
|
|
244
|
+
|
|
245
|
+
# Root to the daemon
|
|
246
|
+
attr_reader :root_path
|
|
247
|
+
|
|
248
|
+
# List of load paths
|
|
249
|
+
attr_accessor :load_paths
|
|
250
|
+
|
|
251
|
+
# Custom logger instance to use
|
|
252
|
+
attr_accessor :logger
|
|
253
|
+
|
|
254
|
+
# The log level to use, defaults to DEBUG
|
|
255
|
+
attr_accessor :log_level
|
|
256
|
+
|
|
257
|
+
# Path to the log file, defaults to 'log/<environment>.log'
|
|
258
|
+
configurable :log_path
|
|
259
|
+
|
|
260
|
+
# Duplicate log data to stdout
|
|
261
|
+
attr_accessor :log_stdout
|
|
262
|
+
|
|
263
|
+
# Path to the pid file, defaults to 'log/<daemon_name>.pid'
|
|
264
|
+
attr_accessor :pid_file
|
|
265
|
+
|
|
266
|
+
# The application name
|
|
267
|
+
configurable :daemon_name, :locked => true
|
|
268
|
+
|
|
269
|
+
# Use the force kill patch? Give the number of seconds
|
|
270
|
+
configurable :force_kill_wait
|
|
271
|
+
|
|
272
|
+
# Should be log backtraces
|
|
273
|
+
configurable :backtraces, false
|
|
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
|
+
|
|
284
|
+
# Collection of signal traps
|
|
285
|
+
attr_reader :signal_traps
|
|
286
|
+
|
|
287
|
+
# Our safety net (#Safety) instance
|
|
288
|
+
attr_accessor :safety_net
|
|
289
|
+
|
|
290
|
+
# :nodoc: Shutdown hooks
|
|
291
|
+
attr_reader :shutdown_hooks
|
|
292
|
+
|
|
293
|
+
def initialize
|
|
294
|
+
parse_arguments!
|
|
295
|
+
|
|
296
|
+
set_root_path!
|
|
297
|
+
set_daemon_defaults!
|
|
298
|
+
|
|
299
|
+
self.load_paths = default_load_paths
|
|
300
|
+
self.log_level ||= default_log_level
|
|
301
|
+
self.log_path ||= default_log_path
|
|
302
|
+
|
|
303
|
+
self.force_kill_wait = false
|
|
304
|
+
|
|
305
|
+
self.safety_net = DaemonKit::Safety.instance
|
|
306
|
+
|
|
307
|
+
@signal_traps = {}
|
|
308
|
+
@shutdown_hooks = []
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def environment
|
|
312
|
+
::DAEMON_ENV
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# The path to the current environment's file (<tt>development.rb</tt>, etc.). By
|
|
316
|
+
# default the file is at <tt>config/environments/#{environment}.rb</tt>.
|
|
317
|
+
def environment_path
|
|
318
|
+
"#{root_path}/config/environments/#{environment}.rb"
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def daemon_initializer
|
|
322
|
+
"#{root_path}/config/initializers/#{self.daemon_name}.rb"
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Add a trap for the specified signal, can be code block or a proc
|
|
326
|
+
def trap( signal, proc = nil, &block )
|
|
327
|
+
return if proc.nil? && !block_given?
|
|
328
|
+
|
|
329
|
+
unless @signal_traps.has_key?( signal )
|
|
330
|
+
set_trap( signal )
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
@signal_traps[signal].unshift( proc || block )
|
|
334
|
+
end
|
|
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
|
+
|
|
343
|
+
def pid_file
|
|
344
|
+
@pid_file ||= "#{File.dirname(self.log_path)}/#{self.daemon_name}.pid"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
protected
|
|
348
|
+
|
|
349
|
+
def run_traps( signal )
|
|
350
|
+
DaemonKit.logger.info "Running signal traps for #{signal}"
|
|
351
|
+
self.signal_traps[ signal ].each { |trap| trap.call }
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
private
|
|
355
|
+
|
|
356
|
+
def set_trap( signal )
|
|
357
|
+
DaemonKit.logger.info "Setting up trap for #{signal}"
|
|
358
|
+
@signal_traps[ signal ] = []
|
|
359
|
+
Signal.trap( signal, Proc.new { self.run_traps( signal ) } )
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def parse_arguments!
|
|
363
|
+
return unless own_args?
|
|
364
|
+
|
|
365
|
+
configs = Arguments.configuration( ARGV ).first
|
|
366
|
+
@unused_arguments = {}
|
|
367
|
+
|
|
368
|
+
configs.each do |c|
|
|
369
|
+
k,v = c.split('=')
|
|
370
|
+
|
|
371
|
+
if v.nil?
|
|
372
|
+
error( "#{k} has no value" )
|
|
373
|
+
next
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
begin
|
|
377
|
+
if self.respond_to?( k )
|
|
378
|
+
self.send( "#{k}=", v ) # pid_file = /var/run/foo.pid
|
|
379
|
+
else
|
|
380
|
+
@unused_arguments[ k ] = v
|
|
381
|
+
end
|
|
382
|
+
rescue => e
|
|
383
|
+
error( "Couldn't set `#{k}' to `#{v}': #{e.message}" )
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# DANGEROUS: Change the value of DAEMON_ENV
|
|
389
|
+
def environment=( env )
|
|
390
|
+
::DAEMON_ENV.replace( env )
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def set_root_path!
|
|
394
|
+
raise "DAEMON_ROOT is not set" unless defined?(::DAEMON_ROOT)
|
|
395
|
+
raise "DAEMON_ROOT is not a directory" unless File.directory?(::DAEMON_ROOT)
|
|
396
|
+
|
|
397
|
+
@root_path = ::DAEMON_ROOT.to_absolute_path
|
|
398
|
+
|
|
399
|
+
Object.const_set(:RELATIVE_DAEMON_ROOT, ::DAEMON_ROOT.dup) unless defined?(::RELATIVE_DAEMON_ROOT)
|
|
400
|
+
::DAEMON_ROOT.replace @root_path
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def set_daemon_defaults!
|
|
404
|
+
self.log_stdout = false
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def default_load_paths
|
|
408
|
+
[ 'lib' ]
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def default_log_path
|
|
412
|
+
File.join(root_path, 'log', "#{environment}.log")
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def default_log_level
|
|
416
|
+
environment == 'production' ? :info : :debug
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def error( msg )
|
|
420
|
+
msg = "[E] Configuration: #{msg}"
|
|
421
|
+
|
|
422
|
+
if DaemonKit.logger
|
|
423
|
+
DaemonKit.logger.error( msg )
|
|
424
|
+
else
|
|
425
|
+
STDERR.puts msg
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# If we are executed with any of these commands, don't allow
|
|
430
|
+
# arguments to be parsed cause they will interfere with the
|
|
431
|
+
# script encapsulating DaemonKit, like capistrano
|
|
432
|
+
def own_args?
|
|
433
|
+
![ 'cap' ].include?( File.basename( $0 ) )
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
end
|