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.
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