logstash-output-elasticsearch 5.4.1-java → 6.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/logstash/outputs/elasticsearch.rb +6 -5
- data/lib/logstash/outputs/elasticsearch/common.rb +3 -4
- data/lib/logstash/outputs/elasticsearch/common_configs.rb +3 -1
- data/lib/logstash/outputs/elasticsearch/http_client.rb +151 -110
- data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +43 -12
- data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +26 -47
- data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +4 -20
- data/lib/logstash/outputs/elasticsearch/template_manager.rb +1 -1
- data/logstash-output-elasticsearch.gemspec +1 -1
- data/spec/integration/outputs/sniffer_spec.rb +5 -1
- data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +25 -2
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +24 -23
- data/spec/unit/outputs/elasticsearch/http_client_spec.rb +111 -44
- data/spec/unit/outputs/elasticsearch_proxy_spec.rb +33 -33
- data/spec/unit/outputs/elasticsearch_spec.rb +74 -16
- data/spec/unit/outputs/elasticsearch_ssl_spec.rb +23 -4
- metadata +2 -5
- data/lib/logstash/outputs/elasticsearch/safe_url.rb +0 -16
- data/spec/unit/safe_url_spec.rb +0 -46
@@ -6,7 +6,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
6
6
|
|
7
7
|
def initialize(response_code, url, body)
|
8
8
|
@response_code = response_code
|
9
|
-
@url =
|
9
|
+
@url = url
|
10
10
|
@body = body
|
11
11
|
end
|
12
12
|
|
@@ -19,7 +19,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
19
19
|
|
20
20
|
def initialize(original_error, url)
|
21
21
|
@original_error = original_error
|
22
|
-
@url =
|
22
|
+
@url = url
|
23
23
|
end
|
24
24
|
|
25
25
|
def message
|
@@ -27,13 +27,12 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
attr_reader :logger, :adapter, :sniffing, :sniffer_delay, :resurrect_delay, :
|
30
|
+
attr_reader :logger, :adapter, :sniffing, :sniffer_delay, :resurrect_delay, :healthcheck_path
|
31
31
|
|
32
32
|
DEFAULT_OPTIONS = {
|
33
33
|
:healthcheck_path => '/'.freeze,
|
34
34
|
:scheme => 'http',
|
35
35
|
:resurrect_delay => 5,
|
36
|
-
:auth => nil, # Can be set to {:user => 'user', :password => 'pass'}
|
37
36
|
:sniffing => false,
|
38
37
|
:sniffer_delay => 10,
|
39
38
|
}.freeze
|
@@ -41,30 +40,27 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
41
40
|
def initialize(logger, adapter, initial_urls=[], options={})
|
42
41
|
@logger = logger
|
43
42
|
@adapter = adapter
|
44
|
-
|
43
|
+
@initial_urls = initial_urls
|
44
|
+
|
45
|
+
raise ArgumentError, "No URL Normalizer specified!" unless options[:url_normalizer]
|
46
|
+
@url_normalizer = options[:url_normalizer]
|
45
47
|
DEFAULT_OPTIONS.merge(options).tap do |merged|
|
46
48
|
@healthcheck_path = merged[:healthcheck_path]
|
47
|
-
@scheme = merged[:scheme]
|
48
49
|
@resurrect_delay = merged[:resurrect_delay]
|
49
|
-
@auth = merged[:auth]
|
50
50
|
@sniffing = merged[:sniffing]
|
51
51
|
@sniffer_delay = merged[:sniffer_delay]
|
52
52
|
end
|
53
53
|
|
54
|
-
# Override the scheme if one is explicitly set in urls
|
55
|
-
if initial_urls.any? {|u| u.scheme == 'https'} && @scheme == 'http'
|
56
|
-
raise ArgumentError, "HTTP was set as scheme, but an HTTPS URL was passed in!"
|
57
|
-
end
|
58
|
-
|
59
54
|
# Used for all concurrent operations in this class
|
60
55
|
@state_mutex = Mutex.new
|
61
56
|
|
62
57
|
# Holds metadata about all URLs
|
63
58
|
@url_info = {}
|
64
59
|
@stopping = false
|
65
|
-
|
66
|
-
|
67
|
-
|
60
|
+
end
|
61
|
+
|
62
|
+
def start
|
63
|
+
update_urls(@initial_urls)
|
68
64
|
start_resurrectionist
|
69
65
|
start_sniffer if @sniffing
|
70
66
|
end
|
@@ -204,7 +200,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
204
200
|
if matches
|
205
201
|
host = matches[1].empty? ? matches[2] : matches[1]
|
206
202
|
port = matches[3]
|
207
|
-
LogStash::Util::SafeURI.new("#{host}:#{port}")
|
203
|
+
::LogStash::Util::SafeURI.new("#{host}:#{port}")
|
208
204
|
end
|
209
205
|
end.compact
|
210
206
|
end
|
@@ -228,26 +224,25 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
228
224
|
def healthcheck!
|
229
225
|
# Try to keep locking granularity low such that we don't affect IO...
|
230
226
|
@state_mutex.synchronize { @url_info.select {|url,meta| meta[:state] != :alive } }.each do |url,meta|
|
231
|
-
safe_url = ::LogStash::Outputs::ElasticSearch::SafeURL.without_credentials(url)
|
232
227
|
begin
|
233
228
|
logger.info("Running health check to see if an Elasticsearch connection is working",
|
234
|
-
url:
|
235
|
-
response = perform_request_to_url(url,
|
229
|
+
url: url, healthcheck_path: healthcheck_path)
|
230
|
+
response = perform_request_to_url(url, :head, healthcheck_path)
|
236
231
|
# If no exception was raised it must have succeeded!
|
237
|
-
logger.warn("Restored connection to ES instance", :url =>
|
232
|
+
logger.warn("Restored connection to ES instance", :url => url.sanitized)
|
238
233
|
@state_mutex.synchronize { meta[:state] = :alive }
|
239
234
|
rescue HostUnreachableError, BadResponseCodeError => e
|
240
|
-
logger.warn("Attempted to resurrect connection to dead ES instance, but got an error.", url:
|
235
|
+
logger.warn("Attempted to resurrect connection to dead ES instance, but got an error.", url: url.sanitized, error_type: e.class, error: e.message)
|
241
236
|
end
|
242
237
|
end
|
243
238
|
end
|
244
239
|
|
245
240
|
def stop_resurrectionist
|
246
|
-
@resurrectionist.join
|
241
|
+
@resurrectionist.join if @resurrectionist
|
247
242
|
end
|
248
243
|
|
249
244
|
def resurrectionist_alive?
|
250
|
-
@resurrectionist.alive?
|
245
|
+
@resurrectionist ? @resurrectionist.alive? : nil
|
251
246
|
end
|
252
247
|
|
253
248
|
def perform_request(method, path, params={}, body=nil)
|
@@ -270,18 +265,11 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
270
265
|
end
|
271
266
|
|
272
267
|
def normalize_url(uri)
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
# Set credentials if need be
|
277
|
-
if @auth && !uri.user
|
278
|
-
uri.user ||= @auth[:user]
|
279
|
-
uri.password ||= @auth[:password]
|
268
|
+
u = @url_normalizer.call(uri)
|
269
|
+
if !u.is_a?(::LogStash::Util::SafeURI)
|
270
|
+
raise "URL Normalizer returned a '#{u.class}' rather than a SafeURI! This shouldn't happen!"
|
280
271
|
end
|
281
|
-
|
282
|
-
uri.scheme = @scheme
|
283
|
-
|
284
|
-
uri
|
272
|
+
u
|
285
273
|
end
|
286
274
|
|
287
275
|
def update_urls(new_urls)
|
@@ -313,7 +301,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
313
301
|
|
314
302
|
if state_changes[:removed].size > 0 || state_changes[:added].size > 0
|
315
303
|
if logger.info?
|
316
|
-
logger.info("Elasticsearch pool URLs updated", :changes =>
|
304
|
+
logger.info("Elasticsearch pool URLs updated", :changes => state_changes)
|
317
305
|
end
|
318
306
|
end
|
319
307
|
|
@@ -324,14 +312,6 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
324
312
|
healthcheck!
|
325
313
|
end
|
326
314
|
|
327
|
-
def safe_state_changes(state_changes)
|
328
|
-
state_changes.reduce({}) do |acc, kv|
|
329
|
-
k,v = kv
|
330
|
-
acc[k] = v.map(&LogStash::Outputs::ElasticSearch::SafeURL.method(:without_credentials)).map(&:to_s)
|
331
|
-
acc
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
315
|
def size
|
336
316
|
@state_mutex.synchronize { @url_info.size }
|
337
317
|
end
|
@@ -378,10 +358,9 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
378
358
|
meta = @url_info[url]
|
379
359
|
# In case a sniff happened removing the metadata just before there's nothing to mark
|
380
360
|
# This is an extreme edge case, but it can happen!
|
381
|
-
return unless meta
|
382
|
-
|
383
|
-
|
384
|
-
:error_message => error.message, :error_class => error.class.name)
|
361
|
+
return unless meta
|
362
|
+
logger.warn("Marking url as dead. Last error: [#{error.class}] #{error.message}",
|
363
|
+
:url => url, :error_message => error.message, :error_class => error.class.name)
|
385
364
|
meta[:state] = :dead
|
386
365
|
meta[:last_error] = error
|
387
366
|
meta[:last_errored_at] = Time.now
|
@@ -6,7 +6,9 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
6
6
|
:pool_max_per_route => params["pool_max_per_route"],
|
7
7
|
:check_connection_timeout => params["validate_after_inactivity"]
|
8
8
|
}
|
9
|
-
|
9
|
+
|
10
|
+
client_settings[:proxy] = params["proxy"] if params["proxy"]
|
11
|
+
|
10
12
|
common_options = {
|
11
13
|
:client_settings => client_settings,
|
12
14
|
:resurrect_delay => params["resurrect_delay"],
|
@@ -31,7 +33,6 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
31
33
|
logger.debug? && logger.debug("Normalizing http path", :path => params["path"], :normalized => client_settings[:path])
|
32
34
|
|
33
35
|
client_settings.merge! setup_ssl(logger, params)
|
34
|
-
client_settings.merge! setup_proxy(logger, params)
|
35
36
|
common_options.merge! setup_basic_auth(logger, params)
|
36
37
|
|
37
38
|
# Update API setup
|
@@ -59,25 +60,8 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
59
60
|
)
|
60
61
|
end
|
61
62
|
|
62
|
-
def self.setup_proxy(logger, params)
|
63
|
-
proxy = params["proxy"]
|
64
|
-
return {} unless proxy
|
65
|
-
|
66
|
-
# Symbolize keys
|
67
|
-
proxy = if proxy.is_a?(Hash)
|
68
|
-
Hash[proxy.map {|k,v| [k.to_sym, v]}]
|
69
|
-
elsif proxy.is_a?(String)
|
70
|
-
proxy
|
71
|
-
else
|
72
|
-
raise LogStash::ConfigurationError, "Expected 'proxy' to be a string or hash, not '#{proxy}''!"
|
73
|
-
end
|
74
|
-
|
75
|
-
return {:proxy => proxy}
|
76
|
-
end
|
77
|
-
|
78
63
|
def self.setup_ssl(logger, params)
|
79
|
-
|
80
|
-
params["ssl"] = true if params["hosts"].any? {|h| h.start_with?("https://")}
|
64
|
+
params["ssl"] = true if params["hosts"].any? {|h| h.scheme == "https" }
|
81
65
|
return {} if params["ssl"].nil?
|
82
66
|
|
83
67
|
return {:ssl => {:enabled => false}} if params["ssl"] == false
|
@@ -8,7 +8,7 @@ module LogStash; module Outputs; class ElasticSearch
|
|
8
8
|
plugin.logger.info("Attempting to install template", :manage_template => template)
|
9
9
|
install(plugin.client, plugin.template_name, template, plugin.template_overwrite)
|
10
10
|
rescue => e
|
11
|
-
plugin.logger.error("Failed to install template.", :message => e.message, :class => e.class.name)
|
11
|
+
plugin.logger.error("Failed to install template.", :message => e.message, :class => e.class.name, :backtrace => e.backtrace)
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
|
3
3
|
s.name = 'logstash-output-elasticsearch'
|
4
|
-
s.version = '
|
4
|
+
s.version = '6.0.0'
|
5
5
|
s.licenses = ['apache-2.0']
|
6
6
|
s.summary = "Logstash Output to Elasticsearch"
|
7
7
|
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
|
@@ -7,10 +7,14 @@ describe "pool sniffer", :integration => true do
|
|
7
7
|
let(:logger) { Cabin::Channel.get }
|
8
8
|
let(:adapter) { LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter.new(logger) }
|
9
9
|
let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://#{get_host_port}")] }
|
10
|
-
let(:options) { {:resurrect_delay => 2} } # Shorten the delay a bit to speed up tests
|
10
|
+
let(:options) { {:resurrect_delay => 2, :url_normalizer => proc {|u| u}} } # Shorten the delay a bit to speed up tests
|
11
11
|
|
12
12
|
subject { LogStash::Outputs::ElasticSearch::HttpClient::Pool.new(logger, adapter, initial_urls, options) }
|
13
13
|
|
14
|
+
before do
|
15
|
+
subject.start
|
16
|
+
end
|
17
|
+
|
14
18
|
shared_examples("sniff parsing") do |check_exact|
|
15
19
|
it "should execute a sniff without error" do
|
16
20
|
expect { subject.check_sniff }.not_to raise_error
|
@@ -9,16 +9,39 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter do
|
|
9
9
|
|
10
10
|
it "should raise an exception if requests are issued after close" do
|
11
11
|
subject.close
|
12
|
-
expect { subject.perform_request("http://localhost:9200", :get, '/') }.to raise_error(::Manticore::ClientStoppedException)
|
12
|
+
expect { subject.perform_request(::LogStash::Util::SafeURI.new("http://localhost:9200"), :get, '/') }.to raise_error(::Manticore::ClientStoppedException)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should implement host unreachable exceptions" do
|
16
16
|
expect(subject.host_unreachable_exceptions).to be_a(Array)
|
17
17
|
end
|
18
|
+
|
19
|
+
describe "auth" do
|
20
|
+
let(:user) { "myuser" }
|
21
|
+
let(:password) { "mypassword" }
|
22
|
+
let(:noauth_uri) { clone = uri.uri.clone;clone.user=nil; clone.password=nil; clone.to_s }
|
23
|
+
let(:uri) { ::LogStash::Util::SafeURI.new("http://#{user}:#{password}@localhost:9200") }
|
24
|
+
|
25
|
+
it "should convert the auth to params" do
|
26
|
+
resp = double("response")
|
27
|
+
allow(resp).to receive(:call)
|
28
|
+
allow(resp).to receive(:code).and_return(200)
|
29
|
+
expect(subject.manticore).to receive(:get).
|
30
|
+
with(noauth_uri, {
|
31
|
+
:auth => {
|
32
|
+
:user => user,
|
33
|
+
:password => password,
|
34
|
+
:eager => true
|
35
|
+
}
|
36
|
+
}).and_return resp
|
37
|
+
|
38
|
+
subject.perform_request(uri, :get, "/")
|
39
|
+
end
|
40
|
+
end
|
18
41
|
|
19
42
|
describe "integration specs", :integration => true do
|
20
43
|
it "should perform correct tests without error" do
|
21
|
-
resp = subject.perform_request("http://localhost:9200", :get, "/")
|
44
|
+
resp = subject.perform_request(::LogStash::Util::SafeURI.new("http://localhost:9200"), :get, "/")
|
22
45
|
expect(resp.code).to eql(200)
|
23
46
|
end
|
24
47
|
end
|
@@ -5,15 +5,30 @@ require "json"
|
|
5
5
|
describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
6
6
|
let(:logger) { Cabin::Channel.get }
|
7
7
|
let(:adapter) { LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter.new(logger) }
|
8
|
-
let(:initial_urls) { [
|
9
|
-
let(:options) { {:resurrect_delay => 2} } # Shorten the delay a bit to speed up tests
|
8
|
+
let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] }
|
9
|
+
let(:options) { {:resurrect_delay => 2, :url_normalizer => proc {|u| u}} } # Shorten the delay a bit to speed up tests
|
10
10
|
|
11
11
|
subject { described_class.new(logger, adapter, initial_urls, options) }
|
12
12
|
|
13
|
+
let(:manticore_double) { double("manticore a") }
|
13
14
|
before do
|
14
|
-
allow(adapter).to receive(:perform_request).with(anything,
|
15
|
-
|
15
|
+
allow(adapter).to receive(:perform_request).with(anything, :head, subject.healthcheck_path, {}, nil)
|
16
|
+
|
17
|
+
|
18
|
+
response_double = double("manticore response").as_null_object
|
19
|
+
# Allow healtchecks
|
20
|
+
allow(manticore_double).to receive(:head).with(any_args).and_return(response_double)
|
21
|
+
allow(manticore_double).to receive(:get).with(any_args).and_return(response_double)
|
22
|
+
|
23
|
+
allow(::Manticore::Client).to receive(:new).and_return(manticore_double)
|
16
24
|
|
25
|
+
subject.start
|
26
|
+
end
|
27
|
+
|
28
|
+
after do
|
29
|
+
subject.close
|
30
|
+
end
|
31
|
+
|
17
32
|
describe "initialization" do
|
18
33
|
it "should be successful" do
|
19
34
|
expect { subject }.not_to raise_error
|
@@ -51,6 +66,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
|
51
66
|
allow(adapter).to receive(:close).and_call_original
|
52
67
|
allow(subject).to receive(:wait_for_in_use_connections).and_call_original
|
53
68
|
allow(subject).to receive(:in_use_connections).and_return([subject.empty_url_meta()],[])
|
69
|
+
allow(subject).to receive(:start)
|
54
70
|
subject.close
|
55
71
|
end
|
56
72
|
|
@@ -73,33 +89,18 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
|
73
89
|
end
|
74
90
|
end
|
75
91
|
|
76
|
-
describe "safe_state_changes" do
|
77
|
-
let(:state_changes) do
|
78
|
-
{
|
79
|
-
:added => [URI.parse("http://sekretu:sekretp@foo1")],
|
80
|
-
:removed => [URI.parse("http://sekretu:sekretp@foo2")]
|
81
|
-
}
|
82
|
-
end
|
83
|
-
let(:processed) { subject.safe_state_changes(state_changes)}
|
84
|
-
|
85
|
-
it "should hide passwords" do
|
86
|
-
expect(processed[:added].any? {|p| p =~ /sekretp/ }).to be false
|
87
|
-
expect(processed[:removed].any? {|p| p =~ /sekretp/ }).to be false
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
92
|
describe "connection management" do
|
92
93
|
context "with only one URL in the list" do
|
93
94
|
it "should use the only URL in 'with_connection'" do
|
94
95
|
subject.with_connection do |c|
|
95
|
-
expect(c).to
|
96
|
+
expect(c).to eq(initial_urls.first)
|
96
97
|
end
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
100
101
|
context "with multiple URLs in the list" do
|
101
|
-
let(:initial_urls) { [
|
102
|
-
|
102
|
+
let(:initial_urls) { [ ::LogStash::Util::SafeURI.new("http://localhost:9200"), ::LogStash::Util::SafeURI.new("http://localhost:9201"), ::LogStash::Util::SafeURI.new("http://localhost:9202") ] }
|
103
|
+
|
103
104
|
it "should minimize the number of connections to a single URL" do
|
104
105
|
connected_urls = []
|
105
106
|
|
@@ -122,7 +123,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
|
122
123
|
|
123
124
|
# The resurrectionist will call this to check on the backend
|
124
125
|
response = double("response")
|
125
|
-
expect(adapter).to receive(:perform_request).with(u,
|
126
|
+
expect(adapter).to receive(:perform_request).with(u, :head, subject.healthcheck_path, {}, nil).and_return(response)
|
126
127
|
|
127
128
|
subject.return_connection(u)
|
128
129
|
subject.mark_dead(u, Exception.new)
|
@@ -3,7 +3,19 @@ require "logstash/outputs/elasticsearch/http_client"
|
|
3
3
|
require "java"
|
4
4
|
|
5
5
|
describe LogStash::Outputs::ElasticSearch::HttpClient do
|
6
|
-
let(:
|
6
|
+
let(:ssl) { nil }
|
7
|
+
let(:base_options) do
|
8
|
+
opts = {
|
9
|
+
:hosts => [::LogStash::Util::SafeURI.new("127.0.0.1")],
|
10
|
+
:logger => Cabin::Channel.get
|
11
|
+
}
|
12
|
+
|
13
|
+
if !ssl.nil? # Shortcut to set this
|
14
|
+
opts[:client_settings] = {:ssl => {:enabled => ssl}}
|
15
|
+
end
|
16
|
+
|
17
|
+
opts
|
18
|
+
end
|
7
19
|
|
8
20
|
describe "Host/URL Parsing" do
|
9
21
|
subject { described_class.new(base_options) }
|
@@ -12,71 +24,83 @@ describe LogStash::Outputs::ElasticSearch::HttpClient do
|
|
12
24
|
let(:ipv6_hostname) { "[::1]" }
|
13
25
|
let(:ipv4_hostname) { "127.0.0.1" }
|
14
26
|
let(:port) { 9202 }
|
15
|
-
let(:hostname_port) { "#{hostname}:#{port}"}
|
16
|
-
let(:
|
17
|
-
let(:
|
18
|
-
let(:
|
19
|
-
|
27
|
+
let(:hostname_port) { "#{hostname}:#{port}" }
|
28
|
+
let(:hostname_port_uri) { ::LogStash::Util::SafeURI.new("//#{hostname_port}") }
|
29
|
+
let(:http_hostname_port) { ::LogStash::Util::SafeURI.new("http://#{hostname_port}") }
|
30
|
+
let(:https_hostname_port) { ::LogStash::Util::SafeURI.new("https://#{hostname_port}") }
|
31
|
+
let(:http_hostname_port_path) { ::LogStash::Util::SafeURI.new("http://#{hostname_port}/path") }
|
32
|
+
|
20
33
|
shared_examples("proper host handling") do
|
21
34
|
it "should properly transform a host:port string to a URL" do
|
22
|
-
expect(subject.
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should raise an error when a partial URL is an invalid format" do
|
26
|
-
expect {
|
27
|
-
subject.send(:host_to_url, "#{hostname_port}/")
|
28
|
-
}.to raise_error(LogStash::ConfigurationError)
|
35
|
+
expect(subject.host_to_url(hostname_port_uri)).to eq(http_hostname_port)
|
29
36
|
end
|
30
37
|
|
31
38
|
it "should not raise an error with a / for a path" do
|
32
|
-
expect(subject.
|
39
|
+
expect(subject.host_to_url(::LogStash::Util::SafeURI.new("#{http_hostname_port}/"))).to eq(LogStash::Util::SafeURI.new("#{http_hostname_port}/"))
|
33
40
|
end
|
34
41
|
|
35
42
|
it "should parse full URLs correctly" do
|
36
|
-
expect(subject.
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should reject full URLs with usernames and passwords" do
|
40
|
-
expect {
|
41
|
-
subject.send(:host_to_url, "http://user:password@host.domain")
|
42
|
-
}.to raise_error(LogStash::ConfigurationError)
|
43
|
+
expect(subject.host_to_url(http_hostname_port)).to eq(http_hostname_port)
|
43
44
|
end
|
44
45
|
|
45
46
|
describe "ssl" do
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
context "when SSL is true" do
|
48
|
+
let(:ssl) { true }
|
49
|
+
let(:base_options) { super.merge(:hosts => [http_hostname_port]) }
|
50
|
+
|
51
|
+
it "should refuse to handle an http url" do
|
52
|
+
expect {
|
53
|
+
subject.host_to_url(http_hostname_port)
|
54
|
+
}.to raise_error(LogStash::ConfigurationError)
|
55
|
+
end
|
50
56
|
end
|
51
57
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
58
|
+
context "when SSL is false" do
|
59
|
+
let(:ssl) { false }
|
60
|
+
let(:base_options) { super.merge(:hosts => [https_hostname_port]) }
|
61
|
+
|
62
|
+
it "should refuse to handle an https url" do
|
63
|
+
expect {
|
64
|
+
subject.host_to_url(https_hostname_port)
|
65
|
+
}.to raise_error(LogStash::ConfigurationError)
|
66
|
+
end
|
56
67
|
end
|
57
68
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
69
|
+
describe "ssl is nil" do
|
70
|
+
let(:base_options) { super.merge(:hosts => [https_hostname_port]) }
|
71
|
+
it "should handle an ssl url correctly when SSL is nil" do
|
72
|
+
subject
|
73
|
+
expect(subject.host_to_url(https_hostname_port)).to eq(https_hostname_port)
|
74
|
+
end
|
75
|
+
end
|
65
76
|
end
|
66
77
|
|
67
78
|
describe "path" do
|
79
|
+
let(:url) { http_hostname_port_path }
|
80
|
+
let(:base_options) { super.merge(:hosts => [url]) }
|
81
|
+
|
68
82
|
it "should allow paths in a url" do
|
69
|
-
expect(subject.
|
83
|
+
expect(subject.host_to_url(url)).to eq(url)
|
70
84
|
end
|
71
85
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
86
|
+
context "with the path option set" do
|
87
|
+
let(:base_options) { super.merge(:client_settings => {:path => "/otherpath"}) }
|
88
|
+
|
89
|
+
it "should not allow paths in two places" do
|
90
|
+
expect {
|
91
|
+
subject.host_to_url(url)
|
92
|
+
}.to raise_error(LogStash::ConfigurationError)
|
93
|
+
end
|
76
94
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
95
|
+
|
96
|
+
context "with a path missing a leading /" do
|
97
|
+
let(:url) { http_hostname_port }
|
98
|
+
let(:base_options) { super.merge(:client_settings => {:path => "otherpath"}) }
|
99
|
+
|
100
|
+
|
101
|
+
it "should automatically insert a / in front of path overlays" do
|
102
|
+
expect(subject.host_to_url(url)).to eq(LogStash::Util::SafeURI.new(url + "/otherpath"))
|
103
|
+
end
|
80
104
|
end
|
81
105
|
end
|
82
106
|
end
|
@@ -97,6 +121,49 @@ describe LogStash::Outputs::ElasticSearch::HttpClient do
|
|
97
121
|
end
|
98
122
|
end
|
99
123
|
|
124
|
+
describe "join_bulk_responses" do
|
125
|
+
subject { described_class.new(base_options) }
|
126
|
+
|
127
|
+
context "when items key is available" do
|
128
|
+
require "json"
|
129
|
+
let(:bulk_response) {
|
130
|
+
LogStash::Json.load ('[{
|
131
|
+
"items": [{
|
132
|
+
"delete": {
|
133
|
+
"_index": "website",
|
134
|
+
"_type": "blog",
|
135
|
+
"_id": "123",
|
136
|
+
"_version": 2,
|
137
|
+
"status": 200,
|
138
|
+
"found": true
|
139
|
+
}
|
140
|
+
}],
|
141
|
+
"errors": false
|
142
|
+
}]')
|
143
|
+
}
|
144
|
+
it "should be handled properly" do
|
145
|
+
s = subject.send(:join_bulk_responses, bulk_response)
|
146
|
+
expect(s["errors"]).to be false
|
147
|
+
expect(s["items"].size).to be 1
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "when items key is not available" do
|
152
|
+
require "json"
|
153
|
+
let(:bulk_response) {
|
154
|
+
JSON.parse ('[{
|
155
|
+
"took": 4,
|
156
|
+
"errors": false
|
157
|
+
}]')
|
158
|
+
}
|
159
|
+
it "should be handled properly" do
|
160
|
+
s = subject.send(:join_bulk_responses, bulk_response)
|
161
|
+
expect(s["errors"]).to be false
|
162
|
+
expect(s["items"].size).to be 0
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
100
167
|
describe "sniffing" do
|
101
168
|
let(:client) { LogStash::Outputs::ElasticSearch::HttpClient.new(base_options.merge(client_opts)) }
|
102
169
|
|