god 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|