elasticsearch-transport 6.1.0 → 6.2.0

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
- SHA1:
3
- metadata.gz: e353dc91e242f5da6770f9bb54d045e73d891406
4
- data.tar.gz: d68e53725c17871ca4dd102b3d0862bc760416d7
2
+ SHA256:
3
+ metadata.gz: a8e4aede9b2a47e14f9913330cb6278300282e8817474f51cee80a35f2f95c29
4
+ data.tar.gz: 1be5538c84ddd5f6b293787ec3b4c779e001c8d19750b83fdff248e67e4cdcd3
5
5
  SHA512:
6
- metadata.gz: 7ff356b55062d3fad230f9849c7416f1450c29e0a5772e7a013351f2fdbdc978adeba64c1ade75d998988f1d81e1dac7a72d5ba017fb6d56eec7639f2d3cea77
7
- data.tar.gz: 56cbe39365f0aa8ad13a6ff4bc2be0e7fe5b90d77033f16d9772d7794178ebd50d197b05ab325d05259907d68dc9c2b010c1ccce88c057e3ef460ec80cf5c6b2
6
+ metadata.gz: af707cc9d8e3675a41bf0e3daa4624c1370047a1ef8f2d10437887744a78329875eb7115d67d05285c51e3301c1f043524b0d8e113eedb6346bd534e9bbcaadf
7
+ data.tar.gz: 451c56226dce076295231890873aac3d10cc27e5de816183b7bdf87c0891f2f40f34f35ea3e51b6609429994d243e3caae265b036464c31131882ac1378d5de4
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/Rakefile CHANGED
@@ -7,7 +7,17 @@ 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
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'
19
+ end
20
+
11
21
  Rake::TestTask.new(:unit) do |test|
12
22
  test.libs << 'lib' << 'test'
13
23
  test.test_files = FileList["test/unit/**/*_test.rb"]
@@ -18,13 +28,13 @@ namespace :test do
18
28
  Rake::TestTask.new(:integration) do |test|
19
29
  test.libs << 'lib' << 'test'
20
30
  test.test_files = FileList["test/integration/**/*_test.rb"]
31
+ test.deps = [ :wait_for_green, :spec ]
21
32
  test.verbose = false
22
33
  test.warning = false
23
34
  end
24
35
 
25
36
  Rake::TestTask.new(:all) do |test|
26
- test.libs << 'lib' << 'test'
27
- test.test_files = FileList["test/unit/**/*_test.rb", "test/integration/**/*_test.rb"]
37
+ test.deps = [ :unit, :integration ]
28
38
  end
29
39
 
30
40
  Rake::TestTask.new(:profile) do |test|
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.email = ["karel.minarik@elasticsearch.org"]
11
11
  s.summary = "Ruby client for Elasticsearch."
12
12
  s.homepage = "https://github.com/elasticsearch/elasticsearch-ruby/tree/master/elasticsearch-transport"
13
- s.license = "Apache 2"
13
+ s.license = "Apache-2.0"
14
14
 
15
15
  s.files = `git ls-files`.split($/)
16
16
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  s.add_dependency "system_timer"
30
30
  end
31
31
 
32
- s.add_development_dependency "bundler", "> 1"
32
+ s.add_development_dependency "bundler"
33
33
 
34
34
  if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9'
35
35
  s.add_development_dependency "rake", "~> 11.1"
@@ -14,6 +14,7 @@ require "elasticsearch/transport/transport/connections/connection"
14
14
  require "elasticsearch/transport/transport/connections/collection"
15
15
  require "elasticsearch/transport/transport/http/faraday"
16
16
  require "elasticsearch/transport/client"
17
+ require "elasticsearch/transport/redacted"
17
18
 
18
19
  require "elasticsearch/transport/version"
19
20
 
@@ -24,6 +24,11 @@ module Elasticsearch
24
24
  logger
25
25
  end
26
26
 
27
+ # The default host and port to use if not otherwise specified.
28
+ #
29
+ # @since 7.0.0
30
+ DEFAULT_HOST = 'localhost:9200'.freeze
31
+
27
32
  # Returns the transport object.
28
33
  #
29
34
  # @see Elasticsearch::Transport::Transport::Base
@@ -83,14 +88,8 @@ module Elasticsearch
83
88
  # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block
84
89
  #
85
90
  def initialize(arguments={}, &block)
91
+ @options = arguments
86
92
  @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
93
  @arguments[:logger] ||= @arguments[:log] ? DEFAULT_LOGGER.call() : nil
95
94
  @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil
96
95
  @arguments[:reload_connections] ||= false
@@ -99,26 +98,40 @@ module Elasticsearch
99
98
  @arguments[:randomize_hosts] ||= false
100
99
  @arguments[:transport_options] ||= {}
101
100
  @arguments[:http] ||= {}
101
+ @options[:http] ||= {}
102
102
 
103
- @arguments[:transport_options].update(:request => { :timeout => @arguments[:request_timeout] } ) if @arguments[:request_timeout]
104
-
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/}
103
+ @seeds = __extract_hosts(@arguments[:hosts] ||
104
+ @arguments[:host] ||
105
+ @arguments[:url] ||
106
+ @arguments[:urls] ||
107
+ ENV['ELASTICSEARCH_URL'] ||
108
+ DEFAULT_HOST)
107
109
 
108
110
  @send_get_body_as = @arguments[:send_get_body_as] || 'GET'
109
111
 
110
- transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
112
+ if @arguments[:request_timeout]
113
+ @arguments[:transport_options][:request] = { :timeout => @arguments[:request_timeout] }
114
+ end
115
+
116
+ @arguments[:transport_options][:headers] ||= {}
117
+
118
+ unless @arguments[:transport_options][:headers].keys.any? {|k| k.to_s.downcase =~ /content\-?\_?type/}
119
+ @arguments[:transport_options][:headers]['Content-Type'] = 'application/json'
120
+ end
111
121
 
112
- @transport = @arguments[:transport] || begin
122
+ if @arguments[:transport]
123
+ @transport = @arguments[:transport]
124
+ else
125
+ transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS
113
126
  if transport_class == Transport::HTTP::Faraday
114
- transport_class.new(:hosts => __extract_hosts(hosts, @arguments), :options => @arguments) do |faraday|
127
+ @transport = transport_class.new(:hosts => @seeds, :options => @arguments) do |faraday|
115
128
  block.call faraday if block
116
129
  unless (h = faraday.builder.handlers.last) && h.name.start_with?("Faraday::Adapter")
117
130
  faraday.adapter(@arguments[:adapter] || __auto_detect_adapter)
118
131
  end
119
132
  end
120
133
  else
121
- transport_class.new(:hosts => __extract_hosts(hosts, @arguments), :options => @arguments)
134
+ @transport = transport_class.new(:hosts => @seeds, :options => @arguments)
122
135
  end
123
136
  end
124
137
  end
@@ -127,10 +140,11 @@ module Elasticsearch
127
140
  #
128
141
  def perform_request(method, path, params={}, body=nil, headers=nil)
129
142
  method = @send_get_body_as if 'GET' == method && body
130
-
131
- transport.perform_request method, path, params, body, headers
143
+ transport.perform_request(method, path, params, body, headers)
132
144
  end
133
145
 
146
+ private
147
+
134
148
  # Normalizes and returns hosts configuration.
135
149
  #
136
150
  # Arrayifies the `hosts_config` argument and extracts `host` and `port` info from strings.
@@ -143,52 +157,57 @@ module Elasticsearch
143
157
  #
144
158
  # @api private
145
159
  #
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?
176
-
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
183
-
184
- # Remove the trailing slash
185
- host_parts[:path].chomp!('/') if host_parts[:path]
186
-
187
- host_parts
188
- end
160
+ def __extract_hosts(hosts_config)
161
+ hosts = case hosts_config
162
+ when String
163
+ hosts_config.split(',').map { |h| h.strip! || h }
164
+ when Array
165
+ hosts_config
166
+ when Hash, URI
167
+ [ hosts_config ]
168
+ else
169
+ Array(hosts_config)
170
+ end
171
+
172
+ host_list = hosts.map { |host| __parse_host(host) }
173
+ @options[:randomize_hosts] ? host_list.shuffle! : host_list
174
+ end
189
175
 
190
- result.shuffle! if options[:randomize_hosts]
191
- result
176
+ def __parse_host(host)
177
+ host_parts = case host
178
+ when String
179
+ if host =~ /^[a-z]+\:\/\//
180
+ uri = URI.parse(host)
181
+ { :scheme => uri.scheme,
182
+ :user => uri.user,
183
+ :password => uri.password,
184
+ :host => uri.host,
185
+ :path => uri.path,
186
+ :port => uri.port }
187
+ else
188
+ host, port = host.split(':')
189
+ { :host => host,
190
+ :port => port }
191
+ end
192
+ when URI
193
+ { :scheme => host.scheme,
194
+ :user => host.user,
195
+ :password => host.password,
196
+ :host => host.host,
197
+ :path => host.path,
198
+ :port => host.port }
199
+ when Hash
200
+ host
201
+ else
202
+ raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given."
203
+ end
204
+
205
+ @options[:http][:user] ||= host_parts[:user]
206
+ @options[:http][:password] ||= host_parts[:password]
207
+
208
+ host_parts[:port] = host_parts[:port].to_i if host_parts[:port]
209
+ host_parts[:path].chomp!('/') if host_parts[:path]
210
+ host_parts
192
211
  end
193
212
 
194
213
  # Auto-detect the best adapter (HTTP "driver") available, based on libraries
@@ -0,0 +1,75 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ module Elasticsearch
19
+ module Transport
20
+
21
+ # Class for wrapping a hash that could have sensitive data.
22
+ # When printed, the sensitive values will be redacted.
23
+ #
24
+ # @since 6.2.0
25
+ class Redacted < ::Hash
26
+
27
+ def initialize(elements = nil)
28
+ super()
29
+ (elements || {}).each_pair{ |key, value| self[key] = value }
30
+ end
31
+
32
+ # The keys whose values will be redacted.
33
+ #
34
+ # @since 6.2.0
35
+ SENSITIVE_KEYS = [ :password,
36
+ :pwd ].freeze
37
+
38
+ # The replacement string used in place of the value for sensitive keys.
39
+ #
40
+ # @since 6.2.0
41
+ STRING_REPLACEMENT = '<REDACTED>'.freeze
42
+
43
+ # Get a string representation of the hash.
44
+ #
45
+ # @return [ String ] The string representation of the hash.
46
+ #
47
+ # @since 6.2.0
48
+ def inspect
49
+ redacted_string(:inspect)
50
+ end
51
+
52
+ # Get a string representation of the hash.
53
+ #
54
+ # @return [ String ] The string representation of the hash.
55
+ #
56
+ # @since 6.2.0
57
+ def to_s
58
+ redacted_string(:to_s)
59
+ end
60
+
61
+ private
62
+
63
+ def redacted_string(method)
64
+ '{' + reduce([]) do |list, (k, v)|
65
+ list << "#{k.send(method)}=>#{redact(k, v, method)}"
66
+ end.join(', ') + '}'
67
+ end
68
+
69
+ def redact(k, v, method)
70
+ return STRING_REPLACEMENT if SENSITIVE_KEYS.include?(k.to_sym)
71
+ v.send(method)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -32,7 +32,7 @@ module Elasticsearch
32
32
  @state_mutex = Mutex.new
33
33
 
34
34
  @hosts = arguments[:hosts] || []
35
- @options = arguments[:options] || {}
35
+ @options = arguments[:options] ? arguments[:options].dup : {}
36
36
  @options[:http] ||= {}
37
37
  @options[:retry_on_status] ||= []
38
38
 
@@ -110,7 +110,7 @@ module Elasticsearch
110
110
 
111
111
  new_connections = __build_connections
112
112
  stale_connections = @connections.all.select { |c| ! new_connections.include?(c) }
113
- new_connections = new_connections.reject { |c| @connections.include?(c) }
113
+ new_connections = new_connections.reject { |c| @connections.all.include?(c) }
114
114
 
115
115
  @connections.remove(stale_connections)
116
116
  @connections.add(new_connections)
@@ -23,7 +23,7 @@ module Elasticsearch
23
23
  # @option arguments [Hash] :options Options (usually passed in from transport)
24
24
  #
25
25
  def initialize(arguments={})
26
- @host = arguments[:host]
26
+ @host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host]
27
27
  @connection = arguments[:connection]
28
28
  @options = arguments[:options] || {}
29
29
  @state_mutex = Mutex.new
@@ -20,11 +20,10 @@ module Elasticsearch
20
20
  super do |connection, url|
21
21
  headers = headers || connection.connection.headers
22
22
 
23
- response = connection.connection.run_request \
24
- method.downcase.to_sym,
25
- url,
26
- ( body ? __convert_to_json(body) : nil ),
27
- headers
23
+ response = connection.connection.run_request(method.downcase.to_sym,
24
+ url,
25
+ ( body ? __convert_to_json(body) : nil ),
26
+ headers)
28
27
 
29
28
  Response.new response.status, response.body, response.headers
30
29
  end
@@ -1,5 +1,5 @@
1
1
  module Elasticsearch
2
2
  module Transport
3
- VERSION = "6.1.0"
3
+ VERSION = "6.2.0"
4
4
  end
5
5
  end
@@ -0,0 +1,81 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'spec_helper'
19
+
20
+ describe Elasticsearch::Transport::Transport::Base do
21
+
22
+ context 'when a host is printed in a logged message' do
23
+
24
+ shared_examples_for 'a redacted string' do
25
+
26
+ let(:client) do
27
+ Elasticsearch::Transport::Client.new(arguments)
28
+ end
29
+
30
+ let(:logger) do
31
+ double('logger', error?: true, error: '')
32
+ end
33
+
34
+ it 'does not include the password in the logged string' do
35
+ expect(logger).not_to receive(:error).with(/secret_password/)
36
+ expect {
37
+ client.cluster.stats
38
+ }.to raise_exception(Faraday::ConnectionFailed)
39
+ end
40
+
41
+ it 'replaces the password with the string \'REDACTED\'' do
42
+ expect(logger).to receive(:error).with(/REDACTED/)
43
+ expect {
44
+ client.cluster.stats
45
+ }.to raise_exception(Faraday::ConnectionFailed)
46
+ end
47
+ end
48
+
49
+ context 'when the user and password are provided as separate arguments' do
50
+
51
+ let(:arguments) do
52
+ { hosts: 'fake',
53
+ logger: logger,
54
+ password: 'secret_password',
55
+ user: 'test' }
56
+ end
57
+
58
+ it_behaves_like 'a redacted string'
59
+ end
60
+
61
+ context 'when the user and password are provided in the string URI' do
62
+
63
+ let(:arguments) do
64
+ { hosts: 'http://test:secret_password@fake.com',
65
+ logger: logger }
66
+ end
67
+
68
+ it_behaves_like 'a redacted string'
69
+ end
70
+
71
+ context 'when the user and password are provided in the URI object' do
72
+
73
+ let(:arguments) do
74
+ { hosts: URI.parse('http://test:secret_password@fake.com'),
75
+ logger: logger }
76
+ end
77
+
78
+ it_behaves_like 'a redacted string'
79
+ end
80
+ end
81
+ end