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/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
- 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
170 171 172 |
# File 'lib/god.rb', line 170 def contact_groups @contact_groups end |
.contacts ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def contacts @contacts end |
.groups ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def groups @groups end |
.inited ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def inited @inited end |
.main ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def main @main end |
.pending_watch_states ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def pending_watch_states @pending_watch_states end |
.pending_watches ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def pending_watches @pending_watches end |
.running ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def running @running end |
.server ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def server @server end |
.watches ⇒ Object
internal
170 171 172 |
# File 'lib/god.rb', line 170 def watches @watches end |
Class Method Details
.at_exit ⇒ Object
To be called on program exit to start god
Returns nothing
635 636 637 |
# File 'lib/god.rb', line 635 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
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 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/god.rb', line 337 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
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/god.rb', line 408 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
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 |
# File 'lib/god.rb', line 194 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
555 556 557 558 559 |
# File 'lib/god.rb', line 555 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
655 656 657 658 659 660 661 |
# File 'lib/god.rb', line 655 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]
518 519 520 521 522 523 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 |
# File 'lib/god.rb', line 518 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
502 503 504 505 506 507 508 509 510 |
# File 'lib/god.rb', line 502 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
561 562 563 564 565 566 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 |
# File 'lib/god.rb', line 561 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
487 488 489 490 491 492 493 |
# File 'lib/god.rb', line 487 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
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 |
# File 'lib/god.rb', line 601 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, … }
474 475 476 477 478 479 480 |
# File 'lib/god.rb', line 474 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
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
# File 'lib/god.rb', line 439 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
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 |
# File 'lib/god.rb', line 252 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!
461 462 463 464 465 |
# File 'lib/god.rb', line 461 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
390 391 392 393 394 395 |
# File 'lib/god.rb', line 390 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
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/god.rb', line 309 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
627 628 629 630 |
# File 'lib/god.rb', line 627 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
239 240 241 |
# File 'lib/god.rb', line 239 def self.watch(&block) self.task(Watch, &block) end |