god 0.2.0 → 0.3.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 (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
@@ -1,7 +1,7 @@
1
- $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext kqueue_handler])
2
1
  require File.join(File.dirname(__FILE__), *%w[.. lib god])
3
2
 
4
3
  require 'test/unit'
4
+ require 'set'
5
5
 
6
6
  begin
7
7
  require 'mocha'
@@ -18,19 +18,6 @@ end
18
18
  include God
19
19
 
20
20
  module God
21
- class AbortCalledError < StandardError
22
- end
23
-
24
- class Base
25
- def abort(msg)
26
- raise AbortCalledError.new("abort called")
27
- end
28
-
29
- def self.abort(msg)
30
- raise AbortCalledError.new("abort called")
31
- end
32
- end
33
-
34
21
  module Conditions
35
22
  class FakeCondition < Condition
36
23
  def test
@@ -55,6 +42,25 @@ module God
55
42
  class FakeBehavior < Behavior
56
43
  end
57
44
  end
45
+
46
+ class << self
47
+ alias :at_exit_orig :at_exit
48
+ end
49
+
50
+ def self.at_exit
51
+ # disable at_exit
52
+ end
53
+
54
+ def self.reset
55
+ self.watches = nil
56
+ self.groups = nil
57
+ self.server = nil
58
+ self.inited = nil
59
+ self.host = nil
60
+ self.port = nil
61
+ self.pid_file_directory = nil
62
+ self.registry.reset
63
+ end
58
64
  end
59
65
 
60
66
  def silence_warnings
@@ -64,6 +70,30 @@ ensure
64
70
  $VERBOSE = old_verbose
65
71
  end
66
72
 
73
+ def no_stdout
74
+ old_stdout = $stdout.dup
75
+ $stdout.reopen(File.open((PLATFORM =~ /mswin/ ? "NUL" : "/dev/null"), 'w'))
76
+ yield
77
+ $stdout.reopen(old_stdout)
78
+ end
79
+
80
+ def no_stderr
81
+ old_stderr = $stderr.dup
82
+ $stderr.reopen(File.open((PLATFORM =~ /mswin/ ? "NUL" : "/dev/null"), 'w'))
83
+ yield
84
+ $stderr.reopen(old_stderr)
85
+ end
86
+
87
+ module Test::Unit::Assertions
88
+ def assert_abort
89
+ assert_raise SystemExit do
90
+ no_stderr do
91
+ yield
92
+ end
93
+ end
94
+ end
95
+ end
96
+
67
97
  # This allows you to be a good OOP citizen and honor encapsulation, but
68
98
  # still make calls to private methods (for testing) by doing
69
99
  #
@@ -2,12 +2,20 @@ require File.dirname(__FILE__) + '/helper'
2
2
 
3
3
  class TestBehavior < Test::Unit::TestCase
4
4
  def test_generate_should_return_an_object_corresponding_to_the_given_type
5
- assert_equal Behaviors::FakeBehavior, Behavior.generate(:fake_behavior).class
5
+ assert_equal Behaviors::FakeBehavior, Behavior.generate(:fake_behavior, nil).class
6
6
  end
7
7
 
8
8
  def test_generate_should_raise_on_invalid_type
9
9
  assert_raise NoSuchBehaviorError do
10
- Behavior.generate(:foo)
10
+ Behavior.generate(:foo, nil)
11
+ end
12
+ end
13
+
14
+ def test_complain
15
+ Syslog.expects(:err).with('foo')
16
+ # Kernel.expects(:puts).with('foo')
17
+ no_stdout do
18
+ assert !Behavior.allocate.bypass.complain('foo')
11
19
  end
12
20
  end
13
21
  end
@@ -1,13 +1,19 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
+ class BadlyImplementedCondition < PollCondition
4
+ end
5
+
3
6
  class TestCondition < Test::Unit::TestCase
7
+
8
+ # generate
9
+
4
10
  def test_generate_should_return_an_object_corresponding_to_the_given_type
5
- assert_equal Conditions::ProcessRunning, Condition.generate(:process_running).class
11
+ assert_equal Conditions::ProcessRunning, Condition.generate(:process_running, nil).class
6
12
  end
7
13
 
8
14
  def test_generate_should_raise_on_invalid_type
9
15
  assert_raise NoSuchConditionError do
10
- Condition.generate(:foo)
16
+ Condition.generate(:foo, nil)
11
17
  end
12
18
  end
13
19
 
@@ -16,11 +22,21 @@ class TestCondition < Test::Unit::TestCase
16
22
  rmsg = nil
17
23
 
18
24
  begin
19
- Condition.generate(:foo_bar)
25
+ Condition.generate(:foo_bar, nil)
20
26
  rescue => e
21
27
  rmsg = e.message
22
28
  end
23
29
 
24
30
  assert_equal emsg, rmsg
25
31
  end
32
+
33
+ # test
34
+
35
+ def test_test_should_raise_if_not_defined_in_subclass
36
+ c = BadlyImplementedCondition.new
37
+
38
+ assert_raise AbstractMethodNotOverriddenError do
39
+ c.test
40
+ end
41
+ end
26
42
  end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestConditionsProcessRunning < Test::Unit::TestCase
4
+ def test_missing_pid_file_returns_opposite
5
+ [true, false].each do |r|
6
+ c = Conditions::ProcessRunning.new
7
+ c.running = r
8
+
9
+ c.stubs(:watch).returns(stub(:pid_file => ''))
10
+
11
+ assert_equal !r, c.test
12
+ end
13
+ end
14
+
15
+ def test_not_running_returns_opposite
16
+ [true, false].each do |r|
17
+ c = Conditions::ProcessRunning.new
18
+ c.running = r
19
+
20
+ File.stubs(:exist?).returns(true)
21
+ c.stubs(:watch).returns(stub(:pid_file => ''))
22
+ File.stubs(:read).returns('5')
23
+ System::Process.any_instance.stubs(:exists?).returns(false)
24
+
25
+ assert_equal !r, c.test
26
+ end
27
+ end
28
+
29
+ def test_running_returns_same
30
+ [true, false].each do |r|
31
+ c = Conditions::ProcessRunning.new
32
+ c.running = r
33
+
34
+ File.stubs(:exist?).returns(true)
35
+ c.stubs(:watch).returns(stub(:pid_file => ''))
36
+ File.stubs(:read).returns('5')
37
+ System::Process.any_instance.stubs(:exists?).returns(true)
38
+
39
+ assert_equal r, c.test
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ module God
4
+ class EventHandler
5
+
6
+ def self.actions=(value)
7
+ @@actions = value
8
+ end
9
+
10
+ def self.actions
11
+ @@actions
12
+ end
13
+
14
+ def self.handler=(value)
15
+ @@handler = value
16
+ end
17
+ end
18
+ end
19
+
20
+ class TestEventHandler < Test::Unit::TestCase
21
+ def setup
22
+ @h = God::EventHandler
23
+ end
24
+
25
+ def test_register_one_event
26
+ pid = 4445
27
+ event = :proc_exit
28
+ block = lambda {
29
+ puts "Hi"
30
+ }
31
+
32
+ mock_handler = mock()
33
+ mock_handler.expects(:register_process).with(pid, [event])
34
+ @h.handler = mock_handler
35
+
36
+ @h.register(pid, event, &block)
37
+ assert_equal @h.actions, {pid => {event => block}}
38
+ end
39
+
40
+ def test_register_multiple_events_per_process
41
+ pid = 4445
42
+ exit_block = lambda { puts "Hi" }
43
+ @h.actions = {pid => {:proc_exit => exit_block}}
44
+
45
+ mock_handler = mock()
46
+ mock_handler.expects(:register_process).with do |a, b|
47
+ a == pid &&
48
+ b.to_set == [:proc_exit, :proc_fork].to_set
49
+ end
50
+ @h.handler = mock_handler
51
+
52
+ fork_block = lambda { puts "Forking" }
53
+ @h.register(pid, :proc_fork, &fork_block)
54
+ assert_equal @h.actions, {pid => {:proc_exit => exit_block,
55
+ :proc_fork => fork_block }}
56
+ end
57
+
58
+ # JIRA PLATFORM-75
59
+ def test_call_should_check_for_pid_and_action_before_executing
60
+ exit_block = mock()
61
+ exit_block.expects(:call).times 1
62
+ @h.actions = {4445 => {:proc_exit => exit_block}}
63
+ @h.call(4446, :proc_exit) # shouldn't call, bad pid
64
+ @h.call(4445, :proc_fork) # shouldn't call, bad event
65
+ @h.call(4445, :proc_exit) # should call
66
+ end
67
+
68
+ def teardown
69
+ # Reset handler
70
+ @h.actions = {}
71
+ @h.load
72
+ end
73
+ end
@@ -1,16 +1,213 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
3
  class TestGod < Test::Unit::TestCase
4
- def test_should_create_new_meddle
5
- Meddle.expects(:new).with(:port => 1).returns(mock(:monitor => true))
6
- Timer.expects(:get).returns(stub(:join => nil)).times(2)
4
+ def setup
5
+ Server.stubs(:new).returns(true)
6
+ God.reset
7
+ end
8
+
9
+ def teardown
10
+ Timer.get.timer.kill
11
+ end
12
+
13
+ # init
14
+
15
+ def test_init_should_initialize_watches_to_empty_array
16
+ God.init { }
17
+ assert_equal Hash.new, God.watches
18
+ end
19
+
20
+ def test_init_should_kick_off_a_server_instance
21
+ Server.expects(:new).returns(true)
22
+ God.init
23
+ end
24
+
25
+ # pid_file_directory
26
+
27
+ def test_pid_file_directory_should_return_default_if_not_set_explicitly
28
+ assert_equal '/var/run/god', God.pid_file_directory
29
+ end
30
+
31
+ def test_pid_file_directory_equals_should_set
32
+ God.pid_file_directory = '/foo'
33
+ assert_equal '/foo', God.pid_file_directory
34
+ end
35
+
36
+ # watch
37
+
38
+ def test_watch_should_get_stored
39
+ watch = nil
40
+ God.watch { |w| watch = w }
41
+
42
+ assert_equal 1, God.watches.size
43
+ assert_equal watch, God.watches.values.first
7
44
 
8
- God.meddle(:port => 1) {}
45
+ assert_equal 0, God.groups.size
9
46
  end
10
-
11
- def test_should_start_monitoring
12
- Meddle.any_instance.expects(:monitor)
13
- Timer.expects(:get).returns(stub(:join => nil)).times(2)
14
- God.meddle {}
47
+
48
+ def test_watch_should_register_processes
49
+ assert_nil God.registry['foo']
50
+ God.watch { |w| w.name = 'foo' }
51
+ assert_kind_of God::Process, God.registry['foo']
52
+ end
53
+
54
+ def test_watch_should_get_stored_by_group
55
+ God.watch do |w|
56
+ w.name = 'foo'
57
+ w.group = 'test'
58
+ end
59
+
60
+ assert_equal({'test' => ['foo']}, God.groups)
61
+ end
62
+
63
+ def test_watches_should_get_stored_by_group
64
+ God.watch do |w|
65
+ w.name = 'foo'
66
+ w.group = 'test'
67
+ end
68
+
69
+ God.watch do |w|
70
+ w.name = 'bar'
71
+ w.group = 'test'
72
+ end
73
+
74
+ assert_equal({'test' => ['foo', 'bar']}, God.groups)
75
+ end
76
+
77
+ def test_watch_should_allow_multiple_watches
78
+ God.watch { |w| w.name = 'foo' }
79
+
80
+ assert_nothing_raised do
81
+ God.watch { |w| w.name = 'bar' }
82
+ end
83
+ end
84
+
85
+ def test_watch_should_disallow_duplicate_watch_names
86
+ God.watch { |w| w.name = 'foo' }
87
+
88
+ assert_abort do
89
+ God.watch { |w| w.name = 'foo' }
90
+ end
91
+ end
92
+
93
+ def test_watch_should_disallow_identical_watch_and_group_names
94
+ God.watch { |w| w.name = 'foo'; w.group = 'bar' }
95
+
96
+ assert_abort do
97
+ God.watch { |w| w.name = 'bar' }
98
+ end
99
+ end
100
+
101
+ def test_watch_should_disallow_identical_watch_and_group_names_other_way
102
+ God.watch { |w| w.name = 'bar' }
103
+
104
+ assert_abort do
105
+ God.watch { |w| w.name = 'foo'; w.group = 'bar' }
106
+ end
107
+ end
108
+
109
+ # control
110
+
111
+ def test_control_should_monitor_on_start
112
+ God.watch { |w| w.name = 'foo' }
113
+
114
+ w = God.watches['foo']
115
+ w.expects(:monitor)
116
+ God.control('foo', 'start')
117
+ end
118
+
119
+ def test_control_should_move_to_restart_on_restart
120
+ God.watch { |w| w.name = 'foo' }
121
+
122
+ w = God.watches['foo']
123
+ w.expects(:move).with(:restart)
124
+ God.control('foo', 'restart')
125
+ end
126
+
127
+ def test_control_should_unmonitor_and_stop_on_stop
128
+ God.watch { |w| w.name = 'foo' }
129
+
130
+ w = God.watches['foo']
131
+ w.expects(:unmonitor).returns(w)
132
+ w.expects(:action).with(:stop)
133
+ God.control('foo', 'stop')
134
+ end
135
+
136
+ def test_control_should_unmonitor_on_unmonitor
137
+ God.watch { |w| w.name = 'foo' }
138
+
139
+ w = God.watches['foo']
140
+ w.expects(:unmonitor).returns(w)
141
+ God.control('foo', 'unmonitor')
142
+ end
143
+
144
+ def test_control_should_raise_on_invalid_command
145
+ God.watch { |w| w.name = 'foo' }
146
+
147
+ assert_raise InvalidCommandError do
148
+ God.control('foo', 'invalid')
149
+ end
150
+ end
151
+
152
+ # start
153
+
154
+ def test_start_should_check_for_at_least_one_watch
155
+ assert_abort do
156
+ God.start
157
+ end
158
+ end
159
+
160
+ def test_start_should_start_event_handler
161
+ God.watch { |w| w.name = 'foo' }
162
+ Timer.any_instance.expects(:join)
163
+ EventHandler.expects(:start).once
164
+ no_stdout do
165
+ God.start
166
+ end
167
+ end
168
+
169
+ def test_start_should_begin_monitoring_autostart_watches
170
+ God.watch do |w|
171
+ w.name = 'foo'
172
+ end
173
+
174
+ Timer.any_instance.expects(:join)
175
+ Watch.any_instance.expects(:monitor).once
176
+ God.start
177
+ end
178
+
179
+ def test_start_should_not_begin_monitoring_non_autostart_watches
180
+ God.watch do |w|
181
+ w.name = 'foo'
182
+ w.autostart = false
183
+ end
184
+
185
+ Timer.any_instance.expects(:join)
186
+ Watch.any_instance.expects(:monitor).never
187
+ God.start
188
+ end
189
+
190
+ def test_start_should_get_and_join_timer
191
+ God.watch { |w| w.name = 'foo' }
192
+ Timer.any_instance.expects(:join)
193
+ no_stdout do
194
+ God.start
195
+ end
196
+ end
197
+
198
+ # at_exit
199
+
200
+ def test_at_exit_should_call_start
201
+ God.expects(:start).once
202
+ God.at_exit_orig
203
+ end
204
+
205
+ # load
206
+
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')
15
212
  end
16
213
  end