right_agent 2.0.8-x86-mingw32 → 2.1.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.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
|