1 $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
12 require 'god/system/process'
13 require 'god/dependency_graph'
14 require 'god/timeline'
15 require 'god/configurable'
17 require 'god/behavior'
18 require 'god/behaviors/clean_pid_file'
19 require 'god/behaviors/notify_when_flapping'
21 require 'god/condition'
22 require 'god/conditions/process_running'
23 require 'god/conditions/process_exits'
24 require 'god/conditions/tries'
25 require 'god/conditions/memory_usage'
26 require 'god/conditions/cpu_usage'
27 require 'god/conditions/always'
28 require 'god/conditions/lambda'
29 require 'god/conditions/degrading_lambda'
30 require 'god/conditions/flapping'
33 require 'god/contacts/email'
35 require 'god/reporter'
44 require 'god/event_handler'
45 require 'god/registry'
50 $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext god])
58 God::EventHandler.load
61 # Override abort to exit without executing the at_exit hook
73 LOG_BUFFER_SIZE_DEFAULT = 100
74 PID_FILE_DIRECTORY_DEFAULT = '/var/run/god'
75 DRB_PORT_DEFAULT = 17165
76 DRB_ALLOW_DEFAULT = ['127.0.0.1']
87 attr_accessor :inited,
99 abort "God.init must be called before any Watches"
104 # yield to the config file
105 yield self if block_given?
108 def self.internal_init
110 return if self.inited
115 self.pending_watches = []
117 self.contact_groups = {}
120 self.log_buffer_size = LOG_BUFFER_SIZE_DEFAULT
121 self.pid_file_directory = PID_FILE_DIRECTORY_DEFAULT
122 self.port = DRB_PORT_DEFAULT
123 self.allow = DRB_ALLOW_DEFAULT
125 # init has been executed
132 # Instantiate a new, empty Watch object and pass it to the mandatory
133 # block. The attributes of the watch will be set by the configuration
141 # if running, completely remove the watch (if necessary) to
142 # prepare for the reload
143 existing_watch = self.watches[w.name]
144 if self.running && existing_watch
145 self.unwatch(existing_watch)
148 # ensure the new watch has a unique name
149 if self.watches[w.name] || self.groups[w.name]
150 abort "Watch name '#{w.name}' already used for a Watch or Group"
153 # ensure watch is internally valid
154 w.valid? || abort("Watch '#{w.name}' is not valid (see above)")
156 # add to list of watches
157 self.watches[w.name] = w
159 # add to pending watches
160 self.pending_watches << w
162 # add to group if specified
164 # ensure group name hasn't been used for a watch already
165 if self.watches[w.group]
166 abort "Group name '#{w.group}' already used for a Watch"
169 self.groups[w.group] ||= []
170 self.groups[w.group] << w
177 def self.unwatch(watch)
184 # remove from watches
185 self.watches.delete(watch.name)
189 self.groups[watch.group].delete(watch)
193 def self.contact(kind)
196 # create the condition
198 c = Contact.generate(kind)
199 rescue NoSuchContactError => e
203 # send to block so config can set attributes
204 yield(c) if block_given?
206 # call prepare on the contact
209 # ensure the new contact has a unique name
210 if self.contacts[c.name] || self.contact_groups[c.name]
211 abort "Contact name '#{c.name}' already used for a Contact or Contact Group"
214 # abort if the Contact is invalid, the Contact will have printed
215 # out its own error messages by now
216 unless Contact.valid?(c) && c.valid?
217 abort "Exiting on invalid contact"
220 # add to list of contacts
221 self.contacts[c.name] = c
223 # add to contact group if specified
225 # ensure group name hasn't been used for a contact already
226 if self.contacts[c.group]
227 abort "Contact Group name '#{c.group}' already used for a Contact"
230 self.contact_groups[c.group] ||= []
231 self.contact_groups[c.group] << c
235 def self.control(name, command)
236 # get the list of watches
237 watches = Array(self.watches[name] || self.groups[name])
243 when "start", "monitor"
244 watches.each { |w| jobs << Thread.new { w.monitor } }
246 watches.each { |w| jobs << Thread.new { w.move(:restart) } }
248 watches.each { |w| jobs << Thread.new { w.unmonitor.action(:stop) } }
250 watches.each { |w| jobs << Thread.new { w.unmonitor } }
252 raise InvalidCommandError.new
255 jobs.each { |j| j.join }
261 self.watches.sort.each do |name, w|
263 w.unmonitor if w.state
264 w.action(:stop) if w.alive?
269 return true unless self.watches.map { |name, w| w.alive? }.any?
282 self.watches.map do |name, w|
283 info[name] = {:state => w.state}
288 def self.running_log(watch_name, since)
289 unless self.watches[watch_name]
290 raise NoSuchWatchError.new
293 LOG.watch_log_since(watch_name, since)
296 def self.running_load(code)
298 self.pending_watches.each { |w| w.monitor if w.autostart? }
299 watches = self.pending_watches.dup
300 self.pending_watches.clear
305 Dir[glob].each do |f|
312 unless test(?d, self.pid_file_directory)
314 FileUtils.mkdir_p(self.pid_file_directory)
315 rescue Errno::EACCES => e
316 abort "Failed to create pid file directory: #{e.message}"
322 unless test(?w, self.pid_file_directory)
323 abort "The pid file directory (#{self.pid_file_directory}) is not writable by #{Etc.getlogin}"
333 self.server = Server.new(self.host, self.port, self.allow)
335 # start event handler system
336 EventHandler.start if EventHandler.loaded?
338 # start the timer system
341 # start monitoring any watches set to autostart
342 self.watches.values.each { |w| w.monitor if w.autostart? }
344 # clear pending watches
345 self.pending_watches.clear
350 # join the timer thread so we don't exit