fiveruns-dash-sensor 0.8.3 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
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)