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.
- data/History.txt +26 -2
- data/Manifest.txt +19 -5
- data/Rakefile +7 -2
- data/bin/god +131 -11
- data/examples/events.god +52 -0
- data/examples/gravatar.god +29 -35
- data/ext/god/extconf.rb +49 -2
- data/ext/god/kqueue_handler.c +2 -2
- data/ext/god/netlink_handler.c +17 -4
- data/lib/god.rb +127 -25
- data/lib/god/behavior.rb +8 -3
- data/lib/god/behaviors/clean_pid_file.rb +2 -8
- data/lib/god/condition.rb +6 -5
- data/lib/god/conditions/cpu_usage.rb +4 -4
- data/lib/god/conditions/lambda.rb +19 -0
- data/lib/god/conditions/memory_usage.rb +4 -4
- data/lib/god/conditions/process_exits.rb +5 -7
- data/lib/god/conditions/process_running.rb +4 -4
- data/lib/god/errors.rb +3 -0
- data/lib/god/event_handler.rb +28 -3
- data/lib/god/event_handlers/dummy_handler.rb +13 -0
- data/lib/god/event_handlers/kqueue_handler.rb +2 -0
- data/lib/god/event_handlers/netlink_handler.rb +2 -0
- data/lib/god/hub.rb +40 -23
- data/lib/god/metric.rb +4 -4
- data/lib/god/process.rb +105 -0
- data/lib/god/registry.rb +28 -0
- data/lib/god/server.rb +3 -4
- data/lib/god/sugar.rb +47 -0
- data/lib/god/system/process.rb +1 -2
- data/lib/god/timer.rb +13 -6
- data/lib/god/watch.rb +61 -29
- data/test/configs/child_events/child_events.god +25 -0
- data/test/configs/child_events/simple_server.rb +3 -0
- data/test/configs/child_polls/child_polls.god +15 -0
- data/test/configs/child_polls/simple_server.rb +3 -0
- data/test/configs/daemon_events/daemon_events.god +30 -0
- data/test/configs/daemon_events/simple_server.rb +6 -0
- data/test/configs/real.rb +47 -49
- data/test/configs/test.rb +52 -62
- data/test/helper.rb +44 -14
- data/test/test_behavior.rb +10 -2
- data/test/test_condition.rb +19 -3
- data/test/test_conditions_process_running.rb +42 -0
- data/test/test_event_handler.rb +73 -0
- data/test/test_god.rb +206 -9
- data/test/test_handlers_kqueue_handler.rb +12 -0
- data/test/test_hub.rb +157 -0
- data/test/test_metric.rb +30 -2
- data/test/test_process.rb +84 -0
- data/test/test_registry.rb +14 -0
- data/test/test_server.rb +3 -2
- data/test/test_sugar.rb +42 -0
- data/test/test_system_process.rb +1 -1
- data/test/test_timer.rb +8 -1
- data/test/test_watch.rb +137 -2
- metadata +28 -17
- data/examples/local.god +0 -60
- data/ext/god/Makefile +0 -149
- data/lib/god/base.rb +0 -13
- data/lib/god/meddle.rb +0 -38
- data/test/test_meddle.rb +0 -46
data/test/helper.rb
CHANGED
@@ -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
|
#
|
data/test/test_behavior.rb
CHANGED
@@ -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
|
data/test/test_condition.rb
CHANGED
@@ -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
|
data/test/test_god.rb
CHANGED
@@ -1,16 +1,213 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/helper'
|
2
2
|
|
3
3
|
class TestGod < Test::Unit::TestCase
|
4
|
-
def
|
5
|
-
|
6
|
-
|
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.
|
45
|
+
assert_equal 0, God.groups.size
|
9
46
|
end
|
10
|
-
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
God.
|
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
|