god 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/History.txt +67 -1
  2. data/Manifest.txt +3 -4
  3. data/Rakefile +1 -1
  4. data/bin/god +19 -1
  5. data/lib/god.rb +86 -49
  6. data/lib/god/cli/command.rb +7 -1
  7. data/lib/god/cli/run.rb +58 -0
  8. data/lib/god/condition.rb +6 -2
  9. data/lib/god/conditions/cpu_usage.rb +7 -6
  10. data/lib/god/conditions/http_response_code.rb +5 -1
  11. data/lib/god/conditions/memory_usage.rb +7 -6
  12. data/lib/god/conditions/process_exits.rb +15 -10
  13. data/lib/god/conditions/process_running.rb +17 -13
  14. data/lib/god/diagnostics.rb +37 -0
  15. data/lib/god/driver.rb +108 -0
  16. data/lib/god/event_handler.rb +41 -1
  17. data/lib/god/logger.rb +69 -19
  18. data/lib/god/metric.rb +2 -2
  19. data/lib/god/process.rb +84 -27
  20. data/lib/god/task.rb +286 -29
  21. data/lib/god/timeline.rb +20 -31
  22. data/lib/god/watch.rb +26 -15
  23. data/test/configs/child_events/child_events.god +0 -5
  24. data/test/configs/child_polls/simple_server.rb +1 -1
  25. data/test/configs/daemon_events/simple_server_stop.rb +2 -0
  26. data/test/configs/stress/stress.god +1 -1
  27. data/test/configs/test.rb +12 -28
  28. data/test/test_condition.rb +8 -0
  29. data/test/test_conditions_http_response_code.rb +5 -5
  30. data/test/test_conditions_process_running.rb +6 -4
  31. data/test/test_driver.rb +11 -0
  32. data/test/test_event_handler.rb +7 -0
  33. data/test/test_god.rb +63 -62
  34. data/test/test_metric.rb +0 -16
  35. data/test/test_process.rb +29 -1
  36. data/test/test_task.rb +177 -1
  37. data/test/test_timeline.rb +2 -1
  38. data/test/test_watch.rb +24 -6
  39. metadata +6 -8
  40. data/lib/god/hub.rb +0 -222
  41. data/lib/god/timer.rb +0 -87
  42. data/test/test_hub.rb +0 -240
  43. data/test/test_timer.rb +0 -69
@@ -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
- class Timeline < Array
4
- def initialize(max_size)
5
- super()
6
- @max_size = max_size
7
- end
8
-
9
- # Push a value onto the Timeline
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
@@ -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
- case a
101
- when :start
102
- call_action(c, :start)
103
- sleep(self.start_grace + self.grace)
104
- when :restart
105
- if self.restart
106
- call_action(c, :restart)
107
- else
108
- action(:stop, c)
109
- action(:start, c)
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)
@@ -1,8 +1,3 @@
1
- module God
2
- module Behaviors
3
- end
4
- end
5
-
6
1
  God.watch do |w|
7
2
  w.name = "child-events"
8
3
  w.interval = 5.seconds
@@ -8,5 +8,5 @@ loop do
8
8
 
9
9
  100000.times { data << 'x' }
10
10
 
11
- sleep 0.1
11
+ sleep 10
12
12
  end
@@ -1,3 +1,5 @@
1
+ # exit!(2)
2
+
1
3
  3.times do
2
4
  puts 'waiting'
3
5
  sleep 1
@@ -1,4 +1,4 @@
1
- ('01'..'20').each do |i|
1
+ ('01'..'40').each do |i|
2
2
  God.watch do |w|
3
3
  w.name = "stress-#{i}"
4
4
  w.start = "ruby " + File.join(File.dirname(__FILE__), *%w[simple_server.rb])
@@ -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
- God.init do |g|
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-3000"
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 3001 -d"
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
@@ -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, :head => stub(:code => 500)))
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, :head => stub(:code => 200)))
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, :head => stub(:code => 200)))
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, :head => stub(:code => 500)))
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, :head => stub(:code => 200))).times(2)
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(:pid_file => '', :name => 'foo'))
9
+ c.stubs(:watch).returns(stub(:pid => 123, :name => 'foo'))
10
10
 
11
- no_stdout { assert_equal !r, c.test }
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(:pid_file => ''))
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(:pid_file => ''))
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
 
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestDriver < Test::Unit::TestCase
4
+ def setup
5
+
6
+ end
7
+
8
+ def test_
9
+
10
+ end
11
+ end
@@ -70,4 +70,11 @@ class TestEventHandler < Test::Unit::TestCase
70
70
  @h.actions = {}
71
71
  @h.load
72
72
  end
73
+ end
74
+
75
+ class TestEventHandlerOperational < Test::Unit::TestCase
76
+ def test_operational
77
+ God::EventHandler.start
78
+ assert God::EventHandler.loaded?
79
+ end
73
80
  end
@@ -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
- Timer.get.timer.kill
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
- God.unwatch(w)
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
- assert_abort do
243
- God.contact(:fake_contact) { |c| c.name = 'tom' }
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
- assert_abort do
250
- God.contact(:fake_contact) { |c| c.name = 'devs' }
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
- def setup
548
- God::Socket.stubs(:new).returns(true)
549
- God.internal_init
550
- God.reset
551
- end
552
-
553
- def teardown
554
- Timer.get.timer.kill
555
- end
556
-
557
- # setup
558
-
559
- def test_setup_should_create_pid_file_directory_if_it_doesnt_exist
560
- God.expects(:test).returns(false)
561
- FileUtils.expects(:mkdir_p).with(God.pid_file_directory)
562
- God.setup
563
- end
564
-
565
- def test_setup_should_raise_if_no_permissions_to_create_pid_file_directory
566
- God.expects(:test).returns(false)
567
- FileUtils.expects(:mkdir_p).raises(Errno::EACCES)
568
-
569
- assert_abort do
570
- God.setup
571
- end
572
- end
573
-
574
- # validate
575
-
576
- def test_validate_should_abort_if_pid_file_directory_is_unwriteable
577
- God.expects(:test).returns(false)
578
- assert_abort do
579
- God.validater
580
- end
581
- end
582
-
583
- def test_validate_should_not_abort_if_pid_file_directory_is_writeable
584
- God.expects(:test).returns(true)
585
- assert_nothing_raised do
586
- God.validater
587
- end
588
- end
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