lib/god/watch.rb in god-0.11.0 vs lib/god/watch.rb in god-0.12.0
- old
+ new
@@ -1,123 +1,263 @@
require 'etc'
require 'forwardable'
module God
-
+ # The Watch class is a specialized Task that handles standard process
+ # workflows. It has four states: init, up, start, and restart.
class Watch < Task
+ # The Array of Symbol valid task states.
VALID_STATES = [:init, :up, :start, :restart]
+
+ # The Sybmol initial state.
INITIAL_STATE = :init
-
- # config
- attr_accessor :grace, :start_grace, :stop_grace, :restart_grace
-
+
+ # Public: The grace period for this process (seconds).
+ attr_accessor :grace
+
+ # Public: The start grace period (seconds).
+ attr_accessor :start_grace
+
+ # Public: The stop grace period (seconds).
+ attr_accessor :stop_grace
+
+ # Public: The restart grace period (seconds).
+ attr_accessor :restart_grace
+
+ # Public: God::Process delegators. See lib/god/process.rb for docs.
extend Forwardable
def_delegators :@process, :name, :uid, :gid, :start, :stop, :restart, :dir,
:name=, :uid=, :gid=, :start=, :stop=, :restart=,
:dir=, :pid_file, :pid_file=, :log, :log=,
:log_cmd, :log_cmd=, :err_log, :err_log=,
:err_log_cmd, :err_log_cmd=, :alive?, :pid,
:unix_socket, :unix_socket=, :chroot, :chroot=,
:env, :env=, :signal, :stop_timeout=,
- :stop_signal=
- #
+ :stop_signal=, :umask, :umask=
+
+ # Initialize a new Watch instance.
def initialize
super
-
+
+ # This God::Process instance holds information specific to the process.
@process = God::Process.new
-
- # valid states
+
+ # Valid states.
self.valid_states = VALID_STATES
self.initial_state = INITIAL_STATE
-
- # no grace period by default
+
+ # No grace period by default.
self.grace = self.start_grace = self.stop_grace = self.restart_grace = 0
end
-
+
+ # Is this Watch valid?
+ #
+ # Returns true if the Watch is valid, false if not.
def valid?
super && @process.valid?
end
-
+
###########################################################################
#
# Behavior
#
###########################################################################
-
+
+ # Public: Add a behavior to this Watch. See lib/god/behavior.rb.
+ #
+ # kind - The Symbol name of the Behavior to add.
+ #
+ # Yields the newly instantiated Behavior.
+ #
+ # Returns nothing.
def behavior(kind)
- # create the behavior
+ # Create the behavior.
begin
b = Behavior.generate(kind, self)
rescue NoSuchBehaviorError => e
abort e.message
end
-
- # send to block so config can set attributes
+
+ # Send to block so config can set attributes.
yield(b) if block_given?
-
- # abort if the Behavior is invalid, the Behavior will have printed
- # out its own error messages by now
+
+ # Abort if the Behavior is invalid, the Behavior will have printed
+ # out its own error messages by now.
abort unless b.valid?
-
+
self.behaviors << b
end
-
+
###########################################################################
#
+ # Quickstart mode
+ #
+ ###########################################################################
+
+ # Default Integer interval at which keepalive will runn poll checks.
+ DEFAULT_KEEPALIVE_INTERVAL = 5.seconds
+
+ # Default Integer or Array of Integers specification of how many times the
+ # memory condition must fail before triggering.
+ DEFAULT_KEEPALIVE_MEMORY_TIMES = [3, 5]
+
+ # Default Integer or Array of Integers specification of how many times the
+ # CPU condition must fail before triggering.
+ DEFAULT_KEEPALIVE_CPU_TIMES = [3, 5]
+
+ # Public: A set of conditions for easily getting started with simple watch
+ # scenarios. Keepalive is intended for use by beginners or on processes
+ # that do not need very sophisticated monitoring.
+ #
+ # If events are enabled, it will use the :process_exit event to determine
+ # if a process fails. Otherwise it will use the :process_running poll.
+ #
+ # options - The option Hash. Possible values are:
+ # :interval - The Integer number of seconds on which to poll
+ # for process status. Affects CPU, memory, and
+ # :process_running conditions (if used).
+ # Default: 5.seconds.
+ # :memory_max - The Integer memory max. A bare integer means
+ # kilobytes. You may use Numeric.kilobytes,
+ # Numeric#megabytes, and Numeric#gigabytes to
+ # makes things more clear.
+ # :memory_times - If :memory_max is set, :memory_times can be
+ # set to either an Integer or a 2 element
+ # Integer Array to specify the number of times
+ # the memory condition must fail. Examples:
+ # 3 (three times), [3, 5] (three out of any five
+ # checks). Default: [3, 5].
+ # :cpu_max - The Integer CPU percentage max. Range is
+ # 0 to 100. You may use the Numberic#percent
+ # sugar to clarify e.g. 50.percent.
+ # :cpu_times - If :cpu_max is set, :cpu_times can be
+ # set to either an Integer or a 2 element
+ # Integer Array to specify the number of times
+ # the memory condition must fail. Examples:
+ # 3 (three times), [3, 5] (three out of any five
+ # checks). Default: [3, 5].
+ def keepalive(options = {})
+ if God::EventHandler.loaded?
+ self.transition(:init, { true => :up, false => :start }) do |on|
+ on.condition(:process_running) do |c|
+ c.interval = options[:interval] || DEFAULT_KEEPALIVE_INTERVAL
+ c.running = true
+ end
+ end
+
+ self.transition([:start, :restart], :up) do |on|
+ on.condition(:process_running) do |c|
+ c.interval = options[:interval] || DEFAULT_KEEPALIVE_INTERVAL
+ c.running = true
+ end
+ end
+
+ self.transition(:up, :start) do |on|
+ on.condition(:process_exits)
+ end
+ else
+ self.start_if do |start|
+ start.condition(:process_running) do |c|
+ c.interval = options[:interval] || DEFAULT_KEEPALIVE_INTERVAL
+ c.running = false
+ end
+ end
+ end
+
+ self.restart_if do |restart|
+ if options[:memory_max]
+ restart.condition(:memory_usage) do |c|
+ c.interval = options[:interval] || DEFAULT_KEEPALIVE_INTERVAL
+ c.above = options[:memory_max]
+ c.times = options[:memory_times] || DEFAULT_KEEPALIVE_MEMORY_TIMES
+ end
+ end
+
+ if options[:cpu_max]
+ restart.condition(:cpu_usage) do |c|
+ c.interval = options[:interval] || DEFAULT_KEEPALIVE_INTERVAL
+ c.above = options[:cpu_max]
+ c.times = options[:cpu_times] || DEFAULT_KEEPALIVE_CPU_TIMES
+ end
+ end
+ end
+ end
+
+ ###########################################################################
+ #
# Simple mode
#
###########################################################################
-
+
+ # Public: Start the process if any of the given conditions are triggered.
+ #
+ # Yields the Metric upon which conditions can be added.
+ #
+ # Returns nothing.
def start_if
self.transition(:up, :start) do |on|
yield(on)
end
end
-
+
+ # Public: Restart the process if any of the given conditions are triggered.
+ #
+ # Yields the Metric upon which conditions can be added.
+ #
+ # Returns nothing.
def restart_if
self.transition(:up, :restart) do |on|
yield(on)
end
end
-
- def stop_if
- self.transition(:up, :stop) do |on|
- yield(on)
- end
+
+ # Public: Stop the process if any of the given conditions are triggered.
+ #
+ # Yields the Metric upon which conditions can be added.
+ #
+ # Returns nothing.
+ def stop_if
+ self.transition(:up, :stop) do |on|
+ yield(on)
+ end
end
-
+
###########################################################################
#
# Lifecycle
#
###########################################################################
-
- # Enable monitoring
+
+ # Enable monitoring. Start at the first available of the init or up states.
+ #
+ # Returns nothing.
def monitor
- # start monitoring at the first available of the init or up states
if !self.metrics[:init].empty?
self.move(:init)
else
self.move(:up)
end
end
-
+
###########################################################################
#
# Actions
#
###########################################################################
-
+
+ # Perform an action.
+ #
+ # a - The Symbol action to perform. One of :start, :restart, :stop.
+ # c - The Condition.
+ #
+ # Returns this Watch.
def action(a, c = nil)
if !self.driver.in_driver_context?
- # called from outside Driver
-
- # send an async message to Driver
+ # Called from outside Driver. Send an async message to Driver.
self.driver.message(:action, [a, c])
else
- # called from within Driver
-
+ # Called from within Driver.
case a
when :start
call_action(c, :start)
sleep(self.start_grace + self.grace)
when :restart
@@ -131,58 +271,70 @@
when :stop
call_action(c, :stop)
sleep(self.stop_grace + self.grace)
end
end
-
+
self
end
-
+
+ # Perform the specifics of the action.
+ #
+ # condition - The Condition.
+ # action - The Sybmol action.
+ #
+ # Returns nothing.
def call_action(condition, action)
- # before
+ # Before.
before_items = self.behaviors
before_items += [condition] if condition
before_items.each do |b|
info = b.send("before_#{action}")
if info
msg = "#{self.name} before_#{action}: #{info} (#{b.base_name})"
applog(self, :info, msg)
end
end
-
- # log
+
+ # Log.
if self.send(action)
msg = "#{self.name} #{action}: #{self.send(action).to_s}"
applog(self, :info, msg)
end
-
+
+ # Execute.
@process.call_action(action)
-
- # after
+
+ # After.
after_items = self.behaviors
after_items += [condition] if condition
after_items.each do |b|
info = b.send("after_#{action}")
if info
msg = "#{self.name} after_#{action}: #{info} (#{b.base_name})"
applog(self, :info, msg)
end
end
end
-
+
###########################################################################
#
# Registration
#
###########################################################################
-
+
+ # Register the Process in the global process registry.
+ #
+ # Returns nothing.
def register!
God.registry.add(@process)
end
-
+
+ # Unregister the Process in the global process registry.
+ #
+ # Returns nothing.
def unregister!
God.registry.remove(@process)
super
end
end
-
end