right_agent 0.13.5 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/right_agent/actors/agent_manager.rb +1 -32
- data/lib/right_agent/agent.rb +243 -230
- data/lib/right_agent/dispatched_cache.rb +4 -5
- data/lib/right_agent/dispatcher.rb +146 -157
- data/lib/right_agent/pid_file.rb +1 -1
- data/lib/right_agent/platform.rb +14 -14
- data/lib/right_agent/scripts/agent_controller.rb +2 -4
- data/lib/right_agent/sender.rb +214 -223
- data/lib/right_agent/serialize/secure_serializer.rb +2 -2
- data/right_agent.gemspec +3 -3
- data/spec/agent_spec.rb +50 -171
- data/spec/dispatched_cache_spec.rb +13 -19
- data/spec/dispatcher_spec.rb +192 -254
- data/spec/sender_spec.rb +212 -168
- metadata +7 -4
data/spec/sender_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2009-
|
2
|
+
# Copyright (c) 2009-2012 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -58,7 +58,6 @@ describe RightScale::Sender do
|
|
58
58
|
|
59
59
|
describe "when monitoring broker connectivity" do
|
60
60
|
before(:each) do
|
61
|
-
flexmock(EM).should_receive(:next_tick).and_yield.by_default
|
62
61
|
@now = Time.at(1000000)
|
63
62
|
flexmock(Time).should_receive(:now).and_return(@now).by_default
|
64
63
|
@broker = flexmock("Broker", :subscribe => true, :publish => ["broker"], :connected? => true,
|
@@ -193,7 +192,135 @@ describe RightScale::Sender do
|
|
193
192
|
end
|
194
193
|
end
|
195
194
|
end
|
196
|
-
|
195
|
+
|
196
|
+
describe "when validating a target" do
|
197
|
+
before(:each) do
|
198
|
+
@timer = flexmock("timer")
|
199
|
+
flexmock(EM::Timer).should_receive(:new).and_return(@timer)
|
200
|
+
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
201
|
+
@broker = flexmock("Broker", :subscribe => true, :publish => true).by_default
|
202
|
+
@agent = flexmock("Agent", :identity => "agent", :broker => @broker).by_default
|
203
|
+
@agent.should_receive(:options).and_return({}).by_default
|
204
|
+
RightScale::Sender.new(@agent)
|
205
|
+
@instance = RightScale::Sender.instance
|
206
|
+
@instance.initialize_offline_queue
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should accept nil target" do
|
210
|
+
@instance.__send__(:validate_target, nil, true).should be_true
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should accept named target" do
|
214
|
+
@instance.__send__(:validate_target, "name", true).should be_true
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "and target is a hash" do
|
218
|
+
|
219
|
+
describe "and selector is allowed" do
|
220
|
+
|
221
|
+
it "should accept :all or :any selector" do
|
222
|
+
@instance.__send__(:validate_target, {:selector => :all}, true).should be_true
|
223
|
+
@instance.__send__(:validate_target, {"selector" => "any"}, true).should be_true
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should reject values other than :all or :any" do
|
227
|
+
lambda { @instance.__send__(:validate_target, {:selector => :other}, true) }.
|
228
|
+
should raise_error(ArgumentError, /Invalid target selector/)
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "and selector is not allowed" do
|
234
|
+
|
235
|
+
it "should reject selector" do
|
236
|
+
lambda { @instance.__send__(:validate_target, {:selector => :all}, false) }.
|
237
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "and tags is specified" do
|
243
|
+
|
244
|
+
it "should accept tags" do
|
245
|
+
@instance.__send__(:validate_target, {:tags => []}, true).should be_true
|
246
|
+
@instance.__send__(:validate_target, {"tags" => ["tag"]}, true).should be_true
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should reject non-array" do
|
250
|
+
lambda { @instance.__send__(:validate_target, {:tags => {}}, true) }.
|
251
|
+
should raise_error(ArgumentError, /Invalid target tags/)
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
|
256
|
+
describe "and scope is specified" do
|
257
|
+
|
258
|
+
it "should accept account" do
|
259
|
+
@instance.__send__(:validate_target, {:scope => {:account => 1}}, true).should be_true
|
260
|
+
@instance.__send__(:validate_target, {"scope" => {"account" => 1}}, true).should be_true
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should accept shard" do
|
264
|
+
@instance.__send__(:validate_target, {:scope => {:shard => 1}}, true).should be_true
|
265
|
+
@instance.__send__(:validate_target, {"scope" => {"shard" => 1}}, true).should be_true
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should accept account and shard" do
|
269
|
+
@instance.__send__(:validate_target, {"scope" => {:shard => 1, "account" => 1}}, true).should be_true
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should reject keys other than account and shard" do
|
273
|
+
target = {"scope" => {:shard => 1, "account" => 1, :other => 2}}
|
274
|
+
lambda { @instance.__send__(:validate_target, target, true) }.
|
275
|
+
should raise_error(ArgumentError, /Invalid target scope/)
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should reject empty hash" do
|
279
|
+
lambda { @instance.__send__(:validate_target, {:scope => {}}, true) }.
|
280
|
+
should raise_error(ArgumentError, /Invalid target scope/)
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "and multiple are specified" do
|
286
|
+
|
287
|
+
it "should accept scope and tags" do
|
288
|
+
@instance.__send__(:validate_target, {:scope => {:shard => 1}, :tags => []}, true).should be_true
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should accept scope, tags, and selector" do
|
292
|
+
target = {:scope => {:shard => 1}, :tags => ["tag"], :selector => :all}
|
293
|
+
@instance.__send__(:validate_target, target, true).should be_true
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should reject selector if not allowed" do
|
297
|
+
target = {:scope => {:shard => 1}, :tags => ["tag"], :selector => :all}
|
298
|
+
lambda { @instance.__send__(:validate_target, target, false) }.
|
299
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should reject keys other than selector, scope, and tags" do
|
305
|
+
target = {:scope => {:shard => 1}, :tags => [], :selector => :all, :other => 2}
|
306
|
+
lambda { @instance.__send__(:validate_target, target, true) }.
|
307
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should reject empty hash" do
|
311
|
+
lambda { @instance.__send__(:validate_target, {}, true) }.
|
312
|
+
should raise_error(ArgumentError, /Invalid target hash/)
|
313
|
+
end
|
314
|
+
|
315
|
+
it "should reject value that is not nil, string, or hash" do
|
316
|
+
lambda { @instance.__send__(:validate_target, [], true) }.
|
317
|
+
should raise_error(ArgumentError, /Invalid target/)
|
318
|
+
end
|
319
|
+
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
323
|
+
|
197
324
|
describe "when making a push request" do
|
198
325
|
before(:each) do
|
199
326
|
@timer = flexmock("timer")
|
@@ -209,22 +336,8 @@ describe RightScale::Sender do
|
|
209
336
|
|
210
337
|
it "should validate target" do
|
211
338
|
@broker.should_receive(:publish)
|
212
|
-
|
213
|
-
|
214
|
-
lambda { @instance.send_push('/foo/bar', nil, {}) }.should be_true
|
215
|
-
lambda { @instance.send_push('/foo/bar', nil, :tags => "tags") }.should be_true
|
216
|
-
lambda { @instance.send_push('/foo/bar', nil, "tags" => "tags") }.should be_true
|
217
|
-
lambda { @instance.send_push('/foo/bar', nil, :tags => "tags", :scope => {:shard => 1}) }.should be_true
|
218
|
-
lambda { @instance.send_push('/foo/bar', nil, "scope" => {:shard => 1, "account" => 1}) }.should be_true
|
219
|
-
lambda { @instance.send_push('/foo/bar', nil, :scope => {}) }.should be_true
|
220
|
-
lambda { @instance.send_push('/foo/bar', nil, :selector => :all) }.should be_true
|
221
|
-
lambda { @instance.send_push('/foo/bar', nil, "selector" => "any") }.should be_true
|
222
|
-
lambda { @instance.send_push('/foo/bar', nil, 1) }.should raise_error(ArgumentError)
|
223
|
-
lambda { @instance.send_push('/foo/bar', nil, []) }.should raise_error(ArgumentError)
|
224
|
-
lambda { @instance.send_push('/foo/bar', nil, :bogus => 1) }.should raise_error(ArgumentError)
|
225
|
-
lambda { @instance.send_push('/foo/bar', nil, :scope => 1) }.should raise_error(ArgumentError)
|
226
|
-
lambda { @instance.send_push('/foo/bar', nil, :scope => {:bogus => 1}) }.should raise_error(ArgumentError)
|
227
|
-
lambda { @instance.send_push('/foo/bar', nil, :selector => :bogus) }.should raise_error(ArgumentError)
|
339
|
+
flexmock(@instance).should_receive(:validate_target).with("target", true).once
|
340
|
+
@instance.send_push('/foo/bar', nil, "target").should be_true
|
228
341
|
end
|
229
342
|
|
230
343
|
it "should create a Push object" do
|
@@ -283,6 +396,15 @@ describe RightScale::Sender do
|
|
283
396
|
@instance.offline_handler.queue.size.should == 1
|
284
397
|
end
|
285
398
|
|
399
|
+
it 'should raise exception if not connected to any brokers and :offline_queueing disabled' do
|
400
|
+
@log.should_receive(:error).with(/Failed to publish request/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
401
|
+
@broker.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers)
|
402
|
+
@agent.should_receive(:options).and_return({:offline_queueing => false})
|
403
|
+
RightScale::Sender.new(@agent)
|
404
|
+
@instance = RightScale::Sender.instance
|
405
|
+
lambda { @instance.send_push('/welcome/aboard', 'iZac') }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
406
|
+
end
|
407
|
+
|
286
408
|
it "should store the response handler if given" do
|
287
409
|
response_handler = lambda {}
|
288
410
|
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return('abc').once
|
@@ -313,6 +435,12 @@ describe RightScale::Sender do
|
|
313
435
|
@instance.pending_requests['xyz'].should_not be_nil
|
314
436
|
@instance.pending_requests['abc'].should be_nil
|
315
437
|
end
|
438
|
+
|
439
|
+
it "should log exceptions and re-raise them" do
|
440
|
+
@log.should_receive(:error).with(/Failed to publish request/, Exception, :trace).once
|
441
|
+
@broker.should_receive(:publish).and_raise(Exception)
|
442
|
+
lambda { @instance.send_push('/welcome/aboard', 'iZac') }.should raise_error(RightScale::Sender::SendFailure)
|
443
|
+
end
|
316
444
|
end
|
317
445
|
|
318
446
|
describe "when making a send_persistent_push request" do
|
@@ -380,7 +508,6 @@ describe RightScale::Sender do
|
|
380
508
|
before(:each) do
|
381
509
|
@timer = flexmock("timer")
|
382
510
|
flexmock(EM::Timer).should_receive(:new).and_return(@timer).by_default
|
383
|
-
flexmock(EM).should_receive(:next_tick).and_yield.by_default
|
384
511
|
@broker_id = "broker"
|
385
512
|
@broker_ids = [@broker_id]
|
386
513
|
@broker = flexmock("Broker", :subscribe => true, :publish => @broker_ids, :connected? => true,
|
@@ -394,33 +521,15 @@ describe RightScale::Sender do
|
|
394
521
|
|
395
522
|
it "should validate target" do
|
396
523
|
@broker.should_receive(:publish)
|
397
|
-
|
398
|
-
|
399
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, {}) }.should be_true
|
400
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :tags => "tags") }.should be_true
|
401
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, "tags" => "tags") }.should be_true
|
402
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :tags => "tags", :scope => {:shard => 1}) }.should be_true
|
403
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, "scope" => {:shard => 1, "account" => 1}) }.should be_true
|
404
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :scope => {}) }.should be_true
|
405
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :selector => :all) }.should raise_error(ArgumentError)
|
406
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, 1) }.should raise_error(ArgumentError)
|
407
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, []) }.should raise_error(ArgumentError)
|
408
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :bogus => 1) }.should raise_error(ArgumentError)
|
409
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :scope => 1) }.should raise_error(ArgumentError)
|
410
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :scope => {:bogus => 1}) }.should raise_error(ArgumentError)
|
411
|
-
lambda { @instance.send_retryable_request('/foo/bar', nil, :selector => :bogus) }.should raise_error(ArgumentError)
|
524
|
+
flexmock(@instance).should_receive(:validate_target).with("target", false).once
|
525
|
+
@instance.send_retryable_request('/foo/bar', nil, "target") {_}.should be_true
|
412
526
|
end
|
413
527
|
|
414
528
|
it "should create a Request object" do
|
415
529
|
@broker.should_receive(:publish).with(hsh(:name => "request"), on do |request|
|
416
530
|
request.class.should == RightScale::Request
|
417
531
|
end, hsh(:persistent => false, :mandatory => true)).once
|
418
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
419
|
-
end
|
420
|
-
|
421
|
-
it "should process request in next tick to preserve pending request data integrity" do
|
422
|
-
flexmock(EM).should_receive(:next_tick).and_yield.once
|
423
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|response|}
|
532
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
424
533
|
end
|
425
534
|
|
426
535
|
it "should set correct attributes on the request message" do
|
@@ -433,7 +542,7 @@ describe RightScale::Sender do
|
|
433
542
|
request.target.should be_nil
|
434
543
|
request.expires_at.should == 1000100
|
435
544
|
end, hsh(:persistent => false, :mandatory => true)).once
|
436
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
545
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
437
546
|
end
|
438
547
|
|
439
548
|
it "should disable time-to-live if disabled in configuration" do
|
@@ -445,14 +554,14 @@ describe RightScale::Sender do
|
|
445
554
|
@broker.should_receive(:publish).with(hsh(:name => "request"), on do |request|
|
446
555
|
request.expires_at.should == 0
|
447
556
|
end, hsh(:persistent => false, :mandatory => true)).once
|
448
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
557
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
449
558
|
end
|
450
559
|
|
451
560
|
it "should set the correct target if specified" do
|
452
561
|
@broker.should_receive(:publish).with(hsh(:name => "request"), on do |request|
|
453
562
|
request.target.should == 'my-target'
|
454
563
|
end, hsh(:persistent => false, :mandatory => true)).once
|
455
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac', 'my-target') {|
|
564
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac', 'my-target') {|_|}
|
456
565
|
end
|
457
566
|
|
458
567
|
it "should set the correct target selectors if specified" do
|
@@ -461,12 +570,12 @@ describe RightScale::Sender do
|
|
461
570
|
request.selector.should == :any
|
462
571
|
request.scope.should == {:account => 123}
|
463
572
|
end, hsh(:persistent => false, :mandatory => true)).once
|
464
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac', :tags => ['tag'], :scope => {:account => 123})
|
573
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac', :tags => ['tag'], :scope => {:account => 123}) {|_|}
|
465
574
|
end
|
466
575
|
|
467
576
|
it "should set up for retrying the request if necessary by default" do
|
468
577
|
flexmock(@instance).should_receive(:publish_with_timeout_retry).once
|
469
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac', 'my-target') {|
|
578
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac', 'my-target') {|_|}
|
470
579
|
end
|
471
580
|
|
472
581
|
it "should store the response handler" do
|
@@ -480,7 +589,7 @@ describe RightScale::Sender do
|
|
480
589
|
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return('abc').once
|
481
590
|
flexmock(Time).should_receive(:now).and_return(Time.at(1000000)).by_default
|
482
591
|
@instance.pending_requests.kind(RightScale::Sender::PendingRequests::REQUEST_KINDS).youngest_age.should be_nil
|
483
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac')
|
592
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
484
593
|
@instance.pending_requests['abc'].receive_time.should == Time.at(1000000)
|
485
594
|
flexmock(Time).should_receive(:now).and_return(Time.at(1000100))
|
486
595
|
@instance.pending_requests.kind(RightScale::Sender::PendingRequests::REQUEST_KINDS).youngest_age.should == 100
|
@@ -494,17 +603,40 @@ describe RightScale::Sender do
|
|
494
603
|
@broker.should_receive(:publish).never
|
495
604
|
@instance.enable_offline_mode
|
496
605
|
@instance.offline_handler.mode.should == :offline
|
497
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac')
|
606
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
498
607
|
@instance.offline_handler.queue.size.should == 1
|
499
608
|
end
|
500
609
|
|
610
|
+
it 'should raise exception if not connected to any brokers and :offline_queueing disabled' do
|
611
|
+
@log.should_receive(:error).with(/Failed to publish request/, RightAMQP::HABrokerClient::NoConnectedBrokers).once
|
612
|
+
@broker.should_receive(:publish).and_raise(RightAMQP::HABrokerClient::NoConnectedBrokers)
|
613
|
+
@agent.should_receive(:options).and_return({:offline_queueing => false})
|
614
|
+
RightScale::Sender.new(@agent)
|
615
|
+
@instance = RightScale::Sender.instance
|
616
|
+
lambda { @instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|} }.should raise_error(RightScale::Sender::TemporarilyOffline)
|
617
|
+
end
|
618
|
+
|
501
619
|
it "should dump the pending requests" do
|
502
620
|
flexmock(RightScale::AgentIdentity).should_receive(:generate).and_return('abc').once
|
503
621
|
flexmock(Time).should_receive(:now).and_return(Time.at(1000000))
|
504
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac')
|
622
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
505
623
|
@instance.dump_requests.should == ["#{Time.at(1000000).localtime} <abc>"]
|
506
624
|
end
|
507
625
|
|
626
|
+
it "should not allow a selector target" do
|
627
|
+
lambda { @instance.send_retryable_request('/welcome/aboard', 'iZac', :selector => :all) }.should raise_error(ArgumentError)
|
628
|
+
end
|
629
|
+
|
630
|
+
it "should raise error if there is no callback block" do
|
631
|
+
lambda { @instance.send_retryable_request('/welcome/aboard', 'iZac') }.should raise_error(ArgumentError)
|
632
|
+
end
|
633
|
+
|
634
|
+
it "should log exceptions and re-raise them" do
|
635
|
+
@log.should_receive(:error).with(/Failed to publish request/, Exception, :trace).once
|
636
|
+
@broker.should_receive(:publish).and_raise(Exception)
|
637
|
+
lambda { @instance.send_retryable_request('/welcome/aboard', 'iZac') {|r|} }.should raise_error(RightScale::Sender::SendFailure)
|
638
|
+
end
|
639
|
+
|
508
640
|
describe "with retry" do
|
509
641
|
it "should not setup for retry if retry_timeout nil" do
|
510
642
|
flexmock(EM).should_receive(:add_timer).never
|
@@ -512,7 +644,7 @@ describe RightScale::Sender do
|
|
512
644
|
RightScale::Sender.new(@agent)
|
513
645
|
@instance = RightScale::Sender.instance
|
514
646
|
@broker.should_receive(:publish).once
|
515
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
647
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
516
648
|
end
|
517
649
|
|
518
650
|
it "should not setup for retry if retry_interval nil" do
|
@@ -521,7 +653,7 @@ describe RightScale::Sender do
|
|
521
653
|
RightScale::Sender.new(@agent)
|
522
654
|
@instance = RightScale::Sender.instance
|
523
655
|
@broker.should_receive(:publish).once
|
524
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
656
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
525
657
|
end
|
526
658
|
|
527
659
|
it "should not setup for retry if publish failed" do
|
@@ -530,7 +662,7 @@ describe RightScale::Sender do
|
|
530
662
|
RightScale::Sender.new(@agent)
|
531
663
|
@instance = RightScale::Sender.instance
|
532
664
|
@broker.should_receive(:publish).and_return([]).once
|
533
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
665
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
534
666
|
end
|
535
667
|
|
536
668
|
it "should setup for retry if retry_timeout and retry_interval not nil and publish successful" do
|
@@ -539,7 +671,7 @@ describe RightScale::Sender do
|
|
539
671
|
RightScale::Sender.new(@agent)
|
540
672
|
@instance = RightScale::Sender.instance
|
541
673
|
@broker.should_receive(:publish).and_return(@broker_ids).once
|
542
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
674
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
543
675
|
end
|
544
676
|
|
545
677
|
it "should adjust retry interval by recent request duration" do
|
@@ -559,12 +691,10 @@ describe RightScale::Sender do
|
|
559
691
|
@instance.send_retryable_request('/welcome/aboard', 'iZac') do |response|
|
560
692
|
result = RightScale::OperationResult.from_results(response)
|
561
693
|
end
|
562
|
-
header = flexmock("amqp header")
|
563
|
-
header.should_receive(:ack).once
|
564
694
|
EM.add_timer(0.15) do
|
565
695
|
@instance.pending_requests.empty?.should be_false
|
566
696
|
result = RightScale::Result.new(token, nil, {'from' => RightScale::OperationResult.success}, nil)
|
567
|
-
@instance.handle_response(result
|
697
|
+
@instance.handle_response(result)
|
568
698
|
end
|
569
699
|
EM.add_timer(0.3) do
|
570
700
|
EM.stop
|
@@ -609,7 +739,7 @@ describe RightScale::Sender do
|
|
609
739
|
@broker.should_receive(:publish).with(hsh(:name => "request"), on do |request|
|
610
740
|
request.expires_at.should == (expires_at ||= request.expires_at)
|
611
741
|
end, hsh(:persistent => false, :mandatory => true)).and_return(@broker_ids).twice
|
612
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|
|
742
|
+
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
613
743
|
EM.add_timer(0.2) { EM.stop }
|
614
744
|
end
|
615
745
|
end
|
@@ -675,7 +805,6 @@ describe RightScale::Sender do
|
|
675
805
|
before(:each) do
|
676
806
|
@timer = flexmock("timer")
|
677
807
|
flexmock(EM::Timer).should_receive(:new).and_return(@timer).by_default
|
678
|
-
flexmock(EM).should_receive(:next_tick).and_yield.by_default
|
679
808
|
@broker_id = "broker"
|
680
809
|
@broker_ids = [@broker_id]
|
681
810
|
@broker = flexmock("Broker", :subscribe => true, :publish => @broker_ids, :connected? => true,
|
@@ -691,7 +820,7 @@ describe RightScale::Sender do
|
|
691
820
|
@broker.should_receive(:publish).with(hsh(:name => "request"), on do |request|
|
692
821
|
request.class.should == RightScale::Request
|
693
822
|
end, hsh(:persistent => true, :mandatory => true)).once
|
694
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac') {|
|
823
|
+
@instance.send_persistent_request('/welcome/aboard', 'iZac') {|_|}
|
695
824
|
end
|
696
825
|
|
697
826
|
it "should set correct attributes on the request message" do
|
@@ -704,14 +833,14 @@ describe RightScale::Sender do
|
|
704
833
|
request.target.should be_nil
|
705
834
|
request.expires_at.should == 0
|
706
835
|
end, hsh(:persistent => true, :mandatory => true)).once
|
707
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac') {|
|
836
|
+
@instance.send_persistent_request('/welcome/aboard', 'iZac') {|_|}
|
708
837
|
end
|
709
838
|
|
710
839
|
it "should set the correct target if specified" do
|
711
840
|
@broker.should_receive(:publish).with(hsh(:name => "request"), on do |request|
|
712
841
|
request.target.should == 'my-target'
|
713
842
|
end, hsh(:persistent => true, :mandatory => true)).once
|
714
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac', 'my-target') {|
|
843
|
+
@instance.send_persistent_request('/welcome/aboard', 'iZac', 'my-target') {|_|}
|
715
844
|
end
|
716
845
|
|
717
846
|
it "should set the correct target selectors if specified" do
|
@@ -720,18 +849,25 @@ describe RightScale::Sender do
|
|
720
849
|
request.selector.should == :any
|
721
850
|
request.scope.should == {:account => 123}
|
722
851
|
end, hsh(:persistent => true, :mandatory => true)).once
|
723
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac', :tags => ['tag'], :scope => {:account => 123})
|
852
|
+
@instance.send_persistent_request('/welcome/aboard', 'iZac', :tags => ['tag'], :scope => {:account => 123}) {|_|}
|
724
853
|
end
|
725
854
|
|
726
855
|
it "should not set up for retrying the request" do
|
727
856
|
flexmock(@instance).should_receive(:publish_with_timeout_retry).never
|
728
|
-
@instance.send_persistent_request('/welcome/aboard', 'iZac', 'my-target') {|
|
857
|
+
@instance.send_persistent_request('/welcome/aboard', 'iZac', 'my-target') {|_|}
|
858
|
+
end
|
859
|
+
|
860
|
+
it "should not allow a selector target" do
|
861
|
+
lambda { @instance.send_retryable_request('/welcome/aboard', 'iZac', :selector => :all) }.should raise_error(ArgumentError)
|
862
|
+
end
|
863
|
+
|
864
|
+
it "should raise error if there is no callback block" do
|
865
|
+
lambda { @instance.send_persistent_request('/welcome/aboard', 'iZac') }.should raise_error(ArgumentError)
|
729
866
|
end
|
730
867
|
end
|
731
868
|
|
732
869
|
describe "when handling a response" do
|
733
870
|
before(:each) do
|
734
|
-
flexmock(EM).should_receive(:next_tick).and_yield.by_default
|
735
871
|
flexmock(EM).should_receive(:defer).and_yield.by_default
|
736
872
|
@broker = flexmock("Broker", :subscribe => true, :publish => ["broker"], :connected? => true,
|
737
873
|
:identity_parts => ["host", 123, 0, 0]).by_default
|
@@ -739,92 +875,61 @@ describe RightScale::Sender do
|
|
739
875
|
RightScale::Sender.new(@agent)
|
740
876
|
@instance = RightScale::Sender.instance
|
741
877
|
flexmock(RightScale::AgentIdentity, :generate => 'token1')
|
742
|
-
@header = flexmock("amqp header")
|
743
878
|
end
|
744
879
|
|
745
880
|
it "should deliver the response for a Request" do
|
746
881
|
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
747
882
|
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
748
|
-
flexmock(@instance).should_receive(:deliver).with(response, RightScale::Sender::PendingRequest
|
749
|
-
@instance.handle_response(response
|
883
|
+
flexmock(@instance).should_receive(:deliver).with(response, RightScale::Sender::PendingRequest).once
|
884
|
+
@instance.handle_response(response)
|
750
885
|
end
|
751
886
|
|
752
887
|
it "should deliver the response for a Push" do
|
753
888
|
@instance.send_push('/welcome/aboard', 'iZac') {|_|}
|
754
889
|
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
755
|
-
flexmock(@instance).should_receive(:deliver).with(response, RightScale::Sender::PendingRequest
|
756
|
-
@instance.handle_response(response
|
890
|
+
flexmock(@instance).should_receive(:deliver).with(response, RightScale::Sender::PendingRequest).once
|
891
|
+
@instance.handle_response(response)
|
757
892
|
end
|
758
893
|
|
759
894
|
it "should not deliver TARGET_NOT_CONNECTED and TTL_EXPIRATION responses for send_retryable_request" do
|
760
|
-
@header.should_receive(:ack).twice
|
761
895
|
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
762
896
|
flexmock(@instance).should_receive(:deliver).never
|
763
897
|
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::TARGET_NOT_CONNECTED)
|
764
898
|
response = RightScale::Result.new('token1', 'to', non_delivery, 'target1')
|
765
|
-
@instance.handle_response(response
|
899
|
+
@instance.handle_response(response)
|
766
900
|
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::TTL_EXPIRATION)
|
767
901
|
response = RightScale::Result.new('token1', 'to', non_delivery, 'target1')
|
768
|
-
@instance.handle_response(response
|
902
|
+
@instance.handle_response(response)
|
769
903
|
end
|
770
904
|
|
771
905
|
it "should record non-delivery regardless of whether there is a response handler" do
|
772
|
-
@header.should_receive(:ack).once
|
773
906
|
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
774
907
|
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::NO_ROUTE_TO_TARGET)
|
775
908
|
response = RightScale::Result.new('token1', 'to', non_delivery, 'target1')
|
776
|
-
@instance.handle_response(response
|
909
|
+
@instance.handle_response(response)
|
777
910
|
@instance.instance_variable_get(:@non_delivery_stats).total.should == 1
|
778
911
|
end
|
779
912
|
|
780
913
|
it "should log non-delivery if there is no response handler" do
|
781
|
-
@header.should_receive(:ack).once
|
782
914
|
@log.should_receive(:info).with(/Non-delivery of/).once
|
783
915
|
@instance.send_push('/welcome/aboard', 'iZac')
|
784
916
|
non_delivery = RightScale::OperationResult.non_delivery(RightScale::OperationResult::NO_ROUTE_TO_TARGET)
|
785
917
|
response = RightScale::Result.new('token1', 'to', non_delivery, 'target1')
|
786
|
-
@instance.handle_response(response
|
918
|
+
@instance.handle_response(response)
|
787
919
|
end
|
788
920
|
|
789
921
|
it "should log a debug message if request no longer pending" do
|
790
|
-
@header.should_receive(:ack).once
|
791
922
|
@log.should_receive(:debug).with(/No pending request for response/).once
|
792
923
|
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
793
924
|
@instance.pending_requests['token1'].should_not be_nil
|
794
925
|
@instance.pending_requests['token2'].should be_nil
|
795
926
|
response = RightScale::Result.new('token2', 'to', RightScale::OperationResult.success, 'target1')
|
796
|
-
@instance.handle_response(response, @header)
|
797
|
-
end
|
798
|
-
|
799
|
-
it "should ack response even if fail while handling it" do
|
800
|
-
@header.should_receive(:ack).once
|
801
|
-
@instance.send_push('/welcome/aboard', 'iZac') {|_|}
|
802
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
803
|
-
flexmock(response).should_receive(:token).and_raise(Exception).once
|
804
|
-
flexmock(@instance).should_receive(:deliver).never
|
805
|
-
lambda { @instance.handle_response(response, @header) }.should raise_error(Exception)
|
806
|
-
end
|
807
|
-
|
808
|
-
it "should not attempt to ack response if fail while handling it and there is no header" do
|
809
|
-
@instance.send_push('/welcome/aboard', 'iZac') {|_|}
|
810
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
811
|
-
exception = Exception.new("test")
|
812
|
-
flexmock(response).should_receive(:token).and_raise(exception).once
|
813
|
-
flexmock(@instance).should_receive(:deliver).never
|
814
|
-
lambda { @instance.handle_response(response, nil) }.should raise_error(Exception, "test")
|
815
|
-
end
|
816
|
-
|
817
|
-
it "should not attempt to ack response if there is no header" do
|
818
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
819
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
820
|
-
flexmock(@instance).should_receive(:deliver).with(response, RightScale::Sender::PendingRequest, nil).once
|
821
927
|
@instance.handle_response(response)
|
822
928
|
end
|
823
929
|
end
|
824
930
|
|
825
931
|
describe "when delivering a response" do
|
826
932
|
before(:each) do
|
827
|
-
flexmock(EM).should_receive(:next_tick).and_yield.by_default
|
828
933
|
flexmock(EM).should_receive(:defer).and_yield.by_default
|
829
934
|
@broker = flexmock("Broker", :subscribe => true, :publish => ["broker"], :connected? => true,
|
830
935
|
:identity_parts => ["host", 123, 0, 0]).by_default
|
@@ -832,15 +937,13 @@ describe RightScale::Sender do
|
|
832
937
|
RightScale::Sender.new(@agent)
|
833
938
|
@instance = RightScale::Sender.instance
|
834
939
|
flexmock(RightScale::AgentIdentity, :generate => 'token1')
|
835
|
-
@header = flexmock("amqp header")
|
836
|
-
@header.should_receive(:ack).once.by_default
|
837
940
|
end
|
838
941
|
|
839
942
|
it "should delete all associated pending Request requests" do
|
840
943
|
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
841
944
|
@instance.pending_requests['token1'].should_not be_nil
|
842
945
|
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
843
|
-
@instance.handle_response(response
|
946
|
+
@instance.handle_response(response)
|
844
947
|
@instance.pending_requests['token1'].should be_nil
|
845
948
|
end
|
846
949
|
|
@@ -848,7 +951,7 @@ describe RightScale::Sender do
|
|
848
951
|
@instance.send_push('/welcome/aboard', 'iZac') {|_|}
|
849
952
|
@instance.pending_requests['token1'].should_not be_nil
|
850
953
|
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
851
|
-
@instance.handle_response(response
|
954
|
+
@instance.handle_response(response)
|
852
955
|
@instance.pending_requests['token1'].should_not be_nil
|
853
956
|
end
|
854
957
|
|
@@ -858,7 +961,7 @@ describe RightScale::Sender do
|
|
858
961
|
@instance.pending_requests['token2'] = @instance.pending_requests['token1'].dup
|
859
962
|
@instance.pending_requests['token2'].retry_parent = 'token1'
|
860
963
|
response = RightScale::Result.new('token2', 'to', RightScale::OperationResult.success, 'target1')
|
861
|
-
@instance.handle_response(response
|
964
|
+
@instance.handle_response(response)
|
862
965
|
@instance.pending_requests['token1'].should be_nil
|
863
966
|
@instance.pending_requests['token2'].should be_nil
|
864
967
|
end
|
@@ -867,67 +970,8 @@ describe RightScale::Sender do
|
|
867
970
|
called = 0
|
868
971
|
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|response| called += 1}
|
869
972
|
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
870
|
-
@instance.handle_response(response, @header)
|
871
|
-
called.should == 1
|
872
|
-
end
|
873
|
-
|
874
|
-
it "should defer the response handler call if not single threaded" do
|
875
|
-
@agent.should_receive(:options).and_return({:single_threaded => false})
|
876
|
-
RightScale::Sender.new(@agent)
|
877
|
-
@instance = RightScale::Sender.instance
|
878
|
-
called = 0
|
879
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|response| called += 1}
|
880
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
881
|
-
flexmock(EM).should_receive(:defer).and_yield.once
|
882
|
-
flexmock(EM).should_receive(:next_tick).never
|
883
|
-
@instance.handle_response(response, @header)
|
884
|
-
called.should == 1
|
885
|
-
end
|
886
|
-
|
887
|
-
it "should not defer the response handler call if single threaded" do
|
888
|
-
@agent.should_receive(:options).and_return({:single_threaded => true})
|
889
|
-
RightScale::Sender.new(@agent)
|
890
|
-
@instance = RightScale::Sender.instance
|
891
|
-
called = 0
|
892
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|response| called += 1}
|
893
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
894
|
-
flexmock(EM).should_receive(:next_tick).and_yield.once
|
895
|
-
flexmock(EM).should_receive(:defer).never
|
896
|
-
@instance.handle_response(response, @header)
|
897
|
-
called.should == 1
|
898
|
-
end
|
899
|
-
|
900
|
-
it "should log an error if the response handler raises an exception but still delete pending request" do
|
901
|
-
@agent.should_receive(:options).and_return({:single_threaded => true})
|
902
|
-
@log.should_receive(:error).with(/Failed processing response/, Exception, :trace).once
|
903
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_| raise Exception}
|
904
|
-
@instance.pending_requests['token1'].should_not be_nil
|
905
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
906
|
-
@instance.handle_response(response, @header)
|
907
|
-
@instance.pending_requests['token1'].should be_nil
|
908
|
-
end
|
909
|
-
|
910
|
-
it "should ack response even if fail while delivering it" do
|
911
|
-
flexmock(EM).should_receive(:defer).and_raise(Exception).once
|
912
|
-
@instance.send_push('/welcome/aboard', 'iZac') {|_|}
|
913
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
914
|
-
lambda { @instance.handle_response(response, @header) }.should raise_error(Exception)
|
915
|
-
end
|
916
|
-
|
917
|
-
it "should not attempt to ack response if fail while delivering it and there is no header" do
|
918
|
-
@header.should_receive(:ack).never
|
919
|
-
exception = Exception.new("test")
|
920
|
-
flexmock(EM).should_receive(:defer).and_raise(exception).once
|
921
|
-
@instance.send_push('/welcome/aboard', 'iZac') {|_|}
|
922
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
923
|
-
lambda { @instance.handle_response(response, nil) }.should raise_error(Exception, "test")
|
924
|
-
end
|
925
|
-
|
926
|
-
it "should not attempt to ack response if there is no header" do
|
927
|
-
@header.should_receive(:ack).never
|
928
|
-
@instance.send_retryable_request('/welcome/aboard', 'iZac') {|_|}
|
929
|
-
response = RightScale::Result.new('token1', 'to', RightScale::OperationResult.success, 'target1')
|
930
973
|
@instance.handle_response(response)
|
974
|
+
called.should == 1
|
931
975
|
end
|
932
976
|
end
|
933
977
|
|