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.
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