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
@@ -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