logstash-output-elasticsearch 10.8.6-java → 11.0.3-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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/docs/index.asciidoc +132 -22
  4. data/lib/logstash/outputs/elasticsearch.rb +125 -64
  5. data/lib/logstash/outputs/elasticsearch/data_stream_support.rb +233 -0
  6. data/lib/logstash/outputs/elasticsearch/http_client.rb +9 -7
  7. data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +49 -62
  8. data/lib/logstash/outputs/elasticsearch/ilm.rb +13 -45
  9. data/lib/logstash/outputs/elasticsearch/license_checker.rb +26 -23
  10. data/lib/logstash/outputs/elasticsearch/template_manager.rb +4 -6
  11. data/lib/logstash/outputs/elasticsearch/templates/ecs-v1/elasticsearch-8x.json +1 -0
  12. data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +157 -153
  13. data/lib/logstash/plugin_mixins/elasticsearch/common.rb +71 -58
  14. data/logstash-output-elasticsearch.gemspec +3 -3
  15. data/spec/es_spec_helper.rb +7 -12
  16. data/spec/fixtures/_nodes/{5x_6x.json → 6x.json} +5 -5
  17. data/spec/integration/outputs/compressed_indexing_spec.rb +47 -46
  18. data/spec/integration/outputs/data_stream_spec.rb +61 -0
  19. data/spec/integration/outputs/delete_spec.rb +49 -51
  20. data/spec/integration/outputs/ilm_spec.rb +236 -248
  21. data/spec/integration/outputs/index_spec.rb +5 -2
  22. data/spec/integration/outputs/index_version_spec.rb +78 -82
  23. data/spec/integration/outputs/ingest_pipeline_spec.rb +58 -58
  24. data/spec/integration/outputs/painless_update_spec.rb +74 -164
  25. data/spec/integration/outputs/parent_spec.rb +67 -75
  26. data/spec/integration/outputs/retry_spec.rb +6 -6
  27. data/spec/integration/outputs/sniffer_spec.rb +15 -54
  28. data/spec/integration/outputs/templates_spec.rb +79 -81
  29. data/spec/integration/outputs/update_spec.rb +99 -101
  30. data/spec/spec_helper.rb +10 -0
  31. data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +528 -0
  32. data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +1 -0
  33. data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +36 -29
  34. data/spec/unit/outputs/elasticsearch/http_client_spec.rb +2 -3
  35. data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +10 -12
  36. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +1 -2
  37. data/spec/unit/outputs/elasticsearch_spec.rb +176 -41
  38. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +1 -2
  39. data/spec/unit/outputs/error_whitelist_spec.rb +3 -2
  40. data/spec/unit/outputs/license_check_spec.rb +0 -16
  41. metadata +29 -36
  42. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-2x.json +0 -95
  43. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-5x.json +0 -46
  44. data/spec/fixtures/_nodes/2x_1x.json +0 -27
  45. data/spec/fixtures/scripts/groovy/scripted_update.groovy +0 -2
  46. data/spec/fixtures/scripts/groovy/scripted_update_nested.groovy +0 -2
  47. data/spec/fixtures/scripts/groovy/scripted_upsert.groovy +0 -2
  48. data/spec/integration/outputs/groovy_update_spec.rb +0 -150
  49. data/spec/integration/outputs/templates_5x_spec.rb +0 -98
@@ -1,5 +1,6 @@
1
1
  require "logstash/devutils/rspec/spec_helper"
2
2
  require "logstash/outputs/elasticsearch/http_client"
3
+ require 'cabin'
3
4
 
4
5
  describe LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter do
5
6
  let(:logger) { Cabin::Channel.get }
@@ -1,6 +1,6 @@
1
1
  require "logstash/devutils/rspec/spec_helper"
2
2
  require "logstash/outputs/elasticsearch/http_client"
3
- require "json"
3
+ require 'cabin'
4
4
 
5
5
  describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
6
6
  let(:logger) { Cabin::Channel.get }
@@ -8,15 +8,12 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
8
8
  let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] }
9
9
  let(:options) { {:resurrect_delay => 2, :url_normalizer => proc {|u| u}} } # Shorten the delay a bit to speed up tests
10
10
  let(:es_node_versions) { [ "0.0.0" ] }
11
- let(:oss) { true }
12
- let(:valid_license) { true }
11
+ let(:license_status) { 'active' }
13
12
 
14
13
  subject { described_class.new(logger, adapter, initial_urls, options) }
15
14
 
16
15
  let(:manticore_double) { double("manticore a") }
17
16
  before(:each) do
18
- stub_const('LogStash::OSS', oss)
19
-
20
17
  response_double = double("manticore response").as_null_object
21
18
  # Allow healtchecks
22
19
  allow(manticore_double).to receive(:head).with(any_args).and_return(response_double)
@@ -26,8 +23,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
26
23
  allow(::Manticore::Client).to receive(:new).and_return(manticore_double)
27
24
 
28
25
  allow(subject).to receive(:get_es_version).with(any_args).and_return(*es_node_versions)
29
- allow(subject.license_checker).to receive(:oss?).and_return(oss)
30
- allow(subject.license_checker).to receive(:valid_es_license?).and_return(valid_license)
26
+ allow(subject.license_checker).to receive(:license_status).and_return(license_status)
31
27
  end
32
28
 
33
29
  after do
@@ -279,35 +275,46 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
279
275
  allow(subject).to receive(:health_check_request)
280
276
  end
281
277
 
282
- context "when using default logstash distribution" do
283
- let(:oss) { false }
278
+ context "if ES doesn't return a valid license" do
279
+ let(:license_status) { nil }
284
280
 
285
- context "if ES doesn't return a valid license" do
286
- let(:valid_license) { false }
281
+ it "marks the url as dead" do
282
+ subject.update_initial_urls
283
+ expect(subject.alive_urls_count).to eq(0)
284
+ end
287
285
 
288
- it "marks the url as active" do
289
- subject.update_initial_urls
290
- expect(subject.alive_urls_count).to eq(1)
291
- end
286
+ it "logs a warning" do
287
+ expect(subject.license_checker).to receive(:warn_no_license).once.and_call_original
288
+ subject.update_initial_urls
289
+ end
290
+ end
292
291
 
293
- it "logs a warning" do
294
- expect(subject.license_checker).to receive(:log_license_deprecation_warn).once
295
- subject.update_initial_urls
296
- end
292
+ context "if ES returns a valid license" do
293
+ let(:license_status) { 'active' }
294
+
295
+ it "marks the url as active" do
296
+ subject.update_initial_urls
297
+ expect(subject.alive_urls_count).to eq(1)
297
298
  end
298
299
 
299
- context "if ES returns a valid license" do
300
- let(:valid_license) { true }
300
+ it "does not log a warning" do
301
+ expect(subject.license_checker).to_not receive(:warn_no_license)
302
+ expect(subject.license_checker).to_not receive(:warn_invalid_license)
303
+ subject.update_initial_urls
304
+ end
305
+ end
301
306
 
302
- it "marks the url as active" do
303
- subject.update_initial_urls
304
- expect(subject.alive_urls_count).to eq(1)
305
- end
307
+ context "if ES returns an invalid license" do
308
+ let(:license_status) { 'invalid' }
306
309
 
307
- it "does not log a warning" do
308
- expect(subject.license_checker).to_not receive(:log_license_deprecation_warn)
309
- subject.update_initial_urls
310
- end
310
+ it "marks the url as active" do
311
+ subject.update_initial_urls
312
+ expect(subject.alive_urls_count).to eq(1)
313
+ end
314
+
315
+ it "logs a warning" do
316
+ expect(subject.license_checker).to receive(:warn_invalid_license).and_call_original
317
+ subject.update_initial_urls
311
318
  end
312
319
  end
313
320
  end
@@ -1,7 +1,6 @@
1
- require_relative "../../../../spec/es_spec_helper"
2
- require "logstash/devutils/rspec/spec_helper"
1
+ require_relative "../../../../spec/spec_helper"
3
2
  require "logstash/outputs/elasticsearch/http_client"
4
- require "java"
3
+ require "cabin"
5
4
 
6
5
  describe LogStash::Outputs::ElasticSearch::HttpClient do
7
6
  let(:ssl) { nil }
@@ -1,24 +1,22 @@
1
1
  require "logstash/devutils/rspec/spec_helper"
2
- require "logstash/outputs/elasticsearch/http_client"
3
- require "java"
4
- require "json"
2
+ require "logstash/outputs/elasticsearch/template_manager"
5
3
 
6
4
  describe LogStash::Outputs::ElasticSearch::TemplateManager do
7
5
 
8
6
  describe ".default_template_path" do
9
- context "elasticsearch 1.x" do
10
- it "chooses the 2x template" do
11
- expect(described_class.default_template_path(1)).to end_with("/templates/ecs-disabled/elasticsearch-2x.json")
7
+ context "elasticsearch 6.x" do
8
+ it "chooses the 6x template" do
9
+ expect(described_class.default_template_path(6)).to end_with("/templates/ecs-disabled/elasticsearch-6x.json")
12
10
  end
13
11
  end
14
- context "elasticsearch 2.x" do
15
- it "chooses the 2x template" do
16
- expect(described_class.default_template_path(2)).to end_with("/templates/ecs-disabled/elasticsearch-2x.json")
12
+ context "elasticsearch 7.x" do
13
+ it "chooses the 7x template" do
14
+ expect(described_class.default_template_path(7)).to end_with("/templates/ecs-disabled/elasticsearch-7x.json")
17
15
  end
18
16
  end
19
- context "elasticsearch 5.x" do
20
- it "chooses the 5x template" do
21
- expect(described_class.default_template_path(5)).to end_with("/templates/ecs-disabled/elasticsearch-5x.json")
17
+ context "elasticsearch 8.x" do
18
+ it "chooses the 8x template" do
19
+ expect(described_class.default_template_path(8)).to end_with("/templates/ecs-disabled/elasticsearch-8x.json")
22
20
  end
23
21
  end
24
22
  end
@@ -1,6 +1,5 @@
1
- require_relative "../../../spec/es_spec_helper"
1
+ require_relative "../../../spec/spec_helper"
2
2
  require 'stud/temporary'
3
- require "logstash/outputs/elasticsearch"
4
3
  require 'manticore/client'
5
4
 
6
5
  describe "Proxy option" do
@@ -1,27 +1,40 @@
1
- require_relative "../../../spec/es_spec_helper"
1
+ require_relative "../../../spec/spec_helper"
2
2
  require "base64"
3
3
  require "flores/random"
4
+ require 'concurrent/atomic/count_down_latch'
4
5
  require "logstash/outputs/elasticsearch"
5
6
 
6
7
  describe LogStash::Outputs::ElasticSearch do
7
8
  subject(:elasticsearch_output_instance) { described_class.new(options) }
8
9
  let(:options) { {} }
9
- let(:maximum_seen_major_version) { [1,2,5,6,7,8].sample }
10
+ let(:maximum_seen_major_version) { [6,7,8].sample }
10
11
 
11
12
  let(:do_register) { true }
12
13
 
14
+ let(:stub_http_client_pool!) do
15
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:start)
16
+ end
17
+
18
+ let(:after_successful_connection_thread_mock) do
19
+ double('after_successful_connection_thread', value: true)
20
+ end
21
+
13
22
  before(:each) do
14
23
  if do_register
15
- # Build the client and set mocks before calling register to avoid races.
16
- subject.build_client
24
+ stub_http_client_pool!
25
+
26
+ allow(subject).to receive(:finish_register) # stub-out thread completion (to avoid error log entries)
27
+
28
+ # emulate 'successful' ES connection on the same thread
29
+ allow(subject).to receive(:after_successful_connection) { |&block| block.call }.
30
+ and_return after_successful_connection_thread_mock
31
+ allow(subject).to receive(:stop_after_successful_connection_thread)
32
+
33
+ subject.register
17
34
 
18
- # Rspec mocks can't handle background threads, so... we can't use any
19
- allow(subject.client.pool).to receive(:start_resurrectionist)
20
- allow(subject.client.pool).to receive(:start_sniffer)
21
- allow(subject.client.pool).to receive(:healthcheck!)
22
35
  allow(subject.client).to receive(:maximum_seen_major_version).at_least(:once).and_return(maximum_seen_major_version)
23
36
  allow(subject.client).to receive(:get_xpack_info)
24
- subject.register
37
+
25
38
  subject.client.pool.adapter.manticore.respond_with(:body => "{}")
26
39
  end
27
40
  end
@@ -44,6 +57,12 @@ describe LogStash::Outputs::ElasticSearch do
44
57
  let(:manticore_urls) { subject.client.pool.urls }
45
58
  let(:manticore_url) { manticore_urls.first }
46
59
 
60
+ let(:stub_http_client_pool!) do
61
+ [:start_resurrectionist, :start_sniffer, :healthcheck!].each do |method|
62
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(method)
63
+ end
64
+ end
65
+
47
66
  describe "getting a document type" do
48
67
  context "if document_type isn't set" do
49
68
  let(:options) { super().merge("document_type" => nil)}
@@ -60,13 +79,6 @@ describe LogStash::Outputs::ElasticSearch do
60
79
  expect(subject.send(:get_event_type, LogStash::Event.new("type" => "foo"))).to eql("doc")
61
80
  end
62
81
  end
63
-
64
- context "for < 6.0 elasticsearch clusters" do
65
- let(:maximum_seen_major_version) { 5 }
66
- it "should get the type from the event" do
67
- expect(subject.send(:get_event_type, LogStash::Event.new("type" => "foo"))).to eql("foo")
68
- end
69
- end
70
82
  end
71
83
 
72
84
  context "with 'document type set'" do
@@ -253,8 +265,7 @@ describe LogStash::Outputs::ElasticSearch do
253
265
  before do
254
266
  allow(subject).to receive(:retrying_submit).with(anything)
255
267
  events.each_with_index do |e,i|
256
- et = events_tuples[i]
257
- allow(subject).to receive(:event_action_tuple).with(e).and_return(et)
268
+ allow(subject).to receive(:event_action_tuple).with(e).and_return(events_tuples[i])
258
269
  end
259
270
  subject.multi_receive(events)
260
271
  end
@@ -325,7 +336,7 @@ describe LogStash::Outputs::ElasticSearch do
325
336
  }
326
337
  }]
327
338
  }
328
- end
339
+ end
329
340
 
330
341
  before(:each) do
331
342
  allow(subject.client).to receive(:bulk_send).with(instance_of(StringIO), instance_of(Array)) do |stream, actions|
@@ -345,7 +356,7 @@ describe LogStash::Outputs::ElasticSearch do
345
356
 
346
357
  it "should log specific error message" do
347
358
  expect(subject.logger).to receive(:error).with(/Encountered an unexpected error/i,
348
- hash_including(:error_message => 'Sent 2 documents but Elasticsearch returned 3 responses (likely a bug with _bulk endpoint)'))
359
+ hash_including(:message => 'Sent 2 documents but Elasticsearch returned 3 responses (likely a bug with _bulk endpoint)'))
349
360
 
350
361
  subject.multi_receive(events)
351
362
  end
@@ -402,7 +413,7 @@ describe LogStash::Outputs::ElasticSearch do
402
413
 
403
414
  before do
404
415
  # Expect a timeout to be logged.
405
- expect(subject.logger).to receive(:error).with(/Attempted to send a bulk request to Elasticsearch/i, anything).at_least(:once)
416
+ expect(subject.logger).to receive(:error).with(/Attempted to send a bulk request/i, anything).at_least(:once)
406
417
  expect(subject.client).to receive(:bulk).at_least(:twice).and_call_original
407
418
  end
408
419
 
@@ -416,13 +427,14 @@ describe LogStash::Outputs::ElasticSearch do
416
427
  end
417
428
 
418
429
  describe "the action option" do
430
+
419
431
  context "with a sprintf action" do
420
432
  let(:options) { {"action" => "%{myactionfield}" } }
421
433
 
422
434
  let(:event) { LogStash::Event.new("myactionfield" => "update", "message" => "blah") }
423
435
 
424
436
  it "should interpolate the requested action value when creating an event_action_tuple" do
425
- expect(subject.event_action_tuple(event).first).to eql("update")
437
+ expect(subject.send(:event_action_tuple, event).first).to eql("update")
426
438
  end
427
439
  end
428
440
 
@@ -432,7 +444,7 @@ describe LogStash::Outputs::ElasticSearch do
432
444
  let(:event) { LogStash::Event.new("myactionfield" => "update", "message" => "blah") }
433
445
 
434
446
  it "should obtain specific action's params from event_action_tuple" do
435
- expect(subject.event_action_tuple(event)[1]).to include(:_upsert)
447
+ expect(subject.send(:event_action_tuple, event)[1]).to include(:_upsert)
436
448
  end
437
449
  end
438
450
 
@@ -440,6 +452,8 @@ describe LogStash::Outputs::ElasticSearch do
440
452
  let(:options) { {"action" => "SOME Garbaaage"} }
441
453
  let(:do_register) { false } # this is what we want to test, so we disable the before(:each) call
442
454
 
455
+ before { allow(subject).to receive(:finish_register) }
456
+
443
457
  it "should raise a configuration error" do
444
458
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
445
459
  end
@@ -447,13 +461,14 @@ describe LogStash::Outputs::ElasticSearch do
447
461
  end
448
462
 
449
463
  describe "the pipeline option" do
464
+
450
465
  context "with a sprintf and set pipeline" do
451
466
  let(:options) { {"pipeline" => "%{pipeline}" } }
452
467
 
453
468
  let(:event) { LogStash::Event.new("pipeline" => "my-ingest-pipeline") }
454
469
 
455
470
  it "should interpolate the pipeline value and set it" do
456
- expect(subject.event_action_tuple(event)[1]).to include(:pipeline => "my-ingest-pipeline")
471
+ expect(subject.send(:event_action_tuple, event)[1]).to include(:pipeline => "my-ingest-pipeline")
457
472
  end
458
473
  end
459
474
 
@@ -463,7 +478,7 @@ describe LogStash::Outputs::ElasticSearch do
463
478
  let(:event) { LogStash::Event.new("pipeline" => "") }
464
479
 
465
480
  it "should interpolate the pipeline value but not set it because it is empty" do
466
- expect(subject.event_action_tuple(event)[1]).not_to include(:pipeline)
481
+ expect(subject.send(:event_action_tuple, event)[1]).not_to include(:pipeline)
467
482
  end
468
483
  end
469
484
  end
@@ -505,8 +520,8 @@ describe LogStash::Outputs::ElasticSearch do
505
520
 
506
521
  it "should not set the retry_on_conflict parameter when creating an event_action_tuple" do
507
522
  allow(subject.client).to receive(:maximum_seen_major_version).and_return(maximum_seen_major_version)
508
- action, params, event_data = subject.event_action_tuple(event)
509
- expect(params).not_to include({subject.retry_on_conflict_action_name => num_retries})
523
+ action, params, event_data = subject.send(:event_action_tuple, event)
524
+ expect(params).not_to include({subject.send(:retry_on_conflict_action_name) => num_retries})
510
525
  end
511
526
  end
512
527
 
@@ -514,8 +529,8 @@ describe LogStash::Outputs::ElasticSearch do
514
529
  let(:options) { super().merge("action" => "update", "retry_on_conflict" => num_retries, "document_id" => 1) }
515
530
 
516
531
  it "should set the retry_on_conflict parameter when creating an event_action_tuple" do
517
- action, params, event_data = subject.event_action_tuple(event)
518
- expect(params).to include({subject.retry_on_conflict_action_name => num_retries})
532
+ action, params, event_data = subject.send(:event_action_tuple, event)
533
+ expect(params).to include({subject.send(:retry_on_conflict_action_name) => num_retries})
519
534
  end
520
535
  end
521
536
 
@@ -523,8 +538,8 @@ describe LogStash::Outputs::ElasticSearch do
523
538
  let(:options) { super().merge("action" => "%{myactionfield}", "retry_on_conflict" => num_retries, "document_id" => 1) }
524
539
 
525
540
  it "should set the retry_on_conflict parameter when creating an event_action_tuple" do
526
- action, params, event_data = subject.event_action_tuple(event)
527
- expect(params).to include({subject.retry_on_conflict_action_name => num_retries})
541
+ action, params, event_data = subject.send(:event_action_tuple, event)
542
+ expect(params).to include({subject.send(:retry_on_conflict_action_name) => num_retries})
528
543
  expect(action).to eq("update")
529
544
  end
530
545
  end
@@ -554,6 +569,8 @@ describe LogStash::Outputs::ElasticSearch do
554
569
  let(:do_register) { false }
555
570
 
556
571
  before :each do
572
+ allow(subject).to receive(:finish_register)
573
+
557
574
  allow(::Manticore::Client).to receive(:new).with(any_args).and_call_original
558
575
  end
559
576
 
@@ -577,6 +594,12 @@ describe LogStash::Outputs::ElasticSearch do
577
594
  let(:custom_parameters_hash) { { "id" => 1, "name" => "logstash" } }
578
595
  let(:custom_parameters_query) { custom_parameters_hash.map {|k,v| "#{k}=#{v}" }.join("&") }
579
596
 
597
+ let(:stub_http_client_pool!) do
598
+ [:start_resurrectionist, :start_sniffer, :healthcheck!].each do |method|
599
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(method)
600
+ end
601
+ end
602
+
580
603
  context "using non-url hosts" do
581
604
 
582
605
  let(:options) {
@@ -741,9 +764,9 @@ describe LogStash::Outputs::ElasticSearch do
741
764
 
742
765
  context 'when getting any other exception' do
743
766
  it 'should log at WARN level' do
744
- dlog = double_logger = double("logger").as_null_object
745
- subject.instance_variable_set(:@logger, dlog)
746
- expect(dlog).to receive(:warn).with(/Could not index/, hash_including(:status, :action, :response))
767
+ logger = double("logger").as_null_object
768
+ subject.instance_variable_set(:@logger, logger)
769
+ expect(logger).to receive(:warn).with(/Could not index/, hash_including(:status, :action, :response))
747
770
  mock_response = { 'index' => { 'error' => { 'type' => 'illegal_argument_exception' } } }
748
771
  subject.handle_dlq_status("Could not index event to Elasticsearch.",
749
772
  [:action, :params, :event], :some_status, mock_response)
@@ -752,9 +775,9 @@ describe LogStash::Outputs::ElasticSearch do
752
775
 
753
776
  context 'when the response does not include [error]' do
754
777
  it 'should not fail, but just log a warning' do
755
- dlog = double_logger = double("logger").as_null_object
756
- subject.instance_variable_set(:@logger, dlog)
757
- expect(dlog).to receive(:warn).with(/Could not index/, hash_including(:status, :action, :response))
778
+ logger = double("logger").as_null_object
779
+ subject.instance_variable_set(:@logger, logger)
780
+ expect(logger).to receive(:warn).with(/Could not index/, hash_including(:status, :action, :response))
758
781
  mock_response = { 'index' => {} }
759
782
  expect do
760
783
  subject.handle_dlq_status("Could not index event to Elasticsearch.",
@@ -774,16 +797,59 @@ describe LogStash::Outputs::ElasticSearch do
774
797
  # We should still log when sending to the DLQ.
775
798
  # This shall be solved by another issue, however: logstash-output-elasticsearch#772
776
799
  it 'should send the event to the DLQ instead, and not log' do
777
- expect(dlq_writer).to receive(:write).once.with(:event, /Could not index/)
800
+ event = LogStash::Event.new("foo" => "bar")
801
+ expect(dlq_writer).to receive(:write).once.with(event, /Could not index/)
778
802
  mock_response = { 'index' => { 'error' => { 'type' => 'illegal_argument_exception' } } }
779
- subject.handle_dlq_status("Could not index event to Elasticsearch.",
780
- [:action, :params, :event], :some_status, mock_response)
803
+ action = LogStash::Outputs::ElasticSearch::EventActionTuple.new(:action, :params, event)
804
+ subject.handle_dlq_status("Could not index event to Elasticsearch.", action, 404, mock_response)
781
805
  end
782
806
  end
807
+
808
+ context 'with response status 400' do
809
+
810
+ let(:options) { super().merge 'document_id' => '%{foo}' }
811
+
812
+ let(:events) { [ LogStash::Event.new("foo" => "bar") ] }
813
+
814
+ let(:dlq_writer) { subject.instance_variable_get(:@dlq_writer) }
815
+
816
+ let(:bulk_response) do
817
+ {
818
+ "took"=>1, "ingest_took"=>11, "errors"=>true, "items"=>
819
+ [{
820
+ "index"=>{"_index"=>"bar", "_type"=>"_doc", "_id"=>'bar', "status"=>400,
821
+ "error"=>{"type" => "illegal_argument_exception", "reason" => "TEST" }
822
+ }
823
+ }]
824
+ }
825
+ end
826
+
827
+ before(:each) do
828
+ allow(subject.client).to receive(:bulk_send).and_return(bulk_response)
829
+ end
830
+
831
+ it "should write event to DLQ" do
832
+ expect(dlq_writer).to receive(:write).and_wrap_original do |method, *args|
833
+ expect( args.size ).to eql 2
834
+
835
+ event, reason = *args
836
+ expect( event ).to be_a LogStash::Event
837
+ expect( event ).to be events.first
838
+ expect( reason ).to start_with 'Could not index event to Elasticsearch. status: 400, action: ["index"'
839
+ expect( reason ).to match /_id=>"bar".*"foo"=>"bar".*response:.*"reason"=>"TEST"/
840
+
841
+ method.call(*args) # won't hurt to call LogStash::Util::DummyDeadLetterQueueWriter
842
+ end.once
843
+
844
+ event_action_tuples = subject.map_events(events)
845
+ subject.send(:submit, event_action_tuples)
846
+ end
847
+
848
+ end if LOGSTASH_VERSION > '7.0'
783
849
  end
784
850
 
785
851
  describe "custom headers" do
786
- let(:manticore_options) { subject.client.pool.adapter.manticore.instance_variable_get(:@options) }
852
+ let(:manticore_options) { subject.client.pool.adapter.manticore.instance_variable_get(:@options) }
787
853
 
788
854
  context "when set" do
789
855
  let(:headers) { { "X-Thing" => "Test" } }
@@ -856,6 +922,75 @@ describe LogStash::Outputs::ElasticSearch do
856
922
  end
857
923
  end
858
924
 
925
+ describe "post-register ES setup" do
926
+ let(:do_register) { false }
927
+ let(:es_version) { '7.10.0' } # DS default on LS 8.x
928
+ let(:options) { { 'hosts' => '127.0.0.1:9999' } }
929
+ let(:logger) { subject.logger }
930
+
931
+ before do
932
+ allow(logger).to receive(:error) # expect tracking
933
+
934
+ allow(subject).to receive(:last_es_version).and_return es_version
935
+ # make successful_connection? return true:
936
+ allow(subject).to receive(:maximum_seen_major_version).and_return Integer(es_version.split('.').first)
937
+ allow(subject).to receive(:stop_after_successful_connection_thread)
938
+ end
939
+
940
+ it "logs inability to retrieve uuid" do
941
+ allow(subject).to receive(:install_template)
942
+ allow(subject).to receive(:ilm_in_use?).and_return nil
943
+ subject.register
944
+ subject.send :wait_for_successful_connection
945
+
946
+ expect(logger).to have_received(:error).with(/Unable to retrieve Elasticsearch cluster uuid/i, anything)
947
+ end if LOGSTASH_VERSION >= '7.0.0'
948
+
949
+ it "logs template install failure" do
950
+ allow(subject).to receive(:discover_cluster_uuid)
951
+ allow(subject).to receive(:ilm_in_use?).and_return nil
952
+ subject.register
953
+ subject.send :wait_for_successful_connection
954
+
955
+ expect(logger).to have_received(:error).with(/Failed to install template/i, anything)
956
+ end
957
+
958
+ context 'error raised' do
959
+
960
+ let(:es_version) { '7.8.0' }
961
+ let(:options) { super().merge('data_stream' => 'true') }
962
+ let(:latch) { Concurrent::CountDownLatch.new }
963
+
964
+ before do
965
+ allow(subject).to receive(:install_template)
966
+ allow(subject).to receive(:discover_cluster_uuid)
967
+ allow(subject).to receive(:ilm_in_use?).and_return nil
968
+ # executes from the after_successful_connection thread :
969
+ allow(subject).to receive(:finish_register) { latch.wait }.and_call_original
970
+ subject.register
971
+ end
972
+
973
+ it 'keeps logging on multi_receive' do
974
+ allow(subject).to receive(:retrying_submit)
975
+ latch.count_down; sleep(1.0)
976
+
977
+ expect_logged_error = lambda do |count|
978
+ expect(logger).to have_received(:error).with(
979
+ /Elasticsearch setup did not complete normally, please review previously logged errors/i,
980
+ hash_including(message: 'A data_stream configuration is only supported since Elasticsearch 7.9.0 (detected version 7.8.0), please upgrade your cluster')
981
+ ).exactly(count).times
982
+ end
983
+
984
+ subject.multi_receive [ LogStash::Event.new('foo' => 1) ]
985
+ expect_logged_error.call(1)
986
+
987
+ subject.multi_receive [ LogStash::Event.new('foo' => 2) ]
988
+ expect_logged_error.call(2)
989
+ end
990
+
991
+ end
992
+ end
993
+
859
994
  @private
860
995
 
861
996
  def stub_manticore_client!(manticore_double = nil)