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