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/simple_logger.rb,
lib/god/contacts/email.rb,
lib/god/contacts/prowl.rb,
lib/god/contacts/scout.rb,
lib/god/system/process.rb,
lib/god/contacts/jabber.rb,
lib/god/conditions/tries.rb,
lib/god/contacts/twitter.rb,
lib/god/contacts/webhook.rb,
lib/god/dependency_graph.rb,
lib/god/dependency_graph.rb,
lib/god/conditions/always.rb,
lib/god/conditions/lambda.rb,
lib/god/contacts/campfire.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/file_mtime.rb,
lib/god/system/portable_poller.rb,
lib/god/conditions/memory_usage.rb,
lib/god/behaviors/clean_pid_file.rb,
lib/god/conditions/process_exits.rb,
lib/god/system/slash_proc_poller.rb,
lib/god/conditions/process_running.rb,
lib/god/behaviors/clean_unix_socket.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, DriverEventQueue, DriverOperation, DummyHandler, EventCondition, EventHandler, EventRegistrationFailedError, InvalidCommandError, KQueueHandler, Logger, Metric, NetlinkHandler, NoSuchBehaviorError, NoSuchConditionError, NoSuchContactError, NoSuchWatchError, PollCondition, Process, Registry, SimpleLogger, Socket, Task, TimedEvent, Timeline, Trigger, TriggerCondition, Watch
Constant Summary collapse
- VERSION =
'0.11.0'
- 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
- TERMINATE_TIMEOUT_DEFAULT =
10
- STOP_TIMEOUT_DEFAULT =
10
- STOP_SIGNAL_DEFAULT =
'TERM'
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
-
.signal(name, signal) ⇒ Object
Send a signal to each task.
-
.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.
- .version ⇒ Object
-
.watch(&block) ⇒ Object
Instantiate a new, empty Watch object and pass it to the mandatory block.
Class Attribute Details
.contact_groups ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def contact_groups @contact_groups end |
.contacts ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def contacts @contacts end |
.groups ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def groups @groups end |
.inited ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def inited @inited end |
.main ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def main @main end |
.pending_watch_states ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def pending_watch_states @pending_watch_states end |
.pending_watches ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def pending_watches @pending_watches end |
.running ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def running @running end |
.server ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def server @server end |
.watches ⇒ Object
internal
178 179 180 |
# File 'lib/god.rb', line 178 def watches @watches end |
Class Method Details
.at_exit ⇒ Object
To be called on program exit to start god
Returns nothing
666 667 668 |
# File 'lib/god.rb', line 666 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
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/god.rb', line 350 def self.contact(kind) self.internal_init # verify contact has been loaded if CONTACT_LOAD_SUCCESS[kind] == false applog(nil, :error, "A required dependency for the #{kind} contact is unavailable.") applog(nil, :error, "Run the following commands to install the dependencies:") CONTACT_DEPS[kind].each do |d| applog(nil, :error, " [sudo] gem install #{d}") end abort end # 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
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 |
# File 'lib/god.rb', line 431 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.action(:stop); w.unmonitor 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
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 |
# File 'lib/god.rb', line 206 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 self.terminate_timeout ||= TERMINATE_TIMEOUT_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
581 582 583 584 585 |
# File 'lib/god.rb', line 581 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
686 687 688 689 690 691 692 |
# File 'lib/god.rb', line 686 def self.pattern_match(pattern, list) regex = pattern.split('').join('.*') list.select do |item| item =~ Regexp.new(regex) end.sort_by { |x| x.size } 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]
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 567 568 569 570 571 572 573 574 575 |
# File 'lib/god.rb', line 541 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 # make sure we quit capturing when we're done LOG.finish_capture 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
525 526 527 528 529 530 531 532 533 |
# File 'lib/god.rb', line 525 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
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 |
# File 'lib/god.rb', line 587 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 if God::Logger.syslog LOG.info("Syslog enabled.") else LOG.info("Syslog disabled.") end applog(nil, :info, "Using pid file directory: #{self.pid_file_directory}") end |
.signal(name, signal) ⇒ Object
Send a signal to each task.
+name+ is the String name of the task or group
+signal+ is the signal to send. e.g. HUP, 9
Returns String[]:task_names
510 511 512 513 514 515 516 |
# File 'lib/god.rb', line 510 def self.signal(name, signal) items = Array(self.watches[name] || self.groups[name]).dup jobs = [] items.each { |w| jobs << Thread.new { w.signal(signal) } } jobs.each { |j| j.join } items.map { |x| x.name } end |
.start ⇒ Object
Initialize and startup the machinery that makes god work.
Returns nothing
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 |
# File 'lib/god.rb', line 633 def self.start self.internal_init # instantiate server self.server = Socket.new(self.port, self.socket_user, self.socket_group, self.socket_perms) # 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, … }
497 498 499 500 501 502 503 |
# File 'lib/god.rb', line 497 def self.status info = {} self.watches.map do |name, w| info[name] = {:state => w.state, :group => w.group} 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
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/god.rb', line 462 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 terminate_timeout.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
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 |
# File 'lib/god.rb', line 265 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!
484 485 486 487 488 |
# File 'lib/god.rb', line 484 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
413 414 415 416 417 418 |
# File 'lib/god.rb', line 413 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
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/god.rb', line 322 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
252 253 254 |
# File 'lib/god.rb', line 252 def self.watch(&block) self.task(Watch, &block) end |