god 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +23 -0
- data/Manifest.txt +9 -0
- data/README.txt +9 -2
- data/Rakefile +8 -1
- data/bin/god +11 -214
- data/examples/single.god +66 -0
- data/ext/god/netlink_handler.c +16 -3
- data/lib/god.rb +153 -17
- data/lib/god/cli/command.rb +189 -0
- data/lib/god/cli/run.rb +120 -0
- data/lib/god/cli/version.rb +23 -0
- data/lib/god/conditions/complex.rb +86 -0
- data/lib/god/conditions/cpu_usage.rb +27 -0
- data/lib/god/conditions/disk_usage.rb +27 -0
- data/lib/god/conditions/flapping.rb +42 -11
- data/lib/god/conditions/http_response_code.rb +63 -3
- data/lib/god/conditions/memory_usage.rb +30 -1
- data/lib/god/conditions/process_exits.rb +24 -2
- data/lib/god/conditions/process_running.rb +32 -0
- data/lib/god/configurable.rb +5 -3
- data/lib/god/event_handler.rb +2 -2
- data/lib/god/hub.rb +12 -19
- data/lib/god/logger.rb +11 -2
- data/lib/god/process.rb +29 -20
- data/lib/god/socket.rb +41 -5
- data/lib/god/task.rb +6 -9
- data/lib/god/timer.rb +20 -13
- data/lib/god/watch.rb +3 -6
- data/test/configs/child_events/child_events.god +1 -1
- data/test/configs/complex/complex.god +59 -0
- data/test/configs/complex/simple_server.rb +3 -0
- data/test/test_conditions_disk_usage.rb +56 -0
- data/test/test_conditions_http_response_code.rb +15 -21
- data/test/test_god.rb +36 -0
- data/test/test_hub.rb +6 -4
- data/test/test_logger.rb +8 -0
- data/test/test_timer.rb +9 -0
- metadata +12 -2
data/lib/god/task.rb
CHANGED
@@ -40,19 +40,19 @@ module God
|
|
40
40
|
# a name must be specified
|
41
41
|
if self.name.nil?
|
42
42
|
valid = false
|
43
|
-
|
43
|
+
applog(self, :error, "No name was specified")
|
44
44
|
end
|
45
45
|
|
46
46
|
# valid_states must be specified
|
47
47
|
if self.valid_states.nil?
|
48
48
|
valid = false
|
49
|
-
|
49
|
+
applog(self, :error, "No valid_states array was specified")
|
50
50
|
end
|
51
51
|
|
52
52
|
# valid_states must be specified
|
53
53
|
if self.initial_state.nil?
|
54
54
|
valid = false
|
55
|
-
|
55
|
+
applog(self, :error, "No initial_state was specified")
|
56
56
|
end
|
57
57
|
|
58
58
|
valid
|
@@ -132,8 +132,7 @@ module God
|
|
132
132
|
from_state = self.state
|
133
133
|
|
134
134
|
msg = "#{self.name} move '#{from_state}' to '#{to_state}'"
|
135
|
-
|
136
|
-
LOG.log(self, :info, msg)
|
135
|
+
applog(self, :info, msg)
|
137
136
|
|
138
137
|
# cleanup from current state
|
139
138
|
self.metrics[from_state].each { |m| m.disable }
|
@@ -199,14 +198,12 @@ module God
|
|
199
198
|
case command
|
200
199
|
when String
|
201
200
|
msg = "#{self.name} #{a}: #{command}"
|
202
|
-
|
203
|
-
LOG.log(self, :info, msg)
|
201
|
+
applog(self, :info, msg)
|
204
202
|
|
205
203
|
system(command)
|
206
204
|
when Proc
|
207
205
|
msg = "#{self.name} #{a}: lambda"
|
208
|
-
|
209
|
-
LOG.log(self, :info, msg)
|
206
|
+
applog(self, :info, msg)
|
210
207
|
|
211
208
|
command.call
|
212
209
|
else
|
data/lib/god/timer.rb
CHANGED
@@ -31,23 +31,30 @@ module God
|
|
31
31
|
|
32
32
|
@timer = Thread.new do
|
33
33
|
loop do
|
34
|
-
|
35
|
-
|
34
|
+
begin
|
35
|
+
# get the current time
|
36
|
+
t = Time.now.to_i
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
# iterate over each event and trigger any that are due
|
39
|
+
@events.each do |event|
|
40
|
+
if t >= event.at
|
41
|
+
self.trigger(event)
|
42
|
+
@mutex.synchronize do
|
43
|
+
@events.delete(event)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
break
|
43
47
|
end
|
44
|
-
else
|
45
|
-
break
|
46
48
|
end
|
47
|
-
end
|
48
49
|
|
49
|
-
|
50
|
-
|
50
|
+
# sleep until next check
|
51
|
+
sleep INTERVAL
|
52
|
+
rescue Exception => e
|
53
|
+
message = format("Unhandled exception (%s): %s\n%s",
|
54
|
+
e.class, e.message, e.backtrace.join("\n"))
|
55
|
+
applog(nil, :fatal, message)
|
56
|
+
sleep INTERVAL
|
57
|
+
end
|
51
58
|
end
|
52
59
|
end
|
53
60
|
end
|
data/lib/god/watch.rb
CHANGED
@@ -123,16 +123,14 @@ module God
|
|
123
123
|
info = b.send("before_#{action}")
|
124
124
|
if info
|
125
125
|
msg = "#{self.name} before_#{action}: #{info} (#{b.base_name})"
|
126
|
-
|
127
|
-
LOG.log(self, :info, msg)
|
126
|
+
applog(self, :info, msg)
|
128
127
|
end
|
129
128
|
end
|
130
129
|
|
131
130
|
# log
|
132
131
|
if self.send(action)
|
133
132
|
msg = "#{self.name} #{action}: #{self.send(action).to_s}"
|
134
|
-
|
135
|
-
LOG.log(self, :info, msg)
|
133
|
+
applog(self, :info, msg)
|
136
134
|
end
|
137
135
|
|
138
136
|
@process.call_action(action)
|
@@ -144,8 +142,7 @@ module God
|
|
144
142
|
info = b.send("after_#{action}")
|
145
143
|
if info
|
146
144
|
msg = "#{self.name} after_#{action}: #{info} (#{b.base_name})"
|
147
|
-
|
148
|
-
LOG.log(self, :info, msg)
|
145
|
+
applog(self, :info, msg)
|
149
146
|
end
|
150
147
|
end
|
151
148
|
end
|
@@ -7,7 +7,7 @@ God.watch do |w|
|
|
7
7
|
w.name = "child-events"
|
8
8
|
w.interval = 5.seconds
|
9
9
|
w.start = File.join(GOD_ROOT, *%w[test configs child_events simple_server.rb])
|
10
|
-
w.log = File.join(GOD_ROOT, *%w[test configs child_events god.log])
|
10
|
+
# w.log = File.join(GOD_ROOT, *%w[test configs child_events god.log])
|
11
11
|
|
12
12
|
# determine the state on startup
|
13
13
|
w.transition(:init, { true => :up, false => :start }) do |on|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
God.watch do |w|
|
2
|
+
w.name = "complex"
|
3
|
+
w.interval = 5.seconds
|
4
|
+
w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
|
5
|
+
# w.log = File.join(GOD_ROOT, *%w[test configs child_events god.log])
|
6
|
+
|
7
|
+
# determine the state on startup
|
8
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
9
|
+
on.condition(:process_running) do |c|
|
10
|
+
c.running = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# determine when process has finished starting
|
15
|
+
w.transition([:start, :restart], :up) do |on|
|
16
|
+
on.condition(:process_running) do |c|
|
17
|
+
c.running = true
|
18
|
+
end
|
19
|
+
|
20
|
+
# failsafe
|
21
|
+
on.condition(:tries) do |c|
|
22
|
+
c.times = 2
|
23
|
+
c.transition = :start
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# start if process is not running
|
28
|
+
w.transition(:up, :start) do |on|
|
29
|
+
on.condition(:process_exits)
|
30
|
+
end
|
31
|
+
|
32
|
+
# restart if process is misbehaving
|
33
|
+
w.transition(:up, :restart) do |on|
|
34
|
+
on.condition(:complex) do |cc|
|
35
|
+
cc.and(:cpu_usage) do |c|
|
36
|
+
c.above = 0.percent
|
37
|
+
c.times = 1
|
38
|
+
end
|
39
|
+
|
40
|
+
cc.and(:memory_usage) do |c|
|
41
|
+
c.above = 0.megabytes
|
42
|
+
c.times = 3
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# lifecycle
|
48
|
+
w.lifecycle do |on|
|
49
|
+
on.condition(:flapping) do |c|
|
50
|
+
c.to_state = [:start, :restart]
|
51
|
+
c.times = 5
|
52
|
+
c.within = 20.seconds
|
53
|
+
c.transition = :unmonitored
|
54
|
+
c.retry_in = 10.seconds
|
55
|
+
c.retry_times = 2
|
56
|
+
c.retry_within = 5.minutes
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class TestConditionsDiskUsage < Test::Unit::TestCase
|
4
|
+
# valid?
|
5
|
+
|
6
|
+
def test_valid_should_return_false_if_no_above_given
|
7
|
+
c = Conditions::DiskUsage.new
|
8
|
+
c.mount_point = '/'
|
9
|
+
c.watch = stub(:name => 'foo')
|
10
|
+
|
11
|
+
no_stdout do
|
12
|
+
assert_equal false, c.valid?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_valid_should_return_false_if_no_mount_point_given
|
17
|
+
c = Conditions::DiskUsage.new
|
18
|
+
c.above = 90
|
19
|
+
c.watch = stub(:name => 'foo')
|
20
|
+
|
21
|
+
no_stdout do
|
22
|
+
assert_equal false, c.valid?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_valid_should_return_true_if_required_options_all_set
|
27
|
+
c = Conditions::DiskUsage.new
|
28
|
+
c.above = 90
|
29
|
+
c.mount_point = '/'
|
30
|
+
c.watch = stub(:name => 'foo')
|
31
|
+
|
32
|
+
assert_equal true, c.valid?
|
33
|
+
end
|
34
|
+
|
35
|
+
# test
|
36
|
+
|
37
|
+
def test_test_should_return_true_if_above_limit
|
38
|
+
c = Conditions::DiskUsage.new
|
39
|
+
c.above = 90
|
40
|
+
c.mount_point = '/'
|
41
|
+
|
42
|
+
c.expects(:`).returns('91')
|
43
|
+
|
44
|
+
assert_equal true, c.test
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_test_should_return_false_if_below_limit
|
48
|
+
c = Conditions::DiskUsage.new
|
49
|
+
c.above = 90
|
50
|
+
c.mount_point = '/'
|
51
|
+
|
52
|
+
c.expects(:`).returns('90')
|
53
|
+
|
54
|
+
assert_equal false, c.test
|
55
|
+
end
|
56
|
+
end
|
@@ -36,27 +36,6 @@ class TestHttpResponseCode < Test::Unit::TestCase
|
|
36
36
|
no_stdout { assert !c.valid? }
|
37
37
|
end
|
38
38
|
|
39
|
-
def test_valid_should_return_false_if_no_port_set
|
40
|
-
c = valid_condition do |cc|
|
41
|
-
cc.port = nil
|
42
|
-
end
|
43
|
-
no_stdout { assert !c.valid? }
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_valid_should_return_false_if_no_path_set
|
47
|
-
c = valid_condition do |cc|
|
48
|
-
cc.path = nil
|
49
|
-
end
|
50
|
-
no_stdout { assert !c.valid? }
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_valid_should_return_false_if_no_timeout_set
|
54
|
-
c = valid_condition do |cc|
|
55
|
-
cc.timeout = nil
|
56
|
-
end
|
57
|
-
no_stdout { assert !c.valid? }
|
58
|
-
end
|
59
|
-
|
60
39
|
# test
|
61
40
|
|
62
41
|
def test_test_should_return_false_if_code_is_is_set_to_200_but_response_is_500
|
@@ -104,6 +83,21 @@ class TestHttpResponseCode < Test::Unit::TestCase
|
|
104
83
|
assert_equal true, c.test
|
105
84
|
end
|
106
85
|
|
86
|
+
def test_test_should_return_false_if_code_is_is_set_to_200_but_cant_connect
|
87
|
+
c = valid_condition
|
88
|
+
Net::HTTP.expects(:start).raises(Errno::ECONNREFUSED, '')
|
89
|
+
assert_equal false, c.test
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_test_should_return_true_if_code_is_not_is_set_to_200_and_cant_connect
|
93
|
+
c = valid_condition do |cc|
|
94
|
+
cc.code_is = nil
|
95
|
+
cc.code_is_not = [200]
|
96
|
+
end
|
97
|
+
Net::HTTP.expects(:start).raises(Errno::ECONNREFUSED, '')
|
98
|
+
assert_equal true, c.test
|
99
|
+
end
|
100
|
+
|
107
101
|
def test_test_should_return_true_if_code_is_is_set_to_200_and_response_is_200_twice_for_times_two_of_two
|
108
102
|
c = valid_condition do |cc|
|
109
103
|
cc.times = [2, 2]
|
data/test/test_god.rb
CHANGED
@@ -12,6 +12,13 @@ class TestGod < Test::Unit::TestCase
|
|
12
12
|
Timer.get.timer.kill
|
13
13
|
end
|
14
14
|
|
15
|
+
# applog
|
16
|
+
|
17
|
+
def test_applog
|
18
|
+
LOG.expects(:log).with(nil, :debug, 'foo')
|
19
|
+
applog(nil, :debug, 'foo')
|
20
|
+
end
|
21
|
+
|
15
22
|
# internal_init
|
16
23
|
|
17
24
|
def test_init_should_initialize_watches_to_empty_array
|
@@ -297,6 +304,15 @@ class TestGod < Test::Unit::TestCase
|
|
297
304
|
God.control('foo', 'unmonitor')
|
298
305
|
end
|
299
306
|
|
307
|
+
def test_control_should_unwatch_on_remove
|
308
|
+
God.watch { |w| w.name = 'foo'; w.start = 'bar' }
|
309
|
+
|
310
|
+
w = God.watches['foo']
|
311
|
+
w.state = :up
|
312
|
+
God.expects(:unwatch)
|
313
|
+
God.control('foo', 'remove')
|
314
|
+
end
|
315
|
+
|
300
316
|
def test_control_should_raise_on_invalid_command
|
301
317
|
God.watch { |w| w.name = 'foo'; w.start = 'bar' }
|
302
318
|
|
@@ -329,6 +345,15 @@ class TestGod < Test::Unit::TestCase
|
|
329
345
|
# terminate
|
330
346
|
|
331
347
|
def test_terminate_should_exit
|
348
|
+
God.pid = nil
|
349
|
+
FileUtils.expects(:rm_f).never
|
350
|
+
God.expects(:exit!)
|
351
|
+
God.terminate
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_terminate_should_delete_pid
|
355
|
+
God.pid = '/foo/bar'
|
356
|
+
FileUtils.expects(:rm_f).with("/foo/bar")
|
332
357
|
God.expects(:exit!)
|
333
358
|
God.terminate
|
334
359
|
end
|
@@ -504,6 +529,17 @@ class TestGod < Test::Unit::TestCase
|
|
504
529
|
God.expects(:start).once
|
505
530
|
God.at_exit
|
506
531
|
end
|
532
|
+
|
533
|
+
# pattern_match
|
534
|
+
|
535
|
+
def test_pattern_match
|
536
|
+
list = %w{ mongrel-3000 mongrel-3001 fuzed fuzed2 apache mysql}
|
537
|
+
|
538
|
+
assert_equal %w{ mongrel-3000 }, God.pattern_match('m3000', list)
|
539
|
+
assert_equal %w{ mongrel-3001 }, God.pattern_match('m31', list)
|
540
|
+
assert_equal %w{ fuzed fuzed2 }, God.pattern_match('fu', list)
|
541
|
+
assert_equal %w{ mysql }, God.pattern_match('sql', list)
|
542
|
+
end
|
507
543
|
end
|
508
544
|
|
509
545
|
|
data/test/test_hub.rb
CHANGED
@@ -125,7 +125,7 @@ class TestHub < Test::Unit::TestCase
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
-
def
|
128
|
+
def test_handle_poll_should_not_abort_on_exception
|
129
129
|
c = Conditions::FakePollCondition.new
|
130
130
|
c.interval = 10
|
131
131
|
|
@@ -134,9 +134,11 @@ class TestHub < Test::Unit::TestCase
|
|
134
134
|
|
135
135
|
c.expects(:test).raises(StandardError.new)
|
136
136
|
|
137
|
-
|
138
|
-
|
139
|
-
|
137
|
+
assert_nothing_raised do
|
138
|
+
no_stdout do
|
139
|
+
t = Hub.handle_poll(c)
|
140
|
+
t.join
|
141
|
+
end
|
140
142
|
end
|
141
143
|
end
|
142
144
|
|
data/test/test_logger.rb
CHANGED
@@ -19,6 +19,14 @@ class TestLogger < Test::Unit::TestCase
|
|
19
19
|
assert_match(/qux/, @log.logs['foo'][0][1])
|
20
20
|
end
|
21
21
|
|
22
|
+
def test_log_should_send_to_syslog
|
23
|
+
Syslog.expects(:crit).with('foo')
|
24
|
+
|
25
|
+
no_stdout do
|
26
|
+
@log.log(stub(:name => 'foo'), :fatal, "foo")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
22
30
|
# watch_log_since
|
23
31
|
|
24
32
|
def test_watch_log_since
|
data/test/test_timer.rb
CHANGED
@@ -51,6 +51,15 @@ class TestTimer < Test::Unit::TestCase
|
|
51
51
|
assert_equal 1, @t.events.size
|
52
52
|
end
|
53
53
|
|
54
|
+
def test_time_should_recover_from_exceptions
|
55
|
+
@t.expects(:trigger).raises(Exception.new)
|
56
|
+
no_stdout do
|
57
|
+
@t.schedule(stub(:interval => 0))
|
58
|
+
sleep(0.3)
|
59
|
+
@t.schedule(stub(:interval => 0))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
54
63
|
# join
|
55
64
|
|
56
65
|
def test_join_should_join
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: god
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.6.0
|
7
|
+
date: 2007-12-04 00:00:00 -08:00
|
8
8
|
summary: Like monit, only awesome
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- bin/god
|
38
38
|
- examples/events.god
|
39
39
|
- examples/gravatar.god
|
40
|
+
- examples/single.god
|
40
41
|
- ext/god/extconf.rb
|
41
42
|
- ext/god/kqueue_handler.c
|
42
43
|
- ext/god/netlink_handler.c
|
@@ -45,10 +46,15 @@ files:
|
|
45
46
|
- lib/god/behavior.rb
|
46
47
|
- lib/god/behaviors/clean_pid_file.rb
|
47
48
|
- lib/god/behaviors/notify_when_flapping.rb
|
49
|
+
- lib/god/cli/command.rb
|
50
|
+
- lib/god/cli/run.rb
|
51
|
+
- lib/god/cli/version.rb
|
48
52
|
- lib/god/condition.rb
|
49
53
|
- lib/god/conditions/always.rb
|
54
|
+
- lib/god/conditions/complex.rb
|
50
55
|
- lib/god/conditions/cpu_usage.rb
|
51
56
|
- lib/god/conditions/degrading_lambda.rb
|
57
|
+
- lib/god/conditions/disk_usage.rb
|
52
58
|
- lib/god/conditions/flapping.rb
|
53
59
|
- lib/god/conditions/http_response_code.rb
|
54
60
|
- lib/god/conditions/lambda.rb
|
@@ -82,6 +88,8 @@ files:
|
|
82
88
|
- test/configs/child_events/simple_server.rb
|
83
89
|
- test/configs/child_polls/child_polls.god
|
84
90
|
- test/configs/child_polls/simple_server.rb
|
91
|
+
- test/configs/complex/complex.god
|
92
|
+
- test/configs/complex/simple_server.rb
|
85
93
|
- test/configs/contact/contact.god
|
86
94
|
- test/configs/contact/simple_server.rb
|
87
95
|
- test/configs/daemon_events/daemon_events.god
|
@@ -102,6 +110,7 @@ files:
|
|
102
110
|
- test/suite.rb
|
103
111
|
- test/test_behavior.rb
|
104
112
|
- test/test_condition.rb
|
113
|
+
- test/test_conditions_disk_usage.rb
|
105
114
|
- test/test_conditions_http_response_code.rb
|
106
115
|
- test/test_conditions_process_running.rb
|
107
116
|
- test/test_conditions_tries.rb
|
@@ -126,6 +135,7 @@ files:
|
|
126
135
|
test_files:
|
127
136
|
- test/test_behavior.rb
|
128
137
|
- test/test_condition.rb
|
138
|
+
- test/test_conditions_disk_usage.rb
|
129
139
|
- test/test_conditions_http_response_code.rb
|
130
140
|
- test/test_conditions_process_running.rb
|
131
141
|
- test/test_conditions_tries.rb
|