elasticsearch-transport 6.1.0 → 6.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|