dosire-god 0.7.9

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 (108) hide show
  1. data/History.txt +261 -0
  2. data/Manifest.txt +107 -0
  3. data/README.txt +59 -0
  4. data/Rakefile +35 -0
  5. data/bin/god +127 -0
  6. data/examples/events.god +84 -0
  7. data/examples/gravatar.god +54 -0
  8. data/examples/single.god +66 -0
  9. data/ext/god/extconf.rb +55 -0
  10. data/ext/god/kqueue_handler.c +123 -0
  11. data/ext/god/netlink_handler.c +167 -0
  12. data/init/god +42 -0
  13. data/lib/god/behavior.rb +52 -0
  14. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  15. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  16. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  17. data/lib/god/cli/command.rb +206 -0
  18. data/lib/god/cli/run.rb +177 -0
  19. data/lib/god/cli/version.rb +23 -0
  20. data/lib/god/condition.rb +96 -0
  21. data/lib/god/conditions/always.rb +23 -0
  22. data/lib/god/conditions/complex.rb +86 -0
  23. data/lib/god/conditions/cpu_usage.rb +80 -0
  24. data/lib/god/conditions/degrading_lambda.rb +52 -0
  25. data/lib/god/conditions/disk_usage.rb +27 -0
  26. data/lib/god/conditions/flapping.rb +128 -0
  27. data/lib/god/conditions/http_response_code.rb +168 -0
  28. data/lib/god/conditions/lambda.rb +25 -0
  29. data/lib/god/conditions/memory_usage.rb +82 -0
  30. data/lib/god/conditions/process_exits.rb +72 -0
  31. data/lib/god/conditions/process_running.rb +74 -0
  32. data/lib/god/conditions/tries.rb +44 -0
  33. data/lib/god/configurable.rb +57 -0
  34. data/lib/god/contact.rb +106 -0
  35. data/lib/god/contacts/email.rb +95 -0
  36. data/lib/god/dependency_graph.rb +41 -0
  37. data/lib/god/diagnostics.rb +37 -0
  38. data/lib/god/driver.rb +206 -0
  39. data/lib/god/errors.rb +24 -0
  40. data/lib/god/event_handler.rb +111 -0
  41. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  42. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  43. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  44. data/lib/god/logger.rb +120 -0
  45. data/lib/god/metric.rb +59 -0
  46. data/lib/god/process.rb +327 -0
  47. data/lib/god/registry.rb +32 -0
  48. data/lib/god/simple_logger.rb +53 -0
  49. data/lib/god/socket.rb +96 -0
  50. data/lib/god/sugar.rb +47 -0
  51. data/lib/god/system/portable_poller.rb +42 -0
  52. data/lib/god/system/process.rb +42 -0
  53. data/lib/god/system/slash_proc_poller.rb +82 -0
  54. data/lib/god/task.rb +487 -0
  55. data/lib/god/timeline.rb +25 -0
  56. data/lib/god/trigger.rb +43 -0
  57. data/lib/god/watch.rb +183 -0
  58. data/lib/god.rb +644 -0
  59. data/test/configs/child_events/child_events.god +44 -0
  60. data/test/configs/child_events/simple_server.rb +3 -0
  61. data/test/configs/child_polls/child_polls.god +37 -0
  62. data/test/configs/child_polls/simple_server.rb +12 -0
  63. data/test/configs/complex/complex.god +59 -0
  64. data/test/configs/complex/simple_server.rb +3 -0
  65. data/test/configs/contact/contact.god +74 -0
  66. data/test/configs/contact/simple_server.rb +3 -0
  67. data/test/configs/daemon_events/daemon_events.god +37 -0
  68. data/test/configs/daemon_events/simple_server.rb +8 -0
  69. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  70. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  71. data/test/configs/daemon_polls/simple_server.rb +6 -0
  72. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  73. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  74. data/test/configs/matias/matias.god +50 -0
  75. data/test/configs/real.rb +59 -0
  76. data/test/configs/running_load/running_load.god +16 -0
  77. data/test/configs/stress/simple_server.rb +3 -0
  78. data/test/configs/stress/stress.god +15 -0
  79. data/test/configs/task/logs/.placeholder +0 -0
  80. data/test/configs/task/task.god +26 -0
  81. data/test/configs/test.rb +61 -0
  82. data/test/helper.rb +151 -0
  83. data/test/suite.rb +6 -0
  84. data/test/test_behavior.rb +21 -0
  85. data/test/test_condition.rb +50 -0
  86. data/test/test_conditions_disk_usage.rb +56 -0
  87. data/test/test_conditions_http_response_code.rb +109 -0
  88. data/test/test_conditions_process_running.rb +44 -0
  89. data/test/test_conditions_tries.rb +67 -0
  90. data/test/test_contact.rb +109 -0
  91. data/test/test_dependency_graph.rb +62 -0
  92. data/test/test_driver.rb +11 -0
  93. data/test/test_event_handler.rb +80 -0
  94. data/test/test_god.rb +598 -0
  95. data/test/test_handlers_kqueue_handler.rb +16 -0
  96. data/test/test_logger.rb +63 -0
  97. data/test/test_metric.rb +72 -0
  98. data/test/test_process.rb +246 -0
  99. data/test/test_registry.rb +15 -0
  100. data/test/test_socket.rb +42 -0
  101. data/test/test_sugar.rb +42 -0
  102. data/test/test_system_portable_poller.rb +17 -0
  103. data/test/test_system_process.rb +30 -0
  104. data/test/test_task.rb +262 -0
  105. data/test/test_timeline.rb +37 -0
  106. data/test/test_trigger.rb +59 -0
  107. data/test/test_watch.rb +279 -0
  108. metadata +186 -0
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestLogger < Test::Unit::TestCase
4
+ def setup
5
+ @log = God::Logger.new
6
+ end
7
+
8
+ # log
9
+
10
+ def test_log
11
+ @log.expects(:info).with("qux")
12
+
13
+ no_stdout do
14
+ @log.log(stub(:name => 'foo'), :info, "qux")
15
+ end
16
+
17
+ assert_equal 1, @log.logs.size
18
+ assert_instance_of Time, @log.logs['foo'][0][0]
19
+ assert_match(/qux/, @log.logs['foo'][0][1])
20
+ end
21
+
22
+ def test_log_should_send_to_syslog
23
+ Syslog.expects(:crit).with('foo')
24
+
25
+ no_stdout do
26
+ @log.log(stub(:name => 'foo'), :fatal, "foo")
27
+ end
28
+ end
29
+
30
+ # watch_log_since
31
+
32
+ def test_watch_log_since
33
+ t1 = Time.now
34
+
35
+ no_stdout do
36
+ @log.log(stub(:name => 'foo'), :info, "one")
37
+ @log.log(stub(:name => 'foo'), :info, "two")
38
+ end
39
+
40
+ assert_match(/one.*two/m, @log.watch_log_since('foo', t1))
41
+
42
+ t2 = Time.now
43
+
44
+ no_stdout do
45
+ @log.log(stub(:name => 'foo'), :info, "three")
46
+ end
47
+
48
+ out = @log.watch_log_since('foo', t2)
49
+
50
+ assert_no_match(/one/, out)
51
+ assert_no_match(/two/, out)
52
+ assert_match(/three/, out)
53
+ end
54
+
55
+ # regular methods
56
+
57
+ def test_fatal
58
+ no_stdout do
59
+ @log.fatal('foo')
60
+ end
61
+ assert_equal 0, @log.logs.size
62
+ end
63
+ end
@@ -0,0 +1,72 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestMetric < Test::Unit::TestCase
4
+ def setup
5
+ @metric = Metric.new(stub(:interval => 10), nil)
6
+ end
7
+
8
+ # watch
9
+
10
+ def test_watch
11
+ w = stub()
12
+ m = Metric.new(w, nil)
13
+ assert_equal w, m.watch
14
+ end
15
+
16
+ # destination
17
+
18
+ def test_destination
19
+ d = stub()
20
+ m = Metric.new(nil, d)
21
+ assert_equal d, m.destination
22
+ end
23
+
24
+ # condition
25
+
26
+ def test_condition_should_be_block_optional
27
+ @metric.condition(:fake_poll_condition)
28
+ assert_equal 1, @metric.conditions.size
29
+ end
30
+
31
+ def test_poll_condition_should_inherit_interval_from_watch_if_not_specified
32
+ @metric.condition(:fake_poll_condition)
33
+ assert_equal 10, @metric.conditions.first.interval
34
+ end
35
+
36
+ def test_poll_condition_should_abort_if_no_interval_and_no_watch_interval
37
+ metric = Metric.new(stub(:name => 'foo', :interval => nil), nil)
38
+
39
+ assert_abort do
40
+ metric.condition(:fake_poll_condition)
41
+ end
42
+ end
43
+
44
+ def test_condition_should_allow_generation_of_subclasses_of_poll_or_event
45
+ metric = Metric.new(stub(:name => 'foo', :interval => 10), nil)
46
+
47
+ assert_nothing_raised do
48
+ metric.condition(:fake_poll_condition)
49
+ metric.condition(:fake_event_condition)
50
+ end
51
+ end
52
+
53
+ def test_condition_should_abort_if_not_subclass_of_poll_or_event
54
+ metric = Metric.new(stub(:name => 'foo', :interval => 10), nil)
55
+
56
+ assert_abort do
57
+ metric.condition(:fake_condition) { |c| }
58
+ end
59
+ end
60
+
61
+ def test_condition_should_abort_on_invalid_condition
62
+ assert_abort do
63
+ @metric.condition(:fake_poll_condition) { |c| c.stubs(:valid?).returns(false) }
64
+ end
65
+ end
66
+
67
+ def test_condition_should_abort_on_no_such_condition
68
+ assert_abort do
69
+ @metric.condition(:invalid) { }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,246 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ module God
4
+ class Process
5
+ # def fork
6
+ # raise "You forgot to stub fork"
7
+ # end
8
+
9
+ def exec(*args)
10
+ raise "You forgot to stub exec"
11
+ end
12
+ end
13
+ end
14
+
15
+ class TestProcessChild < Test::Unit::TestCase
16
+ def setup
17
+ God.internal_init
18
+ @p = God::Process.new
19
+ @p.name = 'foo'
20
+ @p.stubs(:test).returns true # so we don't try to mkdir_p
21
+ Process.stubs(:detach) # because we stub fork
22
+
23
+ ::Process::Sys.stubs(:setuid).returns(true)
24
+ ::Process::Sys.stubs(:setgid).returns(true)
25
+ end
26
+
27
+ # valid?
28
+
29
+ def test_valid_should_return_true_if_auto_daemonized_and_log
30
+ @p.start = 'qux'
31
+ @p.log = 'bar'
32
+
33
+ assert @p.valid?
34
+ end
35
+
36
+ def test_valid_should_return_true_if_auto_daemonized_and_no_stop
37
+ @p.start = 'qux'
38
+ @p.log = 'bar'
39
+
40
+ assert @p.valid?
41
+ end
42
+
43
+ def test_valid_should_return_true_if_uid_exists
44
+ @p.start = 'qux'
45
+ @p.log = '/tmp/foo.log'
46
+ @p.uid = 'root'
47
+
48
+ assert @p.valid?
49
+ end
50
+
51
+ def test_valid_should_return_true_if_uid_does_not_exists
52
+ @p.start = 'qux'
53
+ @p.log = '/tmp/foo.log'
54
+ @p.uid = 'foobarbaz'
55
+
56
+ no_stdout do
57
+ no_stderr do
58
+ assert !@p.valid?
59
+ end
60
+ end
61
+ end
62
+
63
+ def test_valid_should_return_true_if_gid_exists
64
+ @p.start = 'qux'
65
+ @p.log = '/tmp/foo.log'
66
+ @p.gid = 'wheel'
67
+
68
+ assert @p.valid?
69
+ end
70
+
71
+ def test_valid_should_return_false_if_gid_does_not_exists
72
+ @p.start = 'qux'
73
+ @p.log = '/tmp/foo.log'
74
+ @p.gid = 'foobarbaz'
75
+
76
+ no_stdout do
77
+ no_stderr do
78
+ assert !@p.valid?
79
+ end
80
+ end
81
+ end
82
+
83
+ def test_valid_should_return_false_with_bogus_chroot
84
+ @p.chroot = '/bogusroot'
85
+
86
+ no_stdout do
87
+ no_stderr do
88
+ assert !@p.valid?
89
+ end
90
+ end
91
+ end
92
+
93
+ def test_valid_should_return_true_with_chroot_and_valid_log
94
+ @p.start = 'qux'
95
+ @p.chroot = '/tmp'
96
+ @p.log = '/tmp/foo.log'
97
+
98
+ File.expects(:exist?).with('/tmp').returns(true)
99
+ File.expects(:exist?).with('/tmp/foo.log').returns(true)
100
+ File.expects(:exist?).with('/tmp/dev/null').returns(true)
101
+
102
+ assert @p.valid?
103
+ end
104
+
105
+ # call_action
106
+
107
+ def test_call_action_should_write_pid
108
+ # Only for start, restart
109
+ [:start, :restart].each do |action|
110
+ @p.stubs(:test).returns true
111
+ IO.expects(:pipe).returns([StringIO.new('1234'), StringIO.new])
112
+ @p.expects(:fork)
113
+ Process.expects(:waitpid)
114
+ File.expects(:open).with(@p.default_pid_file, 'w')
115
+ @p.send("#{action}=", "run")
116
+ @p.call_action(action)
117
+ end
118
+ end
119
+ end
120
+
121
+ ###############################################################################
122
+ #
123
+ # Daemon
124
+ #
125
+ ###############################################################################
126
+
127
+ class TestProcessDaemon < Test::Unit::TestCase
128
+ def setup
129
+ God.internal_init
130
+ @p = God::Process.new
131
+ @p.name = 'foo'
132
+ @p.pid_file = 'blah.pid'
133
+ @p.stubs(:test).returns true # so we don't try to mkdir_p
134
+ Process.stubs(:detach) # because we stub fork
135
+ end
136
+
137
+ # alive?
138
+
139
+ def test_alive_should_call_system_process_exists
140
+ File.expects(:read).with('blah.pid').times(2).returns('1234')
141
+ System::Process.any_instance.expects(:exists?).returns(false)
142
+ assert !@p.alive?
143
+ end
144
+
145
+ def test_alive_should_return_false_if_no_such_file
146
+ File.expects(:read).with('blah.pid').raises(Errno::ENOENT)
147
+ assert !@p.alive?
148
+ end
149
+
150
+ # valid?
151
+
152
+ def test_valid_should_return_false_if_no_start
153
+ @p.name = 'foo'
154
+ @p.stop = 'baz'
155
+ no_stdout do
156
+ assert !@p.valid?
157
+ end
158
+ end
159
+
160
+ def test_valid_should_return_false_if_self_daemonized_and_no_stop
161
+ @p.start = 'bar'
162
+ @p.pid_file = 'foo'
163
+
164
+ no_stdout do
165
+ assert !@p.valid?
166
+ end
167
+ end
168
+
169
+ # pid
170
+
171
+ def test_pid_should_return_integer_for_valid_pid_files
172
+ File.stubs(:read).returns("123")
173
+ assert_equal 123, @p.pid
174
+ end
175
+
176
+ def test_pid_should_return_nil_for_missing_files
177
+ @p.pid_file = ''
178
+ assert_equal nil, @p.pid
179
+ end
180
+
181
+ def test_pid_should_return_nil_for_invalid_pid_files
182
+ File.stubs(:read).returns("four score and seven years ago")
183
+ assert_equal nil, @p.pid
184
+ end
185
+
186
+ def test_pid_should_retain_last_pid_value_if_pid_file_is_removed
187
+ File.stubs(:read).returns("123")
188
+ assert_equal 123, @p.pid
189
+
190
+ File.stubs(:read).raises(Errno::ENOENT)
191
+ assert_equal 123, @p.pid
192
+
193
+ File.stubs(:read).returns("246")
194
+ assert_equal 246, @p.pid
195
+ end
196
+
197
+ # default_pid_file
198
+
199
+ def test_default_pid_file
200
+ assert_equal File.join(God.pid_file_directory, 'foo.pid'), @p.default_pid_file
201
+ end
202
+
203
+ # unix socket
204
+
205
+ def test_unix_socket_should_return_path_specified
206
+ @p.unix_socket = '/path/to-socket'
207
+ assert_equal '/path/to-socket', @p.unix_socket
208
+ end
209
+
210
+ # call_action
211
+ # These actually excercise call_action in the back at this point - Kev
212
+
213
+ def test_call_action_with_string_should_call_system
214
+ @p.start = "do something"
215
+ @p.expects(:fork)
216
+ Process.expects(:waitpid2).returns([123, 0])
217
+ @p.call_action(:start)
218
+ end
219
+
220
+ def test_call_action_with_lambda_should_call
221
+ cmd = lambda { puts "Hi" }
222
+ cmd.expects(:call)
223
+ @p.start = cmd
224
+ @p.call_action(:start)
225
+ end
226
+
227
+ def test_call_action_with_invalid_command_class_should_raise
228
+ @p.start = 5
229
+ @p.stop = 'baz'
230
+
231
+ assert @p.valid?
232
+
233
+ assert_raise NotImplementedError do
234
+ @p.call_action(:start)
235
+ end
236
+ end
237
+
238
+ # start!/stop!/restart!
239
+
240
+ def test_start_stop_restart_bang
241
+ [:start, :stop, :restart].each do |x|
242
+ @p.expects(:call_action).with(x)
243
+ @p.send("#{x}!")
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestRegistry < Test::Unit::TestCase
4
+ def setup
5
+ God.registry.reset
6
+ end
7
+
8
+ def test_add
9
+ foo = God::Process.new
10
+ foo.name = 'foo'
11
+ God.registry.add(foo)
12
+ assert_equal 1, God.registry.size
13
+ assert_equal foo, God.registry['foo']
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSocket < 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
+ no_stdout do
13
+ God::Socket.new
14
+ end
15
+ end
16
+
17
+ def test_should_use_supplied_port_and_host
18
+ DRb.expects(:start_service).with { |uri, object| uri == "drbunix:///tmp/god.9999.sock" && object.is_a?(God::Socket) }
19
+ no_stdout do
20
+ server = God::Socket.new(9999)
21
+ end
22
+ end
23
+
24
+ def test_should_forward_foreign_method_calls_to_god
25
+ server = nil
26
+ no_stdout do
27
+ server = God::Socket.new
28
+ end
29
+ God.expects(:send).with(:something_random)
30
+ server.something_random
31
+ end
32
+
33
+ # ping
34
+
35
+ def test_ping_should_return_true
36
+ server = nil
37
+ no_stdout do
38
+ server = God::Socket.new
39
+ end
40
+ assert server.ping
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSugar < Test::Unit::TestCase
4
+ def test_seconds
5
+ assert_equal 1, 1.seconds
6
+ assert_equal 1, 1.second
7
+ end
8
+
9
+ def test_minutes
10
+ assert_equal 60, 1.minutes
11
+ assert_equal 60, 1.minute
12
+ end
13
+
14
+ def test_hours
15
+ assert_equal 3600, 1.hours
16
+ assert_equal 3600, 1.hour
17
+ end
18
+
19
+ def test_days
20
+ assert_equal 86400, 1.days
21
+ assert_equal 86400, 1.day
22
+ end
23
+
24
+ def test_kilobytes
25
+ assert_equal 1, 1.kilobytes
26
+ assert_equal 1, 1.kilobyte
27
+ end
28
+
29
+ def test_megabytes
30
+ assert_equal 1024, 1.megabytes
31
+ assert_equal 1024, 1.megabyte
32
+ end
33
+
34
+ def test_gigabytes
35
+ assert_equal 1024 ** 2, 1.gigabytes
36
+ assert_equal 1024 ** 2, 1.gigabyte
37
+ end
38
+
39
+ def test_percent
40
+ assert_equal 1, 1.percent
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSystemPortablePoller < Test::Unit::TestCase
4
+ def setup
5
+ pid = Process.pid
6
+ @process = System::PortablePoller.new(pid)
7
+ end
8
+
9
+ def test_time_string_to_seconds
10
+ assert_equal 0, @process.bypass.time_string_to_seconds('0:00:00')
11
+ assert_equal 0, @process.bypass.time_string_to_seconds('0:00:55')
12
+ assert_equal 27, @process.bypass.time_string_to_seconds('0:27:32')
13
+ assert_equal 75, @process.bypass.time_string_to_seconds('1:15:13')
14
+ assert_equal 735, @process.bypass.time_string_to_seconds('12:15:13')
15
+ end
16
+ end
17
+
@@ -0,0 +1,30 @@
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(9999999).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
+ end
30
+