god 0.3.0 → 0.4.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 (46) hide show
  1. data/History.txt +26 -0
  2. data/Manifest.txt +15 -1
  3. data/Rakefile +2 -7
  4. data/bin/god +104 -16
  5. data/lib/god.rb +169 -37
  6. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  7. data/lib/god/condition.rb +1 -0
  8. data/lib/god/conditions/degrading_lambda.rb +47 -0
  9. data/lib/god/conditions/process_exits.rb +6 -2
  10. data/lib/god/conditions/tries.rb +33 -0
  11. data/lib/god/dependency_graph.rb +41 -0
  12. data/lib/god/errors.rb +6 -0
  13. data/lib/god/hub.rb +43 -20
  14. data/lib/god/logger.rb +44 -0
  15. data/lib/god/process.rb +91 -19
  16. data/lib/god/registry.rb +4 -0
  17. data/lib/god/server.rb +12 -2
  18. data/lib/god/timeline.rb +36 -0
  19. data/lib/god/watch.rb +27 -8
  20. data/test/configs/child_events/child_events.god +7 -2
  21. data/test/configs/child_polls/child_polls.god +3 -1
  22. data/test/configs/child_polls/simple_server.rb +1 -1
  23. data/test/configs/daemon_events/daemon_events.god +7 -3
  24. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  25. data/test/configs/daemon_polls/simple_server.rb +6 -0
  26. data/test/configs/degrading_lambda/degrading_lambda.god +33 -0
  27. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  28. data/test/configs/real.rb +1 -1
  29. data/test/configs/running_load/running_load.god +16 -0
  30. data/test/configs/stress/simple_server.rb +3 -0
  31. data/test/configs/stress/stress.god +15 -0
  32. data/test/configs/test.rb +14 -2
  33. data/test/helper.rb +12 -2
  34. data/test/test_conditions_tries.rb +46 -0
  35. data/test/test_dependency_graph.rb +62 -0
  36. data/test/test_god.rb +289 -33
  37. data/test/test_handlers_kqueue_handler.rb +11 -7
  38. data/test/test_hub.rb +18 -0
  39. data/test/test_logger.rb +55 -0
  40. data/test/test_process.rb +135 -17
  41. data/test/test_registry.rb +2 -1
  42. data/test/test_server.rb +35 -4
  43. data/test/test_timeline.rb +14 -2
  44. data/test/test_watch.rb +7 -0
  45. metadata +21 -4
  46. data/lib/god/conditions/timeline.rb +0 -17
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestDependencyGraph < Test::Unit::TestCase
4
+ def setup
5
+ @dg = DependencyGraph.new
6
+ end
7
+
8
+ # new
9
+
10
+ def test_new_should_accept_zero_arguments
11
+ assert @dg.instance_of?(DependencyGraph)
12
+ end
13
+
14
+ # add
15
+
16
+ def test_add_should_create_and_store_two_new_nodes
17
+ @dg.add('foo', 'bar')
18
+ assert_equal 2, @dg.nodes.size
19
+ assert @dg.nodes['foo'].instance_of?(DependencyGraph::Node)
20
+ assert @dg.nodes['bar'].instance_of?(DependencyGraph::Node)
21
+ end
22
+
23
+ def test_add_should_record_dependency
24
+ @dg.add('foo', 'bar')
25
+ assert_equal 1, @dg.nodes['foo'].dependencies.size
26
+ assert_equal @dg.nodes['bar'], @dg.nodes['foo'].dependencies.first
27
+ end
28
+
29
+ def test_add_should_ignore_dups
30
+ @dg.add('foo', 'bar')
31
+ @dg.add('foo', 'bar')
32
+ assert_equal 2, @dg.nodes.size
33
+ assert_equal 1, @dg.nodes['foo'].dependencies.size
34
+ end
35
+ end
36
+
37
+
38
+ class TestDependencyGraphNode < Test::Unit::TestCase
39
+ def setup
40
+ @foo = DependencyGraph::Node.new('foo')
41
+ @bar = DependencyGraph::Node.new('bar')
42
+ end
43
+
44
+ # new
45
+
46
+ def test_new_should_accept_zero_arguments
47
+ assert @foo.instance_of?(DependencyGraph::Node)
48
+ end
49
+
50
+ # add
51
+
52
+ def test_add_should_store_node_as_dependency
53
+ @foo.add(@bar)
54
+ assert_equal 1, @foo.dependencies.size
55
+ end
56
+
57
+ # has_node?
58
+
59
+ def test_has_node
60
+ assert @foo.has_node?(@foo)
61
+ end
62
+ end
data/test/test_god.rb CHANGED
@@ -3,6 +3,8 @@ require File.dirname(__FILE__) + '/helper'
3
3
  class TestGod < Test::Unit::TestCase
4
4
  def setup
5
5
  Server.stubs(:new).returns(true)
6
+ God.stubs(:setup).returns(true)
7
+ God.stubs(:validater).returns(true)
6
8
  God.reset
7
9
  end
8
10
 
@@ -17,18 +19,23 @@ class TestGod < Test::Unit::TestCase
17
19
  assert_equal Hash.new, God.watches
18
20
  end
19
21
 
20
- def test_init_should_kick_off_a_server_instance
21
- Server.expects(:new).returns(true)
22
- God.init
22
+ def test_init_should_abort_if_called_after_watch
23
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
24
+
25
+ assert_abort do
26
+ God.init { }
27
+ end
23
28
  end
24
29
 
25
30
  # pid_file_directory
26
31
 
27
32
  def test_pid_file_directory_should_return_default_if_not_set_explicitly
33
+ God.init
28
34
  assert_equal '/var/run/god', God.pid_file_directory
29
35
  end
30
36
 
31
37
  def test_pid_file_directory_equals_should_set
38
+ God.init
32
39
  God.pid_file_directory = '/foo'
33
40
  assert_equal '/foo', God.pid_file_directory
34
41
  end
@@ -37,7 +44,11 @@ class TestGod < Test::Unit::TestCase
37
44
 
38
45
  def test_watch_should_get_stored
39
46
  watch = nil
40
- God.watch { |w| watch = w }
47
+ God.watch do |w|
48
+ w.name = 'foo'
49
+ w.start = 'bar'
50
+ watch = w
51
+ end
41
52
 
42
53
  assert_equal 1, God.watches.size
43
54
  assert_equal watch, God.watches.values.first
@@ -45,71 +56,148 @@ class TestGod < Test::Unit::TestCase
45
56
  assert_equal 0, God.groups.size
46
57
  end
47
58
 
59
+ def test_watch_should_get_stored_in_pending_watches
60
+ watch = nil
61
+ God.watch do |w|
62
+ w.name = 'foo'
63
+ w.start = 'bar'
64
+ watch = w
65
+ end
66
+
67
+ assert_equal 1, God.pending_watches.size
68
+ assert_equal watch, God.pending_watches.first
69
+ end
70
+
48
71
  def test_watch_should_register_processes
49
72
  assert_nil God.registry['foo']
50
- God.watch { |w| w.name = 'foo' }
73
+ God.watch do |w|
74
+ w.name = 'foo'
75
+ w.start = 'bar'
76
+ end
51
77
  assert_kind_of God::Process, God.registry['foo']
52
78
  end
53
79
 
54
80
  def test_watch_should_get_stored_by_group
81
+ a = nil
82
+
55
83
  God.watch do |w|
84
+ a = w
56
85
  w.name = 'foo'
86
+ w.start = 'bar'
57
87
  w.group = 'test'
58
88
  end
59
89
 
60
- assert_equal({'test' => ['foo']}, God.groups)
90
+ assert_equal({'test' => [a]}, God.groups)
61
91
  end
62
92
 
63
93
  def test_watches_should_get_stored_by_group
94
+ a = nil
95
+ b = nil
96
+
64
97
  God.watch do |w|
98
+ a = w
65
99
  w.name = 'foo'
100
+ w.start = 'bar'
66
101
  w.group = 'test'
67
102
  end
68
103
 
69
104
  God.watch do |w|
105
+ b = w
70
106
  w.name = 'bar'
107
+ w.start = 'baz'
71
108
  w.group = 'test'
72
109
  end
73
110
 
74
- assert_equal({'test' => ['foo', 'bar']}, God.groups)
111
+ assert_equal({'test' => [a, b]}, God.groups)
75
112
  end
76
113
 
77
114
  def test_watch_should_allow_multiple_watches
78
- God.watch { |w| w.name = 'foo' }
115
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
79
116
 
80
117
  assert_nothing_raised do
81
- God.watch { |w| w.name = 'bar' }
118
+ God.watch { |w| w.name = 'bar'; w.start = 'bar' }
82
119
  end
83
120
  end
84
121
 
85
122
  def test_watch_should_disallow_duplicate_watch_names
86
- God.watch { |w| w.name = 'foo' }
123
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
87
124
 
88
125
  assert_abort do
89
- God.watch { |w| w.name = 'foo' }
126
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
90
127
  end
91
128
  end
92
129
 
93
130
  def test_watch_should_disallow_identical_watch_and_group_names
94
- God.watch { |w| w.name = 'foo'; w.group = 'bar' }
131
+ God.watch { |w| w.name = 'foo'; w.group = 'bar'; w.start = 'bar' }
95
132
 
96
133
  assert_abort do
97
- God.watch { |w| w.name = 'bar' }
134
+ God.watch { |w| w.name = 'bar'; w.start = 'bar' }
98
135
  end
99
136
  end
100
137
 
101
138
  def test_watch_should_disallow_identical_watch_and_group_names_other_way
102
- God.watch { |w| w.name = 'bar' }
139
+ God.watch { |w| w.name = 'bar'; w.start = 'bar' }
103
140
 
104
141
  assert_abort do
105
- God.watch { |w| w.name = 'foo'; w.group = 'bar' }
142
+ God.watch { |w| w.name = 'foo'; w.group = 'bar'; w.start = 'bar' }
143
+ end
144
+ end
145
+
146
+ def test_watch_should_unwatch_new_watch_if_running_and_duplicate_watch
147
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
148
+ God.running = true
149
+
150
+ assert_nothing_raised do
151
+ no_stdout do
152
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
153
+ end
154
+ end
155
+ end
156
+
157
+ # unwatch
158
+
159
+ def test_unwatch_should_unmonitor_watch
160
+ God.watch { |w| w.name = 'bar'; w.start = 'bar' }
161
+ w = God.watches['bar']
162
+ w.expects(:unmonitor)
163
+ God.unwatch(w)
164
+ end
165
+
166
+ def test_unwatch_should_unregister_watch
167
+ God.watch { |w| w.name = 'bar'; w.start = 'bar' }
168
+ w = God.watches['bar']
169
+ w.expects(:unregister!)
170
+ no_stdout do
171
+ God.unwatch(w)
172
+ end
173
+ end
174
+
175
+ def test_unwatch_should_remove_same_name_watches
176
+ God.watch { |w| w.name = 'bar'; w.start = 'bar' }
177
+ w = God.watches['bar']
178
+ no_stdout do
179
+ God.unwatch(w)
180
+ end
181
+ assert_equal 0, God.watches.size
182
+ end
183
+
184
+ def test_unwatch_should_remove_from_group
185
+ God.watch do |w|
186
+ w.name = 'bar'
187
+ w.start = 'baz'
188
+ w.group = 'test'
189
+ end
190
+ w = God.watches['bar']
191
+ no_stdout do
192
+ God.unwatch(w)
106
193
  end
194
+ assert !God.groups[w.group].include?(w)
107
195
  end
108
196
 
109
197
  # control
110
198
 
111
199
  def test_control_should_monitor_on_start
112
- God.watch { |w| w.name = 'foo' }
200
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
113
201
 
114
202
  w = God.watches['foo']
115
203
  w.expects(:monitor)
@@ -117,7 +205,7 @@ class TestGod < Test::Unit::TestCase
117
205
  end
118
206
 
119
207
  def test_control_should_move_to_restart_on_restart
120
- God.watch { |w| w.name = 'foo' }
208
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
121
209
 
122
210
  w = God.watches['foo']
123
211
  w.expects(:move).with(:restart)
@@ -125,7 +213,7 @@ class TestGod < Test::Unit::TestCase
125
213
  end
126
214
 
127
215
  def test_control_should_unmonitor_and_stop_on_stop
128
- God.watch { |w| w.name = 'foo' }
216
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
129
217
 
130
218
  w = God.watches['foo']
131
219
  w.expects(:unmonitor).returns(w)
@@ -134,7 +222,7 @@ class TestGod < Test::Unit::TestCase
134
222
  end
135
223
 
136
224
  def test_control_should_unmonitor_on_unmonitor
137
- God.watch { |w| w.name = 'foo' }
225
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
138
226
 
139
227
  w = God.watches['foo']
140
228
  w.expects(:unmonitor).returns(w)
@@ -142,23 +230,152 @@ class TestGod < Test::Unit::TestCase
142
230
  end
143
231
 
144
232
  def test_control_should_raise_on_invalid_command
145
- God.watch { |w| w.name = 'foo' }
233
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
146
234
 
147
235
  assert_raise InvalidCommandError do
148
236
  God.control('foo', 'invalid')
149
237
  end
150
238
  end
151
239
 
152
- # start
240
+ def test_control_should_operate_on_each_watch_in_group
241
+ God.watch do |w|
242
+ w.name = 'foo1'
243
+ w.start = 'go'
244
+ w.group = 'bar'
245
+ end
246
+
247
+ God.watch do |w|
248
+ w.name = 'foo2'
249
+ w.start = 'go'
250
+ w.group = 'bar'
251
+ end
252
+
253
+ God.watches['foo1'].expects(:monitor)
254
+ God.watches['foo2'].expects(:monitor)
255
+
256
+ God.control('bar', 'start')
257
+ end
153
258
 
154
- def test_start_should_check_for_at_least_one_watch
155
- assert_abort do
156
- God.start
259
+ # stop_all
260
+
261
+ # terminate
262
+
263
+ def test_terminate_should_exit
264
+ God.expects(:exit!).with(0)
265
+ God.terminate
266
+ end
267
+
268
+ # status
269
+
270
+ def test_status_should_show_state
271
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
272
+
273
+ w = God.watches['foo']
274
+ w.state = :up
275
+ assert_equal({'foo' => {:state => :up}}, God.status)
276
+ end
277
+
278
+ def test_status_should_show_unmonitored_for_nil_state
279
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
280
+
281
+ w = God.watches['foo']
282
+ assert_equal({'foo' => {:state => :unmonitored}}, God.status)
283
+ end
284
+
285
+ # running_load
286
+
287
+ def test_running_load_should_eval_code
288
+ code = <<-EOF
289
+ God.watch do |w|
290
+ w.name = 'foo'
291
+ w.start = 'go'
292
+ end
293
+ EOF
294
+
295
+ no_stdout do
296
+ God.running_load(code)
297
+ end
298
+
299
+ assert_equal 1, God.watches.size
300
+ end
301
+
302
+ def test_running_load_should_monitor_new_watches
303
+ code = <<-EOF
304
+ God.watch do |w|
305
+ w.name = 'foo'
306
+ w.start = 'go'
307
+ end
308
+ EOF
309
+
310
+ Watch.any_instance.expects(:monitor)
311
+ no_stdout do
312
+ God.running_load(code)
313
+ end
314
+ end
315
+
316
+ def test_running_load_should_not_monitor_new_watches_with_autostart_false
317
+ code = <<-EOF
318
+ God.watch do |w|
319
+ w.name = 'foo'
320
+ w.start = 'go'
321
+ w.autostart = false
322
+ end
323
+ EOF
324
+
325
+ Watch.any_instance.expects(:monitor).never
326
+ no_stdout do
327
+ God.running_load(code)
328
+ end
329
+ end
330
+
331
+ def test_running_load_should_return_array_of_affected_watches
332
+ code = <<-EOF
333
+ God.watch do |w|
334
+ w.name = 'foo'
335
+ w.start = 'go'
336
+ end
337
+ EOF
338
+
339
+ w = nil
340
+ no_stdout do
341
+ w = God.running_load(code)
157
342
  end
343
+ assert_equal 1, w.size
344
+ assert_equal 'foo', w.first.name
345
+ end
346
+
347
+ def test_running_load_should_clear_pending_watches
348
+ code = <<-EOF
349
+ God.watch do |w|
350
+ w.name = 'foo'
351
+ w.start = 'go'
352
+ end
353
+ EOF
354
+
355
+ no_stdout do
356
+ God.running_load(code)
357
+ end
358
+ assert_equal 0, God.pending_watches.size
359
+ end
360
+
361
+ # load
362
+
363
+ def test_load_should_collect_and_load_globbed_path
364
+ Dir.expects(:[]).with('/path/to/*.thing').returns(['a', 'b'])
365
+ Kernel.expects(:load).with('a').once
366
+ Kernel.expects(:load).with('b').once
367
+ God.load('/path/to/*.thing')
158
368
  end
159
369
 
370
+ # start
371
+
372
+ def test_start_should_kick_off_a_server_instance
373
+ Server.expects(:new).returns(true)
374
+ God.start
375
+ end
376
+
160
377
  def test_start_should_start_event_handler
161
- God.watch { |w| w.name = 'foo' }
378
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
162
379
  Timer.any_instance.expects(:join)
163
380
  EventHandler.expects(:start).once
164
381
  no_stdout do
@@ -169,6 +386,7 @@ class TestGod < Test::Unit::TestCase
169
386
  def test_start_should_begin_monitoring_autostart_watches
170
387
  God.watch do |w|
171
388
  w.name = 'foo'
389
+ w.start = 'go'
172
390
  end
173
391
 
174
392
  Timer.any_instance.expects(:join)
@@ -179,6 +397,7 @@ class TestGod < Test::Unit::TestCase
179
397
  def test_start_should_not_begin_monitoring_non_autostart_watches
180
398
  God.watch do |w|
181
399
  w.name = 'foo'
400
+ w.start = 'go'
182
401
  w.autostart = false
183
402
  end
184
403
 
@@ -188,7 +407,7 @@ class TestGod < Test::Unit::TestCase
188
407
  end
189
408
 
190
409
  def test_start_should_get_and_join_timer
191
- God.watch { |w| w.name = 'foo' }
410
+ God.watch { |w| w.name = 'foo'; w.start = 'bar' }
192
411
  Timer.any_instance.expects(:join)
193
412
  no_stdout do
194
413
  God.start
@@ -201,13 +420,50 @@ class TestGod < Test::Unit::TestCase
201
420
  God.expects(:start).once
202
421
  God.at_exit_orig
203
422
  end
423
+ end
424
+
425
+
426
+ class TestGodOther < Test::Unit::TestCase
427
+ def setup
428
+ Server.stubs(:new).returns(true)
429
+ God.internal_init
430
+ God.reset
431
+ end
204
432
 
205
- # load
433
+ def teardown
434
+ Timer.get.timer.kill
435
+ end
206
436
 
207
- def test_load_should_collect_and_load_globbed_path
208
- Dir.expects(:[]).with('/path/to/*.thing').returns(['a', 'b'])
209
- Kernel.expects(:load).with('a').once
210
- Kernel.expects(:load).with('b').once
211
- God.load('/path/to/*.thing')
437
+ # setup
438
+
439
+ def test_setup_should_create_pid_file_directory_if_it_doesnt_exist
440
+ God.expects(:test).returns(false)
441
+ FileUtils.expects(:mkdir_p).with(God.pid_file_directory)
442
+ God.setup
212
443
  end
213
- end
444
+
445
+ def test_setup_should_raise_if_no_permissions_to_create_pid_file_directory
446
+ God.expects(:test).returns(false)
447
+ FileUtils.expects(:mkdir_p).raises(Errno::EACCES)
448
+
449
+ assert_abort do
450
+ God.setup
451
+ end
452
+ end
453
+
454
+ # validate
455
+
456
+ def test_validate_should_abort_if_pid_file_directory_is_unwriteable
457
+ God.expects(:test).returns(false)
458
+ assert_abort do
459
+ God.validater
460
+ end
461
+ end
462
+
463
+ def test_validate_should_not_abort_if_pid_file_directory_is_writeable
464
+ God.expects(:test).returns(true)
465
+ assert_nothing_raised do
466
+ God.validater
467
+ end
468
+ end
469
+ end