right_agent 2.0.8 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/right_agent.rb +1 -2
- data/lib/right_agent/actors/agent_manager.rb +3 -3
- data/lib/right_agent/agent.rb +14 -4
- data/lib/right_agent/clients.rb +10 -0
- data/lib/right_agent/clients/balanced_http_client.rb +210 -122
- data/lib/right_agent/clients/base_retry_client.rb +25 -6
- data/lib/right_agent/clients/blocking_client.rb +155 -0
- data/lib/right_agent/clients/non_blocking_client.rb +198 -0
- data/lib/right_agent/clients/right_http_client.rb +2 -2
- data/lib/right_agent/clients/router_client.rb +205 -80
- data/lib/right_agent/connectivity_checker.rb +1 -1
- data/lib/right_agent/eventmachine_spawn.rb +70 -0
- data/lib/right_agent/exceptions.rb +4 -20
- data/lib/right_agent/http_exceptions.rb +87 -0
- data/lib/right_agent/minimal.rb +1 -0
- data/lib/right_agent/retryable_request.rb +1 -1
- data/lib/right_agent/scripts/agent_controller.rb +12 -6
- data/lib/right_agent/scripts/agent_deployer.rb +6 -0
- data/lib/right_agent/sender.rb +31 -6
- data/right_agent.gemspec +2 -2
- data/spec/agent_spec.rb +9 -6
- data/spec/clients/balanced_http_client_spec.rb +349 -244
- data/spec/clients/base_retry_client_spec.rb +31 -15
- data/spec/clients/blocking_client_spec.rb +265 -0
- data/spec/clients/non_blocking_client_spec.rb +387 -0
- data/spec/clients/router_client_spec.rb +390 -171
- data/spec/http_exceptions_spec.rb +106 -0
- data/spec/retryable_request_spec.rb +13 -13
- data/spec/sender_spec.rb +48 -22
- data/spec/spec_helper.rb +0 -26
- metadata +10 -3
@@ -32,8 +32,8 @@ describe RightScale::RouterClient do
|
|
32
32
|
@log = flexmock(RightScale::Log)
|
33
33
|
@log.should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
34
34
|
@log.should_receive(:warning).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
35
|
-
@timer = flexmock("timer", :cancel => true
|
36
|
-
flexmock(EM::
|
35
|
+
@timer = flexmock("timer", :cancel => true).by_default
|
36
|
+
flexmock(EM::Timer).should_receive(:new).and_return(@timer).by_default
|
37
37
|
@http_client = flexmock("http client", :get => true, :check_health => true).by_default
|
38
38
|
flexmock(RightScale::BalancedHttpClient).should_receive(:new).and_return(@http_client).by_default
|
39
39
|
@websocket = WebSocketClientMock.new
|
@@ -94,13 +94,13 @@ describe RightScale::RouterClient do
|
|
94
94
|
it "makes post request to router" do
|
95
95
|
flexmock(@client).should_receive(:make_request).with(:post, "/push", @params, @action, @token).
|
96
96
|
and_return(nil).once
|
97
|
-
@client.push(@type, @payload, @target, @token).should
|
97
|
+
@client.push(@type, @payload, @target, @token).should be nil
|
98
98
|
end
|
99
99
|
|
100
100
|
it "does not require token" do
|
101
101
|
flexmock(@client).should_receive(:make_request).with(:post, "/push", @params, @action, nil).
|
102
102
|
and_return(nil).once
|
103
|
-
@client.push(@type, @payload, @target).should
|
103
|
+
@client.push(@type, @payload, @target).should be nil
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
@@ -114,7 +114,7 @@ describe RightScale::RouterClient do
|
|
114
114
|
it "does not require token" do
|
115
115
|
flexmock(@client).should_receive(:make_request).with(:post, "/request", @params, @action, nil).
|
116
116
|
and_return(nil).once
|
117
|
-
@client.request(@type, @payload, @target).should
|
117
|
+
@client.request(@type, @payload, @target).should be nil
|
118
118
|
end
|
119
119
|
end
|
120
120
|
end
|
@@ -122,8 +122,9 @@ describe RightScale::RouterClient do
|
|
122
122
|
context "events" do
|
123
123
|
|
124
124
|
before(:each) do
|
125
|
+
@handler = lambda { |_| }
|
125
126
|
@later = Time.at(@now = Time.now)
|
126
|
-
@tick =
|
127
|
+
@tick = 1
|
127
128
|
flexmock(Time).should_receive(:now).and_return { @later += @tick }
|
128
129
|
end
|
129
130
|
|
@@ -137,14 +138,14 @@ describe RightScale::RouterClient do
|
|
137
138
|
|
138
139
|
it "sends using websocket if available" do
|
139
140
|
@client.send(:connect, @routing_keys) { |_| }
|
140
|
-
@client.notify(@event, @routing_keys).should
|
141
|
+
@client.notify(@event, @routing_keys).should be true
|
141
142
|
@websocket.sent.should == JSON.dump(@params)
|
142
143
|
end
|
143
144
|
|
144
145
|
it "makes post request by default" do
|
145
146
|
flexmock(@client).should_receive(:make_request).with(:post, "/notify", @params, "notify", "uuid",
|
146
147
|
{:filter_params => ["event"]}).once
|
147
|
-
@client.notify(@event, @routing_keys).should
|
148
|
+
@client.notify(@event, @routing_keys).should be true
|
148
149
|
end
|
149
150
|
end
|
150
151
|
|
@@ -153,164 +154,323 @@ describe RightScale::RouterClient do
|
|
153
154
|
lambda { @client.listen(@routing_keys) }.should raise_error(ArgumentError, "Block missing")
|
154
155
|
end
|
155
156
|
|
156
|
-
it "
|
157
|
+
it "initializes listen state and starts loop" do
|
158
|
+
flexmock(@client).should_receive(:listen_loop).with(@routing_keys, Proc).and_return(true).once
|
159
|
+
@client.listen(@routing_keys, &@handler).should be true
|
160
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context :close do
|
165
|
+
it "stops listening" do
|
166
|
+
@client.instance_variable_set(:@listen_timer, @timer)
|
167
|
+
@timer.should_receive(:cancel).once
|
157
168
|
@client.close
|
158
|
-
@client.
|
169
|
+
@client.instance_variable_get(:@listen_timer).should be nil
|
170
|
+
@client.instance_variable_get(:@listen_state).should == :cancel
|
159
171
|
end
|
160
172
|
|
161
|
-
it "
|
162
|
-
@client.
|
163
|
-
@client.
|
173
|
+
it "closes websocket" do
|
174
|
+
@client.send(:connect, nil, &@handler)
|
175
|
+
@client.close
|
176
|
+
@websocket.closed.should be true
|
177
|
+
@websocket.code.should == 1001
|
164
178
|
end
|
179
|
+
end
|
165
180
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
@client.
|
170
|
-
@client.instance_variable_get(:@
|
181
|
+
context :update_listen_state do
|
182
|
+
it "cancels timer if state is :cancel" do
|
183
|
+
@client.instance_variable_set(:@listen_timer, @timer)
|
184
|
+
@client.send(:update_listen_state, :cancel).should be true
|
185
|
+
@client.instance_variable_get(:@listen_state).should == :cancel
|
186
|
+
@client.instance_variable_get(:@listen_timer).should be nil
|
171
187
|
end
|
172
188
|
|
173
|
-
it "
|
174
|
-
@client.
|
175
|
-
|
176
|
-
|
177
|
-
@client.
|
189
|
+
it "can handle a re-cancel" do
|
190
|
+
@client.instance_variable_set(:@listen_timer, @timer)
|
191
|
+
@client.send(:update_listen_state, :cancel).should be true
|
192
|
+
@client.send(:update_listen_state, :cancel).should be true
|
193
|
+
@client.instance_variable_get(:@listen_timer).should be nil
|
178
194
|
end
|
179
195
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
196
|
+
[:choose, :check, :connect, :long_poll, :wait].each do |state|
|
197
|
+
it "sets state and timer interval for valid state #{state}" do
|
198
|
+
@client.send(:update_listen_state, state, 10).should be true
|
199
|
+
@client.instance_variable_get(:@listen_state).should == state
|
200
|
+
@client.instance_variable_get(:@listen_interval).should == 10
|
201
|
+
end
|
185
202
|
end
|
186
203
|
|
187
|
-
it "
|
188
|
-
@client.
|
189
|
-
flexmock(@client).should_receive(:retry_connect?).and_return(false)
|
190
|
-
flexmock(@client).should_receive(:long_poll).and_return { @client.close; ["uuid"] }.once
|
191
|
-
@client.listen(@routing_keys) { |_| }.should be_true
|
204
|
+
it "rejects invalid states" do
|
205
|
+
lambda { @client.send(:update_listen_state, :bogus) }.should raise_error(ArgumentError)
|
192
206
|
end
|
193
|
-
end
|
194
207
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
208
|
+
context "and state set to :check" do
|
209
|
+
it "initializes check count" do
|
210
|
+
@client.instance_variable_get(:@listen_checks).should be nil
|
211
|
+
@client.send(:update_listen_state, :check).should be true
|
212
|
+
@client.instance_variable_get(:@listen_checks).should == 0
|
213
|
+
end
|
214
|
+
|
215
|
+
it "only records start of long-polling when state changes" do
|
216
|
+
@client.send(:update_listen_state, :check).should be true
|
217
|
+
@client.instance_variable_get(:@listen_checks).should == 0
|
218
|
+
@client.instance_variable_set(:@listen_checks, nil)
|
219
|
+
@client.send(:update_listen_state, :check)
|
220
|
+
@client.instance_variable_get(:@listen_checks).should be nil
|
221
|
+
end
|
201
222
|
end
|
202
223
|
end
|
203
224
|
|
204
|
-
context :
|
205
|
-
|
206
|
-
|
225
|
+
context :listen_loop do
|
226
|
+
def when_in_listen_state(state, checks = nil, failures = 0)
|
227
|
+
flexmock(EM).should_receive(:next_tick).by_default
|
228
|
+
flexmock(EM::Timer).should_receive(:new).and_return(@timer).by_default
|
229
|
+
@client.send(:update_listen_state, state)
|
230
|
+
@client.instance_variable_set(:@listen_checks, checks) if checks
|
231
|
+
@client.instance_variable_set(:@listen_failures, failures)
|
207
232
|
@client.instance_variable_set(:@connect_interval, 30)
|
233
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
234
|
+
state
|
208
235
|
end
|
209
236
|
|
210
|
-
|
211
|
-
|
212
|
-
|
237
|
+
context "in :choose state" do
|
238
|
+
it "chooses listen method" do
|
239
|
+
when_in_listen_state(:choose)
|
240
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
241
|
+
@client.instance_variable_get(:@listen_state).should == :connect
|
242
|
+
end
|
213
243
|
end
|
214
244
|
|
215
|
-
|
216
|
-
|
217
|
-
|
245
|
+
context "in :check state" do
|
246
|
+
context "and not connected" do
|
247
|
+
before(:each) do
|
248
|
+
when_in_listen_state(:check)
|
249
|
+
@client.instance_variable_set(:@websocket, nil)
|
250
|
+
end
|
251
|
+
|
252
|
+
it "sets state to :connect if router not responding" do
|
253
|
+
flexmock(@client).should_receive(:router_not_responding?).and_return(true).once
|
254
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
255
|
+
@client.instance_variable_get(:@listen_state).should == :connect
|
256
|
+
@client.instance_variable_get(:@listen_interval).should == 4
|
257
|
+
end
|
258
|
+
|
259
|
+
it "otherwise backs off connect interval and sets state to :long_poll" do
|
260
|
+
flexmock(@client).should_receive(:router_not_responding?).and_return(false).once
|
261
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
262
|
+
@client.instance_variable_get(:@connect_interval).should == 60
|
263
|
+
@client.instance_variable_get(:@listen_state).should == :long_poll
|
264
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
context "and connected" do
|
269
|
+
before(:each) do
|
270
|
+
@client.instance_variable_set(:@websocket, @websocket)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "sets state to :choose if have checked enough" do
|
274
|
+
when_in_listen_state(:check, 5)
|
275
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
276
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
277
|
+
@client.instance_variable_get(:@listen_interval).should == 30
|
278
|
+
end
|
279
|
+
|
280
|
+
it "otherwise stays in same state" do
|
281
|
+
when_in_listen_state(:check, 4)
|
282
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
283
|
+
@client.instance_variable_get(:@listen_state).should == :check
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context "in :connect state" do
|
289
|
+
it "tries to connect" do
|
290
|
+
when_in_listen_state(:connect)
|
291
|
+
flexmock(@client).should_receive(:try_connect).with(@routing_keys, Proc).once
|
292
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context "in :long_poll state" do
|
297
|
+
it "tries long-polling if non-blocking enabled" do
|
298
|
+
@client = RightScale::RouterClient.new(@auth_client, :non_blocking => true)
|
299
|
+
when_in_listen_state(:long_poll)
|
300
|
+
event_uuids = ["uuids"]
|
301
|
+
flexmock(@client).should_receive(:try_long_poll).with(@routing_keys, nil, Proc).and_return(event_uuids).once
|
302
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
303
|
+
@client.instance_variable_get(:@event_uuids).should == event_uuids
|
304
|
+
end
|
305
|
+
|
306
|
+
it "otherwise tries deferred long-polling and sets state to :wait" do
|
307
|
+
when_in_listen_state(:long_poll)
|
308
|
+
event_uuids = ["uuids"]
|
309
|
+
@client.instance_variable_set(:@event_uuids, event_uuids)
|
310
|
+
flexmock(@client).should_receive(:try_deferred_long_poll).with(@routing_keys, event_uuids, Proc).once
|
311
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
312
|
+
@client.instance_variable_get(:@listen_state).should == :wait
|
313
|
+
@client.instance_variable_get(:@listen_interval).should == 1
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context "in :wait state" do
|
318
|
+
it "does nothing" do
|
319
|
+
when_in_listen_state(:wait)
|
320
|
+
@client.send(:listen_loop, @routing_keys, &@handler)
|
321
|
+
@client.instance_variable_get(:@listen_state).should == :wait
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context "in :cancel state" do
|
326
|
+
it "returns false" do
|
327
|
+
when_in_listen_state(:cancel)
|
328
|
+
@client.send(:listen_loop, @routing_keys, &@handler).should be false
|
329
|
+
@client.instance_variable_get(:@listen_state).should == :cancel
|
330
|
+
end
|
218
331
|
end
|
219
332
|
|
220
|
-
context "when
|
333
|
+
context "when unexpected exception" do
|
221
334
|
before(:each) do
|
222
|
-
|
335
|
+
when_in_listen_state(:connect)
|
336
|
+
flexmock(@client).should_receive(:try_connect).and_raise(RuntimeError).once
|
223
337
|
end
|
224
338
|
|
225
|
-
it "
|
226
|
-
@
|
227
|
-
@client.
|
228
|
-
@client.send(:retry_connect?).should be_false
|
229
|
-
@client.instance_variable_set(:@last_connect_time, @now - 30)
|
230
|
-
@client.send(:retry_connect?).should be_true
|
339
|
+
it "logs error" do
|
340
|
+
@log.should_receive(:error).with("Failed to listen", RuntimeError, :trace).once
|
341
|
+
@client.send(:listen_loop, @routing_keys, &@handler).should be true
|
231
342
|
end
|
232
343
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
@client.send(:retry_connect?).should be_true
|
238
|
-
end
|
344
|
+
it "resets state to :choose" do
|
345
|
+
@log.should_receive(:error)
|
346
|
+
@client.send(:listen_loop, @routing_keys, &@handler).should be true
|
347
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
239
348
|
end
|
240
349
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
350
|
+
it "fails if exceeded repeated failure limit" do
|
351
|
+
when_in_listen_state(:connect, 1, 10)
|
352
|
+
@log.should_receive(:error).with("Failed to listen", RuntimeError, :trace).once.ordered
|
353
|
+
@log.should_receive(:error).with("Exceeded maximum repeated listen failures (10), stopping listening").once.ordered
|
354
|
+
@client.send(:listen_loop, @routing_keys, &@handler).should be false
|
355
|
+
@client.instance_variable_get(:@listen_state).should == :cancel
|
356
|
+
@client.state.should == :failed
|
248
357
|
end
|
249
358
|
end
|
359
|
+
|
360
|
+
it "uses next_tick for next loop if interval is 0" do
|
361
|
+
when_in_listen_state(:choose)
|
362
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
363
|
+
flexmock(EM).should_receive(:next_tick).and_yield.once
|
364
|
+
flexmock(EM::Timer).should_receive(:new).with(1, Proc).and_return(@timer).once
|
365
|
+
@client.send(:listen_loop, @routing_keys, &@handler).should be true
|
366
|
+
@client.instance_variable_get(:@listen_state).should == :check
|
367
|
+
end
|
368
|
+
|
369
|
+
it "otherwise uses timer for next loop" do
|
370
|
+
when_in_listen_state(:long_poll)
|
371
|
+
flexmock(@client).should_receive(:try_deferred_long_poll).once
|
372
|
+
flexmock(EM::Timer).should_receive(:new).with(1, Proc).and_return(@timer).once
|
373
|
+
flexmock(EM).should_receive(:next_tick).never
|
374
|
+
@client.send(:listen_loop, @routing_keys, &@handler).should be true
|
375
|
+
@client.instance_variable_get(:@listen_state).should == :wait
|
376
|
+
end
|
250
377
|
end
|
251
378
|
|
252
|
-
context :
|
379
|
+
context :choose_listen_method do
|
253
380
|
before(:each) do
|
254
|
-
@handler = lambda { |_| }
|
255
|
-
@client.instance_variable_get(:@websocket).should be_nil
|
256
381
|
@client.instance_variable_set(:@connect_interval, 30)
|
257
|
-
@client.instance_variable_set(:@reconnect_interval, 2)
|
258
382
|
end
|
259
383
|
|
260
|
-
it "
|
261
|
-
|
262
|
-
|
263
|
-
@client.
|
384
|
+
it "chooses long-polling if only it is enabled" do
|
385
|
+
@client = RightScale::RouterClient.new(@auth_client, :long_polling_only => true)
|
386
|
+
@client.send(:choose_listen_method).should be true
|
387
|
+
@client.instance_variable_get(:@listen_state).should == :long_poll
|
388
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
389
|
+
@client.instance_variable_get(:@connect_interval).should == 60 * 60 * 24
|
264
390
|
end
|
265
391
|
|
266
|
-
it "
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
@client.
|
392
|
+
it "chooses to delay choice if already connected" do
|
393
|
+
@client.instance_variable_set(:@websocket, @websocket)
|
394
|
+
@client.send(:choose_listen_method).should be true
|
395
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
396
|
+
@client.instance_variable_get(:@listen_interval).should == 30
|
271
397
|
end
|
272
398
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
@client.instance_variable_set(:@close_code, RightScale::RouterClient::PROTOCOL_ERROR_CLOSE)
|
278
|
-
@client.instance_variable_set(:@close_reason, "Unexpected response code: 502")
|
279
|
-
end.once
|
280
|
-
flexmock(@client).should_receive(:sleep).with(4).and_return { @client.close }.once
|
281
|
-
@client.send(:try_connect, @routing_keys, &@handler)
|
282
|
-
@client.instance_variable_get(:@reconnect_interval).should == 4
|
283
|
-
@client.instance_variable_get(:@connect_interval).should == 30
|
284
|
-
end
|
399
|
+
context "when not connected" do
|
400
|
+
before(:each) do
|
401
|
+
@client.instance_variable_get(:@websocket).should be nil
|
402
|
+
end
|
285
403
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
404
|
+
it "chooses to connect if never connected" do
|
405
|
+
@client.send(:choose_listen_method).should be true
|
406
|
+
@client.instance_variable_get(:@listen_state).should == :connect
|
407
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
408
|
+
end
|
409
|
+
|
410
|
+
context "but previously attempted" do
|
411
|
+
before(:each) do
|
412
|
+
@client.instance_variable_set(:@attempted_connect_at, @now)
|
413
|
+
end
|
414
|
+
|
415
|
+
it "chooses to connect immediately if enough time has elapsed" do
|
416
|
+
@client.instance_variable_set(:@attempted_connect_at, @now - 30)
|
417
|
+
@client.send(:choose_listen_method).should be true
|
418
|
+
@client.instance_variable_get(:@listen_state).should == :connect
|
419
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
420
|
+
end
|
421
|
+
|
422
|
+
[RightScale::RouterClient::NORMAL_CLOSE, RightScale::RouterClient::SHUTDOWN_CLOSE].each do |code|
|
423
|
+
it "chooses to connect immediately if previous close code is #{code}" do
|
424
|
+
@client.instance_variable_set(:@close_code, code)
|
425
|
+
@client.instance_variable_set(:@connect_interval, 300)
|
426
|
+
@client.send(:choose_listen_method).should be true
|
427
|
+
@client.instance_variable_get(:@listen_state).should == :connect
|
428
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
[502, 503].each do |code|
|
433
|
+
it "chooses to connect immediately if previous close code #{code} indicates router not responding" do
|
434
|
+
@client.instance_variable_set(:@close_code, RightScale::RouterClient::PROTOCOL_ERROR_CLOSE)
|
435
|
+
@client.instance_variable_set(:@close_reason, "Unexpected response code: #{code}")
|
436
|
+
@client.instance_variable_set(:@connect_interval, 300)
|
437
|
+
@client.send(:choose_listen_method).should be true
|
438
|
+
@client.instance_variable_get(:@listen_state).should == :connect
|
439
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
it "otherwise it chooses to connect as soon as connect interval expires" do
|
444
|
+
@client.instance_variable_set(:@attempted_connect_at, @now - 28)
|
445
|
+
@client.send(:choose_listen_method).should be true
|
446
|
+
@client.instance_variable_get(:@listen_state).should == :connect
|
447
|
+
@client.instance_variable_get(:@listen_interval).should == 1
|
448
|
+
end
|
449
|
+
end
|
292
450
|
end
|
451
|
+
end
|
293
452
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
@client.
|
453
|
+
context :try_connect do
|
454
|
+
before(:each) do
|
455
|
+
@client.instance_variable_get(:@websocket).should be nil
|
456
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
457
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
299
458
|
end
|
300
459
|
|
301
|
-
it "
|
302
|
-
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket)
|
303
|
-
flexmock(@client).should_receive(:sleep).with(1).times(4).ordered
|
304
|
-
flexmock(@client).should_receive(:sleep).with(1).and_return { @client.close }.once.ordered
|
460
|
+
it "makes websocket connect request and sets state to :check" do
|
461
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket).once
|
305
462
|
@client.send(:try_connect, @routing_keys, &@handler)
|
463
|
+
@client.instance_variable_get(:@listen_state).should == :check
|
464
|
+
@client.instance_variable_get(:@listen_interval).should == 1
|
306
465
|
end
|
307
466
|
|
308
|
-
it "adjusts connect interval if websocket creation fails" do
|
309
|
-
@log.should_receive(:error).with("Failed creating WebSocket",
|
310
|
-
flexmock(Faye::WebSocket::Client).should_receive(:new).and_raise(
|
311
|
-
flexmock(@client).should_receive(:sleep).never
|
467
|
+
it "adjusts connect interval if websocket creation fails and sets state to :long_poll" do
|
468
|
+
@log.should_receive(:error).with("Failed creating WebSocket", RuntimeError).once
|
469
|
+
flexmock(Faye::WebSocket::Client).should_receive(:new).and_raise(RuntimeError).once
|
312
470
|
@client.send(:try_connect, @routing_keys, &@handler)
|
313
471
|
@client.instance_variable_get(:@connect_interval).should == 60
|
472
|
+
@client.instance_variable_get(:@listen_state).should == :long_poll
|
473
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
314
474
|
end
|
315
475
|
end
|
316
476
|
|
@@ -322,7 +482,7 @@ describe RightScale::RouterClient do
|
|
322
482
|
context "when creating connection" do
|
323
483
|
it "connects to router" do
|
324
484
|
flexmock(Faye::WebSocket::Client).should_receive(:new).with(@ws_url + "/connect", nil, Hash).and_return(@websocket).once
|
325
|
-
@client.send(:connect, @routing_keys)
|
485
|
+
@client.send(:connect, @routing_keys, &@handler)
|
326
486
|
end
|
327
487
|
|
328
488
|
it "chooses scheme based on scheme in router URL" do
|
@@ -331,29 +491,29 @@ describe RightScale::RouterClient do
|
|
331
491
|
@auth_client = AuthClientMock.new(@url, @auth_header, :authorized)
|
332
492
|
@client = RightScale::RouterClient.new(@auth_client, @options)
|
333
493
|
flexmock(Faye::WebSocket::Client).should_receive(:new).with(@ws_url + "/connect", nil, Hash).and_return(@websocket).once
|
334
|
-
@client.send(:connect, @routing_keys)
|
494
|
+
@client.send(:connect, @routing_keys, &@handler)
|
335
495
|
end
|
336
496
|
|
337
497
|
it "uses headers containing only API version and authorization" do
|
338
498
|
headers = @auth_header.merge("X-API-Version" => "2.0")
|
339
499
|
flexmock(Faye::WebSocket::Client).should_receive(:new).with(String, nil, hsh(:headers => headers)).and_return(@websocket).once
|
340
|
-
@client.send(:connect, @routing_keys)
|
500
|
+
@client.send(:connect, @routing_keys, &@handler)
|
341
501
|
end
|
342
502
|
|
343
503
|
it "enables ping" do
|
344
504
|
flexmock(Faye::WebSocket::Client).should_receive(:new).with(String, nil, hsh(:ping => 60)).and_return(@websocket).once
|
345
|
-
@client.send(:connect, @routing_keys)
|
505
|
+
@client.send(:connect, @routing_keys, &@handler)
|
346
506
|
end
|
347
507
|
|
348
508
|
it "adds routing keys as query parameters" do
|
349
509
|
url = @ws_url + "/connect" + "?routing_keys[]=a%3Ab%3Dc"
|
350
510
|
flexmock(Faye::WebSocket::Client).should_receive(:new).with(url, nil, Hash).and_return(@websocket).once
|
351
|
-
@client.send(:connect, ["a:b=c"])
|
511
|
+
@client.send(:connect, ["a:b=c"], &@handler)
|
352
512
|
end
|
353
513
|
|
354
514
|
it "returns websocket" do
|
355
515
|
flexmock(Faye::WebSocket::Client).should_receive(:new).and_return(@websocket).once
|
356
|
-
@client.send(:connect, @routing_keys)
|
516
|
+
@client.send(:connect, @routing_keys, &@handler).should == @websocket
|
357
517
|
@client.instance_variable_get(:@websocket).should == @websocket
|
358
518
|
end
|
359
519
|
end
|
@@ -374,7 +534,6 @@ describe RightScale::RouterClient do
|
|
374
534
|
it "logs event" do
|
375
535
|
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
376
536
|
@log.should_receive(:info).with("Received EVENT <uuid> Push /foo/bar from rs-agent-1-1").once.ordered
|
377
|
-
@log.should_receive(:info).with("Sending EVENT <uuid> Push /foo/bar to rs-agent-1-1").once.ordered
|
378
537
|
event = nil
|
379
538
|
@client.send(:connect, @routing_keys) { |e| event = e }
|
380
539
|
@websocket.onmessage(@json_event)
|
@@ -387,19 +546,6 @@ describe RightScale::RouterClient do
|
|
387
546
|
@websocket.sent.should == @json_ack
|
388
547
|
end
|
389
548
|
|
390
|
-
it "sends event response using websocket" do
|
391
|
-
result = {:uuid => "uuid2", :type => "Result", :from => "rs-agent-2-2", :data => {}, :version => @version}
|
392
|
-
@client.send(:connect, @routing_keys) { |_| result }
|
393
|
-
@websocket.onmessage(@json_event)
|
394
|
-
@websocket.sent.should == [@json_ack, JSON.dump({:event => result, :routing_keys => ["rs-agent-1-1"]})]
|
395
|
-
end
|
396
|
-
|
397
|
-
it "only sends non-nil responses" do
|
398
|
-
@client.send(:connect, @routing_keys) { |_| nil }
|
399
|
-
@websocket.onmessage(@json_event)
|
400
|
-
@websocket.sent.should == @json_ack
|
401
|
-
end
|
402
|
-
|
403
549
|
it "logs failures" do
|
404
550
|
@log.should_receive(:error).with("Failed handling WebSocket event", StandardError, :trace).once
|
405
551
|
request = RightScale::Request.new("/foo/bar", "payload")
|
@@ -412,15 +558,15 @@ describe RightScale::RouterClient do
|
|
412
558
|
it "logs info" do
|
413
559
|
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
414
560
|
@log.should_receive(:info).with("WebSocket closed (1000)").once.ordered
|
415
|
-
@client.send(:connect, @routing_keys)
|
561
|
+
@client.send(:connect, @routing_keys, &@handler)
|
416
562
|
@websocket.onclose(1000)
|
417
|
-
@client.instance_variable_get(:@websocket).should
|
563
|
+
@client.instance_variable_get(:@websocket).should be nil
|
418
564
|
end
|
419
565
|
|
420
566
|
it "logged info includes reason if available" do
|
421
567
|
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
422
568
|
@log.should_receive(:info).with("WebSocket closed (1001: Going Away)").once.ordered
|
423
|
-
@client.send(:connect, @routing_keys)
|
569
|
+
@client.send(:connect, @routing_keys, &@handler)
|
424
570
|
@websocket.onclose(1001, "Going Away")
|
425
571
|
end
|
426
572
|
|
@@ -428,7 +574,7 @@ describe RightScale::RouterClient do
|
|
428
574
|
@log.should_receive(:info).with("Creating WebSocket connection to ws://test.com/connect").once.ordered
|
429
575
|
@log.should_receive(:info).and_raise(RuntimeError).once.ordered
|
430
576
|
@log.should_receive(:error).with("Failed closing WebSocket", RuntimeError, :trace).once
|
431
|
-
@client.send(:connect, @routing_keys)
|
577
|
+
@client.send(:connect, @routing_keys, &@handler)
|
432
578
|
@websocket.onclose(1000)
|
433
579
|
end
|
434
580
|
end
|
@@ -436,13 +582,13 @@ describe RightScale::RouterClient do
|
|
436
582
|
context "on error" do
|
437
583
|
it "logs error" do
|
438
584
|
@log.should_receive(:error).with("WebSocket error (Protocol Error)")
|
439
|
-
@client.send(:connect, @routing_keys)
|
585
|
+
@client.send(:connect, @routing_keys, &@handler)
|
440
586
|
@websocket.onerror("Protocol Error")
|
441
587
|
end
|
442
588
|
|
443
589
|
it "does not log if there is no error data" do
|
444
590
|
@log.should_receive(:error).never
|
445
|
-
@client.send(:connect, @routing_keys)
|
591
|
+
@client.send(:connect, @routing_keys, &@handler)
|
446
592
|
@websocket.onerror(nil)
|
447
593
|
end
|
448
594
|
end
|
@@ -450,37 +596,70 @@ describe RightScale::RouterClient do
|
|
450
596
|
|
451
597
|
context :try_long_poll do
|
452
598
|
before(:each) do
|
453
|
-
@
|
454
|
-
@handler = lambda { |_| }
|
599
|
+
@event_uuids = ["uuid"]
|
455
600
|
@client.instance_variable_set(:@connect_interval, 30)
|
456
601
|
@client.instance_variable_set(:@reconnect_interval, 2)
|
457
602
|
end
|
458
603
|
|
459
604
|
it "makes long-polling request" do
|
460
|
-
flexmock(@client).should_receive(:long_poll).with(@routing_keys, @
|
461
|
-
@client.send(:try_long_poll, @routing_keys, @
|
605
|
+
flexmock(@client).should_receive(:long_poll).with(@routing_keys, @event_uuids, @handler).and_return([]).once
|
606
|
+
@client.send(:try_long_poll, @routing_keys, @event_uuids, &@handler).should == []
|
462
607
|
end
|
463
608
|
|
464
609
|
it "returns UUIDs of events received" do
|
465
|
-
flexmock(@client).should_receive(:long_poll).with(@routing_keys, [], @handler).and_return { @
|
466
|
-
@client.send(:try_long_poll, @routing_keys, [], &@handler).should == @
|
610
|
+
flexmock(@client).should_receive(:long_poll).with(@routing_keys, [], @handler).and_return { @event_uuids }.once
|
611
|
+
@client.send(:try_long_poll, @routing_keys, [], &@handler).should == @event_uuids
|
612
|
+
end
|
613
|
+
|
614
|
+
it "returns exception if there is a long-polling failure" do
|
615
|
+
flexmock(@client).should_receive(:long_poll).and_raise(RuntimeError).once
|
616
|
+
@client.send(:try_long_poll, @routing_keys, @event_uuids, &@handler).should be_a RuntimeError
|
467
617
|
end
|
618
|
+
end
|
468
619
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
@client.
|
620
|
+
context :try_deferred_long_poll do
|
621
|
+
before(:each) do
|
622
|
+
@event_uuids = ["uuid"]
|
623
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
624
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
625
|
+
@client.send(:update_listen_state, :long_poll)
|
626
|
+
flexmock(EM).should_receive(:defer).by_default
|
474
627
|
end
|
475
628
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
@client.
|
629
|
+
it "makes long-polling request using defer thread" do
|
630
|
+
flexmock(EM).should_receive(:defer).with(Proc, Proc).once
|
631
|
+
@client.send(:try_deferred_long_poll, @routing_keys, @event_uuids, &@handler).should be true
|
632
|
+
end
|
633
|
+
|
634
|
+
context "defer_operation_proc" do
|
635
|
+
it "long-polls" do
|
636
|
+
@client.instance_variable_set(:@connect_interval, 1)
|
637
|
+
@client.send(:try_deferred_long_poll, @routing_keys, @event_uuids, &@handler)
|
638
|
+
@defer_operation_proc = @client.instance_variable_get(:@defer_operation_proc)
|
639
|
+
flexmock(@client).should_receive(:long_poll).with(@routing_keys, @event_uuids, @handler).and_return([]).once
|
640
|
+
@defer_operation_proc.call.should == []
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
context "defer_callback_proc" do
|
645
|
+
before(:each) do
|
646
|
+
@client.instance_variable_set(:@connect_interval, 1)
|
647
|
+
@client.send(:try_deferred_long_poll, @routing_keys, @event_uuids, &@handler)
|
648
|
+
@defer_callback_proc = @client.instance_variable_get(:@defer_callback_proc)
|
649
|
+
end
|
650
|
+
|
651
|
+
it "handles UUIDs of events received" do
|
652
|
+
@defer_callback_proc.call(@event_uuids).should == @event_uuids
|
653
|
+
@client.instance_variable_get(:@event_uuids).should == @event_uuids
|
654
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
655
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
656
|
+
end
|
657
|
+
|
658
|
+
it "handles exception if there is a long-polling failure" do
|
659
|
+
@log.should_receive(:error).with("Failed long-polling", RuntimeError, :trace).once
|
660
|
+
@defer_callback_proc.call(RuntimeError.new).should be nil
|
661
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
662
|
+
@client.instance_variable_get(:@listen_interval).should == 4
|
484
663
|
end
|
485
664
|
end
|
486
665
|
end
|
@@ -495,22 +674,23 @@ describe RightScale::RouterClient do
|
|
495
674
|
end
|
496
675
|
|
497
676
|
it "makes listen request to router" do
|
498
|
-
flexmock(@client).should_receive(:make_request).with(:
|
677
|
+
flexmock(@client).should_receive(:make_request).with(:poll, "/listen",
|
499
678
|
on { |a| a[:wait_time].should == 55 && !a.key?(:routing_keys) &&
|
500
679
|
a[:timestamp] == @later.to_f }, "listen", nil, Hash).and_return([@event]).once
|
501
|
-
@client.send(:long_poll, @routing_keys, @ack)
|
680
|
+
@client.send(:long_poll, @routing_keys, @ack, &@handler)
|
502
681
|
end
|
503
682
|
|
504
|
-
it "uses listen timeout for request" do
|
505
|
-
|
506
|
-
|
507
|
-
|
683
|
+
it "uses listen timeout for request poll timeout and connect interval for request timeout" do
|
684
|
+
@client.instance_variable_set(:@connect_interval, 300)
|
685
|
+
flexmock(@client).should_receive(:make_request).with(:poll, "/listen", Hash, "listen", nil,
|
686
|
+
{:poll_timeout => 60, :request_timeout => 300, :log_level => :debug}).and_return([@event]).once
|
687
|
+
@client.send(:long_poll, @routing_keys, @ack, &@handler)
|
508
688
|
end
|
509
689
|
|
510
690
|
it "logs event" do
|
511
691
|
@log.should_receive(:info).with("Received EVENT <uuid> Push /foo/bar from rs-agent-1-1").once
|
512
692
|
flexmock(@client).should_receive(:make_request).and_return([@event])
|
513
|
-
@client.send(:long_poll, @routing_keys, @ack)
|
693
|
+
@client.send(:long_poll, @routing_keys, @ack, &@handler)
|
514
694
|
end
|
515
695
|
|
516
696
|
it "presents event to handler" do
|
@@ -533,18 +713,57 @@ describe RightScale::RouterClient do
|
|
533
713
|
flexmock(@client).should_receive(:make_request).and_return(nil)
|
534
714
|
event = nil
|
535
715
|
@client.send(:long_poll, @routing_keys, @ack) { |e| event = e }
|
536
|
-
event.should
|
716
|
+
event.should be nil
|
537
717
|
end
|
538
718
|
|
539
719
|
it "returns event UUIDs" do
|
540
720
|
flexmock(@client).should_receive(:make_request).and_return([@event])
|
541
|
-
@client.send(:long_poll, @routing_keys, @ack)
|
721
|
+
@client.send(:long_poll, @routing_keys, @ack, &@handler).should == ["uuid"]
|
542
722
|
end
|
543
723
|
|
544
724
|
it "returns nil if no events are received" do
|
545
725
|
flexmock(@client).should_receive(:make_request).and_return(nil)
|
546
|
-
@client.send(:long_poll, @routing_keys, @ack)
|
547
|
-
|
726
|
+
@client.send(:long_poll, @routing_keys, @ack, &@handler).should be nil
|
727
|
+
end
|
728
|
+
end
|
729
|
+
|
730
|
+
context :process_long_poll do
|
731
|
+
before(:each) do
|
732
|
+
@event_uuids = ["uuid"]
|
733
|
+
@client.instance_variable_set(:@connect_interval, 30)
|
734
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
735
|
+
@client.send(:update_listen_state, :long_poll)
|
736
|
+
end
|
737
|
+
|
738
|
+
[RightScale::Exceptions::Unauthorized.new("error"),
|
739
|
+
RightScale::Exceptions::ConnectivityFailure.new("error"),
|
740
|
+
RightScale::Exceptions::RetryableError.new("error"),
|
741
|
+
RightScale::Exceptions::InternalServerError.new("error", "server")].each do |e|
|
742
|
+
it "does not trace #{e} exceptions but sets state to :choose" do
|
743
|
+
@log.should_receive(:error).with("Failed long-polling", e, :no_trace).once
|
744
|
+
@client.send(:process_long_poll, e).should be nil
|
745
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
746
|
+
@client.instance_variable_get(:@listen_interval).should == 4
|
747
|
+
end
|
748
|
+
end
|
749
|
+
|
750
|
+
it "traces unexpected exceptions and sets state to :choose" do
|
751
|
+
e = RuntimeError.new
|
752
|
+
@log.should_receive(:error).with("Failed long-polling", e, :trace).once
|
753
|
+
@client.send(:process_long_poll, e).should be nil
|
754
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
755
|
+
@client.instance_variable_get(:@listen_interval).should == 4
|
756
|
+
end
|
757
|
+
|
758
|
+
context "when no exception" do
|
759
|
+
it "sets state to :choose" do
|
760
|
+
@client.instance_variable_set(:@reconnect_interval, 2)
|
761
|
+
@client.send(:process_long_poll, @event_uuids).should == @event_uuids
|
762
|
+
@client.instance_variable_get(:@listen_state).should == :choose
|
763
|
+
@client.instance_variable_get(:@reconnect_interval).should == 2
|
764
|
+
@client.instance_variable_get(:@listen_interval).should == 0
|
765
|
+
end
|
766
|
+
end
|
548
767
|
end
|
549
768
|
|
550
769
|
context :backoff_connect_interval do
|
@@ -587,7 +806,7 @@ describe RightScale::RouterClient do
|
|
587
806
|
it "does not declare not responding for other close codes" do
|
588
807
|
@client.instance_variable_set(:@close_code, RightScale::RouterClient::UNEXPECTED_ERROR_CLOSE)
|
589
808
|
@client.instance_variable_set(:@close_reason, "Unexpected response code: 502")
|
590
|
-
@client.send(:router_not_responding?).should
|
809
|
+
@client.send(:router_not_responding?).should be false
|
591
810
|
end
|
592
811
|
end
|
593
812
|
end
|