default stop for auto-daemonized; bug fixes
[god.git] / lib / god.rb
blob0ff334f15ab07c85f9468ad467b61fd31ff65578
1 $:.unshift File.dirname(__FILE__)     # For use/testing when no gem is installed
3 # stdlib
4 require 'syslog'
6 # internal requires
7 require 'god/errors'
9 require 'god/system/process'
11 require 'god/dependency_graph'
13 require 'god/behavior'
14 require 'god/behaviors/clean_pid_file'
15 require 'god/behaviors/notify_when_flapping'
17 require 'god/condition'
18 require 'god/conditions/timeline'
19 require 'god/conditions/process_running'
20 require 'god/conditions/process_exits'
21 require 'god/conditions/tries'
22 require 'god/conditions/memory_usage'
23 require 'god/conditions/cpu_usage'
24 require 'god/conditions/always'
25 require 'god/conditions/lambda'
26 require 'god/conditions/degrading_lambda'
28 require 'god/reporter'
29 require 'god/server'
30 require 'god/timer'
31 require 'god/hub'
33 require 'god/metric'
34 require 'god/watch'
36 require 'god/event_handler'
37 require 'god/registry'
38 require 'god/process'
40 require 'god/sugar'
42 $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext god])
44 begin
45   Syslog.open('god')
46 rescue RuntimeError
47   Syslog.reopen('god')
48 end
50 God::EventHandler.load
52 module God
53   VERSION = '0.3.2'
54   
55   class << self
56     attr_accessor :inited, :running, :pending_watches, :host, :port
57     
58     # drb
59     attr_accessor :server
60     
61     # api
62     attr_accessor :watches, :groups
63   end
64   
65   def self.init
66     if self.inited
67       abort "God.init must be called before any Watches"
68     end
69     
70     self.internal_init
71   end
72   
73   def self.internal_init
74     # only do this once
75     return if self.inited
76     
77     # variable init
78     self.watches = {}
79     self.groups = {}
80     self.pending_watches = []
81     
82     # yield to the config file
83     yield self if block_given?
84     
85     # instantiate server
86     self.server = Server.new(self.host, self.port)
87     
88     # init has been executed
89     self.inited = true
90     
91     # not yet running
92     self.running = false
93   end
94     
95   # Where pid files created by god will go by default
96   def self.pid_file_directory
97     @pid_file_directory ||= '/var/run/god'
98   end
99   
100   def self.pid_file_directory=(value)
101     @pid_file_directory = value
102   end
103   
104   # Instantiate a new, empty Watch object and pass it to the mandatory
105   # block. The attributes of the watch will be set by the configuration
106   # file.
107   def self.watch
108     self.internal_init
109     
110     w = Watch.new
111     yield(w)
112     
113     # if running, completely remove the watch (if necessary) to
114     # prepare for the reload
115     existing_watch = self.watches[w.name]
116     if self.running && existing_watch
117       self.unwatch(existing_watch)
118     end
119     
120     # ensure the new watch has a unique name
121     if self.watches[w.name] || self.groups[w.name]
122       abort "Watch name '#{w.name}' already used for a Watch or Group"
123     end
124     
125     # ensure watch is internally valid
126     w.valid? || abort("Watch '#{w.name}' is not valid (see above)")
127     
128     # add to list of watches
129     self.watches[w.name] = w
130     
131     # add to pending watches
132     self.pending_watches << w
133     
134     # add to group if specified
135     if w.group
136       # ensure group name hasn't been used for a watch already
137       if self.watches[w.group]
138         abort "Group name '#{w.group}' already used for a Watch"
139       end
140     
141       self.groups[w.group] ||= []
142       self.groups[w.group] << w
143     end
145     # register watch
146     w.register!
147   end
148   
149   def self.unwatch(watch)
150     # unmonitor
151     watch.unmonitor
152     
153     # unregister
154     watch.unregister!
155     
156     # remove from watches
157     self.watches.delete(watch.name)
158     
159     # remove from groups
160     if watch.group
161       self.groups[watch.group].delete(watch)
162     end
163   end
164   
165   def self.control(name, command)
166     # get the list of watches
167     watches = Array(self.watches[name] || self.groups[name])
168   
169     # do the command
170     case command
171       when "start", "monitor"
172         watches.each { |w| w.monitor }
173       when "restart"
174         watches.each { |w| w.move(:restart) }
175       when "stop"
176         watches.each { |w| w.unmonitor.action(:stop) }
177       when "unmonitor"
178         watches.each { |w| w.unmonitor }
179       else
180         raise InvalidCommandError.new
181     end
182     
183     watches
184   end
185   
186   def self.running_load(code)
187     eval(code)
188     self.pending_watches.each { |w| w.monitor if w.autostart? }
189     watches = self.pending_watches.dup
190     self.pending_watches.clear
191     watches
192   end
193   
194   def self.load(glob)
195     Dir[glob].each do |f|
196       Kernel.load f
197     end
198   end
199   
200   def self.start
201     # make sure there's something to do
202     if self.watches.nil? || self.watches.empty?
203       abort "You must specify at least one watch!"
204     end
205     
206     # start event handler system
207     EventHandler.start if EventHandler.loaded?
208     
209     # start the timer system
210     Timer.get
212     # start monitoring any watches set to autostart
213     self.watches.values.each { |w| w.monitor if w.autostart? }
214     
215     # clear pending watches
216     self.pending_watches.clear
217     
218     # mark as running
219     self.running = true
220     
221     # join the timer thread so we don't exit
222     Timer.get.join
223   end
224   
225   def self.at_exit
226     self.start
227   end
230 at_exit do
231   God.at_exit