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
@@ -0,0 +1,79 @@
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
+
5
+ # Licensed to Elasticsearch B.V. under one or more contributor
6
+ # license agreements. See the NOTICE file distributed with
7
+ # this work for additional information regarding copyright
8
+ # ownership. Elasticsearch B.V. licenses this file to you under
9
+ # the Apache License, Version 2.0 (the "License"); you may
10
+ # not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing,
16
+ # software distributed under the License is distributed on an
17
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
+ # KIND, either express or implied. See the License for the
19
+ # specific language governing permissions and limitations
20
+ # under the License.
21
+
22
+ module Elasticsearch
23
+ module Transport
24
+
25
+ # Class for wrapping a hash that could have sensitive data.
26
+ # When printed, the sensitive values will be redacted.
27
+ #
28
+ # @since 6.2.0
29
+ class Redacted < ::Hash
30
+
31
+ def initialize(elements = nil)
32
+ super()
33
+ (elements || {}).each_pair{ |key, value| self[key] = value }
34
+ end
35
+
36
+ # The keys whose values will be redacted.
37
+ #
38
+ # @since 6.2.0
39
+ SENSITIVE_KEYS = [ :password,
40
+ :pwd ].freeze
41
+
42
+ # The replacement string used in place of the value for sensitive keys.
43
+ #
44
+ # @since 6.2.0
45
+ STRING_REPLACEMENT = '<REDACTED>'.freeze
46
+
47
+ # Get a string representation of the hash.
48
+ #
49
+ # @return [ String ] The string representation of the hash.
50
+ #
51
+ # @since 6.2.0
52
+ def inspect
53
+ redacted_string(:inspect)
54
+ end
55
+
56
+ # Get a string representation of the hash.
57
+ #
58
+ # @return [ String ] The string representation of the hash.
59
+ #
60
+ # @since 6.2.0
61
+ def to_s
62
+ redacted_string(:to_s)
63
+ end
64
+
65
+ private
66
+
67
+ def redacted_string(method)
68
+ '{' + reduce([]) do |list, (k, v)|
69
+ list << "#{k.send(method)}=>#{redact(k, v, method)}"
70
+ end.join(', ') + '}'
71
+ end
72
+
73
+ def redact(k, v, method)
74
+ return STRING_REPLACEMENT if SENSITIVE_KEYS.include?(k.to_sym)
75
+ v.send(method)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -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
  module Transport
@@ -16,7 +20,7 @@ module Elasticsearch
16
20
  attr_reader :hosts, :options, :connections, :counter, :last_request_at, :protocol
17
21
  attr_accessor :serializer, :sniffer, :logger, :tracer,
18
22
  :reload_connections, :reload_after,
19
- :resurrect_after, :max_retries
23
+ :resurrect_after
20
24
 
21
25
  # Creates a new transport object
22
26
  #
@@ -32,7 +36,7 @@ module Elasticsearch
32
36
  @state_mutex = Mutex.new
33
37
 
34
38
  @hosts = arguments[:hosts] || []
35
- @options = arguments[:options] || {}
39
+ @options = arguments[:options] ? arguments[:options].dup : {}
36
40
  @options[:http] ||= {}
37
41
  @options[:retry_on_status] ||= []
38
42
 
@@ -52,7 +56,6 @@ module Elasticsearch
52
56
  @reload_connections = options[:reload_connections]
53
57
  @reload_after = options[:reload_connections].is_a?(Integer) ? options[:reload_connections] : DEFAULT_RELOAD_AFTER
54
58
  @resurrect_after = options[:resurrect_after] || DEFAULT_RESURRECT_AFTER
55
- @max_retries = options[:retry_on_failure].is_a?(Integer) ? options[:retry_on_failure] : DEFAULT_MAX_RETRIES
56
59
  @retry_on_status = Array(options[:retry_on_status]).map { |d| d.to_i }
57
60
  end
58
61
 
@@ -110,7 +113,7 @@ module Elasticsearch
110
113
 
111
114
  new_connections = __build_connections
112
115
  stale_connections = @connections.all.select { |c| ! new_connections.include?(c) }
113
- new_connections = new_connections.reject { |c| @connections.include?(c) }
116
+ new_connections = new_connections.reject { |c| @connections.all.include?(c) }
114
117
 
115
118
  @connections.remove(stale_connections)
116
119
  @connections.add(new_connections)
@@ -129,7 +132,7 @@ module Elasticsearch
129
132
  Connections::Collection.new \
130
133
  :connections => hosts.map { |host|
131
134
  host[:protocol] = host[:scheme] || options[:scheme] || options[:http][:scheme] || DEFAULT_PROTOCOL
132
- host[:port] ||= options[:port] || options[:http][:scheme] || DEFAULT_PORT
135
+ host[:port] ||= options[:port] || options[:http][:port] || DEFAULT_PORT
133
136
  if (options[:user] || options[:http][:user]) && !host[:user]
134
137
  host[:user] ||= options[:user] || options[:http][:user]
135
138
  host[:password] ||= options[:password] || options[:http][:password]
@@ -184,11 +187,14 @@ module Elasticsearch
184
187
  #
185
188
  # @api private
186
189
  #
187
- def __trace(method, path, params, body, url, response, json, took, duration)
190
+ def __trace(method, path, params, headers, body, url, response, json, took, duration)
188
191
  trace_url = "http://localhost:9200/#{path}?pretty" +
189
192
  ( params.empty? ? '' : "&#{::Faraday::Utils::ParamsHash[params].to_query}" )
190
193
  trace_body = body ? " -d '#{__convert_to_json(body, :pretty => true)}'" : ''
191
- tracer.info "curl -X #{method.to_s.upcase} '#{trace_url}'#{trace_body}\n"
194
+ trace_command = "curl -X #{method.to_s.upcase}"
195
+ trace_command += " -H '#{headers.inject('') { |memo,item| memo << item[0] + ': ' + item[1] }}'" if headers && !headers.empty?
196
+ trace_command += " '#{trace_url}'#{trace_body}\n"
197
+ tracer.info trace_command
192
198
  tracer.debug "# #{Time.now.iso8601} [#{response.status}] (#{format('%.3f', duration)}s)\n#"
193
199
  tracer.debug json ? serializer.dump(json, :pretty => true).gsub(/^/, '# ').sub(/\}$/, "\n# }")+"\n" : "# #{response.body}\n"
194
200
  end
@@ -233,6 +239,7 @@ module Elasticsearch
233
239
  # @param path [String] The API endpoint
234
240
  # @param params [Hash] Request parameters (will be serialized by {Connections::Connection#full_url})
235
241
  # @param body [Hash] Request body (will be serialized by the {#serializer})
242
+ # @param headers [Hash] Request headers (will be serialized by the {#serializer})
236
243
  # @param block [Proc] Code block to evaluate, passed from the implementation
237
244
  #
238
245
  # @return [Response]
@@ -240,10 +247,17 @@ module Elasticsearch
240
247
  # @raise [ServerError] If request failed on server
241
248
  # @raise [Error] If no connection is available
242
249
  #
243
- def perform_request(method, path, params={}, body=nil, &block)
250
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={}, &block)
244
251
  raise NoMethodError, "Implement this method in your transport class" unless block_given?
245
252
  start = Time.now if logger || tracer
246
253
  tries = 0
254
+ reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure])
255
+
256
+ max_retries = if opts.key?(:retry_on_failure)
257
+ opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure]
258
+ elsif options.key?(:retry_on_failure)
259
+ options[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : options[:retry_on_failure]
260
+ end
247
261
 
248
262
  params = params.clone
249
263
 
@@ -267,9 +281,9 @@ module Elasticsearch
267
281
  __raise_transport_error(response) if response.status.to_i >= 300 && @retry_on_status.include?(response.status.to_i)
268
282
 
269
283
  rescue Elasticsearch::Transport::Transport::ServerError => e
270
- if @retry_on_status.include?(response.status)
284
+ if response && @retry_on_status.include?(response.status)
271
285
  logger.warn "[#{e.class}] Attempt #{tries} to get response from #{url}" if logger
272
- if tries <= max_retries
286
+ if tries <= (max_retries || DEFAULT_MAX_RETRIES)
273
287
  retry
274
288
  else
275
289
  logger.fatal "[#{e.class}] Cannot get response from #{url} after #{tries} tries" if logger
@@ -284,12 +298,12 @@ module Elasticsearch
284
298
 
285
299
  connection.dead!
286
300
 
287
- if @options[:reload_on_failure] and tries < connections.all.size
301
+ if reload_on_failure and tries < connections.all.size
288
302
  logger.warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})" if logger
289
303
  reload_connections! and retry
290
304
  end
291
305
 
292
- if @options[:retry_on_failure]
306
+ if max_retries
293
307
  logger.warn "[#{e.class}] Attempt #{tries} connecting to #{connection.host.inspect}" if logger
294
308
  if tries <= max_retries
295
309
  retry
@@ -311,7 +325,7 @@ module Elasticsearch
311
325
 
312
326
  if response.status.to_i >= 300
313
327
  __log method, path, params, body, url, response, nil, 'N/A', duration if logger
314
- __trace method, path, params, body, url, response, nil, 'N/A', duration if tracer
328
+ __trace method, path, params, headers, body, url, response, nil, 'N/A', duration if tracer
315
329
 
316
330
  # Log the failure only when `ignore` doesn't match the response status
317
331
  __log_failed response if logger && !ignore.include?(response.status.to_i)
@@ -323,7 +337,7 @@ module Elasticsearch
323
337
  took = (json['took'] ? sprintf('%.3fs', json['took']/1000.0) : 'n/a') rescue 'n/a' if logger || tracer
324
338
 
325
339
  __log method, path, params, body, url, response, json, took, duration if logger && !ignore.include?(response.status.to_i)
326
- __trace method, path, params, body, url, response, json, took, duration if tracer
340
+ __trace method, path, params, headers, body, url, response, json, took, duration if tracer
327
341
 
328
342
  Response.new response.status, json || response.body, response.headers
329
343
  ensure
@@ -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
  module Transport
@@ -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
  module Transport
@@ -23,7 +27,7 @@ module Elasticsearch
23
27
  # @option arguments [Hash] :options Options (usually passed in from transport)
24
28
  #
25
29
  def initialize(arguments={})
26
- @host = arguments[:host]
30
+ @host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
27
31
  @connection = arguments[:connection]
28
32
  @options = arguments[:options] || {}
29
33
  @state_mutex = Mutex.new
@@ -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
  module Transport
@@ -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
  module Transport
@@ -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
  module Transport
@@ -15,8 +19,8 @@ module Elasticsearch
15
19
  # @return [Response]
16
20
  # @see Transport::Base#perform_request
17
21
  #
18
- def perform_request(method, path, params={}, body=nil)
19
- super do |connection,url|
22
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
23
+ super do |connection, url|
20
24
  connection.connection.url = url
21
25
 
22
26
  case method
@@ -26,6 +30,7 @@ module Elasticsearch
26
30
  connection.connection.set :nobody, false
27
31
 
28
32
  connection.connection.put_data = __convert_to_json(body) if body
33
+ connection.connection.headers = headers if headers
29
34
  else raise ArgumentError, "Unsupported HTTP method: #{method}"
30
35
  end
31
36
 
@@ -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
  module Transport
@@ -16,15 +20,14 @@ module Elasticsearch
16
20
  # @return [Response]
17
21
  # @see Transport::Base#perform_request
18
22
  #
19
- def perform_request(method, path, params={}, body=nil)
23
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
20
24
  super do |connection, url|
21
- headers = connection.connection.headers
25
+ headers = headers || connection.connection.headers
22
26
 
23
- response = connection.connection.run_request \
24
- method.downcase.to_sym,
25
- url,
26
- ( body ? __convert_to_json(body) : nil ),
27
- headers
27
+ response = connection.connection.run_request(method.downcase.to_sym,
28
+ url,
29
+ ( body ? __convert_to_json(body) : nil ),
30
+ headers)
28
31
 
29
32
  Response.new response.status, response.body, response.headers
30
33
  end
@@ -44,7 +47,7 @@ module Elasticsearch
44
47
  # @return [Array]
45
48
  #
46
49
  def host_unreachable_exceptions
47
- [::Faraday::Error::ConnectionFailed, ::Faraday::Error::TimeoutError]
50
+ [::Faraday::ConnectionFailed, ::Faraday::TimeoutError]
48
51
  end
49
52
  end
50
53
  end
@@ -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
  require 'manticore'
2
6
 
3
7
  module Elasticsearch
@@ -63,9 +67,10 @@ module Elasticsearch
63
67
  # @return [Response]
64
68
  # @see Transport::Base#perform_request
65
69
  #
66
- def perform_request(method, path, params={}, body=nil)
70
+ def perform_request(method, path, params={}, body=nil, headers=nil, opts={})
67
71
  super do |connection, url|
68
72
  params[:body] = __convert_to_json(body) if body
73
+ params[:headers] = headers if headers
69
74
  params = params.merge @request_options
70
75
  case method
71
76
  when "GET"
@@ -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
  module Transport
@@ -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
  module Transport
@@ -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
  module Transport
@@ -28,11 +32,12 @@ module Elasticsearch
28
32
  #
29
33
  def hosts
30
34
  Timeout::timeout(timeout, SnifferTimeoutError) do
31
- nodes = transport.perform_request('GET', '_nodes/http').body
35
+ nodes = transport.perform_request('GET', '_nodes/http', {}, nil, nil,
36
+ reload_on_failure: false).body
32
37
 
33
- hosts = nodes['nodes'].map do |id,info|
38
+ hosts = nodes['nodes'].map do |id, info|
34
39
  if info[PROTOCOL]
35
- host, port = info[PROTOCOL]['publish_address'].split(':')
40
+ host, port = parse_publish_address(info[PROTOCOL]['publish_address'])
36
41
 
37
42
  { :id => id,
38
43
  :name => info['name'],
@@ -48,6 +53,29 @@ module Elasticsearch
48
53
  hosts
49
54
  end
50
55
  end
56
+
57
+ private
58
+
59
+ def parse_publish_address(publish_address)
60
+ # publish_address is in the format hostname/ip:port
61
+ if publish_address =~ /\//
62
+ parts = publish_address.partition('/')
63
+ [ parts[0], parse_address_port(parts[2])[1] ]
64
+ else
65
+ parse_address_port(publish_address)
66
+ end
67
+ end
68
+
69
+ def parse_address_port(publish_address)
70
+ # address is ipv6
71
+ if publish_address =~ /[\[\]]/
72
+ if parts = publish_address.match(/\A\[(.+)\](?::(\d+))?\z/)
73
+ [ parts[1], parts[2] ]
74
+ end
75
+ else
76
+ publish_address.split(':')
77
+ end
78
+ end
51
79
  end
52
80
  end
53
81
  end
@@ -1,5 +1,9 @@
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
- VERSION = "5.0.5"
7
+ VERSION = '6.8.2'
4
8
  end
5
9
  end
@@ -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
  require "uri"
2
6
  require "time"
3
7
  require "timeout"
@@ -14,6 +18,7 @@ require "elasticsearch/transport/transport/connections/connection"
14
18
  require "elasticsearch/transport/transport/connections/collection"
15
19
  require "elasticsearch/transport/transport/http/faraday"
16
20
  require "elasticsearch/transport/client"
21
+ require "elasticsearch/transport/redacted"
17
22
 
18
23
  require "elasticsearch/transport/version"
19
24
 
@@ -1 +1,5 @@
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
  require 'elasticsearch/transport'