god 0.6.0 → 0.7.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 +67 -1
- data/Manifest.txt +3 -4
- data/Rakefile +1 -1
- data/bin/god +19 -1
- data/lib/god.rb +86 -49
- data/lib/god/cli/command.rb +7 -1
- data/lib/god/cli/run.rb +58 -0
- data/lib/god/condition.rb +6 -2
- data/lib/god/conditions/cpu_usage.rb +7 -6
- data/lib/god/conditions/http_response_code.rb +5 -1
- data/lib/god/conditions/memory_usage.rb +7 -6
- data/lib/god/conditions/process_exits.rb +15 -10
- data/lib/god/conditions/process_running.rb +17 -13
- data/lib/god/diagnostics.rb +37 -0
- data/lib/god/driver.rb +108 -0
- data/lib/god/event_handler.rb +41 -1
- data/lib/god/logger.rb +69 -19
- data/lib/god/metric.rb +2 -2
- data/lib/god/process.rb +84 -27
- data/lib/god/task.rb +286 -29
- data/lib/god/timeline.rb +20 -31
- data/lib/god/watch.rb +26 -15
- data/test/configs/child_events/child_events.god +0 -5
- data/test/configs/child_polls/simple_server.rb +1 -1
- data/test/configs/daemon_events/simple_server_stop.rb +2 -0
- data/test/configs/stress/stress.god +1 -1
- data/test/configs/test.rb +12 -28
- data/test/test_condition.rb +8 -0
- data/test/test_conditions_http_response_code.rb +5 -5
- data/test/test_conditions_process_running.rb +6 -4
- data/test/test_driver.rb +11 -0
- data/test/test_event_handler.rb +7 -0
- data/test/test_god.rb +63 -62
- data/test/test_metric.rb +0 -16
- data/test/test_process.rb +29 -1
- data/test/test_task.rb +177 -1
- data/test/test_timeline.rb +2 -1
- data/test/test_watch.rb +24 -6
- metadata +6 -8
- data/lib/god/hub.rb +0 -222
- data/lib/god/timer.rb +0 -87
- data/test/test_hub.rb +0 -240
- data/test/test_timer.rb +0 -69
data/lib/god/timeline.rb
CHANGED
@@ -1,36 +1,25 @@
|
|
1
1
|
module God
|
2
|
+
|
3
|
+
class Timeline < Array
|
4
|
+
# Instantiate a new Timeline
|
5
|
+
# +max_size+ is the maximum size to which the timeline should grow
|
6
|
+
#
|
7
|
+
# Returns Timeline
|
8
|
+
def initialize(max_size)
|
9
|
+
super()
|
10
|
+
@max_size = max_size
|
11
|
+
end
|
2
12
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
# Implementation explanation:
|
12
|
-
# A performance optimization appears here to speed up the push time.
|
13
|
-
# In essence, the code does this:
|
14
|
-
#
|
15
|
-
# def push(val)
|
16
|
-
# super(val)
|
17
|
-
# shift if size > @max_size
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# But that's super slow due to the shift, so we resort to reverse! and pop
|
21
|
-
# which gives us a 2x speedup with 100 elements and a 6x speedup with 1000
|
22
|
-
def push(val)
|
23
|
-
if (size + 1) > @max_size
|
24
|
-
reverse!
|
25
|
-
pop
|
26
|
-
reverse!
|
27
|
-
end
|
28
|
-
super(val)
|
29
|
-
end
|
30
|
-
|
31
|
-
def <<(val)
|
32
|
-
push(val)
|
33
|
-
end
|
13
|
+
# Push a value onto the Timeline
|
14
|
+
# +val+ is the value to push
|
15
|
+
#
|
16
|
+
# Returns Timeline
|
17
|
+
def push(val)
|
18
|
+
self.concat([val])
|
19
|
+
shift if size > @max_size
|
34
20
|
end
|
35
21
|
|
22
|
+
alias_method :<<, :push
|
23
|
+
end
|
24
|
+
|
36
25
|
end
|
data/lib/god/watch.rb
CHANGED
@@ -13,7 +13,7 @@ module God
|
|
13
13
|
extend Forwardable
|
14
14
|
def_delegators :@process, :name, :uid, :gid, :start, :stop, :restart,
|
15
15
|
:name=, :uid=, :gid=, :start=, :stop=, :restart=,
|
16
|
-
:pid_file, :pid_file=, :log, :log=, :alive
|
16
|
+
:pid_file, :pid_file=, :log, :log=, :alive?, :pid
|
17
17
|
#
|
18
18
|
def initialize
|
19
19
|
super
|
@@ -97,22 +97,33 @@ module God
|
|
97
97
|
###########################################################################
|
98
98
|
|
99
99
|
def action(a, c = nil)
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
100
|
+
if Thread.current != self.driver.thread
|
101
|
+
# called from outside Driver
|
102
|
+
|
103
|
+
# send an async message to Driver
|
104
|
+
self.driver.message(:action, [a, c])
|
105
|
+
else
|
106
|
+
# called from within Driver
|
107
|
+
|
108
|
+
case a
|
109
|
+
when :start
|
110
|
+
call_action(c, :start)
|
111
|
+
sleep(self.start_grace + self.grace)
|
112
|
+
when :restart
|
113
|
+
if self.restart
|
114
|
+
call_action(c, :restart)
|
115
|
+
else
|
116
|
+
action(:stop, c)
|
117
|
+
action(:start, c)
|
118
|
+
end
|
119
|
+
sleep(self.restart_grace + self.grace)
|
120
|
+
when :stop
|
121
|
+
call_action(c, :stop)
|
122
|
+
sleep(self.stop_grace + self.grace)
|
110
123
|
end
|
111
|
-
sleep(self.restart_grace + self.grace)
|
112
|
-
when :stop
|
113
|
-
call_action(c, :stop)
|
114
|
-
sleep(self.stop_grace + self.grace)
|
115
124
|
end
|
125
|
+
|
126
|
+
self
|
116
127
|
end
|
117
128
|
|
118
129
|
def call_action(condition, action)
|
data/test/configs/test.rb
CHANGED
@@ -1,46 +1,21 @@
|
|
1
|
-
if $0 == __FILE__
|
2
|
-
require File.join(File.dirname(__FILE__), *%w[.. .. lib god])
|
3
|
-
end
|
4
|
-
|
5
1
|
ENV['GOD_TEST_RAILS_ROOT'] || abort("Set a rails root for testing in an environment variable called GOD_TEST_RAILS_ROOT")
|
6
2
|
|
7
3
|
RAILS_ROOT = ENV['GOD_TEST_RAILS_ROOT']
|
8
4
|
|
9
|
-
|
10
|
-
# g.host =
|
11
|
-
# g.port = 7777
|
12
|
-
# g.pid_file_directory =
|
13
|
-
end
|
14
|
-
|
15
|
-
class SimpleNotifier
|
16
|
-
def self.notify(str)
|
17
|
-
puts "Notifying: #{str}"
|
18
|
-
end
|
19
|
-
end
|
5
|
+
port = 5000
|
20
6
|
|
21
7
|
God.watch do |w|
|
22
|
-
w.name = "local
|
8
|
+
w.name = "local-#{port}"
|
23
9
|
w.interval = 5.seconds
|
24
|
-
w.start = "mongrel_rails start -P ./log/mongrel.pid -c #{RAILS_ROOT} -p
|
10
|
+
w.start = "mongrel_rails start -P ./log/mongrel.pid -c #{RAILS_ROOT} -p #{port} -d"
|
25
11
|
w.restart = "mongrel_rails restart -P ./log/mongrel.pid -c #{RAILS_ROOT}"
|
26
12
|
w.stop = "mongrel_rails stop -P ./log/mongrel.pid -c #{RAILS_ROOT}"
|
27
|
-
w.restart_grace = 5.seconds
|
28
|
-
w.stop_grace = 5.seconds
|
29
|
-
w.autostart = true
|
30
|
-
w.uid = 'kev'
|
31
|
-
w.gid = 'kev'
|
32
13
|
w.group = 'mongrels'
|
33
14
|
w.pid_file = File.join(RAILS_ROOT, "log/mongrel.pid")
|
34
15
|
|
35
16
|
# clean pid files before start if necessary
|
36
17
|
w.behavior(:clean_pid_file)
|
37
18
|
|
38
|
-
w.behavior(:notify_when_flapping) do |b|
|
39
|
-
b.failures = 5
|
40
|
-
b.seconds = 60.seconds
|
41
|
-
b.notifier = SimpleNotifier
|
42
|
-
end
|
43
|
-
|
44
19
|
# determine the state on startup
|
45
20
|
w.transition(:init, { true => :up, false => :start }) do |on|
|
46
21
|
on.condition(:process_running) do |c|
|
@@ -73,5 +48,14 @@ God.watch do |w|
|
|
73
48
|
c.above = 10.percent
|
74
49
|
c.times = [3, 5]
|
75
50
|
end
|
51
|
+
|
52
|
+
on.condition(:http_response_code) do |c|
|
53
|
+
c.host = 'localhost'
|
54
|
+
c.port = port
|
55
|
+
c.path = '/'
|
56
|
+
c.code_is_not = 200
|
57
|
+
c.timeout = 10.seconds
|
58
|
+
c.times = [3, 5]
|
59
|
+
end
|
76
60
|
end
|
77
61
|
end
|
data/test/test_condition.rb
CHANGED
@@ -17,6 +17,14 @@ class TestCondition < Test::Unit::TestCase
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
def test_generate_should_abort_on_event_condition_without_loaded_event_system
|
21
|
+
God::EventHandler.stubs(:operational?).returns(false)
|
22
|
+
assert_abort do
|
23
|
+
God::EventHandler.start
|
24
|
+
Condition.generate(:process_exits, nil).class
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
20
28
|
def test_generate_should_return_a_good_error_message_for_invalid_types
|
21
29
|
emsg = "No Condition found with the class name God::Conditions::FooBar"
|
22
30
|
rmsg = nil
|
@@ -40,7 +40,7 @@ class TestHttpResponseCode < Test::Unit::TestCase
|
|
40
40
|
|
41
41
|
def test_test_should_return_false_if_code_is_is_set_to_200_but_response_is_500
|
42
42
|
c = valid_condition
|
43
|
-
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :
|
43
|
+
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :get => stub(:code => 500)))
|
44
44
|
assert_equal false, c.test
|
45
45
|
end
|
46
46
|
|
@@ -49,13 +49,13 @@ class TestHttpResponseCode < Test::Unit::TestCase
|
|
49
49
|
cc.code_is = nil
|
50
50
|
cc.code_is_not = [200]
|
51
51
|
end
|
52
|
-
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :
|
52
|
+
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :get => stub(:code => 200)))
|
53
53
|
assert_equal false, c.test
|
54
54
|
end
|
55
55
|
|
56
56
|
def test_test_should_return_true_if_code_is_is_set_to_200_and_response_is_200
|
57
57
|
c = valid_condition
|
58
|
-
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :
|
58
|
+
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :get => stub(:code => 200)))
|
59
59
|
assert_equal true, c.test
|
60
60
|
end
|
61
61
|
|
@@ -64,7 +64,7 @@ class TestHttpResponseCode < Test::Unit::TestCase
|
|
64
64
|
cc.code_is = nil
|
65
65
|
cc.code_is_not = [200]
|
66
66
|
end
|
67
|
-
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :
|
67
|
+
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :get => stub(:code => 500)))
|
68
68
|
assert_equal true, c.test
|
69
69
|
end
|
70
70
|
|
@@ -102,7 +102,7 @@ class TestHttpResponseCode < Test::Unit::TestCase
|
|
102
102
|
c = valid_condition do |cc|
|
103
103
|
cc.times = [2, 2]
|
104
104
|
end
|
105
|
-
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :
|
105
|
+
Net::HTTP.expects(:start).yields(stub(:read_timeout= => nil, :get => stub(:code => 200))).times(2)
|
106
106
|
assert_equal false, c.test
|
107
107
|
assert_equal true, c.test
|
108
108
|
end
|
@@ -6,9 +6,11 @@ class TestConditionsProcessRunning < Test::Unit::TestCase
|
|
6
6
|
c = Conditions::ProcessRunning.new
|
7
7
|
c.running = r
|
8
8
|
|
9
|
-
c.stubs(:watch).returns(stub(:
|
9
|
+
c.stubs(:watch).returns(stub(:pid => 123, :name => 'foo'))
|
10
10
|
|
11
|
-
no_stdout
|
11
|
+
# no_stdout do
|
12
|
+
assert_equal !r, c.test
|
13
|
+
# end
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
@@ -18,7 +20,7 @@ class TestConditionsProcessRunning < Test::Unit::TestCase
|
|
18
20
|
c.running = r
|
19
21
|
|
20
22
|
File.stubs(:exist?).returns(true)
|
21
|
-
c.stubs(:watch).returns(stub(:
|
23
|
+
c.stubs(:watch).returns(stub(:pid => 123))
|
22
24
|
File.stubs(:read).returns('5')
|
23
25
|
System::Process.any_instance.stubs(:exists?).returns(false)
|
24
26
|
|
@@ -32,7 +34,7 @@ class TestConditionsProcessRunning < Test::Unit::TestCase
|
|
32
34
|
c.running = r
|
33
35
|
|
34
36
|
File.stubs(:exist?).returns(true)
|
35
|
-
c.stubs(:watch).returns(stub(:
|
37
|
+
c.stubs(:watch).returns(stub(:pid => 123))
|
36
38
|
File.stubs(:read).returns('5')
|
37
39
|
System::Process.any_instance.stubs(:exists?).returns(true)
|
38
40
|
|
data/test/test_driver.rb
ADDED
data/test/test_event_handler.rb
CHANGED
data/test/test_god.rb
CHANGED
@@ -5,11 +5,18 @@ class TestGod < Test::Unit::TestCase
|
|
5
5
|
God::Socket.stubs(:new).returns(true)
|
6
6
|
God.stubs(:setup).returns(true)
|
7
7
|
God.stubs(:validater).returns(true)
|
8
|
+
Thread.any_instance.stubs(:join).returns(true)
|
8
9
|
God.reset
|
10
|
+
God.pid_file_directory = '/var/run/god'
|
9
11
|
end
|
10
12
|
|
11
13
|
def teardown
|
12
|
-
|
14
|
+
God.main && God.main.kill
|
15
|
+
if God.watches
|
16
|
+
God.watches.each do |k, w|
|
17
|
+
w.driver.thread.kill
|
18
|
+
end
|
19
|
+
end
|
13
20
|
end
|
14
21
|
|
15
22
|
# applog
|
@@ -170,7 +177,9 @@ class TestGod < Test::Unit::TestCase
|
|
170
177
|
w = God.watches['bar']
|
171
178
|
w.state = :up
|
172
179
|
w.expects(:unmonitor)
|
173
|
-
|
180
|
+
no_stdout do
|
181
|
+
God.unwatch(w)
|
182
|
+
end
|
174
183
|
end
|
175
184
|
|
176
185
|
def test_unwatch_should_unregister_watch
|
@@ -239,15 +248,19 @@ class TestGod < Test::Unit::TestCase
|
|
239
248
|
|
240
249
|
def test_contact_should_abort_on_duplicate_contact_name
|
241
250
|
God.contact(:fake_contact) { |c| c.name = 'tom' }
|
242
|
-
|
243
|
-
|
251
|
+
no_stdout do
|
252
|
+
assert_nothing_raised do
|
253
|
+
God.contact(:fake_contact) { |c| c.name = 'tom' }
|
254
|
+
end
|
244
255
|
end
|
245
256
|
end
|
246
257
|
|
247
258
|
def test_contact_should_abort_on_contact_with_same_name_as_group
|
248
259
|
God.contact(:fake_contact) { |c| c.name = 'tom'; c.group = 'devs' }
|
249
|
-
|
250
|
-
|
260
|
+
no_stdout do
|
261
|
+
assert_nothing_raised do
|
262
|
+
God.contact(:fake_contact) { |c| c.name = 'devs' }
|
263
|
+
end
|
251
264
|
end
|
252
265
|
end
|
253
266
|
|
@@ -482,15 +495,6 @@ class TestGod < Test::Unit::TestCase
|
|
482
495
|
God::Socket.expects(:new).returns(true)
|
483
496
|
God.start
|
484
497
|
end
|
485
|
-
|
486
|
-
def test_start_should_start_event_handler
|
487
|
-
God.watch { |w| w.name = 'foo'; w.start = 'bar' }
|
488
|
-
Timer.any_instance.expects(:join)
|
489
|
-
EventHandler.expects(:start).once
|
490
|
-
no_stdout do
|
491
|
-
God.start
|
492
|
-
end
|
493
|
-
end
|
494
498
|
|
495
499
|
def test_start_should_begin_monitoring_autostart_watches
|
496
500
|
God.watch do |w|
|
@@ -498,7 +502,6 @@ class TestGod < Test::Unit::TestCase
|
|
498
502
|
w.start = 'go'
|
499
503
|
end
|
500
504
|
|
501
|
-
Timer.any_instance.expects(:join)
|
502
505
|
Watch.any_instance.expects(:monitor).once
|
503
506
|
God.start
|
504
507
|
end
|
@@ -510,14 +513,12 @@ class TestGod < Test::Unit::TestCase
|
|
510
513
|
w.autostart = false
|
511
514
|
end
|
512
515
|
|
513
|
-
Timer.any_instance.expects(:join)
|
514
516
|
Watch.any_instance.expects(:monitor).never
|
515
517
|
God.start
|
516
518
|
end
|
517
519
|
|
518
520
|
def test_start_should_get_and_join_timer
|
519
521
|
God.watch { |w| w.name = 'foo'; w.start = 'bar' }
|
520
|
-
Timer.any_instance.expects(:join)
|
521
522
|
no_stdout do
|
522
523
|
God.start
|
523
524
|
end
|
@@ -543,47 +544,47 @@ class TestGod < Test::Unit::TestCase
|
|
543
544
|
end
|
544
545
|
|
545
546
|
|
546
|
-
class TestGodOther < Test::Unit::TestCase
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
end
|
547
|
+
# class TestGodOther < Test::Unit::TestCase
|
548
|
+
# def setup
|
549
|
+
# God::Socket.stubs(:new).returns(true)
|
550
|
+
# God.internal_init
|
551
|
+
# God.reset
|
552
|
+
# end
|
553
|
+
#
|
554
|
+
# def teardown
|
555
|
+
# God.main && God.main.kill
|
556
|
+
# end
|
557
|
+
#
|
558
|
+
# # setup
|
559
|
+
#
|
560
|
+
# def test_setup_should_create_pid_file_directory_if_it_doesnt_exist
|
561
|
+
# God.expects(:test).returns(false)
|
562
|
+
# FileUtils.expects(:mkdir_p).with(God.pid_file_directory)
|
563
|
+
# God.setup
|
564
|
+
# end
|
565
|
+
#
|
566
|
+
# def test_setup_should_raise_if_no_permissions_to_create_pid_file_directory
|
567
|
+
# God.expects(:test).returns(false)
|
568
|
+
# FileUtils.expects(:mkdir_p).raises(Errno::EACCES)
|
569
|
+
#
|
570
|
+
# assert_abort do
|
571
|
+
# God.setup
|
572
|
+
# end
|
573
|
+
# end
|
574
|
+
#
|
575
|
+
# # validate
|
576
|
+
#
|
577
|
+
# def test_validate_should_abort_if_pid_file_directory_is_unwriteable
|
578
|
+
# God.expects(:test).returns(false)
|
579
|
+
# assert_abort do
|
580
|
+
# God.validater
|
581
|
+
# end
|
582
|
+
# end
|
583
|
+
#
|
584
|
+
# def test_validate_should_not_abort_if_pid_file_directory_is_writeable
|
585
|
+
# God.expects(:test).returns(true)
|
586
|
+
# assert_nothing_raised do
|
587
|
+
# God.validater
|
588
|
+
# end
|
589
|
+
# end
|
590
|
+
# end
|