god 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/History.txt +26 -2
  2. data/Manifest.txt +19 -5
  3. data/Rakefile +7 -2
  4. data/bin/god +131 -11
  5. data/examples/events.god +52 -0
  6. data/examples/gravatar.god +29 -35
  7. data/ext/god/extconf.rb +49 -2
  8. data/ext/god/kqueue_handler.c +2 -2
  9. data/ext/god/netlink_handler.c +17 -4
  10. data/lib/god.rb +127 -25
  11. data/lib/god/behavior.rb +8 -3
  12. data/lib/god/behaviors/clean_pid_file.rb +2 -8
  13. data/lib/god/condition.rb +6 -5
  14. data/lib/god/conditions/cpu_usage.rb +4 -4
  15. data/lib/god/conditions/lambda.rb +19 -0
  16. data/lib/god/conditions/memory_usage.rb +4 -4
  17. data/lib/god/conditions/process_exits.rb +5 -7
  18. data/lib/god/conditions/process_running.rb +4 -4
  19. data/lib/god/errors.rb +3 -0
  20. data/lib/god/event_handler.rb +28 -3
  21. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  22. data/lib/god/event_handlers/kqueue_handler.rb +2 -0
  23. data/lib/god/event_handlers/netlink_handler.rb +2 -0
  24. data/lib/god/hub.rb +40 -23
  25. data/lib/god/metric.rb +4 -4
  26. data/lib/god/process.rb +105 -0
  27. data/lib/god/registry.rb +28 -0
  28. data/lib/god/server.rb +3 -4
  29. data/lib/god/sugar.rb +47 -0
  30. data/lib/god/system/process.rb +1 -2
  31. data/lib/god/timer.rb +13 -6
  32. data/lib/god/watch.rb +61 -29
  33. data/test/configs/child_events/child_events.god +25 -0
  34. data/test/configs/child_events/simple_server.rb +3 -0
  35. data/test/configs/child_polls/child_polls.god +15 -0
  36. data/test/configs/child_polls/simple_server.rb +3 -0
  37. data/test/configs/daemon_events/daemon_events.god +30 -0
  38. data/test/configs/daemon_events/simple_server.rb +6 -0
  39. data/test/configs/real.rb +47 -49
  40. data/test/configs/test.rb +52 -62
  41. data/test/helper.rb +44 -14
  42. data/test/test_behavior.rb +10 -2
  43. data/test/test_condition.rb +19 -3
  44. data/test/test_conditions_process_running.rb +42 -0
  45. data/test/test_event_handler.rb +73 -0
  46. data/test/test_god.rb +206 -9
  47. data/test/test_handlers_kqueue_handler.rb +12 -0
  48. data/test/test_hub.rb +157 -0
  49. data/test/test_metric.rb +30 -2
  50. data/test/test_process.rb +84 -0
  51. data/test/test_registry.rb +14 -0
  52. data/test/test_server.rb +3 -2
  53. data/test/test_sugar.rb +42 -0
  54. data/test/test_system_process.rb +1 -1
  55. data/test/test_timer.rb +8 -1
  56. data/test/test_watch.rb +137 -2
  57. metadata +28 -17
  58. data/examples/local.god +0 -60
  59. data/ext/god/Makefile +0 -149
  60. data/lib/god/base.rb +0 -13
  61. data/lib/god/meddle.rb +0 -38
  62. data/test/test_meddle.rb +0 -46
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestHandlersKqueueHandler < Test::Unit::TestCase
4
+ def test_register_process
5
+ KQueueHandler.expects(:monitor_process).with(1234, 2147483648)
6
+ KQueueHandler.register_process(1234, [:proc_exit])
7
+ end
8
+
9
+ def test_events_mask
10
+ assert_equal 2147483648, KQueueHandler.events_mask([:proc_exit])
11
+ end
12
+ end
@@ -0,0 +1,157 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestHub < Test::Unit::TestCase
4
+ def setup
5
+ Server.stubs(:new).returns(true)
6
+ God.reset
7
+
8
+ God.watch do |w|
9
+ w.name = 'foo'
10
+ w.interval = 10
11
+ end
12
+
13
+ @watch = God.watches['foo']
14
+ end
15
+
16
+ # attach
17
+
18
+ def test_attach_should_store_condition_metric_association
19
+ c = Conditions::FakePollCondition.new
20
+ m = Metric.new(@watch, :foo)
21
+ Hub.attach(c, m)
22
+
23
+ assert_equal m, Hub.directory[c]
24
+ end
25
+
26
+ def test_attach_should_schedule_for_poll_condition
27
+ c = Conditions::FakePollCondition.new
28
+ m = Metric.new(@watch, :foo)
29
+
30
+ Timer.any_instance.expects(:schedule).with(c, 0)
31
+
32
+ Hub.attach(c, m)
33
+ end
34
+
35
+ def test_attach_should_regsiter_for_event_condition
36
+ c = Conditions::FakeEventCondition.new
37
+ m = Metric.new(@watch, :foo)
38
+
39
+ c.expects(:register)
40
+
41
+ Hub.attach(c, m)
42
+ end
43
+
44
+ # detach
45
+
46
+ def test_detach_should_remove_condition_metric_association
47
+ c = Conditions::FakePollCondition.new
48
+ m = Metric.new(@watch, :foo)
49
+
50
+ Hub.attach(c, m)
51
+ Hub.detach(c)
52
+
53
+ assert_nil Hub.directory[c]
54
+ end
55
+
56
+ def test_detach_should_unschedule_poll_conditions
57
+ c = Conditions::FakePollCondition.new
58
+ m = Metric.new(@watch, :foo)
59
+ Hub.attach(c, m)
60
+
61
+ Timer.any_instance.expects(:unschedule).with(c)
62
+ c.expects(:deregister).never
63
+
64
+ Hub.detach(c)
65
+ end
66
+
67
+ def test_detach_should_deregister_event_conditions
68
+ c = Conditions::FakeEventCondition.new
69
+ m = Metric.new(@watch, :foo)
70
+ Hub.attach(c, m)
71
+
72
+ c.expects(:deregister).once
73
+
74
+ Hub.detach(c)
75
+ end
76
+
77
+ # trigger
78
+
79
+ def test_trigger_should_handle_poll_for_poll_condition
80
+ c = Conditions::FakePollCondition.new
81
+ Hub.expects(:handle_poll).with(c)
82
+
83
+ Hub.trigger(c)
84
+ end
85
+
86
+ def test_trigger_should_handle_event_for_event_condition
87
+ c = Conditions::FakeEventCondition.new
88
+ Hub.expects(:handle_event).with(c)
89
+
90
+ Hub.trigger(c)
91
+ end
92
+
93
+ # handle_poll
94
+
95
+ def test_handle_poll_no_change_should_reschedule
96
+ c = Conditions::FakePollCondition.new
97
+ c.interval = 10
98
+
99
+ m = Metric.new(@watch, {true => :up})
100
+ Hub.attach(c, m)
101
+
102
+ c.expects(:test).returns(false)
103
+ Timer.any_instance.expects(:schedule)
104
+
105
+ no_stdout do
106
+ t = Hub.handle_poll(c)
107
+ t.join
108
+ end
109
+ end
110
+
111
+ def test_handle_poll_change_should_move
112
+ c = Conditions::FakePollCondition.new
113
+ c.interval = 10
114
+
115
+ m = Metric.new(@watch, {true => :up})
116
+ Hub.attach(c, m)
117
+
118
+ c.expects(:test).returns(true)
119
+ @watch.expects(:move).with(:up)
120
+
121
+ no_stdout do
122
+ t = Hub.handle_poll(c)
123
+ t.join
124
+ end
125
+ end
126
+
127
+ def test_handle_poll_should_abort_on_exception
128
+ c = Conditions::FakePollCondition.new
129
+ c.interval = 10
130
+
131
+ m = Metric.new(@watch, {true => :up})
132
+ Hub.attach(c, m)
133
+
134
+ c.expects(:test).raises(StandardError.new)
135
+
136
+ assert_abort do
137
+ t = Hub.handle_poll(c)
138
+ t.join
139
+ end
140
+ end
141
+
142
+ # handle_event
143
+
144
+ def test_handle_event_should_move
145
+ c = Conditions::FakeEventCondition.new
146
+
147
+ m = Metric.new(@watch, {true => :up})
148
+ Hub.attach(c, m)
149
+
150
+ @watch.expects(:move).with(:up)
151
+
152
+ no_stdout do
153
+ t = Hub.handle_event(c)
154
+ t.join
155
+ end
156
+ end
157
+ end
@@ -36,7 +36,7 @@ class TestMetric < Test::Unit::TestCase
36
36
  def test_poll_condition_should_abort_if_no_interval_and_no_watch_interval
37
37
  metric = Metric.new(stub(:name => 'foo', :interval => nil), nil)
38
38
 
39
- assert_raise AbortCalledError do
39
+ assert_abort do
40
40
  metric.condition(:fake_poll_condition)
41
41
  end
42
42
  end
@@ -53,8 +53,36 @@ class TestMetric < Test::Unit::TestCase
53
53
  def test_condition_should_abort_if_not_subclass_of_poll_or_event
54
54
  metric = Metric.new(stub(:name => 'foo', :interval => 10), nil)
55
55
 
56
- assert_raise AbortCalledError do
56
+ assert_abort do
57
57
  metric.condition(:fake_condition) { |c| }
58
58
  end
59
59
  end
60
+
61
+ def test_condition_should_abort_on_invalid_condition
62
+ assert_abort do
63
+ @metric.condition(:fake_poll_condition) { |c| c.stubs(:valid?).returns(false) }
64
+ end
65
+ end
66
+
67
+ def test_condition_should_abort_on_no_such_condition
68
+ assert_abort do
69
+ @metric.condition(:invalid) { }
70
+ end
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
60
88
  end
@@ -0,0 +1,84 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ module God
4
+ class Process
5
+ def fork
6
+ raise "You forgot to stub fork"
7
+ end
8
+
9
+ def exec(*args)
10
+ raise "You forgot to stub exec"
11
+ end
12
+ end
13
+ end
14
+
15
+ class TestProcess < Test::Unit::TestCase
16
+ def setup
17
+ @p = God::Process.new(:name => 'foo', :pid_file => 'blah.pid')
18
+ @p.stubs(:test).returns true # so we don't try to mkdir_p
19
+ Process.stubs(:detach) # because we stub fork
20
+ end
21
+
22
+ # These actually excercise call_action in the back at this point - Kev
23
+ def test_call_action_with_string_should_fork_exec
24
+ @p.start = "do something"
25
+ IO.expects(:pipe).returns([StringIO.new('1234'), StringIO.new])
26
+ @p.expects(:fork)
27
+ Process.expects(:waitpid)
28
+ @p.call_action(:start)
29
+ end
30
+
31
+ def test_call_action_with_lambda_should_call
32
+ cmd = lambda { puts "Hi" }
33
+ cmd.expects(:call)
34
+ @p.start = cmd
35
+ @p.call_action(:start)
36
+ end
37
+
38
+ def test_default_pid_file
39
+ assert_equal File.join(God.pid_file_directory, 'foo.pid'), @p.default_pid_file
40
+ end
41
+
42
+ def test_call_action_without_pid_should_write_pid
43
+ # Only for start, restart
44
+ [:start, :restart].each do |action|
45
+ @p = God::Process.new(:name => 'foo')
46
+ @p.stubs(:test).returns true
47
+ IO.expects(:pipe).returns([StringIO.new('1234'), StringIO.new])
48
+ @p.expects(:fork)
49
+ Process.expects(:waitpid)
50
+ File.expects(:open).with(@p.default_pid_file, 'w')
51
+ @p.send("#{action}=", "run")
52
+ @p.call_action(action)
53
+ end
54
+ end
55
+
56
+ def test_call_action_should_not_write_pid_for_stop
57
+ @p.pid_file = nil
58
+ IO.expects(:pipe).returns([StringIO.new('1234'), StringIO.new])
59
+ @p.expects(:fork)
60
+ Process.expects(:waitpid)
61
+ File.expects(:open).times(0)
62
+ @p.stop = "stopping"
63
+ @p.call_action(:stop)
64
+ end
65
+
66
+ def test_call_action_should_mkdir_p_if_pid_file_dir_existence_test_fails
67
+ @p.pid_file = nil
68
+ IO.expects(:pipe).returns([StringIO.new('1234'), StringIO.new])
69
+ @p.expects(:fork)
70
+ Process.expects(:waitpid)
71
+ @p.expects(:test).returns(false, true)
72
+ FileUtils.expects(:mkdir_p).with(God.pid_file_directory)
73
+ File.expects(:open)
74
+ @p.start = "starting"
75
+ @p.call_action(:start)
76
+ end
77
+
78
+ def test_start_stop_restart_bang
79
+ [:start, :stop, :restart].each do |x|
80
+ @p.expects(:call_action).with(x)
81
+ @p.send("#{x}!")
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestRegistry < Test::Unit::TestCase
4
+ def setup
5
+ God.registry.reset
6
+ end
7
+
8
+ def test_add
9
+ foo = God::Process.new(:name => 'foo')
10
+ God.registry.add(foo)
11
+ assert_equal 1, God.registry.size
12
+ assert_equal foo, God.registry['foo']
13
+ end
14
+ end
@@ -14,11 +14,12 @@ class TestServer < Test::Unit::TestCase
14
14
 
15
15
  def test_should_use_supplied_port_and_host
16
16
  DRb.expects(:start_service).with { |uri, object| uri == "druby://host:port" && object.is_a?(Server) }
17
- server = Server.new(nil, 'host', 'port')
17
+ server = Server.new('host', 'port')
18
18
  end
19
19
 
20
20
  def test_should_forward_foreign_method_calls_to_meddle
21
- server = Server.new(mock(:something_random => true))
21
+ server = Server.new
22
+ God.expects(:send).with(:something_random)
22
23
  server.something_random
23
24
  end
24
25
  end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSugar < Test::Unit::TestCase
4
+ def test_seconds
5
+ assert_equal 1, 1.seconds
6
+ assert_equal 1, 1.second
7
+ end
8
+
9
+ def test_minutes
10
+ assert_equal 60, 1.minutes
11
+ assert_equal 60, 1.minute
12
+ end
13
+
14
+ def test_hours
15
+ assert_equal 3600, 1.hours
16
+ assert_equal 3600, 1.hour
17
+ end
18
+
19
+ def test_days
20
+ assert_equal 86400, 1.days
21
+ assert_equal 86400, 1.day
22
+ end
23
+
24
+ def test_kilobytes
25
+ assert_equal 1, 1.kilobytes
26
+ assert_equal 1, 1.kilobyte
27
+ end
28
+
29
+ def test_megabytes
30
+ assert_equal 1024, 1.megabytes
31
+ assert_equal 1024, 1.megabyte
32
+ end
33
+
34
+ def test_gigabytes
35
+ assert_equal 1024 ** 2, 1.gigabytes
36
+ assert_equal 1024 ** 2, 1.gigabyte
37
+ end
38
+
39
+ def test_percent
40
+ assert_equal 1, 1.percent
41
+ end
42
+ end
@@ -11,7 +11,7 @@ class TestSystemProcess < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def test_exists_should_return_false_for_non_existant_process
14
- assert_equal false, System::Process.new(0).exists?
14
+ assert_equal false, System::Process.new(9999999).exists?
15
15
  end
16
16
 
17
17
  def test_memory
@@ -13,7 +13,7 @@ class TestTimer < Test::Unit::TestCase
13
13
  def test_schedule_should_queue_event
14
14
  Time.stubs(:now).returns(0)
15
15
 
16
- w = Watch.new(nil)
16
+ w = Watch.new
17
17
  @t.schedule(stub(:interval => 20))
18
18
 
19
19
  assert_equal 1, @t.events.size
@@ -50,4 +50,11 @@ class TestTimer < Test::Unit::TestCase
50
50
  @t.unschedule(a)
51
51
  assert_equal 1, @t.events.size
52
52
  end
53
+
54
+ # join
55
+
56
+ def test_join_should_join
57
+ Thread.any_instance.expects(:join)
58
+ @t.join
59
+ end
53
60
  end
@@ -2,7 +2,10 @@ require File.dirname(__FILE__) + '/helper'
2
2
 
3
3
  class TestWatch < Test::Unit::TestCase
4
4
  def setup
5
- @watch = Watch.new(nil)
5
+ @watch = Watch.new
6
+ @watch.name = 'foo'
7
+ @watch.start = lambda { }
8
+ @watch.stop = lambda { }
6
9
  end
7
10
 
8
11
  # new
@@ -47,10 +50,16 @@ class TestWatch < Test::Unit::TestCase
47
50
  assert_equal beh, @watch.behaviors.first
48
51
  end
49
52
 
53
+ def test_invalid_behavior_should_abort
54
+ assert_abort do
55
+ @watch.behavior(:invalid)
56
+ end
57
+ end
58
+
50
59
  # transition
51
60
 
52
61
  def test_transition_should_abort_on_invalid_start_state
53
- assert_raise AbortCalledError do
62
+ assert_abort do
54
63
  @watch.transition(:foo, :bar)
55
64
  end
56
65
  end
@@ -82,6 +91,132 @@ class TestWatch < Test::Unit::TestCase
82
91
  assert_equal 1, @watch.metrics[:up].size
83
92
  end
84
93
 
94
+ # monitor
95
+
96
+ def test_monitor_should_move_to_init_if_available
97
+ @watch.instance_eval do
98
+ transition(:init, :up) { }
99
+ end
100
+ @watch.expects(:move).with(:init)
101
+ @watch.monitor
102
+ end
103
+
104
+ def test_monitor_should_move_to_up_if_no_init_available
105
+ @watch.expects(:move).with(:up)
106
+ @watch.monitor
107
+ end
108
+
109
+ # unmonitor
110
+
111
+ def test_unmonitor_should_move_to_nil
112
+ @watch.expects(:move).with(nil)
113
+ @watch.unmonitor
114
+ end
115
+
116
+ # move
117
+
118
+ def test_move_should_not_clean_up_if_from_state_is_nil
119
+ metric = nil
120
+
121
+ @watch.instance_eval do
122
+ transition(:init, :up) do |on|
123
+ metric = on
124
+ on.condition(:process_running) do |c|
125
+ c.running = true
126
+ c.interval = 10
127
+ end
128
+ end
129
+ end
130
+
131
+ metric.expects(:disable).never
132
+
133
+ no_stdout { @watch.move(:init) }
134
+ end
135
+
136
+ def test_move_should_clean_up_from_state_if_not_nil
137
+ metric = nil
138
+
139
+ @watch.instance_eval do
140
+ transition(:init, :up) do |on|
141
+ metric = on
142
+ on.condition(:process_running) do |c|
143
+ c.running = true
144
+ c.interval = 10
145
+ end
146
+ end
147
+ end
148
+
149
+ no_stdout { @watch.move(:init) }
150
+
151
+ metric.expects(:disable)
152
+
153
+ no_stdout { @watch.move(:up) }
154
+ end
155
+
156
+ def test_move_should_call_action
157
+ @watch.expects(:action).with(:start)
158
+
159
+ no_stdout { @watch.move(:start) }
160
+ end
161
+
162
+ def test_move_should_move_to_up_state_if_no_start_or_restart_metric
163
+ [:start, :restart].each do |state|
164
+ @watch.expects(:action)
165
+ no_stdout { @watch.move(state) }
166
+ assert_equal :up, @watch.state
167
+ end
168
+ end
169
+
170
+ def test_move_should_enable_destination_metric
171
+ metric = nil
172
+
173
+ @watch.instance_eval do
174
+ transition(:init, :up) do |on|
175
+ metric = on
176
+ on.condition(:process_running) do |c|
177
+ c.running = true
178
+ c.interval = 10
179
+ end
180
+ end
181
+ end
182
+
183
+ metric.expects(:enable)
184
+
185
+ no_stdout { @watch.move(:init) }
186
+ end
187
+
188
+ # action
189
+
190
+ def test_action_should_pass_start_and_stop_actions_to_call_action
191
+ c = Conditions::FakePollCondition.new
192
+ [:start, :stop].each do |cmd|
193
+ @watch.expects(:call_action).with(c, cmd)
194
+ no_stdout { @watch.action(cmd, c) }
195
+ end
196
+ end
197
+
198
+ def test_action_should_do_stop_then_start_if_no_restart_command
199
+ c = Conditions::FakePollCondition.new
200
+ @watch.expects(:call_action).with(c, :stop)
201
+ @watch.expects(:call_action).with(c, :start)
202
+ no_stdout { @watch.action(:restart, c) }
203
+ end
204
+
205
+ def test_action_should_restart_to_call_action_if_present
206
+ @watch.restart = lambda { }
207
+ c = Conditions::FakePollCondition.new
208
+ @watch.expects(:call_action).with(c, :restart)
209
+ no_stdout { @watch.action(:restart, c) }
210
+ end
211
+
212
+ # call_action
213
+
214
+ def test_call_action
215
+ c = Conditions::FakePollCondition.new
216
+ God::Process.any_instance.expects(:call_action).with(:start)
217
+ @watch.call_action(c, :start)
218
+ end
219
+
85
220
  # canonical_hash_form
86
221
 
87
222
  def test_canonical_hash_form_should_convert_symbol_to_hash