opensearch-transport 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +3 -0
  3. data/.gitignore +17 -0
  4. data/Gemfile +47 -0
  5. data/LICENSE +202 -0
  6. data/README.md +551 -0
  7. data/Rakefile +89 -0
  8. data/lib/opensearch/transport/client.rb +354 -0
  9. data/lib/opensearch/transport/redacted.rb +84 -0
  10. data/lib/opensearch/transport/transport/base.rb +450 -0
  11. data/lib/opensearch/transport/transport/connections/collection.rb +136 -0
  12. data/lib/opensearch/transport/transport/connections/connection.rb +169 -0
  13. data/lib/opensearch/transport/transport/connections/selector.rb +101 -0
  14. data/lib/opensearch/transport/transport/errors.rb +100 -0
  15. data/lib/opensearch/transport/transport/http/curb.rb +140 -0
  16. data/lib/opensearch/transport/transport/http/faraday.rb +101 -0
  17. data/lib/opensearch/transport/transport/http/manticore.rb +188 -0
  18. data/lib/opensearch/transport/transport/loggable.rb +94 -0
  19. data/lib/opensearch/transport/transport/response.rb +46 -0
  20. data/lib/opensearch/transport/transport/serializer/multi_json.rb +62 -0
  21. data/lib/opensearch/transport/transport/sniffer.rb +111 -0
  22. data/lib/opensearch/transport/version.rb +31 -0
  23. data/lib/opensearch/transport.rb +46 -0
  24. data/lib/opensearch-transport.rb +27 -0
  25. data/opensearch-transport.gemspec +92 -0
  26. data/spec/opensearch/connections/collection_spec.rb +275 -0
  27. data/spec/opensearch/connections/selector_spec.rb +183 -0
  28. data/spec/opensearch/transport/base_spec.rb +313 -0
  29. data/spec/opensearch/transport/client_spec.rb +1818 -0
  30. data/spec/opensearch/transport/sniffer_spec.rb +284 -0
  31. data/spec/spec_helper.rb +99 -0
  32. data/test/integration/transport_test.rb +108 -0
  33. data/test/profile/client_benchmark_test.rb +141 -0
  34. data/test/test_helper.rb +97 -0
  35. data/test/unit/connection_test.rb +145 -0
  36. data/test/unit/response_test.rb +41 -0
  37. data/test/unit/serializer_test.rb +42 -0
  38. data/test/unit/transport_base_test.rb +673 -0
  39. data/test/unit/transport_curb_test.rb +143 -0
  40. data/test/unit/transport_faraday_test.rb +237 -0
  41. data/test/unit/transport_manticore_test.rb +191 -0
  42. data.tar.gz.sig +1 -0
  43. metadata +456 -0
  44. metadata.gz.sig +1 -0
@@ -0,0 +1,673 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # The OpenSearch Contributors require contributions made to
4
+ # this file be licensed under the Apache-2.0 license or a
5
+ # compatible open source license.
6
+ #
7
+ # Modifications Copyright OpenSearch Contributors. See
8
+ # GitHub history for details.
9
+ #
10
+ # Licensed to Elasticsearch B.V. under one or more contributor
11
+ # license agreements. See the NOTICE file distributed with
12
+ # this work for additional information regarding copyright
13
+ # ownership. Elasticsearch B.V. licenses this file to you under
14
+ # the Apache License, Version 2.0 (the "License"); you may
15
+ # not use this file except in compliance with the License.
16
+ # You may obtain a copy of the License at
17
+ #
18
+ # http://www.apache.org/licenses/LICENSE-2.0
19
+ #
20
+ # Unless required by applicable law or agreed to in writing,
21
+ # software distributed under the License is distributed on an
22
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
+ # KIND, either express or implied. See the License for the
24
+ # specific language governing permissions and limitations
25
+ # under the License.
26
+
27
+ require 'test_helper'
28
+
29
+ class OpenSearch::Transport::Transport::BaseTest < Minitest::Test
30
+
31
+ class EmptyTransport
32
+ include OpenSearch::Transport::Transport::Base
33
+ end
34
+
35
+ class DummyTransport
36
+ include OpenSearch::Transport::Transport::Base
37
+ def __build_connection(host, options={}, block=nil)
38
+ OpenSearch::Transport::Transport::Connections::Connection.new :host => host, :connection => Object.new
39
+ end
40
+ end
41
+
42
+ class DummyTransportPerformer < DummyTransport
43
+ def perform_request(method, path, params={}, body=nil, &block); super; end
44
+ end
45
+
46
+ class DummySerializer
47
+ def initialize(*); end
48
+ end
49
+
50
+ class DummySniffer
51
+ def initialize(*); end
52
+ end
53
+
54
+ context "Transport::Base" do
55
+ should "raise exception when it doesn't implement __build_connection" do
56
+ assert_raise NoMethodError do
57
+ EmptyTransport.new.__build_connection({ :host => 'foo'}, {})
58
+ end
59
+ end
60
+
61
+ should "build connections on initialization" do
62
+ DummyTransport.any_instance.expects(:__build_connections)
63
+ transport = DummyTransport.new
64
+ end
65
+
66
+ should "have default serializer" do
67
+ transport = DummyTransport.new
68
+ assert_instance_of OpenSearch::Transport::Transport::Base::DEFAULT_SERIALIZER_CLASS, transport.serializer
69
+ end
70
+
71
+ should "have custom serializer" do
72
+ transport = DummyTransport.new :options => { :serializer_class => DummySerializer }
73
+ assert_instance_of DummySerializer, transport.serializer
74
+
75
+ transport = DummyTransport.new :options => { :serializer => DummySerializer.new }
76
+ assert_instance_of DummySerializer, transport.serializer
77
+ end
78
+
79
+ should "have default sniffer" do
80
+ transport = DummyTransport.new
81
+ assert_instance_of OpenSearch::Transport::Transport::Sniffer, transport.sniffer
82
+ end
83
+
84
+ should "have custom sniffer" do
85
+ transport = DummyTransport.new :options => { :sniffer_class => DummySniffer }
86
+ assert_instance_of DummySniffer, transport.sniffer
87
+ end
88
+
89
+ context "when combining the URL" do
90
+ setup do
91
+ @transport = DummyTransport.new
92
+ @basic_parts = { :protocol => 'http', :host => 'myhost', :port => 8080 }
93
+ end
94
+
95
+ should "combine basic parts" do
96
+ assert_equal 'http://myhost:8080', @transport.__full_url(@basic_parts)
97
+ end
98
+
99
+ should "combine path" do
100
+ assert_equal 'http://myhost:8080/api', @transport.__full_url(@basic_parts.merge :path => '/api')
101
+ end
102
+
103
+ should "combine authentication credentials" do
104
+ assert_equal 'http://U:P@myhost:8080', @transport.__full_url(@basic_parts.merge :user => 'U', :password => 'P')
105
+ end
106
+
107
+ should "escape the username and password" do
108
+ assert_equal 'http://user%40domain:foo%2Fbar@myhost:8080',
109
+ @transport.__full_url(@basic_parts.merge :user => 'user@domain', :password => 'foo/bar')
110
+ end
111
+ end
112
+ end
113
+
114
+ context "getting a connection" do
115
+ setup do
116
+ @transport = DummyTransportPerformer.new :options => { :reload_connections => 5 }
117
+ @transport.stubs(:connections).returns(stub :get_connection => Object.new)
118
+ @transport.stubs(:sniffer).returns(stub :hosts => [])
119
+ end
120
+
121
+ should "get a connection" do
122
+ assert_not_nil @transport.get_connection
123
+ end
124
+
125
+ should "increment the counter" do
126
+ assert_equal 0, @transport.counter
127
+ 3.times { @transport.get_connection }
128
+ assert_equal 3, @transport.counter
129
+ end
130
+
131
+ should "reload connections when it hits the threshold" do
132
+ @transport.expects(:reload_connections!).twice
133
+ 12.times { @transport.get_connection }
134
+ assert_equal 12, @transport.counter
135
+ end
136
+
137
+ should "not reload connections by default" do
138
+ @transport = DummyTransportPerformer.new
139
+ @transport.stubs(:connections).returns(stub :get_connection => Object.new)
140
+ @transport.expects(:reload_connections!).never
141
+
142
+ 10_010.times { @transport.get_connection }
143
+ assert_equal 10_010, @transport.counter
144
+ end
145
+
146
+ should "not reload connections when the option is set to false" do
147
+ @transport = DummyTransportPerformer.new :options => { :reload_connections => false }
148
+ @transport.stubs(:connections).returns(stub :get_connection => Object.new)
149
+ @transport.expects(:reload_connections!).never
150
+
151
+ 10_010.times { @transport.get_connection }
152
+ assert_equal 10_010, @transport.counter
153
+ end
154
+ end
155
+
156
+ context "performing a request" do
157
+ setup do
158
+ @transport = DummyTransportPerformer.new
159
+ end
160
+
161
+ should "raise an error when no block is passed" do
162
+ assert_raise NoMethodError do
163
+ @transport.peform_request 'GET', '/'
164
+ end
165
+ end
166
+
167
+ should "get the connection" do
168
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
169
+ @transport.perform_request 'GET', '/' do; OpenSearch::Transport::Transport::Response.new 200, 'OK'; end
170
+ end
171
+
172
+ should "raise an error when no connection is available" do
173
+ @transport.expects(:get_connection).returns(nil)
174
+ assert_raise OpenSearch::Transport::Transport::Error do
175
+ @transport.perform_request 'GET', '/' do; OpenSearch::Transport::Transport::Response.new 200, 'OK'; end
176
+ end
177
+ end
178
+
179
+ should "call the passed block" do
180
+ x = 0
181
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
182
+
183
+ @transport.perform_request 'GET', '/' do |connection, url|
184
+ x += 1
185
+ OpenSearch::Transport::Transport::Response.new 200, 'OK'
186
+ end
187
+
188
+ assert_equal 1, x
189
+ end
190
+
191
+ should "deserialize a response JSON body" do
192
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
193
+ @transport.serializer.expects(:load).returns({'foo' => 'bar'})
194
+
195
+ response = @transport.perform_request 'GET', '/' do
196
+ OpenSearch::Transport::Transport::Response.new 200, '{"foo":"bar"}', {"content-type" => 'application/json'}
197
+ end
198
+
199
+ assert_instance_of OpenSearch::Transport::Transport::Response, response
200
+ assert_equal 'bar', response.body['foo']
201
+ end
202
+
203
+ should "not deserialize a response string body" do
204
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
205
+ @transport.serializer.expects(:load).never
206
+ response = @transport.perform_request 'GET', '/' do
207
+ OpenSearch::Transport::Transport::Response.new 200, 'FOOBAR', {"content-type" => 'text/plain'}
208
+ end
209
+
210
+ assert_instance_of OpenSearch::Transport::Transport::Response, response
211
+ assert_equal 'FOOBAR', response.body
212
+ end
213
+
214
+ should "not deserialize an empty response body" do
215
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
216
+ @transport.serializer.expects(:load).never
217
+ response = @transport.perform_request 'GET', '/' do
218
+ OpenSearch::Transport::Transport::Response.new 200, '', {"content-type" => 'application/json'}
219
+ end
220
+
221
+ assert_instance_of OpenSearch::Transport::Transport::Response, response
222
+ assert_equal '', response.body
223
+ end
224
+
225
+ should "serialize non-String objects" do
226
+ @transport.serializer.expects(:dump).times(3)
227
+ @transport.__convert_to_json({:foo => 'bar'})
228
+ @transport.__convert_to_json([1, 2, 3])
229
+ @transport.__convert_to_json(nil)
230
+ end
231
+
232
+ should "not serialize a String object" do
233
+ @transport.serializer.expects(:dump).never
234
+ @transport.__convert_to_json('{"foo":"bar"}')
235
+ end
236
+
237
+ should "raise an error for HTTP status 404" do
238
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
239
+ assert_raise OpenSearch::Transport::Transport::Errors::NotFound do
240
+ @transport.perform_request 'GET', '/' do
241
+ OpenSearch::Transport::Transport::Response.new 404, 'NOT FOUND'
242
+ end
243
+ end
244
+ end
245
+
246
+ should "raise an error for HTTP status 404 with application/json content type" do
247
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
248
+ assert_raise OpenSearch::Transport::Transport::Errors::NotFound do
249
+ @transport.perform_request 'GET', '/' do
250
+ OpenSearch::Transport::Transport::Response.new 404, 'NOT FOUND', {"content-type" => 'application/json'}
251
+ end
252
+ end
253
+ end
254
+
255
+ should "raise an error on server failure" do
256
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
257
+ assert_raise OpenSearch::Transport::Transport::Errors::InternalServerError do
258
+ @transport.perform_request 'GET', '/' do
259
+ OpenSearch::Transport::Transport::Response.new 500, 'ERROR'
260
+ end
261
+ end
262
+ end
263
+
264
+ should "raise an error on connection failure" do
265
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
266
+
267
+ # `block.expects(:call).raises(::Errno::ECONNREFUSED)` fails on Ruby 1.8
268
+ block = lambda { |a,b| raise ::Errno::ECONNREFUSED }
269
+
270
+ assert_raise ::Errno::ECONNREFUSED do
271
+ @transport.perform_request 'GET', '/', &block
272
+ end
273
+ end
274
+
275
+ should 'raise TooManyRequestsError on 429' do
276
+ @transport.expects(:get_connection).returns(stub_everything :failures => 1)
277
+ assert_raise OpenSearch::Transport::Transport::Errors::TooManyRequests do
278
+ @transport.perform_request 'GET', '/' do
279
+ OpenSearch::Transport::Transport::Response.new 429, 'ERROR'
280
+ end
281
+ end
282
+ end
283
+
284
+ should "not raise an error when the :ignore argument has been passed" do
285
+ @transport.stubs(:get_connection).returns(stub_everything :failures => 1)
286
+
287
+ assert_raise OpenSearch::Transport::Transport::Errors::BadRequest do
288
+ @transport.perform_request 'GET', '/' do
289
+ OpenSearch::Transport::Transport::Response.new 400, 'CLIENT ERROR'
290
+ end
291
+ end
292
+
293
+ # No `BadRequest` error
294
+ @transport.perform_request 'GET', '/', :ignore => 400 do
295
+ OpenSearch::Transport::Transport::Response.new 400, 'CLIENT ERROR'
296
+ end
297
+ end
298
+
299
+ should "mark the connection as dead on failure" do
300
+ c = stub_everything :failures => 1
301
+ @transport.expects(:get_connection).returns(c)
302
+
303
+ block = lambda { |a,b| raise ::Errno::ECONNREFUSED }
304
+
305
+ c.expects(:dead!)
306
+
307
+ assert_raise( ::Errno::ECONNREFUSED ) { @transport.perform_request 'GET', '/', &block }
308
+ end
309
+ end
310
+
311
+ context "performing a request with reload connections on connection failures" do
312
+ setup do
313
+ fake_collection = stub_everything :get_connection => stub_everything(:failures => 1),
314
+ :all => stub_everything(:size => 2)
315
+ @transport = DummyTransportPerformer.new :options => { :reload_on_failure => 2 }
316
+ @transport.stubs(:connections).
317
+ returns(fake_collection)
318
+ @block = lambda { |c, u| puts "UNREACHABLE" }
319
+ end
320
+
321
+ should "reload connections when host is unreachable" do
322
+ @block.expects(:call).times(2).
323
+ raises(Errno::ECONNREFUSED).
324
+ then.returns(stub_everything :failures => 1)
325
+
326
+ @transport.expects(:reload_connections!).returns([])
327
+
328
+ @transport.perform_request('GET', '/', &@block)
329
+ assert_equal 2, @transport.counter
330
+ end
331
+ end
332
+
333
+ context "performing a request with retry on connection failures" do
334
+ setup do
335
+ @transport = DummyTransportPerformer.new :options => { :retry_on_failure => true }
336
+ @transport.stubs(:connections).returns(stub :get_connection => stub_everything(:failures => 1))
337
+ @block = Proc.new { |c, u| puts "UNREACHABLE" }
338
+ end
339
+
340
+ should "retry DEFAULT_MAX_RETRIES when host is unreachable" do
341
+ @block.expects(:call).times(4).
342
+ raises(Errno::ECONNREFUSED).
343
+ then.raises(Errno::ECONNREFUSED).
344
+ then.raises(Errno::ECONNREFUSED).
345
+ then.returns(stub_everything :failures => 1)
346
+
347
+ assert_nothing_raised do
348
+ @transport.perform_request('GET', '/', &@block)
349
+ assert_equal 4, @transport.counter
350
+ end
351
+ end
352
+
353
+ should "raise an error after max tries" do
354
+ @block.expects(:call).times(4).
355
+ raises(Errno::ECONNREFUSED).
356
+ then.raises(Errno::ECONNREFUSED).
357
+ then.raises(Errno::ECONNREFUSED).
358
+ then.raises(Errno::ECONNREFUSED).
359
+ then.returns(stub_everything :failures => 1)
360
+
361
+ assert_raise Errno::ECONNREFUSED do
362
+ @transport.perform_request('GET', '/', &@block)
363
+ end
364
+ end
365
+ end
366
+
367
+ context "performing a request with retry on status" do
368
+ setup do
369
+ DummyTransportPerformer.any_instance.stubs(:connections).returns(stub :get_connection => stub_everything(:failures => 1))
370
+
371
+ logger = Logger.new(STDERR)
372
+ logger.level = Logger::DEBUG
373
+ DummyTransportPerformer.any_instance.stubs(:logger).returns(logger)
374
+ @block = Proc.new { |c, u| puts "ERROR" }
375
+ end
376
+
377
+ should "not retry when the status code does not match" do
378
+ @transport = DummyTransportPerformer.new :options => { :retry_on_status => 500 }
379
+ assert_equal [500], @transport.instance_variable_get(:@retry_on_status)
380
+
381
+ @block.expects(:call).
382
+ returns(OpenSearch::Transport::Transport::Response.new 400, 'Bad Request').
383
+ times(1)
384
+
385
+ @transport.logger.
386
+ expects(:warn).
387
+ with( regexp_matches(/Attempt \d to get response/) ).
388
+ never
389
+
390
+ assert_raise OpenSearch::Transport::Transport::Errors::BadRequest do
391
+ @transport.perform_request('GET', '/', {}, nil, &@block)
392
+ end
393
+ end
394
+
395
+ should "retry when the status code does match" do
396
+ @transport = DummyTransportPerformer.new :options => { :retry_on_status => 500 }
397
+ assert_equal [500], @transport.instance_variable_get(:@retry_on_status)
398
+
399
+ @block.expects(:call).
400
+ returns(OpenSearch::Transport::Transport::Response.new 500, 'Internal Error').
401
+ times(4)
402
+
403
+ @transport.logger.
404
+ expects(:warn).
405
+ with( regexp_matches(/Attempt \d to get response/) ).
406
+ times(4)
407
+
408
+ assert_raise OpenSearch::Transport::Transport::Errors::InternalServerError do
409
+ @transport.perform_request('GET', '/', &@block)
410
+ end
411
+ end
412
+ end
413
+
414
+ context "logging" do
415
+ setup do
416
+ @transport = DummyTransportPerformer.new :options => { :logger => Logger.new('/dev/null') }
417
+
418
+ fake_connection = stub :full_url => 'localhost:9200/_search?size=1',
419
+ :host => 'localhost',
420
+ :connection => stub_everything,
421
+ :failures => 0,
422
+ :healthy! => true
423
+
424
+ @transport.stubs(:get_connection).returns(fake_connection)
425
+ @transport.serializer.stubs(:load).returns 'foo' => 'bar'
426
+ @transport.serializer.stubs(:dump).returns '{"foo":"bar"}'
427
+ end
428
+
429
+ should "log the request and response" do
430
+ @transport.logger.expects(:info). with do |line|
431
+ line =~ %r|POST localhost\:9200/_search\?size=1 \[status\:200, request:.*s, query:n/a\]|
432
+ end
433
+ @transport.logger.expects(:debug). with '> {"foo":"bar"}'
434
+ @transport.logger.expects(:debug). with '< {"foo":"bar"}'
435
+
436
+ @transport.perform_request 'POST', '_search', {:size => 1}, {:foo => 'bar'} do
437
+ OpenSearch::Transport::Transport::Response.new 200, '{"foo":"bar"}'
438
+ end
439
+ end
440
+
441
+ should "sanitize password in the URL" do
442
+ fake_connection = stub :full_url => 'http://user:password@localhost:9200/_search?size=1',
443
+ :host => 'localhost',
444
+ :connection => stub_everything,
445
+ :failures => 0,
446
+ :healthy! => true
447
+ @transport.stubs(:get_connection).returns(fake_connection)
448
+
449
+ @transport.logger.expects(:info).with do |message|
450
+ assert_match(/http:\/\/user:\*{1,15}@localhost\:9200/, message)
451
+ true
452
+ end
453
+
454
+
455
+ @transport.perform_request('GET', '/') {OpenSearch::Transport::Transport::Response.new 200, '{"foo":"bar"}' }
456
+ end
457
+
458
+ should "log a failed OpenSearch request as fatal" do
459
+ @block = Proc.new { |c, u| puts "ERROR" }
460
+ @block.expects(:call).returns(OpenSearch::Transport::Transport::Response.new 500, 'ERROR')
461
+
462
+ @transport.expects(:__log_response)
463
+ @transport.logger.expects(:fatal)
464
+
465
+ assert_raise OpenSearch::Transport::Transport::Errors::InternalServerError do
466
+ @transport.perform_request('POST', '_search', &@block)
467
+ end
468
+ end
469
+
470
+ should "not log a failed OpenSearch request as fatal" do
471
+ @block = Proc.new { |c, u| puts "ERROR" }
472
+ @block.expects(:call).returns(OpenSearch::Transport::Transport::Response.new 500, 'ERROR')
473
+
474
+ @transport.expects(:__log_response).once
475
+ @transport.logger.expects(:fatal).never
476
+
477
+ # No `BadRequest` error
478
+ @transport.perform_request('POST', '_search', :ignore => 500, &@block)
479
+ end
480
+
481
+ should "log and re-raise a Ruby exception" do
482
+ @block = Proc.new { |c, u| puts "ERROR" }
483
+ @block.expects(:call).raises(Exception)
484
+
485
+ @transport.expects(:__log_response).never
486
+ @transport.logger.expects(:fatal)
487
+
488
+ assert_raise(Exception) { @transport.perform_request('POST', '_search', &@block) }
489
+ end
490
+ end
491
+
492
+ context "tracing" do
493
+ setup do
494
+ @transport = DummyTransportPerformer.new :options => { :tracer => Logger.new('/dev/null') }
495
+
496
+ fake_connection = stub :full_url => 'localhost:9200/_search?size=1',
497
+ :host => 'localhost',
498
+ :connection => stub_everything,
499
+ :failures => 0,
500
+ :healthy! => true
501
+
502
+ @transport.stubs(:get_connection).returns(fake_connection)
503
+ @transport.serializer.stubs(:load).returns 'foo' => 'bar'
504
+ @transport.serializer.stubs(:dump).returns <<-JSON.gsub(/^ /, '')
505
+ {
506
+ "foo" : {
507
+ "bar" : {
508
+ "bam" : true
509
+ }
510
+ }
511
+ }
512
+ JSON
513
+ end
514
+
515
+ should "trace the request" do
516
+ @transport.tracer.expects(:info). with do |message|
517
+ message == <<-CURL.gsub(/^ /, '')
518
+ curl -X POST 'http://localhost:9200/_search?pretty&size=1' -d '{
519
+ "foo" : {
520
+ "bar" : {
521
+ "bam" : true
522
+ }
523
+ }
524
+ }
525
+ '
526
+ CURL
527
+ end.once
528
+
529
+ @transport.perform_request 'POST', '_search', {:size => 1}, {:q => 'foo'} do
530
+ OpenSearch::Transport::Transport::Response.new 200, '{"foo":"bar"}'
531
+ end
532
+ end
533
+
534
+ should "trace a failed OpenSearch request" do
535
+ @block = Proc.new { |c, u| puts "ERROR" }
536
+ @block.expects(:call).returns(OpenSearch::Transport::Transport::Response.new 500, 'ERROR')
537
+
538
+ @transport.expects(:__trace)
539
+
540
+ assert_raise OpenSearch::Transport::Transport::Errors::InternalServerError do
541
+ @transport.perform_request('POST', '_search', &@block)
542
+ end
543
+ end
544
+
545
+ end
546
+
547
+ context "reloading connections" do
548
+ setup do
549
+ @transport = DummyTransport.new :options => { :logger => Logger.new('/dev/null') }
550
+ end
551
+
552
+ should "rebuild connections" do
553
+ @transport.sniffer.expects(:hosts).returns([])
554
+ @transport.expects(:__rebuild_connections)
555
+ @transport.reload_connections!
556
+ end
557
+
558
+ should "log error and continue when timing out while sniffing hosts" do
559
+ @transport.sniffer.expects(:hosts).raises(OpenSearch::Transport::Transport::SnifferTimeoutError)
560
+ @transport.logger.expects(:error)
561
+ assert_nothing_raised do
562
+ @transport.reload_connections!
563
+ end
564
+ end
565
+
566
+ should "keep existing connections" do
567
+ @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 } ], :options => { :http => {} }
568
+ assert_equal 1, @transport.connections.size
569
+
570
+ old_connection_id = @transport.connections.first.object_id
571
+
572
+ @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 },
573
+ { :host => 'node2', :port => 2 } ],
574
+ :options => { :http => {} }
575
+
576
+ assert_equal 2, @transport.connections.size
577
+ assert_equal old_connection_id, @transport.connections.first.object_id
578
+ end
579
+
580
+ should "remove dead connections" do
581
+ @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 },
582
+ { :host => 'node2', :port => 2 } ],
583
+ :options => { :http => {} }
584
+ assert_equal 2, @transport.connections.size
585
+
586
+ @transport.connections[1].dead!
587
+
588
+ @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 } ], :options => { :http => {} }
589
+
590
+ assert_equal 1, @transport.connections.size
591
+ assert_equal 1, @transport.connections.all.size
592
+ end
593
+
594
+ should "not duplicate connections" do
595
+ @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 },
596
+ { :host => 'node2', :port => 2 } ],
597
+ :options => { :http => {} }
598
+ assert_equal 2, @transport.connections.size
599
+
600
+ @transport.connections[0].dead!
601
+
602
+ @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 },
603
+ { :host => 'node2', :port => 2 } ],
604
+ :options => { :http => {} }
605
+
606
+ assert_equal 2, @transport.connections.all.size
607
+ assert_equal 1, @transport.connections.size
608
+ end
609
+ end
610
+
611
+ context "rebuilding connections" do
612
+ setup do
613
+ @transport = DummyTransport.new
614
+ end
615
+
616
+ should "close connections" do
617
+ @transport.expects(:__close_connections)
618
+ @transport.__rebuild_connections :hosts => [ { :scheme => 'http', :host => 'foo', :port => 1 } ], :options => { :http => {} }
619
+ end
620
+
621
+ should "should replace the connections" do
622
+ assert_equal 0, @transport.connections.size
623
+
624
+ @transport.__rebuild_connections :hosts => [{ :scheme => 'http', :host => 'foo', :port => 1 }],
625
+ :options => { :http => {} }
626
+
627
+ assert_equal 1, @transport.connections.size
628
+ end
629
+ end
630
+
631
+ context "resurrecting connections" do
632
+ setup do
633
+ @transport = DummyTransportPerformer.new
634
+ end
635
+
636
+ should "delegate to dead connections" do
637
+ @transport.connections.expects(:dead).returns([])
638
+ @transport.resurrect_dead_connections!
639
+ end
640
+
641
+ should "not resurrect connections until timeout" do
642
+ @transport.connections.expects(:get_connection).returns(stub_everything :failures => 1).times(5)
643
+ @transport.expects(:resurrect_dead_connections!).never
644
+ 5.times { @transport.get_connection }
645
+ end
646
+
647
+ should "resurrect connections after timeout" do
648
+ @transport.connections.expects(:get_connection).returns(stub_everything :failures => 1).times(5)
649
+ @transport.expects(:resurrect_dead_connections!)
650
+
651
+ 4.times { @transport.get_connection }
652
+
653
+ now = Time.now + 60*2
654
+ Time.stubs(:now).returns(now)
655
+
656
+ @transport.get_connection
657
+ end
658
+
659
+ should "mark connection healthy if it succeeds" do
660
+ c = stub_everything(:failures => 1)
661
+ @transport.expects(:get_connection).returns(c)
662
+ c.expects(:healthy!)
663
+
664
+ @transport.perform_request('GET', '/') { |connection, url| OpenSearch::Transport::Transport::Response.new 200, 'OK' }
665
+ end
666
+ end
667
+
668
+ context "errors" do
669
+ should "raise highest-level Error exception for any ServerError" do
670
+ assert_kind_of OpenSearch::Transport::Transport::Error, OpenSearch::Transport::Transport::ServerError.new
671
+ end
672
+ end
673
+ end