god 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/History.txt +67 -1
  2. data/Manifest.txt +3 -4
  3. data/Rakefile +1 -1
  4. data/bin/god +19 -1
  5. data/lib/god.rb +86 -49
  6. data/lib/god/cli/command.rb +7 -1
  7. data/lib/god/cli/run.rb +58 -0
  8. data/lib/god/condition.rb +6 -2
  9. data/lib/god/conditions/cpu_usage.rb +7 -6
  10. data/lib/god/conditions/http_response_code.rb +5 -1
  11. data/lib/god/conditions/memory_usage.rb +7 -6
  12. data/lib/god/conditions/process_exits.rb +15 -10
  13. data/lib/god/conditions/process_running.rb +17 -13
  14. data/lib/god/diagnostics.rb +37 -0
  15. data/lib/god/driver.rb +108 -0
  16. data/lib/god/event_handler.rb +41 -1
  17. data/lib/god/logger.rb +69 -19
  18. data/lib/god/metric.rb +2 -2
  19. data/lib/god/process.rb +84 -27
  20. data/lib/god/task.rb +286 -29
  21. data/lib/god/timeline.rb +20 -31
  22. data/lib/god/watch.rb +26 -15
  23. data/test/configs/child_events/child_events.god +0 -5
  24. data/test/configs/child_polls/simple_server.rb +1 -1
  25. data/test/configs/daemon_events/simple_server_stop.rb +2 -0
  26. data/test/configs/stress/stress.god +1 -1
  27. data/test/configs/test.rb +12 -28
  28. data/test/test_condition.rb +8 -0
  29. data/test/test_conditions_http_response_code.rb +5 -5
  30. data/test/test_conditions_process_running.rb +6 -4
  31. data/test/test_driver.rb +11 -0
  32. data/test/test_event_handler.rb +7 -0
  33. data/test/test_god.rb +63 -62
  34. data/test/test_metric.rb +0 -16
  35. data/test/test_process.rb +29 -1
  36. data/test/test_task.rb +177 -1
  37. data/test/test_timeline.rb +2 -1
  38. data/test/test_watch.rb +24 -6
  39. metadata +6 -8
  40. data/lib/god/hub.rb +0 -222
  41. data/lib/god/timer.rb +0 -87
  42. data/test/test_hub.rb +0 -240
  43. data/test/test_timer.rb +0 -69
@@ -69,20 +69,4 @@ class TestMetric < Test::Unit::TestCase
69
69
  @metric.condition(:invalid) { }
70
70
  end
71
71
  end
72
-
73
- # enable
74
-
75
- def test_enable_should_attach_conditions
76
- @metric.condition(:fake_poll_condition)
77
- Hub.expects(:attach).with(@metric.conditions.first, @metric)
78
- @metric.enable
79
- end
80
-
81
- # disable
82
-
83
- def test_disable_should_detach_conditions
84
- @metric.condition(:fake_poll_condition)
85
- Hub.expects(:detach).with(@metric.conditions.first)
86
- @metric.disable
87
- end
88
72
  end
@@ -144,6 +144,34 @@ class TestProcessDaemon < Test::Unit::TestCase
144
144
  end
145
145
  end
146
146
 
147
+ # pid
148
+
149
+ def test_pid_should_return_integer_for_valid_pid_files
150
+ File.stubs(:read).returns("123")
151
+ assert_equal 123, @p.pid
152
+ end
153
+
154
+ def test_pid_should_return_nil_for_missing_files
155
+ @p.pid_file = ''
156
+ assert_equal nil, @p.pid
157
+ end
158
+
159
+ def test_pid_should_return_nil_for_invalid_pid_files
160
+ File.stubs(:read).returns("four score and seven years ago")
161
+ assert_equal nil, @p.pid
162
+ end
163
+
164
+ def test_pid_should_retain_last_pid_value_if_pid_file_is_removed
165
+ File.stubs(:read).returns("123")
166
+ assert_equal 123, @p.pid
167
+
168
+ File.stubs(:read).returns("")
169
+ assert_equal 123, @p.pid
170
+
171
+ File.stubs(:read).returns("246")
172
+ assert_equal 246, @p.pid
173
+ end
174
+
147
175
  # defaul_pid_file
148
176
 
149
177
  def test_default_pid_file
@@ -156,7 +184,7 @@ class TestProcessDaemon < Test::Unit::TestCase
156
184
  def test_call_action_with_string_should_call_system
157
185
  @p.start = "do something"
158
186
  @p.expects(:fork)
159
- Process.expects(:waitpid)
187
+ Process.expects(:waitpid2).returns([123, 0])
160
188
  @p.call_action(:start)
161
189
  end
162
190
 
@@ -67,20 +67,196 @@ class TestTask < Test::Unit::TestCase
67
67
 
68
68
  def test_action_should_send_string_commands_to_system
69
69
  @task.foo = 'foo'
70
+ Thread.current.stubs(:==).returns(true)
70
71
  @task.expects(:system).with('foo')
71
72
  no_stdout { @task.action(:foo, nil) }
72
73
  end
73
74
 
74
- def test_action_should_cal_lambda_commands
75
+ def test_action_should_call_lambda_commands
75
76
  @task.foo = lambda { }
77
+ Thread.current.stubs(:==).returns(true)
76
78
  @task.foo.expects(:call)
77
79
  no_stdout { @task.action(:foo, nil) }
78
80
  end
79
81
 
80
82
  def test_action_should_raise_not_implemented_on_non_string_or_lambda_action
83
+ Thread.current.stubs(:==).returns(true)
81
84
  assert_raise NotImplementedError do
82
85
  @task.foo = 7
83
86
  @task.action(:foo, nil)
84
87
  end
85
88
  end
89
+
90
+ def test_action_from_outside_driver_should_send_message_to_driver
91
+ @task.foo = 'foo'
92
+ @task.driver.expects(:message).with(:action, [:foo, nil])
93
+ @task.action(:foo, nil)
94
+ end
95
+
96
+ # attach
97
+
98
+ def test_attach_should_schedule_for_poll_condition
99
+ c = Conditions::FakePollCondition.new
100
+ @task.driver.expects(:schedule).with(c, 0)
101
+ @task.attach(c)
102
+ end
103
+
104
+ def test_attach_should_regsiter_for_event_condition
105
+ c = Conditions::FakeEventCondition.new
106
+ c.expects(:register)
107
+ @task.attach(c)
108
+ end
109
+
110
+ # detach
111
+
112
+ def test_detach_should_reset_poll_condition
113
+ c = Conditions::FakePollCondition.new
114
+ c.expects(:reset)
115
+ c.expects(:deregister).never
116
+ @task.detach(c)
117
+ end
118
+
119
+ def test_detach_should_deregister_event_conditions
120
+ c = Conditions::FakeEventCondition.new
121
+ c.expects(:deregister).once
122
+ @task.detach(c)
123
+ end
124
+
125
+ # trigger
126
+
127
+ def test_trigger_should_send_message_to_driver
128
+ c = Conditions::FakePollCondition.new
129
+ @task.driver.expects(:message).with(:handle_event, [c])
130
+ @task.trigger(c)
131
+ end
132
+
133
+ # handle_poll
134
+
135
+ def test_handle_poll_no_change_should_reschedule
136
+ c = Conditions::FakePollCondition.new
137
+ c.watch = @task
138
+ c.interval = 10
139
+
140
+ m = Metric.new(@task, {true => :up})
141
+ @task.directory[c] = m
142
+
143
+ c.expects(:test).returns(false)
144
+ @task.driver.expects(:schedule)
145
+
146
+ no_stdout do
147
+ @task.handle_poll(c)
148
+ end
149
+ end
150
+
151
+ def test_handle_poll_change_should_move
152
+ c = Conditions::FakePollCondition.new
153
+ c.watch = @task
154
+ c.interval = 10
155
+
156
+ m = Metric.new(@task, {true => :up})
157
+ @task.directory[c] = m
158
+
159
+ c.expects(:test).returns(true)
160
+ @task.expects(:move).with(:up)
161
+
162
+ no_stdout do
163
+ @task.handle_poll(c)
164
+ end
165
+ end
166
+
167
+ def test_handle_poll_should_use_overridden_transition
168
+ c = Conditions::Tries.new
169
+ c.watch = @task
170
+ c.times = 1
171
+ c.transition = :start
172
+ c.prepare
173
+
174
+ m = Metric.new(@task, {true => :up})
175
+ @task.directory[c] = m
176
+
177
+ @task.expects(:move).with(:start)
178
+
179
+ no_stdout do
180
+ @task.handle_poll(c)
181
+ end
182
+ end
183
+
184
+ def test_handle_poll_should_notify_if_triggering
185
+ c = Conditions::FakePollCondition.new
186
+ c.watch = @task
187
+ c.interval = 10
188
+ c.notify = 'tom'
189
+
190
+ m = Metric.new(@task, {true => :up})
191
+ @task.directory[c] = m
192
+
193
+ c.expects(:test).returns(true)
194
+ @task.expects(:notify)
195
+
196
+ no_stdout do
197
+ @task.handle_poll(c)
198
+ end
199
+ end
200
+
201
+ def test_handle_poll_should_not_notify_if_not_triggering
202
+ c = Conditions::FakePollCondition.new
203
+ c.watch = @task
204
+ c.interval = 10
205
+ c.notify = 'tom'
206
+
207
+ m = Metric.new(@task, {true => :up})
208
+ @task.directory[c] = m
209
+
210
+ c.expects(:test).returns(false)
211
+ @task.expects(:notify).never
212
+
213
+ no_stdout do
214
+ @task.handle_poll(c)
215
+ end
216
+ end
217
+
218
+ # handle_event
219
+
220
+ def test_handle_event_should_move
221
+ c = Conditions::FakeEventCondition.new
222
+ c.watch = @task
223
+
224
+ m = Metric.new(@task, {true => :up})
225
+ @task.directory[c] = m
226
+
227
+ @task.expects(:move).with(:up)
228
+
229
+ no_stdout do
230
+ @task.handle_event(c)
231
+ end
232
+ end
233
+
234
+ def test_handle_event_should_notify_if_triggering
235
+ c = Conditions::FakeEventCondition.new
236
+ c.watch = @task
237
+ c.notify = 'tom'
238
+
239
+ m = Metric.new(@task, {true => :up})
240
+ @task.directory[c] = m
241
+
242
+ @task.expects(:notify)
243
+
244
+ no_stdout do
245
+ @task.handle_event(c)
246
+ end
247
+ end
248
+
249
+ def test_handle_event_should_not_notify_if_no_notify_set
250
+ c = Conditions::FakeEventCondition.new
251
+ c.watch = @task
252
+
253
+ m = Metric.new(@task, {true => :up})
254
+ @task.directory[c] = m
255
+
256
+ @task.expects(:notify).never
257
+
258
+ no_stdout do
259
+ @task.handle_event(c)
260
+ end
261
+ end
86
262
  end
@@ -18,7 +18,8 @@ class TestTimeline < Test::Unit::TestCase
18
18
  end
19
19
 
20
20
  def test_clear_should_clear_array
21
- @timeline.push(1)
21
+ @timeline << 1
22
+ assert_equal [1], @timeline
22
23
  assert_equal [], @timeline.clear
23
24
  end
24
25
 
@@ -37,12 +37,6 @@ class TestWatch < Test::Unit::TestCase
37
37
  assert_equal :unmonitored, @watch.state
38
38
  end
39
39
 
40
- # mutex
41
-
42
- def test_mutex_should_return_the_same_mutex_each_time
43
- assert_equal @watch.mutex, @watch.mutex
44
- end
45
-
46
40
  # valid?
47
41
 
48
42
  def test_valid?
@@ -132,6 +126,9 @@ class TestWatch < Test::Unit::TestCase
132
126
  # move
133
127
 
134
128
  def test_move_should_not_clean_up_if_from_state_is_nil
129
+ Thread.current.stubs(:==).returns(true)
130
+ @watch.driver.expects(:message).never
131
+
135
132
  metric = nil
136
133
 
137
134
  @watch.instance_eval do
@@ -150,6 +147,9 @@ class TestWatch < Test::Unit::TestCase
150
147
  end
151
148
 
152
149
  def test_move_should_clean_up_from_state_if_not_nil
150
+ Thread.current.stubs(:==).returns(true)
151
+ @watch.driver.expects(:message).never
152
+
153
153
  metric = nil
154
154
 
155
155
  @watch.instance_eval do
@@ -170,12 +170,18 @@ class TestWatch < Test::Unit::TestCase
170
170
  end
171
171
 
172
172
  def test_move_should_call_action
173
+ Thread.current.stubs(:==).returns(true)
174
+ @watch.driver.expects(:message).never
175
+
173
176
  @watch.expects(:action).with(:start)
174
177
 
175
178
  no_stdout { @watch.move(:start) }
176
179
  end
177
180
 
178
181
  def test_move_should_move_to_up_state_if_no_start_or_restart_metric
182
+ Thread.current.stubs(:==).returns(true)
183
+ @watch.driver.expects(:message).never
184
+
179
185
  [:start, :restart].each do |state|
180
186
  @watch.expects(:action)
181
187
  no_stdout { @watch.move(state) }
@@ -184,6 +190,9 @@ class TestWatch < Test::Unit::TestCase
184
190
  end
185
191
 
186
192
  def test_move_should_enable_destination_metric
193
+ Thread.current.stubs(:==).returns(true)
194
+ @watch.driver.expects(:message).never
195
+
187
196
  metric = nil
188
197
 
189
198
  @watch.instance_eval do
@@ -204,6 +213,9 @@ class TestWatch < Test::Unit::TestCase
204
213
  # action
205
214
 
206
215
  def test_action_should_pass_start_and_stop_actions_to_call_action
216
+ Thread.current.stubs(:==).returns(true)
217
+ @watch.driver.expects(:message).never
218
+
207
219
  c = Conditions::FakePollCondition.new
208
220
  [:start, :stop].each do |cmd|
209
221
  @watch.expects(:call_action).with(c, cmd)
@@ -212,6 +224,9 @@ class TestWatch < Test::Unit::TestCase
212
224
  end
213
225
 
214
226
  def test_action_should_do_stop_then_start_if_no_restart_command
227
+ Thread.current.stubs(:==).returns(true)
228
+ @watch.driver.expects(:message).never
229
+
215
230
  c = Conditions::FakePollCondition.new
216
231
  @watch.expects(:call_action).with(c, :stop)
217
232
  @watch.expects(:call_action).with(c, :start)
@@ -219,6 +234,9 @@ class TestWatch < Test::Unit::TestCase
219
234
  end
220
235
 
221
236
  def test_action_should_restart_to_call_action_if_present
237
+ Thread.current.stubs(:==).returns(true)
238
+ @watch.driver.expects(:message).never
239
+
222
240
  @watch.restart = lambda { }
223
241
  c = Conditions::FakePollCondition.new
224
242
  @watch.expects(:call_action).with(c, :restart)
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: god
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.6.0
7
- date: 2007-12-04 00:00:00 -08:00
6
+ version: 0.7.0
7
+ date: 2008-02-01 00:00:00 -08:00
8
8
  summary: Like monit, only awesome
9
9
  require_paths:
10
10
  - lib
@@ -66,12 +66,13 @@ files:
66
66
  - lib/god/contact.rb
67
67
  - lib/god/contacts/email.rb
68
68
  - lib/god/dependency_graph.rb
69
+ - lib/god/diagnostics.rb
70
+ - lib/god/driver.rb
69
71
  - lib/god/errors.rb
70
72
  - lib/god/event_handler.rb
71
73
  - lib/god/event_handlers/dummy_handler.rb
72
74
  - lib/god/event_handlers/kqueue_handler.rb
73
75
  - lib/god/event_handlers/netlink_handler.rb
74
- - lib/god/hub.rb
75
76
  - lib/god/logger.rb
76
77
  - lib/god/metric.rb
77
78
  - lib/god/process.rb
@@ -81,7 +82,6 @@ files:
81
82
  - lib/god/system/process.rb
82
83
  - lib/god/task.rb
83
84
  - lib/god/timeline.rb
84
- - lib/god/timer.rb
85
85
  - lib/god/trigger.rb
86
86
  - lib/god/watch.rb
87
87
  - test/configs/child_events/child_events.god
@@ -116,10 +116,10 @@ files:
116
116
  - test/test_conditions_tries.rb
117
117
  - test/test_contact.rb
118
118
  - test/test_dependency_graph.rb
119
+ - test/test_driver.rb
119
120
  - test/test_event_handler.rb
120
121
  - test/test_god.rb
121
122
  - test/test_handlers_kqueue_handler.rb
122
- - test/test_hub.rb
123
123
  - test/test_logger.rb
124
124
  - test/test_metric.rb
125
125
  - test/test_process.rb
@@ -129,7 +129,6 @@ files:
129
129
  - test/test_system_process.rb
130
130
  - test/test_task.rb
131
131
  - test/test_timeline.rb
132
- - test/test_timer.rb
133
132
  - test/test_trigger.rb
134
133
  - test/test_watch.rb
135
134
  test_files:
@@ -141,10 +140,10 @@ test_files:
141
140
  - test/test_conditions_tries.rb
142
141
  - test/test_contact.rb
143
142
  - test/test_dependency_graph.rb
143
+ - test/test_driver.rb
144
144
  - test/test_event_handler.rb
145
145
  - test/test_god.rb
146
146
  - test/test_handlers_kqueue_handler.rb
147
- - test/test_hub.rb
148
147
  - test/test_logger.rb
149
148
  - test/test_metric.rb
150
149
  - test/test_process.rb
@@ -154,7 +153,6 @@ test_files:
154
153
  - test/test_system_process.rb
155
154
  - test/test_task.rb
156
155
  - test/test_timeline.rb
157
- - test/test_timer.rb
158
156
  - test/test_trigger.rb
159
157
  - test/test_watch.rb
160
158
  rdoc_options:
@@ -1,222 +0,0 @@
1
- module God
2
-
3
- class Hub
4
- class << self
5
- # directory to hold conditions and their corresponding metric
6
- # {condition => metric}
7
- attr_accessor :directory
8
- end
9
-
10
- self.directory = {}
11
-
12
- def self.attach(condition, metric)
13
- self.directory[condition] = metric
14
- condition.reset
15
-
16
- case condition
17
- when PollCondition
18
- Timer.get.schedule(condition, 0)
19
- when EventCondition, TriggerCondition
20
- condition.register
21
- end
22
- end
23
-
24
- def self.detach(condition)
25
- self.directory.delete(condition)
26
-
27
- case condition
28
- when PollCondition
29
- Timer.get.unschedule(condition)
30
- when EventCondition, TriggerCondition
31
- condition.deregister
32
- end
33
- end
34
-
35
- def self.trigger(condition)
36
- case condition
37
- when PollCondition
38
- self.handle_poll(condition)
39
- when EventCondition, TriggerCondition
40
- self.handle_event(condition)
41
- end
42
- end
43
-
44
- def self.handle_poll(condition)
45
- Thread.new do
46
- begin
47
- metric = self.directory[condition]
48
-
49
- # it's possible that the timer will trigger an event before it can be cleared
50
- # by an exiting metric, in which case it should be ignored
51
- unless metric.nil?
52
- watch = metric.watch
53
-
54
- watch.mutex.synchronize do
55
- # run the test
56
- result = condition.test
57
-
58
- # log
59
- messages = self.log(watch, metric, condition, result)
60
-
61
- # notify
62
- if condition.notify && self.trigger?(metric, result)
63
- self.notify(condition, messages.last)
64
- end
65
-
66
- # after-condition
67
- condition.after
68
-
69
- # get the destination
70
- dest =
71
- if result && condition.transition
72
- # condition override
73
- condition.transition
74
- else
75
- # regular
76
- metric.destination && metric.destination[result]
77
- end
78
-
79
- # transition or reschedule
80
- if dest
81
- # transition
82
- begin
83
- watch.move(dest)
84
- rescue EventRegistrationFailedError
85
- msg = watch.name + ' Event registration failed, moving back to previous state'
86
- applog(watch, :info, msg)
87
-
88
- dest = watch.state
89
- retry
90
- end
91
- else
92
- # reschedule
93
- Timer.get.schedule(condition)
94
- end
95
- end
96
- end
97
- rescue Exception => e
98
- message = format("Unhandled exception (%s): %s\n%s",
99
- e.class, e.message, e.backtrace.join("\n"))
100
- applog(nil, :fatal, message)
101
- end
102
- end
103
- end
104
-
105
- def self.handle_event(condition)
106
- Thread.new do
107
- begin
108
- metric = self.directory[condition]
109
-
110
- unless metric.nil?
111
- watch = metric.watch
112
-
113
- watch.mutex.synchronize do
114
- # log
115
- messages = self.log(watch, metric, condition, true)
116
-
117
- # notify
118
- if condition.notify && self.trigger?(metric, true)
119
- self.notify(condition, messages.last)
120
- end
121
-
122
- # get the destination
123
- dest =
124
- if condition.transition
125
- # condition override
126
- condition.transition
127
- else
128
- # regular
129
- metric.destination && metric.destination[true]
130
- end
131
-
132
- if dest
133
- watch.move(dest)
134
- end
135
- end
136
- end
137
- rescue Exception => e
138
- message = format("Unhandled exception (%s): %s\n%s",
139
- e.class, e.message, e.backtrace.join("\n"))
140
- applog(nil, :fatal, message)
141
- end
142
- end
143
- end
144
-
145
- # helpers
146
-
147
- def self.trigger?(metric, result)
148
- (metric.destination && metric.destination.keys.size == 2) || result == true
149
- end
150
-
151
- def self.log(watch, metric, condition, result)
152
- status =
153
- if self.trigger?(metric, result)
154
- "[trigger]"
155
- else
156
- "[ok]"
157
- end
158
-
159
- messages = []
160
-
161
- # log info if available
162
- if condition.info
163
- Array(condition.info).each do |condition_info|
164
- messages << "#{watch.name} #{status} #{condition_info} (#{condition.base_name})"
165
- applog(watch, :info, messages.last)
166
- end
167
- else
168
- messages << "#{watch.name} #{status} (#{condition.base_name})"
169
- applog(watch, :info, messages.last)
170
- end
171
-
172
- # log
173
- debug_message = watch.name + ' ' + condition.base_name + " [#{result}] " + self.dest_desc(metric, condition)
174
- applog(watch, :debug, debug_message)
175
-
176
- messages
177
- end
178
-
179
- def self.dest_desc(metric, condition)
180
- if condition.transition
181
- {true => condition.transition}.inspect
182
- else
183
- if metric.destination
184
- metric.destination.inspect
185
- else
186
- 'none'
187
- end
188
- end
189
- end
190
-
191
- def self.notify(condition, message)
192
- spec = Contact.normalize(condition.notify)
193
- unmatched = []
194
-
195
- # resolve contacts
196
- resolved_contacts =
197
- spec[:contacts].inject([]) do |acc, contact_name_or_group|
198
- cons = Array(God.contacts[contact_name_or_group] || God.contact_groups[contact_name_or_group])
199
- unmatched << contact_name_or_group if cons.empty?
200
- acc += cons
201
- acc
202
- end
203
-
204
- # warn about unmatched contacts
205
- unless unmatched.empty?
206
- msg = "#{condition.watch.name} no matching contacts for '#{unmatched.join(", ")}'"
207
- applog(condition.watch, :warn, msg)
208
- end
209
-
210
- # notify each contact
211
- resolved_contacts.each do |c|
212
- host = `hostname`.chomp rescue 'none'
213
- c.notify(message, Time.now, spec[:priority], spec[:category], host)
214
-
215
- msg = "#{condition.watch.name} #{c.info ? c.info : "notification sent for contact: #{c.name}"} (#{c.base_name})"
216
-
217
- applog(condition.watch, :info, msg % [])
218
- end
219
- end
220
- end
221
-
222
- end