right_agent 2.1.5 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2009-2013 RightScale Inc
2
+ # Copyright (c) 2009-2014 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
@@ -75,14 +75,14 @@ module RightScale
75
75
  # :offline_queueing(Boolean):: Whether to queue request if client currently disconnected,
76
76
  # also requires agent invocation of initialize_offline_queue and start_offline_queue methods below,
77
77
  # as well as enable_offline_mode and disable_offline_mode as client connection status changes
78
- # :ping_interval(Integer):: Minimum number of seconds since last message receipt to ping RightNet
78
+ # :ping_interval(Numeric):: Minimum number of seconds since last message receipt to ping RightNet
79
79
  # to check connectivity, defaults to 0 meaning do not ping
80
80
  # :restart_callback(Proc):: Callback that is activated on each restart vote with votes being initiated
81
81
  # by offline queue exceeding MAX_QUEUED_REQUESTS or by repeated failures to access RightNet when online
82
82
  # :retry_timeout(Numeric):: Maximum number of seconds to retry request before give up
83
83
  # :retry_interval(Numeric):: Number of seconds before initial request retry, increases exponentially
84
- # :time_to_live(Integer):: Number of seconds before a request expires and is to be ignored
85
- # by the receiver, 0 means never expire
84
+ # :time_to_live(Numeric):: Number of seconds before a request expires and is to be ignored;
85
+ # non-positive value means never expire
86
86
  # :async_response(Boolean):: Whether to handle responses asynchronously or to handle them immediately
87
87
  # upon arrival (for use by applications that were written expecting asynchronous AMQP responses)
88
88
  # :secure(Boolean):: true indicates to use Security features of rabbitmq to restrict agents to themselves
@@ -188,7 +188,10 @@ module RightScale
188
188
  # ones with no shard id
189
189
  # :selector(Symbol):: Which of the matched targets to be selected, either :any or :all,
190
190
  # defaults to :any
191
- # token(String|NilClass):: Token uniquely identifying request; defaults to random generated
191
+ # options(Hash):: Request options
192
+ # :token(String):: Universally unique ID for request; defaults to random generated
193
+ # :time_to_live(Numeric):: Number of seconds before a request expires and is to be ignored;
194
+ # non-positive value or nil means never expire; defaults to 0
192
195
  #
193
196
  # === Block
194
197
  # Optional block used to process routing responses asynchronously with the following parameter:
@@ -205,8 +208,8 @@ module RightScale
205
208
  # SendFailure:: If sending of request failed unexpectedly
206
209
  # TemporarilyOffline:: If cannot send request because RightNet client currently disconnected
207
210
  # and offline queueing is disabled
208
- def send_push(type, payload = nil, target = nil, token = nil, &callback)
209
- build_and_send_packet(:send_push, type, payload, target, token, callback)
211
+ def send_push(type, payload = nil, target = nil, options = {}, &callback)
212
+ build_and_send_packet(:send_push, type, payload, target, options, &callback)
210
213
  end
211
214
 
212
215
  # Send a request to a single target with a response expected
@@ -231,7 +234,10 @@ module RightScale
231
234
  # :account(Integer):: Restrict to agents with this account id
232
235
  # :shard(Integer):: Restrict to agents with this shard id, or if value is Packet::GLOBAL,
233
236
  # ones with no shard id
234
- # token(String|NilClass):: Token uniquely identifying request; defaults to random generated
237
+ # options(Hash):: Request options
238
+ # :token(String):: Universally unique ID for request; defaults to random generated
239
+ # :time_to_live(Numeric):: Number of seconds before a request expires and is to be ignored;
240
+ # non-positive value or nil means never expire; defaults to configured :time_to_live
235
241
  #
236
242
  # === Block
237
243
  # Required block used to process response asynchronously with the following parameter:
@@ -243,9 +249,9 @@ module RightScale
243
249
  #
244
250
  # === Raise
245
251
  # ArgumentError:: If target invalid or block missing
246
- def send_request(type, payload = nil, target = nil, token = nil, &callback)
252
+ def send_request(type, payload = nil, target = nil, options = {}, &callback)
247
253
  raise ArgumentError, "Missing block for response callback" unless callback
248
- build_and_send_packet(:send_request, type, payload, target, token, callback)
254
+ build_and_send_packet(:send_request, type, payload, target, options, &callback)
249
255
  end
250
256
 
251
257
  # Build and send packet
@@ -254,7 +260,7 @@ module RightScale
254
260
  # kind(Symbol):: Kind of request: :send_push or :send_request
255
261
  # type(String):: Dispatch route for the request; typically identifies actor and action
256
262
  # payload(Object):: Data to be sent with marshalling en route
257
- # target(Hash|NilClass):: Identity of specific target as string, or hash for selecting targets
263
+ # target(Hash|NilClass):: Target for request
258
264
  # :agent_id(String):: Identity of specific target
259
265
  # :tags(Array):: Tags that must all be associated with a target for it to be selected
260
266
  # :scope(Hash):: Scoping to be used to restrict routing
@@ -262,20 +268,27 @@ module RightScale
262
268
  # :shard(Integer):: Restrict to agents with this shard id, or if value is Packet::GLOBAL,
263
269
  # ones with no shard id
264
270
  # :selector(Symbol):: Which of the matched targets to be selected: :any or :all
265
- # token(String|NilClass):: Token uniquely identifying request; defaults to random generated
266
- # callback(Proc|nil):: Block used to process routing response
271
+ # options(Hash):: Request options
272
+ # :token(String):: Universally unique ID for request; defaults to random generated
273
+ # :time_to_live(Numeric):: Number of seconds before a request expires and is to be ignored;
274
+ # non-positive value or nil means never expire for :send_push and means use configured
275
+ # time-to-live for :send_request
276
+ #
277
+ # === Block
278
+ # Optional block used to process response asynchronously with the following parameter:
279
+ # result(Result):: Response with an OperationResult of SUCCESS, RETRY, NON_DELIVERY, or ERROR
267
280
  #
268
281
  # === Return
269
282
  # true:: Always return true
270
283
  #
271
284
  # === Raise
272
285
  # ArgumentError:: If target invalid
273
- def build_and_send_packet(kind, type, payload, target, token, callback)
274
- if (packet = build_packet(kind, type, payload, target, token, callback))
286
+ def build_and_send_packet(kind, type, payload, target, options = {}, &callback)
287
+ if (packet = build_packet(kind, type, payload, target, options, &callback))
275
288
  action = type.split('/').last
276
289
  received_at = @request_stats.update(action, packet.token)
277
290
  @request_kind_stats.update((packet.selector == :all ? "fanout" : kind.to_s)[5..-1])
278
- send("#{@mode}_send", kind, target, packet, received_at, callback)
291
+ send("#{@mode}_send", kind, target, packet, received_at, &callback)
279
292
  end
280
293
  true
281
294
  end
@@ -286,7 +299,7 @@ module RightScale
286
299
  # kind(Symbol):: Kind of request: :send_push or :send_request
287
300
  # type(String):: Dispatch route for the request; typically identifies actor and action
288
301
  # payload(Object):: Data to be sent with marshalling en route
289
- # target(Hash|NilClass):: Identity of specific target as string, or hash for selecting targets
302
+ # target(Hash|NilClass):: Target for request
290
303
  # :agent_id(String):: Identity of specific target
291
304
  # :tags(Array):: Tags that must all be associated with a target for it to be selected
292
305
  # :scope(Hash):: Scoping to be used to restrict routing
@@ -294,43 +307,52 @@ module RightScale
294
307
  # :shard(Integer):: Restrict to agents with this shard id, or if value is Packet::GLOBAL,
295
308
  # ones with no shard id
296
309
  # :selector(Symbol):: Which of the matched targets to be selected: :any or :all
297
- # token(String|NilClass):: Token uniquely identifying request; defaults to random generated
298
- # callback(Boolean):: Whether this request has an associated response callback
310
+ # options(Hash):: Request options
311
+ # :token(String):: Universally unique ID for request; defaults to random generated
312
+ # :time_to_live(Numeric):: Number of seconds before a request expires and is to be ignored;
313
+ # non-positive value or nil means never expire for :send_push and means use configured
314
+ # time-to-live for :send_request
315
+ #
316
+ # === Block
317
+ # Optional block used to process response asynchronously with the following parameter:
318
+ # result(Result):: Response with an OperationResult of SUCCESS, RETRY, NON_DELIVERY, or ERROR
299
319
  #
300
320
  # === Return
301
321
  # (Push|Request|NilClass):: Packet created, or nil if queued instead
302
322
  #
303
323
  # === Raise
304
324
  # ArgumentError:: If target is invalid
305
- def build_packet(kind, type, payload, target, token, callback = false)
325
+ def build_packet(kind, type, payload, target, options = {}, &callback)
306
326
  validate_target(target, kind == :send_push)
307
- if queueing?
308
- @offline_handler.queue_request(kind, type, payload, target, callback)
309
- nil
327
+ if kind == :send_push
328
+ packet = Push.new(type, payload)
329
+ packet.selector = target[:selector] || :any if target.is_a?(Hash)
330
+ packet.persistent = true
331
+ packet.confirm = true if callback
332
+ time_to_live = options[:time_to_live] || 0
310
333
  else
311
- if kind == :send_push
312
- packet = Push.new(type, payload)
313
- packet.selector = target[:selector] || :any if target.is_a?(Hash)
314
- packet.persistent = true
315
- packet.confirm = true if callback
316
- else
317
- packet = Request.new(type, payload)
318
- ttl = @options[:time_to_live]
319
- packet.expires_at = Time.now.to_i + ttl if ttl && ttl != 0
320
- packet.selector = :any
321
- end
322
- packet.from = @identity
323
- packet.token = token || RightSupport::Data::UUID.generate
324
- if target.is_a?(Hash)
325
- if (agent_id = target[:agent_id])
326
- packet.target = agent_id
327
- else
328
- packet.tags = target[:tags] || []
329
- packet.scope = target[:scope]
330
- end
334
+ packet = Request.new(type, payload)
335
+ packet.selector = :any
336
+ time_to_live = options[:time_to_live] || @options[:time_to_live]
337
+ end
338
+ packet.from = @identity
339
+ packet.token = options[:token] || RightSupport::Data::UUID.generate
340
+ packet.expires_at = Time.now.to_i + time_to_live if time_to_live && time_to_live > 0
341
+ if target.is_a?(Hash)
342
+ if (agent_id = target[:agent_id])
343
+ packet.target = agent_id
331
344
  else
332
- packet.target = target
345
+ packet.tags = target[:tags] || []
346
+ packet.scope = target[:scope]
333
347
  end
348
+ else
349
+ packet.target = target
350
+ end
351
+
352
+ if queueing?
353
+ @offline_handler.queue_request(kind, type, payload, target, packet.token, packet.expires_at, &callback)
354
+ nil
355
+ else
334
356
  packet
335
357
  end
336
358
  end
@@ -577,25 +599,28 @@ module RightScale
577
599
  #
578
600
  # === Parameters
579
601
  # kind(Symbol):: Kind of request: :send_push or :send_request
580
- # target(Hash|String|nil):: Target for request
602
+ # target(Hash|NilClass):: Target for request
581
603
  # packet(Push|Request):: Request packet to send
582
604
  # received_at(Time):: Time when request received
583
- # callback(Proc|nil):: Block used to process response
605
+ #
606
+ # === Block
607
+ # Optional block used to process response asynchronously with the following parameter:
608
+ # result(Result):: Response with an OperationResult of SUCCESS, RETRY, NON_DELIVERY, or ERROR
584
609
  #
585
610
  # === Return
586
611
  # true:: Always return true
587
- def http_send(kind, target, packet, received_at, callback)
612
+ def http_send(kind, target, packet, received_at, &callback)
588
613
  if @options[:async_response]
589
614
  EM_S.next_tick do
590
615
  begin
591
- http_send_once(kind, target, packet, received_at, callback)
616
+ http_send_once(kind, target, packet, received_at, &callback)
592
617
  rescue Exception => e
593
618
  Log.error("Failed sending or handling response for #{packet.trace} #{packet.type}", e, :trace)
594
619
  @exception_stats.track("request", e)
595
620
  end
596
621
  end
597
622
  else
598
- http_send_once(kind, target, packet, received_at, callback)
623
+ http_send_once(kind, target, packet, received_at, &callback)
599
624
  end
600
625
  true
601
626
  end
@@ -604,22 +629,27 @@ module RightScale
604
629
  #
605
630
  # === Parameters
606
631
  # kind(Symbol):: Kind of request: :send_push or :send_request
607
- # target(Hash|String|nil):: Target for request
632
+ # target(Hash|NilClass):: Target for request
608
633
  # packet(Push|Request):: Request packet to send
609
634
  # received_at(Time):: Time when request received
610
- # callback(Proc|nil):: Block used to process response
635
+ #
636
+ # === Block
637
+ # Optional block used to process response asynchronously with the following parameter:
638
+ # result(Result):: Response with an OperationResult of SUCCESS, RETRY, NON_DELIVERY, or ERROR
611
639
  #
612
640
  # === Return
613
641
  # true:: Always return true
614
- def http_send_once(kind, target, packet, received_at, callback)
642
+ def http_send_once(kind, target, packet, received_at, &callback)
615
643
  begin
616
644
  method = packet.class.name.split("::").last.downcase
617
- result = success_result(@agent.client.send(method, packet.type, packet.payload, target, packet.token))
645
+ options = {:request_uuid => packet.token}
646
+ options[:time_to_live] = (packet.expires_at - Time.now.to_i) if packet.expires_at > 0
647
+ result = success_result(@agent.client.send(method, packet.type, packet.payload, target, options))
618
648
  rescue Exceptions::Unauthorized => e
619
649
  result = error_result(e.message)
620
650
  rescue Exceptions::ConnectivityFailure => e
621
651
  if queueing?
622
- @offline_handler.queue_request(kind, packet.type, packet.payload, target, callback)
652
+ @offline_handler.queue_request(kind, packet.type, packet.payload, target, packet.token, packet.expires_at, &callback)
623
653
  result = nil
624
654
  else
625
655
  result = retry_result(e.message)
@@ -631,7 +661,7 @@ module RightScale
631
661
  rescue Exceptions::Terminating => e
632
662
  result = nil
633
663
  rescue StandardError => e
634
- # These errors are either unexpected errors or RestClient errors with an http_body
664
+ # These errors are either unexpected errors or HttpExceptions with an http_body
635
665
  # giving details about the error that are conveyed in the error_result
636
666
  if e.respond_to?(:http_body)
637
667
  # No need to log here since any HTTP request errors have already been logged
@@ -658,14 +688,17 @@ module RightScale
658
688
  #
659
689
  # === Parameters
660
690
  # kind(Symbol):: Kind of request: :send_push or :send_request
661
- # target(Hash|String|nil):: Target for request
662
- # received_at(Time):: Time when request received
691
+ # target(Hash|NilClass):: Target for request
663
692
  # packet(Push|Request):: Request packet to send
664
- # callback(Proc|nil):: Block used to process response
693
+ # received_at(Time):: Time when request received
694
+ #
695
+ # === Block
696
+ # Optional block used to process response asynchronously with the following parameter:
697
+ # result(Result):: Response with an OperationResult of SUCCESS, RETRY, NON_DELIVERY, or ERROR
665
698
  #
666
699
  # === Return
667
700
  # true:: Always return true
668
- def amqp_send(kind, target, packet, received_at, callback)
701
+ def amqp_send(kind, target, packet, received_at, &callback)
669
702
  begin
670
703
  @pending_requests[packet.token] = PendingRequest.new(kind, received_at, callback) if callback
671
704
  if packet.class == Request
@@ -676,7 +709,7 @@ module RightScale
676
709
  rescue TemporarilyOffline => e
677
710
  if queueing?
678
711
  # Queue request until come back online
679
- @offline_handler.queue_request(kind, packet.type, packet.payload, target, callback)
712
+ @offline_handler.queue_request(kind, packet.type, packet.payload, target, packet.token, packet.expires_at, &callback)
680
713
  @pending_requests.delete(packet.token) if callback
681
714
  else
682
715
  # Send retry response so that requester, e.g., RetryableRequest, can retry
@@ -751,7 +784,7 @@ module RightScale
751
784
  if @pending_requests[parent_token]
752
785
  count += 1
753
786
  elapsed += interval
754
- if elapsed < @retry_timeout
787
+ if elapsed < @retry_timeout && (packet.expires_at <= 0 || Time.now.to_i < packet.expires_at)
755
788
  packet.tries << packet.token
756
789
  packet.token = RightSupport::Data::UUID.generate
757
790
  @pending_requests[parent_token].retry_parent_token = parent_token if count == 1
data/right_agent.gemspec CHANGED
@@ -25,8 +25,8 @@ require 'rbconfig'
25
25
 
26
26
  Gem::Specification.new do |spec|
27
27
  spec.name = 'right_agent'
28
- spec.version = '2.1.5'
29
- spec.date = '2014-04-15'
28
+ spec.version = '2.2.0'
29
+ spec.date = '2014-05-06'
30
30
  spec.authors = ['Lee Kirchhoff', 'Raphael Simon', 'Tony Spataro', 'Scott Messier']
31
31
  spec.email = 'lee@rightscale.com'
32
32
  spec.homepage = 'https://github.com/rightscale/right_agent'
@@ -60,6 +60,14 @@ describe RightScale::AgentTagManager do
60
60
  @result.should == [@tag]
61
61
  end
62
62
 
63
+ it "applies timeout when retrieving agent tags" do
64
+ @retryable_request.should_receive(:new).with("/router/query_tags",
65
+ {:agent_identity => @identity, :hrefs => [@agent_href]}, {:timeout => 30}).and_return(@request).once
66
+ @request.should_receive(:callback).and_yield({@agent_href => {"tags" => [@tag]}}).once
67
+ @manager.tags(:timeout => 30) { |r| @result = r }
68
+ @result.should == [@tag]
69
+ end
70
+
63
71
  it "retrieves current agent tags using agent ID if not in :http mode" do
64
72
  @agent.should_receive(:mode).and_return(:amqp)
65
73
  @retryable_request.should_receive(:new).with("/router/query_tags",
@@ -123,6 +131,13 @@ describe RightScale::AgentTagManager do
123
131
  @result.should == {@agent_href => {"tags" => @tags}}
124
132
  end
125
133
 
134
+ it "applies timeout when querying for agents with tags" do
135
+ @retryable_request.should_receive(:new).with("/router/query_tags",
136
+ {:agent_identity => @identity, :tags => [@tag]}, {:timeout => 30}).and_return(@request).once
137
+ @request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}, @agent_href => {"tags" => [@tag]}}).once
138
+ @manager.query_tags(@tag, :timeout => 30) { |r| @result = r }
139
+ end
140
+
126
141
  it "forwards options" do
127
142
  @retryable_request.should_receive(:new).with("/router/query_tags",
128
143
  {:agent_identity => @identity, :tags => @tags}, {:timeout => 9}).and_return(@request).once
@@ -181,6 +196,13 @@ describe RightScale::AgentTagManager do
181
196
  @result.should == "raw response"
182
197
  end
183
198
 
199
+ it "applies timeout when querying" do
200
+ @retryable_request.should_receive(:new).with("/router/query_tags",
201
+ {:agent_identity => @identity, :hrefs => @hrefs, :tags => @tags}, {:timeout => 30}).and_return(@request).once
202
+ @request.should_receive(:callback).and_yield({@agent_href => {"tags" => @tags}}).once
203
+ @manager.query_tags_raw(@tags, @hrefs, :timeout => 30) { |r| @result = r }
204
+ end
205
+
184
206
  it "forwards timeout option" do
185
207
  @retryable_request.should_receive(:new).with("/router/query_tags",
186
208
  {:agent_identity => @identity, :tags => @tags}, {:timeout => 9}).and_return(@request).once
@@ -199,17 +221,22 @@ describe RightScale::AgentTagManager do
199
221
  end
200
222
 
201
223
  it "adds individual tag to agent" do
202
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
224
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {}).and_return(@request).once
203
225
  @manager.add_tags(@tag).should be_true
204
226
  end
205
227
 
206
228
  it "adds multiple tags to agent" do
207
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => @tags}).and_return(@request).once
229
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => @tags}, {}).and_return(@request).once
208
230
  @manager.add_tags(@tags).should be_true
209
231
  end
210
232
 
233
+ it "applies timeout when adding tags" do
234
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {:timeout => 30}).and_return(@request).once
235
+ @manager.add_tags(@tag, :timeout => 30).should be_true
236
+ end
237
+
211
238
  it "optionally yields raw response" do
212
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => @tags}).and_return(@request).once
239
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => @tags}, {}).and_return(@request).once
213
240
  @request.should_receive(:callback).and_yield("result").once
214
241
  @manager.add_tags(@tags) { |r| @result = r }
215
242
  @result.should == "raw response"
@@ -218,7 +245,7 @@ describe RightScale::AgentTagManager do
218
245
  it "updates local tags" do
219
246
  @agent.should_receive(:tags).and_return([@tag1]).once
220
247
  @agent.should_receive(:tags=).should_receive([@tag]).once
221
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
248
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {}).and_return(@request).once
222
249
  @manager.add_tags(@tag).should be_true
223
250
  end
224
251
  end
@@ -232,17 +259,22 @@ describe RightScale::AgentTagManager do
232
259
  end
233
260
 
234
261
  it "removes individual tag to agent" do
235
- @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag]}).and_return(@request).once
262
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag]}, {}).and_return(@request).once
236
263
  @manager.remove_tags(@tag).should be_true
237
264
  end
238
265
 
239
266
  it "removes multiple tags to agent" do
240
- @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}).and_return(@request).once
267
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}, {}).and_return(@request).once
241
268
  @manager.remove_tags(@tags).should be_true
242
269
  end
243
270
 
271
+ it "applies timeout when removing tags" do
272
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag]}, {:timeout => 30}).and_return(@request).once
273
+ @manager.remove_tags(@tag, :timeout => 30).should be_true
274
+ end
275
+
244
276
  it "optionally yields raw response" do
245
- @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}).and_return(@request).once
277
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}, {}).and_return(@request).once
246
278
  @request.should_receive(:callback).and_yield("result").once
247
279
  @manager.remove_tags(@tags) { |r| @result = r }
248
280
  @result.should == "raw response"
@@ -251,7 +283,7 @@ describe RightScale::AgentTagManager do
251
283
  it "updates local tags" do
252
284
  @agent.should_receive(:tags).and_return([]).once
253
285
  @agent.should_receive(:tags=).should_receive([@tag]).once
254
- @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag]}).and_return(@request).once
286
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag]}, {}).and_return(@request).once
255
287
  @manager.remove_tags(@tag).should be_true
256
288
  end
257
289
  end
@@ -276,19 +308,24 @@ describe RightScale::AgentTagManager do
276
308
  end
277
309
 
278
310
  it "adds tags for agent" do
279
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
311
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {}).and_return(@request).once
280
312
  @agent.should_receive(:tags=).never
281
313
  @manager.send(:do_update, [@tag], []).should be_true
282
314
  end
283
315
 
284
316
  it "removes tags for agent" do
285
- @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag1]}).and_return(@request).once
317
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag1]}, {}).and_return(@request).once
286
318
  @agent.should_receive(:tags=).never
287
319
  @manager.send(:do_update, [], [@tag1]).should be_true
288
320
  end
289
321
 
322
+ it "applies timeout" do
323
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {:timeout => 30}).and_return(@request).once
324
+ @manager.send(:do_update, [@tag], [], :timeout => 30).should be_true
325
+ end
326
+
290
327
  it "yields raw response if block given" do
291
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
328
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {}).and_return(@request).once
292
329
  @request.should_receive(:raw_response).and_return("raw response").once
293
330
  @agent.should_receive(:tags=).once
294
331
  @manager.send(:do_update, [@tag], []) { |r| @result = r }
@@ -296,7 +333,7 @@ describe RightScale::AgentTagManager do
296
333
  end
297
334
 
298
335
  it "updates local tags if block given and successful" do
299
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
336
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {}).and_return(@request).once
300
337
  @request.should_receive(:raw_response).and_return("raw response").once
301
338
  @agent.should_receive(:tags=).with([@tag]).once
302
339
  @manager.send(:do_update, [@tag], []) { |r| @result = r }
@@ -304,7 +341,7 @@ describe RightScale::AgentTagManager do
304
341
  end
305
342
 
306
343
  it "yields error result and does not update local tags" do
307
- @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
344
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}, {}).and_return(@request).once
308
345
  @request.should_receive(:raw_response).and_return("error").once
309
346
  @request.should_receive(:errback).and_yield("error").once
310
347
  @request.should_receive(:callback).once
@@ -320,9 +357,17 @@ describe RightScale::AgentTagManager do
320
357
  @request.should_receive(:raw_response).and_return("raw response").once
321
358
  @agent.should_receive(:tags).and_return(@tags).twice
322
359
  @agent.should_receive(:tags=).with([]).once
323
- @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}).and_return(@request).once
360
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}, {}).and_return(@request).once
324
361
  @manager.clear { |r| @result = r }
325
362
  @result.should == "raw response"
326
363
  end
364
+
365
+ it "applies timeout" do
366
+ @request.should_receive(:raw_response).and_return("raw response").once
367
+ @agent.should_receive(:tags).and_return(@tags).twice
368
+ @agent.should_receive(:tags=).with([]).once
369
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}, {:timeout => 30}).and_return(@request).once
370
+ @manager.clear(:timeout => 30) { |r| @result = r }
371
+ end
327
372
  end
328
373
  end