1 $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
13 require 'god/system/process'
14 require 'god/dependency_graph'
15 require 'god/timeline'
16 require 'god/configurable'
18 require 'god/behavior'
19 require 'god/behaviors/clean_pid_file'
20 require 'god/behaviors/notify_when_flapping'
22 require 'god/condition'
23 require 'god/conditions/process_running'
24 require 'god/conditions/process_exits'
25 require 'god/conditions/tries'
26 require 'god/conditions/memory_usage'
27 require 'god/conditions/cpu_usage'
28 require 'god/conditions/always'
29 require 'god/conditions/lambda'
30 require 'god/conditions/degrading_lambda'
31 require 'god/conditions/flapping'
34 require 'god/contacts/email'
36 require 'god/reporter'
45 require 'god/event_handler'
46 require 'god/registry'
51 $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext god])
59 God::EventHandler.load
62 # Override abort to exit without executing the at_exit hook
74 LOG_BUFFER_SIZE_DEFAULT = 100
75 PID_FILE_DIRECTORY_DEFAULT = '/var/run/god'
76 DRB_PORT_DEFAULT = 17165
77 DRB_ALLOW_DEFAULT = ['127.0.0.1']
88 attr_accessor :inited,
100 abort "God.init must be called before any Watches"
105 # yield to the config file
106 yield self if block_given?
109 def self.internal_init
111 return if self.inited
116 self.pending_watches = []
118 self.contact_groups = {}
121 self.log_buffer_size = LOG_BUFFER_SIZE_DEFAULT
122 self.pid_file_directory = PID_FILE_DIRECTORY_DEFAULT
123 self.port = DRB_PORT_DEFAULT
124 self.allow = DRB_ALLOW_DEFAULT
126 # init has been executed
133 # Instantiate a new, empty Watch object and pass it to the mandatory
134 # block. The attributes of the watch will be set by the configuration
142 # if running, completely remove the watch (if necessary) to
143 # prepare for the reload
144 existing_watch = self.watches[w.name]
145 if self.running && existing_watch
146 self.unwatch(existing_watch)
149 # ensure the new watch has a unique name
150 if self.watches[w.name] || self.groups[w.name]
151 abort "Watch name '#{w.name}' already used for a Watch or Group"
154 # ensure watch is internally valid
155 w.valid? || abort("Watch '#{w.name}' is not valid (see above)")
157 # add to list of watches
158 self.watches[w.name] = w
160 # add to pending watches
161 self.pending_watches << w
163 # add to group if specified
165 # ensure group name hasn't been used for a watch already
166 if self.watches[w.group]
167 abort "Group name '#{w.group}' already used for a Watch"
170 self.groups[w.group] ||= []
171 self.groups[w.group] << w
178 def self.unwatch(watch)
185 # remove from watches
186 self.watches.delete(watch.name)
190 self.groups[watch.group].delete(watch)
194 def self.contact(kind)
197 # create the condition
199 c = Contact.generate(kind)
200 rescue NoSuchContactError => e
204 # send to block so config can set attributes
205 yield(c) if block_given?
207 # call prepare on the contact
210 # ensure the new contact has a unique name
211 if self.contacts[c.name] || self.contact_groups[c.name]
212 abort "Contact name '#{c.name}' already used for a Contact or Contact Group"
215 # abort if the Contact is invalid, the Contact will have printed
216 # out its own error messages by now
217 unless Contact.valid?(c) && c.valid?
218 abort "Exiting on invalid contact"
221 # add to list of contacts
222 self.contacts[c.name] = c
224 # add to contact group if specified
226 # ensure group name hasn't been used for a contact already
227 if self.contacts[c.group]
228 abort "Contact Group name '#{c.group}' already used for a Contact"
231 self.contact_groups[c.group] ||= []
232 self.contact_groups[c.group] << c
236 def self.control(name, command)
237 # get the list of watches
238 watches = Array(self.watches[name] || self.groups[name])
244 when "start", "monitor"
245 watches.each { |w| jobs << Thread.new { w.monitor } }
247 watches.each { |w| jobs << Thread.new { w.move(:restart) } }
249 watches.each { |w| jobs << Thread.new { w.unmonitor.action(:stop) } }
251 watches.each { |w| jobs << Thread.new { w.unmonitor } }
253 raise InvalidCommandError.new
256 jobs.each { |j| j.join }
262 self.watches.sort.each do |name, w|
264 w.unmonitor if w.state
265 w.action(:stop) if w.alive?
270 return true unless self.watches.map { |name, w| w.alive? }.any?
283 self.watches.map do |name, w|
284 info[name] = {:state => w.state}
289 def self.running_log(watch_name, since)
290 unless self.watches[watch_name]
291 raise NoSuchWatchError.new
294 LOG.watch_log_since(watch_name, since)
297 def self.running_load(code)
299 self.pending_watches.each { |w| w.monitor if w.autostart? }
300 watches = self.pending_watches.dup
301 self.pending_watches.clear
306 Dir[glob].each do |f|
313 unless test(?d, self.pid_file_directory)
315 FileUtils.mkdir_p(self.pid_file_directory)
316 rescue Errno::EACCES => e
317 abort "Failed to create pid file directory: #{e.message}"
323 unless test(?w, self.pid_file_directory)
324 abort "The pid file directory (#{self.pid_file_directory}) is not writable by #{Etc.getlogin}"
334 self.server = Server.new(self.host, self.port, self.allow)
336 # start event handler system
337 EventHandler.start if EventHandler.loaded?
339 # start the timer system
342 # start monitoring any watches set to autostart
343 self.watches.values.each { |w| w.monitor if w.autostart? }
345 # clear pending watches
346 self.pending_watches.clear
351 # join the timer thread so we don't exit