mcproc 2016.2.20

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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/Announce.txt +135 -0
  3. data/Gemfile +9 -0
  4. data/History.txt +469 -0
  5. data/LICENSE +22 -0
  6. data/README.md +37 -0
  7. data/Rakefile +185 -0
  8. data/TODO.md +37 -0
  9. data/bin/mcproc +134 -0
  10. data/doc/intro.asciidoc +20 -0
  11. data/doc/mcproc.asciidoc +1592 -0
  12. data/ext/god/.gitignore +5 -0
  13. data/ext/god/extconf.rb +56 -0
  14. data/ext/god/kqueue_handler.c +133 -0
  15. data/ext/god/netlink_handler.c +182 -0
  16. data/lib/god.rb +780 -0
  17. data/lib/god/behavior.rb +52 -0
  18. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  19. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  20. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  21. data/lib/god/cli/command.rb +268 -0
  22. data/lib/god/cli/run.rb +170 -0
  23. data/lib/god/cli/version.rb +23 -0
  24. data/lib/god/compat19.rb +33 -0
  25. data/lib/god/condition.rb +96 -0
  26. data/lib/god/conditions/always.rb +36 -0
  27. data/lib/god/conditions/complex.rb +86 -0
  28. data/lib/god/conditions/cpu_usage.rb +80 -0
  29. data/lib/god/conditions/degrading_lambda.rb +52 -0
  30. data/lib/god/conditions/disk_usage.rb +32 -0
  31. data/lib/god/conditions/file_mtime.rb +28 -0
  32. data/lib/god/conditions/file_touched.rb +44 -0
  33. data/lib/god/conditions/flapping.rb +128 -0
  34. data/lib/god/conditions/http_response_code.rb +184 -0
  35. data/lib/god/conditions/lambda.rb +25 -0
  36. data/lib/god/conditions/memory_usage.rb +82 -0
  37. data/lib/god/conditions/process_exits.rb +66 -0
  38. data/lib/god/conditions/process_running.rb +63 -0
  39. data/lib/god/conditions/socket_responding.rb +142 -0
  40. data/lib/god/conditions/tries.rb +44 -0
  41. data/lib/god/configurable.rb +57 -0
  42. data/lib/god/contact.rb +114 -0
  43. data/lib/god/contacts/airbrake.rb +44 -0
  44. data/lib/god/contacts/campfire.rb +121 -0
  45. data/lib/god/contacts/email.rb +130 -0
  46. data/lib/god/contacts/hipchat.rb +117 -0
  47. data/lib/god/contacts/jabber.rb +75 -0
  48. data/lib/god/contacts/prowl.rb +57 -0
  49. data/lib/god/contacts/scout.rb +55 -0
  50. data/lib/god/contacts/sensu.rb +59 -0
  51. data/lib/god/contacts/slack.rb +98 -0
  52. data/lib/god/contacts/statsd.rb +46 -0
  53. data/lib/god/contacts/twitter.rb +51 -0
  54. data/lib/god/contacts/webhook.rb +74 -0
  55. data/lib/god/driver.rb +238 -0
  56. data/lib/god/errors.rb +24 -0
  57. data/lib/god/event_handler.rb +112 -0
  58. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  59. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  60. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  61. data/lib/god/logger.rb +109 -0
  62. data/lib/god/metric.rb +87 -0
  63. data/lib/god/process.rb +381 -0
  64. data/lib/god/registry.rb +32 -0
  65. data/lib/god/simple_logger.rb +59 -0
  66. data/lib/god/socket.rb +113 -0
  67. data/lib/god/sugar.rb +62 -0
  68. data/lib/god/sys_logger.rb +45 -0
  69. data/lib/god/system/portable_poller.rb +42 -0
  70. data/lib/god/system/process.rb +50 -0
  71. data/lib/god/system/slash_proc_poller.rb +92 -0
  72. data/lib/god/task.rb +552 -0
  73. data/lib/god/timeline.rb +25 -0
  74. data/lib/god/trigger.rb +43 -0
  75. data/lib/god/watch.rb +340 -0
  76. data/mcproc.gemspec +192 -0
  77. data/test/configs/child_events/child_events.god +44 -0
  78. data/test/configs/child_events/simple_server.rb +3 -0
  79. data/test/configs/child_polls/child_polls.god +37 -0
  80. data/test/configs/child_polls/simple_server.rb +12 -0
  81. data/test/configs/complex/complex.god +59 -0
  82. data/test/configs/complex/simple_server.rb +3 -0
  83. data/test/configs/contact/contact.god +118 -0
  84. data/test/configs/contact/simple_server.rb +3 -0
  85. data/test/configs/daemon_events/daemon_events.god +37 -0
  86. data/test/configs/daemon_events/simple_server.rb +8 -0
  87. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  88. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  89. data/test/configs/daemon_polls/simple_server.rb +6 -0
  90. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  91. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  92. data/test/configs/keepalive/keepalive.god +9 -0
  93. data/test/configs/keepalive/keepalive.rb +12 -0
  94. data/test/configs/lifecycle/lifecycle.god +25 -0
  95. data/test/configs/matias/matias.god +50 -0
  96. data/test/configs/real.rb +59 -0
  97. data/test/configs/running_load/running_load.god +16 -0
  98. data/test/configs/stop_options/simple_server.rb +12 -0
  99. data/test/configs/stop_options/stop_options.god +39 -0
  100. data/test/configs/stress/simple_server.rb +3 -0
  101. data/test/configs/stress/stress.god +15 -0
  102. data/test/configs/task/logs/.placeholder +0 -0
  103. data/test/configs/task/task.god +26 -0
  104. data/test/configs/test.rb +61 -0
  105. data/test/configs/usr1_trapper.rb +10 -0
  106. data/test/helper.rb +172 -0
  107. data/test/suite.rb +6 -0
  108. data/test/test_airbrake.rb +14 -0
  109. data/test/test_behavior.rb +18 -0
  110. data/test/test_campfire.rb +22 -0
  111. data/test/test_condition.rb +52 -0
  112. data/test/test_conditions_disk_usage.rb +50 -0
  113. data/test/test_conditions_http_response_code.rb +109 -0
  114. data/test/test_conditions_process_running.rb +40 -0
  115. data/test/test_conditions_socket_responding.rb +176 -0
  116. data/test/test_conditions_tries.rb +67 -0
  117. data/test/test_contact.rb +109 -0
  118. data/test/test_driver.rb +26 -0
  119. data/test/test_email.rb +34 -0
  120. data/test/test_event_handler.rb +82 -0
  121. data/test/test_god.rb +710 -0
  122. data/test/test_god_system.rb +201 -0
  123. data/test/test_handlers_kqueue_handler.rb +16 -0
  124. data/test/test_hipchat.rb +23 -0
  125. data/test/test_jabber.rb +29 -0
  126. data/test/test_logger.rb +55 -0
  127. data/test/test_metric.rb +74 -0
  128. data/test/test_process.rb +263 -0
  129. data/test/test_prowl.rb +15 -0
  130. data/test/test_registry.rb +15 -0
  131. data/test/test_sensu.rb +11 -0
  132. data/test/test_slack.rb +57 -0
  133. data/test/test_socket.rb +34 -0
  134. data/test/test_statsd.rb +22 -0
  135. data/test/test_sugar.rb +42 -0
  136. data/test/test_system_portable_poller.rb +17 -0
  137. data/test/test_system_process.rb +30 -0
  138. data/test/test_task.rb +246 -0
  139. data/test/test_timeline.rb +37 -0
  140. data/test/test_trigger.rb +63 -0
  141. data/test/test_watch.rb +286 -0
  142. data/test/test_webhook.rb +22 -0
  143. metadata +475 -0
@@ -0,0 +1,201 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestGodSystem < MiniTest::Test
4
+ def assert_watch_running(watch_name)
5
+ assert_equal true, God.watches[watch_name].alive?
6
+ end
7
+
8
+ def with_god_cleanup
9
+ old_terminate = God.method(:terminate)
10
+ # necessary cuz actual god terminate will do exit(0) will stops tests
11
+ God.class_eval do
12
+ def self.terminate
13
+ FileUtils.rm_f(self.pid) if self.pid
14
+ self.server.stop if self.server
15
+ end
16
+ end
17
+ begin
18
+ yield
19
+ ensure
20
+ God.stop_all
21
+ God.terminate # use our monkeypatched terminate
22
+ God.watches.each do |name, w|
23
+ w.stop_signal = 'KILL'
24
+ w.action(:stop)
25
+ end
26
+ God.inited = false
27
+ God.terminate_timeout = ::God::TERMINATE_TIMEOUT_DEFAULT
28
+ God.internal_init # reset config, set running to false, etc.
29
+ # set termiante back to old method, for other tests
30
+ God.define_singleton_method(:terminate, old_terminate)
31
+ end
32
+ end
33
+
34
+ def test_start_running
35
+ with_god_cleanup do
36
+ God.start
37
+ assert_equal(God.running, true)
38
+ end
39
+ end
40
+
41
+ def test_add_watch
42
+ with_god_cleanup do
43
+ God.start
44
+ God.watch do |w|
45
+ w.name = 'add_watch'
46
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
47
+ end
48
+ assert God.watches['add_watch'] != nil
49
+ end
50
+ end
51
+
52
+ def test_start_watch
53
+ with_god_cleanup do
54
+ God.start
55
+ God.watch do |w|
56
+ w.name = 'start_watch'
57
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
58
+ end
59
+ God.watches['start_watch'].action(:start)
60
+ sleep 2
61
+ assert_equal true, God.watches['start_watch'].alive?
62
+ end
63
+ end
64
+
65
+ def test_start_watch
66
+ with_god_cleanup do
67
+ God.start
68
+ God.watch do |w|
69
+ w.name = 'start_watch'
70
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
71
+ end
72
+ God.watches['start_watch'].action(:start)
73
+ sleep 2
74
+ assert_equal true, God.watches['start_watch'].alive?
75
+ end
76
+ end
77
+
78
+ def test_stop_all_with_one
79
+ with_god_cleanup do
80
+ God.start
81
+ God.watch do |w|
82
+ w.name = 'start_watch'
83
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
84
+ end
85
+ God.watches['start_watch'].action(:start)
86
+ sleep 2
87
+ assert_equal true, God.watches['start_watch'].alive?
88
+ God.stop_all
89
+ assert_equal false, God.watches.any? { |name, w| w.alive? }
90
+ end
91
+ end
92
+
93
+ # default 10s timeout will expire before SIGKILL sent
94
+ def test_stop_all_with_non_killing_signal_long_timeout
95
+ with_god_cleanup do
96
+ God.start
97
+ God.watch do |w|
98
+ w.name = 'long_timeout'
99
+ w.stop_signal = 'USR1'
100
+ w.stop_timeout = ::God::STOP_TIMEOUT_DEFAULT + 1
101
+ w.start = File.join(GOD_ROOT, *%w[test configs usr1_trapper.rb])
102
+ end
103
+ God.watches['long_timeout'].action(:start)
104
+ sleep 2
105
+ assert_equal true, God.watches['long_timeout'].alive?
106
+ God.stop_all
107
+ assert_watch_running('long_timeout')
108
+ end
109
+ end
110
+
111
+ # use short timeout to send SIGKILL before 10s timeout
112
+ def test_stop_all_with_non_killing_signal_short_timeout
113
+ with_god_cleanup do
114
+ God.start
115
+ God.watch do |w|
116
+ w.name = 'short_timeout'
117
+ w.stop_signal = 'USR1'
118
+ w.stop_timeout = ::God::STOP_TIMEOUT_DEFAULT - 1
119
+ w.start = File.join(GOD_ROOT, *%w[test configs usr1_trapper.rb])
120
+ end
121
+ God.watches['short_timeout'].action(:start)
122
+ sleep 2
123
+ assert_equal true, God.watches['short_timeout'].alive?
124
+ God.stop_all
125
+ assert_equal false, God.watches.any? { |name, w| w.alive? }
126
+ end
127
+ end
128
+
129
+ # should be able to stop many simple watches within default timeout
130
+ def test_stop_all_with_many_watches
131
+ with_god_cleanup do
132
+ God.start
133
+ 20.times do |i|
134
+ God.watch do |w|
135
+ w.name = "many_watches_#{i}"
136
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
137
+ end
138
+ God.watches["many_watches_#{i}"].action(:start)
139
+ end
140
+ while true do
141
+ all_running = God.watches.select{ |name, w| name =~ /many_watches_/ }.all?{ |name, w| w.alive? }
142
+ size = God.watches.size
143
+ break if all_running && size >= 20
144
+ sleep 2
145
+ end
146
+ God.stop_all
147
+ assert_equal false, God.watches.any? { |name, w| w.alive? }
148
+ end
149
+ end
150
+
151
+ # should be able to stop many simple watches within short timeout
152
+ def test_stop_all_with_many_watches_short_timeout
153
+ with_god_cleanup do
154
+ God.start
155
+ God.terminate_timeout = 1
156
+ 100.times do |i|
157
+ God.watch do |w|
158
+ w.name = "tons_of_watches_#{i}"
159
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
160
+ w.keepalive
161
+ end
162
+ God.watches["tons_of_watches_#{i}"].action(:start)
163
+ end
164
+ while true do
165
+ all_running = God.watches.select{ |name, w| name =~ /tons_of_watches_/ }.all?{ |name, w| w.alive? }
166
+ size = God.watches.size
167
+ break if all_running && size >= 100
168
+ sleep 2
169
+ end
170
+ God.stop_all
171
+ assert_equal false, God.watches.any? { |name, w| w.alive? }
172
+ end
173
+ end
174
+
175
+ def test_god_terminate_with_many_watches_short_timeout
176
+ with_god_cleanup do
177
+ God.start
178
+ God.terminate_timeout = 1
179
+ 100.times do |i|
180
+ God.watch do |w|
181
+ w.name = "tons_of_watches_#{i}"
182
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
183
+ w.keepalive
184
+ end
185
+ God.watches["tons_of_watches_#{i}"].action(:start)
186
+ end
187
+ while true do
188
+ all_running = God.watches.select{ |name, w| name =~ /tons_of_watches_/ }.all?{ |name, w| w.alive? }
189
+ size = God.watches.size
190
+ break if all_running && size >= 100
191
+ sleep 2
192
+ end
193
+ begin
194
+ God::CLI::Command.new('terminate', {port: 17165}, [])
195
+ rescue SystemExit
196
+ ensure
197
+ assert_equal false, God.watches.any? { |name, w| w.alive? }
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ if God::EventHandler.event_system == "kqueue"
4
+
5
+ class TestHandlersKqueueHandler < Minitest::Test
6
+ def test_register_process
7
+ KQueueHandler.expects(:monitor_process).with(1234, 2147483648)
8
+ KQueueHandler.register_process(1234, [:proc_exit])
9
+ end
10
+
11
+ def test_events_mask
12
+ assert_equal 2147483648, KQueueHandler.events_mask([:proc_exit])
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestHipchat < Minitest::Test
4
+ def setup
5
+ @hipchat = God::Contacts::Hipchat.new
6
+ end
7
+
8
+ def test_exists
9
+ God::Contacts::Hipchat
10
+ end
11
+
12
+ def test_notify
13
+ @hipchat.token = 'ee64d6e2337310af'
14
+ @hipchat.ssl = 'true'
15
+ @hipchat.room = 'testroom'
16
+ @hipchat.from = 'test'
17
+
18
+ time = Time.now
19
+ body = "[#{time.strftime('%H:%M:%S')}] host - msg"
20
+ Marshmallow::Connection.any_instance.expects(:speak).with('testroom', body)
21
+ @hipchat.notify('msg', time, 'prio', 'cat', 'host')
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/helper'
3
+
4
+ class TestJabber < Minitest::Test
5
+
6
+ def setup
7
+ @jabber = God::Contacts::Jabber.new
8
+ end
9
+
10
+ def test_notify
11
+ @jabber.host = 'talk.google.com'
12
+ @jabber.from_jid = 'god@jabber.org'
13
+ @jabber.password = 'secret'
14
+ @jabber.to_jid = 'dev@jabber.org'
15
+
16
+ time = Time.now
17
+ body = God::Contacts::Jabber.format.call('msg', time, 'prio', 'cat', 'host')
18
+
19
+ assert_equal "Message: msg\nHost: host\nPriority: prio\nCategory: cat\n", body
20
+
21
+ Jabber::Client.any_instance.expects(:connect).with('talk.google.com', 5222)
22
+ Jabber::Client.any_instance.expects(:auth).with('secret')
23
+ Jabber::Client.any_instance.expects(:send)
24
+ Jabber::Client.any_instance.expects(:close)
25
+
26
+ @jabber.notify('msg', Time.now, 'prio', 'cat', 'host')
27
+ assert_equal "sent jabber message to dev@jabber.org", @jabber.info
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestLogger < Minitest::Test
4
+ def setup
5
+ @log = God::Logger.new(StringIO.new('/dev/null'))
6
+ end
7
+
8
+ # log
9
+
10
+ def test_log_should_keep_logs_when_wanted
11
+ @log.watch_log_since('foo', Time.now)
12
+ @log.expects(:info).with("qux")
13
+
14
+ @log.log(stub(:name => 'foo'), :info, "qux")
15
+
16
+ assert_equal 1, @log.logs.size
17
+ assert_instance_of Time, @log.logs['foo'][0][0]
18
+ assert_match(/qux/, @log.logs['foo'][0][1])
19
+ end
20
+
21
+ def test_log_should_send_to_syslog
22
+ SysLogger.expects(:log).with(:fatal, 'foo')
23
+ @log.log(stub(:name => 'foo'), :fatal, "foo")
24
+ end
25
+
26
+ # watch_log_since
27
+
28
+ def test_watch_log_since
29
+ t1 = Time.now
30
+
31
+ @log.watch_log_since('foo', t1)
32
+
33
+ @log.log(stub(:name => 'foo'), :info, "one")
34
+ @log.log(stub(:name => 'foo'), :info, "two")
35
+
36
+ assert_match(/one.*two/m, @log.watch_log_since('foo', t1))
37
+
38
+ t2 = Time.now
39
+
40
+ @log.log(stub(:name => 'foo'), :info, "three")
41
+
42
+ out = @log.watch_log_since('foo', t2)
43
+
44
+ assert(/one/ !~ out)
45
+ assert(/two/ !~ out)
46
+ assert_match(/three/, out)
47
+ end
48
+
49
+ # regular methods
50
+
51
+ def test_fatal
52
+ @log.fatal('foo')
53
+ assert_equal 0, @log.logs.size
54
+ end
55
+ end
@@ -0,0 +1,74 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestMetric < Minitest::Test
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
+ # This doesn't currently work:
45
+ #
46
+ # def test_condition_should_allow_generation_of_subclasses_of_poll_or_event
47
+ # metric = Metric.new(stub(:name => 'foo', :interval => 10), nil)
48
+ #
49
+ # assert_nothing_raised do
50
+ # metric.condition(:fake_poll_condition)
51
+ # metric.condition(:fake_event_condition)
52
+ # end
53
+ # end
54
+
55
+ def test_condition_should_abort_if_not_subclass_of_poll_or_event
56
+ metric = Metric.new(stub(:name => 'foo', :interval => 10), nil)
57
+
58
+ assert_abort do
59
+ metric.condition(:fake_condition) { |c| }
60
+ end
61
+ end
62
+
63
+ def test_condition_should_abort_on_invalid_condition
64
+ assert_abort do
65
+ @metric.condition(:fake_poll_condition) { |c| c.stubs(:valid?).returns(false) }
66
+ end
67
+ end
68
+
69
+ def test_condition_should_abort_on_no_such_condition
70
+ assert_abort do
71
+ @metric.condition(:invalid) { }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,263 @@
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 < Minitest::Test
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
+ ::Process.stubs(:groups=)
49
+ ::Process.stubs(:initgroups)
50
+
51
+ assert @p.valid?
52
+ end
53
+
54
+ def test_valid_should_return_true_if_uid_does_not_exists
55
+ @p.start = 'qux'
56
+ @p.log = '/tmp/foo.log'
57
+ @p.uid = 'foobarbaz'
58
+
59
+ assert !@p.valid?
60
+ end
61
+
62
+ def test_valid_should_return_true_if_gid_exists
63
+ @p.start = 'qux'
64
+ @p.log = '/tmp/foo.log'
65
+ @p.gid = Etc.getgrgid(::Process.gid).name
66
+
67
+ ::Process.stubs(:groups=)
68
+
69
+ assert @p.valid?
70
+ end
71
+
72
+ def test_valid_should_return_false_if_gid_does_not_exists
73
+ @p.start = 'qux'
74
+ @p.log = '/tmp/foo.log'
75
+ @p.gid = 'foobarbaz'
76
+
77
+ assert !@p.valid?
78
+ end
79
+
80
+ def test_valid_should_return_true_if_dir_exists
81
+ @p.start = 'qux'
82
+ @p.log = '/tmp/foo.log'
83
+ @p.dir = '/tmp'
84
+
85
+ assert @p.valid?
86
+ end
87
+
88
+ def test_valid_should_return_false_if_dir_does_not_exists
89
+ @p.start = 'qux'
90
+ @p.log = '/tmp/foo.log'
91
+ @p.dir = '/tmp/doesnotexist'
92
+
93
+ assert !@p.valid?
94
+ end
95
+
96
+ def test_valid_should_return_false_if_dir_is_not_a_dir
97
+ @p.start = 'qux'
98
+ @p.log = '/tmp/foo.log'
99
+ @p.dir = '/etc/passwd'
100
+
101
+ assert !@p.valid?
102
+ end
103
+
104
+ def test_valid_should_return_false_with_bogus_chroot
105
+ @p.chroot = '/bogusroot'
106
+
107
+ assert !@p.valid?
108
+ end
109
+
110
+ def test_valid_should_return_true_with_chroot_and_valid_log
111
+ @p.start = 'qux'
112
+ @p.chroot = Dir.pwd
113
+ @p.log = "#{@p.chroot}/foo.log"
114
+
115
+ File.expects(:exist?).with(@p.chroot).returns(true)
116
+ File.expects(:exist?).with(@p.log).returns(true)
117
+ File.expects(:exist?).with("#{@p.chroot}/dev/null").returns(true)
118
+
119
+ File.stubs(:writable?).with('/foo.log').returns(true)
120
+
121
+ ::Dir.stubs(:chroot)
122
+
123
+ assert @p.valid?
124
+ end
125
+
126
+ # call_action
127
+
128
+ def test_call_action_should_write_pid
129
+ # Only for start, restart
130
+ [:start, :restart].each do |action|
131
+ @p.stubs(:test).returns true
132
+ IO.expects(:pipe).returns([StringIO.new('1234'), StringIO.new])
133
+ @p.expects(:fork)
134
+ Process.expects(:waitpid)
135
+ File.expects(:open).with(@p.default_pid_file, 'w')
136
+ @p.send("#{action}=", "run")
137
+ @p.call_action(action)
138
+ end
139
+ end
140
+ end
141
+
142
+ ###############################################################################
143
+ #
144
+ # Daemon
145
+ #
146
+ ###############################################################################
147
+
148
+ class TestProcessDaemon < Minitest::Test
149
+ def setup
150
+ God.internal_init
151
+ @p = God::Process.new
152
+ @p.name = 'foo'
153
+ @p.pid_file = 'blah.pid'
154
+ @p.stubs(:test).returns true # so we don't try to mkdir_p
155
+ God::System::Process.stubs(:fetch_system_poller).returns(God::System::PortablePoller)
156
+ Process.stubs(:detach) # because we stub fork
157
+ end
158
+
159
+ # alive?
160
+
161
+ def test_alive_should_call_system_process_exists
162
+ File.expects(:read).with('blah.pid').times(2).returns('1234')
163
+ System::Process.any_instance.expects(:exists?).returns(false)
164
+ assert !@p.alive?
165
+ end
166
+
167
+ def test_alive_should_return_false_if_no_such_file
168
+ File.expects(:read).with('blah.pid').raises(Errno::ENOENT)
169
+ assert !@p.alive?
170
+ end
171
+
172
+ # valid?
173
+
174
+ def test_valid_should_return_false_if_no_start
175
+ @p.name = 'foo'
176
+ @p.stop = 'baz'
177
+ assert !@p.valid?
178
+ end
179
+
180
+ # pid
181
+
182
+ def test_pid_should_return_integer_for_valid_pid_files
183
+ File.stubs(:read).returns("123")
184
+ assert_equal 123, @p.pid
185
+ end
186
+
187
+ def test_pid_should_return_nil_for_missing_files
188
+ @p.pid_file = ''
189
+ assert_equal nil, @p.pid
190
+ end
191
+
192
+ def test_pid_should_return_nil_for_invalid_pid_files
193
+ File.stubs(:read).returns("four score and seven years ago")
194
+ assert_equal nil, @p.pid
195
+ end
196
+
197
+ def test_pid_should_retain_last_pid_value_if_pid_file_is_removed
198
+ File.stubs(:read).returns("123")
199
+ assert_equal 123, @p.pid
200
+
201
+ File.stubs(:read).raises(Errno::ENOENT)
202
+ assert_equal 123, @p.pid
203
+
204
+ File.stubs(:read).returns("246")
205
+ assert_equal 246, @p.pid
206
+ end
207
+
208
+ # default_pid_file
209
+
210
+ def test_default_pid_file
211
+ assert_equal File.join(God.pid_file_directory, 'foo.pid'), @p.default_pid_file
212
+ end
213
+
214
+ # unix socket
215
+
216
+ def test_unix_socket_should_return_path_specified
217
+ @p.unix_socket = '/path/to-socket'
218
+ assert_equal '/path/to-socket', @p.unix_socket
219
+ end
220
+
221
+ # umask
222
+ def test_umask_should_return_umask_specified
223
+ @p.umask = 002
224
+ assert_equal 002, @p.umask
225
+ end
226
+
227
+ # call_action
228
+ # These actually excercise call_action in the back at this point - Kev
229
+
230
+ def test_call_action_with_string_should_call_system
231
+ @p.start = "do something"
232
+ @p.expects(:fork)
233
+ Process.expects(:waitpid2).returns([123, 0])
234
+ @p.call_action(:start)
235
+ end
236
+
237
+ def test_call_action_with_lambda_should_call
238
+ cmd = lambda { puts "Hi" }
239
+ cmd.expects(:call)
240
+ @p.start = cmd
241
+ @p.call_action(:start)
242
+ end
243
+
244
+ def test_call_action_with_invalid_command_class_should_raise
245
+ @p.start = 5
246
+ @p.stop = 'baz'
247
+
248
+ assert @p.valid?
249
+
250
+ assert_raises NotImplementedError do
251
+ @p.call_action(:start)
252
+ end
253
+ end
254
+
255
+ # start!/stop!/restart!
256
+
257
+ def test_start_stop_restart_bang
258
+ [:start, :stop, :restart].each do |x|
259
+ @p.expects(:call_action).with(x)
260
+ @p.send("#{x}!")
261
+ end
262
+ end
263
+ end