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