god 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/History.txt +43 -7
  2. data/Manifest.txt +20 -4
  3. data/Rakefile +1 -1
  4. data/bin/god +263 -195
  5. data/examples/events.god +66 -34
  6. data/examples/gravatar.god +25 -12
  7. data/init/god +42 -0
  8. data/lib/god/behavior.rb +9 -29
  9. data/lib/god/behaviors/clean_pid_file.rb +6 -2
  10. data/lib/god/behaviors/notify_when_flapping.rb +4 -4
  11. data/lib/god/condition.rb +48 -6
  12. data/lib/god/conditions/always.rb +5 -1
  13. data/lib/god/conditions/cpu_usage.rb +13 -5
  14. data/lib/god/conditions/degrading_lambda.rb +8 -3
  15. data/lib/god/conditions/flapping.rb +97 -0
  16. data/lib/god/conditions/http_response_code.rb +97 -0
  17. data/lib/god/conditions/lambda.rb +8 -2
  18. data/lib/god/conditions/memory_usage.rb +13 -5
  19. data/lib/god/conditions/process_exits.rb +11 -3
  20. data/lib/god/conditions/process_running.rb +22 -4
  21. data/lib/god/conditions/tries.rb +16 -5
  22. data/lib/god/configurable.rb +54 -0
  23. data/lib/god/contact.rb +106 -0
  24. data/lib/god/contacts/email.rb +73 -0
  25. data/lib/god/errors.rb +3 -0
  26. data/lib/god/hub.rb +138 -33
  27. data/lib/god/logger.rb +21 -4
  28. data/lib/god/metric.rb +3 -4
  29. data/lib/god/process.rb +93 -49
  30. data/lib/god/socket.rb +60 -0
  31. data/lib/god/task.rb +233 -0
  32. data/lib/god/trigger.rb +43 -0
  33. data/lib/god/watch.rb +48 -114
  34. data/lib/god.rb +216 -63
  35. data/test/configs/child_events/child_events.god +20 -1
  36. data/test/configs/child_polls/child_polls.god +26 -6
  37. data/test/configs/child_polls/simple_server.rb +10 -1
  38. data/test/configs/contact/contact.god +74 -0
  39. data/test/configs/contact/simple_server.rb +3 -0
  40. data/test/configs/daemon_events/daemon_events.god +5 -2
  41. data/test/configs/daemon_events/simple_server.rb +2 -0
  42. data/test/configs/daemon_events/simple_server_stop.rb +9 -0
  43. data/test/configs/degrading_lambda/degrading_lambda.god +1 -3
  44. data/test/configs/task/logs/.placeholder +0 -0
  45. data/test/configs/task/task.god +26 -0
  46. data/test/helper.rb +19 -11
  47. data/test/test_conditions_http_response_code.rb +115 -0
  48. data/test/test_conditions_process_running.rb +2 -2
  49. data/test/test_conditions_tries.rb +21 -0
  50. data/test/test_contact.rb +109 -0
  51. data/test/test_god.rb +101 -17
  52. data/test/test_hub.rb +64 -1
  53. data/test/test_process.rb +43 -56
  54. data/test/{test_server.rb → test_socket.rb} +6 -20
  55. data/test/test_task.rb +86 -0
  56. data/test/test_trigger.rb +59 -0
  57. data/test/test_watch.rb +32 -7
  58. metadata +27 -8
  59. data/lib/god/reporter.rb +0 -25
  60. data/lib/god/server.rb +0 -37
  61. data/test/test_reporter.rb +0 -18
data/lib/god/task.rb ADDED
@@ -0,0 +1,233 @@
1
+ module God
2
+
3
+ class Task
4
+ attr_accessor :name, :interval, :group, :valid_states, :initial_state
5
+
6
+ attr_writer :autostart
7
+ def autostart?; @autostart; end
8
+
9
+ # api
10
+ attr_accessor :state, :behaviors, :metrics
11
+
12
+ # internal
13
+ attr_accessor :mutex
14
+
15
+ def initialize
16
+ @autostart ||= true
17
+
18
+ # initial state is unmonitored
19
+ self.state = :unmonitored
20
+
21
+ # the list of behaviors
22
+ self.behaviors = []
23
+
24
+ # the list of conditions for each action
25
+ self.metrics = {nil => [], :unmonitored => []}
26
+
27
+ # mutex
28
+ self.mutex = Monitor.new
29
+ end
30
+
31
+ def prepare
32
+ self.valid_states.each do |state|
33
+ self.metrics[state] ||= []
34
+ end
35
+ end
36
+
37
+ def valid?
38
+ valid = true
39
+
40
+ # a name must be specified
41
+ if self.name.nil?
42
+ valid = false
43
+ LOG.log(self, :error, "No name was specified")
44
+ end
45
+
46
+ # valid_states must be specified
47
+ if self.valid_states.nil?
48
+ valid = false
49
+ LOG.log(self, :error, "No valid_states array was specified")
50
+ end
51
+
52
+ # valid_states must be specified
53
+ if self.initial_state.nil?
54
+ valid = false
55
+ LOG.log(self, :error, "No initial_state was specified")
56
+ end
57
+
58
+ valid
59
+ end
60
+
61
+ ###########################################################################
62
+ #
63
+ # Advanced mode
64
+ #
65
+ ###########################################################################
66
+
67
+ def canonical_hash_form(to)
68
+ to.instance_of?(Symbol) ? {true => to} : to
69
+ end
70
+
71
+ # Define a transition handler which consists of a set of conditions
72
+ def transition(start_states, end_states)
73
+ # convert end_states into canonical hash form
74
+ canonical_end_states = canonical_hash_form(end_states)
75
+
76
+ Array(start_states).each do |start_state|
77
+ # validate start state
78
+ unless self.valid_states.include?(start_state)
79
+ abort "Invalid state :#{start_state}. Must be one of the symbols #{self.valid_states.map{|x| ":#{x}"}.join(', ')}"
80
+ end
81
+
82
+ # create a new metric to hold the watch, end states, and conditions
83
+ m = Metric.new(self, canonical_end_states)
84
+
85
+ if block_given?
86
+ # let the config file define some conditions on the metric
87
+ yield(m)
88
+ else
89
+ # add an :always condition if no block
90
+ m.condition(:always) do |c|
91
+ c.what = true
92
+ end
93
+ end
94
+
95
+ # record the metric
96
+ self.metrics[start_state] ||= []
97
+ self.metrics[start_state] << m
98
+ end
99
+ end
100
+
101
+ def lifecycle
102
+ # create a new metric to hold the watch and conditions
103
+ m = Metric.new(self)
104
+
105
+ # let the config file define some conditions on the metric
106
+ yield(m)
107
+
108
+ # record the metric
109
+ self.metrics[nil] << m
110
+ end
111
+
112
+ ###########################################################################
113
+ #
114
+ # Lifecycle
115
+ #
116
+ ###########################################################################
117
+
118
+ # Enable monitoring
119
+ def monitor
120
+ self.move(self.initial_state)
121
+ end
122
+
123
+ # Disable monitoring
124
+ def unmonitor
125
+ self.move(:unmonitored)
126
+ end
127
+
128
+ # Move from one state to another
129
+ def move(to_state)
130
+ self.mutex.synchronize do
131
+ orig_to_state = to_state
132
+ from_state = self.state
133
+
134
+ msg = "#{self.name} move '#{from_state}' to '#{to_state}'"
135
+ Syslog.debug(msg)
136
+ LOG.log(self, :info, msg)
137
+
138
+ # cleanup from current state
139
+ self.metrics[from_state].each { |m| m.disable }
140
+
141
+ if to_state == :unmonitored
142
+ self.metrics[nil].each { |m| m.disable }
143
+ end
144
+
145
+ # perform action
146
+ self.action(to_state)
147
+
148
+ # enable simple mode
149
+ if [:start, :restart].include?(to_state) && self.metrics[to_state].empty?
150
+ to_state = :up
151
+ end
152
+
153
+ # move to new state
154
+ self.metrics[to_state].each { |m| m.enable }
155
+
156
+ # if no from state, enable lifecycle metric
157
+ if from_state == :unmonitored
158
+ self.metrics[nil].each { |m| m.enable }
159
+ end
160
+
161
+ # set state
162
+ self.state = to_state
163
+
164
+ # trigger
165
+ Trigger.broadcast(self, :state_change, [from_state, orig_to_state])
166
+
167
+ # return self
168
+ self
169
+ end
170
+ end
171
+
172
+ ###########################################################################
173
+ #
174
+ # Actions
175
+ #
176
+ ###########################################################################
177
+
178
+ def method_missing(sym, *args)
179
+ unless (sym.to_s =~ /=$/)
180
+ super
181
+ end
182
+
183
+ base = sym.to_s.chop.intern
184
+
185
+ unless self.valid_states.include?(base)
186
+ super
187
+ end
188
+
189
+ self.class.send(:attr_accessor, base)
190
+ self.send(sym, *args)
191
+ end
192
+
193
+ # +a+ is the action Symbol
194
+ # +c+ is the Condition
195
+ def action(a, c = nil)
196
+ if self.respond_to?(a)
197
+ command = self.send(a)
198
+
199
+ case command
200
+ when String
201
+ msg = "#{self.name} #{a}: #{command}"
202
+ Syslog.debug(msg)
203
+ LOG.log(self, :info, msg)
204
+
205
+ system(command)
206
+ when Proc
207
+ msg = "#{self.name} #{a}: lambda"
208
+ Syslog.debug(msg)
209
+ LOG.log(self, :info, msg)
210
+
211
+ command.call
212
+ else
213
+ raise NotImplementedError
214
+ end
215
+ end
216
+ end
217
+
218
+ ###########################################################################
219
+ #
220
+ # Registration
221
+ #
222
+ ###########################################################################
223
+
224
+ def register!
225
+ # override if necessary
226
+ end
227
+
228
+ def unregister!
229
+ # override if necessary
230
+ end
231
+ end
232
+
233
+ end
@@ -0,0 +1,43 @@
1
+ module God
2
+
3
+ class Trigger
4
+
5
+ class << self
6
+ attr_accessor :triggers # {task.name => condition}
7
+ end
8
+
9
+ # init
10
+ self.triggers = {}
11
+ @mutex = Mutex.new
12
+
13
+ def self.register(condition)
14
+ @mutex.synchronize do
15
+ self.triggers[condition.watch.name] ||= []
16
+ self.triggers[condition.watch.name] << condition
17
+ end
18
+ end
19
+
20
+ def self.deregister(condition)
21
+ @mutex.synchronize do
22
+ self.triggers[condition.watch.name].delete(condition)
23
+ self.triggers.delete(condition.watch.name) if self.triggers[condition.watch.name].empty?
24
+ end
25
+ end
26
+
27
+ def self.broadcast(task, message, payload)
28
+ return unless self.triggers[task.name]
29
+
30
+ @mutex.synchronize do
31
+ self.triggers[task.name].each do |t|
32
+ t.process(message, payload)
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.reset
38
+ self.triggers.clear
39
+ end
40
+
41
+ end
42
+
43
+ end
data/lib/god/watch.rb CHANGED
@@ -3,51 +3,33 @@ require 'forwardable'
3
3
 
4
4
  module God
5
5
 
6
- class Watch
6
+ class Watch < Task
7
7
  VALID_STATES = [:init, :up, :start, :restart]
8
+ INITIAL_STATE = :init
8
9
 
9
10
  # 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
11
+ attr_accessor :grace, :start_grace, :stop_grace, :restart_grace
16
12
 
17
13
  extend Forwardable
18
14
  def_delegators :@process, :name, :uid, :gid, :start, :stop, :restart,
19
15
  :name=, :uid=, :gid=, :start=, :stop=, :restart=,
20
- :pid_file, :pid_file=, :log, :log=, :alive?
21
-
22
- # api
23
- attr_accessor :behaviors, :metrics
24
-
25
- # internal
26
- attr_accessor :mutex
27
-
16
+ :pid_file, :pid_file=, :log, :log=, :alive?
28
17
  #
29
18
  def initialize
30
- @autostart ||= true
19
+ super
20
+
31
21
  @process = God::Process.new
32
-
33
- # no grace period by default
34
- self.grace = self.start_grace = self.stop_grace = self.restart_grace = 0
35
22
 
36
- # the list of behaviors
37
- self.behaviors = []
23
+ # valid states
24
+ self.valid_states = VALID_STATES
25
+ self.initial_state = INITIAL_STATE
38
26
 
39
- # the list of conditions for each action
40
- self.metrics = {:init => [],
41
- :start => [],
42
- :restart => [],
43
- :up => []}
44
-
45
- # mutex
46
- self.mutex = Mutex.new
27
+ # no grace period by default
28
+ self.grace = self.start_grace = self.stop_grace = self.restart_grace = 0
47
29
  end
48
30
 
49
31
  def valid?
50
- @process.valid?
32
+ super && @process.valid?
51
33
  end
52
34
 
53
35
  ###########################################################################
@@ -74,35 +56,6 @@ module God
74
56
  self.behaviors << b
75
57
  end
76
58
 
77
- ###########################################################################
78
- #
79
- # Advanced mode
80
- #
81
- ###########################################################################
82
-
83
- # Define a transition handler which consists of a set of conditions
84
- def transition(start_states, end_states)
85
- # convert to into canonical hash form
86
- canonical_end_states = canonical_hash_form(end_states)
87
-
88
- # for each start state do
89
- Array(start_states).each do |start_state|
90
- # validate start state
91
- unless VALID_STATES.include?(start_state)
92
- abort "Invalid state :#{start_state}. Must be one of the symbols #{VALID_STATES.map{|x| ":#{x}"}.join(', ')}"
93
- end
94
-
95
- # create a new metric to hold the watch, end states, and conditions
96
- m = Metric.new(self, canonical_end_states)
97
-
98
- # let the config file define some conditions on the metric
99
- yield(m)
100
-
101
- # record the metric
102
- self.metrics[start_state] << m
103
- end
104
- end
105
-
106
59
  ###########################################################################
107
60
  #
108
61
  # Simple mode
@@ -126,7 +79,7 @@ module God
126
79
  # Lifecycle
127
80
  #
128
81
  ###########################################################################
129
-
82
+
130
83
  # Enable monitoring
131
84
  def monitor
132
85
  # start monitoring at the first available of the init or up states
@@ -137,56 +90,19 @@ module God
137
90
  end
138
91
  end
139
92
 
140
- # Disable monitoring
141
- def unmonitor
142
- self.move(nil)
143
- end
144
-
145
- # Move from one state to another
146
- def move(to_state)
147
- msg = "#{self.name} move '#{self.state}' to '#{to_state}'"
148
- Syslog.debug(msg)
149
- LOG.log(self, :info, msg)
150
-
151
- # cleanup from current state
152
- from_state = self.state
153
- if from_state
154
- self.metrics[from_state].each { |m| m.disable }
155
- end
156
-
157
- # perform action
158
- self.action(to_state)
159
-
160
- # enable simple mode
161
- if [:start, :restart].include?(to_state) && self.metrics[to_state].empty?
162
- to_state = :up
163
- end
164
-
165
- # move to new state
166
- if to_state
167
- self.metrics[to_state].each { |m| m.enable }
168
- end
169
-
170
- # set state
171
- self.state = to_state
172
-
173
- # return self
174
- self
175
- end
93
+ ###########################################################################
94
+ #
95
+ # Actions
96
+ #
97
+ ###########################################################################
176
98
 
177
99
  def action(a, c = nil)
178
100
  case a
179
101
  when :start
180
- msg = "#{self.name} start: #{self.start.to_s}"
181
- Syslog.debug(msg)
182
- LOG.log(self, :info, msg)
183
102
  call_action(c, :start)
184
103
  sleep(self.start_grace + self.grace)
185
104
  when :restart
186
105
  if self.restart
187
- msg = "#{self.name} restart: #{self.restart.to_s}"
188
- Syslog.debug(msg)
189
- LOG.log(self, :info, msg)
190
106
  call_action(c, :restart)
191
107
  else
192
108
  action(:stop, c)
@@ -194,33 +110,51 @@ module God
194
110
  end
195
111
  sleep(self.restart_grace + self.grace)
196
112
  when :stop
197
- if self.stop
198
- msg = "#{self.name} stop: #{self.stop.to_s}"
199
- Syslog.debug(msg)
200
- LOG.log(self, :info, msg)
201
- end
202
113
  call_action(c, :stop)
203
114
  sleep(self.stop_grace + self.grace)
204
- end
115
+ end
205
116
  end
206
117
 
207
118
  def call_action(condition, action)
208
119
  # before
209
120
  before_items = self.behaviors
210
121
  before_items += [condition] if condition
211
- before_items.each { |b| b.send("before_#{action}") }
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
+ Syslog.debug(msg)
127
+ LOG.log(self, :info, msg)
128
+ end
129
+ end
130
+
131
+ # log
132
+ if self.send(action)
133
+ msg = "#{self.name} #{action}: #{self.send(action).to_s}"
134
+ Syslog.debug(msg)
135
+ LOG.log(self, :info, msg)
136
+ end
212
137
 
213
138
  @process.call_action(action)
214
-
139
+
215
140
  # after
216
141
  after_items = self.behaviors
217
142
  after_items += [condition] if condition
218
- after_items.each { |b| b.send("after_#{action}") }
143
+ after_items.each do |b|
144
+ info = b.send("after_#{action}")
145
+ if info
146
+ msg = "#{self.name} after_#{action}: #{info} (#{b.base_name})"
147
+ Syslog.debug(msg)
148
+ LOG.log(self, :info, msg)
149
+ end
150
+ end
219
151
  end
220
152
 
221
- def canonical_hash_form(to)
222
- to.instance_of?(Symbol) ? {true => to} : to
223
- end
153
+ ###########################################################################
154
+ #
155
+ # Registration
156
+ #
157
+ ###########################################################################
224
158
 
225
159
  def register!
226
160
  God.registry.add(@process)