mcollective-client 2.9.1 → 2.10.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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZWEwMDY2OTlmMTkwOWM4NGRjODVmYzQzMTk1ZGJmN2M4MDA0MTdlZg==
5
+ data.tar.gz: !binary |-
6
+ MjAyNjQ5NDQxZTE1MmM1NzEwODcwMDU1MTQzNGVlNmNhYjdkZDcyMw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NjNjZWZiZjIzMTk5ODE1ZTNkZDVmYThhMGNmNzFhNzY4ZGViMGJiNWU0OGU3
10
+ MDcyMTlhNjEyZDI3ZGZiMjE4YzVjNzk2OTVkOGY2ZDdlZjg5MTFjNTcwOWUx
11
+ N2YzZDU4YTg1MThhZWEwZTVhYjQ4NTk1ZDNlZWU2ZTc0ODRhOWQ=
12
+ data.tar.gz: !binary |-
13
+ Mjc1NjBiNjJjNWMyYjljNDE0OWFiZmE3OWYwOGNmODEzZmE2ZmIxNWJhNDdk
14
+ NzA0MmQ5ZmI3Y2JjYmI5ODIwMGE4NzcwMjNmZTdkOWJhYmRkOGJjZWVjN2Vk
15
+ Njg2YzZiNGI2NThjYWQ2MjA4ZTA3NDUyNDA0M2FhOTgxYmI3MGE=
data/lib/mcollective.rb CHANGED
@@ -59,7 +59,7 @@ module MCollective
59
59
 
60
60
  MCollective::Vendor.load_vendored
61
61
 
62
- VERSION="2.9.1"
62
+ VERSION="2.10.0"
63
63
 
64
64
  def self.version
65
65
  VERSION
@@ -6,6 +6,8 @@ class MCollective::Application::Find<MCollective::Application
6
6
 
7
7
  starttime = Time.now
8
8
 
9
+ mc.detect_and_set_stdin_discovery
10
+
9
11
  nodes = mc.discover
10
12
 
11
13
  discoverytime = Time.now - starttime
@@ -96,18 +96,8 @@ class MCollective::Application::Rpc<MCollective::Application
96
96
  puts "Request sent with id: " + mc.send(configuration[:action], configuration[:arguments])
97
97
  else
98
98
  discover_args = {:verbose => true}
99
- # IF the discovery method hasn't been explicitly overridden
100
- # and we're not being run interactively,
101
- # and someone has piped us some data
102
- # Then we assume it's a discovery list - this can be either:
103
- # - list of hosts in plaintext
104
- # - JSON that came from another rpc or printrpc
105
- if mc.default_discovery_method && !STDIN.tty? && !STDIN.eof?
106
- # Then we override discovery to try to grok the data on STDIN
107
- mc.discovery_method = 'stdin'
108
- mc.discovery_options = 'auto'
109
- discover_args = {:verbose => false}
110
- end
99
+
100
+ mc.detect_and_set_stdin_discovery
111
101
 
112
102
  mc.discover discover_args
113
103
 
@@ -159,10 +159,10 @@ module MCollective
159
159
  #
160
160
  # It returns a hash of times and timeouts for discovery and total run is taken from the options
161
161
  # hash which in turn is generally built using MCollective::Optionparser
162
- def req(body, agent=nil, options=false, waitfor=0, &block)
162
+ def req(body, agent=nil, options=false, waitfor=[], &block)
163
163
  if body.is_a?(Message)
164
164
  agent = body.agent
165
- waitfor = body.discovered_hosts.size || 0
165
+ waitfor = body.discovered_hosts || []
166
166
  @options = body.options
167
167
  end
168
168
 
@@ -170,12 +170,11 @@ module MCollective
170
170
  threaded = @options[:threaded]
171
171
  timeout = @discoverer.discovery_timeout(@options[:timeout], @options[:filter])
172
172
  request = createreq(body, agent, @options[:filter])
173
- publish_timeout = @options[:publish_timeout]
173
+ publish_timeout = @options[:publish_timeout] || @config.publish_timeout
174
174
  stat = {:starttime => Time.now.to_f, :discoverytime => 0, :blocktime => 0, :totaltime => 0}
175
175
  STDOUT.sync = true
176
176
  hosts_responded = 0
177
177
 
178
-
179
178
  begin
180
179
  if threaded
181
180
  hosts_responded = threaded_req(request, publish_timeout, timeout, waitfor, &block)
@@ -238,16 +237,41 @@ module MCollective
238
237
  def start_receiver(requestid, waitfor, timeout, &block)
239
238
  Log.debug("Starting response receiver with timeout of #{timeout}")
240
239
  hosts_responded = 0
240
+
241
+ if (waitfor.is_a?(Array))
242
+ unfinished = Hash.new(0)
243
+ waitfor.each {|w| unfinished[w] += 1}
244
+ else
245
+ unfinished = []
246
+ end
247
+
241
248
  begin
242
249
  Timeout.timeout(timeout) do
243
- begin
250
+ loop do
244
251
  resp = receive(requestid)
245
252
  yield resp.payload
246
253
  hosts_responded += 1
247
- end while (waitfor == 0 || hosts_responded < waitfor)
254
+
255
+ if (waitfor.is_a?(Array))
256
+ sender = resp.payload[:senderid]
257
+ if unfinished[sender] <= 1
258
+ unfinished.delete(sender)
259
+ else
260
+ unfinished[sender] -= 1
261
+ end
262
+
263
+ break if !waitfor.empty? && unfinished.empty?
264
+ else
265
+ break unless waitfor == 0 || hosts_responded < waitfor
266
+ end
267
+ end
248
268
  end
249
269
  rescue Timeout::Error => e
250
- if (waitfor > hosts_responded)
270
+ if waitfor.is_a?(Array)
271
+ if !unfinished.empty?
272
+ Log.warn("Could not receive all responses. Did not receive responses from #{unfinished.keys.join(', ')}")
273
+ end
274
+ elsif (waitfor > hosts_responded)
251
275
  Log.warn("Could not receive all responses. Expected : #{waitfor}. Received : #{hosts_responded}")
252
276
  end
253
277
  end
@@ -541,6 +541,7 @@ module MCollective
541
541
  raise("Unknown target type #{type}") unless [:directed, :broadcast, :reply, :request, :direct_request].include?(type)
542
542
  raise("Unknown collective '#{collective}' known collectives are '#{@config.collectives.join ', '}'") unless @config.collectives.include?(collective)
543
543
 
544
+ agents_multiplex = get_bool_option("activemq.agents_multiplex", "false")
544
545
  target = {:name => nil, :headers => {}}
545
546
 
546
547
  case type
@@ -548,10 +549,18 @@ module MCollective
548
549
  target[:name] = ["/queue/" + collective, :reply, "#{Config.instance.identity}_#{$$}", Client.request_sequence].join(".")
549
550
 
550
551
  when :broadcast
551
- target[:name] = ["/topic/" + collective, agent, :agent].join(".")
552
+ if agents_multiplex
553
+ target[:name] = ["/topic/" + collective, :agents].join(".")
554
+ else
555
+ target[:name] = ["/topic/" + collective, agent, :agent].join(".")
556
+ end
552
557
 
553
558
  when :request
554
- target[:name] = ["/topic/" + collective, agent, :agent].join(".")
559
+ if agents_multiplex
560
+ target[:name] = ["/topic/" + collective, :agents].join(".")
561
+ else
562
+ target[:name] = ["/topic/" + collective, agent, :agent].join(".")
563
+ end
555
564
 
556
565
  when :direct_request
557
566
  target[:name] = ["/queue/" + collective, :nodes].join(".")
@@ -387,6 +387,7 @@ module MCollective
387
387
  raise("Unknown target type #{type}") unless [:directed, :broadcast, :reply, :request, :direct_request].include?(type)
388
388
  raise("Unknown collective '#{collective}' known collectives are '#{@config.collectives.join ', '}'") unless @config.collectives.include?(collective)
389
389
 
390
+ agents_multiplex = get_bool_option("rabbitmq.agents_multiplex", "false")
390
391
  target = {:name => "", :headers => {}, :id => nil}
391
392
 
392
393
  if reply_to
@@ -402,13 +403,18 @@ module MCollective
402
403
  target[:id] = "mcollective_%s_replies" % agent
403
404
 
404
405
  when :broadcast, :request # publishing a request to all nodes with an agent
405
- target[:name] = "/exchange/%s_broadcast/%s" % [collective, agent]
406
+ if agents_multiplex
407
+ target[:name] = "/exchange/%s_broadcast" % collective
408
+ target[:id] = "%s_broadcast" % collective
409
+ else
410
+ target[:name] = "/exchange/%s_broadcast/%s" % [collective, agent]
411
+ target[:id] = "%s_broadcast_%s" % [collective, agent]
412
+ end
406
413
  if reply_to
407
414
  target[:headers]["reply-to"] = reply_to
408
415
  else
409
416
  target[:headers]["reply-to"] = reply_path
410
417
  end
411
- target[:id] = "%s_broadcast_%s" % [collective, agent]
412
418
 
413
419
  when :direct_request # a request to a specific node
414
420
  raise "Directed requests need to have a node identity" unless node
@@ -18,7 +18,7 @@ module MCollective
18
18
  file = STDIN.read
19
19
 
20
20
  if file =~ /^\s*$/
21
- raise("data piped on STDIN contained only whitespace - could not discover hosts from it.")
21
+ raise("data piped on STDIN contained only whitespace - could not discover hosts from it.")
22
22
  end
23
23
 
24
24
  if type == 'auto'
@@ -29,8 +29,10 @@ module MCollective
29
29
  end
30
30
  end
31
31
 
32
+ Log.debug("Parsing STDIN input as type %s" % type)
33
+
32
34
  if type == 'json'
33
- hosts = MCollective::RPC::Helpers.extract_hosts_from_json(file)
35
+ hosts = RPC::Helpers.extract_hosts_from_json(file)
34
36
  elsif type == 'text'
35
37
  hosts = file.split("\n")
36
38
  else
@@ -119,6 +119,12 @@ module MCollective
119
119
  @stdout = STDOUT
120
120
  @stdout.sync = true
121
121
  end
122
+
123
+ if initial_options[:stdin]
124
+ @stdin = initial_options[:stdin]
125
+ else
126
+ @stdin = STDIN
127
+ end
122
128
  end
123
129
 
124
130
  # Disconnects cleanly from the middleware
@@ -459,6 +465,24 @@ module MCollective
459
465
  agent_filter @agent
460
466
  end
461
467
 
468
+ # Detects data on STDIN and sets the STDIN discovery method
469
+ #
470
+ # IF the discovery method hasn't been explicitly overridden
471
+ # and we're not being run interactively,
472
+ # and someone has piped us some data
473
+ #
474
+ # Then we assume it's a discovery list - this can be either:
475
+ # - list of hosts in plaintext
476
+ # - JSON that came from another rpc or printrpc
477
+ #
478
+ # Then we override discovery to try to grok the data on STDIN
479
+ def detect_and_set_stdin_discovery
480
+ if self.default_discovery_method && !@stdin.tty? && !@stdin.eof?
481
+ self.discovery_method = 'stdin'
482
+ self.discovery_options = 'auto'
483
+ end
484
+ end
485
+
462
486
  # Does discovery based on the filters set, if a discovery was
463
487
  # previously done return that else do a new discovery.
464
488
  #
@@ -845,6 +869,7 @@ module MCollective
845
869
  end
846
870
 
847
871
  @stats.noresponsefrom.concat @client.stats[:noresponsefrom]
872
+ @stats.unexpectedresponsefrom.concat @client.stats[:unexpectedresponsefrom]
848
873
  @stats.responses += @client.stats[:responses]
849
874
  @stats.blocktime += @client.stats[:blocktime] + sleep_time
850
875
  @stats.totaltime += @client.stats[:totaltime]
@@ -2,8 +2,8 @@ module MCollective
2
2
  module RPC
3
3
  # Various utilities for the RPC system
4
4
  class Helpers
5
- # Parse JSON output as produced by printrpc and extract
6
- # the "sender" of each rpc response
5
+ # Parse JSON output as produced by printrpc or puppet query
6
+ # and extract the "sender" / "certname" of each entry
7
7
  #
8
8
  # The simplist valid JSON based data would be:
9
9
  #
@@ -11,6 +11,13 @@ module MCollective
11
11
  # {"sender" => "example.com"},
12
12
  # {"sender" => "another.com"}
13
13
  # ]
14
+ #
15
+ # or
16
+ #
17
+ # [
18
+ # {"certname" => "example.com"},
19
+ # {"certname" => "another.com"}
20
+ # ]
14
21
  def self.extract_hosts_from_json(json)
15
22
  hosts = JSON.parse(json)
16
23
 
@@ -18,9 +25,12 @@ module MCollective
18
25
 
19
26
  hosts.map do |host|
20
27
  raise "JSON host list is not an array of Hashes" unless host.is_a?(Hash)
21
- raise "JSON host list does not have senders in it" unless host.include?("sender")
22
28
 
23
- host["sender"]
29
+ unless host.include?("sender") || host.include?("certname")
30
+ raise "JSON host list does not have senders in it"
31
+ end
32
+
33
+ host["sender"] || host["certname"]
24
34
  end.uniq
25
35
  end
26
36
 
@@ -264,7 +274,7 @@ module MCollective
264
274
  parser.on('--one', '-1', 'Send request to only one discovered nodes') do |v|
265
275
  options[:mcollective_limit_targets] = 1
266
276
  end
267
-
277
+
268
278
  parser.on('--batch SIZE', 'Do requests in batches') do |v|
269
279
  # validate batch string. Is it x% where x > 0 or is it an integer
270
280
  if ((v =~ /^(\d+)%$/ && Integer($1) != 0) || v =~ /^(\d+)$/)
@@ -2,9 +2,9 @@ module MCollective
2
2
  module RPC
3
3
  # Class to wrap all the stats and to keep track of some timings
4
4
  class Stats
5
- attr_accessor :noresponsefrom, :starttime, :discoverytime, :blocktime, :responses, :totaltime
6
- attr_accessor :discovered, :discovered_nodes, :okcount, :failcount, :noresponsefrom, :responsesfrom
7
- attr_accessor :requestid, :aggregate_summary, :ddl, :aggregate_failures
5
+ attr_accessor :noresponsefrom, :unexpectedresponsefrom, :starttime, :discoverytime, :blocktime, :responses
6
+ attr_accessor :totaltime, :discovered, :discovered_nodes, :okcount, :failcount, :noresponsefrom
7
+ attr_accessor :responsesfrom, :requestid, :aggregate_summary, :ddl, :aggregate_failures
8
8
 
9
9
  def initialize
10
10
  reset
@@ -13,6 +13,7 @@ module MCollective
13
13
  # Resets stats, if discovery time is set we keep it as it was
14
14
  def reset
15
15
  @noresponsefrom = []
16
+ @unexpectedresponsefrom = []
16
17
  @responsesfrom = []
17
18
  @responses = 0
18
19
  @starttime = Time.now.to_f
@@ -23,7 +24,6 @@ module MCollective
23
24
  @discovered_nodes = []
24
25
  @okcount = 0
25
26
  @failcount = 0
26
- @noresponsefrom = []
27
27
  @requestid = nil
28
28
  @aggregate_summary = []
29
29
  @aggregate_failures = []
@@ -32,6 +32,7 @@ module MCollective
32
32
  # returns a hash of our stats
33
33
  def to_hash
34
34
  {:noresponsefrom => @noresponsefrom,
35
+ :unexpectedresponsefrom => @unexpectedresponsefrom,
35
36
  :starttime => @starttime,
36
37
  :discoverytime => @discoverytime,
37
38
  :blocktime => @blocktime,
@@ -70,6 +71,7 @@ module MCollective
70
71
  # Re-initializes the object with stats from the basic client
71
72
  def client_stats=(stats)
72
73
  @noresponsefrom = stats[:noresponsefrom]
74
+ @unexpectedresponsefrom = stats[:unexpectedresponsefrom]
73
75
  @responses = stats[:responses]
74
76
  @starttime = stats[:starttime]
75
77
  @blocktime = stats[:blocktime]
@@ -115,13 +117,19 @@ module MCollective
115
117
  def finish_request
116
118
  @totaltime = @blocktime + @discoverytime
117
119
 
118
- # figures out who we had no responses from
120
+ # figure out who responded unexpectedly
121
+ rhosts = @responsesfrom.clone
122
+ @discovered_nodes.each {|d| rhosts.delete(d)}
123
+ @unexpectedresponsefrom = rhosts
124
+
125
+ # figure out who we had no responses from
119
126
  dhosts = @discovered_nodes.clone
120
127
  @responsesfrom.each {|r| dhosts.delete(r)}
121
128
  @noresponsefrom = dhosts
122
129
  rescue
123
130
  @totaltime = 0
124
131
  @noresponsefrom = []
132
+ @unexpectedresponsefrom = []
125
133
  end
126
134
 
127
135
  # Helper to keep track of who we received responses from
@@ -225,8 +233,22 @@ module MCollective
225
233
  end
226
234
  end
227
235
 
228
- if no_response_report != ""
229
- result_text << "" << no_response_report
236
+ no_response_r = no_response_report
237
+ unexpected_response_r = unexpected_response_report
238
+ if no_response_r || unexpected_response_r
239
+ result_text << ""
240
+ end
241
+
242
+ if no_response_r != ""
243
+ result_text << "" << no_response_r
244
+ end
245
+
246
+ if unexpected_response_r != ""
247
+ result_text << "" << unexpected_response_r
248
+ end
249
+
250
+ if no_response_r || unexpected_response_r
251
+ result_text << ""
230
252
  end
231
253
 
232
254
  result_text.join("\n")
@@ -237,7 +259,6 @@ module MCollective
237
259
  result_text = StringIO.new
238
260
 
239
261
  if @noresponsefrom.size > 0
240
- result_text.puts
241
262
  result_text.puts Util.colorize(:red, "No response from:")
242
263
  result_text.puts
243
264
 
@@ -248,8 +269,26 @@ module MCollective
248
269
  @noresponsefrom.sort.in_groups_of(fields_num) do |c|
249
270
  result_text.puts format % c
250
271
  end
272
+ end
251
273
 
274
+ result_text.string
275
+ end
276
+
277
+ # Returns a blob of text indicating what nodes responded but weren't discovered
278
+ def unexpected_response_report
279
+ result_text = StringIO.new
280
+
281
+ if @unexpectedresponsefrom.size > 0
282
+ result_text.puts Util.colorize(:red, "Unexpected response from:")
252
283
  result_text.puts
284
+
285
+ field_size = Util.field_size(@unexpectedresponsefrom, 30)
286
+ fields_num = Util.field_number(field_size)
287
+ format = " " + ( " %-#{field_size}s" * fields_num )
288
+
289
+ @unexpectedresponsefrom.sort.in_groups_of(fields_num) do |c|
290
+ result_text.puts format % c
291
+ end
253
292
  end
254
293
 
255
294
  result_text.string
@@ -205,14 +205,25 @@ module MCollective
205
205
 
206
206
  it "should thread the publisher and receiver if configured" do
207
207
  client.instance_variable_get(:@options)[:threaded] = true
208
- client.expects(:threaded_req).with(request, nil, 0, 1)
208
+ client.expects(:threaded_req).with(request, 2, 0, ['rspec'])
209
209
  message.options[:threaded] = true
210
210
  client.req(message)
211
211
  end
212
212
 
213
213
  it "should not thread the publisher and receiver if configured" do
214
214
  client.instance_variable_set(:@threaded, false)
215
- client.expects(:unthreaded_req).with(request, nil, 0, 1)
215
+ client.expects(:unthreaded_req).with(request, 2, 0, ['rspec'])
216
+ client.req(message)
217
+ end
218
+
219
+ it "uses the publish_timeout from options when passed as an option" do
220
+ client.expects(:unthreaded_req).with(request, 5, 0, ['rspec'])
221
+ client.req(message, nil, message.options.merge(:publish_timeout => 5))
222
+ end
223
+
224
+ it "uses the publish_timeout from config when passed as a config value" do
225
+ client.expects(:unthreaded_req).with(request, 10, 0, ['rspec'])
226
+ client.instance_variable_get(:@config).expects(:publish_timeout).returns(10)
216
227
  client.req(message)
217
228
  end
218
229
  end
@@ -267,50 +278,132 @@ module MCollective
267
278
  end
268
279
 
269
280
  describe "#start_receiver" do
270
- it "should go into a receive loop and receive until it reaches waitfor" do
271
- results = []
272
- Timeout.stubs(:timeout).yields
273
- message = mock
274
- client.stubs(:receive).with("erfs123").returns(message)
275
- message.stubs(:payload).returns("msg1", "msg2", "msg3")
276
- client.start_receiver("erfs123", 3, 5) do |msg|
277
- results << msg
281
+ describe "waitfor is a number" do
282
+ it "should go into a receive loop and receive until it reaches waitfor" do
283
+ results = []
284
+ Timeout.stubs(:timeout).yields
285
+ message = mock
286
+ client.stubs(:receive).with("erfs123").returns(message)
287
+ message.stubs(:payload).returns("msg1", "msg2", "msg3")
288
+ client.start_receiver("erfs123", 3, 5) do |msg|
289
+ results << msg
290
+ end
291
+ results.should == ["msg1", "msg2", "msg3"]
278
292
  end
279
- results.should == ["msg1", "msg2", "msg3"]
280
- end
281
293
 
282
- it "should log a warning if a timeout occurs" do
283
- results = []
284
- Timeout.stubs(:timeout).yields
285
- message = mock
286
- client.stubs(:receive).with("erfs123").returns(message)
287
- message.stubs(:payload).returns("msg1", "msg2", "timeout")
288
- Log.expects(:warn).with("Could not receive all responses. Expected : 3. Received : 2")
289
- responded = client.start_receiver("erfs123", 3, 5) do |msg|
290
- if msg == "timeout"
291
- raise Timeout::Error
294
+ it "should log a warning if a timeout occurs" do
295
+ results = []
296
+ Timeout.stubs(:timeout).yields
297
+ message = mock
298
+ client.stubs(:receive).with("erfs123").returns(message)
299
+ message.stubs(:payload).returns("msg1", "msg2", "timeout")
300
+ Log.expects(:warn).with("Could not receive all responses. Expected : 3. Received : 2")
301
+ responded = client.start_receiver("erfs123", 3, 5) do |msg|
302
+ if msg == "timeout"
303
+ raise Timeout::Error
304
+ end
305
+ results << msg
306
+ end
307
+ results.should == ["msg1", "msg2"]
308
+ responded.should == 2
309
+ end
310
+
311
+ it "should not log a warning if a the response count is larger or equal to the expected number of responses" do
312
+ results = []
313
+ Timeout.stubs(:timeout).yields
314
+ message = mock
315
+ client.stubs(:receive).with("erfs123").returns(message)
316
+ message.stubs(:payload).returns("msg1", "msg2", "timeout")
317
+ Log.expects(:warn).never
318
+ responded = client.start_receiver("erfs123", 2, 5) do |msg|
319
+ if msg == "timeout"
320
+ raise Timeout::Error
321
+ end
322
+ results << msg
292
323
  end
293
- results << msg
324
+ results.should == ["msg1", "msg2"]
325
+ responded.should == 2
294
326
  end
295
- results.should == ["msg1", "msg2"]
296
- responded.should == 2
297
327
  end
298
328
 
299
- it "should not log a warning if a the response count is larger or equal to the expected number of responses" do
300
- results = []
301
- Timeout.stubs(:timeout).yields
302
- message = mock
303
- client.stubs(:receive).with("erfs123").returns(message)
304
- message.stubs(:payload).returns("msg1", "msg2", "timeout")
305
- Log.expects(:warn).never
306
- responded = client.start_receiver("erfs123", 2, 5) do |msg|
307
- if msg == "timeout"
308
- raise Timeout::Error
329
+ describe "waitfor is an array" do
330
+ it "should go into a receive loop and receive until it matches waitfor" do
331
+ senders = ["sender1", "sender2", "sender3", "sender4"]
332
+ expected = senders.map {|s| Message.new({:callerid => "caller", :senderid => s}, nil, :type => :reply)}
333
+ results = []
334
+ Timeout.stubs(:timeout).yields
335
+ client.stubs(:receive).with("erfs123").returns(*expected)
336
+ client.start_receiver("erfs123", senders[0,3], 5) do |msg|
337
+ results << msg
338
+ end
339
+ results.should == expected[0,3].map {|m| m.payload}
340
+ end
341
+
342
+ it "receive until it gets all expected responses" do
343
+ senders = ["sender1", "sender2", "sender3", "sender4"]
344
+ expected = senders.map {|s| Message.new({:callerid => "caller", :senderid => s}, nil, :type => :reply)}
345
+ results = []
346
+ Timeout.stubs(:timeout).yields
347
+ client.stubs(:receive).with("erfs123").returns(*expected)
348
+ client.start_receiver("erfs123", senders[1,3], 5) do |msg|
349
+ results << msg
350
+ end
351
+ results.should == expected.map {|m| m.payload}
352
+ end
353
+
354
+ it "should log a warning if a timeout occurs" do
355
+ senders = ["sender1", "sender2", "sender3"]
356
+ messages = ["msg1", "msg2", "timeout"]
357
+ expected = senders.zip(messages).map {|s, m| Message.new({:callerid => "caller", :senderid => s, :body => m}, nil, :type => :reply)}
358
+ results = []
359
+ Timeout.stubs(:timeout).yields
360
+ client.stubs(:receive).with("erfs123").returns(*expected)
361
+ Log.expects(:warn).with("Could not receive all responses. Did not receive responses from sender3")
362
+ responded = client.start_receiver("erfs123", senders, 5) do |msg|
363
+ if msg[:body] == "timeout"
364
+ raise Timeout::Error
365
+ end
366
+ results << msg
367
+ end
368
+ results.should == expected[0,2].map {|m| m.payload}
369
+ responded.should == 2
370
+ end
371
+
372
+ it "should not log a warning if accepting all responses" do
373
+ senders = ["sender1", "sender2", "sender3"]
374
+ messages = ["msg1", "msg2", "timeout"]
375
+ expected = senders.zip(messages).map {|s, m| Message.new({:callerid => "caller", :senderid => s, :body => m}, nil, :type => :reply)}
376
+ results = []
377
+ Timeout.stubs(:timeout).yields
378
+ client.stubs(:receive).with("erfs123").returns(*expected)
379
+ Log.expects(:warn).never
380
+ responded = client.start_receiver("erfs123", [], 1) do |msg|
381
+ if msg[:body] == "timeout"
382
+ raise Timeout::Error
383
+ end
384
+ results << msg
385
+ end
386
+ results.should == expected[0,2].map {|m| m.payload}
387
+ responded.should == 2
388
+ end
389
+
390
+ it "should not log a warning if the response count is larger or equal to the expected number of responses" do
391
+ senders = ["sender1", "sender2", "sender3"]
392
+ messages = ["msg1", "msg2", "timeout"]
393
+ expected = senders.zip(messages).map {|s, m| Message.new({:callerid => "caller", :senderid => s, :body => m}, nil, :type => :reply)}
394
+ results = []
395
+ Timeout.stubs(:timeout).yields
396
+ client.stubs(:receive).with("erfs123").returns(*expected)
397
+ Log.expects(:warn).never
398
+ responded = client.start_receiver("erfs123", senders[0,2], 5) do |msg|
399
+ if msg[:body] == "timeout"
400
+ raise Timeout::Error
401
+ end
402
+ results << msg
309
403
  end
310
- results << msg
404
+ results.should == expected[0,2].map {|m| m.payload}
405
+ responded.should == 2
311
406
  end
312
- results.should == ["msg1", "msg2"]
313
- responded.should == 2
314
407
  end
315
408
  end
316
409