Register the watch's process at the end of meddle.
[god.git] / lib / god / watch.rb
blobde0872688e82c3ffafe3af138ac18b52a6d3eaa4
1 require 'etc'
2 require 'forwardable'
4 module God
5   
6   class Watch < Base
7     VALID_STATES = [:init, :up, :start, :restart]
8     
9     # config
10     attr_accessor :state, :interval, :group,
11                   :grace, :start_grace, :stop_grace, :restart_grace
12                   
13     
14     attr_writer   :autostart
15     def autostart?; @autostart; end
16     
17     extend Forwardable
18     def_delegators :@process, :name, :uid, :gid, :start, :stop, :restart,
19                               :name=, :uid=, :gid=, :start=, :stop=, :restart=
20     
21     # api
22     attr_accessor :behaviors, :metrics
23     
24     # internal
25     attr_accessor :mutex
26     
27     # 
28     def initialize(meddle)
29       @autostart ||= true
30       @process = God::Process.new
31       
32       @meddle = meddle
33             
34       # no grace period by default
35       self.grace = self.start_grace = self.stop_grace = self.restart_grace = 0
36       
37       # the list of behaviors
38       self.behaviors = []
39       
40       # the list of conditions for each action
41       self.metrics = {:init => [],
42                       :start => [],
43                       :restart => [],
44                       :up => []}
45                          
46       # mutex
47       self.mutex = Mutex.new
48     end
49     
50     def behavior(kind)
51       # create the behavior
52       begin
53         b = Behavior.generate(kind)
54       rescue NoSuchBehaviorError => e
55         abort e.message
56       end
57       
58       # send to block so config can set attributes
59       yield(b) if block_given?
60       
61       # abort if the Behavior is invalid, the Behavior will have printed
62       # out its own error messages by now
63       abort unless b.valid?
64       
65       self.behaviors << b
66     end
67     
68     ###########################################################################
69     #
70     # Advanced mode
71     #
72     ###########################################################################
73     
74     # Define a transition handler which consists of a set of conditions
75     def transition(start_states, end_states)
76       # convert to into canonical hash form
77       canonical_end_states = canonical_hash_form(end_states)
78       
79       # for each start state do
80       Array(start_states).each do |start_state|
81         # validate start state
82         unless VALID_STATES.include?(start_state)
83           abort "Invalid state :#{start_state}. Must be one of the symbols #{VALID_STATES.map{|x| ":#{x}"}.join(', ')}"
84         end
85         
86         # create a new metric to hold the watch, end states, and conditions
87         m = Metric.new(self, canonical_end_states)
88         
89         # let the config file define some conditions on the metric
90         yield(m)
91         
92         # record the metric
93         self.metrics[start_state] << m
94       end
95     end
96     
97     ###########################################################################
98     #
99     # Simple mode
100     #
101     ###########################################################################
102     
103     def start_if
104       self.transition(:up, :start) do |on|
105         yield(on)
106       end
107     end
108     
109     def restart_if
110       self.transition(:up, :restart) do |on|
111         yield(on)
112       end
113     end
114     
115     ###########################################################################
116     #
117     # Lifecycle
118     #
119     ###########################################################################
120         
121     # Enable monitoring
122     def monitor
123       # start monitoring at the first available of the init or up states
124       if !self.metrics[:init].empty?
125         self.move(:init)
126       else
127         self.move(:up)
128       end
129     end
130     
131     # Disable monitoring
132     def unmonitor
133       self.move(nil)
134     end
135     
136     # Move from one state to another
137     def move(to_state)
138       msg = "move '#{self.state}' to '#{to_state}'"
139       Syslog.debug(msg)
140       puts msg
141        
142       # cleanup from current state
143       if from_state = self.state
144         self.metrics[from_state].each { |m| m.disable }
145       end
146       
147       # perform action (if available)
148       self.action(to_state)
149       
150       # move to new state
151       if to_state
152         self.metrics[to_state].each { |m| m.enable }
153       end
154       
155       # set state
156       self.state = to_state
157       
158       # return self
159       self
160     end
161     
162     def action(a, c = nil)
163       case a
164       when :start
165         Syslog.debug(self.start)
166         puts self.start
167         call_action(c, :start)
168         sleep(self.start_grace + self.grace)
169       when :restart
170         if self.restart
171           Syslog.debug(self.restart)
172           puts self.restart
173           call_action(c, :restart)
174         else
175           action(:stop, c)
176           action(:start, c)
177         end
178         sleep(self.restart_grace + self.grace)
179       when :stop
180         Syslog.debug(self.stop)
181         puts self.stop
182         call_action(c, :stop)
183         sleep(self.stop_grace + self.grace)
184       end      
185     end
186     
187     def call_action(condition, action)
188       # before
189       before_items = self.behaviors
190       before_items += [condition] if condition
191       before_items.each { |b| b.send("before_#{action}") }
192       
193       @process.call_action(action)
194       
195       # after
196       after_items = self.behaviors
197       after_items += [condition] if condition
198       after_items.each { |b| b.send("after_#{action}") }
199     end
200     
201     def canonical_hash_form(to)
202       to.instance_of?(Symbol) ? {true => to} : to
203     end
204     
205     def register!
206       God.registry.add @process
207     end
208   end
209