logstash-integration-logstash 0.0.5-java → 1.0.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/VERSION +1 -1
  4. data/docs/index.asciidoc +8 -5
  5. data/docs/output-logstash.asciidoc +6 -6
  6. data/lib/logstash/outputs/logstash.rb +251 -127
  7. data/lib/logstash/utils/load_balancer.rb +81 -0
  8. data/logstash-integration-logstash.gemspec +2 -1
  9. data/spec/fixtures/certs/generated/client_from_root.jks +0 -0
  10. data/spec/fixtures/certs/generated/client_from_root.key.pem +50 -50
  11. data/spec/fixtures/certs/generated/client_from_root.key.pkcs8.pem +52 -52
  12. data/spec/fixtures/certs/generated/client_from_root.p12 +0 -0
  13. data/spec/fixtures/certs/generated/client_from_root.pem +28 -28
  14. data/spec/fixtures/certs/generated/client_from_untrusted.jks +0 -0
  15. data/spec/fixtures/certs/generated/client_from_untrusted.key.pem +50 -50
  16. data/spec/fixtures/certs/generated/client_from_untrusted.key.pkcs8.pem +52 -52
  17. data/spec/fixtures/certs/generated/client_from_untrusted.p12 +0 -0
  18. data/spec/fixtures/certs/generated/client_from_untrusted.pem +28 -28
  19. data/spec/fixtures/certs/generated/client_self_signed.jks +0 -0
  20. data/spec/fixtures/certs/generated/client_self_signed.key.pem +50 -50
  21. data/spec/fixtures/certs/generated/client_self_signed.key.pkcs8.pem +52 -52
  22. data/spec/fixtures/certs/generated/client_self_signed.p12 +0 -0
  23. data/spec/fixtures/certs/generated/client_self_signed.pem +28 -28
  24. data/spec/fixtures/certs/generated/root.key.pem +50 -50
  25. data/spec/fixtures/certs/generated/root.pem +28 -28
  26. data/spec/fixtures/certs/generated/server_from_root-key-pkcs8.pem +50 -50
  27. data/spec/fixtures/certs/generated/server_from_root.jks +0 -0
  28. data/spec/fixtures/certs/generated/server_from_root.key.pem +50 -50
  29. data/spec/fixtures/certs/generated/server_from_root.key.pkcs8.pem +52 -52
  30. data/spec/fixtures/certs/generated/server_from_root.p12 +0 -0
  31. data/spec/fixtures/certs/generated/server_from_root.pem +29 -29
  32. data/spec/fixtures/certs/generated/untrusted.key.pem +50 -50
  33. data/spec/fixtures/certs/generated/untrusted.pem +28 -28
  34. data/spec/unit/full_transmission_spec.rb +10 -2
  35. data/spec/unit/load_balancer_spec.rb +67 -0
  36. data/spec/unit/logstash_output_spec.rb +178 -17
  37. 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
- expect(registered_plugin.construct_host_uri.eql?(::LogStash::Util::SafeURI.new("https://127.0.0.1:9800/"))).to be_truthy
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
- expect(registered_plugin.construct_host_uri.eql?(::LogStash::Util::SafeURI.new("http://127.0.0.1:9800/"))).to be_truthy
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
- expect(registered_plugin.construct_host_uri.eql?(::LogStash::Util::SafeURI.new("https://127.0.0.1:9808/"))).to be_truthy
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{ plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
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 = '`ssl_key` is REQUIRED when `ssl_certificate` is provided'
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!('client_from_root.jks')) }
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 = 'SSL identity can be configured with EITHER `ssl_certificate` OR `ssl_keystore_*`, but not both'
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 = '`ssl_keystore_password` is REQUIRED when `ssl_keystore_path` is provided'
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 = '`ssl_keystore_password` is not allowed unless `ssl_keystore_path` is configured'
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!('root.pem')) }
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 = 'SSL Certificate Authorities cannot be configured when `ssl_verification_mode => none`'
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!('client_self_signed.jks')) }
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 = 'SSL trust can be configured with EITHER `ssl_certificate_authorities` OR `ssl_truststore_*`, but not both'
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 = '`ssl_truststore_password` is REQUIRED when `ssl_truststore_path` is provided'
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 = 'SSL Truststore cannot be configured when `ssl_verification_mode => none`'
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 = '`ssl_truststore_password` not allowed unless `ssl_truststore_path` is configured'
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.5
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-10-03 00:00:00.000000000 Z
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: 5.6.0
95
- name: logstash-output-http
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: 5.6.0
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