fiveruns-dash-sensor 0.8.3 → 0.8.4

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/README.rdoc CHANGED
@@ -16,23 +16,20 @@ gem install fiveruns-dash-sensor -s http://gems.github.com
16
16
 
17
17
  == Configuration
18
18
 
19
- When you run 'fiveruns-dash-sensor start' for the first time, it will create a blank
19
+ When you run 'fiveruns-dash-sensor' for the first time, it will create a blank
20
20
  configuration for you in ~/.fiveruns-dash-sensor/config.rb. You will need to edit
21
21
  this file with your app token and activate the plugins for your environment. Once
22
- done, you should be able to run 'fiveruns-dash-sensor run' for a few minutes to verify
22
+ done, you should be able to restart 'fiveruns-dash-sensor' for a few minutes to verify
23
23
  everything is working as designed. If there are no problems, you can run
24
- 'fiveruns-dash-sensor start' to spin off the daemon into the background and forget about it.
24
+ 'fiveruns-dash-sensor -d' to spin off the daemon into the background and forget about it.
25
25
 
26
26
  == Command Summary
27
27
 
28
- 'fiveruns-dash-sensor run' - start the daemon in the foreground, for debugging purposes.
29
- 'fiveruns-dash-sensor start' - start the daemon in the background, output and PID file goes
30
- in the ~/.fiveruns-dash-sensor/log/ directory.
31
- 'fiveruns-dash-sensor stop' - stop the daemon in the background
28
+ fiveruns-dash-sensor --help
32
29
 
33
- You can run Sensor in verbose mode to get more debug output:
34
-
35
- 'fiveruns-dash-sensor run -- -v'
30
+ Sensor allows you to control OS-specific parameters like log file and PID file location,
31
+ the user and group to run as, etc. -d will start Sensor as a daemon. To stop it, just
32
+ kill the associated PID.
36
33
 
37
34
  == Creating your own Plugins
38
35
 
@@ -1,42 +1,26 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
2
 
4
- unless File.exist?("#{ENV['HOME']}/.fiveruns-dash-sensor/config.rb")
5
- local = File.join(File.dirname(__FILE__), "..", "config.rb")
6
- dir = File.expand_path("~/.fiveruns-dash-sensor")
7
- if File.exist?(local)
8
- require 'fileutils'
9
- FileUtils.mkdir_p dir
10
- FileUtils.cp local, dir
11
- puts ""
12
- puts "Configuration Required"
13
- puts ""
14
- puts "A blank configuration file has been placed in ~/.fiveruns-dash-sensor/config.rb"
15
- puts "Please edit this file to add your application token and configure the plugins"
16
- puts "appropriate to this environment."
17
- puts ""
18
- end
19
- exit
3
+ begin
4
+ require 'runner'
5
+ rescue LoadError
6
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ require 'runner'
20
8
  end
21
9
 
22
- root = File.expand_path("~/.fiveruns-dash-sensor")
23
- log_dir = File.join(root, "log")
24
- unless File.exist? log_dir
25
- require 'fileutils'
26
- FileUtils.mkdir_p log_dir
27
- end
28
-
29
- gem 'daemons'
30
- require 'daemons'
31
-
32
- options = {
33
- :app_name => "fiveruns-dash-sensor",
34
- :dir_mode => :normal,
35
- :dir => log_dir,
36
- :multiple => true,
37
- :backtrace => true,
38
- :monitor => false,
39
- :log_output => true
10
+ app_options = {
11
+ :config_file => File.expand_path('~/.fiveruns-dash-sensor/config.rb')
40
12
  }
41
13
 
42
- Daemons.run(File.join(File.dirname(__FILE__), '..', 'lib', 'daemon.rb'), options)
14
+ app = Dash::Runner.new
15
+ app.parse do |opts|
16
+ opts.on("-c CONFIG", "--config FILE", "Select config file [default: #{app_options[:config_file]}]") do |v|
17
+ app_options[:config_file] = v
18
+ end
19
+ end
20
+ app.start do |options|
21
+ require "sensor"
22
+
23
+ all_opts = app_options.merge(options)
24
+ engine = Dash::Sensor::Engine.new(all_opts)
25
+ engine.start(all_opts)
26
+ end
data/lib/runner.rb ADDED
@@ -0,0 +1,230 @@
1
+ require 'optparse'
2
+ require 'fileutils'
3
+ require 'logger'
4
+
5
+ # Handles daemonizing of Ruby code
6
+ module Dash
7
+ class Runner
8
+
9
+ attr_accessor :options
10
+ private :options, :options=
11
+
12
+ def self.shutdown
13
+ @@instance.shutdown
14
+ end
15
+
16
+ def initialize
17
+ @app_name = File.basename($0)
18
+ @@instance = self
19
+ end
20
+
21
+ def parse(&block)
22
+ self.options = {
23
+ :log_level => Logger::INFO,
24
+ :daemonize => false,
25
+ :pid_file => File.join('/var', 'run', "#{@app_name}.pid"),
26
+ :log_file => File.join('/var', 'log', "#{@app_name}.log"),
27
+ }
28
+
29
+ OptionParser.new do |opts|
30
+ opts.summary_width = 25
31
+
32
+ opts.banner = "usage: #{@app_name} [options...]\n",
33
+ " #{@app_name} --help\n"
34
+
35
+ opts.separator ""; opts.separator "Process:"
36
+
37
+ opts.on("-d", "Run as a daemon.") do
38
+ options[:daemonize] = true
39
+ end
40
+
41
+ opts.on("--pid FILENAME", "save PID in FILENAME when using -d option.", "(default: #{options[:pid_file]})") do |pid_file|
42
+ options[:pid_file] = File.expand_path(pid_file)
43
+ end
44
+
45
+ opts.on("--user USER", "User to run as") do |user|
46
+ options[:user] = user.to_i == 0 ? Etc.getpwnam(user).uid : user.to_i
47
+ end
48
+
49
+ opts.on("--group GROUP", "Group to run as") do |group|
50
+ options[:group] = group.to_i == 0 ? Etc.getgrnam(group).gid : group.to_i
51
+ end
52
+
53
+ opts.separator ""; opts.separator "Logging:"
54
+
55
+ opts.on("-L", "--log [FILE]", "Path to print debugging information.") do |log_path|
56
+ options[:logger] = File.expand_path(log_path)
57
+ end
58
+
59
+ opts.on("-l", "--syslog CHANNEL", "Write logs to the syslog instead of a log file.") do |channel|
60
+ begin
61
+ require 'syslog_logger'
62
+ rescue LoadError => e
63
+ require 'rubygems'
64
+ require 'syslog_logger'
65
+ end
66
+ options[:syslog_channel] = channel
67
+ end
68
+
69
+ opts.on("-v", "Increase logging verbosity (may be used multiple times).") do
70
+ options[:log_level] -= 1
71
+ end
72
+
73
+ opts.separator ""; opts.separator "Miscellaneous:"
74
+
75
+ opts.on_tail("-?", "--help", "Display this usage information.") do
76
+ puts "#{opts}\n"
77
+ exit
78
+ end
79
+
80
+ # application-specific parameters
81
+ yield opts if block_given?
82
+
83
+ end.parse!
84
+ end
85
+
86
+ def start
87
+ raise ArgumentError, "Please pass an application block to execute" unless block_given?
88
+
89
+ @process = ProcessHelper.new(options)
90
+ pid = @process.running?
91
+ if pid
92
+ STDERR.puts "There is already a #{@app_name} process running (pid #{pid}), exiting."
93
+ exit(1)
94
+ elsif pid.nil?
95
+ STDERR.puts "Cleaning up stale pidfile at #{options[:pid_file]}."
96
+ end
97
+
98
+ drop_privileges
99
+
100
+ @process.daemonize if options[:daemonize]
101
+
102
+ setup_signal_traps
103
+ @process.write_pid_file if options[:daemonize]
104
+
105
+ yield options
106
+ # never reached
107
+ end
108
+
109
+ def drop_privileges
110
+ Process.egid = options[:group] if options[:group]
111
+ Process.euid = options[:user] if options[:user]
112
+ end
113
+
114
+ def shutdown
115
+ STDOUT.puts "Shutting down."
116
+ @process.remove_pid_file
117
+ exit(0)
118
+ end
119
+
120
+ def setup_signal_traps
121
+ Signal.trap("INT") { shutdown }
122
+ Signal.trap("TERM") { shutdown }
123
+ end
124
+ end
125
+
126
+ class ProcessHelper
127
+
128
+ def initialize(options)
129
+ @log_file = options[:logger]
130
+ @pid_file = options[:pid_file]
131
+ @user = options[:user]
132
+ @group = options[:group]
133
+ end
134
+
135
+ def safefork
136
+ begin
137
+ if pid = fork
138
+ return pid
139
+ end
140
+ rescue Errno::EWOULDBLOCK
141
+ sleep 5
142
+ retry
143
+ end
144
+ end
145
+
146
+ def daemonize
147
+ sess_id = detach_from_terminal
148
+ exit if pid = safefork
149
+
150
+ Dir.chdir("/")
151
+ File.umask 0000
152
+
153
+ close_io_handles
154
+ redirect_io
155
+
156
+ return sess_id
157
+ end
158
+
159
+ def detach_from_terminal
160
+ srand
161
+ safefork and exit
162
+
163
+ unless sess_id = Process.setsid
164
+ raise "Couldn't detach from controlling terminal."
165
+ end
166
+
167
+ trap 'SIGHUP', 'IGNORE'
168
+
169
+ sess_id
170
+ end
171
+
172
+ def close_io_handles
173
+ ObjectSpace.each_object(IO) do |io|
174
+ unless [STDIN, STDOUT, STDERR].include?(io)
175
+ begin
176
+ io.close unless io.closed?
177
+ rescue Exception
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def redirect_io
184
+ begin; STDIN.reopen('/dev/null'); rescue Exception; end
185
+
186
+ if @log_file
187
+ begin
188
+ STDOUT.reopen(@log_file, "a")
189
+ STDOUT.sync = true
190
+ rescue Exception
191
+ begin; STDOUT.reopen('/dev/null'); rescue Exception; end
192
+ end
193
+ else
194
+ begin; STDOUT.reopen('/dev/null'); rescue Exception; end
195
+ end
196
+
197
+ begin; STDERR.reopen(STDOUT); rescue Exception; end
198
+ STDERR.sync = true
199
+ end
200
+
201
+ def write_pid_file
202
+ return unless @pid_file
203
+ FileUtils.mkdir_p(File.dirname(@pid_file))
204
+ File.open(@pid_file, "w") { |f| f.write(Process.pid) }
205
+ File.chmod(0644, @pid_file)
206
+ end
207
+
208
+ def remove_pid_file
209
+ return unless @pid_file
210
+ File.unlink(@pid_file) if File.exists?(@pid_file)
211
+ end
212
+
213
+ def running?
214
+ return false unless @pid_file
215
+
216
+ pid = File.read(@pid_file).chomp.to_i rescue nil
217
+ pid = nil if pid == 0
218
+ return false unless pid
219
+
220
+ begin
221
+ Process.kill(0, pid)
222
+ return pid
223
+ rescue Errno::ESRCH
224
+ return nil
225
+ rescue Errno::EPERM
226
+ return pid
227
+ end
228
+ end
229
+ end
230
+ end
data/lib/sensor.rb CHANGED
@@ -1,43 +1,75 @@
1
+ require 'rubygems'
1
2
  require 'fiveruns/dash'
2
3
  require 'sensor_plugin'
3
4
 
5
+ def logger
6
+ Dash::Sensor::Engine.logger
7
+ end
8
+
4
9
  module Dash
5
10
  module Sensor
6
11
 
7
12
  RECIPES = []
8
13
 
9
14
  class Engine
15
+
16
+ def initialize(options)
17
+ if options[:syslog_channel]
18
+ begin
19
+ require 'syslog_logger'
20
+ @@logger = SyslogLogger.new(options[:syslog_channel])
21
+ rescue LoadError
22
+ # SyslogLogger isn't available, so we're just going to use Logger
23
+ end
24
+ end
10
25
 
26
+ @@logger ||= case options[:logger]
27
+ when IO, String
28
+ Logger.new(options[:logger])
29
+ else
30
+ Logger.new(STDERR)
31
+ end
32
+ @@logger.level = options[:log_level]
33
+ end
34
+
35
+ def self.logger
36
+ @@logger
37
+ end
38
+
11
39
  def self.registered(name, url, klass)
12
40
  klass.instance = klass.new
13
41
  RECIPES << [name, url, klass]
14
42
  end
15
43
 
16
44
  def start(options)
17
- load_plugins(options)
45
+ logger.info("Starting Dash Sensor [#{$$}] at #{Time.now}")
46
+ logger.info("Options: #{options.inspect}")
47
+ logger.debug("Debugging enabled")
48
+
49
+ load_plugins_from options[:config_file]
18
50
  wait_forever
19
51
  end
20
52
 
21
53
  private
22
-
23
- def load_plugins(options)
54
+
55
+ def load_plugins_from(file)
24
56
  # Add the user's config directory to the load path so they can load custom plugins
25
57
  # without modifying this gem.
26
58
  $PLUGIN_PATH = [
27
- File.dirname(options.config_file),
59
+ File.dirname(file),
28
60
  File.expand_path(File.join(File.dirname(__FILE__), '..', 'plugins')),
29
61
  nil
30
62
  ]
31
63
 
32
64
  setup = Setup.new
33
- config = File.read(options.config_file)
34
- setup.instance_eval(config, options.config_file)
65
+ config = File.read(file)
66
+ setup.instance_eval(config, file)
35
67
 
36
68
  start_dash(setup)
37
69
  end
38
70
 
39
71
  def start_dash(setup)
40
- LOG.info("Configured Dash Sensor for #{setup.name} [#{setup.token}]")
72
+ logger.info("Configured Dash Sensor for #{setup.name} [#{setup.token}]")
41
73
  Fiveruns::Dash.configure :app => setup.token do |config|
42
74
  RECIPES.each do |(name, url, _)|
43
75
  config.add_recipe name.to_sym, url
@@ -64,13 +96,13 @@ module Dash
64
96
  end
65
97
 
66
98
  def plugin(name, options={})
67
- LOG.info("Loading plugin #{name} with options #{options.inspect}")
99
+ logger.info("Loading plugin #{name} with options #{options.inspect}")
68
100
 
69
101
  $PLUGIN_PATH.each do |path|
70
102
  raise ArgumentError, "Unable to find #{name}.rb in plugin path: #{$PLUGIN_PATH[0..-2].inspect}" unless path
71
103
  file = File.join(path, name)
72
104
  if File.exist?("#{file}.rb")
73
- LOG.debug "Loading #{file}"
105
+ logger.debug "Loading #{file}"
74
106
  require file
75
107
  break
76
108
  end
data/plugins/apache.rb CHANGED
@@ -30,7 +30,7 @@ module Dash::Sensor::Plugins
30
30
  def self.stats
31
31
  if !@time || @time < Time.now - 55
32
32
  @old_stats = @stats || Hash.new(0)
33
- LOG.debug "Fetching status at #{Time.now}"
33
+ logger.debug "Fetching status at #{Time.now}"
34
34
  @stats = instance.send(:stats_data)
35
35
  @time = Time.now
36
36
  end
@@ -46,11 +46,11 @@ module Dash::Sensor::Plugins
46
46
  lines = open(@url).read.split("\n")
47
47
  Hash[*lines.map {|line| line.split(':') }.flatten]
48
48
  rescue => e
49
- LOG.error "Error contacting #{@url}"
50
- LOG.error "#{e.class.name}: #{e.message}"
49
+ logger.error "Error contacting #{@url}"
50
+ logger.error "#{e.class.name}: #{e.message}"
51
51
  Hash.new(0)
52
52
  end
53
53
  end
54
54
 
55
55
  end
56
- end
56
+ end
data/plugins/memcached.rb CHANGED
@@ -23,7 +23,7 @@ module Dash::Sensor::Plugins
23
23
 
24
24
  def self.stats
25
25
  if !@time || @time < Time.now - 55
26
- LOG.debug "Fetching stats at #{Time.now}"
26
+ logger.debug "Fetching stats at #{Time.now}"
27
27
  @stats = parse(instance.send(:stats_data))
28
28
  @time = Time.now
29
29
  end
@@ -47,8 +47,8 @@ module Dash::Sensor::Plugins
47
47
  end
48
48
  sock.close
49
49
  rescue Exception => e
50
- LOG.error "Error contacting Starling at #{@host}:#{@port}"
51
- LOG.error "#{e.class.name}: #{e.message}"
50
+ logger.error "Error contacting Starling at #{@host}:#{@port}"
51
+ logger.error "#{e.class.name}: #{e.message}"
52
52
  end
53
53
  data
54
54
  end
@@ -62,4 +62,4 @@ module Dash::Sensor::Plugins
62
62
  end
63
63
 
64
64
  end
65
- end
65
+ end
data/plugins/nginx.rb CHANGED
@@ -27,7 +27,7 @@ module Dash::Sensor::Plugins
27
27
  def self.stats
28
28
  if !@time || @time < Time.now - 55
29
29
  @old_stats = @stats || Hash.new(0)
30
- LOG.debug "Fetching status at #{Time.now}"
30
+ logger.debug "Fetching status at #{Time.now}"
31
31
  @stats = instance.send(:stats_data)
32
32
  @time = Time.now
33
33
  end
@@ -52,11 +52,11 @@ module Dash::Sensor::Plugins
52
52
  end
53
53
  results
54
54
  rescue Exception => e
55
- LOG.error "Error contacting #{@url}"
56
- LOG.error "#{e.class.name}: #{e.message}"
55
+ logger.error "Error contacting #{@url}"
56
+ logger.error "#{e.class.name}: #{e.message}"
57
57
  Hash.new(0)
58
58
  end
59
59
  end
60
60
 
61
61
  end
62
- end
62
+ end
data/plugins/starling.rb CHANGED
@@ -22,7 +22,7 @@ module Dash::Sensor::Plugins
22
22
 
23
23
  def self.stats
24
24
  if !@time || @time < Time.now - 55
25
- LOG.debug "Fetching stats at #{Time.now}"
25
+ logger.debug "Fetching stats at #{Time.now}"
26
26
  @stats = parse(instance.send(:stats_data))
27
27
  @time = Time.now
28
28
  end
@@ -46,8 +46,8 @@ module Dash::Sensor::Plugins
46
46
  end
47
47
  sock.close
48
48
  rescue Exception => e
49
- LOG.error "Error contacting Starling at #{@host}:#{@port}"
50
- LOG.error "#{e.class.name}: #{e.message}"
49
+ logger.error "Error contacting Starling at #{@host}:#{@port}"
50
+ logger.error "#{e.class.name}: #{e.message}"
51
51
  end
52
52
  data
53
53
  end
@@ -61,4 +61,4 @@ module Dash::Sensor::Plugins
61
61
  end
62
62
 
63
63
  end
64
- end
64
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fiveruns-dash-sensor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - FiveRuns
@@ -21,11 +21,10 @@ extensions: []
21
21
 
22
22
  extra_rdoc_files:
23
23
  - README.rdoc
24
- - History.rdoc
25
24
  files:
26
25
  - bin/fiveruns-dash-sensor
27
26
  - config.rb
28
- - lib/daemon.rb
27
+ - lib/runner.rb
29
28
  - lib/sensor.rb
30
29
  - lib/sensor_plugin.rb
31
30
  - plugins/memcached.rb
@@ -33,7 +32,6 @@ files:
33
32
  - plugins/nginx.rb
34
33
  - plugins/apache.rb
35
34
  - README.rdoc
36
- - History.rdoc
37
35
  has_rdoc: false
38
36
  homepage: http://github.com/fiveruns/dash-sensor/
39
37
  post_install_message:
data/History.rdoc DELETED
@@ -1,5 +0,0 @@
1
- = Changelog
2
-
3
- == 0.8.0 (2009-01-07)
4
-
5
- * Initial internal release.
data/lib/daemon.rb DELETED
@@ -1,46 +0,0 @@
1
- require 'ostruct'
2
- require 'optparse'
3
-
4
- # Sent by daemons when you run '<script> stop'
5
- Signal.trap('TERM') { puts "fiveruns-dash-sensor PID #{$$} exiting at #{Time.now}..."; exit(0) }
6
- # Sent by daemons when you hit Ctrl-C after '<script> run'
7
- Signal.trap('INT') { puts "fiveruns-dash-sensor terminated at #{Time.now}..."; exit(0) }
8
-
9
- options = OpenStruct.new
10
- options.environment = ENV['RAILS_ENV'] || 'production'
11
- options.verbose = false
12
- options.config_file = "#{ENV['HOME']}/.fiveruns-dash-sensor/config.rb"
13
-
14
- op = OptionParser.new do |opts|
15
- opts.banner = "Usage: fiveruns-dash-sensor [options]"
16
- opts.separator "General Options:"
17
- opts.on("-e ENVIRONMENT", "--environment NAME", "Select environment [default: #{options.environment}]") do |v|
18
- options.environment = v
19
- end
20
- opts.on("-c CONFIG", "--config FILE", "Select config file [default: #{options.config_file}]") do |v|
21
- options.config_file = v
22
- end
23
- opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
24
- options.verbose = v
25
- end
26
- opts.separator "Other Options:"
27
- opts.on("-h", "--help", "Display this message") do |v|
28
- STDERR.puts opts
29
- exit
30
- end
31
- end
32
- op.parse!(ARGV)
33
-
34
- unless File.exist? options.config_file
35
- puts "Please create a configuration file for your environment in ~/.fiveruns-dash-sensor/config.rb"
36
- exit(1)
37
- end
38
-
39
- require 'logger'
40
- LOG = Logger.new(STDOUT)
41
- LOG.level = options.verbose ? Logger::DEBUG : Logger::INFO
42
-
43
- $LOAD_PATH.unshift File.dirname(__FILE__)
44
- require "sensor"
45
- LOG.info("Starting Dash Sensor [#{$$}] at #{Time.now}")
46
- Dash::Sensor::Engine.new.start(options)