mcproc 2016.2.20

Sign up to get free protection for your applications and to get access to all the features.
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,15 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/helper'
3
+
4
+ class TestProwl < Minitest::Test
5
+ def test_live_notify
6
+ prowl = God::Contacts::Prowl.new
7
+ prowl.name = "Prowly"
8
+ prowl.apikey = 'put_your_apikey_here'
9
+
10
+ Prowly.expects(:notify).returns(mock(:succeeded? => true))
11
+
12
+ prowl.notify("Test", Time.now, "Test", "Test", "")
13
+ assert_equal "sent prowl notification to #{prowl.name}", prowl.info
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestRegistry < Minitest::Test
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,11 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSensu < Minitest::Test
4
+ def test_sensu_notify
5
+ sensu = God::Contacts::Sensu.new
6
+ sensu.check_name = "TestSensuContact"
7
+
8
+ UDPSocket.any_instance.expects(:send)
9
+ sensu.notify("Test", Time.now, "Test", "Test", "")
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSlack < Minitest::Test
4
+ def setup
5
+ @slack = God::Contacts::Slack.new
6
+
7
+ # new slack webhook url contains three random 'tokens' with length of 9, 9 and 24 characters
8
+ @slack.url = "https://hooks.slack.com/services/ABCABCABC/DEFDEFDEF/ABCDEFABCDEFABCDEFABCDEF"
9
+
10
+ @sample_data = {
11
+ :message => "a sample message",
12
+ :time => "2038-01-01 00:00:00",
13
+ :priority => "High",
14
+ :category => "Test",
15
+ :host => "example.com"
16
+ }
17
+ end
18
+
19
+ def test_api_url
20
+ assert_equal @slack.url, @slack.api_url.to_s
21
+ end
22
+
23
+ def test_notify
24
+ Net::HTTP.any_instance.expects(:request).returns(Net::HTTPSuccess.new('a', 'b', 'c'))
25
+
26
+ @slack.channel = "#ops"
27
+
28
+ @slack.notify('msg', Time.now, 'prio', 'cat', 'host')
29
+ assert_equal "successfully notified slack on channel #ops", @slack.info
30
+ end
31
+
32
+ def test_default_channel
33
+ Net::HTTP.any_instance.expects(:request).returns(Net::HTTPSuccess.new('a', 'b', 'c'))
34
+
35
+ @slack.notify('msg', Time.now, 'prio', 'cat', 'host')
36
+ assert_equal "successfully notified slack on channel #general", @slack.info
37
+ end
38
+
39
+ def test_default_formatting
40
+ text = @slack.text(@sample_data)
41
+ assert_equal "High alert on example.com: a sample message (Test, 2038-01-01 00:00:00)", text
42
+ end
43
+
44
+ def test_custom_formatting
45
+ @slack.format = "%{host}: %{message}"
46
+ text = @slack.text(@sample_data)
47
+ assert_equal "example.com: a sample message", text
48
+ end
49
+
50
+ def test_notify_channel
51
+ @slack.notify_channel = true
52
+ @slack.format = ""
53
+ text = @slack.text(@sample_data)
54
+ assert_equal "<!channel> ", text
55
+ end
56
+ end
57
+
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSocket < Minitest::Test
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
+ God::Socket.new
13
+ end
14
+
15
+ def test_should_use_supplied_port_and_host
16
+ DRb.expects(:start_service).with { |uri, object| uri == "drbunix:///tmp/god.9999.sock" && object.is_a?(God::Socket) }
17
+ server = God::Socket.new(9999)
18
+ end
19
+
20
+ def test_should_forward_foreign_method_calls_to_god
21
+ server = nil
22
+ server = God::Socket.new
23
+ God.expects(:send).with(:something_random)
24
+ server.something_random
25
+ end
26
+
27
+ # ping
28
+
29
+ def test_ping_should_return_true
30
+ server = nil
31
+ server = God::Socket.new
32
+ assert server.ping
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestStatsd < Minitest::Test
4
+ def setup
5
+ @statsd = God::Contacts::Statsd.new
6
+ end
7
+
8
+ def test_exists
9
+ God::Contacts::Statsd
10
+ end
11
+
12
+ def test_notify
13
+ [
14
+ 'cpu out of bounds',
15
+ 'memory out of bounds',
16
+ 'process is flapping'
17
+ ].each do |event_type|
18
+ ::Statsd.any_instance.expects(:increment).with("god.#{event_type.gsub(/\s/, '_')}.127_0_0_1.myapp-thin-1234")
19
+ @statsd.notify("myapp-thin-1234 [trigger] #{event_type}", Time.now, 'some priority', 'and some category', '127.0.0.1')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSugar < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
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
+
data/test/test_task.rb ADDED
@@ -0,0 +1,246 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestTask < Minitest::Test
4
+ def setup
5
+ God.internal_init
6
+ @task = Task.new
7
+ @task.name = 'foo'
8
+ @task.valid_states = [:foo, :bar]
9
+ @task.initial_state = :foo
10
+ @task.interval = 5
11
+ @task.prepare
12
+ end
13
+
14
+ # valid?
15
+
16
+ def test_valid_should_return_false_if_no_name
17
+ @task.name = nil
18
+ assert !@task.valid?
19
+ end
20
+
21
+ def test_valid_should_return_false_if_no_valid_states
22
+ @task.valid_states = nil
23
+ assert !@task.valid?
24
+ end
25
+
26
+ def test_valid_should_return_false_if_no_initial_state
27
+ @task.initial_state = nil
28
+ assert !@task.valid?
29
+ end
30
+
31
+ # transition
32
+
33
+ def test_transition_should_be_always_if_no_block_was_given
34
+ @task.transition(:foo, :bar)
35
+
36
+ assert_equal 5, @task.metrics.size
37
+ assert_equal Conditions::Always, @task.metrics[:foo].first.conditions.first.class
38
+ end
39
+
40
+ # method_missing
41
+
42
+ def test_method_missing_should_create_accessor_for_states
43
+ assert_nothing_raised do
44
+ @task.foo = 'testing'
45
+ end
46
+ end
47
+
48
+ def test_method_missing_should_raise_for_non_states
49
+ assert_raises NoMethodError do
50
+ @task.baz = 5
51
+ end
52
+ end
53
+
54
+ def test_method_missing_should_raise_for_non_setters
55
+ assert_raises NoMethodError do
56
+ @task.baz
57
+ end
58
+ end
59
+
60
+ # action
61
+
62
+ def test_action_should_send_string_commands_to_system
63
+ @task.foo = 'foo'
64
+ @task.driver.stubs(:in_driver_context?).returns(true)
65
+ @task.expects(:system).with('foo')
66
+ @task.action(:foo, nil)
67
+ end
68
+
69
+ def test_action_should_call_lambda_commands
70
+ @task.foo = lambda { }
71
+ @task.driver.stubs(:in_driver_context?).returns(true)
72
+ @task.foo.expects(:call)
73
+ @task.action(:foo, nil)
74
+ end
75
+
76
+ def test_action_should_raise_not_implemented_on_non_string_or_lambda_action
77
+ @task.driver.stubs(:in_driver_context?).returns(true)
78
+ assert_raises NotImplementedError do
79
+ @task.foo = 7
80
+ @task.action(:foo, nil)
81
+ end
82
+ end
83
+
84
+ def test_action_from_outside_driver_should_send_message_to_driver
85
+ @task.foo = 'foo'
86
+ @task.driver.expects(:message).with(:action, [:foo, nil])
87
+ @task.action(:foo, nil)
88
+ end
89
+
90
+ # attach
91
+
92
+ def test_attach_should_schedule_for_poll_condition
93
+ c = Conditions::FakePollCondition.new
94
+ @task.driver.expects(:schedule).with(c, 0)
95
+ @task.attach(c)
96
+ end
97
+
98
+ def test_attach_should_regsiter_for_event_condition
99
+ c = Conditions::FakeEventCondition.new
100
+ c.expects(:register)
101
+ @task.attach(c)
102
+ end
103
+
104
+ # detach
105
+
106
+ def test_detach_should_reset_poll_condition
107
+ c = Conditions::FakePollCondition.new
108
+ c.expects(:reset)
109
+ c.expects(:deregister).never
110
+ @task.detach(c)
111
+ end
112
+
113
+ def test_detach_should_deregister_event_conditions
114
+ c = Conditions::FakeEventCondition.new
115
+ c.expects(:deregister).once
116
+ @task.detach(c)
117
+ end
118
+
119
+ # trigger
120
+
121
+ def test_trigger_should_send_message_to_driver
122
+ c = Conditions::FakePollCondition.new
123
+ @task.driver.expects(:message).with(:handle_event, [c])
124
+ @task.trigger(c)
125
+ end
126
+
127
+ # handle_poll
128
+
129
+ def test_handle_poll_no_change_should_reschedule
130
+ c = Conditions::FakePollCondition.new
131
+ c.watch = @task
132
+ c.interval = 10
133
+
134
+ m = Metric.new(@task, {true => :up})
135
+ @task.directory[c] = m
136
+
137
+ c.expects(:test).returns(false)
138
+ @task.driver.expects(:schedule)
139
+ @task.handle_poll(c)
140
+ end
141
+
142
+ def test_handle_poll_change_should_move
143
+ c = Conditions::FakePollCondition.new
144
+ c.watch = @task
145
+ c.interval = 10
146
+
147
+ m = Metric.new(@task, {true => :up})
148
+ @task.directory[c] = m
149
+
150
+ c.expects(:test).returns(true)
151
+ @task.expects(:move).with(:up)
152
+ @task.handle_poll(c)
153
+ end
154
+
155
+ def test_handle_poll_should_use_overridden_transition
156
+ c = Conditions::Tries.new
157
+ c.watch = @task
158
+ c.times = 1
159
+ c.transition = :start
160
+ c.prepare
161
+
162
+ m = Metric.new(@task, {true => :up})
163
+ @task.directory[c] = m
164
+
165
+ @task.expects(:move).with(:start)
166
+ @task.handle_poll(c)
167
+ end
168
+
169
+ def test_handle_poll_should_notify_if_triggering
170
+ c = Conditions::FakePollCondition.new
171
+ c.watch = @task
172
+ c.interval = 10
173
+ c.notify = 'tom'
174
+
175
+ m = Metric.new(@task, {true => :up})
176
+ @task.directory[c] = m
177
+
178
+ c.expects(:test).returns(true)
179
+ @task.expects(:notify)
180
+ @task.handle_poll(c)
181
+ end
182
+
183
+ def test_handle_poll_should_not_notify_if_not_triggering
184
+ c = Conditions::FakePollCondition.new
185
+ c.watch = @task
186
+ c.interval = 10
187
+ c.notify = 'tom'
188
+
189
+ m = Metric.new(@task, {true => :up})
190
+ @task.directory[c] = m
191
+
192
+ c.expects(:test).returns(false)
193
+ @task.expects(:notify).never
194
+ @task.handle_poll(c)
195
+ end
196
+
197
+ def test_handle_poll_unexpected_exception_should_reschedule
198
+ c = Conditions::FakePollCondition.new
199
+ c.watch = @task
200
+ c.interval = 10
201
+
202
+ m = Metric.new(@task, {true => :up})
203
+ @task.directory[c] = m
204
+
205
+ c.expects(:test).raises(StandardError)
206
+ @task.driver.expects(:schedule)
207
+
208
+ @task.handle_poll(c)
209
+ end
210
+
211
+ # handle_event
212
+
213
+ def test_handle_event_should_move
214
+ c = Conditions::FakeEventCondition.new
215
+ c.watch = @task
216
+
217
+ m = Metric.new(@task, {true => :up})
218
+ @task.directory[c] = m
219
+
220
+ @task.expects(:move).with(:up)
221
+ @task.handle_event(c)
222
+ end
223
+
224
+ def test_handle_event_should_notify_if_triggering
225
+ c = Conditions::FakeEventCondition.new
226
+ c.watch = @task
227
+ c.notify = 'tom'
228
+
229
+ m = Metric.new(@task, {true => :up})
230
+ @task.directory[c] = m
231
+
232
+ @task.expects(:notify)
233
+ @task.handle_event(c)
234
+ end
235
+
236
+ def test_handle_event_should_not_notify_if_no_notify_set
237
+ c = Conditions::FakeEventCondition.new
238
+ c.watch = @task
239
+
240
+ m = Metric.new(@task, {true => :up})
241
+ @task.directory[c] = m
242
+
243
+ @task.expects(:notify).never
244
+ @task.handle_event(c)
245
+ end
246
+ end