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 +5 -5
- data/Gemfile +5 -0
- data/Rakefile +12 -2
- data/elasticsearch-transport.gemspec +2 -2
- data/lib/elasticsearch/transport.rb +1 -0
- data/lib/elasticsearch/transport/client.rb +81 -62
- data/lib/elasticsearch/transport/redacted.rb +75 -0
- data/lib/elasticsearch/transport/transport/base.rb +2 -2
- data/lib/elasticsearch/transport/transport/connections/connection.rb +1 -1
- data/lib/elasticsearch/transport/transport/http/faraday.rb +4 -5
- data/lib/elasticsearch/transport/version.rb +1 -1
- data/spec/elasticsearch/transport/base_spec.rb +81 -0
- data/spec/elasticsearch/transport/client_spec.rb +929 -0
- data/spec/spec_helper.rb +61 -0
- data/test/integration/transport_test.rb +5 -5
- data/test/test_helper.rb +6 -0
- data/test/unit/transport_base_test.rb +16 -0
- metadata +15 -13
- data/test/integration/client_test.rb +0 -242
- data/test/unit/client_test.rb +0 -373
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a8e4aede9b2a47e14f9913330cb6278300282e8817474f51cee80a35f2f95c29
|
4
|
+
data.tar.gz: 1be5538c84ddd5f6b293787ec3b4c779e001c8d19750b83fdff248e67e4cdcd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
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"
|
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
|
-
@
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
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
|
-
|
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 =>
|
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 =>
|
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
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
191
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
@@ -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
|