god 0.1.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.
@@ -0,0 +1,64 @@
1
+ if $0 == __FILE__
2
+ require File.join(File.dirname(__FILE__), *%w[.. .. lib god])
3
+ end
4
+
5
+ RAILS_ROOT = "/Users/tom/dev/git/helloworld"
6
+
7
+ God.meddle do |god|
8
+ god.watch do |w|
9
+ w.name = "local-3000"
10
+ w.interval = 5 # seconds
11
+ w.start = "mongrel_rails start -P ./log/mongrel.pid -c #{RAILS_ROOT} -d"
12
+ w.stop = "mongrel_rails stop -P ./log/mongrel.pid -c #{RAILS_ROOT}"
13
+ w.grace = 5
14
+
15
+ pid_file = File.join(RAILS_ROOT, "log/mongrel.pid")
16
+
17
+ # clean pid files before start if necessary
18
+ w.behavior(:clean_pid_file) do |b|
19
+ b.pid_file = pid_file
20
+ end
21
+
22
+ # start if process is not running
23
+ w.start_if do |start|
24
+ # start.condition(:process_exits) do |c|
25
+ # c.pid_file = pid_file
26
+ # end
27
+
28
+ start.condition(:process_not_running) do |c|
29
+ c.pid_file = pid_file
30
+ end
31
+ end
32
+
33
+ # restart if memory or cpu is too high
34
+ w.restart_if do |restart|
35
+ restart.condition(:memory_usage) do |c|
36
+ c.interval = 20
37
+ c.pid_file = pid_file
38
+ c.above = (50 * 1024) # 50mb
39
+ c.times = [3, 5]
40
+ end
41
+
42
+ restart.condition(:cpu_usage) do |c|
43
+ c.interval = 10
44
+ c.pid_file = pid_file
45
+ c.above = 10 # percent
46
+ c.times = [3, 5]
47
+ end
48
+ end
49
+ end
50
+
51
+ # clear old session files
52
+ # god.watch do |w|
53
+ # w.name = "local-session-cleanup"
54
+ # w.start = lambda do
55
+ # Dir["#{RAILS_ROOT}/tmp/sessions/ruby_sess.*"].select do |f|
56
+ # File.mtime(f) < Time.now - (7 * 24 * 60 * 60)
57
+ # end.each { |f| File.delete(f) }
58
+ # end
59
+ #
60
+ # w.start_if do |start|
61
+ # start.condition(:always)
62
+ # end
63
+ # end
64
+ end
@@ -0,0 +1,93 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. lib god])
2
+
3
+ require 'test/unit'
4
+
5
+ begin
6
+ require 'mocha'
7
+ rescue LoadError
8
+ unless gems ||= false
9
+ require 'rubygems'
10
+ gems = true
11
+ retry
12
+ else
13
+ abort "=> You need the Mocha gem to run these tests."
14
+ end
15
+ end
16
+
17
+ include God
18
+
19
+ module God
20
+ class AbortCalledError < StandardError
21
+ end
22
+
23
+ class Base
24
+ def abort(msg)
25
+ raise AbortCalledError.new("abort called")
26
+ end
27
+
28
+ def self.abort(msg)
29
+ raise AbortCalledError.new("abort called")
30
+ end
31
+ end
32
+
33
+ module Conditions
34
+ class FakeCondition < Condition
35
+ def test
36
+ true
37
+ end
38
+ end
39
+
40
+ class FakePollCondition < PollCondition
41
+ def test
42
+ true
43
+ end
44
+ end
45
+
46
+ class FakeEventCondition < EventCondition
47
+ def test
48
+ true
49
+ end
50
+ end
51
+ end
52
+
53
+ module Behaviors
54
+ class FakeBehavior < Behavior
55
+ end
56
+ end
57
+ end
58
+
59
+ def silence_warnings
60
+ old_verbose, $VERBOSE = $VERBOSE, nil
61
+ yield
62
+ ensure
63
+ $VERBOSE = old_verbose
64
+ end
65
+
66
+ # This allows you to be a good OOP citizen and honor encapsulation, but
67
+ # still make calls to private methods (for testing) by doing
68
+ #
69
+ # obj.bypass.private_thingie(arg1, arg2)
70
+ #
71
+ # Which is easier on the eye than
72
+ #
73
+ # obj.send(:private_thingie, arg1, arg2)
74
+ #
75
+ class Object
76
+ class Bypass
77
+ instance_methods.each do |m|
78
+ undef_method m unless m =~ /^__/
79
+ end
80
+
81
+ def initialize(ref)
82
+ @ref = ref
83
+ end
84
+
85
+ def method_missing(sym, *args)
86
+ @ref.__send__(sym, *args)
87
+ end
88
+ end
89
+
90
+ def bypass
91
+ Bypass.new(self)
92
+ end
93
+ end
@@ -0,0 +1,6 @@
1
+ require 'test/unit'
2
+
3
+ tests = Dir["#{File.dirname(__FILE__)}/test_*.rb"]
4
+ tests.each do |file|
5
+ require file
6
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestBehavior < Test::Unit::TestCase
4
+ def test_generate_should_return_an_object_corresponding_to_the_given_type
5
+ assert_equal Behaviors::FakeBehavior, Behavior.generate(:fake_behavior).class
6
+ end
7
+
8
+ def test_generate_should_raise_on_invalid_type
9
+ assert_raise NoSuchBehaviorError do
10
+ Behavior.generate(:foo)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestCondition < Test::Unit::TestCase
4
+ def test_generate_should_return_an_object_corresponding_to_the_given_type
5
+ assert_equal Conditions::ProcessNotRunning, Condition.generate(:process_not_running).class
6
+ end
7
+
8
+ def test_generate_should_raise_on_invalid_type
9
+ assert_raise NoSuchConditionError do
10
+ Condition.generate(:foo)
11
+ end
12
+ end
13
+
14
+ def test_generate_should_return_a_good_error_message_for_invalid_types
15
+ emsg = "No Condition found with the class name God::Conditions::FooBar"
16
+ rmsg = nil
17
+
18
+ begin
19
+ Condition.generate(:foo_bar)
20
+ rescue => e
21
+ rmsg = e.message
22
+ end
23
+
24
+ assert_equal emsg, rmsg
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestGod < Test::Unit::TestCase
4
+ def test_should_create_new_meddle
5
+ Meddle.expects(:new).with(:port => 1).returns(mock(:monitor => true, :timer => stub(:join => nil)))
6
+
7
+ God.meddle(:port => 1) {}
8
+ end
9
+
10
+ def test_should_start_monitoring
11
+ Meddle.any_instance.expects(:monitor)
12
+ Meddle.any_instance.expects(:timer).returns(stub(:join => nil))
13
+ God.meddle {}
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestMeddle < Test::Unit::TestCase
4
+ def setup
5
+ Server.stubs(:new).returns(true)
6
+ @meddle = Meddle.new
7
+ end
8
+
9
+ def test_should_initialize_watches_to_empty_array
10
+ assert_equal [], @meddle.watches
11
+ end
12
+
13
+ def test_watches_should_get_stored
14
+ watch = nil
15
+ @meddle.watch { |w| watch = w }
16
+
17
+ assert_equal 1, @meddle.watches.size
18
+ assert_equal watch, @meddle.watches.first
19
+ end
20
+
21
+ def test_should_kick_off_a_server_instance
22
+ Server.expects(:new).returns(true)
23
+ Meddle.new
24
+ end
25
+
26
+ def test_should_take_an_options_hash
27
+ Server.expects(:new)
28
+ Meddle.new(:port => 5555)
29
+ end
30
+
31
+ def test_should_allow_multiple_watches
32
+ @meddle.watch { |w| w.name = 'foo' }
33
+
34
+ assert_nothing_raised do
35
+ @meddle.watch { |w| w.name = 'bar' }
36
+ end
37
+ end
38
+
39
+ def test_should_disallow_duplicate_watch_names
40
+ @meddle.watch { |w| w.name = 'foo' }
41
+
42
+ assert_raise AbortCalledError do
43
+ @meddle.watch { |w| w.name = 'foo' }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestReporter < Test::Unit::TestCase
4
+
5
+ def test_should_create_a_drb_object
6
+ DRb.expects(:start_service)
7
+ DRbObject.expects(:new).with(nil, "druby://host:port").returns(stub(:anything => true))
8
+
9
+ Reporter.new('host', 'port').anything
10
+ end
11
+
12
+ def test_should_forward_unknown_methods_to_drb_object
13
+ Reporter.any_instance.expects(:service).returns(mock(:something_fake => true))
14
+
15
+ reporter = Reporter.new('host', 'port')
16
+ reporter.something_fake
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestServer < Test::Unit::TestCase
4
+ def setup
5
+ silence_warnings do
6
+ Object.const_set(:DRb, stub_everything)
7
+ end
8
+ end
9
+
10
+ def test_should_start_a_drb_server
11
+ DRb.expects(:start_service)
12
+ Server.new
13
+ end
14
+
15
+ def test_should_use_supplied_port_and_host
16
+ DRb.expects(:start_service).with { |uri, object| uri == "druby://host:port" && object.is_a?(Server) }
17
+ server = Server.new(nil, 'host', 'port')
18
+ end
19
+
20
+ def test_should_forward_foreign_method_calls_to_meddle
21
+ server = Server.new(mock(:something_random => true))
22
+ server.something_random
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSystemProcess < Test::Unit::TestCase
4
+ def setup
5
+ pid = Process.pid
6
+ @process = System::Process.new(pid)
7
+ end
8
+
9
+ def test_exists_should_return_true_for_running_process
10
+ assert_equal true, @process.exists?
11
+ end
12
+
13
+ def test_exists_should_return_false_for_non_existant_process
14
+ assert_equal false, System::Process.new(0).exists?
15
+ end
16
+
17
+ def test_memory
18
+ assert_kind_of Integer, @process.memory
19
+ assert @process.memory > 0
20
+ end
21
+
22
+ def test_percent_memory
23
+ assert_kind_of Float, @process.percent_memory
24
+ end
25
+
26
+ def test_percent_cpu
27
+ assert_kind_of Float, @process.percent_cpu
28
+ end
29
+
30
+ def test_cpu_time
31
+ assert_kind_of Integer, @process.cpu_time
32
+ end
33
+
34
+ def test_time_string_to_seconds
35
+ assert_equal 0, @process.bypass.time_string_to_seconds('0:00:00')
36
+ assert_equal 0, @process.bypass.time_string_to_seconds('0:00:55')
37
+ assert_equal 27, @process.bypass.time_string_to_seconds('0:27:32')
38
+ assert_equal 75, @process.bypass.time_string_to_seconds('1:15:13')
39
+ assert_equal 735, @process.bypass.time_string_to_seconds('12:15:13')
40
+ end
41
+ end
42
+
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestTimeline < Test::Unit::TestCase
4
+ def setup
5
+ @timeline = Conditions::Timeline.new(5)
6
+ end
7
+
8
+ def test_new_should_be_empty
9
+ assert_equal 0, @timeline.size
10
+ end
11
+
12
+ def test_should_not_grow_to_more_than_size
13
+ (1..10).each do |i|
14
+ @timeline.push(i)
15
+ end
16
+
17
+ assert_equal [10, 9, 8, 7, 6], @timeline
18
+ end
19
+
20
+ def test_clear_should_clear_array
21
+ @timeline.push(1)
22
+ assert_equal [], @timeline.clear
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestTimer < Test::Unit::TestCase
4
+ def setup
5
+ @t = Timer.new
6
+ end
7
+
8
+ def test_new_timer_should_have_no_events
9
+ assert_equal 0, @t.events.size
10
+ end
11
+
12
+ def test_register_should_queue_event
13
+ Time.stubs(:now).returns(0)
14
+
15
+ w = Watch.new(nil)
16
+ @t.register(w, stub(:interval => 20), nil)
17
+
18
+ assert_equal 1, @t.events.size
19
+ assert_equal w, @t.events.first.watch
20
+ end
21
+
22
+ def test_timer_should_remove_expired_events
23
+ @t.register(nil, stub(:interval => 0), nil)
24
+ sleep(0.3)
25
+ assert_equal 0, @t.events.size
26
+ end
27
+
28
+ def test_timer_should_remove_only_expired_events
29
+ @t.register(nil, stub(:interval => 0), nil)
30
+ @t.register(nil, stub(:interval => 1000), nil)
31
+ sleep(0.3)
32
+ assert_equal 1, @t.events.size
33
+ end
34
+
35
+ def test_timer_should_sort_timer_events
36
+ @t.register(nil, stub(:interval => 1000), nil)
37
+ @t.register(nil, stub(:interval => 800), nil)
38
+ @t.register(nil, stub(:interval => 900), nil)
39
+ @t.register(nil, stub(:interval => 100), nil)
40
+ sleep(0.3)
41
+ assert_equal [100, 800, 900, 1000], @t.events.map { |x| x.condition.interval }
42
+ end
43
+ end
@@ -0,0 +1,123 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestWatch < Test::Unit::TestCase
4
+ def setup
5
+ @watch = Watch.new(nil)
6
+ end
7
+
8
+ def test_should_have_empty_start_conditions
9
+ assert_equal [], @watch.conditions[:start]
10
+ end
11
+
12
+ def test_should_have_empty_restart_conditions
13
+ assert_equal [], @watch.conditions[:restart]
14
+ end
15
+
16
+ def test_should_have_standard_attributes
17
+ assert_nothing_raised do
18
+ @watch.name = 'foo'
19
+ @watch.start = 'start'
20
+ @watch.stop = 'stop'
21
+ @watch.restart = 'restart'
22
+ @watch.interval = 30
23
+ @watch.grace = 5
24
+ end
25
+ end
26
+
27
+ # _if methods
28
+
29
+ def test_start_if_should_modify_action_within_scope
30
+ assert_equal nil, @watch.instance_variable_get(:@action)
31
+ @watch.start_if do |w|
32
+ assert_equal :start, @watch.instance_variable_get(:@action)
33
+ end
34
+ assert_equal nil, @watch.instance_variable_get(:@action)
35
+ end
36
+
37
+ def test_restart_if_should_modify_action_within_scope
38
+ assert_equal nil, @watch.instance_variable_get(:@action)
39
+ @watch.restart_if do |w|
40
+ assert_equal :restart, @watch.instance_variable_get(:@action)
41
+ end
42
+ assert_equal nil, @watch.instance_variable_get(:@action)
43
+ end
44
+
45
+ # condition
46
+
47
+ def test_start_condition_should_record_condition_in_correct_list
48
+ cond = nil
49
+ @watch.interval = 0
50
+ @watch.start_if do |w|
51
+ w.condition(:fake_poll_condition) { |c| cond = c }
52
+ end
53
+ assert_equal 1, @watch.conditions[:start].size
54
+ assert_equal cond, @watch.conditions[:start].first
55
+ end
56
+
57
+ def test_restart_condition_should_record_condition_in_correct_list
58
+ cond = nil
59
+ @watch.interval = 0
60
+ @watch.restart_if do |w|
61
+ w.condition(:fake_poll_condition) { |c| cond = c }
62
+ end
63
+ assert_equal 1, @watch.conditions[:restart].size
64
+ assert_equal cond, @watch.conditions[:restart].first
65
+ end
66
+
67
+ def test_condition_called_from_outside_if_block_should_raise
68
+ assert_raise AbortCalledError do
69
+ @watch.condition(:fake_poll_condition) { |c| cond = c }
70
+ end
71
+ end
72
+
73
+ def test_condition_should_be_block_optional
74
+ @watch.interval = 0
75
+ @watch.start_if do |w|
76
+ w.condition(:always)
77
+ end
78
+ assert_equal 1, @watch.conditions[:start].size
79
+ end
80
+
81
+ def test_poll_condition_should_inherit_interval_from_watch_if_not_specified
82
+ @watch.interval = 27
83
+ @watch.start_if do |w|
84
+ w.condition(:fake_poll_condition)
85
+ end
86
+ assert_equal 27, @watch.conditions[:start].first.interval
87
+ end
88
+
89
+ def test_poll_condition_should_abort_if_no_interval_and_no_watch_interval
90
+ assert_raise AbortCalledError do
91
+ @watch.start_if do |w|
92
+ w.condition(:fake_poll_condition)
93
+ end
94
+ end
95
+ end
96
+
97
+ def test_condition_should_allow_generation_of_subclasses_of_poll_or_event
98
+ @watch.interval = 27
99
+ assert_nothing_raised do
100
+ @watch.start_if do |w|
101
+ w.condition(:fake_poll_condition)
102
+ w.condition(:fake_event_condition)
103
+ end
104
+ end
105
+ end
106
+
107
+ def test_condition_should_abort_if_not_subclass_of_poll_or_event
108
+ assert_raise AbortCalledError do
109
+ @watch.start_if do |w|
110
+ w.condition(:fake_condition) { |c| }
111
+ end
112
+ end
113
+ end
114
+
115
+ # behavior
116
+
117
+ def test_behavior_should_record_behavior
118
+ beh = nil
119
+ @watch.behavior(:fake_behavior) { |b| beh = b }
120
+ assert_equal 1, @watch.behaviors.size
121
+ assert_equal beh, @watch.behaviors.first
122
+ end
123
+ end