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.
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