elasticsearch-transport 7.9.0 → 7.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9248a875060a4bd8e85ffb8f146beff61553150c155b0144e6c1c85112cdc640
4
- data.tar.gz: 517e365d3b98a44646daea7e89ae1d457220b2be00f5a432c780e6f556b36728
3
+ metadata.gz: 6e2f0e5d372dfa4495b762d82a182fab89c57fa56b7c62649587843dea8c2faf
4
+ data.tar.gz: f9d02a71dec534954eb87f1a777b1410578d378b15454193295aca9dfcaf5586
5
5
  SHA512:
6
- metadata.gz: 4921027c53dcf8b98a0d7e2051c4dfe879f4d169f1e0d1c02c0aefe874f9f5b649f1ed6b6f82409e045582cccb8e69c87ab00dcd73a9e2f200f16ef07a04f59e
7
- data.tar.gz: 7951d5d57c08454ddaa514fb2af1e12703d4028149853d928609af9f3fae8d6252fd50f18514e78a9d335bf47da6660a272445d4cd4b5c93fe3f6ba71422808f
6
+ metadata.gz: 625ac145cc74529c6a383309e6a2ee0729bf57f2858b2dc7d769f79025cf925256b9c5a531be52e070a419d3ed7efd525474eb8781aca87caad7b7fffa21c6e0
7
+ data.tar.gz: 683c674b9778ff15d2d82ac05d667e05944e897be3e0deebaf7277dbdaf837d5df2ad17ff9b4fb91b75f4b162a229e1ac8c9b6eee419c717db2a7e2e7668531f
data/Gemfile CHANGED
@@ -32,7 +32,7 @@ if File.exist? File.expand_path('../../elasticsearch/elasticsearch.gemspec', __F
32
32
  gem 'elasticsearch', path: File.expand_path('../../elasticsearch', __FILE__), require: false
33
33
  end
34
34
 
35
- group :development do
35
+ group :development, :test do
36
36
  gem 'rspec'
37
37
  if defined?(JRUBY_VERSION)
38
38
  gem 'pry-nav'
data/README.md CHANGED
@@ -136,7 +136,7 @@ Please see below for an exception to this when connecting using an Elastic Cloud
136
136
 
137
137
  If you are using [Elastic Cloud](https://www.elastic.co/cloud), you can provide your cloud id to the client.
138
138
  You must supply your username and password separately, and optionally a port. If no port is supplied,
139
- port 9243 will be used.
139
+ port 443 will be used.
140
140
 
141
141
  Note: Do not enable sniffing when using Elastic Cloud. The nodes are behind a load balancer so
142
142
  Elastic Cloud will take care of everything for you.
@@ -187,18 +187,24 @@ Elasticsearch::Client.new(
187
187
 
188
188
  ### Logging
189
189
 
190
- To log requests and responses to standard output with the default logger (an instance of Ruby's {::Logger} class),
191
- set the `log` argument:
190
+ To log requests and responses to standard output with the default logger (an instance of Ruby's {::Logger} class), set the `log` argument to true:
192
191
 
193
192
  ```ruby
194
- Elasticsearch::Client.new log: true
193
+ Elasticsearch::Client.new(log: true)
194
+ ```
195
+
196
+ You can also use [ecs-logging](https://github.com/elastic/ecs-logging-ruby). `ecs-logging` is a set of libraries that allows you to transform your application logs to structured logs that comply with the [Elastic Common Schema (ECS)](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html):
197
+
198
+ ```ruby
199
+ logger = EcsLogging::Logger.new($stdout)
200
+ Elasticsearch::Client.new(logger: logger)
195
201
  ```
196
202
 
197
203
 
198
204
  To trace requests and responses in the _Curl_ format, set the `trace` argument:
199
205
 
200
206
  ```ruby
201
- Elasticsearch::Client.new trace: true
207
+ Elasticsearch::Client.new(trace: true)
202
208
  ```
203
209
 
204
210
  You can customize the default logger or tracer:
@@ -211,7 +217,7 @@ You can customize the default logger or tracer:
211
217
  Or, you can use a custom `::Logger` instance:
212
218
 
213
219
  ```ruby
214
- Elasticsearch::Client.new logger: Logger.new(STDERR)
220
+ Elasticsearch::Client.new(logger: Logger.new(STDERR))
215
221
  ```
216
222
 
217
223
  You can pass the client any conforming logger implementation:
@@ -223,7 +229,7 @@ log = Logging.logger['elasticsearch']
223
229
  log.add_appenders Logging.appenders.stdout
224
230
  log.level = :info
225
231
 
226
- client = Elasticsearch::Client.new logger: log
232
+ client = Elasticsearch::Client.new(logger: log)
227
233
  ```
228
234
 
229
235
  ### Custom HTTP Headers
@@ -293,11 +299,16 @@ on a different host:
293
299
 
294
300
  Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], retry_on_failure: true
295
301
 
296
- You can specify how many times should the client retry the request before it raises an exception
297
- (the default is 3 times):
302
+ By default, the client will retry the request 3 times. You can specify how many times to retry before it raises an exception by passing a number to `retry_on_failure`:
298
303
 
299
304
  Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], retry_on_failure: 5
300
305
 
306
+ These two parameters can also be used together:
307
+
308
+ ```ruby
309
+ Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], retry_on_status: [502, 503], retry_on_failure: 10
310
+ ```
311
+
301
312
  ### Reloading Hosts
302
313
 
303
314
  Elasticsearch by default dynamically discovers new nodes in the cluster. You can leverage this
@@ -49,9 +49,15 @@ module Elasticsearch
49
49
  DEFAULT_HOST = 'localhost:9200'.freeze
50
50
 
51
51
  # The default port to use if connecting using a Cloud ID.
52
+ # Updated from 9243 to 443 in client version 7.10.1
52
53
  #
53
54
  # @since 7.2.0
54
- DEFAULT_CLOUD_PORT = 9243
55
+ DEFAULT_CLOUD_PORT = 443
56
+
57
+ # The default port to use if not otherwise specified.
58
+ #
59
+ # @since 7.2.0
60
+ DEFAULT_PORT = 9200
55
61
 
56
62
  # Returns the transport object.
57
63
  #
@@ -114,8 +120,11 @@ module Elasticsearch
114
120
  #
115
121
  # @option api_key [String, Hash] :api_key Use API Key Authentication, either the base64 encoding of `id` and `api_key`
116
122
  # joined by a colon as a String, or a hash with the `id` and `api_key` values.
117
- # @option opaque_id_prefix [String] :opaque_id_prefix set a prefix for X-Opaque-Id when initializing the client. This
118
- # will be prepended to the id you set before each request if you're using X-Opaque-Id
123
+ # @option opaque_id_prefix [String] :opaque_id_prefix set a prefix for X-Opaque-Id when initializing the client.
124
+ # This will be prepended to the id you set before each request
125
+ # if you're using X-Opaque-Id
126
+ # @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud.
127
+ # (Default: true)
119
128
  #
120
129
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
121
130
  #
@@ -130,6 +139,7 @@ module Elasticsearch
130
139
  @arguments[:randomize_hosts] ||= false
131
140
  @arguments[:transport_options] ||= {}
132
141
  @arguments[:http] ||= {}
142
+ @arguments[:enable_meta_header] = arguments.fetch(:enable_meta_header) { true }
133
143
  @options[:http] ||= {}
134
144
 
135
145
  set_api_key if (@api_key = @arguments[:api_key])
@@ -152,15 +162,18 @@ module Elasticsearch
152
162
  if @arguments[:transport]
153
163
  @transport = @arguments[:transport]
154
164
  else
155
- transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
156
- if transport_class == Transport::HTTP::Faraday
157
- @transport = transport_class.new(hosts: @seeds, options: @arguments) do |faraday|
158
- faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
159
- block&.call faraday
160
- end
161
- else
162
- @transport = transport_class.new(hosts: @seeds, options: @arguments)
163
- end
165
+ @transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
166
+ @transport = if @transport_class == Transport::HTTP::Faraday
167
+ @arguments[:adapter] ||= __auto_detect_adapter
168
+ set_meta_header
169
+ @transport_class.new(hosts: @seeds, options: @arguments) do |faraday|
170
+ faraday.adapter(@arguments[:adapter])
171
+ block&.call faraday
172
+ end
173
+ else
174
+ set_meta_header
175
+ @transport_class.new(hosts: @seeds, options: @arguments)
176
+ end
164
177
  end
165
178
  end
166
179
 
@@ -180,24 +193,111 @@ module Elasticsearch
180
193
 
181
194
  def set_api_key
182
195
  @api_key = __encode(@api_key) if @api_key.is_a? Hash
196
+ add_header('Authorization' => "ApiKey #{@api_key}")
197
+ @arguments.delete(:user)
198
+ @arguments.delete(:password)
199
+ end
200
+
201
+ def add_header(header)
183
202
  headers = @arguments[:transport_options]&.[](:headers) || {}
184
- headers.merge!('Authorization' => "ApiKey #{@api_key}")
203
+ headers.merge!(header)
185
204
  @arguments[:transport_options].merge!(
186
205
  headers: headers
187
206
  )
188
- @arguments.delete(:user)
189
- @arguments.delete(:password)
207
+ end
208
+
209
+ def set_meta_header
210
+ return if @arguments[:enable_meta_header] == false
211
+
212
+ service, version = meta_header_service_version
213
+
214
+ meta_headers = {
215
+ service.to_sym => version,
216
+ rb: RUBY_VERSION,
217
+ t: Elasticsearch::Transport::VERSION
218
+ }
219
+ meta_headers.merge!(meta_header_engine) if meta_header_engine
220
+ meta_headers.merge!(meta_header_adapter) if meta_header_adapter
221
+
222
+ add_header({ 'x-elastic-client-meta' => meta_headers.map { |k, v| "#{k}=#{v}" }.join(',') })
223
+ end
224
+
225
+ def meta_header_service_version
226
+ if defined?(Elastic::META_HEADER_SERVICE_VERSION)
227
+ Elastic::META_HEADER_SERVICE_VERSION
228
+ elsif defined?(Elasticsearch::VERSION)
229
+ [:es, client_meta_version(Elasticsearch::VERSION)]
230
+ else
231
+ [:es, client_meta_version(Elasticsearch::Transport::VERSION)]
232
+ end
233
+ end
234
+
235
+ def client_meta_version(version)
236
+ regexp = /^([0-9]+\.[0-9]+\.[0-9]+)(\.?[a-z0-9.-]+)?$/
237
+ match = version.match(regexp)
238
+ return "#{match[1]}p" if (match[2])
239
+
240
+ version
241
+ end
242
+
243
+ def meta_header_engine
244
+ case RUBY_ENGINE
245
+ when 'ruby'
246
+ {}
247
+ when 'jruby'
248
+ { jv: ENV_JAVA['java.version'], jr: JRUBY_VERSION }
249
+ when 'rbx'
250
+ { rbx: RUBY_VERSION }
251
+ else
252
+ { RUBY_ENGINE.to_sym => RUBY_VERSION }
253
+ end
254
+ end
255
+
256
+ def meta_header_adapter
257
+ if @transport_class == Transport::HTTP::Faraday
258
+ {fd: Faraday::VERSION}.merge(
259
+ case @arguments[:adapter]
260
+ when :patron
261
+ {pt: Patron::VERSION}
262
+ when :net_http
263
+ {nh: defined?(Net::HTTP::VERSION) ? Net::HTTP::VERSION : Net::HTTP::HTTPVersion}
264
+ when :typhoeus
265
+ {ty: Typhoeus::VERSION}
266
+ when :httpclient
267
+ {hc: HTTPClient::VERSION}
268
+ when :net_http_persistent
269
+ {np: Net::HTTP::Persistent::VERSION}
270
+ end
271
+ )
272
+ elsif defined?(Transport::HTTP::Curb) && @transport_class == Transport::HTTP::Curb
273
+ {cl: Curl::CURB_VERSION}
274
+ elsif defined?(Transport::HTTP::Manticore) && @transport_class == Transport::HTTP::Manticore
275
+ {mc: Manticore::VERSION}
276
+ end
190
277
  end
191
278
 
192
279
  def extract_cloud_creds(arguments)
193
- return unless arguments[:cloud_id]
280
+ return unless arguments[:cloud_id] && !arguments[:cloud_id].empty?
281
+
194
282
  name = arguments[:cloud_id].split(':')[0]
195
283
  cloud_url, elasticsearch_instance = Base64.decode64(arguments[:cloud_id].gsub("#{name}:", '')).split('$')
196
- [ { scheme: 'https',
284
+
285
+ if cloud_url.include?(':')
286
+ url, port = cloud_url.split(':')
287
+ host = "#{elasticsearch_instance}.#{url}"
288
+ else
289
+ host = "#{elasticsearch_instance}.#{cloud_url}"
290
+ port = arguments[:port] || DEFAULT_CLOUD_PORT
291
+ end
292
+ [
293
+ {
294
+ scheme: 'https',
197
295
  user: arguments[:user],
198
296
  password: arguments[:password],
199
- host: "#{elasticsearch_instance}.#{cloud_url}",
200
- port: arguments[:port] || DEFAULT_CLOUD_PORT } ]
297
+ host: host,
298
+ port: port.to_i
299
+ }
300
+ ]
201
301
  end
202
302
 
203
303
  # Normalizes and returns hosts configuration.
@@ -230,36 +330,38 @@ module Elasticsearch
230
330
 
231
331
  def __parse_host(host)
232
332
  host_parts = case host
233
- when String
234
- if host =~ /^[a-z]+\:\/\//
235
- # Construct a new `URI::Generic` directly from the array returned by URI::split.
236
- # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports.
237
- uri = URI::Generic.new(*URI.split(host))
238
-
239
- { :scheme => uri.scheme,
240
- :user => uri.user,
241
- :password => uri.password,
242
- :host => uri.host,
243
- :path => uri.path,
244
- :port => uri.port }
245
- else
246
- host, port = host.split(':')
247
- { :host => host,
248
- :port => port }
249
- end
250
- when URI
251
- { :scheme => host.scheme,
252
- :user => host.user,
253
- :password => host.password,
254
- :host => host.host,
255
- :path => host.path,
256
- :port => host.port }
257
- when Hash
258
- host
259
- else
260
- raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
261
- end
262
-
333
+ when String
334
+ if host =~ /^[a-z]+\:\/\//
335
+ # Construct a new `URI::Generic` directly from the array returned by URI::split.
336
+ # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports.
337
+ uri = URI::Generic.new(*URI.split(host))
338
+ default_port = uri.scheme == 'https' ? 443 : DEFAULT_PORT
339
+ {
340
+ scheme: uri.scheme,
341
+ user: uri.user,
342
+ password: uri.password,
343
+ host: uri.host,
344
+ path: uri.path,
345
+ port: uri.port || default_port
346
+ }
347
+ else
348
+ host, port = host.split(':')
349
+ { host: host, port: port }
350
+ end
351
+ when URI
352
+ {
353
+ scheme: host.scheme,
354
+ user: host.user,
355
+ password: host.password,
356
+ host: host.host,
357
+ path: host.path,
358
+ port: host.port
359
+ }
360
+ when Hash
361
+ host
362
+ else
363
+ raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
364
+ end
263
365
  if @api_key
264
366
  # Remove Basic Auth if using API KEY
265
367
  host_parts.delete(:user)
@@ -19,7 +19,6 @@ module Elasticsearch
19
19
  module Transport
20
20
  module Transport
21
21
  module Connections
22
-
23
22
  # Wraps the connection information and logic.
24
23
  #
25
24
  # The Connection instance wraps the host information (hostname, port, attributes, etc),
@@ -54,12 +53,14 @@ module Elasticsearch
54
53
  #
55
54
  # @return [String]
56
55
  #
57
- def full_url(path, params={})
56
+ def full_url(path, params = {})
58
57
  url = "#{host[:protocol]}://"
59
58
  url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user]
60
59
  url += "#{host[:host]}:#{host[:port]}"
61
60
  url += "#{host[:path]}" if host[:path]
62
- url += "/#{full_path(path, params)}"
61
+ full_path = full_path(path, params)
62
+ url += '/' unless full_path.match?(/^\//)
63
+ url += full_path
63
64
  end
64
65
 
65
66
  # Returns the complete endpoint path with serialized parameters.
@@ -33,9 +33,17 @@ module Elasticsearch
33
33
  # @return [Response]
34
34
  # @see Transport::Base#perform_request
35
35
  #
36
- def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
36
+ def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {})
37
37
  super do |connection, url|
38
- headers = headers || connection.connection.headers
38
+ headers = if connection.connection.headers
39
+ if !headers.nil?
40
+ connection.connection.headers.merge(headers)
41
+ else
42
+ connection.connection.headers
43
+ end
44
+ else
45
+ headers
46
+ end
39
47
 
40
48
  response = connection.connection.run_request(method.downcase.to_sym,
41
49
  url,
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Elasticsearch
19
19
  module Transport
20
- VERSION = "7.9.0"
20
+ VERSION = '7.11.0'.freeze
21
21
  end
22
22
  end
@@ -46,10 +46,12 @@ describe Elasticsearch::Transport::Transport::Base do
46
46
 
47
47
  context 'when the user and password are provided as separate arguments' do
48
48
  let(:arguments) do
49
- { hosts: 'fake',
49
+ {
50
+ hosts: 'fake',
50
51
  logger: logger,
51
52
  password: 'secret_password',
52
- user: 'test' }
53
+ user: 'test'
54
+ }
53
55
  end
54
56
 
55
57
  it_behaves_like 'a redacted string'
@@ -57,8 +59,10 @@ describe Elasticsearch::Transport::Transport::Base do
57
59
 
58
60
  context 'when the user and password are provided in the string URI' do
59
61
  let(:arguments) do
60
- { hosts: 'https://test:secret_password@fake_local_elasticsearch',
61
- logger: logger }
62
+ {
63
+ hosts: 'https://test:secret_password@fake_local_elasticsearch',
64
+ logger: logger
65
+ }
62
66
  end
63
67
 
64
68
  it_behaves_like 'a redacted string'
@@ -66,8 +70,10 @@ describe Elasticsearch::Transport::Transport::Base do
66
70
 
67
71
  context 'when the user and password are provided in the URI object' do
68
72
  let(:arguments) do
69
- { hosts: URI.parse('https://test:secret_password@fake_local_elasticsearch'),
70
- logger: logger }
73
+ {
74
+ hosts: URI.parse('https://test:secret_password@fake_local_elasticsearch'),
75
+ logger: logger
76
+ }
71
77
  end
72
78
 
73
79
  it_behaves_like 'a redacted string'
@@ -75,36 +81,32 @@ describe Elasticsearch::Transport::Transport::Base do
75
81
  end
76
82
 
77
83
  context 'when reload_on_failure is true and and hosts are unreachable' do
78
-
79
84
  let(:client) do
80
85
  Elasticsearch::Transport::Client.new(arguments)
81
86
  end
82
87
 
83
88
  let(:arguments) do
84
89
  {
85
- hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
86
- reload_on_failure: true,
87
- sniffer_timeout: 5
90
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
91
+ reload_on_failure: true,
92
+ sniffer_timeout: 5
88
93
  }
89
94
  end
90
95
 
91
96
  it 'raises an exception' do
92
- expect {
93
- client.info
94
- }.to raise_exception(Faraday::ConnectionFailed)
97
+ expect { client.info }.to raise_exception(Faraday::ConnectionFailed)
95
98
  end
96
99
  end
97
100
 
98
101
  context 'when the client has `retry_on_failure` set to an integer' do
99
-
100
102
  let(:client) do
101
103
  Elasticsearch::Transport::Client.new(arguments)
102
104
  end
103
105
 
104
106
  let(:arguments) do
105
107
  {
106
- hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
107
- retry_on_failure: 2
108
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
109
+ retry_on_failure: 2
108
110
  }
109
111
  end
110
112
 
@@ -120,15 +122,34 @@ describe Elasticsearch::Transport::Transport::Base do
120
122
  end
121
123
  end
122
124
 
125
+ context 'when `perform_request` is called with a `retry_on_status` option value' do
126
+ before do
127
+ expect(client.transport).to receive(:__raise_transport_error).exactly(6).times.and_call_original
128
+ end
129
+
130
+ let(:arguments) do
131
+ {
132
+ hosts: ['http://localhost:9250'],
133
+ retry_on_status: ['404']
134
+ }
135
+ end
136
+
137
+ it 'retries on 404 status the specified number of max_retries' do
138
+ expect do
139
+ client.transport.perform_request('GET', 'myindex/mydoc/1?routing=FOOBARBAZ', {}, nil, nil, retry_on_failure: 5)
140
+ end.to raise_exception(Elasticsearch::Transport::Transport::Errors::NotFound)
141
+ end
142
+ end
143
+
123
144
  context 'when `perform_request` is called with a `retry_on_failure` option value' do
124
145
  before do
125
146
  expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
126
147
  end
127
148
 
128
149
  it 'uses the option `retry_on_failure` value' do
129
- expect {
150
+ expect do
130
151
  client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
131
- }.to raise_exception(Faraday::ConnectionFailed)
152
+ end.to raise_exception(Faraday::ConnectionFailed)
132
153
  end
133
154
  end
134
155
  end
@@ -209,40 +230,35 @@ describe Elasticsearch::Transport::Transport::Base do
209
230
  end
210
231
 
211
232
  context 'when the client has no `retry_on_failure` set' do
212
-
213
233
  let(:client) do
214
234
  Elasticsearch::Transport::Client.new(arguments)
215
235
  end
216
236
 
217
237
  let(:arguments) do
218
- {
219
- hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
220
- }
238
+ { hosts: ['http://unavailable:9200', 'http://unavailable:9201'] }
221
239
  end
222
240
 
223
241
  context 'when `perform_request` is called without a `retry_on_failure` option value' do
224
-
225
242
  before do
226
243
  expect(client.transport).to receive(:get_connection).exactly(1).times.and_call_original
227
244
  end
228
245
 
229
246
  it 'does not retry' do
230
- expect {
247
+ expect do
231
248
  client.transport.perform_request('GET', '/info')
232
- }.to raise_exception(Faraday::ConnectionFailed)
249
+ end.to raise_exception(Faraday::ConnectionFailed)
233
250
  end
234
251
  end
235
252
 
236
253
  context 'when `perform_request` is called with a `retry_on_failure` option value' do
237
-
238
254
  before do
239
255
  expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
240
256
  end
241
257
 
242
258
  it 'uses the option `retry_on_failure` value' do
243
- expect {
259
+ expect do
244
260
  client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
245
- }.to raise_exception(Faraday::ConnectionFailed)
261
+ end.to raise_exception(Faraday::ConnectionFailed)
246
262
  end
247
263
  end
248
264
  end