logstash-output-amazon_es 2.0.1-java → 6.4.0-java
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/CONTRIBUTORS +12 -0
- data/Gemfile +8 -0
- data/LICENSE +10 -199
- data/README.md +34 -65
- data/lib/logstash/outputs/amazon_es.rb +218 -423
- data/lib/logstash/outputs/amazon_es/common.rb +347 -0
- data/lib/logstash/outputs/amazon_es/common_configs.rb +141 -0
- data/lib/logstash/outputs/amazon_es/elasticsearch-template-es2x.json +95 -0
- data/lib/logstash/outputs/amazon_es/elasticsearch-template-es5x.json +46 -0
- data/lib/logstash/outputs/amazon_es/elasticsearch-template-es6x.json +45 -0
- data/lib/logstash/outputs/amazon_es/elasticsearch-template-es7x.json +46 -0
- data/lib/logstash/outputs/amazon_es/http_client.rb +359 -74
- data/lib/logstash/outputs/amazon_es/http_client/manticore_adapter.rb +169 -0
- data/lib/logstash/outputs/amazon_es/http_client/pool.rb +457 -0
- data/lib/logstash/outputs/amazon_es/http_client_builder.rb +164 -0
- data/lib/logstash/outputs/amazon_es/template_manager.rb +36 -0
- data/logstash-output-amazon_es.gemspec +13 -22
- data/spec/es_spec_helper.rb +37 -0
- data/spec/unit/http_client_builder_spec.rb +189 -0
- data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +105 -0
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +198 -0
- data/spec/unit/outputs/elasticsearch/http_client_spec.rb +222 -0
- data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +25 -0
- data/spec/unit/outputs/elasticsearch_spec.rb +615 -0
- data/spec/unit/outputs/error_whitelist_spec.rb +60 -0
- metadata +49 -110
- data/lib/logstash/outputs/amazon_es/aws_transport.rb +0 -109
- data/lib/logstash/outputs/amazon_es/aws_v4_signer.rb +0 -7
- data/lib/logstash/outputs/amazon_es/aws_v4_signer_impl.rb +0 -62
- data/lib/logstash/outputs/amazon_es/elasticsearch-template.json +0 -41
- data/spec/amazon_es_spec_helper.rb +0 -69
- data/spec/unit/outputs/amazon_es_spec.rb +0 -50
- data/spec/unit/outputs/elasticsearch/protocol_spec.rb +0 -36
- data/spec/unit/outputs/elasticsearch_proxy_spec.rb +0 -58
@@ -0,0 +1,105 @@
|
|
1
|
+
require "logstash/devutils/rspec/spec_helper"
|
2
|
+
require "logstash/outputs/amazon_es/http_client"
|
3
|
+
|
4
|
+
describe LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter do
|
5
|
+
let(:logger) { Cabin::Channel.get }
|
6
|
+
let(:options) { {:aws_access_key_id => 'AAAAAAAAAAAAAAAAAAAA',
|
7
|
+
:aws_secret_access_key => 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'} }
|
8
|
+
|
9
|
+
subject { described_class.new(logger, options) }
|
10
|
+
|
11
|
+
it "should raise an exception if requests are issued after close" do
|
12
|
+
subject.close
|
13
|
+
expect { subject.perform_request(::LogStash::Util::SafeURI.new("http://localhost:9200"), :get, '/') }.to raise_error(::Manticore::ClientStoppedException)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should implement host unreachable exceptions" do
|
17
|
+
expect(subject.host_unreachable_exceptions).to be_a(Array)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
describe "bad response codes" do
|
22
|
+
let(:uri) { ::LogStash::Util::SafeURI.new("http://localhost:9200") }
|
23
|
+
|
24
|
+
it "should raise a bad response code error" do
|
25
|
+
resp = double("response")
|
26
|
+
allow(resp).to receive(:call)
|
27
|
+
allow(resp).to receive(:code).and_return(500)
|
28
|
+
allow(resp).to receive(:body).and_return("a body")
|
29
|
+
|
30
|
+
expect(subject.manticore).to receive(:get).
|
31
|
+
with(uri.to_s + "/", anything).
|
32
|
+
and_return(resp)
|
33
|
+
|
34
|
+
uri_with_path = uri.clone
|
35
|
+
uri_with_path.path = "/"
|
36
|
+
|
37
|
+
expect(::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError).to receive(:new).
|
38
|
+
with(resp.code, uri_with_path, nil, resp.body).and_call_original
|
39
|
+
|
40
|
+
expect do
|
41
|
+
subject.perform_request(uri, :get, "/")
|
42
|
+
end.to raise_error(::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "format_url" do
|
47
|
+
let(:url) { ::LogStash::Util::SafeURI.new("http://localhost:9200/path/") }
|
48
|
+
let(:path) { "_bulk" }
|
49
|
+
subject { described_class.new(double("logger"),
|
50
|
+
{:aws_access_key_id => 'AAAAAAAAAAAAAAAAAAAA',
|
51
|
+
:aws_secret_access_key => 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'} ) }
|
52
|
+
|
53
|
+
it "should add the path argument to the uri's path" do
|
54
|
+
expect(subject.format_url(url, path).path).to eq("/path/_bulk")
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when uri contains query parameters" do
|
58
|
+
let(:query_params) { "query=value&key=value2" }
|
59
|
+
let(:url) { ::LogStash::Util::SafeURI.new("http://localhost:9200/path/?#{query_params}") }
|
60
|
+
let(:formatted) { subject.format_url(url, path)}
|
61
|
+
|
62
|
+
|
63
|
+
it "should retain query_params after format" do
|
64
|
+
expect(formatted.query).to eq(query_params)
|
65
|
+
end
|
66
|
+
|
67
|
+
context "and the path contains query parameters" do
|
68
|
+
let(:path) { "/special_path?specialParam=123" }
|
69
|
+
|
70
|
+
it "should join the query correctly" do
|
71
|
+
expect(formatted.query).to eq(query_params + "&specialParam=123")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when the path contains query parameters" do
|
77
|
+
let(:path) { "/special_bulk?pathParam=1"}
|
78
|
+
let(:formatted) { subject.format_url(url, path) }
|
79
|
+
|
80
|
+
it "should add the path correctly" do
|
81
|
+
expect(formatted.path).to eq("#{url.path}special_bulk")
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should add the query parameters correctly" do
|
85
|
+
expect(formatted.query).to eq("pathParam=1")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when uri contains credentials" do
|
90
|
+
let(:url) { ::LogStash::Util::SafeURI.new("http://myuser:mypass@localhost:9200") }
|
91
|
+
let(:formatted) { subject.format_url(url, path) }
|
92
|
+
|
93
|
+
it "should remove credentials after format" do
|
94
|
+
expect(formatted.userinfo).to be_nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "integration specs", :integration => true do
|
100
|
+
it "should perform correct tests without error" do
|
101
|
+
resp = subject.perform_request(::LogStash::Util::SafeURI.new("http://localhost:9200"), :get, "/")
|
102
|
+
expect(resp.code).to eql(200)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require "logstash/devutils/rspec/spec_helper"
|
2
|
+
require "logstash/outputs/amazon_es/http_client"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
6
|
+
let(:logger) { Cabin::Channel.get }
|
7
|
+
let(:adapter) { LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter.new(logger,
|
8
|
+
{:aws_access_key_id => 'AAAAAAAAAAAAAAAAAAAA',
|
9
|
+
:aws_secret_access_key => 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'}) }
|
10
|
+
let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] }
|
11
|
+
let(:options) { {:resurrect_delay => 2, :url_normalizer => proc {|u| u}} } # Shorten the delay a bit to speed up tests
|
12
|
+
let(:es_node_versions) { [ "0.0.0" ] }
|
13
|
+
|
14
|
+
subject { described_class.new(logger, adapter, initial_urls, options) }
|
15
|
+
|
16
|
+
let(:manticore_double) { double("manticore a") }
|
17
|
+
before(:each) do
|
18
|
+
|
19
|
+
response_double = double("manticore response").as_null_object
|
20
|
+
# Allow healtchecks
|
21
|
+
allow(manticore_double).to receive(:head).with(any_args).and_return(response_double)
|
22
|
+
allow(manticore_double).to receive(:get).with(any_args).and_return(response_double)
|
23
|
+
allow(manticore_double).to receive(:close)
|
24
|
+
|
25
|
+
allow(::Manticore::Client).to receive(:new).and_return(manticore_double)
|
26
|
+
|
27
|
+
allow(subject).to receive(:get_es_version).with(any_args).and_return(*es_node_versions)
|
28
|
+
end
|
29
|
+
|
30
|
+
after do
|
31
|
+
subject.close
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "initialization" do
|
35
|
+
it "should be successful" do
|
36
|
+
expect { subject }.not_to raise_error
|
37
|
+
subject.start
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "the resurrectionist" do
|
42
|
+
before(:each) { subject.start }
|
43
|
+
it "should start the resurrectionist when created" do
|
44
|
+
expect(subject.resurrectionist_alive?).to eql(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should attempt to resurrect connections after the ressurrect delay" do
|
48
|
+
expect(subject).to receive(:healthcheck!).once
|
49
|
+
sleep(subject.resurrect_delay + 1)
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "healthcheck url handling" do
|
53
|
+
let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] }
|
54
|
+
|
55
|
+
context "and not setting healthcheck_path" do
|
56
|
+
it "performs the healthcheck to the root" do
|
57
|
+
expect(adapter).to receive(:perform_request) do |url, method, req_path, _, _|
|
58
|
+
expect(method).to eq(:head)
|
59
|
+
expect(url.path).to be_empty
|
60
|
+
expect(req_path).to eq("/")
|
61
|
+
end
|
62
|
+
subject.healthcheck!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "and setting healthcheck_path" do
|
67
|
+
let(:healthcheck_path) { "/my/health" }
|
68
|
+
let(:options) { super.merge(:healthcheck_path => healthcheck_path) }
|
69
|
+
it "performs the healthcheck to the healthcheck_path" do
|
70
|
+
expect(adapter).to receive(:perform_request) do |url, method, req_path, _, _|
|
71
|
+
expect(method).to eq(:head)
|
72
|
+
expect(url.path).to be_empty
|
73
|
+
expect(req_path).to eq(healthcheck_path)
|
74
|
+
end
|
75
|
+
subject.healthcheck!
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "the sniffer" do
|
82
|
+
before(:each) { subject.start }
|
83
|
+
it "should not start the sniffer by default" do
|
84
|
+
expect(subject.sniffer_alive?).to eql(nil)
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when enabled" do
|
88
|
+
let(:options) { super.merge(:sniffing => true)}
|
89
|
+
|
90
|
+
it "should start the sniffer" do
|
91
|
+
expect(subject.sniffer_alive?).to eql(true)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "closing" do
|
97
|
+
before do
|
98
|
+
subject.start
|
99
|
+
# Simulate a single in use connection on the first check of this
|
100
|
+
allow(adapter).to receive(:close).and_call_original
|
101
|
+
allow(subject).to receive(:wait_for_in_use_connections).and_call_original
|
102
|
+
allow(subject).to receive(:in_use_connections).and_return([subject.empty_url_meta()],[])
|
103
|
+
allow(subject).to receive(:start)
|
104
|
+
subject.close
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should close the adapter" do
|
108
|
+
expect(adapter).to have_received(:close)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should stop the resurrectionist" do
|
112
|
+
expect(subject.resurrectionist_alive?).to eql(false)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should stop the sniffer" do
|
116
|
+
# If no sniffer (the default) returns nil
|
117
|
+
expect(subject.sniffer_alive?).to be_falsey
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should wait for in use connections to terminate" do
|
121
|
+
expect(subject).to have_received(:wait_for_in_use_connections).once
|
122
|
+
expect(subject).to have_received(:in_use_connections).twice
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "connection management" do
|
127
|
+
before(:each) { subject.start }
|
128
|
+
context "with only one URL in the list" do
|
129
|
+
it "should use the only URL in 'with_connection'" do
|
130
|
+
subject.with_connection do |c|
|
131
|
+
expect(c).to eq(initial_urls.first)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "with multiple URLs in the list" do
|
137
|
+
before :each do
|
138
|
+
allow(adapter).to receive(:perform_request).with(anything, :head, subject.healthcheck_path, {}, nil)
|
139
|
+
end
|
140
|
+
let(:initial_urls) { [ ::LogStash::Util::SafeURI.new("http://localhost:9200") ] }
|
141
|
+
|
142
|
+
it "should minimize the number of connections to a single URL" do
|
143
|
+
connected_urls = []
|
144
|
+
|
145
|
+
# If we make 2x the number requests as we have URLs we should
|
146
|
+
# connect to each URL exactly 2 times
|
147
|
+
(initial_urls.size*2).times do
|
148
|
+
u, meta = subject.get_connection
|
149
|
+
connected_urls << u
|
150
|
+
end
|
151
|
+
|
152
|
+
connected_urls.each {|u| subject.return_connection(u) }
|
153
|
+
initial_urls.each do |url|
|
154
|
+
conn_count = connected_urls.select {|u| u == url}.size
|
155
|
+
expect(conn_count).to eql(2)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should correctly resurrect the dead" do
|
160
|
+
u,m = subject.get_connection
|
161
|
+
|
162
|
+
# The resurrectionist will call this to check on the backend
|
163
|
+
response = double("response")
|
164
|
+
expect(adapter).to receive(:perform_request).with(u, :head, subject.healthcheck_path, {}, nil).and_return(response)
|
165
|
+
|
166
|
+
subject.return_connection(u)
|
167
|
+
subject.mark_dead(u, Exception.new)
|
168
|
+
|
169
|
+
expect(subject.url_meta(u)[:state]).to eql(:dead)
|
170
|
+
sleep subject.resurrect_delay + 1
|
171
|
+
expect(subject.url_meta(u)[:state]).to eql(:alive)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "version tracking" do
|
177
|
+
let(:initial_urls) { [
|
178
|
+
::LogStash::Util::SafeURI.new("http://somehost:9200"),
|
179
|
+
::LogStash::Util::SafeURI.new("http://otherhost:9201")
|
180
|
+
] }
|
181
|
+
|
182
|
+
before(:each) do
|
183
|
+
allow(subject).to receive(:perform_request_to_url).and_return(nil)
|
184
|
+
subject.start
|
185
|
+
end
|
186
|
+
|
187
|
+
it "picks the largest major version" do
|
188
|
+
expect(subject.maximum_seen_major_version).to eq(0)
|
189
|
+
end
|
190
|
+
|
191
|
+
context "if there are nodes with multiple major versions" do
|
192
|
+
let(:es_node_versions) { [ "0.0.0", "6.0.0" ] }
|
193
|
+
it "picks the largest major version" do
|
194
|
+
expect(subject.maximum_seen_major_version).to eq(6)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require "logstash/devutils/rspec/spec_helper"
|
2
|
+
require "logstash/outputs/amazon_es/http_client"
|
3
|
+
require "java"
|
4
|
+
|
5
|
+
describe LogStash::Outputs::ElasticSearch::HttpClient do
|
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
|
+
:metric => ::LogStash::Instrument::NullMetric.new(:dummy).namespace(:alsodummy),
|
12
|
+
:protocol => "http",
|
13
|
+
:port => 9200,
|
14
|
+
:aws_access_key_id => "AAAAAAAAAAAAAAAAAAAA",
|
15
|
+
:aws_secret_access_key => "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
16
|
+
}
|
17
|
+
|
18
|
+
if !ssl.nil? # Shortcut to set this
|
19
|
+
opts[:client_settings] = {:ssl => {:enabled => ssl}}
|
20
|
+
end
|
21
|
+
|
22
|
+
opts
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "Host/URL Parsing" do
|
26
|
+
subject { described_class.new(base_options) }
|
27
|
+
|
28
|
+
let(:true_hostname) { "my-dash.hostname" }
|
29
|
+
let(:ipv6_hostname) { "[::1]" }
|
30
|
+
let(:ipv4_hostname) { "127.0.0.1" }
|
31
|
+
let(:port) { 9200 }
|
32
|
+
let(:protocol) {"http"}
|
33
|
+
let(:aws_access_key_id) {"AAAAAAAAAAAAAAAAAAAA"}
|
34
|
+
let(:aws_secret_access_key) {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}
|
35
|
+
let(:hostname_port) { "#{hostname}:#{port}" }
|
36
|
+
let(:hostname_port_uri) { ::LogStash::Util::SafeURI.new("//#{hostname_port}") }
|
37
|
+
let(:http_hostname_port) { ::LogStash::Util::SafeURI.new("http://#{hostname_port}") }
|
38
|
+
let(:https_hostname_port) { ::LogStash::Util::SafeURI.new("https://#{hostname_port}") }
|
39
|
+
let(:http_hostname_port_path) { ::LogStash::Util::SafeURI.new("http://#{hostname_port}/path") }
|
40
|
+
|
41
|
+
shared_examples("proper host handling") do
|
42
|
+
it "should properly transform a host:port string to a URL" do
|
43
|
+
expect(subject.host_to_url(hostname_port_uri).to_s).to eq(http_hostname_port.to_s + "/")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not raise an error with a / for a path" do
|
47
|
+
expect(subject.host_to_url(::LogStash::Util::SafeURI.new("#{http_hostname_port}/"))).to eq(LogStash::Util::SafeURI.new("#{http_hostname_port}/"))
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should parse full URLs correctly" do
|
51
|
+
expect(subject.host_to_url(http_hostname_port).to_s).to eq(http_hostname_port.to_s + "/")
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
describe "path" do
|
56
|
+
let(:url) { http_hostname_port_path }
|
57
|
+
let(:base_options) { super.merge(:hosts => [url]) }
|
58
|
+
|
59
|
+
it "should allow paths in a url" do
|
60
|
+
expect(subject.host_to_url(url)).to eq(url)
|
61
|
+
end
|
62
|
+
|
63
|
+
context "with the path option set" do
|
64
|
+
let(:base_options) { super.merge(:client_settings => {:path => "/otherpath"}) }
|
65
|
+
|
66
|
+
it "should not allow paths in two places" do
|
67
|
+
expect {
|
68
|
+
subject.host_to_url(url)
|
69
|
+
}.to raise_error(LogStash::ConfigurationError)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with a path missing a leading /" do
|
74
|
+
let(:url) { http_hostname_port }
|
75
|
+
let(:base_options) { super.merge(:client_settings => {:path => "otherpath"}) }
|
76
|
+
|
77
|
+
|
78
|
+
it "should automatically insert a / in front of path overlays" do
|
79
|
+
expected = url.clone
|
80
|
+
expected.path = url.path + "/otherpath"
|
81
|
+
expect(subject.host_to_url(url)).to eq(expected)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "an regular hostname" do
|
88
|
+
let(:hostname) { true_hostname }
|
89
|
+
include_examples("proper host handling")
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "an ipv4 host" do
|
93
|
+
let(:hostname) { ipv4_hostname }
|
94
|
+
include_examples("proper host handling")
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "get" do
|
100
|
+
subject { described_class.new(base_options) }
|
101
|
+
let(:body) { "foobar" }
|
102
|
+
let(:path) { "/hello-id" }
|
103
|
+
let(:get_response) {
|
104
|
+
double("response", :body => LogStash::Json::dump( { "body" => body }))
|
105
|
+
}
|
106
|
+
|
107
|
+
it "returns the hash response" do
|
108
|
+
expect(subject.pool).to receive(:get).with(path, nil).and_return(get_response)
|
109
|
+
expect(subject.get(path)["body"]).to eq(body)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "join_bulk_responses" do
|
114
|
+
subject { described_class.new(base_options) }
|
115
|
+
|
116
|
+
context "when items key is available" do
|
117
|
+
require "json"
|
118
|
+
let(:bulk_response) {
|
119
|
+
LogStash::Json.load ('[{
|
120
|
+
"items": [{
|
121
|
+
"delete": {
|
122
|
+
"_index": "website",
|
123
|
+
"_type": "blog",
|
124
|
+
"_id": "123",
|
125
|
+
"_version": 2,
|
126
|
+
"status": 200,
|
127
|
+
"found": true
|
128
|
+
}
|
129
|
+
}],
|
130
|
+
"errors": false
|
131
|
+
}]')
|
132
|
+
}
|
133
|
+
it "should be handled properly" do
|
134
|
+
s = subject.send(:join_bulk_responses, bulk_response)
|
135
|
+
expect(s["errors"]).to be false
|
136
|
+
expect(s["items"].size).to be 1
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "when items key is not available" do
|
141
|
+
require "json"
|
142
|
+
let(:bulk_response) {
|
143
|
+
JSON.parse ('[{
|
144
|
+
"took": 4,
|
145
|
+
"errors": false
|
146
|
+
}]')
|
147
|
+
}
|
148
|
+
it "should be handled properly" do
|
149
|
+
s = subject.send(:join_bulk_responses, bulk_response)
|
150
|
+
expect(s["errors"]).to be false
|
151
|
+
expect(s["items"].size).to be 0
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#bulk" do
|
157
|
+
subject { described_class.new(base_options) }
|
158
|
+
|
159
|
+
require "json"
|
160
|
+
let(:message) { "hey" }
|
161
|
+
let(:actions) { [
|
162
|
+
["index", {:_id=>nil, :_index=>"logstash"}, {"message"=> message}],
|
163
|
+
]}
|
164
|
+
|
165
|
+
context "if a message is over TARGET_BULK_BYTES" do
|
166
|
+
let(:target_bulk_bytes) { LogStash::Outputs::ElasticSearch::TARGET_BULK_BYTES }
|
167
|
+
let(:message) { "a" * (target_bulk_bytes + 1) }
|
168
|
+
|
169
|
+
it "should be handled properly" do
|
170
|
+
allow(subject).to receive(:join_bulk_responses)
|
171
|
+
expect(subject).to receive(:bulk_send).once do |data|
|
172
|
+
expect(data.size).to be > target_bulk_bytes
|
173
|
+
end
|
174
|
+
s = subject.send(:bulk, actions)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "with two messages" do
|
179
|
+
let(:message1) { "hey" }
|
180
|
+
let(:message2) { "you" }
|
181
|
+
let(:actions) { [
|
182
|
+
["index", {:_id=>nil, :_index=>"logstash"}, {"message"=> message1}],
|
183
|
+
["index", {:_id=>nil, :_index=>"logstash"}, {"message"=> message2}],
|
184
|
+
]}
|
185
|
+
it "executes one bulk_send operation" do
|
186
|
+
allow(subject).to receive(:join_bulk_responses)
|
187
|
+
expect(subject).to receive(:bulk_send).once
|
188
|
+
s = subject.send(:bulk, actions)
|
189
|
+
end
|
190
|
+
|
191
|
+
context "if one exceeds TARGET_BULK_BYTES" do
|
192
|
+
let(:target_bulk_bytes) { LogStash::Outputs::ElasticSearch::TARGET_BULK_BYTES }
|
193
|
+
let(:message1) { "a" * (target_bulk_bytes + 1) }
|
194
|
+
it "executes two bulk_send operations" do
|
195
|
+
allow(subject).to receive(:join_bulk_responses)
|
196
|
+
expect(subject).to receive(:bulk_send).twice
|
197
|
+
s = subject.send(:bulk, actions)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "sniffing" do
|
204
|
+
let(:client) { LogStash::Outputs::ElasticSearch::HttpClient.new(base_options.merge(client_opts)) }
|
205
|
+
|
206
|
+
context "with sniffing enabled" do
|
207
|
+
let(:client_opts) { {:sniffing => true, :sniffing_delay => 1 } }
|
208
|
+
|
209
|
+
it "should start the sniffer" do
|
210
|
+
expect(client.pool.sniffing).to be_truthy
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context "with sniffing disabled" do
|
215
|
+
let(:client_opts) { {:sniffing => false} }
|
216
|
+
|
217
|
+
it "should not start the sniffer" do
|
218
|
+
expect(client.pool.sniffing).to be_falsey
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|