logstash-integration-logstash 0.0.5-java → 1.0.1-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 +6 -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 +254 -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 +28 -28
  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 +179 -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 }
@@ -16,6 +21,7 @@ describe LogStash::Outputs::Logstash do
16
21
  it { is_expected.to be_a_kind_of Class }
17
22
  it { is_expected.to be <= LogStash::Outputs::Base }
18
23
  it { is_expected.to have_attributes(:config_name => "logstash") }
24
+ it { is_expected.to have_attributes(:concurrency => :shared) }
19
25
  end
20
26
 
21
27
  describe "a plugin instance with minimal config" do
@@ -28,19 +34,22 @@ describe LogStash::Outputs::Logstash do
28
34
  end
29
35
 
30
36
  describe "plugin register" do
31
- let(:registered_plugin) { plugin.tap(&:register) }
32
37
 
33
38
  describe "construct host URI" do
34
39
 
35
40
  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
41
+ normalized_hosts = registered_plugin.send(:normalize_host_uris)
42
+ expect(normalized_hosts).to have_attributes(:size => 1)
43
+ expect(normalized_hosts.first).to eql("https://127.0.0.1:9800")
37
44
  end
38
45
 
39
46
  describe "SSL disabled" do
40
47
  let(:config) { super().merge("ssl_enabled" => false) }
41
48
 
42
49
  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
50
+ normalized_hosts = registered_plugin.send(:normalize_host_uris)
51
+ expect(normalized_hosts).to have_attributes(:size => 1)
52
+ expect(normalized_hosts.first).to eql("http://127.0.0.1:9800")
44
53
  end
45
54
  end
46
55
 
@@ -48,7 +57,9 @@ describe LogStash::Outputs::Logstash do
48
57
  let(:config) { super().merge("hosts" => "127.0.0.1:9808") }
49
58
 
50
59
  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
60
+ normalized_hosts = registered_plugin.send(:normalize_host_uris)
61
+ expect(normalized_hosts).to have_attributes(:size => 1)
62
+ expect(normalized_hosts.first).to eql("https://127.0.0.1:9808")
52
63
  end
53
64
  end
54
65
  end
@@ -61,7 +72,7 @@ describe LogStash::Outputs::Logstash do
61
72
 
62
73
  it "requires `password`" do
63
74
  expected_message = "`password` is REQUIRED when `username` is provided"
64
- expect{ plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
75
+ expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
65
76
  end
66
77
  end
67
78
 
@@ -95,15 +106,15 @@ describe LogStash::Outputs::Logstash do
95
106
  let(:config) { super().merge("ssl_certificate" => cert_fixture!('server_from_root.pem')) }
96
107
 
97
108
  it "requires `ssl_key`" do
98
- expected_message = '`ssl_key` is REQUIRED when `ssl_certificate` is provided'
109
+ expected_message = "`ssl_key` is REQUIRED when `ssl_certificate` is provided"
99
110
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
100
111
  end
101
112
 
102
113
  context "with keystore" do
103
- let(:config) { super().merge("ssl_keystore_path" => cert_fixture!('client_from_root.jks')) }
114
+ let(:config) { super().merge("ssl_keystore_path" => cert_fixture!("client_from_root.jks"), "ssl_key" => cert_fixture!("server_from_root.key.pem")) }
104
115
 
105
116
  it "cannot be used together" do
106
- expected_message = 'SSL identity can be configured with EITHER `ssl_certificate` OR `ssl_keystore_*`, but not both'
117
+ expected_message = "SSL identity can be configured with EITHER `ssl_certificate` OR `ssl_keystore_*`, but not both"
107
118
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
108
119
  end
109
120
  end
@@ -122,7 +133,7 @@ describe LogStash::Outputs::Logstash do
122
133
  let(:config) { super().merge("ssl_keystore_path" => cert_fixture!('server_from_root.jks')) }
123
134
 
124
135
  it "requires `ssl_keystore_password`" do
125
- expected_message = '`ssl_keystore_password` is REQUIRED when `ssl_keystore_path` is provided'
136
+ expected_message = "`ssl_keystore_password` is REQUIRED when `ssl_keystore_path` is provided"
126
137
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
127
138
  end
128
139
  end
@@ -131,7 +142,7 @@ describe LogStash::Outputs::Logstash do
131
142
  let(:config) { super().merge("ssl_keystore_password" => "pa$$w0rd") }
132
143
 
133
144
  it "requires `ssl_keystore_path`" do
134
- expected_message = '`ssl_keystore_password` is not allowed unless `ssl_keystore_path` is configured'
145
+ expected_message = "`ssl_keystore_password` is not allowed unless `ssl_keystore_path` is configured"
135
146
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
136
147
  end
137
148
  end
@@ -141,22 +152,22 @@ describe LogStash::Outputs::Logstash do
141
152
  let(:config) { super().merge("ssl_enabled" => true) }
142
153
 
143
154
  context "with CA" do
144
- let(:config) { super().merge("ssl_certificate_authorities" => cert_fixture!('root.pem')) }
155
+ let(:config) { super().merge("ssl_certificate_authorities" => cert_fixture!("root.pem")) }
145
156
 
146
157
  context "and `ssl_verification_mode` is 'none'" do
147
158
  let(:config) { super().merge("ssl_verification_mode" => "none") }
148
159
 
149
160
  it "not allowed" do
150
- expected_message = 'SSL Certificate Authorities cannot be configured when `ssl_verification_mode => none`'
161
+ expected_message = "SSL Certificate Authorities cannot be configured when `ssl_verification_mode => none`"
151
162
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
152
163
  end
153
164
  end
154
165
 
155
166
  context "and truststore" do
156
- let(:config) { super().merge("ssl_truststore_path" => cert_fixture!('client_self_signed.jks')) }
167
+ let(:config) { super().merge("ssl_truststore_path" => cert_fixture!("client_self_signed.jks")) }
157
168
 
158
169
  it "not allowed" do
159
- expected_message = 'SSL trust can be configured with EITHER `ssl_certificate_authorities` OR `ssl_truststore_*`, but not both'
170
+ expected_message = "SSL trust can be configured with EITHER `ssl_certificate_authorities` OR `ssl_truststore_*`, but not both"
160
171
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
161
172
  end
162
173
  end
@@ -166,7 +177,7 @@ describe LogStash::Outputs::Logstash do
166
177
  let(:config) { super().merge("ssl_truststore_path" => cert_fixture!('client_self_signed.jks')) }
167
178
 
168
179
  it "requires truststore password" do
169
- expected_message = '`ssl_truststore_password` is REQUIRED when `ssl_truststore_path` is provided'
180
+ expected_message = "`ssl_truststore_password` is REQUIRED when `ssl_truststore_path` is provided"
170
181
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
171
182
  end
172
183
 
@@ -174,7 +185,7 @@ describe LogStash::Outputs::Logstash do
174
185
  let(:config) { super().merge("ssl_verification_mode" => "none") }
175
186
 
176
187
  it "not allowed" do
177
- expected_message = 'SSL Truststore cannot be configured when `ssl_verification_mode => none`'
188
+ expected_message = "SSL Truststore cannot be configured when `ssl_verification_mode => none`"
178
189
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
179
190
  end
180
191
  end
@@ -184,10 +195,161 @@ describe LogStash::Outputs::Logstash do
184
195
  let(:config) { super().merge("ssl_truststore_password" => "pa$$w0rd") }
185
196
 
186
197
  it "not allowed" do
187
- expected_message = '`ssl_truststore_password` not allowed unless `ssl_truststore_path` is configured'
198
+ expected_message = "`ssl_truststore_password` not allowed unless `ssl_truststore_path` is configured"
188
199
  expect{ registered_plugin }.to raise_error(LogStash::ConfigurationError).with_message(expected_message)
189
200
  end
190
201
  end
191
202
  end
192
203
  end
204
+
205
+ describe "#transmit" do
206
+ let(:normalized_host_uri) { "https://127.0.0.1:9800" }
207
+
208
+ let(:encoded_body) { "[]" }
209
+ 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 }
210
+
211
+ subject(:transmit_result) { registered_plugin.send(:transmit, encoded_body, compressed_body) }
212
+
213
+ context "successful transmission" do
214
+ before(:each) do
215
+ registered_plugin.http_client.stub(normalized_host_uri, body: "Response body", code: 200)
216
+ end
217
+ it "returns :done" do
218
+ expect(transmit_result).to eql :done
219
+ end
220
+ end
221
+
222
+ context "retriable HTTP errors" do
223
+ [429, 500].each do |retriable_response_code|
224
+ context "when http client emits #{retriable_response_code} retriable error response" do
225
+ before(:each) do
226
+ registered_plugin.http_client.stub(normalized_host_uri, body: "Response body", code: retriable_response_code)
227
+ end
228
+ it 'returns :retry' do
229
+ expect(transmit_result).to eql :retry
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ context "terminal HTTP errors" do
236
+ [301, 400, 404].each do |terminal_response_code|
237
+ context "when http client emits #{terminal_response_code} terminal error response" do
238
+ before(:each) do
239
+ registered_plugin.http_client.stub(normalized_host_uri, body: "Response body", code: terminal_response_code)
240
+ end
241
+ it 'returns :abort' do
242
+ expect(transmit_result).to eql :abort
243
+ end
244
+ end
245
+ end
246
+ end
247
+
248
+ context "retriable transmission exceptions" do
249
+ [
250
+ Manticore::Timeout.new,
251
+ Manticore::SocketException.new,
252
+ Manticore::ClientProtocolException.new,
253
+ Manticore::ResolutionFailure.new,
254
+ Manticore::SocketTimeout.new,
255
+ Manticore::UnknownException.new("Connection reset by peer"),
256
+ Manticore::UnknownException.new("Read Timed out"),
257
+ ].each do |manticore_exception|
258
+ context "when http client raises retriable exception `#{manticore_exception}`" do
259
+ before(:each) do
260
+ expect(registered_plugin.http_client).to receive(:post).and_raise(manticore_exception)
261
+ end
262
+ it "returns :retry" do
263
+ expect(transmit_result).to eql :retry
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ describe '#multi_receive' do
271
+ let(:events) { [event] }
272
+
273
+ context "when first transmit succeeds" do
274
+ before(:each) do
275
+ allow(registered_plugin).to receive(:transmit).and_return(:abort).once
276
+ end
277
+ it "transmits once" do
278
+ registered_plugin.multi_receive(events)
279
+ expect(registered_plugin).to have_received(:transmit).once
280
+ end
281
+ end
282
+
283
+ context "when first transmit gets terminal failure" do
284
+ before(:each) do
285
+ allow(registered_plugin).to receive(:transmit).and_return(:abort).once
286
+ end
287
+ it "transmits once" do
288
+ registered_plugin.multi_receive(events)
289
+ expect(registered_plugin).to have_received(:transmit).once
290
+ end
291
+ end
292
+
293
+ context "when transmit indicates that a retry is required" do
294
+ # Configure a _sequence_ of `#transmit` responses
295
+ # emits :retry `retry_count` times, invoking `limit_met_hook` (if present) before the
296
+ # LAST normal retry, then emits `limit_met_next_action`.
297
+ # as a safeguard, this mock can be called AT MOST `retry_count + 1` times
298
+ let(:retry_count) { 3 }
299
+ let(:limit_met_hook) { nil }
300
+ let(:limit_met_next_action) { :retry }
301
+ before(:each) do
302
+ attempts_made = 0
303
+ next_action = :retry
304
+ allow(registered_plugin).to receive(:transmit).with(any_args) do
305
+ current_action = next_action
306
+ attempts_made += 1
307
+ if attempts_made == retry_count
308
+ limit_met_hook&.call
309
+ next_action = limit_met_next_action
310
+ end
311
+ current_action
312
+ end.at_most(retry_count + 1).times
313
+ end
314
+
315
+ context "and transmit eventually succeeds" do
316
+ let(:limit_met_next_action) { :done }
317
+ it "stops retrying" do
318
+ registered_plugin.multi_receive(events)
319
+
320
+ expect(registered_plugin).to have_received(:transmit).exactly(retry_count + 1).times
321
+ end
322
+ end
323
+
324
+ context "and transmit eventually gets terminal failure" do
325
+ let(:limit_met_next_action) { :abort }
326
+ it "stops retrying" do
327
+ registered_plugin.multi_receive(events)
328
+
329
+ expect(registered_plugin).to have_received(:transmit).exactly(retry_count + 1).times
330
+ end
331
+ end
332
+
333
+ context "and the pipeline shutdown is requested before transmission succeeds" do
334
+ let(:limit_met_hook) do
335
+ ->() { expect(registered_plugin).to receive(:pipeline_shutdown_requested?).and_return(:true) }
336
+ end
337
+ let(:limit_met_next_action) { :retry }
338
+
339
+ if ::Gem::Version.create(LOGSTASH_VERSION) >= ::Gem::Version.create('8.8.0')
340
+ it 'aborts the batch' do
341
+ expect { registered_plugin.multi_receive(events) }.to raise_exception(org.logstash.execution.AbortedBatchException)
342
+
343
+ expect(registered_plugin).to have_received(:transmit).exactly(retry_count).times
344
+ end
345
+ else
346
+ it "stops retrying" do
347
+ registered_plugin.multi_receive(events)
348
+
349
+ expect(registered_plugin).to have_received(:transmit).exactly(retry_count).times
350
+ end
351
+ end
352
+ end
353
+ end
354
+ end
193
355
  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.1
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-16 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