mcollective-client 2.2.4 → 2.4.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.

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