scout 5.3.5 → 5.4.4.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/CHANGELOG +0 -12
- data/Gemfile +4 -0
- data/README +8 -0
- data/Rakefile +6 -108
- data/bin/scout +1 -0
- data/lib/scout.rb +5 -4
- data/lib/scout/command.rb +11 -12
- data/lib/scout/command/install.rb +1 -1
- data/lib/scout/command/run.rb +13 -1
- data/lib/scout/command/sign.rb +2 -8
- data/lib/scout/command/stream.rb +50 -0
- data/lib/scout/command/test.rb +1 -1
- data/lib/scout/daemon_spawn.rb +215 -0
- data/lib/scout/plugin.rb +20 -1
- data/lib/scout/server.rb +16 -111
- data/lib/scout/server_base.rb +100 -0
- data/lib/scout/streamer.rb +162 -0
- data/lib/scout/streamer_control.rb +43 -0
- data/lib/scout/version.rb +3 -0
- data/scout.gemspec +27 -0
- data/test/plugins/disk_usage.rb +86 -0
- data/test/scout_test.rb +598 -0
- data/vendor/pusher-gem/Gemfile +2 -0
- data/vendor/pusher-gem/LICENSE +20 -0
- data/vendor/pusher-gem/README.md +80 -0
- data/vendor/pusher-gem/Rakefile +11 -0
- data/vendor/pusher-gem/examples/async_message.rb +28 -0
- data/vendor/pusher-gem/lib/pusher.rb +107 -0
- data/vendor/pusher-gem/lib/pusher/channel.rb +154 -0
- data/vendor/pusher-gem/lib/pusher/request.rb +107 -0
- data/vendor/pusher-gem/pusher.gemspec +28 -0
- data/vendor/pusher-gem/spec/channel_spec.rb +274 -0
- data/vendor/pusher-gem/spec/pusher_spec.rb +87 -0
- data/vendor/pusher-gem/spec/spec_helper.rb +13 -0
- data/vendor/ruby-hmac/History.txt +15 -0
- data/vendor/ruby-hmac/Manifest.txt +11 -0
- data/vendor/ruby-hmac/README.md +41 -0
- data/vendor/ruby-hmac/Rakefile +23 -0
- data/vendor/ruby-hmac/lib/hmac-md5.rb +11 -0
- data/vendor/ruby-hmac/lib/hmac-rmd160.rb +11 -0
- data/vendor/ruby-hmac/lib/hmac-sha1.rb +11 -0
- data/vendor/ruby-hmac/lib/hmac-sha2.rb +25 -0
- data/vendor/ruby-hmac/lib/hmac.rb +118 -0
- data/vendor/ruby-hmac/lib/ruby_hmac.rb +2 -0
- data/vendor/ruby-hmac/ruby-hmac.gemspec +33 -0
- data/vendor/ruby-hmac/test/test_hmac.rb +89 -0
- data/vendor/signature/.document +5 -0
- data/vendor/signature/.gitignore +21 -0
- data/vendor/signature/Gemfile +3 -0
- data/vendor/signature/Gemfile.lock +29 -0
- data/vendor/signature/LICENSE +20 -0
- data/vendor/signature/README.md +55 -0
- data/vendor/signature/Rakefile +2 -0
- data/vendor/signature/VERSION +1 -0
- data/vendor/signature/lib/signature.rb +142 -0
- data/vendor/signature/lib/signature/version.rb +3 -0
- data/vendor/signature/signature.gemspec +22 -0
- data/vendor/signature/spec/signature_spec.rb +176 -0
- data/vendor/signature/spec/spec_helper.rb +10 -0
- data/vendor/util/lib/core_extensions.rb +60 -0
- metadata +120 -84
- data/AUTHORS +0 -4
- data/COPYING +0 -340
- data/INSTALL +0 -18
- data/TODO +0 -6
data/.gitignore
ADDED
data/CHANGELOG
CHANGED
@@ -1,15 +1,3 @@
|
|
1
|
-
== Master
|
2
|
-
|
3
|
-
== 5.3.5
|
4
|
-
|
5
|
-
* Moved proxy support to explicit command line flags --http_proxy and https_proxy
|
6
|
-
* fixed two unused variables that were causing warnings under 1.9.3
|
7
|
-
|
8
|
-
== 5.3.4
|
9
|
-
|
10
|
-
* Incorporating sleep interval into Server#time_to_checkin?
|
11
|
-
* Added proxy support command line flags
|
12
|
-
|
13
1
|
== 5.3.3
|
14
2
|
|
15
3
|
* Sending embedded options to server for local & plugin overrides.
|
data/Gemfile
ADDED
data/README
CHANGED
@@ -64,3 +64,11 @@ It's often helpful to log the output to a file. To do so:
|
|
64
64
|
|
65
65
|
|
66
66
|
For additional help, please visit http://scoutapp.com
|
67
|
+
|
68
|
+
== Credits / Contact
|
69
|
+
|
70
|
+
Contact support@scoutapp.com with questions.
|
71
|
+
|
72
|
+
Primary maintainers: Andre Lewis (andre@scoutapp.com) and Derek Haynes (derek@scoutapp.com)
|
73
|
+
|
74
|
+
Many thanks to James Edward Gray II, Charles Brian Quinn, and Matt Todd for early work on the Scout agent!
|
data/Rakefile
CHANGED
@@ -1,109 +1,7 @@
|
|
1
|
-
require "
|
2
|
-
require "rake/testtask"
|
3
|
-
require "rake/gempackagetask"
|
4
|
-
require "rake/contrib/rubyforgepublisher"
|
5
|
-
require "net/ssh"
|
1
|
+
require "bundler/gem_tasks"
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
|
13
|
-
history = File.read("CHANGELOG").split(/^(===.*)/)
|
14
|
-
changes ||= history[0..2].join.strip
|
15
|
-
|
16
|
-
need_tar = true
|
17
|
-
need_zip = true
|
18
|
-
|
19
|
-
task :default => [:test]
|
20
|
-
|
21
|
-
Rake::TestTask.new do |test|
|
22
|
-
test.libs << "test"
|
23
|
-
test.test_files = [ "test/scout_test.rb" ]
|
24
|
-
test.verbose = true
|
25
|
-
end
|
26
|
-
|
27
|
-
Rake::RDocTask.new do |rdoc|
|
28
|
-
rdoc.main = "README"
|
29
|
-
rdoc.rdoc_dir = "doc/html"
|
30
|
-
rdoc.title = "Scout Client Documentation"
|
31
|
-
rdoc.rdoc_files.include( "README", "INSTALL",
|
32
|
-
"TODO", "CHANGELOG",
|
33
|
-
"AUTHORS", "COPYING",
|
34
|
-
"LICENSE", "lib/" )
|
35
|
-
end
|
36
|
-
|
37
|
-
spec = Gem::Specification.new do |spec|
|
38
|
-
spec.name = "scout"
|
39
|
-
spec.version = version
|
40
|
-
|
41
|
-
spec.platform = Gem::Platform::RUBY
|
42
|
-
spec.summary = "Scout makes monitoring and reporting on your web applications as flexible and simple as possible."
|
43
|
-
|
44
|
-
# TODO: test suite
|
45
|
-
# spec.test_suite_file = "test/ts_all.rb"
|
46
|
-
spec.files = Dir.glob("{bin,lib}/**/*.rb") +
|
47
|
-
Dir.glob("{data,vendor}/**/*") +
|
48
|
-
%w[Rakefile]
|
49
|
-
spec.executables = ["scout"]
|
50
|
-
|
51
|
-
spec.has_rdoc = true
|
52
|
-
spec.extra_rdoc_files = %w[ AUTHORS COPYING README INSTALL TODO CHANGELOG
|
53
|
-
LICENSE ]
|
54
|
-
spec.rdoc_options << "--title" << "Scout Client Documentation" <<
|
55
|
-
"--main" << "README"
|
56
|
-
|
57
|
-
spec.require_path = "lib"
|
58
|
-
|
59
|
-
spec.add_dependency "elif"
|
60
|
-
|
61
|
-
spec.author = "Scout Monitoring"
|
62
|
-
spec.email = "support@scoutapp.com"
|
63
|
-
spec.rubyforge_project = "scout"
|
64
|
-
spec.homepage = "http://scoutapp.com"
|
65
|
-
spec.description = <<END_DESC
|
66
|
-
Scout makes monitoring and reporting on your web applications as flexible and simple as possible.
|
67
|
-
END_DESC
|
68
|
-
end
|
69
|
-
|
70
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
71
|
-
pkg.need_zip = need_tar
|
72
|
-
pkg.need_tar = need_zip
|
73
|
-
end
|
74
|
-
|
75
|
-
desc "Publishes to Scout Gem Server and Rubyforge"
|
76
|
-
task :publish => [:package, :publish_rubyforge]
|
77
|
-
|
78
|
-
desc "Publishes Gem to Rubyforge"
|
79
|
-
task :publish_rubyforge => [:package] do
|
80
|
-
puts "Publishing on RubyForge"
|
81
|
-
forge = RubyForge.new
|
82
|
-
forge.configure
|
83
|
-
puts "Logging in"
|
84
|
-
forge.login
|
85
|
-
|
86
|
-
release = forge.userconfig
|
87
|
-
release["release_changes"] = File.read(File.join(dir, "CHANGELOG"))
|
88
|
-
release["preformatted"] = true
|
89
|
-
|
90
|
-
package = "pkg/#{spec.name}-#{version}"
|
91
|
-
files = %W[#{package}.tgz #{package}.zip #{package}.gem].compact
|
92
|
-
|
93
|
-
puts "Releasing #{spec.name}-#{version}"
|
94
|
-
forge.add_release(spec.rubyforge_project, spec.name, version, *files)
|
95
|
-
end
|
96
|
-
|
97
|
-
desc "Upload current documentation to Scout Gem Server and RubyForge"
|
98
|
-
task :upload_docs => [:rdoc] do
|
99
|
-
sh "scp -r doc/html/* " +
|
100
|
-
"deploy@gems.scoutapp.com:/var/www/gems/docs"
|
101
|
-
|
102
|
-
config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
103
|
-
host = "#{config["username"]}@rubyforge.org"
|
104
|
-
|
105
|
-
remote_dir = "/var/www/gforge-projects/#{spec.rubyforge_project}"
|
106
|
-
local_dir = 'doc/html'
|
107
|
-
|
108
|
-
sh %{rsync -av --delete #{local_dir}/ #{host}:#{remote_dir}}
|
109
|
-
end
|
3
|
+
# Provides the Following tasks:
|
4
|
+
#
|
5
|
+
# rake build # Builds gem into the pkg directory
|
6
|
+
# rake install # Build and install the current version into system gems
|
7
|
+
# rake release # Create, tag, build, and publish the current version to rubygems.org
|
data/bin/scout
CHANGED
data/lib/scout.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby -wKU
|
2
|
-
|
3
|
-
module Scout
|
4
|
-
VERSION = "5.3.5".freeze
|
5
|
-
end
|
2
|
+
require "scout/version"
|
6
3
|
|
7
4
|
require "scout/command"
|
8
5
|
require "scout/plugin"
|
9
6
|
require "scout/plugin_options"
|
10
7
|
require "scout/scout_logger"
|
8
|
+
require "scout/server_base"
|
11
9
|
require "scout/server"
|
10
|
+
require "scout/streamer"
|
11
|
+
require "scout/daemon_spawn"
|
12
|
+
require "scout/streamer_control"
|
data/lib/scout/command.rb
CHANGED
@@ -72,16 +72,6 @@ module Scout
|
|
72
72
|
options[:server_name] = server_name
|
73
73
|
end
|
74
74
|
|
75
|
-
opts.on("--http-proxy URL", String,
|
76
|
-
"Optional http proxy for non-SSL traffic." ) do |http_proxy|
|
77
|
-
options[:http_proxy] = http_proxy
|
78
|
-
end
|
79
|
-
|
80
|
-
opts.on("--https-proxy URL", String,
|
81
|
-
"Optional https proxy for SSL traffic." ) do |https_proxy|
|
82
|
-
options[:https_proxy] = https_proxy
|
83
|
-
end
|
84
|
-
|
85
75
|
opts.separator " "
|
86
76
|
opts.separator "Common Options:"
|
87
77
|
opts.separator "--------------------------------------------------------------------------"
|
@@ -105,6 +95,16 @@ module Scout
|
|
105
95
|
options[:force] = bool
|
106
96
|
end
|
107
97
|
|
98
|
+
opts.on( "-p", "--plugin_ids PLUGINS", String,
|
99
|
+
"A subset of plugin ids, for streaming mode only - not generally used manually.") do |plugins|
|
100
|
+
options[:plugin_ids] = plugins.split(",").map(&:to_i)
|
101
|
+
end
|
102
|
+
|
103
|
+
opts.on( "-k", "--streaming_key KEY", String,
|
104
|
+
"for streaming mode only - not generally used manually.") do |streaming_key|
|
105
|
+
options[:streaming_key] = streaming_key
|
106
|
+
end
|
107
|
+
|
108
108
|
opts.separator " "
|
109
109
|
opts.separator "Troubleshooting Options:"
|
110
110
|
opts.separator "--------------------------------------------------------------------------"
|
@@ -164,9 +164,8 @@ module Scout
|
|
164
164
|
@level = options[:level] || "info"
|
165
165
|
@force = options[:force] || false
|
166
166
|
@server_name = options[:server_name]
|
167
|
-
@http_proxy = options[:http_proxy] || ""
|
168
|
-
@https_proxy = options[:https_proxy] || ""
|
169
167
|
|
168
|
+
@options = options
|
170
169
|
@args = args
|
171
170
|
|
172
171
|
# create config dir if necessary
|
@@ -22,7 +22,7 @@ module Scout
|
|
22
22
|
|
23
23
|
puts "\nAttempting to contact the server..."
|
24
24
|
begin
|
25
|
-
Scout::Server.new(server, key, history, log
|
25
|
+
Scout::Server.new(server, key, history, log) do |scout|
|
26
26
|
scout.fetch_plan
|
27
27
|
scout.run_plugins_by_plan
|
28
28
|
end
|
data/lib/scout/command/run.rb
CHANGED
@@ -9,7 +9,7 @@ module Scout
|
|
9
9
|
configuration_directory = config_dir
|
10
10
|
log.debug("Configuration directory is #{configuration_directory} ") if log
|
11
11
|
# TODO: too much external logic of command doing things TO server. This should be moved into the server class.
|
12
|
-
@scout = Scout::Server.new(server, key, history, log, server_name
|
12
|
+
@scout = Scout::Server.new(server, key, history, log, server_name)
|
13
13
|
@scout.load_history
|
14
14
|
|
15
15
|
unless $stdin.tty?
|
@@ -19,6 +19,18 @@ module Scout
|
|
19
19
|
|
20
20
|
@scout.fetch_plan
|
21
21
|
|
22
|
+
log.info "streamer command=#{@scout.streamer_command}"
|
23
|
+
# Spawn streamer if directed to, or stop it. @scout.streamer_command should only be [start|stop]
|
24
|
+
if @scout.streamer_command.is_a?(String) && @scout.streamer_command.start_with?("start") || @scout.streamer_command == "stop"
|
25
|
+
tokens = @scout.streamer_command.split(",")
|
26
|
+
tokens.shift # gets rid of the "start"
|
27
|
+
streaming_key=tokens.shift
|
28
|
+
plugin_ids = tokens.map(&:to_i)
|
29
|
+
stream=Scout::Command::Stream.new(@options.merge(:streaming_key=>streaming_key,:plugin_ids=>plugin_ids), [key, @scout.streamer_command])
|
30
|
+
stream.run
|
31
|
+
end
|
32
|
+
|
33
|
+
# Check in if appropriate
|
22
34
|
if @scout.new_plan || @scout.time_to_checkin? || @force
|
23
35
|
if @scout.new_plan
|
24
36
|
log.info("Now checking in with new plugin plan") if log
|
data/lib/scout/command/sign.rb
CHANGED
@@ -11,7 +11,7 @@ module Scout
|
|
11
11
|
VERIFY_MODE = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
12
12
|
|
13
13
|
def run
|
14
|
-
url = @args
|
14
|
+
url, *provided_options = @args
|
15
15
|
# read the plugin_code from the file specified
|
16
16
|
if url.nil? or url == ''
|
17
17
|
puts "Please specify the path to the plugin (scout sign /path/to/plugin.rb)"
|
@@ -35,13 +35,7 @@ module Scout
|
|
35
35
|
|
36
36
|
puts "Posting Signature..."
|
37
37
|
uri = URI.parse(url)
|
38
|
-
|
39
|
-
# take care of http/https proxy, if specified in command line options
|
40
|
-
# Given a blank string, the proxy_uri URI instance's host/port/user/pass will be nil
|
41
|
-
# Net::HTTP::Proxy returns a regular Net::HTTP class if the first argument (host) is nil
|
42
|
-
proxy_uri = URI.parse(uri.is_a?(URI::HTTPS) ? @https_proxy : @http_proxy)
|
43
|
-
http=Net::HTTP::Proxy(proxy_uri.host,proxy_uri.port,proxy_uri.user,proxy_uri.port).new(uri.host, uri.port)
|
44
|
-
|
38
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
45
39
|
if uri.is_a?(URI::HTTPS)
|
46
40
|
http.use_ssl = true
|
47
41
|
http.ca_file = CA_FILE
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Scout
|
6
|
+
class Command
|
7
|
+
class Stream < Command
|
8
|
+
PID_FILENAME="streamer.pid"
|
9
|
+
|
10
|
+
def run
|
11
|
+
@key = @args[0]
|
12
|
+
daemon_command = @args[1]
|
13
|
+
@plugin_ids = @options[:plugin_ids]
|
14
|
+
|
15
|
+
if !@key
|
16
|
+
puts "usage: scout stream [your_scout_key] [start|stop]"
|
17
|
+
exit(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
# server and history methods are inherited from Scout::Command base class
|
21
|
+
streamer_log_file=File.join(File.dirname(history),"scout_streamer.log")
|
22
|
+
streamer_pid_file=File.join(File.dirname(history),"scout_streamer.pid")
|
23
|
+
|
24
|
+
streamer_control_options = {:log_file => streamer_log_file,
|
25
|
+
:pid_file => streamer_pid_file,
|
26
|
+
:sync_log => true,
|
27
|
+
:working_dir => File.dirname(history)}
|
28
|
+
|
29
|
+
# we use STDOUT for the logger because daemon_spawn directs STDOUT to a log file
|
30
|
+
streamer_control_args = [server, @key, history, @plugin_ids, @options[:streaming_key],Logger.new(STDOUT)]
|
31
|
+
|
32
|
+
if daemon_command.include? "start" # can be 'start' or 'restart'
|
33
|
+
if File.exists?(streamer_pid_file)
|
34
|
+
puts "PID file existed. Restarting ..."
|
35
|
+
Scout::StreamerControl.restart(streamer_control_options,streamer_control_args)
|
36
|
+
else
|
37
|
+
puts "Starting ... "
|
38
|
+
Scout::StreamerControl.start(streamer_control_options,streamer_control_args)
|
39
|
+
end
|
40
|
+
elsif daemon_command == "stop"
|
41
|
+
puts "Stopping ..."
|
42
|
+
Scout::StreamerControl.stop(streamer_control_options,[])
|
43
|
+
else
|
44
|
+
puts "usage: scout stream [your_scout_key] [start|stop]"
|
45
|
+
exit(1)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/scout/command/test.rb
CHANGED
@@ -45,7 +45,7 @@ module Scout
|
|
45
45
|
puts "== You haven't provided any options for running this plugin."
|
46
46
|
end
|
47
47
|
|
48
|
-
Scout::Server.new(nil, nil, history, log
|
48
|
+
Scout::Server.new(nil, nil, history, log) do |scout|
|
49
49
|
scout.prepare_checkin
|
50
50
|
scout.process_plugin( 'interval' => 0,
|
51
51
|
'plugin_id' => 1,
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
# Taken unchanged from https://github.com/alexvollmer/daemon-spawn
|
4
|
+
#
|
5
|
+
# Large portions of this were liberally stolen from the
|
6
|
+
# 'simple-daemon' project at http://simple-daemon.rubyforge.org/
|
7
|
+
module DaemonSpawn
|
8
|
+
VERSION = '0.3.0'
|
9
|
+
|
10
|
+
def self.usage(msg=nil) #:nodoc:
|
11
|
+
print "#{msg}, " if msg
|
12
|
+
puts "usage: #{$0} <command> [options]"
|
13
|
+
puts "Where <command> is one of start, stop, restart or status"
|
14
|
+
puts "[options] are additional options passed to the underlying process"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.alive?(pid)
|
18
|
+
Process.kill 0, pid
|
19
|
+
rescue Errno::ESRCH
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.start(daemon, args) #:nodoc:
|
24
|
+
if !File.writable?(File.dirname(daemon.log_file))
|
25
|
+
STDERR.puts "Unable to write log file to #{daemon.log_file}"
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
if !File.writable?(File.dirname(daemon.pid_file))
|
30
|
+
STDERR.puts "Unable to write PID file to #{daemon.pid_file}"
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
if daemon.alive? && daemon.singleton
|
35
|
+
STDERR.puts "An instance of #{daemon.app_name} is already " +
|
36
|
+
"running (PID #{daemon.pid})"
|
37
|
+
exit 0
|
38
|
+
end
|
39
|
+
|
40
|
+
fork do
|
41
|
+
Process.setsid
|
42
|
+
exit if fork
|
43
|
+
open(daemon.pid_file, 'w') { |f| f << Process.pid }
|
44
|
+
Dir.chdir daemon.working_dir
|
45
|
+
old_umask = File.umask 0000
|
46
|
+
log = File.new(daemon.log_file, "a")
|
47
|
+
File.umask old_umask
|
48
|
+
log.sync = daemon.sync_log
|
49
|
+
STDIN.reopen "/dev/null"
|
50
|
+
STDOUT.reopen log
|
51
|
+
STDERR.reopen STDOUT
|
52
|
+
trap("TERM") {daemon.stop; exit}
|
53
|
+
daemon.start(args)
|
54
|
+
end
|
55
|
+
puts "#{daemon.app_name} started."
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.stop(daemon) #:nodoc:
|
59
|
+
if pid = daemon.pid
|
60
|
+
FileUtils.rm(daemon.pid_file)
|
61
|
+
Process.kill(daemon.signal, pid)
|
62
|
+
begin
|
63
|
+
Process.wait(pid)
|
64
|
+
rescue Errno::ECHILD
|
65
|
+
end
|
66
|
+
if ticks = daemon.timeout
|
67
|
+
while ticks > 0 and alive?(pid) do
|
68
|
+
puts "Process is still alive. #{ticks} seconds until I kill -9 it..."
|
69
|
+
sleep 1
|
70
|
+
ticks -= 1
|
71
|
+
end
|
72
|
+
if alive?(pid)
|
73
|
+
puts "Process didn't quit after timeout of #{daemon.timeout} seconds. Killing..."
|
74
|
+
Process.kill 9, pid
|
75
|
+
end
|
76
|
+
end
|
77
|
+
else
|
78
|
+
puts "PID file not found. Is the daemon started?"
|
79
|
+
end
|
80
|
+
rescue Errno::ESRCH
|
81
|
+
puts "PID file found, but process was not running. The daemon may have died."
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.status(daemon) #:nodoc:
|
85
|
+
puts "#{daemon.app_name} is #{daemon.alive? ? "" : "NOT "}running (PID #{daemon.pid})"
|
86
|
+
end
|
87
|
+
|
88
|
+
class Base
|
89
|
+
attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :app_name, :singleton, :index, :signal, :timeout
|
90
|
+
|
91
|
+
def initialize(opts = {})
|
92
|
+
raise 'You must specify a :working_dir' unless opts[:working_dir]
|
93
|
+
self.working_dir = opts[:working_dir]
|
94
|
+
self.app_name = opts[:application] || classname
|
95
|
+
self.pid_file = opts[:pid_file] || File.join(working_dir, 'tmp', 'pids', app_name + '.pid')
|
96
|
+
self.log_file = opts[:log_file] || File.join(working_dir, 'logs', app_name + '.log')
|
97
|
+
self.signal = opts[:signal] || 'TERM'
|
98
|
+
self.timeout = opts[:timeout]
|
99
|
+
self.index = opts[:index] || 0
|
100
|
+
if self.index > 0
|
101
|
+
self.pid_file += ".#{self.index}"
|
102
|
+
self.log_file += ".#{self.index}"
|
103
|
+
end
|
104
|
+
self.sync_log = opts[:sync_log]
|
105
|
+
self.singleton = opts[:singleton] || false
|
106
|
+
end
|
107
|
+
|
108
|
+
def classname #:nodoc:
|
109
|
+
self.class.to_s.split('::').last
|
110
|
+
end
|
111
|
+
|
112
|
+
# Provide your implementation. These are provided as a reminder
|
113
|
+
# only and will raise an error if invoked. When started, this
|
114
|
+
# method will be invoked with the remaining command-line arguments.
|
115
|
+
def start(args)
|
116
|
+
raise "You must implement a 'start' method in your class!"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Provide your implementation. These are provided as a reminder
|
120
|
+
# only and will raise an error if invoked.
|
121
|
+
def stop
|
122
|
+
raise "You must implement a 'stop' method in your class!"
|
123
|
+
end
|
124
|
+
|
125
|
+
def alive? #:nodoc:
|
126
|
+
if File.file?(pid_file)
|
127
|
+
DaemonSpawn.alive? pid
|
128
|
+
else
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def pid #:nodoc:
|
134
|
+
IO.read(self.pid_file).to_i rescue nil
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.build(options)
|
138
|
+
count = options.delete(:processes) || 1
|
139
|
+
daemons = []
|
140
|
+
count.times do |index|
|
141
|
+
daemons << new(options.merge(:index => index))
|
142
|
+
end
|
143
|
+
daemons
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.find(options)
|
147
|
+
pid_file = new(options).pid_file
|
148
|
+
basename = File.basename(pid_file).split('.').first
|
149
|
+
pid_files = Dir.glob(File.join(File.dirname(pid_file), "#{basename}.*pid*"))
|
150
|
+
pid_files.map { |f| new(options.merge(:pid_file => f)) }
|
151
|
+
end
|
152
|
+
|
153
|
+
# Invoke this method to process command-line args and dispatch
|
154
|
+
# appropriately. Valid options include the following _symbols_:
|
155
|
+
# - <tt>:working_dir</tt> -- the working directory (required)
|
156
|
+
# - <tt>:log_file</tt> -- path to the log file
|
157
|
+
# - <tt>:pid_file</tt> -- path to the pid file
|
158
|
+
# - <tt>:sync_log</tt> -- indicate whether or not to sync log IO
|
159
|
+
# - <tt>:singleton</tt> -- If set to true, only one instance is
|
160
|
+
# allowed to start
|
161
|
+
# args must begin with 'start', 'stop', 'status', or 'restart'.
|
162
|
+
# The first token will be removed and any remaining arguments
|
163
|
+
# passed to the daemon's start method.
|
164
|
+
def self.spawn!(opts = {}, args = ARGV)
|
165
|
+
case args.any? and command = args.shift
|
166
|
+
when 'start', 'stop', 'status', 'restart'
|
167
|
+
send(command, opts, args)
|
168
|
+
when '-h', '--help', 'help'
|
169
|
+
DaemonSpawn.usage
|
170
|
+
exit
|
171
|
+
else
|
172
|
+
DaemonSpawn.usage "Invalid command"
|
173
|
+
exit 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.start(opts, args)
|
178
|
+
living_daemons = find(opts).select { |d| d.alive? }
|
179
|
+
if living_daemons.any?
|
180
|
+
puts "Daemons already started! PIDS: #{living_daemons.map {|d| d.pid}.join(', ')}"
|
181
|
+
exit 1
|
182
|
+
else
|
183
|
+
build(opts).map { |d| DaemonSpawn.start(d, args) }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.stop(opts, args)
|
188
|
+
daemons = find(opts)
|
189
|
+
if daemons.empty?
|
190
|
+
puts "No PID files found. Is the daemon started?"
|
191
|
+
exit 1
|
192
|
+
else
|
193
|
+
daemons.each { |d| DaemonSpawn.stop(d) }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.status(opts, args)
|
198
|
+
daemons = find(opts)
|
199
|
+
if daemons.empty?
|
200
|
+
puts 'No PIDs found'
|
201
|
+
else
|
202
|
+
daemons.each { |d| DaemonSpawn.status(d) }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.restart(opts, args)
|
207
|
+
daemons = build(opts)
|
208
|
+
daemons.map do |daemon|
|
209
|
+
DaemonSpawn.stop(daemon)
|
210
|
+
sleep 0.1
|
211
|
+
DaemonSpawn.start(daemon, args)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|