evented_bluepill 0.0.47 → 0.0.50

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG.md +14 -0
  2. data/DESIGN.md +2 -2
  3. data/README.md +35 -37
  4. data/Rakefile +9 -0
  5. data/bin/bluepill +9 -102
  6. data/bin/evented_bluepill +13 -0
  7. data/evented_bluepill.gemspec +10 -6
  8. data/{lib → example}/example.rb +4 -7
  9. data/{lib → example}/runit_example.rb +2 -2
  10. data/{bin → example}/sample_forking_server +2 -2
  11. data/lib/bluepill.rb +3 -31
  12. data/lib/{bluepill → evented_bluepill}/application/client.rb +1 -1
  13. data/lib/evented_bluepill/application/server.rb +24 -0
  14. data/lib/{bluepill → evented_bluepill}/application.rb +14 -14
  15. data/lib/{bluepill → evented_bluepill}/controller.rb +30 -20
  16. data/lib/{bluepill → evented_bluepill}/dsl/app_proxy.rb +3 -3
  17. data/lib/{bluepill → evented_bluepill}/dsl/process_factory.rb +8 -5
  18. data/lib/{bluepill → evented_bluepill}/dsl/process_proxy.rb +3 -3
  19. data/lib/{bluepill → evented_bluepill}/dsl.rb +5 -2
  20. data/lib/{bluepill → evented_bluepill}/event.rb +1 -1
  21. data/lib/{bluepill → evented_bluepill}/group.rb +2 -2
  22. data/lib/{bluepill → evented_bluepill}/logger.rb +5 -2
  23. data/lib/evented_bluepill/options.rb +121 -0
  24. data/lib/{bluepill → evented_bluepill}/process.rb +12 -16
  25. data/lib/{bluepill → evented_bluepill}/process_conditions/always_true.rb +7 -3
  26. data/lib/evented_bluepill/process_conditions/cpu_usage.rb +25 -0
  27. data/lib/{bluepill → evented_bluepill}/process_conditions/http.rb +8 -3
  28. data/lib/evented_bluepill/process_conditions/mem_usage.rb +41 -0
  29. data/lib/evented_bluepill/process_conditions/process_condition.rb +72 -0
  30. data/lib/{bluepill → evented_bluepill}/process_conditions.rb +2 -2
  31. data/lib/{bluepill → evented_bluepill}/process_statistics.rb +10 -10
  32. data/lib/{bluepill → evented_bluepill}/socket.rb +2 -1
  33. data/lib/{bluepill → evented_bluepill}/system.rb +24 -21
  34. data/lib/{bluepill → evented_bluepill}/trigger.rb +4 -4
  35. data/lib/{bluepill → evented_bluepill}/triggers/flapping.rb +3 -8
  36. data/lib/evented_bluepill/util/rotational_array.rb +21 -0
  37. data/lib/evented_bluepill/version.rb +12 -0
  38. data/lib/evented_bluepill.rb +22 -0
  39. data/spec/always_true_spec.rb +19 -0
  40. data/spec/cpu_usage_spec.rb +28 -0
  41. data/spec/mem_usage_spec.rb +46 -0
  42. data/spec/process_condition_spec.rb +30 -0
  43. data/spec/spec_helper.rb +4 -0
  44. data/spec/system_spec.rb +15 -0
  45. metadata +137 -122
  46. data/lib/bluepill/application/server.rb +0 -26
  47. data/lib/bluepill/condition_watch.rb +0 -61
  48. data/lib/bluepill/process_conditions/cpu_usage.rb +0 -20
  49. data/lib/bluepill/process_conditions/mem_usage.rb +0 -33
  50. data/lib/bluepill/process_conditions/process_condition.rb +0 -23
  51. data/lib/bluepill/util/rotational_array.rb +0 -74
  52. data/lib/bluepill/version.rb +0 -5
@@ -1,27 +1,36 @@
1
1
  # -*- encoding: utf-8 -*-
2
-
3
2
  require 'fileutils'
3
+ require 'rbconfig'
4
+
5
+ require 'evented_bluepill/options'
4
6
 
5
- module Bluepill
7
+ module EventedBluepill
6
8
  class Controller
7
- attr_accessor :base_dir, :log_file, :sockets_dir, :pids_dir
9
+ attr_accessor :base_dir, :log_file
8
10
 
9
- def initialize(options = {})
10
- self.log_file = options[:log_file]
11
- self.base_dir = options[:base_dir]
12
- self.sockets_dir = File.join(base_dir, 'socks')
13
- self.pids_dir = File.join(base_dir, 'pids')
11
+ def initialize()
12
+ self.log_file = EventedBluepill::Options[:log_file]
13
+ self.base_dir = EventedBluepill::Options[:base_dir]
14
14
 
15
15
  setup_dir_structure
16
16
  cleanup_bluepill_directory
17
17
  end
18
18
 
19
- def running_applications
20
- Dir[File.join(sockets_dir, "*.sock")].map{|x| File.basename(x, ".sock")}
21
- end
22
-
23
19
  def handle_command(application, command, *args)
24
20
  case command.to_sym
21
+ when :load
22
+ file = args.first
23
+ if File.exists?(file)
24
+ # Restart the ruby interpreter for the config file so that anything loaded here
25
+ # does not stay in memory for the daemon
26
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
27
+ load_path = File.expand_path("#{File.dirname(__FILE__)}/../")
28
+ file_path = File.expand_path(file)
29
+
30
+ exec(ruby, "-I#{load_path}", '-revented_bluepill', file_path)
31
+ else
32
+ $stderr.puts "Can't find file: #{file}"
33
+ end
25
34
  when :status
26
35
  puts self.send_to_daemon(application, :status, *args)
27
36
  when *Application::PROCESS_COMMANDS
@@ -39,9 +48,9 @@ module Bluepill
39
48
  pid = pid_for(application)
40
49
  if System.pid_alive?(pid)
41
50
  ::Process.kill("TERM", pid)
42
- puts "Killing bluepilld[#{pid}]"
51
+ puts "Killing evented_bluepilld[#{pid}]"
43
52
  else
44
- puts "bluepilld[#{pid}] not running"
53
+ puts "evented_bluepilld[#{pid}] not running"
45
54
  end
46
55
  when :log
47
56
  log_file_location = self.send_to_daemon(application, :log_file)
@@ -83,14 +92,15 @@ module Bluepill
83
92
  pattern = [application, query].compact.join(':')
84
93
  ['\[.*', Regexp.escape(pattern), '.*'].compact.join
85
94
  end
95
+
86
96
  private
87
97
 
88
98
  def cleanup_bluepill_directory
89
- self.running_applications.each do |app|
99
+ EventedBluepill::Options.running_applications.each do |app|
90
100
  pid = pid_for(app)
91
101
  if !pid || !System.pid_alive?(pid)
92
- pid_file = File.join(self.pids_dir, "#{app}.pid")
93
- sock_file = File.join(self.sockets_dir, "#{app}.sock")
102
+ pid_file = File.join(EventedBluepill::Options.pids_dir, "#{app}.pid")
103
+ sock_file = File.join(EventedBluepill::Options.sockets_dir, "#{app}.sock")
94
104
  File.unlink(pid_file) if File.exists?(pid_file)
95
105
  File.unlink(sock_file) if File.exists?(sock_file)
96
106
  end
@@ -103,7 +113,7 @@ module Bluepill
103
113
  end
104
114
 
105
115
  def setup_dir_structure
106
- [@sockets_dir, @pids_dir].each do |dir|
116
+ [EventedBluepill::Options.sockets_dir, EventedBluepill::Options.pids_dir].each do |dir|
107
117
  FileUtils.mkdir_p(dir) unless File.exists?(dir)
108
118
  end
109
119
  end
@@ -111,8 +121,8 @@ module Bluepill
111
121
  def verify_version!(application)
112
122
  begin
113
123
  version = Socket.client_command(base_dir, application, "version")
114
- if version != Bluepill::VERSION
115
- abort("The running version of your daemon seems to be out of date.\nDaemon Version: #{version}, CLI Version: #{Bluepill::VERSION}")
124
+ if version != EventedBluepill::VERSION::STRING
125
+ abort("The running version of your daemon seems to be out of date.\nDaemon Version: #{version}, CLI Version: #{EventedBluepill::VERSION::STRING}")
116
126
  end
117
127
  rescue ArgumentError
118
128
  abort("The running version of your daemon seems to be out of date.")
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require 'bluepill/application'
4
- require 'bluepill/dsl/process_factory'
3
+ require 'evented_bluepill/application'
4
+ require 'evented_bluepill/dsl/process_factory'
5
5
 
6
- module Bluepill
6
+ module EventedBluepill
7
7
  class AppProxy
8
8
  APP_ATTRIBUTES = [:working_dir, :uid, :gid, :environment]
9
9
 
@@ -1,8 +1,8 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require 'bluepill/dsl/process_proxy'
3
+ require 'evented_bluepill/dsl/process_proxy'
4
4
 
5
- module Bluepill
5
+ module EventedBluepill
6
6
  class ProcessFactory
7
7
  attr_reader :attributes
8
8
 
@@ -17,7 +17,7 @@ module Bluepill
17
17
  def create_process(name, pids_dir)
18
18
  self.assign_default_pid_file(name, pids_dir)
19
19
 
20
- process = Bluepill::ProcessProxy.new(name, @attributes, @process_block)
20
+ process = EventedBluepill::ProcessProxy.new(name, @attributes, @process_block)
21
21
  child_process_block = @attributes.delete(:child_process_block)
22
22
  @attributes[:child_process_factory] = ProcessFactory.new(@attributes, child_process_block) if @attributes[:monitor_children]
23
23
 
@@ -31,9 +31,12 @@ module Bluepill
31
31
  attributes[:actual_pid] = pid
32
32
  attributes[:logger] = logger
33
33
 
34
- child = Bluepill::ProcessProxy.new(name, attributes, @process_block)
34
+ child = EventedBluepill::ProcessProxy.new(name, attributes, @process_block)
35
35
  self.validate_child_process! child
36
- child.to_process
36
+ process = child.to_process
37
+
38
+ process.determine_initial_state
39
+ process
37
40
  end
38
41
 
39
42
  protected
@@ -1,8 +1,8 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require 'bluepill/process'
3
+ require 'evented_bluepill/process'
4
4
 
5
- module Bluepill
5
+ module EventedBluepill
6
6
  class ProcessProxy
7
7
  attr_reader :attributes, :watches, :name
8
8
  def initialize(process_name, attributes, process_block)
@@ -33,7 +33,7 @@ module Bluepill
33
33
  end
34
34
 
35
35
  def to_process
36
- Bluepill::Process.new(@name, @watches, @attributes)
36
+ EventedBluepill::Process.new(@name, @watches, @attributes)
37
37
  end
38
38
  end
39
39
  end
@@ -1,8 +1,11 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require 'bluepill/dsl/app_proxy'
3
+ require 'active_support/core_ext/numeric'
4
+ require 'active_support/core_ext/hash'
4
5
 
5
- module Bluepill
6
+ require 'evented_bluepill/dsl/app_proxy'
7
+
8
+ module EventedBluepill
6
9
  def self.application(app_name, options = {}, &block)
7
10
  app_proxy = AppProxy.new(app_name, options)
8
11
  yield(app_proxy)
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'coolio'
4
4
 
5
- module Bluepill
5
+ module EventedBluepill
6
6
  module Event
7
7
  extend self
8
8
 
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- module Bluepill
3
+ module EventedBluepill
4
4
  class GroupTimer < Coolio::TimerWatcher
5
5
  def initialize(process, event)
6
6
  @process = process
@@ -43,7 +43,7 @@ module Bluepill
43
43
  self.processes.each do |process|
44
44
  next if process_name && process_name != process.name
45
45
  affected << [self.name, process.name].join(":")
46
- Bluepill::Event.attach(Bluepill::GroupTimer.new(process, "#{event}"))
46
+ EventedBluepill::Event.attach(EventedBluepill::GroupTimer.new(process, "#{event}"))
47
47
  end
48
48
  affected
49
49
  end
@@ -1,6 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- module Bluepill
3
+ require 'syslog'
4
+ require 'logger'
5
+
6
+ module EventedBluepill
4
7
  class Logger
5
8
  LOG_METHODS = [:emerg, :alert, :crit, :err, :warning, :notice, :info, :debug]
6
9
 
@@ -47,7 +50,7 @@ module Bluepill
47
50
  LoggerAdapter.new(@options[:log_file])
48
51
  else
49
52
  Syslog.close if Syslog.opened? # need to explictly close it before reopening it
50
- Syslog.open(@options[:identity] || 'bluepilld', Syslog::LOG_PID, Syslog::LOG_LOCAL6)
53
+ Syslog.open(@options[:identity] || 'evented_bluepilld', Syslog::LOG_PID, Syslog::LOG_LOCAL6)
51
54
  end
52
55
  end
53
56
 
@@ -0,0 +1,121 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'optparse'
3
+
4
+ require 'evented_bluepill/version'
5
+
6
+ module EventedBluepill
7
+ module Options
8
+ extend self
9
+
10
+ APPLICATION_COMMANDS = %w(status start stop restart unmonitor quit log)
11
+
12
+ # Default options
13
+ @options = {
14
+ :log_file => "/var/log/evented_bluepill.log",
15
+ :base_dir => "/var/evented_bluepill",
16
+ :privileged => true
17
+ }
18
+
19
+ def parse!
20
+ parse_options!
21
+ parse_application!
22
+
23
+ # Check for root
24
+ unless privileged?
25
+ $stderr.puts "You must run evented_bluepill as root or use --no-privileged option."
26
+ exit(3)
27
+ end
28
+ end
29
+
30
+ def [](key)
31
+ @options[key]
32
+ end
33
+
34
+ def running_applications
35
+ Dir[File.join(EventedBluepill::Options.sockets_dir, "*.sock")].map {|x| File.basename(x, ".sock")}
36
+ end
37
+
38
+ def sockets_dir
39
+ File.join(@options[:base_dir], 'socks')
40
+ end
41
+
42
+ def pids_dir
43
+ File.join(@options[:base_dir], 'pids')
44
+ end
45
+
46
+ def privileged?
47
+ !@options[:privileged] || ::Process.euid == 0
48
+ end
49
+
50
+ private
51
+
52
+ def parse_options!
53
+ OptionParser.new do |opts|
54
+ opts.banner = "Usage: evented_bluepill [app] cmd [options]"
55
+ opts.on('-l', "--logfile LOGFILE", "Path to logfile, defaults to #{@options[:log_file]}") do |file|
56
+ @options[:log_file] = file
57
+ end
58
+
59
+ opts.on('-c', "--base-dir DIR", "Directory to store evented_bluepill socket and pid files, defaults to #{@options[:base_dir]}") do |base_dir|
60
+ @options[:base_dir] = base_dir
61
+ end
62
+
63
+ opts.on("-v", "--version") do
64
+ puts "evented_bluepill, version #{EventedBluepill::VERSION::STRING}"
65
+ exit
66
+ end
67
+
68
+ opts.on("--[no-]privileged", "Allow/disallow to run #{$0} as non-privileged process. disallowed by default") do |v|
69
+ @options[:privileged] = v
70
+ end
71
+
72
+ help = proc do
73
+ puts opts
74
+ puts
75
+ puts "Commands:"
76
+ puts " load CONFIG_FILE\t\tLoads new instance of evented_bluepill using the specified config file"
77
+ puts " status\t\t\tLists the status of the proceses for the specified app"
78
+ puts " start [TARGET]\t\tIssues the start command for the target process or group, defaults to all processes"
79
+ puts " stop [TARGET]\t\tIssues the stop command for the target process or group, defaults to all processes"
80
+ puts " restart [TARGET]\t\tIssues the restart command for the target process or group, defaults to all processes"
81
+ puts " unmonitor [TARGET]\t\tStop monitoring target process or group, defaults to all processes"
82
+ puts " log [TARGET]\t\tShow the log for the specified process or group, defaults to all for app"
83
+ puts " quit\t\t\tStop evented_bluepill"
84
+ puts
85
+ puts "See http://github.com/msnexploder/evented_bluepill for README"
86
+ exit
87
+ end
88
+
89
+ opts.on_tail('-h','--help', 'Show this message', &help)
90
+ help.call if ARGV.empty?
91
+ end.parse!
92
+ end
93
+
94
+ def parse_application!
95
+ if running_applications.include?(File.basename($0)) && File.symlink?($0)
96
+ # evented_bluepill was called as a symlink with the name of the target application
97
+ options[:application] = File.basename($0)
98
+ elsif running_applications.include?(ARGV.first)
99
+ # the first arg is the application name
100
+ options[:application] = ARGV.shift
101
+ elsif APPLICATION_COMMANDS.include?(ARGV.first)
102
+ if running_applications.length == 1
103
+ # there is only one, let's just use that
104
+ options[:application] = running_applications.first
105
+ elsif running_applications.length > 1
106
+ # There is more than one, tell them the list and exit
107
+ $stderr.puts "You must specify an application name to run that command. Here's the list of running applications:"
108
+ running_applications.each_with_index do |app, index|
109
+ $stderr.puts " #{index + 1}. #{app}"
110
+ end
111
+ $stderr.puts "Usage: evented_bluepill [app] cmd [options]"
112
+ exit(1)
113
+ else
114
+ # There are none running AND they aren't trying to start one
115
+ $stderr.puts "Error: There are no running evented_bluepill daemons.\nTo start a evented_bluepill daemon, use: evented_bluepill load <config file>"
116
+ exit(2)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -1,9 +1,13 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
+ require 'timeout'
4
+
3
5
  require 'state_machine'
4
6
  require 'daemons'
5
7
 
6
- module Bluepill
8
+ require 'evented_bluepill/process_conditions'
9
+
10
+ module EventedBluepill
7
11
  class ProcessTimer < Coolio::TimerWatcher
8
12
  attr_accessor :process
9
13
 
@@ -111,7 +115,6 @@ module Bluepill
111
115
 
112
116
  def initialize(process_name, checks, options = {})
113
117
  @name = process_name
114
- @transition_history = Util::RotationalArray.new(10)
115
118
  @watches = []
116
119
  @triggers = []
117
120
  @children_timer = []
@@ -120,7 +123,7 @@ module Bluepill
120
123
  self.logger = options[:logger]
121
124
 
122
125
  checks.each do |name, opts|
123
- if Bluepill::Trigger[name]
126
+ if EventedBluepill::Trigger[name]
124
127
  self.add_trigger(name, opts)
125
128
  else
126
129
  self.add_watch(name, opts)
@@ -174,7 +177,7 @@ module Bluepill
174
177
 
175
178
  # Watch related methods
176
179
  def add_watch(name, options = {})
177
- self.watches << ConditionWatch.new(name, self, options.merge(:logger => self.logger))
180
+ self.watches << ProcessConditions[name].new(name, self, options.merge(:logger => self.logger))
178
181
  end
179
182
 
180
183
  def add_trigger(name, options = {})
@@ -185,7 +188,7 @@ module Bluepill
185
188
  if self.process_running?(true)
186
189
  self.state = 'up'
187
190
  else
188
- # TODO: or "unmonitored" if bluepill was started in no auto-start mode.
191
+ # TODO: or "unmonitored" if evented_bluepill was started in no auto-start mode.
189
192
  self.state = 'down'
190
193
  end
191
194
 
@@ -194,10 +197,10 @@ module Bluepill
194
197
  end
195
198
 
196
199
  def set_timer
197
- @timer = Bluepill::ProcessTimer.new(self)
198
- Bluepill::Event.attach(self.timer)
200
+ @timer = EventedBluepill::ProcessTimer.new(self)
201
+ EventedBluepill::Event.attach(self.timer)
199
202
 
200
- self.watches.each {|w| Bluepill::Event.attach(w) }
203
+ self.watches.each {|w| EventedBluepill::Event.attach(w) }
201
204
  end
202
205
 
203
206
  def handle_user_command(cmd)
@@ -370,13 +373,6 @@ module Bluepill
370
373
  logger = self.logger.prefix_with(name)
371
374
 
372
375
  child = self.child_process_factory.create_child_process(name, child_pid, logger)
373
-
374
- child.initialize_state_machines
375
- child.state = "up"
376
-
377
- child.set_timer
378
- child.watches.each {|w| w.process = child }
379
-
380
376
  @children_timer << child.timer
381
377
  end
382
378
  end
@@ -404,7 +400,7 @@ module Bluepill
404
400
 
405
401
  rescue Timeout::Error
406
402
  logger.err "Execution is taking longer than expected. Unmonitoring."
407
- logger.err "Did you forget to tell bluepill to daemonize this process?"
403
+ logger.err "Did you forget to tell evented_bluepill to daemonize this process?"
408
404
  self.dispatch!("unmonitor")
409
405
  end
410
406
  end
@@ -1,13 +1,17 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- module Bluepill
3
+ require 'evented_bluepill/process_conditions/process_condition'
4
+
5
+ module EventedBluepill
4
6
  module ProcessConditions
5
7
  class AlwaysTrue < ProcessCondition
6
- def initialize(options = {})
8
+ def initialize(name, process, options = {})
7
9
  @below = options[:below]
10
+
11
+ super
8
12
  end
9
13
 
10
- def run(pid)
14
+ def run
11
15
  1
12
16
  end
13
17
 
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'evented_bluepill/process_conditions/process_condition'
4
+ require 'evented_bluepill/system'
5
+
6
+ module EventedBluepill
7
+ module ProcessConditions
8
+ class CpuUsage < ProcessCondition
9
+ def initialize(name, process, options = {})
10
+ @below = options[:below]
11
+
12
+ super
13
+ end
14
+
15
+ def run
16
+ # third col in the ps axu output
17
+ EventedBluepill::System.cpu_usage(process.actual_pid).to_f
18
+ end
19
+
20
+ def check(value)
21
+ value < @below
22
+ end
23
+ end
24
+ end
25
+ end
@@ -3,10 +3,13 @@
3
3
  require 'net/http'
4
4
  require 'uri'
5
5
 
6
- module Bluepill
6
+ require 'evented_bluepill/process_conditions/process_condition'
7
+
8
+ # really this needs reworking
9
+ module EventedBluepill
7
10
  module ProcessConditions
8
11
  class Http < ProcessCondition
9
- def initialize(options = {})
12
+ def initialize(name, process, options = {})
10
13
  @uri = URI.parse(options[:url])
11
14
  @kind = case options[:kind]
12
15
  when Fixnum then Net::HTTPResponse::CODE_TO_OBJ[options[:kind].to_s]
@@ -17,9 +20,11 @@ module Bluepill
17
20
  @pattern = options[:pattern] || nil
18
21
  @open_timeout = (options[:open_timeout] || options[:timeout] || 5).to_i
19
22
  @read_timeout = (options[:read_timeout] || options[:timeout] || 5).to_i
23
+
24
+ super
20
25
  end
21
26
 
22
- def run(pid)
27
+ def run
23
28
  session = Net::HTTP.new(@uri.host, @uri.port)
24
29
  session.open_timeout = @open_timeout
25
30
  session.read_timeout = @read_timeout
@@ -0,0 +1,41 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'active_support/core_ext/numeric/bytes'
4
+
5
+ require 'evented_bluepill/process_conditions/process_condition'
6
+ require 'evented_bluepill/system'
7
+
8
+ module EventedBluepill
9
+ module ProcessConditions
10
+ class MemUsage < ProcessCondition
11
+ KB = 1024.0
12
+ MB = KB ** 2
13
+ FORMAT_STR = "%.1f%s"
14
+ MB_LABEL = "MB"
15
+ KB_LABEL = "KB"
16
+
17
+ def initialize(name, process, options = {})
18
+ @below = options[:below]
19
+
20
+ super
21
+ end
22
+
23
+ def run
24
+ # rss is on the 5th col
25
+ EventedBluepill::System.memory_usage(process.actual_pid).to_f.kilobytes
26
+ end
27
+
28
+ def check(value)
29
+ value < @below
30
+ end
31
+
32
+ def format_value(value)
33
+ if value >= MB
34
+ FORMAT_STR % [(value / MB), MB_LABEL]
35
+ else
36
+ FORMAT_STR % [(value / KB), KB_LABEL]
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,72 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'coolio'
4
+
5
+ require 'evented_bluepill/util/rotational_array'
6
+
7
+ module EventedBluepill
8
+ module ProcessConditions
9
+ class HistoryValue < Struct.new(:value, :critical)
10
+ end
11
+
12
+ class ProcessCondition < Coolio::TimerWatcher
13
+ attr_accessor :logger
14
+ attr_reader :every, :process_condition, :fires, :process, :name
15
+
16
+ def initialize(name, process, options = {})
17
+ @name = name
18
+
19
+ @logger = options.delete(:logger)
20
+ @fires = options.delete(:fires) || :restart
21
+ @every = options.delete(:every)
22
+ @times = options.delete(:times) || [1,1]
23
+ @times = [@times, @times] unless @times.is_a?(Array) # handles :times => 5
24
+ @process = process
25
+
26
+ self.clear_history!
27
+
28
+ super(@every, true)
29
+ end
30
+
31
+ def run
32
+ raise "Implement in subclass!"
33
+ end
34
+
35
+ def check(value)
36
+ raise "Implement in subclass!"
37
+ end
38
+
39
+ def format_value(value)
40
+ value
41
+ end
42
+
43
+ def clear_history!
44
+ @history = EventedBluepill::Util::RotationalArray.new(@times.last)
45
+ end
46
+
47
+ def to_s
48
+ data = @history.collect {|v| "#{v.value}#{'*' unless v.critical}"}.join(", ")
49
+ "#{@name}: [#{data}]"
50
+ end
51
+
52
+ protected
53
+
54
+ def fired?
55
+ @history.count {|v| not v.critical} >= @times.first
56
+ end
57
+
58
+ private
59
+
60
+ def on_timer
61
+ value = self.run
62
+ @history << HistoryValue.new(self.format_value(value), self.check(value))
63
+ self.logger.info(self.to_s)
64
+
65
+ if self.fired?
66
+ @process.dispatch!(self.fires, self.to_s)
67
+ logger.info "#{self.name} dispatched: #{self.fires}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- module Bluepill
3
+ module EventedBluepill
4
4
  module ProcessConditions
5
5
  def self.[](name)
6
6
  const_get(name.to_s.camelcase)
@@ -8,7 +8,7 @@ module Bluepill
8
8
  end
9
9
  end
10
10
 
11
- require "bluepill/process_conditions/process_condition"
11
+ require 'evented_bluepill/process_conditions/process_condition'
12
12
  Dir["#{File.dirname(__FILE__)}/process_conditions/*.rb"].each do |pc|
13
13
  require pc
14
14
  end
@@ -1,11 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- module Bluepill
3
+ require 'evented_bluepill/util/rotational_array'
4
+
5
+ module EventedBluepill
4
6
  class ProcessStatistics
5
7
  STRFTIME = "%m/%d/%Y %H:%I:%S"
8
+
6
9
  # possibly persist this data.
7
- def initialize
8
- @events = Util::RotationalArray.new(10)
10
+ def initialize(size = 10)
11
+ @events = EventedBluepill::Util::RotationalArray.new(size)
9
12
  end
10
13
 
11
14
  def record_event(event, reason)
@@ -13,14 +16,11 @@ module Bluepill
13
16
  end
14
17
 
15
18
  def to_s
16
- str = []
17
- @events.each do |(event, reason, time)|
19
+ str = @events.reverse.collect do |(event, reason, time)|
18
20
  str << " #{event} at #{time.strftime(STRFTIME)} - #{reason || "unspecified"}"
19
- end
20
- if str.size > 0
21
- str << "event history:"
22
- end
23
- str.reverse.join("\n")
21
+ end.join("\n")
22
+
23
+ "event history:\n#{str}"
24
24
  end
25
25
  end
26
26
  end