logstash-integration-logstash 0.0.5-java → 1.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 +3 -0
- data/VERSION +1 -1
- data/docs/index.asciidoc +8 -5
- data/docs/output-logstash.asciidoc +6 -6
- data/lib/logstash/outputs/logstash.rb +251 -127
- data/lib/logstash/utils/load_balancer.rb +81 -0
- data/logstash-integration-logstash.gemspec +2 -1
- data/spec/fixtures/certs/generated/client_from_root.jks +0 -0
- data/spec/fixtures/certs/generated/client_from_root.key.pem +50 -50
- data/spec/fixtures/certs/generated/client_from_root.key.pkcs8.pem +52 -52
- data/spec/fixtures/certs/generated/client_from_root.p12 +0 -0
- data/spec/fixtures/certs/generated/client_from_root.pem +28 -28
- data/spec/fixtures/certs/generated/client_from_untrusted.jks +0 -0
- data/spec/fixtures/certs/generated/client_from_untrusted.key.pem +50 -50
- data/spec/fixtures/certs/generated/client_from_untrusted.key.pkcs8.pem +52 -52
- data/spec/fixtures/certs/generated/client_from_untrusted.p12 +0 -0
- data/spec/fixtures/certs/generated/client_from_untrusted.pem +28 -28
- data/spec/fixtures/certs/generated/client_self_signed.jks +0 -0
- data/spec/fixtures/certs/generated/client_self_signed.key.pem +50 -50
- data/spec/fixtures/certs/generated/client_self_signed.key.pkcs8.pem +52 -52
- data/spec/fixtures/certs/generated/client_self_signed.p12 +0 -0
- data/spec/fixtures/certs/generated/client_self_signed.pem +28 -28
- data/spec/fixtures/certs/generated/root.key.pem +50 -50
- data/spec/fixtures/certs/generated/root.pem +28 -28
- data/spec/fixtures/certs/generated/server_from_root-key-pkcs8.pem +50 -50
- data/spec/fixtures/certs/generated/server_from_root.jks +0 -0
- data/spec/fixtures/certs/generated/server_from_root.key.pem +50 -50
- data/spec/fixtures/certs/generated/server_from_root.key.pkcs8.pem +52 -52
- data/spec/fixtures/certs/generated/server_from_root.p12 +0 -0
- data/spec/fixtures/certs/generated/server_from_root.pem +29 -29
- data/spec/fixtures/certs/generated/untrusted.key.pem +50 -50
- data/spec/fixtures/certs/generated/untrusted.pem +28 -28
- data/spec/unit/full_transmission_spec.rb +10 -2
- data/spec/unit/load_balancer_spec.rb +67 -0
- data/spec/unit/logstash_output_spec.rb +178 -17
- metadata +22 -5
@@ -9,6 +9,11 @@ describe LogStash::Outputs::Logstash do
|
|
9
9
|
let(:config) {{ "hosts" => "127.0.0.1" }}
|
10
10
|
|
11
11
|
subject(:plugin) { LogStash::Outputs::Logstash.new(config) }
|
12
|
+
let(:registered_plugin) { plugin.tap(&:register) }
|
13
|
+
|
14
|
+
let(:event) {
|
15
|
+
LogStash::Event.new({"message" => "Sending my hello to upstream input"})
|
16
|
+
}
|
12
17
|
|
13
18
|
describe "a plugin class" do
|
14
19
|
subject { described_class }
|
@@ -28,19 +33,22 @@ describe LogStash::Outputs::Logstash do
|
|
28
33
|
end
|
29
34
|
|
30
35
|
describe "plugin register" do
|
31
|
-
let(:registered_plugin) { plugin.tap(&:register) }
|
32
36
|
|
33
37
|
describe "construct host URI" do
|
34
38
|
|
35
39
|
it "applies default https scheme and 9800 port" do
|
36
|
-
|
40
|
+
normalized_hosts = registered_plugin.send(:normalize_host_uris)
|
41
|
+
expect(normalized_hosts).to have_attributes(:size => 1)
|
42
|
+
expect(normalized_hosts.first).to eql("https://127.0.0.1:9800")
|
37
43
|
end
|
38
44
|
|
39
45
|
describe "SSL disabled" do
|
40
46
|
let(:config) { super().merge("ssl_enabled" => false) }
|
41
47
|
|
42
48
|
it "causes HTTP scheme" do
|
43
|
-
|
49
|
+
normalized_hosts = registered_plugin.send(:normalize_host_uris)
|
50
|
+
expect(normalized_hosts).to have_attributes(:size => 1)
|
51
|
+
expect(normalized_hosts.first).to eql("http://127.0.0.1:9800")
|
44
52
|
end
|
45
53
|
end
|
46
54
|
|
@@ -48,7 +56,9 @@ describe LogStash::Outputs::Logstash do
|
|
48
56
|
let(:config) { super().merge("hosts" => "127.0.0.1:9808") }
|
49
57
|
|
50
58
|
it "will be applied" do
|
51
|
-
|
59
|
+
normalized_hosts = registered_plugin.send(:normalize_host_uris)
|
60
|
+
expect(normalized_hosts).to have_attributes(:size => 1)
|
61
|
+
expect(normalized_hosts.first).to eql("https://127.0.0.1:9808")
|
52
62
|
end
|
53
63
|
end
|
54
64
|
end
|
@@ -61,7 +71,7 @@ describe LogStash::Outputs::Logstash do
|
|
61
71
|
|
62
72
|
it "requires `password`" do
|
63
73
|
expected_message = "`password` is REQUIRED when `username` is provided"
|
64
|
-
expect{
|
74
|
+
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
@@ -95,15 +105,15 @@ describe LogStash::Outputs::Logstash do
|
|
95
105
|
let(:config) { super().merge("ssl_certificate" => cert_fixture!('server_from_root.pem')) }
|
96
106
|
|
97
107
|
it "requires `ssl_key`" do
|
98
|
-
expected_message =
|
108
|
+
expected_message = "`ssl_key` is REQUIRED when `ssl_certificate` is provided"
|
99
109
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
100
110
|
end
|
101
111
|
|
102
112
|
context "with keystore" do
|
103
|
-
let(:config) { super().merge("ssl_keystore_path" => cert_fixture!(
|
113
|
+
let(:config) { super().merge("ssl_keystore_path" => cert_fixture!("client_from_root.jks"), "ssl_key" => cert_fixture!("server_from_root.key.pem")) }
|
104
114
|
|
105
115
|
it "cannot be used together" do
|
106
|
-
expected_message =
|
116
|
+
expected_message = "SSL identity can be configured with EITHER `ssl_certificate` OR `ssl_keystore_*`, but not both"
|
107
117
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
108
118
|
end
|
109
119
|
end
|
@@ -122,7 +132,7 @@ describe LogStash::Outputs::Logstash do
|
|
122
132
|
let(:config) { super().merge("ssl_keystore_path" => cert_fixture!('server_from_root.jks')) }
|
123
133
|
|
124
134
|
it "requires `ssl_keystore_password`" do
|
125
|
-
expected_message =
|
135
|
+
expected_message = "`ssl_keystore_password` is REQUIRED when `ssl_keystore_path` is provided"
|
126
136
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
127
137
|
end
|
128
138
|
end
|
@@ -131,7 +141,7 @@ describe LogStash::Outputs::Logstash do
|
|
131
141
|
let(:config) { super().merge("ssl_keystore_password" => "pa$$w0rd") }
|
132
142
|
|
133
143
|
it "requires `ssl_keystore_path`" do
|
134
|
-
expected_message =
|
144
|
+
expected_message = "`ssl_keystore_password` is not allowed unless `ssl_keystore_path` is configured"
|
135
145
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
136
146
|
end
|
137
147
|
end
|
@@ -141,22 +151,22 @@ describe LogStash::Outputs::Logstash do
|
|
141
151
|
let(:config) { super().merge("ssl_enabled" => true) }
|
142
152
|
|
143
153
|
context "with CA" do
|
144
|
-
let(:config) { super().merge("ssl_certificate_authorities" => cert_fixture!(
|
154
|
+
let(:config) { super().merge("ssl_certificate_authorities" => cert_fixture!("root.pem")) }
|
145
155
|
|
146
156
|
context "and `ssl_verification_mode` is 'none'" do
|
147
157
|
let(:config) { super().merge("ssl_verification_mode" => "none") }
|
148
158
|
|
149
159
|
it "not allowed" do
|
150
|
-
expected_message =
|
160
|
+
expected_message = "SSL Certificate Authorities cannot be configured when `ssl_verification_mode => none`"
|
151
161
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
152
162
|
end
|
153
163
|
end
|
154
164
|
|
155
165
|
context "and truststore" do
|
156
|
-
let(:config) { super().merge("ssl_truststore_path" => cert_fixture!(
|
166
|
+
let(:config) { super().merge("ssl_truststore_path" => cert_fixture!("client_self_signed.jks")) }
|
157
167
|
|
158
168
|
it "not allowed" do
|
159
|
-
expected_message =
|
169
|
+
expected_message = "SSL trust can be configured with EITHER `ssl_certificate_authorities` OR `ssl_truststore_*`, but not both"
|
160
170
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
161
171
|
end
|
162
172
|
end
|
@@ -166,7 +176,7 @@ describe LogStash::Outputs::Logstash do
|
|
166
176
|
let(:config) { super().merge("ssl_truststore_path" => cert_fixture!('client_self_signed.jks')) }
|
167
177
|
|
168
178
|
it "requires truststore password" do
|
169
|
-
expected_message =
|
179
|
+
expected_message = "`ssl_truststore_password` is REQUIRED when `ssl_truststore_path` is provided"
|
170
180
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
171
181
|
end
|
172
182
|
|
@@ -174,7 +184,7 @@ describe LogStash::Outputs::Logstash do
|
|
174
184
|
let(:config) { super().merge("ssl_verification_mode" => "none") }
|
175
185
|
|
176
186
|
it "not allowed" do
|
177
|
-
expected_message =
|
187
|
+
expected_message = "SSL Truststore cannot be configured when `ssl_verification_mode => none`"
|
178
188
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
179
189
|
end
|
180
190
|
end
|
@@ -184,10 +194,161 @@ describe LogStash::Outputs::Logstash do
|
|
184
194
|
let(:config) { super().merge("ssl_truststore_password" => "pa$$w0rd") }
|
185
195
|
|
186
196
|
it "not allowed" do
|
187
|
-
expected_message =
|
197
|
+
expected_message = "`ssl_truststore_password` not allowed unless `ssl_truststore_path` is configured"
|
188
198
|
expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
|
189
199
|
end
|
190
200
|
end
|
191
201
|
end
|
192
202
|
end
|
203
|
+
|
204
|
+
describe "#transmit" do
|
205
|
+
let(:normalized_host_uri) { "https://127.0.0.1:9800" }
|
206
|
+
|
207
|
+
let(:encoded_body) { "[]" }
|
208
|
+
let(:compressed_body) { "\x1F\xC3\xA3\b\x00\xE2\x80\xBA\xE2\x80\x9ACe\x00\x03\xC3\xA3\xC3\xA9\xC3\x82\x02\x00D\xE2\x80\x9Chp\x03\x00\x00\x00".b }
|
209
|
+
|
210
|
+
subject(:transmit_result) { registered_plugin.send(:transmit, encoded_body, compressed_body) }
|
211
|
+
|
212
|
+
context "successful transmission" do
|
213
|
+
before(:each) do
|
214
|
+
registered_plugin.http_client.stub(normalized_host_uri, body: "Response body", code: 200)
|
215
|
+
end
|
216
|
+
it "returns :done" do
|
217
|
+
expect(transmit_result).to eql :done
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "retriable HTTP errors" do
|
222
|
+
[429, 500].each do |retriable_response_code|
|
223
|
+
context "when http client emits #{retriable_response_code} retriable error response" do
|
224
|
+
before(:each) do
|
225
|
+
registered_plugin.http_client.stub(normalized_host_uri, body: "Response body", code: retriable_response_code)
|
226
|
+
end
|
227
|
+
it 'returns :retry' do
|
228
|
+
expect(transmit_result).to eql :retry
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "terminal HTTP errors" do
|
235
|
+
[301, 400, 404].each do |terminal_response_code|
|
236
|
+
context "when http client emits #{terminal_response_code} terminal error response" do
|
237
|
+
before(:each) do
|
238
|
+
registered_plugin.http_client.stub(normalized_host_uri, body: "Response body", code: terminal_response_code)
|
239
|
+
end
|
240
|
+
it 'returns :abort' do
|
241
|
+
expect(transmit_result).to eql :abort
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context "retriable transmission exceptions" do
|
248
|
+
[
|
249
|
+
Manticore::Timeout.new,
|
250
|
+
Manticore::SocketException.new,
|
251
|
+
Manticore::ClientProtocolException.new,
|
252
|
+
Manticore::ResolutionFailure.new,
|
253
|
+
Manticore::SocketTimeout.new,
|
254
|
+
Manticore::UnknownException.new("Connection reset by peer"),
|
255
|
+
Manticore::UnknownException.new("Read Timed out"),
|
256
|
+
].each do |manticore_exception|
|
257
|
+
context "when http client raises retriable exception `#{manticore_exception}`" do
|
258
|
+
before(:each) do
|
259
|
+
expect(registered_plugin.http_client).to receive(:post).and_raise(manticore_exception)
|
260
|
+
end
|
261
|
+
it "returns :retry" do
|
262
|
+
expect(transmit_result).to eql :retry
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe '#multi_receive' do
|
270
|
+
let(:events) { [event] }
|
271
|
+
|
272
|
+
context "when first transmit succeeds" do
|
273
|
+
before(:each) do
|
274
|
+
allow(registered_plugin).to receive(:transmit).and_return(:abort).once
|
275
|
+
end
|
276
|
+
it "transmits once" do
|
277
|
+
registered_plugin.multi_receive(events)
|
278
|
+
expect(registered_plugin).to have_received(:transmit).once
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context "when first transmit gets terminal failure" do
|
283
|
+
before(:each) do
|
284
|
+
allow(registered_plugin).to receive(:transmit).and_return(:abort).once
|
285
|
+
end
|
286
|
+
it "transmits once" do
|
287
|
+
registered_plugin.multi_receive(events)
|
288
|
+
expect(registered_plugin).to have_received(:transmit).once
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context "when transmit indicates that a retry is required" do
|
293
|
+
# Configure a _sequence_ of `#transmit` responses
|
294
|
+
# emits :retry `retry_count` times, invoking `limit_met_hook` (if present) before the
|
295
|
+
# LAST normal retry, then emits `limit_met_next_action`.
|
296
|
+
# as a safeguard, this mock can be called AT MOST `retry_count + 1` times
|
297
|
+
let(:retry_count) { 3 }
|
298
|
+
let(:limit_met_hook) { nil }
|
299
|
+
let(:limit_met_next_action) { :retry }
|
300
|
+
before(:each) do
|
301
|
+
attempts_made = 0
|
302
|
+
next_action = :retry
|
303
|
+
allow(registered_plugin).to receive(:transmit).with(any_args) do
|
304
|
+
current_action = next_action
|
305
|
+
attempts_made += 1
|
306
|
+
if attempts_made == retry_count
|
307
|
+
limit_met_hook&.call
|
308
|
+
next_action = limit_met_next_action
|
309
|
+
end
|
310
|
+
current_action
|
311
|
+
end.at_most(retry_count + 1).times
|
312
|
+
end
|
313
|
+
|
314
|
+
context "and transmit eventually succeeds" do
|
315
|
+
let(:limit_met_next_action) { :done }
|
316
|
+
it "stops retrying" do
|
317
|
+
registered_plugin.multi_receive(events)
|
318
|
+
|
319
|
+
expect(registered_plugin).to have_received(:transmit).exactly(retry_count + 1).times
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "and transmit eventually gets terminal failure" do
|
324
|
+
let(:limit_met_next_action) { :abort }
|
325
|
+
it "stops retrying" do
|
326
|
+
registered_plugin.multi_receive(events)
|
327
|
+
|
328
|
+
expect(registered_plugin).to have_received(:transmit).exactly(retry_count + 1).times
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "and the pipeline shutdown is requested before transmission succeeds" do
|
333
|
+
let(:limit_met_hook) do
|
334
|
+
->() { expect(registered_plugin).to receive(:pipeline_shutdown_requested?).and_return(:true) }
|
335
|
+
end
|
336
|
+
let(:limit_met_next_action) { :retry }
|
337
|
+
|
338
|
+
if ::Gem::Version.create(LOGSTASH_VERSION) >= ::Gem::Version.create('8.8.0')
|
339
|
+
it 'aborts the batch' do
|
340
|
+
expect { registered_plugin.multi_receive(events) }.to raise_exception(org.logstash.execution.AbortedBatchException)
|
341
|
+
|
342
|
+
expect(registered_plugin).to have_received(:transmit).exactly(retry_count).times
|
343
|
+
end
|
344
|
+
else
|
345
|
+
it "stops retrying" do
|
346
|
+
registered_plugin.multi_receive(events)
|
347
|
+
|
348
|
+
expect(registered_plugin).to have_received(:transmit).exactly(retry_count).times
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
193
354
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-integration-logstash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,6 +72,20 @@ dependencies:
|
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '3.1'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '7.3'
|
81
|
+
name: logstash-mixin-http_client
|
82
|
+
prerelease: false
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '7.3'
|
75
89
|
- !ruby/object:Gem::Dependency
|
76
90
|
requirement: !ruby/object:Gem::Requirement
|
77
91
|
requirements:
|
@@ -91,15 +105,15 @@ dependencies:
|
|
91
105
|
requirements:
|
92
106
|
- - ">="
|
93
107
|
- !ruby/object:Gem::Version
|
94
|
-
version:
|
95
|
-
name:
|
108
|
+
version: '0'
|
109
|
+
name: stud
|
96
110
|
prerelease: false
|
97
111
|
type: :runtime
|
98
112
|
version_requirements: !ruby/object:Gem::Requirement
|
99
113
|
requirements:
|
100
114
|
- - ">="
|
101
115
|
- !ruby/object:Gem::Version
|
102
|
-
version:
|
116
|
+
version: '0'
|
103
117
|
- !ruby/object:Gem::Dependency
|
104
118
|
requirement: !ruby/object:Gem::Requirement
|
105
119
|
requirements:
|
@@ -161,6 +175,7 @@ files:
|
|
161
175
|
- docs/output-logstash.asciidoc
|
162
176
|
- lib/logstash/inputs/logstash.rb
|
163
177
|
- lib/logstash/outputs/logstash.rb
|
178
|
+
- lib/logstash/utils/load_balancer.rb
|
164
179
|
- logstash-integration-logstash.gemspec
|
165
180
|
- spec/fixtures/certs/generate.sh
|
166
181
|
- spec/fixtures/certs/generated/README.txt
|
@@ -192,6 +207,7 @@ files:
|
|
192
207
|
- spec/fixtures/certs/openssl.cnf
|
193
208
|
- spec/spec_helper.rb
|
194
209
|
- spec/unit/full_transmission_spec.rb
|
210
|
+
- spec/unit/load_balancer_spec.rb
|
195
211
|
- spec/unit/logstash_input_spec.rb
|
196
212
|
- spec/unit/logstash_output_spec.rb
|
197
213
|
homepage: https://www.elastic.co/logstash
|
@@ -253,5 +269,6 @@ test_files:
|
|
253
269
|
- spec/fixtures/certs/openssl.cnf
|
254
270
|
- spec/spec_helper.rb
|
255
271
|
- spec/unit/full_transmission_spec.rb
|
272
|
+
- spec/unit/load_balancer_spec.rb
|
256
273
|
- spec/unit/logstash_input_spec.rb
|
257
274
|
- spec/unit/logstash_output_spec.rb
|