mcollective-client 2.2.4 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mcollective-client might be problematic. Click here for more details.

Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/lib/mcollective/application.rb +25 -34
  3. data/lib/mcollective/client.rb +91 -33
  4. data/lib/mcollective/config.rb +42 -43
  5. data/lib/mcollective/data/base.rb +1 -1
  6. data/lib/mcollective/data/result.rb +6 -2
  7. data/lib/mcollective/ddl/agentddl.rb +28 -1
  8. data/lib/mcollective/ddl/base.rb +8 -6
  9. data/lib/mcollective/log.rb +11 -3
  10. data/lib/mcollective/logger/file_logger.rb +4 -4
  11. data/lib/mcollective/matcher.rb +9 -1
  12. data/lib/mcollective/message.rb +14 -23
  13. data/lib/mcollective/optionparser.rb +9 -1
  14. data/lib/mcollective/pluginpackager.rb +24 -3
  15. data/lib/mcollective/pluginpackager/agent_definition.rb +12 -12
  16. data/lib/mcollective/pluginpackager/standard_definition.rb +12 -12
  17. data/lib/mcollective/rpc/agent.rb +15 -12
  18. data/lib/mcollective/rpc/client.rb +67 -31
  19. data/lib/mcollective/rpc/helpers.rb +7 -1
  20. data/lib/mcollective/rpc/reply.rb +3 -1
  21. data/lib/mcollective/shell.rb +45 -8
  22. data/lib/mcollective/util.rb +37 -1
  23. data/lib/mcollective/windows_daemon.rb +14 -3
  24. data/spec/spec_helper.rb +2 -0
  25. data/spec/unit/application_spec.rb +45 -26
  26. data/spec/unit/cache_spec.rb +3 -3
  27. data/spec/unit/client_spec.rb +269 -24
  28. data/spec/unit/config_spec.rb +89 -26
  29. data/spec/unit/data/base_spec.rb +1 -0
  30. data/spec/unit/data/result_spec.rb +19 -1
  31. data/spec/unit/data_spec.rb +3 -0
  32. data/spec/unit/ddl/agentddl_spec.rb +32 -0
  33. data/spec/unit/ddl/base_spec.rb +4 -0
  34. data/spec/unit/ddl/dataddl_spec.rb +1 -1
  35. data/spec/unit/log_spec.rb +44 -27
  36. data/spec/unit/logger/base_spec.rb +1 -1
  37. data/spec/unit/matcher_spec.rb +14 -0
  38. data/spec/unit/message_spec.rb +24 -0
  39. data/spec/unit/optionparser_spec.rb +99 -0
  40. data/spec/unit/pluginpackager/agent_definition_spec.rb +48 -17
  41. data/spec/unit/pluginpackager/standard_definition_spec.rb +44 -20
  42. data/spec/unit/pluginpackager_spec.rb +31 -7
  43. data/spec/unit/plugins/mcollective/agent/rpcutil_spec.rb +176 -0
  44. data/spec/unit/plugins/mcollective/application/plugin_spec.rb +81 -0
  45. data/spec/unit/plugins/mcollective/audit/logfile_spec.rb +44 -0
  46. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +118 -27
  47. data/spec/unit/plugins/mcollective/connector/rabbitmq_spec.rb +168 -34
  48. data/spec/unit/plugins/mcollective/data/agent_data_spec.rb +1 -0
  49. data/spec/unit/plugins/mcollective/data/fstat_data_spec.rb +1 -0
  50. data/spec/unit/plugins/mcollective/discovery/flatfile_spec.rb +10 -0
  51. data/spec/unit/plugins/mcollective/discovery/stdin_spec.rb +65 -0
  52. data/spec/unit/plugins/mcollective/facts/yaml_facts_spec.rb +65 -0
  53. data/spec/unit/plugins/mcollective/packagers/debpackage_packager_spec.rb +240 -219
  54. data/spec/unit/plugins/mcollective/packagers/modulepackage_packager_spec.rb +209 -0
  55. data/spec/unit/plugins/mcollective/packagers/rpmpackage_packager_spec.rb +223 -109
  56. data/spec/unit/rpc/actionrunner_spec.rb +2 -1
  57. data/spec/unit/rpc/agent_spec.rb +130 -1
  58. data/spec/unit/rpc/client_spec.rb +169 -3
  59. data/spec/unit/security/base_spec.rb +0 -1
  60. data/spec/unit/shell_spec.rb +76 -3
  61. data/spec/unit/util_spec.rb +69 -1
  62. data/spec/unit/windows_daemon_spec.rb +30 -9
  63. metadata +104 -90
  64. data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +0 -34
  65. data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +0 -424
  66. data/spec/unit/plugins/mcollective/validator/any_validator_spec.rb +0 -15
@@ -151,7 +151,8 @@ module MCollective
151
151
  if Util.windows?
152
152
  @runner.canrun?(File.join(ENV['SystemRoot'], "explorer.exe")).should == true
153
153
  else
154
- @runner.canrun?("/bin/true").should == true
154
+ true_exe = ENV["PATH"].split(File::PATH_SEPARATOR).map {|f| p = File.join(f, "true") ;p if File.exists?(p)}.compact.first
155
+ @runner.canrun?(true_exe).should == true
155
156
  end
156
157
  end
157
158
 
@@ -8,6 +8,8 @@ module MCollective
8
8
  before do
9
9
  ddl = stub
10
10
  ddl.stubs(:meta).returns({})
11
+ ddl.stubs(:action).returns([])
12
+ ddl.stubs(:validate_rpc_request).returns(true)
11
13
  DDL.stubs(:new).returns(ddl)
12
14
 
13
15
  @agent = Agent.new
@@ -15,9 +17,136 @@ module MCollective
15
17
  @agent.request = {}
16
18
  end
17
19
 
20
+ describe "#handlemsg" do
21
+ before do
22
+ Reply.any_instance.stubs(:initialize_data)
23
+
24
+ @agent.stubs(:respond_to?).with("rspec_action_action").returns(true)
25
+ @agent.stubs(:respond_to?).with("authorization_hook").returns(false)
26
+ @agent.stubs(:rspec_action_action).returns(nil)
27
+
28
+ @msg = {:msgtime => 1356006671,
29
+ :senderid => "example.com",
30
+ :requestid => "55f8abe1442328321667877a08bdc586",
31
+ :body => {:agent => "rspec_agent",
32
+ :action => "rspec_action",
33
+ :data => {}},
34
+ :caller => "cert=rspec"}
35
+ end
36
+
37
+ it "should or validate the incoming request" do
38
+ Request.any_instance.expects(:validate!).raises(DDLValidationError, "Failed to validate")
39
+
40
+ reply = @agent.handlemsg(@msg, DDL.new)
41
+
42
+ reply[:statuscode].should == 4
43
+ reply[:statusmsg].should == "Failed to validate"
44
+ end
45
+
46
+ it "should call the authorization hook if set" do
47
+ @agent.expects(:respond_to?).with("authorization_hook").returns(true)
48
+ @agent.expects(:authorization_hook).raises("authorization denied")
49
+ Log.stubs(:error)
50
+
51
+ reply = @agent.handlemsg(@msg, DDL.new)
52
+
53
+ reply[:statuscode].should == 5
54
+ reply[:statusmsg].should == "authorization denied"
55
+ end
56
+
57
+ it "should audit the request" do
58
+ @agent.expects(:audit_request)
59
+
60
+ reply = @agent.handlemsg(@msg, DDL.new)
61
+ reply[:statuscode].should == 0
62
+ end
63
+
64
+ it "should call the before_processing_hook" do
65
+ @agent.expects(:before_processing_hook)
66
+
67
+ reply = @agent.handlemsg(@msg, DDL.new)
68
+ reply[:statuscode].should == 0
69
+ end
70
+
71
+ it "should fail if the action does not exist" do
72
+ @agent.expects(:respond_to?).with("rspec_action_action").returns(false)
73
+ reply = @agent.handlemsg(@msg, DDL.new)
74
+ reply[:statuscode].should == 2
75
+ end
76
+
77
+ it "should call the action correctly" do
78
+ @agent.expects(:rspec_action_action)
79
+ reply = @agent.handlemsg(@msg, DDL.new)
80
+ reply[:statuscode].should == 0
81
+ end
82
+
83
+ it "should handle RPC Aborted errors" do
84
+ @agent.expects(:rspec_action_action).raises(RPCAborted, "rspec test")
85
+ reply = @agent.handlemsg(@msg, DDL.new)
86
+ reply[:statuscode].should == 1
87
+ reply[:statusmsg].should == "rspec test"
88
+ end
89
+
90
+ it "should handle Unknown Action errors" do
91
+ @agent.stubs(:respond_to?).with("rspec_action_action").returns(false)
92
+ reply = @agent.handlemsg(@msg, DDL.new)
93
+ reply[:statuscode].should == 2
94
+ reply[:statusmsg].should == "Unknown action 'rspec_action' for agent 'rspec_agent'"
95
+ end
96
+
97
+ it "should handle Missing Data errors" do
98
+ @agent.expects(:rspec_action_action).raises(MissingRPCData, "rspec test")
99
+ reply = @agent.handlemsg(@msg, DDL.new)
100
+ reply[:statuscode].should == 3
101
+ reply[:statusmsg].should == "rspec test"
102
+ end
103
+
104
+ it "should handle Invalid Data errors" do
105
+ @agent.expects(:rspec_action_action).raises(InvalidRPCData, "rspec test")
106
+ reply = @agent.handlemsg(@msg, DDL.new)
107
+ reply[:statuscode].should == 4
108
+ reply[:statusmsg].should == "rspec test"
109
+ end
110
+
111
+ it "should handle unknown errors" do
112
+ @agent.expects(:rspec_action_action).raises(UnknownRPCError, "rspec test")
113
+ Log.expects(:error).twice
114
+
115
+ reply = @agent.handlemsg(@msg, DDL.new)
116
+ reply[:statuscode].should == 5
117
+ reply[:statusmsg].should == "rspec test"
118
+ end
119
+
120
+ it "should handle arbitrary exceptions" do
121
+ @agent.expects(:rspec_action_action).raises(Exception, "rspec test")
122
+ Log.expects(:error).twice
123
+
124
+ reply = @agent.handlemsg(@msg, DDL.new)
125
+ reply[:statuscode].should == 5
126
+ reply[:statusmsg].should == "rspec test"
127
+ end
128
+
129
+ it "should call the after_processing_hook" do
130
+ @agent.expects(:after_processing_hook)
131
+ reply = @agent.handlemsg(@msg, DDL.new)
132
+ end
133
+
134
+ it "should respond if required" do
135
+ Request.any_instance.expects(:should_respond?).returns(true)
136
+ Reply.any_instance.expects(:to_hash).returns({})
137
+ @agent.handlemsg(@msg, DDL.new).should == {}
138
+ end
139
+
140
+ it "should not respond when not required" do
141
+ Request.any_instance.expects(:should_respond?).returns(false)
142
+ Reply.any_instance.expects(:to_hash).never
143
+ @agent.handlemsg(@msg, DDL.new).should == nil
144
+ end
145
+ end
146
+
18
147
  describe "#meta" do
19
148
  it "should be deprecated" do
20
- Log.expects(:warn).with(regexp_matches(/setting meta data in agents have been deprecated/))
149
+ Log.expects(:warn).with(regexp_matches(/Setting metadata in agents has been deprecated/))
21
150
  Agent.metadata("foo")
22
151
  end
23
152
  end
@@ -62,6 +62,19 @@ module MCollective
62
62
  end
63
63
  end
64
64
 
65
+ describe "#validate_request" do
66
+ it "should fail when a DDL isn't present" do
67
+ @client.instance_variable_set("@ddl", nil)
68
+ expect { @client.validate_request("rspec", {}) }.to raise_error("No DDL found for agent foo cannot validate inputs")
69
+ end
70
+
71
+ it "should validate the input arguments" do
72
+ @client.ddl.expects(:set_default_input_arguments).with("rspec", {})
73
+ @client.ddl.expects(:validate_rpc_request).with("rspec", {})
74
+ @client.validate_request("rspec", {})
75
+ end
76
+ end
77
+
65
78
  describe "#process_results_with_block" do
66
79
  it "should inform the stats object correctly for passed requests" do
67
80
  response = {:senderid => "rspec", :body => {:statuscode => 0}}
@@ -215,7 +228,9 @@ module MCollective
215
228
 
216
229
  describe "#discovery_method=" do
217
230
  it "should set the method" do
231
+ @client.default_discovery_method.should == true
218
232
  @client.discovery_method = "rspec"
233
+ @client.default_discovery_method.should == false
219
234
  @client.discovery_method.should == "rspec"
220
235
  end
221
236
 
@@ -224,6 +239,7 @@ module MCollective
224
239
  client.discovery_method = "rspec"
225
240
  client.discovery_method.should == "rspec"
226
241
  client.discovery_options.should == ["rspec"]
242
+ client.default_discovery_method.should == false
227
243
  end
228
244
 
229
245
  it "should clear the options if none are given initially" do
@@ -263,6 +279,77 @@ module MCollective
263
279
  end
264
280
  end
265
281
 
282
+ describe "#class_filter" do
283
+ it "should add a class to the filter" do
284
+ @client.class_filter("rspec")
285
+ @client.filter["cf_class"].should == ["rspec"]
286
+ end
287
+
288
+ it "should be idempotent" do
289
+ @client.class_filter("rspec")
290
+ @client.class_filter("rspec")
291
+ @client.filter["cf_class"].should == ["rspec"]
292
+ end
293
+ end
294
+
295
+ describe "#fact_filter" do
296
+ before do
297
+ Util.stubs(:parse_fact_string).with("rspec=present").returns({:value => "present", :fact => "rspec", :operator => "=="})
298
+ end
299
+
300
+ it "should add a fact to the filter" do
301
+ @client.fact_filter("rspec", "present", "=")
302
+ @client.filter["fact"].should == [{:value=>"present", :fact=>"rspec", :operator=>"=="}]
303
+ end
304
+
305
+ it "should be idempotent" do
306
+ @client.fact_filter("rspec", "present", "=")
307
+ @client.fact_filter("rspec", "present", "=")
308
+ @client.filter["fact"].should == [{:value=>"present", :fact=>"rspec", :operator=>"=="}]
309
+ end
310
+ end
311
+
312
+ describe "#agent_filter" do
313
+ it "should add an agent to the filter" do
314
+ @client.filter["agent"].should == ["foo"]
315
+ end
316
+
317
+ it "should be idempotent" do
318
+ @client.agent_filter("foo")
319
+ @client.filter["agent"].should == ["foo"]
320
+ end
321
+ end
322
+
323
+ describe "#identity_filter" do
324
+ it "should add a node to the filter" do
325
+ @client.identity_filter("rspec_node")
326
+ @client.filter["identity"].should == ["rspec_node"]
327
+ end
328
+
329
+ it "should be idempotent" do
330
+ @client.identity_filter("rspec_node")
331
+ @client.identity_filter("rspec_node")
332
+ @client.filter["identity"].should == ["rspec_node"]
333
+ end
334
+ end
335
+
336
+ describe "#compound_filter" do
337
+ before do
338
+ Matcher.stubs(:create_compound_callstack).with("filter").returns("filter")
339
+ end
340
+
341
+ it "should add a compound filter" do
342
+ @client.compound_filter("filter")
343
+ @client.filter["compound"].should == ["filter"]
344
+ end
345
+
346
+ it "should be idempotent" do
347
+ @client.compound_filter("filter")
348
+ @client.compound_filter("filter")
349
+ @client.filter["compound"].should == ["filter"]
350
+ end
351
+ end
352
+
266
353
  describe "#discovery_timeout" do
267
354
  it "should favour the initial options supplied timeout" do
268
355
  client = Client.new("rspec", {:options => {:disctimeout => 3, :filter => Util.empty_filter, :config => "/nonexisting"}})
@@ -321,9 +408,7 @@ module MCollective
321
408
 
322
409
  client.stubs(:call_agent)
323
410
 
324
- ddl = mock
325
- ddl.expects(:validate_rpc_request).with("rspec", {:arg => :val}).raises("validation failed")
326
- client.instance_variable_set("@ddl", ddl)
411
+ client.expects(:validate_request).with("rspec", {:arg => :val}).raises("validation failed")
327
412
 
328
413
  expect { client.rspec(:arg => :val) }.to raise_error("validation failed")
329
414
  end
@@ -482,6 +567,87 @@ module MCollective
482
567
  end
483
568
  end
484
569
 
570
+ describe "#fire_and_forget_request" do
571
+ before do
572
+ @client = stub
573
+ @discoverer = stub
574
+ @ddl = stub
575
+
576
+ @ddl.stubs(:meta).returns({:timeout => 2})
577
+
578
+ @discoverer.stubs(:force_direct_mode?).returns(false)
579
+ @discoverer.stubs(:ddl).returns(@ddl)
580
+ @discoverer.stubs(:discovery_method).returns("mc")
581
+
582
+ @client.stubs("options=")
583
+ @client.stubs(:collective).returns("mcollective")
584
+ @client.stubs(:discoverer).returns(@discoverer)
585
+ @client.stubs(:sendreq)
586
+
587
+ Config.instance.stubs(:loadconfig).with("/nonexisting").returns(true)
588
+ MCollective::Client.expects(:new).returns(@client)
589
+ Config.instance.stubs(:direct_addressing).returns(true)
590
+
591
+ @rpcclient = Client.new("foo", {:options => {:filter => Util.empty_filter, :config => "/nonexisting"}})
592
+ @rpcclient.stubs(:validate_request)
593
+ @request = stub
594
+ @rpcclient.stubs(:new_request).returns(@request)
595
+ end
596
+
597
+ it "should validate the request" do
598
+ @rpcclient.expects(:validate_request).with("rspec", {:rspec => "test"}).raises("rspec")
599
+
600
+ expect {
601
+ @rpcclient.fire_and_forget_request("rspec", {:rspec => "test"})
602
+ }.to raise_error("rspec")
603
+ end
604
+
605
+ it "should set the filter if it was specifically supplied" do
606
+ message = mock
607
+ Message.expects(:new).with(@request, nil, {:agent => "foo", :type => :request, :collective => "mcollective", :filter => "filter", :options => @rpcclient.options}).returns(message)
608
+
609
+ @rpcclient.expects(:identity_filter_discovery_optimization)
610
+ @rpcclient.fire_and_forget_request("rspec", {:rspec => "test"}, "filter")
611
+ end
612
+
613
+ it "should set reply_to if set" do
614
+ message = mock
615
+ Message.expects(:new).with(@request, nil, {:agent => "foo", :type => :request, :collective => "mcollective", :filter => @rpcclient.filter, :options => @rpcclient.options}).returns(message)
616
+
617
+ @rpcclient.reply_to = "/reply/to"
618
+ message.expects(:reply_to=).with("/reply/to")
619
+
620
+ @rpcclient.expects(:identity_filter_discovery_optimization)
621
+ @rpcclient.fire_and_forget_request("rspec", {:rspec => "test"})
622
+ end
623
+
624
+ it "should support direct_requests with discovery data supplied" do
625
+ message = mock
626
+ Message.expects(:new).with(@request, nil, {:agent => "foo", :type => :request, :collective => "mcollective", :filter => @rpcclient.filter, :options => @rpcclient.options}).returns(message)
627
+
628
+ @rpcclient.discover :nodes => "rspec"
629
+ message.expects(:discovered_hosts=).with(["rspec"])
630
+ message.expects(:type=).with(:direct_request)
631
+
632
+ @rpcclient.expects(:identity_filter_discovery_optimization)
633
+ @rpcclient.fire_and_forget_request("rspec", {:rspec => "test"})
634
+ end
635
+
636
+ it "should support direct_requests with discoverers that force direct mode" do
637
+ message = mock
638
+ Message.expects(:new).with(@request, nil, {:agent => "foo", :type => :request, :collective => "mcollective", :filter => @rpcclient.filter, :options => @rpcclient.options}).returns(message)
639
+
640
+ @discoverer.stubs(:force_direct_mode?).returns(true)
641
+ @rpcclient.stubs(:discover).returns(["rspec"])
642
+
643
+ message.expects(:discovered_hosts=).with(["rspec"])
644
+ message.expects(:type=).with(:direct_request)
645
+
646
+ @rpcclient.expects(:identity_filter_discovery_optimization)
647
+ @rpcclient.fire_and_forget_request("rspec", {:rspec => "test"})
648
+ end
649
+ end
650
+
485
651
  describe "#call_agent_batched" do
486
652
  before do
487
653
  @client = stub
@@ -10,7 +10,6 @@ module MCollective
10
10
  @config.stubs(:identity).returns("test")
11
11
  @config.stubs(:configured).returns(true)
12
12
  @config.stubs(:topicsep).returns(".")
13
- @config.stubs(:topicprefix).returns("/topic/")
14
13
 
15
14
  @stats = mock("stats")
16
15
 
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env rspec
2
2
 
3
3
  require 'spec_helper'
4
+ require 'pathname'
4
5
 
5
6
  module MCollective
6
7
  describe Shell do
@@ -79,8 +80,15 @@ module MCollective
79
80
  end
80
81
  end
81
82
 
83
+ before :each do
84
+ @systemu = mock
85
+ @thread = mock
86
+ @thread.stubs(:kill)
87
+ @systemu.stubs(:thread).returns(@thread)
88
+ end
89
+
82
90
  it "should run the command" do
83
- Shell.any_instance.stubs("systemu").returns(true).once.with("date", "stdout" => '', "stderr" => '', "env" => {"LC_ALL" => "C"}, 'cwd' => Dir.tmpdir)
91
+ Shell.any_instance.stubs("systemu").returns(@systemu).once.with("date", "stdout" => '', "stderr" => '', "env" => {"LC_ALL" => "C"}, 'cwd' => Dir.tmpdir)
84
92
  s = Shell.new("date")
85
93
  s.runcommand
86
94
  end
@@ -127,11 +135,12 @@ module MCollective
127
135
  end
128
136
 
129
137
  it "should run in the correct cwd" do
130
- s = Shell.new('ruby -e "puts Dir.pwd"', :cwd => Dir.tmpdir)
138
+ tmpdir = Pathname.new(Dir.tmpdir).realpath.to_s
139
+ s = Shell.new('ruby -e "puts Dir.pwd"', :cwd => tmpdir)
131
140
 
132
141
  s.runcommand
133
142
 
134
- s.stdout.should == "#{Dir.tmpdir}#{nl}"
143
+ s.stdout.should == "#{tmpdir}#{nl}"
135
144
  end
136
145
 
137
146
  it "should send the stdin" do
@@ -147,6 +156,70 @@ module MCollective
147
156
 
148
157
  s.stdout.should == "first line#{nl}#{nl}2nd line#{nl}"
149
158
  end
159
+
160
+ it "should quietly catch Errno::ESRCH if the systemu process has completed" do
161
+ s = Shell.new("echo foo")
162
+ Thread.any_instance.stubs(:alive?).raises(Errno::ESRCH)
163
+ s.runcommand
164
+ end
165
+
166
+ describe "timeout has been set" do
167
+ before do
168
+ thread = mock
169
+ thread.stubs(:alive?).returns(false)
170
+ Thread.stubs(:current).returns(thread)
171
+ Util.stubs(:windows?).returns(false)
172
+ Thread.stubs(:alive?).returns(false)
173
+ Process.expects(:kill).with("TERM", 1234)
174
+ Process.expects(:waitpid).with(1234)
175
+ end
176
+
177
+ it "should terminate the systemu process after the specified timeout is exceeded" do
178
+ s = Shell.new(%{ruby -e 'sleep 5'}, :timeout => 1)
179
+ s.stubs(:systemu).yields(1234).returns(@systemu)
180
+ s.stubs(:sleep).with(2)
181
+ s.expects(:sleep).with(1)
182
+ Process.stubs(:kill).with(0, 1234).returns(1, nil)
183
+ s.runcommand
184
+ end
185
+
186
+ it "should kill an unresponsive systemu process on timeout" do
187
+ s = Shell.new(%{ruby -e 'sleep 5'}, :timeout => 1)
188
+ s.stubs(:systemu).yields(1234).returns(@systemu)
189
+ s.expects(:sleep).with(1)
190
+ s.stubs(:sleep).with(2)
191
+ Process.stubs(:kill).with(0, 1234).returns(1)
192
+ Process.expects(:kill).with("KILL", 1234)
193
+ s.runcommand
194
+ end
195
+
196
+ it "should kill the systemu process if the parent thread exits and :on_thread_exit is specified" do
197
+ s = Shell.new(%{ruby -e 'sleep 5'}, :timeout => :on_thread_exit)
198
+ s.stubs(:systemu).yields(1234).returns(@systemu)
199
+ s.stubs(:sleep).with(2)
200
+ Process.stubs(:kill).with(0, 1234).returns(1)
201
+ Process.expects(:kill).with("KILL", 1234)
202
+ s.runcommand
203
+ end
204
+ end
205
+
206
+ it "should log a warning if the child process cannot be reaped" do
207
+ s = Shell.new('ruby -e "sleep 2"', :timeout=> 1)
208
+ Thread.stubs(:current)
209
+ s.stubs(:systemu).yields(1234).returns(@systemu)
210
+ s.stubs(:sleep).with(1).raises(Errno::ECHILD)
211
+ Log.expects(:warn).with("Could not reap process '1234'.")
212
+ s.runcommand
213
+ end
214
+
215
+ it "should kill the guard thread when the process returns" do
216
+ s = Shell.new("echo hello world")
217
+ Thread.stubs(:current)
218
+ s.expects(:systemu).returns(@systemu)
219
+ @thread.expects(:kill)
220
+ result = s.runcommand
221
+ result.should == @systemu
222
+ end
150
223
  end
151
224
  end
152
225
  end