Module: God
- Defined in:
- lib/god.rb,
lib/god/task.rb,
lib/god/watch.rb,
lib/god/driver.rb,
lib/god/errors.rb,
lib/god/logger.rb,
lib/god/metric.rb,
lib/god/socket.rb,
lib/god/cli/run.rb,
lib/god/contact.rb,
lib/god/process.rb,
lib/god/trigger.rb,
lib/god/behavior.rb,
lib/god/registry.rb,
lib/god/timeline.rb,
lib/god/condition.rb,
lib/god/cli/command.rb,
lib/god/cli/version.rb,
lib/god/configurable.rb,
lib/god/event_handler.rb,
lib/god/contacts/email.rb,
lib/god/system/process.rb,
lib/god/conditions/tries.rb,
lib/god/dependency_graph.rb,
lib/god/dependency_graph.rb,
lib/god/conditions/always.rb,
lib/god/conditions/lambda.rb,
lib/god/conditions/complex.rb,
lib/god/conditions/flapping.rb,
lib/god/conditions/cpu_usage.rb,
lib/god/conditions/disk_usage.rb,
lib/god/conditions/memory_usage.rb,
lib/god/behaviors/clean_pid_file.rb,
lib/god/conditions/process_exits.rb,
lib/god/conditions/process_running.rb,
lib/god/conditions/degrading_lambda.rb,
lib/god/event_handlers/dummy_handler.rb,
lib/god/conditions/http_response_code.rb,
lib/god/event_handlers/kqueue_handler.rb,
lib/god/behaviors/notify_when_flapping.rb,
lib/god/event_handlers/netlink_handler.rb
Defined Under Namespace
Modules: Behaviors, CLI, Conditions, Configurable, Contacts, System Classes: AbstractMethodNotOverriddenError, Behavior, Condition, Contact, DependencyGraph, Driver, DriverEvent, DummyHandler, EventCondition, EventHandler, EventRegistrationFailedError, InvalidCommandError, KQueueHandler, Logger, Metric, NetlinkHandler, NoSuchBehaviorError, NoSuchConditionError, NoSuchContactError, NoSuchWatchError, PollCondition, Process, Registry, Socket, Task, Timeline, Trigger, TriggerCondition, Watch
Constant Summary collapse
- VERSION =
'0.7.3'
- LOG_BUFFER_SIZE_DEFAULT =
100
- PID_FILE_DIRECTORY_DEFAULTS =
['/var/run/god', '~/.god/pids']
- DRB_PORT_DEFAULT =
17165
- DRB_ALLOW_DEFAULT =
['127.0.0.1']
- LOG_LEVEL_DEFAULT =
:info
Class Attribute Summary collapse
-
.contact_groups ⇒ Object
internal.
-
.contacts ⇒ Object
internal.
-
.groups ⇒ Object
internal.
-
.inited ⇒ Object
internal.
-
.main ⇒ Object
internal.
-
.pending_watch_states ⇒ Object
internal.
-
.pending_watches ⇒ Object
internal.
-
.running ⇒ Object
internal.
-
.server ⇒ Object
internal.
-
.watches ⇒ Object
internal.
Class Method Summary collapse
-
.at_exit ⇒ Object
To be called on program exit to start god.
-
.contact(kind) {|c| ... } ⇒ Object
Instantiate a new Contact of the given kind and send it to the block.
-
.control(name, command) ⇒ Object
Control the lifecycle of the given task(s).
-
.internal_init ⇒ Object
Initialize internal data.
-
.load(glob) ⇒ Object
Load the given file(s) according to the given glob.
-
.pattern_match(pattern, list) ⇒ Object
Match a shortened pattern against a list of String candidates.
- .registry ⇒ Object
-
.running_load(code, filename) ⇒ Object
Load a config file into a running god instance.
-
.running_log(watch_name, since) ⇒ Object
Log lines for the given task since the specified time.
- .setup ⇒ Object
-
.start ⇒ Object
Initialize and startup the machinery that makes god work.
-
.status ⇒ Object
Gather the status of each task.
-
.stop_all ⇒ Object
Unmonitor and stop all tasks.
-
.task(klass = Task) {|t| ... } ⇒ Object
Instantiate a new, empty Task object and yield it to the mandatory block.
-
.terminate ⇒ Object
Force the termination of god.
-
.uncontact(contact) ⇒ Object
Remove the given contact from god.
-
.unwatch(watch) ⇒ Object
Unmonitor and remove the given watch from god.
-
.watch(&block) ⇒ Object
Instantiate a new, empty Watch object and pass it to the mandatory block.
Class Attribute Details
.contact_groups ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def contact_groups @contact_groups end |
.contacts ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def contacts @contacts end |
.groups ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def groups @groups end |
.inited ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def inited @inited end |
.main ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def main @main end |
.pending_watch_states ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def pending_watch_states @pending_watch_states end |
.pending_watches ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def pending_watches @pending_watches end |
.running ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def running @running end |
.server ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def server @server end |
.watches ⇒ Object
internal
153 154 155 |
# File 'lib/god.rb', line 153 def watches @watches end |
Class Method Details
.at_exit ⇒ Object
To be called on program exit to start god
Returns nothing
600 601 602 |
# File 'lib/god.rb', line 600 def self.at_exit self.start end |
.contact(kind) {|c| ... } ⇒ Object
Instantiate a new Contact of the given kind and send it to the block. Then prepare, validate, and record the Contact.
+kind+ is the contact class specifier
Aborts on invalid kind
duplicate contact name
invalid contact
conflicting group name
Returns nothing
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/god.rb', line 320 def self.contact(kind) self.internal_init # create the contact begin c = Contact.generate(kind) rescue NoSuchContactError => e abort e. end # send to block so config can set attributes yield(c) if block_given? # call prepare on the contact c.prepare # remove existing contacts of same name existing_contact = self.contacts[c.name] if self.running && existing_contact self.uncontact(existing_contact) end # warn and noop if the contact has been defined before if self.contacts[c.name] || self.contact_groups[c.name] applog(nil, :warn, "Contact name '#{c.name}' already used for a Contact or Contact Group") return end # abort if the Contact is invalid, the Contact will have printed # out its own error messages by now unless Contact.valid?(c) && c.valid? abort "Exiting on invalid contact" end # add to list of contacts self.contacts[c.name] = c # add to contact group if specified if c.group # ensure group name hasn't been used for a contact already if self.contacts[c.group] abort "Contact Group name '#{c.group}' already used for a Contact" end self.contact_groups[c.group] ||= [] self.contact_groups[c.group] << c end end |
.control(name, command) ⇒ Object
Control the lifecycle of the given task(s).
+name+ is the name of a task/group (String)
+command+ is the command to run (String)
one of: "start"
"monitor"
"restart"
"stop"
"unmonitor"
"remove"
Returns String[]:task_names
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/god.rb', line 391 def self.control(name, command) # get the list of items items = Array(self.watches[name] || self.groups[name]).dup jobs = [] # do the command case command when "start", "monitor" items.each { |w| jobs << Thread.new { w.monitor if w.state != :up } } when "restart" items.each { |w| jobs << Thread.new { w.move(:restart) } } when "stop" items.each { |w| jobs << Thread.new { w.unmonitor.action(:stop) if w.state != :unmonitored } } when "unmonitor" items.each { |w| jobs << Thread.new { w.unmonitor if w.state != :unmonitored } } when "remove" items.each { |w| self.unwatch(w) } else raise InvalidCommandError.new end jobs.each { |j| j.join } items.map { |x| x.name } end |
.internal_init ⇒ Object
Initialize internal data.
Returns nothing
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 |
# File 'lib/god.rb', line 177 def self.internal_init # only do this once return if self.inited # variable init self.watches = {} self.groups = {} self.pending_watches = [] self.pending_watch_states = {} self.contacts = {} self.contact_groups = {} # set defaults self.log_buffer_size ||= LOG_BUFFER_SIZE_DEFAULT self.port ||= DRB_PORT_DEFAULT self.allow ||= DRB_ALLOW_DEFAULT self.log_level ||= LOG_LEVEL_DEFAULT # additional setup self.setup # log level log_level_map = {:debug => Logger::DEBUG, :info => Logger::INFO, :warn => Logger::WARN, :error => Logger::ERROR, :fatal => Logger::FATAL} LOG.level = log_level_map[self.log_level] # init has been executed self.inited = true # not yet running self.running = false end |
.load(glob) ⇒ Object
Load the given file(s) according to the given glob.
+glob+ is the glob-enabled path to load
Returns nothing
525 526 527 528 529 |
# File 'lib/god.rb', line 525 def self.load(glob) Dir[glob].each do |f| Kernel.load f end end |
.pattern_match(pattern, list) ⇒ Object
Match a shortened pattern against a list of String candidates. The pattern is expanded into a regular expression by inserting .* between each character.
+pattern+ is the String containing the abbreviation
+list+ is the Array of Strings to match against
Examples
list = %w{ foo bar bars }
pattern = 'br'
God.pattern_match(list, pattern)
# => ['bar', 'bars']
Returns String[]:matched_elements
620 621 622 623 624 625 626 |
# File 'lib/god.rb', line 620 def self.pattern_match(pattern, list) regex = pattern.split('').join('.*') list.select do |item| item =~ Regexp.new(regex) end end |
.registry ⇒ Object
2 3 4 |
# File 'lib/god/registry.rb', line 2 def self.registry @registry ||= Registry.new end |
.running_load(code, filename) ⇒ Object
Load a config file into a running god instance. Rescues any exceptions that the config may raise and reports these back to the caller.
+code+ is a String containing the config file
+filename+ is the filename of the config file
Returns [String[]:task_names, String:errors]
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/god.rb', line 488 def self.running_load(code, filename) errors = "" watches = [] begin LOG.start_capture Gem.clear_paths eval(code, root_binding, filename) self.pending_watches.each do |w| if previous_state = self.pending_watch_states[w.name] w.monitor unless previous_state == :unmonitored else w.monitor if w.autostart? end end watches = self.pending_watches.dup self.pending_watches.clear self.pending_watch_states.clear rescue Exception => e # don't ever let running_load take down god errors << LOG.finish_capture unless e.instance_of?(SystemExit) errors << e. << "\n" errors << e.backtrace.join("\n") end end names = watches.map { |x| x.name } [names, errors] end |
.running_log(watch_name, since) ⇒ Object
Log lines for the given task since the specified time.
+watch_name+ is the name of the task (may be abbreviated)
+since+ is the Time since which to report log lines
Raises God::NoSuchWatchError if no tasks matched
Returns String:joined_log_lines
472 473 474 475 476 477 478 479 480 |
# File 'lib/god.rb', line 472 def self.running_log(watch_name, since) matches = pattern_match(watch_name, self.watches.keys) unless matches.first raise NoSuchWatchError.new end LOG.watch_log_since(matches.first, since) end |
.setup ⇒ Object
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
# File 'lib/god.rb', line 531 def self.setup if self.pid_file_directory # pid file dir was specified, ensure it is created and writable unless File.exist?(self.pid_file_directory) begin FileUtils.mkdir_p(self.pid_file_directory) rescue Errno::EACCES => e abort "Failed to create pid file directory: #{e.}" end end unless File.writable?(self.pid_file_directory) abort "The pid file directory (#{self.pid_file_directory}) is not writable by #{Etc.getlogin}" end else # no pid file dir specified, try defaults PID_FILE_DIRECTORY_DEFAULTS.each do |idir| dir = File.(idir) begin FileUtils.mkdir_p(dir) if File.writable?(dir) self.pid_file_directory = dir break end rescue Errno::EACCES => e end end unless self.pid_file_directory dirs = PID_FILE_DIRECTORY_DEFAULTS.map { |x| File.(x) } abort "No pid file directory exists, could be created, or is writable at any of #{dirs.join(', ')}" end end applog(nil, :info, "Using pid file directory: #{self.pid_file_directory}") end |
.start ⇒ Object
Initialize and startup the machinery that makes god work.
Returns nothing
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
# File 'lib/god.rb', line 571 def self.start self.internal_init # instantiate server self.server = Socket.new(self.port) # start monitoring any watches set to autostart self.watches.values.each { |w| w.monitor if w.autostart? } # clear pending watches self.pending_watches.clear # mark as running self.running = true # don't exit self.main = Thread.new do loop do sleep 60 end end self.main.join end |
.status ⇒ Object
Gather the status of each task.
Examples
God.status
# => { 'mongrel' => :up, 'nginx' => :up }
Returns { String:task_name => Symbol:status, … }
457 458 459 460 461 462 463 |
# File 'lib/god.rb', line 457 def self.status info = {} self.watches.map do |name, w| info[name] = {:state => w.state} end info end |
.stop_all ⇒ Object
Unmonitor and stop all tasks.
Returns true on success
false if all tasks could not be stopped within 10 seconds
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/god.rb', line 422 def self.stop_all self.watches.sort.each do |name, w| Thread.new do w.unmonitor if w.state != :unmonitored w.action(:stop) if w.alive? end end 10.times do return true unless self.watches.map { |name, w| w.alive? }.any? sleep 1 end return false end |
.task(klass = Task) {|t| ... } ⇒ Object
Instantiate a new, empty Task object and yield it to the mandatory block. The attributes of the task will be set by the configuration file.
Aborts on duplicate task name
invalid task
conflicting group name
Returns nothing
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 |
# File 'lib/god.rb', line 235 def self.task(klass = Task) self.internal_init t = klass.new yield(t) # do the post-configuration t.prepare # if running, completely remove the watch (if necessary) to # prepare for the reload existing_watch = self.watches[t.name] if self.running && existing_watch self.pending_watch_states[existing_watch.name] = existing_watch.state self.unwatch(existing_watch) end # ensure the new watch has a unique name if self.watches[t.name] || self.groups[t.name] abort "Task name '#{t.name}' already used for a Task or Group" end # ensure watch is internally valid t.valid? || abort("Task '#{t.name}' is not valid (see above)") # add to list of watches self.watches[t.name] = t # add to pending watches self.pending_watches << t # add to group if specified if t.group # ensure group name hasn't been used for a watch already if self.watches[t.group] abort "Group name '#{t.group}' already used for a Task" end self.groups[t.group] ||= [] self.groups[t.group] << t end # register watch t.register! # log if self.running && existing_watch applog(t, :info, "#{t.name} Reloaded config") elsif self.running applog(t, :info, "#{t.name} Loaded config") end end |
.terminate ⇒ Object
Force the termination of god.
* Clean up pid file if one exists
* Stop DRb service
* Hard exit using exit!
Never returns because the process will no longer exist!
444 445 446 447 448 |
# File 'lib/god.rb', line 444 def self.terminate FileUtils.rm_f(self.pid) if self.pid self.server.stop if self.server exit!(0) end |
.uncontact(contact) ⇒ Object
Remove the given contact from god.
+contact+ is the Contact to remove
Returns nothing
373 374 375 376 377 378 |
# File 'lib/god.rb', line 373 def self.uncontact(contact) self.contacts.delete(contact.name) if contact.group self.contact_groups[contact.group].delete(contact) end end |
.unwatch(watch) ⇒ Object
Unmonitor and remove the given watch from god.
+watch+ is the Watch to remove
Returns nothing
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/god.rb', line 292 def self.unwatch(watch) # unmonitor watch.unmonitor unless watch.state == :unmonitored # unregister watch.unregister! # remove from watches self.watches.delete(watch.name) # remove from groups if watch.group self.groups[watch.group].delete(watch) end applog(watch, :info, "#{watch.name} unwatched") end |
.watch(&block) ⇒ Object
Instantiate a new, empty Watch object and pass it to the mandatory block. The attributes of the watch will be set by the configuration file.
Aborts on duplicate watch name
invalid watch
conflicting group name
Returns nothing
222 223 224 |
# File 'lib/god.rb', line 222 def self.watch(&block) self.task(Watch, &block) end |