elasticsearch-transport 5.0.5 → 6.8.2

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.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +5 -0
  3. data/README.md +88 -34
  4. data/Rakefile +14 -17
  5. data/elasticsearch-transport.gemspec +44 -63
  6. data/lib/elasticsearch/transport/client.rb +120 -61
  7. data/lib/elasticsearch/transport/redacted.rb +79 -0
  8. data/lib/elasticsearch/transport/transport/base.rb +28 -14
  9. data/lib/elasticsearch/transport/transport/connections/collection.rb +4 -0
  10. data/lib/elasticsearch/transport/transport/connections/connection.rb +5 -1
  11. data/lib/elasticsearch/transport/transport/connections/selector.rb +4 -0
  12. data/lib/elasticsearch/transport/transport/errors.rb +4 -0
  13. data/lib/elasticsearch/transport/transport/http/curb.rb +7 -2
  14. data/lib/elasticsearch/transport/transport/http/faraday.rb +11 -8
  15. data/lib/elasticsearch/transport/transport/http/manticore.rb +6 -1
  16. data/lib/elasticsearch/transport/transport/response.rb +4 -0
  17. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +4 -0
  18. data/lib/elasticsearch/transport/transport/sniffer.rb +31 -3
  19. data/lib/elasticsearch/transport/version.rb +5 -1
  20. data/lib/elasticsearch/transport.rb +5 -0
  21. data/lib/elasticsearch-transport.rb +4 -0
  22. data/spec/elasticsearch/transport/base_spec.rb +260 -0
  23. data/spec/elasticsearch/transport/client_spec.rb +1025 -0
  24. data/spec/elasticsearch/transport/sniffer_spec.rb +269 -0
  25. data/spec/spec_helper.rb +65 -0
  26. data/test/integration/transport_test.rb +9 -5
  27. data/test/profile/client_benchmark_test.rb +23 -25
  28. data/test/test_helper.rb +10 -0
  29. data/test/unit/connection_collection_test.rb +4 -0
  30. data/test/unit/connection_selector_test.rb +4 -0
  31. data/test/unit/connection_test.rb +4 -0
  32. data/test/unit/response_test.rb +5 -1
  33. data/test/unit/serializer_test.rb +4 -0
  34. data/test/unit/transport_base_test.rb +21 -1
  35. data/test/unit/transport_curb_test.rb +12 -0
  36. data/test/unit/transport_faraday_test.rb +16 -0
  37. data/test/unit/transport_manticore_test.rb +11 -0
  38. metadata +82 -84
  39. data/test/integration/client_test.rb +0 -237
  40. data/test/unit/client_test.rb +0 -366
  41. data/test/unit/sniffer_test.rb +0 -179
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9141220bdecbb5cd5fe9eef893010d24419a62a6
4
- data.tar.gz: e2a2ad61469a9398e749b4004909ec2234360262
2
+ SHA256:
3
+ metadata.gz: c036b32f60221fef3a8c617ddc30a55c50c916b4f3c235fe34f91917060c9ae4
4
+ data.tar.gz: 18d4582f2c6479fe2af5bf10a6f6cc97d6083172274be5de85d82a47f62b1a04
5
5
  SHA512:
6
- metadata.gz: 47916a61140347aba5eb71728587d4c611f0219e8f03d3584405fde93d10fe991f38652b5125e073ad37f16cedd72fe452faea82197f892b539c95809db5cf74
7
- data.tar.gz: 9130c01c4a9d7687f42fa57848c3dac0ae7382972e1c68776319c829b079307ccda469846f6231e2e64c783225c43a75386d35b6de8f3d5614516266d24e80c8
6
+ metadata.gz: a06a56942b78b094e562ef1fa4d6adfd9433a823aeb3e21e06296574c39bcb5c228b52a009fe3a375a8682a1d0e9c9d370ff74a5a84dce4d2800170ede4e3d5c
7
+ data.tar.gz: 8646819dd552fae0bbba12d03e0a2ed1c996f2d0493a0ad65c4f07bd5e2ae763d8067aefc66c8d52963def632d1a552c7271744233ba12a5b43945d281800d1c
data/Gemfile CHANGED
@@ -14,3 +14,8 @@ end
14
14
  if File.exist? File.expand_path("../../elasticsearch/elasticsearch.gemspec", __FILE__)
15
15
  gem 'elasticsearch', :path => File.expand_path("../../elasticsearch", __FILE__), :require => false
16
16
  end
17
+
18
+ group :development do
19
+ gem 'rspec'
20
+ gem 'pry-nav'
21
+ end
data/README.md CHANGED
@@ -16,24 +16,28 @@ data serialization and transport.
16
16
  It does not handle calling the Elasticsearch API;
17
17
  see the [`elasticsearch-api`](https://github.com/elasticsearch/elasticsearch-ruby/tree/master/elasticsearch-api) library.
18
18
 
19
- The library is compatible with Ruby 1.8.7 or higher and with all versions of Elasticsearch since 0.90.
19
+ The library is compatible with Ruby 1.9 or higher and with all versions of Elasticsearch since 0.90.
20
20
 
21
21
  Features overview:
22
22
 
23
23
  * Pluggable logging and tracing
24
- * Plugabble connection selection strategies (round-robin, random, custom)
24
+ * Pluggable connection selection strategies (round-robin, random, custom)
25
25
  * Pluggable transport implementation, customizable and extendable
26
26
  * Pluggable serializer implementation
27
27
  * Request retries and dead connections handling
28
28
  * Node reloading (based on cluster state) on errors or on demand
29
29
 
30
30
  For optimal performance, use a HTTP library which supports persistent ("keep-alive") connections,
31
- such as [Typhoeus](https://github.com/typhoeus/typhoeus).
32
- Just require the library (`require 'typhoeus'; require 'typhoeus/adapters/faraday'`) in your code,
33
- and it will be automatically used; currently these libraries will be automatically detected and used:
34
- [Patron](https://github.com/toland/patron),
35
- [HTTPClient](https://rubygems.org/gems/httpclient) and
36
- [Net::HTTP::Persistent](https://rubygems.org/gems/net-http-persistent).
31
+ such as [patron](https://github.com/toland/patron).
32
+ Just require the library (`require 'patron'`) in your code,
33
+ and it will be automatically used.
34
+
35
+ Currently these libraries will be automatically detected and used:
36
+ - [Patron](https://github.com/toland/patron)
37
+ - [HTTPClient](https://rubygems.org/gems/httpclient)
38
+ - [Net::HTTP::Persistent](https://rubygems.org/gems/net-http-persistent)
39
+
40
+ **Note on [Typhoeus](https://github.com/typhoeus/typhoeus)**: Typhoeus is compatible and will be automatically detected too. However, the latest release (v1.3.1 at the moment of writing this) is not compatible with Faraday 1.0. [It still uses the deprecated `Faraday::Error` namespace](https://github.com/typhoeus/typhoeus/blob/v1.3.1/lib/typhoeus/adapters/faraday.rb#L100). If you want to use it with this gem, we suggest getting `master` from GitHub, since this has been fixed for v1.4.0. We'll update this if/when v1.4.0 is released.a
37
41
 
38
42
  For detailed information, see example configurations [below](#transport-implementations).
39
43
 
@@ -69,6 +73,22 @@ Full documentation is available at <http://rubydoc.info/gems/elasticsearch-trans
69
73
 
70
74
  ## Configuration
71
75
 
76
+ * [Setting Hosts](#setting-hosts)
77
+ * [Default port](#default-port)
78
+ * [Connect using an Elastic Cloud ID](#connect-using-an-elastic-cloud-id)
79
+ * [Authentication](#authentication)
80
+ * [Logging](#logging)
81
+ * [Identifying running tasks with X-Opaque-Id](#identifying-running-tasks-with-x-opaque-id)
82
+ * [Setting Timeouts](#setting-timeouts)
83
+ * [Randomizing Hosts](#randomizing-hosts)
84
+ * [Retrying on Failures](#retrying-on-failures)
85
+ * [Reloading Hosts](#reloading-hosts)
86
+ * [Connection Selector](#connection-selector)
87
+ * [Transport Implementations](#transport-implementations)
88
+ * [Serializer implementations](#serializer-implementations)
89
+ * [Exception Handling](#exception-handling)
90
+ * [Development and Community](#development-and-community)
91
+
72
92
  The client supports many configurations options for setting up and managing connections,
73
93
  configuring logging, customizing the transport library, etc.
74
94
 
@@ -128,6 +148,26 @@ use the `transport_options` option:
128
148
  Elasticsearch::Client.new url: 'https://username:password@example.com:9200',
129
149
  transport_options: { ssl: { ca_file: '/path/to/cacert.pem' } }
130
150
 
151
+ You can also use [**API Key authentication**](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html):
152
+
153
+ ``` ruby
154
+ Elasticsearch::Client.new(
155
+ host: host,
156
+ transport_options: transport_options,
157
+ api_key: credentials
158
+ )
159
+ ```
160
+
161
+ Where credentials is either the base64 encoding of `id` and `api_key` joined by a colon or a hash with the `id` and `api_key`:
162
+
163
+ ``` ruby
164
+ Elasticsearch::Client.new(
165
+ host: host,
166
+ transport_options: transport_options,
167
+ api_key: {id: 'my_id', api_key: 'my_api_key'}
168
+ )
169
+ ```
170
+
131
171
  ### Logging
132
172
 
133
173
  To log requests and responses to standard output with the default logger (an instance of Ruby's {::Logger} class),
@@ -158,6 +198,29 @@ You can pass the client any conforming logger implementation:
158
198
 
159
199
  client = Elasticsearch::Client.new logger: log
160
200
 
201
+ ### Identifying running tasks with X-Opaque-Id
202
+
203
+ The X-Opaque-Id header allows to track certain calls, or associate certain tasks with the client that started them ([more on the Elasticsearch docs](https://www.elastic.co/guide/en/elasticsearch/reference/master/tasks.html#_identifying_running_tasks)). To use this feature, you need to set an id for `opaque_id` on the client on each request. Example:
204
+
205
+ ```ruby
206
+ client = Elasticsearch::Client.new
207
+ client.search(index: 'myindex', q: 'title:test', opaque_id: '123456')
208
+ ```
209
+ The search request will include the following HTTP Header:
210
+ ```
211
+ X-Opaque-Id: 123456
212
+ ```
213
+
214
+ You can also set a prefix for X-Opaque-Id when initializing the client. This will be prepended to the id you set before each request if you're using X-Opaque-Id. Example:
215
+ ```ruby
216
+ client = Elasticsearch::Client.new(opaque_id_prefix: 'eu-west1')
217
+ client.search(index: 'myindex', q: 'title:test', opaque_id: '123456')
218
+ ```
219
+ The request will include the following HTTP Header:
220
+ ```
221
+ X-Opaque-Id: eu-west1_123456
222
+ ```
223
+
161
224
  ### Setting Timeouts
162
225
 
163
226
  For many operations in Elasticsearch, the default timeouts of HTTP libraries are too low.
@@ -292,44 +355,35 @@ constructor, use the `transport_options` key:
292
355
 
293
356
  To configure the _Faraday_ instance directly, use a block:
294
357
 
295
- require 'typhoeus'
296
- require 'typhoeus/adapters/faraday'
358
+ require 'patron'
297
359
 
298
360
  client = Elasticsearch::Client.new(host: 'localhost', port: '9200') do |f|
299
361
  f.response :logger
300
- f.adapter :typhoeus
362
+ f.adapter :patron
301
363
  end
302
364
 
303
- You can use any standard Faraday middleware and plugins in the configuration block,
304
- for example sign the requests for the [AWS Elasticsearch service](https://aws.amazon.com/elasticsearch-service/):
305
-
306
- require 'faraday_middleware/aws_signers_v4'
307
-
308
- client = Elasticsearch::Client.new url: 'https://search-my-cluster-abc123....es.amazonaws.com' do |f|
309
- f.request :aws_signers_v4,
310
- credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY'], ENV['AWS_SECRET_ACCESS_KEY']),
311
- service_name: 'es',
312
- region: 'us-east-1'
313
- end
365
+ You can use any standard Faraday middleware and plugins in the configuration block, for example sign the requests for the [AWS Elasticsearch service](https://aws.amazon.com/elasticsearch-service/). See [the AWS documentation](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-request-signing.html#es-request-signing-ruby) for an example.
314
366
 
315
367
  You can also initialize the transport class yourself, and pass it to the client constructor
316
368
  as the `transport` argument:
317
369
 
318
- require 'typhoeus'
319
- require 'typhoeus/adapters/faraday'
370
+ ```ruby
371
+ require 'patron'
320
372
 
321
- transport_configuration = lambda do |f|
322
- f.response :logger
323
- f.adapter :typhoeus
324
- end
373
+ transport_configuration = lambda do |f|
374
+ f.response :logger
375
+ f.adapter :patron
376
+ end
325
377
 
326
- transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new \
327
- hosts: [ { host: 'localhost', port: '9200' } ],
328
- &transport_configuration
378
+ transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new \
379
+ hosts: [ { host: 'localhost', port: '9200' } ],
380
+ &transport_configuration
381
+
382
+ # Pass the transport to the client
383
+ #
384
+ client = Elasticsearch::Client.new transport: transport
385
+ ```
329
386
 
330
- # Pass the transport to the client
331
- #
332
- client = Elasticsearch::Client.new transport: transport
333
387
 
334
388
  Instead of passing the transport to the constructor, you can inject it at run time:
335
389
 
data/Rakefile CHANGED
@@ -7,40 +7,37 @@ task :test => 'test:unit'
7
7
  # ----- Test tasks ------------------------------------------------------------
8
8
 
9
9
  require 'rake/testtask'
10
+ require 'rspec/core/rake_task'
11
+
10
12
  namespace :test do
11
- task :ci_reporter do
12
- ENV['CI_REPORTS'] ||= 'tmp/reports'
13
- if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
14
- require 'ci/reporter/rake/test_unit'
15
- Rake::Task['ci:setup:testunit'].invoke
16
- else
17
- require 'ci/reporter/rake/minitest'
18
- Rake::Task['ci:setup:minitest'].invoke
19
- end
13
+
14
+ RSpec::Core::RakeTask.new(:spec)
15
+
16
+ desc "Wait for Elasticsearch to be in a green state"
17
+ task :wait_for_green do
18
+ sh '../scripts/wait-cluster.sh'
20
19
  end
21
20
 
22
21
  Rake::TestTask.new(:unit) do |test|
23
- Rake::Task['test:ci_reporter'].invoke if ENV['CI']
24
22
  test.libs << 'lib' << 'test'
25
23
  test.test_files = FileList["test/unit/**/*_test.rb"]
26
- # test.verbose = true
27
- # test.warning = true
24
+ test.verbose = false
25
+ test.warning = false
28
26
  end
29
27
 
30
28
  Rake::TestTask.new(:integration) do |test|
31
- Rake::Task['test:ci_reporter'].invoke if ENV['CI']
32
29
  test.libs << 'lib' << 'test'
33
30
  test.test_files = FileList["test/integration/**/*_test.rb"]
31
+ test.deps = [ :wait_for_green, :spec ]
32
+ test.verbose = false
33
+ test.warning = false
34
34
  end
35
35
 
36
36
  Rake::TestTask.new(:all) do |test|
37
- Rake::Task['test:ci_reporter'].invoke if ENV['CI']
38
- test.libs << 'lib' << 'test'
39
- test.test_files = FileList["test/unit/**/*_test.rb", "test/integration/**/*_test.rb"]
37
+ test.deps = [ :unit, :integration ]
40
38
  end
41
39
 
42
40
  Rake::TestTask.new(:profile) do |test|
43
- Rake::Task['test:ci_reporter'].invoke if ENV['CI']
44
41
  test.libs << 'lib' << 'test'
45
42
  test.test_files = FileList["test/profile/**/*_test.rb"]
46
43
  end
@@ -4,74 +4,55 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'elasticsearch/transport/version'
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = "elasticsearch-transport"
7
+ s.name = 'elasticsearch-transport'
8
8
  s.version = Elasticsearch::Transport::VERSION
9
- s.authors = ["Karel Minarik"]
10
- s.email = ["karel.minarik@elasticsearch.org"]
11
- s.summary = "Ruby client for Elasticsearch."
12
- s.homepage = "https://github.com/elasticsearch/elasticsearch-ruby/tree/master/elasticsearch-transport"
13
- s.license = "Apache 2"
14
-
9
+ s.authors = ['Karel Minarik']
10
+ s.email = ['karel.minarik@elasticsearch.org']
11
+ s.summary = 'Ruby client for Elasticsearch.'
12
+ s.homepage = 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html'
13
+ s.license = 'Apache-2.0'
14
+ s.metadata = {
15
+ 'homepage_uri' => 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html',
16
+ 'changelog_uri' => 'https://github.com/elastic/elasticsearch-ruby/blob/6.x/CHANGELOG.md',
17
+ 'source_code_uri' => 'https://github.com/elastic/elasticsearch-ruby/tree/6.x/elasticsearch-transport',
18
+ 'bug_tracker_uri' => 'https://github.com/elastic/elasticsearch-ruby/issues'
19
+ }
15
20
  s.files = `git ls-files`.split($/)
16
21
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
22
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
- s.require_paths = ["lib"]
19
-
20
- s.extra_rdoc_files = [ "README.md", "LICENSE.txt" ]
21
- s.rdoc_options = [ "--charset=UTF-8" ]
22
-
23
- s.add_dependency "multi_json"
24
- s.add_dependency "faraday"
25
-
26
- if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
27
- s.add_dependency "system_timer"
28
- end
29
-
30
- s.add_development_dependency "bundler", "> 1"
31
-
32
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
33
- s.add_development_dependency "rake", "~> 11.1"
34
- else
35
- s.add_development_dependency "rake", "< 11.0"
36
- end
37
-
38
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
39
- s.add_development_dependency "elasticsearch-extensions"
40
- end
41
-
42
- s.add_development_dependency "ansi"
43
- s.add_development_dependency "shoulda-context"
44
- s.add_development_dependency "mocha"
45
- s.add_development_dependency "turn"
46
- s.add_development_dependency "yard"
47
- s.add_development_dependency "pry"
48
- s.add_development_dependency "ci_reporter", "~> 1.9"
49
-
23
+ s.require_paths = ['lib']
24
+
25
+ s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
26
+ s.rdoc_options = ['--charset=UTF-8']
27
+
28
+ s.required_ruby_version = '>= 2.4'
29
+
30
+ s.add_dependency 'multi_json'
31
+ s.add_dependency 'faraday', '~> 1'
32
+ s.add_development_dependency 'ansi'
33
+ s.add_development_dependency 'bundler'
34
+ s.add_development_dependency 'elasticsearch-extensions'
35
+ s.add_development_dependency 'mocha'
36
+ s.add_development_dependency 'pry'
37
+ s.add_development_dependency 'rake', '~> 13'
38
+ s.add_development_dependency 'shoulda-context'
39
+ s.add_development_dependency 'turn'
40
+ s.add_development_dependency 'yard'
50
41
  # Gems for testing integrations
51
- s.add_development_dependency "curb" unless defined? JRUBY_VERSION
52
- s.add_development_dependency "patron" unless defined? JRUBY_VERSION
53
- s.add_development_dependency "typhoeus", '~> 0.6'
54
- s.add_development_dependency "net-http-persistent"
55
- s.add_development_dependency "manticore", '~> 0.5.2' if defined? JRUBY_VERSION
56
- s.add_development_dependency "hashie"
57
-
58
- # Prevent unit test failures on Ruby 1.8
59
- if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
60
- s.add_development_dependency "test-unit", '~> 2'
61
- s.add_development_dependency "json", '~> 1.8'
62
- end
63
-
64
- if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
65
- s.add_development_dependency "minitest", "~> 4.0"
66
- s.add_development_dependency "ruby-prof" unless defined?(JRUBY_VERSION) || defined?(Rubinius)
67
- s.add_development_dependency "require-prof" unless defined?(JRUBY_VERSION) || defined?(Rubinius)
68
- s.add_development_dependency "simplecov"
69
- s.add_development_dependency "simplecov-rcov"
70
- s.add_development_dependency "cane"
71
- end
72
-
73
- if defined?(RUBY_VERSION) && RUBY_VERSION > '2.2'
74
- s.add_development_dependency "test-unit", '~> 2'
42
+ s.add_development_dependency 'cane'
43
+ s.add_development_dependency 'hashie'
44
+ s.add_development_dependency 'manticore', '~> 0.5.2' if defined? JRUBY_VERSION
45
+ s.add_development_dependency 'minitest', '~> 4.0'
46
+ s.add_development_dependency 'net-http-persistent'
47
+ s.add_development_dependency 'simplecov', '~> 0.17', '< 0.18'
48
+ s.add_development_dependency 'simplecov-rcov'
49
+ s.add_development_dependency 'test-unit', '~> 2'
50
+ s.add_development_dependency 'typhoeus', '~> 0.6'
51
+ unless defined?(JRUBY_VERSION) || defined?(Rubinius)
52
+ s.add_development_dependency 'curb'
53
+ s.add_development_dependency 'patron'
54
+ s.add_development_dependency 'require-prof'
55
+ s.add_development_dependency 'ruby-prof'
75
56
  end
76
57
 
77
58
  s.description = <<-DESC.gsub(/^ /, '')
@@ -1,3 +1,7 @@
1
+ # Licensed to Elasticsearch B.V under one or more agreements.
2
+ # Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3
+ # See the LICENSE file in the project root for more information
4
+
1
5
  module Elasticsearch
2
6
  module Transport
3
7
 
@@ -24,6 +28,11 @@ module Elasticsearch
24
28
  logger
25
29
  end
26
30
 
31
+ # The default host and port to use if not otherwise specified.
32
+ #
33
+ # @since 7.0.0
34
+ DEFAULT_HOST = 'localhost:9200'.freeze
35
+
27
36
  # Returns the transport object.
28
37
  #
29
38
  # @see Elasticsearch::Transport::Transport::Base
@@ -80,17 +89,16 @@ module Elasticsearch
80
89
  # @option arguments [String] :send_get_body_as Specify the HTTP method to use for GET requests with a body.
81
90
  # (Default: GET)
82
91
  #
92
+ # @option api_key [String, Hash] :api_key Use API Key Authentication, either the base64 encoding of `id` and `api_key`
93
+ # joined by a colon as a String, or a hash with the `id` and `api_key` values.
94
+ # @option opaque_id_prefix [String] :opaque_id_prefix set a prefix for X-Opaque-Id when initializing the client. This
95
+ # will be prepended to the id you set before each request if you're using X-Opaque-Id
96
+ #
83
97
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
84
98
  #
85
99
  def initialize(arguments={}, &block)
100
+ @options = arguments
86
101
  @arguments = arguments
87
-
88
- hosts = @arguments[:hosts] || \
89
- @arguments[:host] || \
90
- @arguments[:url] || \
91
- @arguments[:urls] || \
92
- ENV.fetch('ELASTICSEARCH_URL', 'localhost:9200')
93
-
94
102
  @arguments[:logger] ||= @arguments[:log] ? DEFAULT_LOGGER.call() : nil
95
103
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
96
104
  @arguments[:reload_connections] ||= false
@@ -99,36 +107,69 @@ module Elasticsearch
99
107
  @arguments[:randomize_hosts] ||= false
100
108
  @arguments[:transport_options] ||= {}
101
109
  @arguments[:http] ||= {}
110
+ @options[:http] ||= {}
102
111
 
103
- @arguments[:transport_options].update(:request => { :timeout => @arguments[:request_timeout] } ) if @arguments[:request_timeout]
112
+ set_api_key if (@api_key = @arguments[:api_key])
104
113
 
105
- @arguments[:transport_options][:headers] ||= {}
106
- @arguments[:transport_options][:headers].update 'Content-Type' => 'application/json' unless @arguments[:transport_options][:headers].keys.any? {|k| k.to_s.downcase =~ /content\-?\_?type/}
114
+
115
+ @seeds ||= __extract_hosts(@arguments[:hosts] ||
116
+ @arguments[:host] ||
117
+ @arguments[:url] ||
118
+ @arguments[:urls] ||
119
+ ENV['ELASTICSEARCH_URL'] ||
120
+ DEFAULT_HOST)
107
121
 
108
122
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
123
+ @opaque_id_prefix = @arguments[:opaque_id_prefix] || nil
109
124
 
110
- transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
125
+ if @arguments[:request_timeout]
126
+ @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] }
127
+ end
128
+
129
+ @arguments[:transport_options][:headers] ||= {}
111
130
 
112
- @transport = @arguments[:transport] || begin
131
+ unless @arguments[:transport_options][:headers].keys.any? {|k| k.to_s.downcase =~ /content\-?\_?type/}
132
+ @arguments[:transport_options][:headers]['Content-Type'] = 'application/json'
133
+ end
134
+
135
+ if @arguments[:transport]
136
+ @transport = @arguments[:transport]
137
+ else
138
+ transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
113
139
  if transport_class == Transport::HTTP::Faraday
114
- transport_class.new(:hosts => __extract_hosts(hosts, @arguments), :options => @arguments) do |faraday|
115
- block.call faraday if block
116
- unless (h = faraday.builder.handlers.last) && h.name.start_with?("Faraday::Adapter")
117
- faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
118
- end
140
+ @transport = transport_class.new(hosts: @seeds, options: @arguments) do |faraday|
141
+ faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
142
+ block&.call faraday
119
143
  end
120
144
  else
121
- transport_class.new(:hosts => __extract_hosts(hosts, @arguments), :options => @arguments)
145
+ @transport = transport_class.new(hosts: @seeds, options: @arguments)
122
146
  end
123
147
  end
124
148
  end
125
149
 
126
150
  # Performs a request through delegation to {#transport}.
127
151
  #
128
- def perform_request(method, path, params={}, body=nil)
152
+ def perform_request(method, path, params = {}, body = nil, headers = nil)
129
153
  method = @send_get_body_as if 'GET' == method && body
154
+ if (opaque_id = params.delete(:opaque_id))
155
+ headers = {} if headers.nil?
156
+ opaque_id = @opaque_id_prefix ? "#{@opaque_id_prefix}#{opaque_id}" : opaque_id
157
+ headers.merge!('X-Opaque-Id' => opaque_id)
158
+ end
159
+ transport.perform_request(method, path, params, body, headers)
160
+ end
161
+
162
+ private
130
163
 
131
- transport.perform_request method, path, params, body
164
+ def set_api_key
165
+ @api_key = __encode(@api_key) if @api_key.is_a? Hash
166
+ headers = @arguments[:transport_options]&.[](:headers) || {}
167
+ headers.merge!('Authorization' => "ApiKey #{@api_key}")
168
+ @arguments[:transport_options].merge!(
169
+ headers: headers
170
+ )
171
+ @arguments.delete(:user)
172
+ @arguments.delete(:password)
132
173
  end
133
174
 
134
175
  # Normalizes and returns hosts configuration.
@@ -143,52 +184,63 @@ module Elasticsearch
143
184
  #
144
185
  # @api private
145
186
  #
146
- def __extract_hosts(hosts_config, options={})
147
- if hosts_config.is_a?(Hash)
148
- hosts = [ hosts_config ]
149
- else
150
- if hosts_config.is_a?(String) && hosts_config.include?(',')
151
- hosts = hosts_config.split(/\s*,\s*/)
152
- else
153
- hosts = Array(hosts_config)
154
- end
155
- end
156
-
157
- result = hosts.map do |host|
158
- host_parts = case host
159
- when String
160
- if host =~ /^[a-z]+\:\/\//
161
- uri = URI.parse(host)
162
- { :scheme => uri.scheme, :user => uri.user, :password => uri.password, :host => uri.host, :path => uri.path, :port => uri.port }
163
- else
164
- host, port = host.split(':')
165
- { :host => host, :port => port }
166
- end
167
- when URI
168
- { :scheme => host.scheme, :user => host.user, :password => host.password, :host => host.host, :path => host.path, :port => host.port }
169
- when Hash
170
- host
171
- else
172
- raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
173
- end
174
-
175
- host_parts[:port] = host_parts[:port].to_i unless host_parts[:port].nil?
187
+ def __extract_hosts(hosts_config)
188
+ hosts = case hosts_config
189
+ when String
190
+ hosts_config.split(',').map { |h| h.strip! || h }
191
+ when Array
192
+ hosts_config
193
+ when Hash, URI
194
+ [ hosts_config ]
195
+ else
196
+ Array(hosts_config)
197
+ end
176
198
 
177
- # Transfer the selected host parts such as authentication credentials to `options`,
178
- # so we can re-use them when reloading connections
179
- #
180
- host_parts.select { |k,v| [:scheme, :port, :user, :password].include?(k) }.each do |k,v|
181
- @arguments[:http][k] ||= v
182
- end
199
+ host_list = hosts.map { |host| __parse_host(host) }
200
+ @options[:randomize_hosts] ? host_list.shuffle! : host_list
201
+ end
183
202
 
184
- # Remove the trailing slash
185
- host_parts[:path].chomp!('/') if host_parts[:path]
203
+ def __parse_host(host)
204
+ host_parts = case host
205
+ when String
206
+ if host =~ /^[a-z]+\:\/\//
207
+ uri = URI.parse(host)
208
+ { :scheme => uri.scheme,
209
+ :user => uri.user,
210
+ :password => uri.password,
211
+ :host => uri.host,
212
+ :path => uri.path,
213
+ :port => uri.port }
214
+ else
215
+ host, port = host.split(':')
216
+ { :host => host,
217
+ :port => port }
218
+ end
219
+ when URI
220
+ { :scheme => host.scheme,
221
+ :user => host.user,
222
+ :password => host.password,
223
+ :host => host.host,
224
+ :path => host.path,
225
+ :port => host.port }
226
+ when Hash
227
+ host
228
+ else
229
+ raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
230
+ end
186
231
 
187
- host_parts
232
+ if @api_key
233
+ # Remove Basic Auth if using API KEY
234
+ host_parts.delete(:user)
235
+ host_parts.delete(:password)
236
+ else
237
+ @options[:http][:user] ||= host_parts[:user]
238
+ @options[:http][:password] ||= host_parts[:password]
188
239
  end
189
240
 
190
- result.shuffle! if options[:randomize_hosts]
191
- result
241
+ host_parts[:port] = host_parts[:port].to_i if host_parts[:port]
242
+ host_parts[:path].chomp!('/') if host_parts[:path]
243
+ host_parts
192
244
  end
193
245
 
194
246
  # Auto-detect the best adapter (HTTP "driver") available, based on libraries
@@ -213,6 +265,13 @@ module Elasticsearch
213
265
  ::Faraday.default_adapter
214
266
  end
215
267
  end
268
+
269
+ # Encode credentials for the Authorization Header
270
+ # Credentials is the base64 encoding of id and api_key joined by a colon
271
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html
272
+ def __encode(api_key)
273
+ Base64.strict_encode64([api_key[:id], api_key[:api_key]].join(':'))
274
+ end
216
275
  end
217
276
  end
218
277
  end