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