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.
- data/lib/right_agent/agent_tag_manager.rb +17 -9
- data/lib/right_agent/clients/api_client.rb +28 -27
- data/lib/right_agent/clients/balanced_http_client.rb +25 -5
- data/lib/right_agent/clients/base_retry_client.rb +76 -42
- data/lib/right_agent/clients/blocking_client.rb +11 -1
- data/lib/right_agent/clients/non_blocking_client.rb +32 -9
- data/lib/right_agent/clients/right_http_client.rb +14 -8
- data/lib/right_agent/clients/router_client.rb +41 -14
- data/lib/right_agent/command/command_client.rb +1 -0
- data/lib/right_agent/http_exceptions.rb +6 -1
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +2 -2
- data/lib/right_agent/offline_handler.rb +22 -9
- data/lib/right_agent/retryable_request.rb +18 -12
- data/lib/right_agent/scripts/agent_controller.rb +9 -3
- data/lib/right_agent/sender.rb +94 -61
- data/right_agent.gemspec +2 -2
- data/spec/agent_tag_manager_spec.rb +59 -14
- data/spec/clients/api_client_spec.rb +48 -36
- data/spec/clients/balanced_http_client_spec.rb +46 -2
- data/spec/clients/base_retry_client_spec.rb +118 -48
- data/spec/clients/blocking_client_spec.rb +16 -0
- data/spec/clients/non_blocking_client_spec.rb +43 -6
- data/spec/clients/router_client_spec.rb +54 -49
- data/spec/http_exceptions_spec.rb +7 -0
- data/spec/offline_handler_spec.rb +22 -11
- data/spec/retryable_request_spec.rb +35 -20
- data/spec/sender_spec.rb +185 -122
- metadata +3 -3
@@ -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",
|
82
|
-
@client.push("/auditor/update_entry", @payload.merge(:audit_id => 111, :detail => "details"), @target
|
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 "
|
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",
|
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",
|
96
|
-
@client.
|
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",
|
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
|
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,
|
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",
|
132
|
-
@client.send(:map_request, "/booter/declare", @payload.merge(:r_s_version => @version),
|
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",
|
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"),
|
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).
|
185
|
-
|
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).
|
192
|
-
|
193
|
-
@client.
|
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).
|
200
|
-
|
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).
|
212
|
-
|
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).
|
217
|
-
|
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).
|
228
|
-
|
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).
|
233
|
-
|
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 "
|
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,
|
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, @
|
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, @
|
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, @
|
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", @
|
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
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
529
|
-
@client.send(:handle_exception, e, @type, @request_uuid, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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, @
|
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
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
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
|
-
|
651
|
-
|
652
|
-
|
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
|