1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
|
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
# 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=, :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.
self.valid_states = VALID_STATES
self.initial_state = INITIAL_STATE
# 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.
begin
b = Behavior.generate(kind, self)
rescue NoSuchBehaviorError => e
abort e.message
end
# 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 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
# 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. Start at the first available of the init or up states.
#
# Returns nothing.
def monitor
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.
self.driver.message(:action, [a, c])
else
# Called from within Driver.
case a
when :start
call_action(c, :start)
sleep(self.start_grace + self.grace)
when :restart
if self.restart
call_action(c, :restart)
else
action(:stop, c)
action(:start, c)
end
sleep(self.restart_grace + self.grace)
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_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.
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_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
|