right_agent 2.1.5-x86-mingw32 → 2.2.0-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|