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/contacts/sms.rb,
lib/god/event_handler.rb,
lib/god/simple_logger.rb,
lib/god/contacts/email.rb,
lib/god/system/process.rb,
lib/god/conditions/ping.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/condition_helper.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
- 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
-
.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
176 177 178 |
# File 'lib/god.rb', line 176 def contact_groups @contact_groups end |
.contacts ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def contacts @contacts end |
.groups ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def groups @groups end |
.inited ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def inited @inited end |
.main ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def main @main end |
.pending_watch_states ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def pending_watch_states @pending_watch_states end |
.pending_watches ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def pending_watches @pending_watches end |
.running ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def running @running end |
.server ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def server @server end |
.watches ⇒ Object
internal
176 177 178 |
# File 'lib/god.rb', line 176 def watches @watches end |
Class Method Details
.at_exit ⇒ Object
To be called on program exit to start god
Returns nothing
641 642 643 |
# File 'lib/god.rb', line 641 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
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 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/god.rb', line 343 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
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
# File 'lib/god.rb', line 414 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
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 |
# File 'lib/god.rb', line 200 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
561 562 563 564 565 |
# File 'lib/god.rb', line 561 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
661 662 663 664 665 666 667 |
# File 'lib/god.rb', line 661 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]
524 525 526 527 528 529 530 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 |
# File 'lib/god.rb', line 524 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
508 509 510 511 512 513 514 515 516 |
# File 'lib/god.rb', line 508 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
567 568 569 570 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 596 597 598 599 600 601 602 |
# File 'lib/god.rb', line 567 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 |
.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
493 494 495 496 497 498 499 |
# File 'lib/god.rb', line 493 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
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 |
# File 'lib/god.rb', line 607 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, … }
480 481 482 483 484 485 486 |
# File 'lib/god.rb', line 480 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
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/god.rb', line 445 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
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 |
# File 'lib/god.rb', line 258 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!
467 468 469 470 471 |
# File 'lib/god.rb', line 467 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
396 397 398 399 400 401 |
# File 'lib/god.rb', line 396 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
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/god.rb', line 315 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 |
.version ⇒ Object
633 634 635 636 |
# File 'lib/god.rb', line 633 def self.version yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml]))) "#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}" 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
245 246 247 |
# File 'lib/god.rb', line 245 def self.watch(&block) self.task(Watch, &block) end |