scout 5.3.5 → 5.4.4.alpha

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 (66) hide show
  1. data/.gitignore +6 -0
  2. data/CHANGELOG +0 -12
  3. data/Gemfile +4 -0
  4. data/README +8 -0
  5. data/Rakefile +6 -108
  6. data/bin/scout +1 -0
  7. data/lib/scout.rb +5 -4
  8. data/lib/scout/command.rb +11 -12
  9. data/lib/scout/command/install.rb +1 -1
  10. data/lib/scout/command/run.rb +13 -1
  11. data/lib/scout/command/sign.rb +2 -8
  12. data/lib/scout/command/stream.rb +50 -0
  13. data/lib/scout/command/test.rb +1 -1
  14. data/lib/scout/daemon_spawn.rb +215 -0
  15. data/lib/scout/plugin.rb +20 -1
  16. data/lib/scout/server.rb +16 -111
  17. data/lib/scout/server_base.rb +100 -0
  18. data/lib/scout/streamer.rb +162 -0
  19. data/lib/scout/streamer_control.rb +43 -0
  20. data/lib/scout/version.rb +3 -0
  21. data/scout.gemspec +27 -0
  22. data/test/plugins/disk_usage.rb +86 -0
  23. data/test/scout_test.rb +598 -0
  24. data/vendor/pusher-gem/Gemfile +2 -0
  25. data/vendor/pusher-gem/LICENSE +20 -0
  26. data/vendor/pusher-gem/README.md +80 -0
  27. data/vendor/pusher-gem/Rakefile +11 -0
  28. data/vendor/pusher-gem/examples/async_message.rb +28 -0
  29. data/vendor/pusher-gem/lib/pusher.rb +107 -0
  30. data/vendor/pusher-gem/lib/pusher/channel.rb +154 -0
  31. data/vendor/pusher-gem/lib/pusher/request.rb +107 -0
  32. data/vendor/pusher-gem/pusher.gemspec +28 -0
  33. data/vendor/pusher-gem/spec/channel_spec.rb +274 -0
  34. data/vendor/pusher-gem/spec/pusher_spec.rb +87 -0
  35. data/vendor/pusher-gem/spec/spec_helper.rb +13 -0
  36. data/vendor/ruby-hmac/History.txt +15 -0
  37. data/vendor/ruby-hmac/Manifest.txt +11 -0
  38. data/vendor/ruby-hmac/README.md +41 -0
  39. data/vendor/ruby-hmac/Rakefile +23 -0
  40. data/vendor/ruby-hmac/lib/hmac-md5.rb +11 -0
  41. data/vendor/ruby-hmac/lib/hmac-rmd160.rb +11 -0
  42. data/vendor/ruby-hmac/lib/hmac-sha1.rb +11 -0
  43. data/vendor/ruby-hmac/lib/hmac-sha2.rb +25 -0
  44. data/vendor/ruby-hmac/lib/hmac.rb +118 -0
  45. data/vendor/ruby-hmac/lib/ruby_hmac.rb +2 -0
  46. data/vendor/ruby-hmac/ruby-hmac.gemspec +33 -0
  47. data/vendor/ruby-hmac/test/test_hmac.rb +89 -0
  48. data/vendor/signature/.document +5 -0
  49. data/vendor/signature/.gitignore +21 -0
  50. data/vendor/signature/Gemfile +3 -0
  51. data/vendor/signature/Gemfile.lock +29 -0
  52. data/vendor/signature/LICENSE +20 -0
  53. data/vendor/signature/README.md +55 -0
  54. data/vendor/signature/Rakefile +2 -0
  55. data/vendor/signature/VERSION +1 -0
  56. data/vendor/signature/lib/signature.rb +142 -0
  57. data/vendor/signature/lib/signature/version.rb +3 -0
  58. data/vendor/signature/signature.gemspec +22 -0
  59. data/vendor/signature/spec/signature_spec.rb +176 -0
  60. data/vendor/signature/spec/spec_helper.rb +10 -0
  61. data/vendor/util/lib/core_extensions.rb +60 -0
  62. metadata +120 -84
  63. data/AUTHORS +0 -4
  64. data/COPYING +0 -340
  65. data/INSTALL +0 -18
  66. data/TODO +0 -6
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ doc/*
2
+ pkg/*
3
+ .DS_Store
4
+ **/.DS_Store
5
+ .idea
6
+ working_dir/
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
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in scout.gemspec
4
+ gemspec
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 "rake/rdoctask"
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
- require "rubygems"
8
- require "rubyforge"
9
-
10
- dir = File.dirname(__FILE__)
11
- lib = File.join(dir, "lib", "scout.rb")
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
@@ -5,6 +5,7 @@ $VERBOSE = true # -w
5
5
  #$KCODE = "u" # -Ku
6
6
 
7
7
  $LOAD_PATH << File.join(File.dirname(__FILE__), *%w[.. lib])
8
+
8
9
  require "scout"
9
10
 
10
11
  Scout::Command.dispatch(ARGV)
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, @http_proxy, @https_proxy) do |scout|
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
@@ -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, @http_proxy, @https_proxy)
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
@@ -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.first
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
@@ -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, @http_proxy, @https_proxy) do |scout|
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