prevent carry-over conditions
[god.git] / lib / god / watch.rb
blobce698d3fd4a8b537c222f82ba5d71e13d464b33c
1 require 'etc'
2 require 'forwardable'
4 module God
5   
6   class Watch < Task
7     VALID_STATES = [:init, :up, :start, :restart]
8     INITIAL_STATE = :init
9     
10     # config
11     attr_accessor :grace, :start_grace, :stop_grace, :restart_grace
12     
13     extend Forwardable
14     def_delegators :@process, :name, :uid, :gid, :start, :stop, :restart,
15                               :name=, :uid=, :gid=, :start=, :stop=, :restart=,
16                               :pid_file, :pid_file=, :log, :log=, :alive?, :pid
17     # 
18     def initialize
19       super
20       
21       @process = God::Process.new
22       
23       # valid states
24       self.valid_states = VALID_STATES
25       self.initial_state = INITIAL_STATE
26       
27       # no grace period by default
28       self.grace = self.start_grace = self.stop_grace = self.restart_grace = 0
29     end
30     
31     def valid?
32       super && @process.valid?
33     end
34     
35     ###########################################################################
36     #
37     # Behavior
38     #
39     ###########################################################################
40     
41     def behavior(kind)
42       # create the behavior
43       begin
44         b = Behavior.generate(kind, self)
45       rescue NoSuchBehaviorError => e
46         abort e.message
47       end
48       
49       # send to block so config can set attributes
50       yield(b) if block_given?
51       
52       # abort if the Behavior is invalid, the Behavior will have printed
53       # out its own error messages by now
54       abort unless b.valid?
55       
56       self.behaviors << b
57     end
58     
59     ###########################################################################
60     #
61     # Simple mode
62     #
63     ###########################################################################
64     
65     def start_if
66       self.transition(:up, :start) do |on|
67         yield(on)
68       end
69     end
70     
71     def restart_if
72       self.transition(:up, :restart) do |on|
73         yield(on)
74       end
75     end
76     
77     ###########################################################################
78     #
79     # Lifecycle
80     #
81     ###########################################################################
82     
83     # Enable monitoring
84     def monitor
85       # start monitoring at the first available of the init or up states
86       if !self.metrics[:init].empty?
87         self.move(:init)
88       else
89         self.move(:up)
90       end
91     end
92     
93     ###########################################################################
94     #
95     # Actions
96     #
97     ###########################################################################
98     
99     def action(a, c = nil)
100       case a
101       when :start
102         call_action(c, :start)
103         sleep(self.start_grace + self.grace)
104       when :restart
105         if self.restart
106           call_action(c, :restart)
107         else
108           action(:stop, c)
109           action(:start, c)
110         end
111         sleep(self.restart_grace + self.grace)
112       when :stop
113         call_action(c, :stop)
114         sleep(self.stop_grace + self.grace)
115       end
116     end
117     
118     def call_action(condition, action)
119       # before
120       before_items = self.behaviors
121       before_items += [condition] if condition
122       before_items.each do |b|
123         info = b.send("before_#{action}")
124         if info
125           msg = "#{self.name} before_#{action}: #{info} (#{b.base_name})"
126           applog(self, :info, msg)
127         end
128       end
129       
130       # log
131       if self.send(action)
132         msg = "#{self.name} #{action}: #{self.send(action).to_s}"
133         applog(self, :info, msg)
134       end
135       
136       @process.call_action(action)
137       
138       # after
139       after_items = self.behaviors
140       after_items += [condition] if condition
141       after_items.each do |b|
142         info = b.send("after_#{action}")
143         if info
144           msg = "#{self.name} after_#{action}: #{info} (#{b.base_name})"
145           applog(self, :info, msg)
146         end
147       end
148     end
149     
150     ###########################################################################
151     #
152     # Registration
153     #
154     ###########################################################################
155     
156     def register!
157       God.registry.add(@process)
158     end
159     
160     def unregister!
161       God.registry.remove(@process)
162     end
163   end
164