elasticsearch-transport 7.9.0.pre → 7.11.0.pre.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e172f03d64feff3991f8ef723219c1f3f26824d624cfaee35195168c2224361
4
- data.tar.gz: d8039d3a4e5a5f406aae9ff94a56ef0c1068dd985b71c36a4d21d553b3404775
3
+ metadata.gz: 1e364f5d86474b878377249e59488749009a24682da9d35e3eaeb430a0c9d9d4
4
+ data.tar.gz: 9952aede93dd570e563acef81038e9423793f4e0682b9916d5155fa37edb0792
5
5
  SHA512:
6
- metadata.gz: 1ae8684bffd3b1c1badd932702491266d93dafbe63cc7b02de814601bd120c0955914ff6e3f3220e7cfafe0654f87ecca3fea652c6df4783d106b9749b69eaca
7
- data.tar.gz: 8607504cde6a896a5c11d2e7ab753829dddb9c1d7c2ae960e3cdb3a9bfff2f3d46e9bec0790222e7d34f8309ea86d5c096e9646fdadd51cfae6b0891defed73d
6
+ metadata.gz: be3c7ecd096850addb44e502f471ae2e88e43e3ee93dc342eb4b32b36eb994d66131e35f6458cec5026a02e7edd25e71d058efaa5a675fdacfe38c567a478a7f
7
+ data.tar.gz: 9ed532c04edc174e1eb903bb116add80f946f1610360a0294bed09d6d3f3b26444432745c67431e3611d3c478434e5afb18db9271450854c3255ef67e049e506
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,103 @@ 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', Elasticsearch::VERSION]
230
+ else
231
+ ['es', Elasticsearch::Transport::VERSION]
232
+ end
233
+ end
234
+
235
+ def meta_header_engine
236
+ case RUBY_ENGINE
237
+ when 'ruby'
238
+ {}
239
+ when 'jruby'
240
+ { jv: ENV_JAVA['java.version'], jr: JRUBY_VERSION }
241
+ when 'rbx'
242
+ { rbx: RUBY_VERSION }
243
+ else
244
+ { RUBY_ENGINE.to_sym => RUBY_VERSION }
245
+ end
246
+ end
247
+
248
+ def meta_header_adapter
249
+ if @transport_class == Transport::HTTP::Faraday
250
+ {fd: Faraday::VERSION}.merge(
251
+ case @arguments[:adapter]
252
+ when :patron
253
+ {pt: Patron::VERSION}
254
+ when :net_http
255
+ {nh: defined?(Net::HTTP::VERSION) ? Net::HTTP::VERSION : Net::HTTP::HTTPVersion}
256
+ when :typhoeus
257
+ {ty: Typhoeus::VERSION}
258
+ when :httpclient
259
+ {hc: HTTPClient::VERSION}
260
+ when :net_http_persistent
261
+ {np: Net::HTTP::Persistent::VERSION}
262
+ end
263
+ )
264
+ elsif defined?(Transport::HTTP::Curb) && @transport_class == Transport::HTTP::Curb
265
+ {cl: Curl::CURB_VERSION}
266
+ elsif defined?(Transport::HTTP::Manticore) && @transport_class == Transport::HTTP::Manticore
267
+ {mc: Manticore::VERSION}
268
+ end
190
269
  end
191
270
 
192
271
  def extract_cloud_creds(arguments)
193
- return unless arguments[:cloud_id]
272
+ return unless arguments[:cloud_id] && !arguments[:cloud_id].empty?
273
+
194
274
  name = arguments[:cloud_id].split(':')[0]
195
275
  cloud_url, elasticsearch_instance = Base64.decode64(arguments[:cloud_id].gsub("#{name}:", '')).split('$')
196
- [ { scheme: 'https',
276
+
277
+ if cloud_url.include?(':')
278
+ url, port = cloud_url.split(':')
279
+ host = "#{elasticsearch_instance}.#{url}"
280
+ else
281
+ host = "#{elasticsearch_instance}.#{cloud_url}"
282
+ port = arguments[:port] || DEFAULT_CLOUD_PORT
283
+ end
284
+ [
285
+ {
286
+ scheme: 'https',
197
287
  user: arguments[:user],
198
288
  password: arguments[:password],
199
- host: "#{elasticsearch_instance}.#{cloud_url}",
200
- port: arguments[:port] || DEFAULT_CLOUD_PORT } ]
289
+ host: host,
290
+ port: port.to_i
291
+ }
292
+ ]
201
293
  end
202
294
 
203
295
  # Normalizes and returns hosts configuration.
@@ -230,36 +322,38 @@ module Elasticsearch
230
322
 
231
323
  def __parse_host(host)
232
324
  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
-
325
+ when String
326
+ if host =~ /^[a-z]+\:\/\//
327
+ # Construct a new `URI::Generic` directly from the array returned by URI::split.
328
+ # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports.
329
+ uri = URI::Generic.new(*URI.split(host))
330
+ default_port = uri.scheme == 'https' ? 443 : DEFAULT_PORT
331
+ {
332
+ scheme: uri.scheme,
333
+ user: uri.user,
334
+ password: uri.password,
335
+ host: uri.host,
336
+ path: uri.path,
337
+ port: uri.port || default_port
338
+ }
339
+ else
340
+ host, port = host.split(':')
341
+ { host: host, port: port }
342
+ end
343
+ when URI
344
+ {
345
+ scheme: host.scheme,
346
+ user: host.user,
347
+ password: host.password,
348
+ host: host.host,
349
+ path: host.path,
350
+ port: host.port
351
+ }
352
+ when Hash
353
+ host
354
+ else
355
+ raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
356
+ end
263
357
  if @api_key
264
358
  # Remove Basic Auth if using API KEY
265
359
  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.
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Elasticsearch
19
19
  module Transport
20
- VERSION = "7.9.0.pre"
20
+ VERSION = '7.11.0.pre.1'.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