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.
@@ -51,7 +51,6 @@ describe RightScale::ApiClient do
51
51
  @version = RightScale::AgentConfig.protocol_version
52
52
  @payload = {:agent_identity => @agent_id}
53
53
  @target = nil
54
- @token = "random token"
55
54
  end
56
55
 
57
56
  context :initialize do
@@ -78,36 +77,38 @@ describe RightScale::ApiClient do
78
77
  context :push do
79
78
  it "makes mapped request" do
80
79
  flexmock(@client).should_receive(:make_request).with(:post, "/audit_entries/111/append", {:detail => "details"},
81
- "update_entry", @token, Hash).and_return(nil).once
82
- @client.push("/auditor/update_entry", @payload.merge(:audit_id => 111, :detail => "details"), @target, @token).should be_nil
80
+ "update_entry", Hash).and_return(nil).once
81
+ @client.push("/auditor/update_entry", @payload.merge(:audit_id => 111, :detail => "details"), @target).should be_nil
83
82
  end
84
83
 
85
- it "does not require token" do
84
+ it "applies request options" do
85
+ options = {:request_uuid => "uuid", :time_to_live => 60}
86
86
  flexmock(@client).should_receive(:make_request).with(:post, "/audit_entries/111/append", {:detail => "details"},
87
- "update_entry", nil, Hash).and_return(nil).once
88
- @client.push("/auditor/update_entry", @payload.merge(:audit_id => 111, :detail => "details"), @target).should be_nil
87
+ "update_entry", on { |a| a[:request_uuid] == "uuid" && a[:time_to_live] == 60 }).and_return(nil).once
88
+ @client.push("/auditor/update_entry", @payload.merge(:audit_id => 111, :detail => "details"), @target, options).should be_nil
89
89
  end
90
90
  end
91
91
 
92
92
  context :request do
93
93
  it "makes mapped request" do
94
94
  flexmock(@client).should_receive(:make_request).with(:post, "/right_net/booter/declare", {:r_s_version => @version},
95
- "declare", @token, Hash).and_return(nil).once
96
- @client.push("/booter/declare", @payload.merge(:r_s_version => @version), @target, @token).should be_nil
95
+ "declare", {}).and_return(nil).once
96
+ @client.request("/booter/declare", @payload.merge(:r_s_version => @version), @target).should be_nil
97
+ end
98
+
99
+ it "applies request options" do
100
+ options = {:request_uuid => "uuid", :time_to_live => 60}
101
+ flexmock(@client).should_receive(:make_request).with(:post, "/right_net/booter/declare", {:r_s_version => @version},
102
+ "declare", on { |a| a[:request_uuid] == "uuid" && a[:time_to_live] == 60 }).and_return(nil).once
103
+ @client.request("/booter/declare", @payload.merge(:r_s_version => @version), @target, options).should be_nil
97
104
  end
98
105
 
99
106
  # Currently not supporting query_tags via RightApi
100
107
  #it "maps query_tags request" do
101
- # flexmock(@client).should_receive(:map_query_tags).with(:post, {:tags => ["a:b=c"]}, "query_tags", @token, Hash).
108
+ # flexmock(@client).should_receive(:map_query_tags).with(:post, {:tags => ["a:b=c"]}, "query_tags", Hash).
102
109
  # and_return({}).once
103
- # @client.request("/router/query_tags", @payload.merge(:tags => ["a:b=c"]), @target, @token).should == {}
110
+ # @client.request("/router/query_tags", @payload.merge(:tags => ["a:b=c"]), @target).should == {}
104
111
  #end
105
-
106
- it "does not require token" do
107
- flexmock(@client).should_receive(:make_request).with(:post, "/right_net/booter/declare", {:r_s_version => @version},
108
- "declare", nil, Hash).and_return(nil).once
109
- @client.push("/booter/declare", @payload.merge(:r_s_version => @version), @target).should be_nil
110
- end
111
112
  end
112
113
 
113
114
  context :support? do
@@ -122,21 +123,22 @@ describe RightScale::ApiClient do
122
123
 
123
124
  context :map_request do
124
125
  it "raises if request type not supported" do
125
- lambda { @client.send(:map_request, "/instance_scheduler/execute", @payload, @token) }.should \
126
+ lambda { @client.send(:map_request, "/instance_scheduler/execute", @payload, {}) }.should \
126
127
  raise_error(ArgumentError, "Unsupported request type: /instance_scheduler/execute")
127
128
  end
128
129
 
129
130
  it "makes request" do
131
+ options = {:request_uuid => "uuid", :time_to_live => 60}
130
132
  flexmock(@client).should_receive(:make_request).with(:post, "/right_net/booter/declare", {:r_s_version => @version},
131
- "declare", @token, Hash).and_return(nil).once
132
- @client.send(:map_request, "/booter/declare", @payload.merge(:r_s_version => @version), @token).should be_nil
133
+ "declare", options).and_return(nil).once
134
+ @client.send(:map_request, "/booter/declare", @payload.merge(:r_s_version => @version), options).should be_nil
133
135
  end
134
136
 
135
137
  it "returns mapped response" do
136
138
  flexmock(@client).should_receive(:make_request).with(:post, "/audit_entries",
137
- {:audit_entry => {:auditee_href => @agent_href, :summary => "summary"}}, "create_entry", @token, Hash).
139
+ {:audit_entry => {:auditee_href => @agent_href, :summary => "summary"}}, "create_entry", Hash).
138
140
  and_return("/api/audit_entries/111").once
139
- @client.send(:map_request, "/auditor/create_entry", @payload.merge(:summary => "summary"), @token).should == "111"
141
+ @client.send(:map_request, "/auditor/create_entry", @payload.merge(:summary => "summary"), {}).should == "111"
140
142
  end
141
143
  end
142
144
 
@@ -181,23 +183,28 @@ describe RightScale::ApiClient do
181
183
  params = {:tags => @tags}
182
184
  params2 = params.merge(:match_all => false, :resource_type => "instances")
183
185
  flexmock(@client).should_receive(:query_by_resource).never
184
- flexmock(@client).should_receive(:make_request).with(:post, "/tags/by_tag", params2, @action, @token, @options).and_return({}).once
185
- @client.send(:map_query_tags, :post, params, @action, @token, @options).should == {}
186
+ flexmock(@client).should_receive(:make_request).
187
+ with(:post, "/tags/by_tag", params2, @action, @options).and_return({}).once
188
+ @client.send(:map_query_tags, :post, params, @action, @options).should == {}
186
189
  end
187
190
 
188
191
  it "appends retrieved hrefs to any specified resource hrefs" do
189
192
  params = {:tags => @tags, :resource_hrefs => @hrefs}
190
193
  params2 = {:resource_hrefs => [@agent_href2, @agent_href]}
191
- flexmock(@client).should_receive(:query_by_tag).with(:post, @tags, @action, @token, @options).and_return([@agent_href]).once
192
- flexmock(@client).should_receive(:make_request).with(:post, "/tags/by_resource", params2, @action, @token, @options).and_return({}).once
193
- @client.send(:map_query_tags, :post, params, @action, @token, @options).should == {}
194
+ flexmock(@client).should_receive(:query_by_tag).
195
+ with(:post, @tags, @action, @options).and_return([@agent_href]).once
196
+ flexmock(@client).should_receive(:make_request).
197
+ with(:post, "/tags/by_resource", params2, @action, @options).and_return({}).once
198
+ @client.send(:map_query_tags, :post, params, @action, @options).should == {}
194
199
  end
195
200
 
196
201
  it "queries for tags for each resource href" do
197
202
  params = {:resource_hrefs => @hrefs}
198
203
  flexmock(@client).should_receive(:query_by_tag).never
199
- flexmock(@client).should_receive(:make_request).with(:post, "/tags/by_resource", params, @action, @token, @options).and_return(@response).once
200
- @client.send(:map_query_tags, :post, params, @action, @token, @options).should == {@agent_href => {"tags" => ["a:b=c", "x:y=z"]}}
204
+ flexmock(@client).should_receive(:make_request).
205
+ with(:post, "/tags/by_resource", params, @action, @options).and_return(@response).once
206
+ @client.send(:map_query_tags, :post, params, @action, @options).
207
+ should == {@agent_href => {"tags" => ["a:b=c", "x:y=z"]}}
201
208
  end
202
209
  end
203
210
 
@@ -208,13 +215,15 @@ describe RightScale::ApiClient do
208
215
  end
209
216
 
210
217
  it "queries for tags using specified tags" do
211
- flexmock(@client).should_receive(:make_request).with(:post, "/tags/by_tag", @params2, @action, @token, @options).and_return({}).once
212
- @client.send(:query_by_tag, :post, @tags, @action, @token, @options).should == []
218
+ flexmock(@client).should_receive(:make_request).
219
+ with(:post, "/tags/by_tag", @params2, @action, @options).and_return({}).once
220
+ @client.send(:query_by_tag, :post, @tags, @action, @options).should == []
213
221
  end
214
222
 
215
223
  it "maps response" do
216
- flexmock(@client).should_receive(:make_request).with(:post, "/tags/by_tag", @params2, @action, @token, @options).and_return(@response).once
217
- @client.send(:query_by_tag, :post, @tags, @action, @token, @options).should == [@agent_href]
224
+ flexmock(@client).should_receive(:make_request).
225
+ with(:post, "/tags/by_tag", @params2, @action, @options).and_return(@response).once
226
+ @client.send(:query_by_tag, :post, @tags, @action, @options).should == [@agent_href]
218
227
  end
219
228
  end
220
229
 
@@ -224,13 +233,16 @@ describe RightScale::ApiClient do
224
233
  end
225
234
 
226
235
  it "queries for tags using specified hrefs" do
227
- flexmock(@client).should_receive(:make_request).with(:post, "/tags/by_resource", @params, @action, @token, @options).and_return({}).once
228
- @client.send(:query_by_resource, :post, @hrefs, @action, @token, @options).should == {}
236
+ flexmock(@client).should_receive(:make_request).
237
+ with(:post, "/tags/by_resource", @params, @action, @options).and_return({}).once
238
+ @client.send(:query_by_resource, :post, @hrefs, @action, @options).should == {}
229
239
  end
230
240
 
231
241
  it "maps response" do
232
- flexmock(@client).should_receive(:make_request).with(:post, "/tags/by_resource", @params, @action, @token, @options).and_return(@response).once
233
- @client.send(:query_by_resource, :post, @hrefs, @action, @token, @options).should == {@agent_href => {"tags" => ["a:b=c", "x:y=z"]}}
242
+ flexmock(@client).should_receive(:make_request).
243
+ with(:post, "/tags/by_resource", @params, @action, @options).and_return(@response).once
244
+ @client.send(:query_by_resource, :post, @hrefs, @action, @options).
245
+ should == {@agent_href => {"tags" => ["a:b=c", "x:y=z"]}}
234
246
  end
235
247
  end
236
248
  end
@@ -214,6 +214,22 @@ describe RightScale::BalancedHttpClient do
214
214
  end
215
215
  end
216
216
 
217
+ context :close do
218
+ before(:each) do
219
+ @client = RightScale::BalancedHttpClient.new(@urls)
220
+ end
221
+
222
+ it "closes HTTP client" do
223
+ flexmock(@client.instance_variable_get(:@http_client)).should_receive(:close).with("terminating").once
224
+ @client.close("terminating").should be true
225
+ end
226
+
227
+ it "does nothing if there is no HTTP client" do
228
+ @client.instance_variable_set(:@http_client, nil)
229
+ @client.close("terminating").should be true
230
+ end
231
+ end
232
+
217
233
  context :request_headers do
218
234
  before(:each) do
219
235
  @request_uuid = "my uuid"
@@ -350,6 +366,26 @@ describe RightScale::BalancedHttpClient do
350
366
  @client.send(:poll_request, @path, @connect_options, @request_options, @request_timeout, @started_at, @used)
351
367
  @used[:host].should == "http://my.com"
352
368
  end
369
+
370
+ it "converts retryable exceptions to NotResponding for poll requests" do
371
+ bad_gateway = RightScale::HttpExceptions.create(502)
372
+ @connection[:expires_at] = @later + 10
373
+ @http_client.instance_variable_get(:@connections)[@path] = @connection
374
+ flexmock(@http_client).should_receive(:poll).with(@connection, @request_options, @stop_at).and_raise(bad_gateway).once
375
+ lambda do
376
+ @client.send(:poll_request, @path, @connect_options, @request_options, @request_timeout, @started_at, @used).should == @response
377
+ end.should raise_error(RightScale::BalancedHttpClient::NotResponding)
378
+ end
379
+
380
+ it "converts RequestTimeout without a status code to NotResponding for poll requests" do
381
+ request_timeout = RestClient::Exceptions::EXCEPTIONS_MAP[408].new
382
+ @connection[:expires_at] = @later + 10
383
+ @http_client.instance_variable_get(:@connections)[@path] = @connection
384
+ flexmock(@http_client).should_receive(:poll).with(@connection, @request_options, @stop_at).and_raise(request_timeout).once
385
+ lambda do
386
+ @client.send(:poll_request, @path, @connect_options, @request_options, @request_timeout, @started_at, @used).should == @response
387
+ end.should raise_error(RightScale::BalancedHttpClient::NotResponding, "Request timeout")
388
+ end
353
389
  end
354
390
  end
355
391
 
@@ -394,7 +430,7 @@ describe RightScale::BalancedHttpClient do
394
430
  @yielded.should == gateway_timeout
395
431
  end
396
432
 
397
- it "uses raise NotResponding if status code is 504 and http_body is nil or empty" do
433
+ it "raises NotResponding if status code is 504 and http_body is nil or empty" do
398
434
  gateway_timeout = RightScale::HttpExceptions.create(504, "")
399
435
  @no_result = RightSupport::Net::NoResult.new("no result", {@url => gateway_timeout})
400
436
  lambda { @client.send(:handle_no_result, @no_result, @url, &@proc) }.should \
@@ -402,7 +438,7 @@ describe RightScale::BalancedHttpClient do
402
438
  @yielded.should == gateway_timeout
403
439
  end
404
440
 
405
- [502, 503].each do |code|
441
+ [408, 502, 503].each do |code|
406
442
  it "uses server name in NotResponding exception if status code is #{code}" do
407
443
  e = RightScale::HttpExceptions.create(code)
408
444
  @no_result = RightSupport::Net::NoResult.new("no result", {@url => e})
@@ -411,6 +447,14 @@ describe RightScale::BalancedHttpClient do
411
447
  end
412
448
  end
413
449
 
450
+ it "raises NotResponding for RequestTimeout even if exception has no status code" do
451
+ request_timeout = RestClient::Exceptions::EXCEPTIONS_MAP[408].new
452
+ @no_result = RightSupport::Net::NoResult.new("no result", {@url => request_timeout})
453
+ lambda { @client.send(:handle_no_result, @no_result, @url, &@proc) }.should \
454
+ raise_error(RightScale::BalancedHttpClient::NotResponding, "Request timeout")
455
+ @yielded.should == request_timeout
456
+ end
457
+
414
458
  it "raises last exception in details if not retryable" do
415
459
  bad_request = RightScale::HttpExceptions.create(400, "bad data")
416
460
  @no_result = RightSupport::Net::NoResult.new("no result", {@url => bad_request})
@@ -35,7 +35,7 @@ describe RightScale::BaseRetryClient do
35
35
  @url = "http://test.com"
36
36
  @timer = flexmock("timer", :cancel => true, :interval= => 0).by_default
37
37
  flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).by_default
38
- @http_client = flexmock("http client", :get => true, :check_health => true).by_default
38
+ @http_client = flexmock("http client", :get => true, :check_health => true, :close => true).by_default
39
39
  flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).by_default
40
40
  @auth_header = {"Authorization" => "Bearer <session>"}
41
41
  @auth_client = AuthClientMock.new(@url, @auth_header)
@@ -163,6 +163,11 @@ describe RightScale::BaseRetryClient do
163
163
  @client.close(:receive)
164
164
  @client.state.should == :closing
165
165
  end
166
+
167
+ it "closes underlying HTTP client connections" do
168
+ @http_client.should_receive(:close).with("terminating").once
169
+ @client.close
170
+ end
166
171
  end
167
172
 
168
173
  context :state= do
@@ -257,11 +262,34 @@ describe RightScale::BaseRetryClient do
257
262
  flexmock(RightScale::BalancedHttpClient).should_receive(:new).with(@url,
258
263
  on { |a| a[:server_name] == "Test" &&
259
264
  a[:api_version] == "2.0" &&
260
- a[:open_timeout] == 1 &&
261
- a[:request_timeout] == 2 &&
262
265
  a[:filter_params] == ["secret"] }).and_return(@http_client).once
263
266
  @client.send(:create_http_client).should == @http_client
264
267
  end
268
+
269
+ it "closes existing client before creating new one" do
270
+ flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).twice
271
+ @client.send(:create_http_client).should == @http_client
272
+ @http_client.should_receive(:close).with("reconnecting").once
273
+ @client.send(:create_http_client)
274
+ end
275
+ end
276
+
277
+ context :close_http_client do
278
+ it "closes HTTP client" do
279
+ flexmock(@client.instance_variable_get(:@http_client)).should_receive(:close).with("terminating").once
280
+ @client.send(:close_http_client, "terminating").should be true
281
+ end
282
+
283
+ it "does nothing if there is no HTTP client" do
284
+ @client.instance_variable_set(:@http_client, nil)
285
+ @client.send(:close_http_client, "terminating").should be true
286
+ end
287
+
288
+ it "logs any close exceptions" do
289
+ @log.should_receive(:error).with("Failed closing connection", RuntimeError, :trace).once
290
+ flexmock(@client.instance_variable_get(:@http_client)).should_receive(:close).and_raise(RuntimeError).once
291
+ @client.send(:close_http_client, "terminating").should be false
292
+ end
265
293
  end
266
294
 
267
295
  context :enable_use do
@@ -389,6 +417,7 @@ describe RightScale::BaseRetryClient do
389
417
  @params = {:some => "data"}
390
418
  @request_uuid = "random uuid"
391
419
  @now = Time.now
420
+ @expires_at = @now + 25
392
421
  flexmock(Time).should_receive(:now).and_return(@now).by_default
393
422
  flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@request_uuid)
394
423
  flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
@@ -418,16 +447,26 @@ describe RightScale::BaseRetryClient do
418
447
  a[:request_timeout] == 35 &&
419
448
  a[:request_uuid] == "uuid" &&
420
449
  a[:headers] == @auth_header }).once
421
- @client.send(:make_request, :get, @path, @params, nil, "uuid")
450
+ @client.send(:make_request, :get, @path, @params, nil, :request_uuid => "uuid")
422
451
  end
423
452
 
424
453
  it "overrides HTTP options with those supplied on request" do
425
454
  @http_client.should_receive(:get).with(@path, @params,
426
455
  on { |a| a[:open_timeout] == 2 &&
427
456
  a[:request_timeout] == 20 &&
428
- a[:request_uuid] == "uuid" &&
429
457
  a[:headers] == @auth_header }).once
430
- @client.send(:make_request, :get, @path, @params, nil, "uuid", {:request_timeout => 20})
458
+ @client.send(:make_request, :get, @path, @params, nil, {:request_timeout => 20})
459
+ end
460
+
461
+ it "sets X-Expires-At header if time-to-live specified" do
462
+ @http_client.should_receive(:get).with(@path, @params,
463
+ on { |a| a[:headers] == @auth_header.merge("X-Expires-At" => @now + 99) }).once
464
+ @client.send(:make_request, :get, @path, @params, nil, :time_to_live => 99)
465
+ end
466
+
467
+ it "does not set X-Expires-At header if time-to-live is non-positive" do
468
+ @http_client.should_receive(:get).with(@path, @params, on { |a| a[:headers] == @auth_header }).once
469
+ @client.send(:make_request, :get, @path, @params, nil, :time_to_live => -1)
431
470
  end
432
471
 
433
472
  it "makes request using HTTP client" do
@@ -446,23 +485,37 @@ describe RightScale::BaseRetryClient do
446
485
  context "when exception" do
447
486
  it "handles any exceptions" do
448
487
  @http_client.should_receive(:get).and_raise(StandardError, "test").once
449
- flexmock(@client).should_receive(:handle_exception).with(StandardError, "type", @request_uuid, @now, 1).
488
+ flexmock(@client).should_receive(:handle_exception).with(StandardError, "type", @request_uuid, @expires_at, 1).
450
489
  and_raise(StandardError, "failed").once
451
490
  lambda { @client.send(:make_request, :get, @path, @params, "type") }.should raise_error(StandardError, "failed")
452
491
  end
453
492
 
454
493
  it "uses path for request type if no request type specified" do
455
494
  @http_client.should_receive(:get).and_raise(StandardError, "test").once
456
- flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @now, 1).
495
+ flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @expires_at, 1).
457
496
  and_raise(StandardError, "failed").once
458
497
  lambda { @client.send(:make_request, :get, @path, @params) }.should raise_error(StandardError, "failed")
459
498
  end
460
499
 
500
+ it "uses specified time-to-live to control how long to retry if less than configured retry timeout" do
501
+ @http_client.should_receive(:get).and_raise(StandardError, "test").once
502
+ flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @now + 19, 1).
503
+ and_raise(StandardError, "failed").once
504
+ lambda { @client.send(:make_request, :get, @path, @params, nil, :time_to_live => 19) }.should raise_error(StandardError, "failed")
505
+ end
506
+
507
+ it "uses configure retry timeout to control how long to retry if time-to-live exceeds it" do
508
+ @http_client.should_receive(:get).and_raise(StandardError, "test").once
509
+ flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @expires_at, 1).
510
+ and_raise(StandardError, "failed").once
511
+ lambda { @client.send(:make_request, :get, @path, @params, nil, :time_to_live => 99) }.should raise_error(StandardError, "failed")
512
+ end
513
+
461
514
  it "retries if exception handling does not result in raise" do
462
515
  @http_client.should_receive(:get).and_raise(StandardError, "test").twice
463
- flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @now, 1).
516
+ flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, @request_uuid, @expires_at, 1).
464
517
  and_return("updated uuid").once.ordered
465
- flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, "updated uuid", @now, 2).
518
+ flexmock(@client).should_receive(:handle_exception).with(StandardError, @path, "updated uuid", @expires_at, 2).
466
519
  and_raise(StandardError, "failed").once.ordered
467
520
  lambda { @client.send(:make_request, :get, @path, @params) }.should raise_error(StandardError, "failed")
468
521
  end
@@ -478,8 +531,9 @@ describe RightScale::BaseRetryClient do
478
531
  before(:each) do
479
532
  @type = "type"
480
533
  @request_uuid = "random uuid"
481
- @now = Time.now
482
- flexmock(Time).should_receive(:now).and_return(@now, @now + 10, @now + 20)
534
+ @later = (@now = Time.now)
535
+ flexmock(Time).should_receive(:now).and_return { @later += 1 }
536
+ @expires_at = @now + 25
483
537
  flexmock(RightSupport::Data::UUID).should_receive(:generate).and_return(@request_uuid)
484
538
  flexmock(EM::PeriodicTimer).should_receive(:new).and_return(@timer).and_yield
485
539
  @client.instance_variable_set(:@reconnecting, nil)
@@ -492,50 +546,50 @@ describe RightScale::BaseRetryClient do
492
546
  it "handles #{http_code} redirect" do
493
547
  e = RightScale::HttpExceptions.create(http_code, "redirect")
494
548
  flexmock(@client).should_receive(:handle_redirect).with(e, @type, @request_uuid).once
495
- @client.send(:handle_exception, e, @type, @request_uuid, @now, 1)
549
+ @client.send(:handle_exception, e, @type, @request_uuid, @expires_at, 1)
496
550
  end
497
551
  end
498
552
  end
499
553
 
500
554
  it "raises if unauthorized" do
501
555
  e = RightScale::HttpExceptions.create(401, "unauthorized")
502
- lambda { @client.send(:handle_exception, e, @type, @request_uuid, @now, 1) }.should \
556
+ lambda { @client.send(:handle_exception, e, @type, @request_uuid, @expires_at, 1) }.should \
503
557
  raise_error(RightScale::Exceptions::Unauthorized, "unauthorized")
504
558
  end
505
559
 
506
560
  it "notifies auth client and raises retryable if session expired" do
507
561
  e = RightScale::HttpExceptions.create(403, "forbidden")
508
- lambda { @client.send(:handle_exception, e, @type, @request_uuid, @now, 1) }.should \
562
+ lambda { @client.send(:handle_exception, e, @type, @request_uuid, @expires_at, 1) }.should \
509
563
  raise_error(RightScale::Exceptions::RetryableError, "Authorization expired")
510
564
  @auth_client.expired_called.should be_true
511
565
  end
512
566
 
513
567
  it "handles retry with and updates request_uuid to distinguish for retry" do
514
568
  e = RightScale::HttpExceptions.create(449, "retry with")
515
- flexmock(@client).should_receive(:handle_retry_with).with(e, @type, @request_uuid, @now, 1).
569
+ flexmock(@client).should_receive(:handle_retry_with).with(e, @type, @request_uuid, @expires_at, 1).
516
570
  and_return("modified uuid").once
517
- @client.send(:handle_exception, e, @type, @request_uuid, @now, 1).should == "modified uuid"
571
+ @client.send(:handle_exception, e, @type, @request_uuid, @expires_at, 1).should == "modified uuid"
518
572
  end
519
573
 
520
574
  it "handles internal server error" do
521
575
  e = RightScale::HttpExceptions.create(500, "test internal error")
522
- lambda { @client.send(:handle_exception, e, @type, @request_uuid, @now, 1) }.should \
576
+ lambda { @client.send(:handle_exception, e, @type, @request_uuid, @expires_at, 1) }.should \
523
577
  raise_error(RightScale::Exceptions::InternalServerError, "test internal error")
524
578
  end
525
579
 
526
580
  it "handles not responding" do
527
581
  e = RightScale::BalancedHttpClient::NotResponding.new("not responding")
528
- flexmock(@client).should_receive(:handle_not_responding).with(e, @type, @request_uuid, @now, 1).once
529
- @client.send(:handle_exception, e, @type, @request_uuid, @now, 1)
582
+ flexmock(@client).should_receive(:handle_not_responding).with(e, @type, @request_uuid, @expires_at, 1).once
583
+ @client.send(:handle_exception, e, @type, @request_uuid, @expires_at, 1)
530
584
  end
531
585
 
532
586
  it "causes other HTTP exceptions to be re-raised by returning nil" do
533
587
  e = RightScale::HttpExceptions.create(400, "bad request")
534
- @client.send(:handle_exception, e, @type, @request_uuid, @now, 1).should be_nil
588
+ @client.send(:handle_exception, e, @type, @request_uuid, @expires_at, 1).should be_nil
535
589
  end
536
590
 
537
591
  it "causes other non-HTTP exceptions to be re-raised by returning nil" do
538
- @client.send(:handle_exception, StandardError, @type, @request_uuid, @now, 1).should be_nil
592
+ @client.send(:handle_exception, StandardError, @type, @request_uuid, @expires_at, 1).should be_nil
539
593
  end
540
594
  end
541
595
 
@@ -566,29 +620,29 @@ describe RightScale::BaseRetryClient do
566
620
  it "waits for configured interval and does not raise if retry still viable" do
567
621
  @log.should_receive(:error).with(/Retrying type request/).once
568
622
  flexmock(@client).should_receive(:sleep).with(4).once
569
- @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1)
623
+ @client.send(:handle_retry_with, @exception, @type, @request_uuid, @expires_at, 1)
570
624
  end
571
625
 
572
626
  it "returns modified request_uuid" do
573
627
  @log.should_receive(:error)
574
628
  flexmock(@client).should_receive(:sleep)
575
- @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1).should == "#{@request_uuid}:retry"
629
+ @client.send(:handle_retry_with, @exception, @type, @request_uuid, @expires_at, 1).should == "#{@request_uuid}:retry"
576
630
  end
577
631
 
578
632
  it "does not retry more than once" do
579
- lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 2) }.should \
633
+ lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @expires_at, 2) }.should \
580
634
  raise_error(RightScale::Exceptions::RetryableError)
581
635
  end
582
636
 
583
637
  it "raises retryable error if retry timed out" do
584
638
  @client.init(:test, @auth_client, @options.merge(:retry_enabled => true, :retry_timeout => 10))
585
- lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1) }.should \
639
+ lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now + 10, 1) }.should \
586
640
  raise_error(RightScale::Exceptions::RetryableError)
587
641
  end
588
642
 
589
643
  it "raises retryable error if retry disabled" do
590
644
  @client.init(:test, @auth_client, @options.merge(:retry_enabled => false))
591
- lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @now, 1) }.should \
645
+ lambda { @client.send(:handle_retry_with, @exception, @type, @request_uuid, @expires_at, 1) }.should \
592
646
  raise_error(RightScale::Exceptions::RetryableError)
593
647
  end
594
648
  end
@@ -601,18 +655,13 @@ describe RightScale::BaseRetryClient do
601
655
  it "waits for configured interval and does not raise if retry still viable" do
602
656
  @log.should_receive(:error).with(/Retrying type request/).once
603
657
  flexmock(@client).should_receive(:sleep).with(4).once
604
- @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 1)
658
+ @client.send(:handle_not_responding, @exception, @type, @request_uuid, @expires_at, 1)
605
659
  end
606
660
 
607
661
  it "changes wait interval for successive retries" do
608
662
  @log.should_receive(:error).with(/Retrying type request/).once
609
663
  flexmock(@client).should_receive(:sleep).with(12).once
610
- @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 2)
611
- end
612
-
613
- it "does not retry more than configured number of retry intervals" do
614
- lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 4) }.should \
615
- raise_error(RightScale::Exceptions::ConnectivityFailure, "Server not responding after 4 attempts")
664
+ @client.send(:handle_not_responding, @exception, @type, @request_uuid, @expires_at, 2)
616
665
  end
617
666
 
618
667
  it "sets state to :disconnected and raises connectivity error if retry timed out" do
@@ -620,7 +669,7 @@ describe RightScale::BaseRetryClient do
620
669
  # Need to shut off reconnect, otherwise since timers are always yielding,
621
670
  # setting state to :disconnected sets it to :connected
622
671
  flexmock(@client).should_receive(:reconnect).once
623
- lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 3) }.should \
672
+ lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now + 10, 3) }.should \
624
673
  raise_error(RightScale::Exceptions::ConnectivityFailure, "Server not responding after 3 attempts")
625
674
  @client.state.should == :disconnected
626
675
  end
@@ -630,26 +679,47 @@ describe RightScale::BaseRetryClient do
630
679
  # Need to shut off reconnect, otherwise since timers are always yielding,
631
680
  # setting state to :disconnected sets it to :connected
632
681
  flexmock(@client).should_receive(:reconnect).once
633
- lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @now, 1) }.should \
682
+ lambda { @client.send(:handle_not_responding, @exception, @type, @request_uuid, @expires_at, 1) }.should \
634
683
  raise_error(RightScale::Exceptions::ConnectivityFailure, "Server not responding")
635
684
  @client.state.should == :disconnected
636
685
  end
637
686
  end
638
- end
639
687
 
640
- context :wait do
641
- it "waits using timer if non-blocking enabled" do
642
- @fiber = flexmock("fiber", :resume => true).by_default
643
- flexmock(Fiber).should_receive(:current).and_return(@fiber)
644
- flexmock(Fiber).should_receive(:yield).once
645
- flexmock(EM).should_receive(:add_timer).with(1, Proc).and_yield.once
646
- @client.init(:test, @auth_client, @options.merge(:non_blocking => true))
647
- @client.send(:wait, 1).should be true
688
+ context :retry_interval do
689
+ [[1, 4], [2, 12], [3, 36], [4, 36]].each do |attempt, interval|
690
+ it "returns retry interval when should retry after attempt #{attempt}" do
691
+ @client.send(:retry_interval, @now + 120, attempt).should == interval
692
+ end
693
+ end
694
+
695
+ it "returns 0 if another retry would exceed expiration time" do
696
+ @client.send(:retry_interval, @now + 11, 2).should == 0
697
+ end
698
+
699
+ it "returns 0 if exceeded max retries" do
700
+ @client.send(:retry_interval, @expires_at, 2, 1).should == 0
701
+ end
702
+
703
+ it "returns nil if retry disabled" do
704
+ @client.init(:test, @auth_client, @options.merge(:retry_enabled => false))
705
+ @client.send(:retry_interval, @expires_at, 1).should be nil
706
+ end
648
707
  end
649
708
 
650
- it " waits using sleep if non-blocking disabled" do
651
- flexmock(@client).should_receive(:sleep).with(1).once
652
- @client.send(:wait, 1).should be true
709
+ context :wait do
710
+ it "waits using timer if non-blocking enabled" do
711
+ @fiber = flexmock("fiber", :resume => true).by_default
712
+ flexmock(Fiber).should_receive(:current).and_return(@fiber)
713
+ flexmock(Fiber).should_receive(:yield).once
714
+ flexmock(EM).should_receive(:add_timer).with(1, Proc).and_yield.once
715
+ @client.init(:test, @auth_client, @options.merge(:non_blocking => true))
716
+ @client.send(:wait, 1).should be true
717
+ end
718
+
719
+ it " waits using sleep if non-blocking disabled" do
720
+ flexmock(@client).should_receive(:sleep).with(1).once
721
+ @client.send(:wait, 1).should be true
722
+ end
653
723
  end
654
724
  end
655
725
  end