god 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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