logstash-output-elasticsearch 10.8.6-java → 11.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/docs/index.asciidoc +132 -22
  4. data/lib/logstash/outputs/elasticsearch.rb +122 -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 +47 -34
  8. data/lib/logstash/outputs/elasticsearch/ilm.rb +11 -12
  9. data/lib/logstash/outputs/elasticsearch/license_checker.rb +19 -22
  10. data/lib/logstash/outputs/elasticsearch/template_manager.rb +3 -5
  11. data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +157 -153
  12. data/lib/logstash/plugin_mixins/elasticsearch/common.rb +70 -58
  13. data/logstash-output-elasticsearch.gemspec +2 -2
  14. data/spec/es_spec_helper.rb +3 -6
  15. data/spec/integration/outputs/data_stream_spec.rb +61 -0
  16. data/spec/integration/outputs/ilm_spec.rb +6 -2
  17. data/spec/integration/outputs/ingest_pipeline_spec.rb +4 -2
  18. data/spec/integration/outputs/retry_spec.rb +4 -4
  19. data/spec/integration/outputs/sniffer_spec.rb +0 -1
  20. data/spec/spec_helper.rb +14 -0
  21. data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +542 -0
  22. data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +1 -0
  23. data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +24 -10
  24. data/spec/unit/outputs/elasticsearch/http_client_spec.rb +2 -3
  25. data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +1 -3
  26. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +1 -2
  27. data/spec/unit/outputs/elasticsearch_spec.rb +122 -23
  28. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +1 -2
  29. data/spec/unit/outputs/error_whitelist_spec.rb +3 -2
  30. data/spec/unit/outputs/license_check_spec.rb +0 -16
  31. metadata +23 -16
@@ -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 }
@@ -9,7 +9,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
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
11
  let(:oss) { true }
12
- let(:valid_license) { true }
12
+ let(:license_status) { 'active' }
13
13
 
14
14
  subject { described_class.new(logger, adapter, initial_urls, options) }
15
15
 
@@ -26,8 +26,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
26
26
  allow(::Manticore::Client).to receive(:new).and_return(manticore_double)
27
27
 
28
28
  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)
29
+ allow(subject.license_checker).to receive(:license_status).and_return(license_status)
31
30
  end
32
31
 
33
32
  after do
@@ -283,21 +282,21 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
283
282
  let(:oss) { false }
284
283
 
285
284
  context "if ES doesn't return a valid license" do
286
- let(:valid_license) { false }
285
+ let(:license_status) { nil }
287
286
 
288
- it "marks the url as active" do
287
+ it "marks the url as dead" do
289
288
  subject.update_initial_urls
290
- expect(subject.alive_urls_count).to eq(1)
289
+ expect(subject.alive_urls_count).to eq(0)
291
290
  end
292
291
 
293
292
  it "logs a warning" do
294
- expect(subject.license_checker).to receive(:log_license_deprecation_warn).once
293
+ expect(subject.license_checker).to receive(:warn_no_license).once.and_call_original
295
294
  subject.update_initial_urls
296
295
  end
297
296
  end
298
297
 
299
298
  context "if ES returns a valid license" do
300
- let(:valid_license) { true }
299
+ let(:license_status) { 'active' }
301
300
 
302
301
  it "marks the url as active" do
303
302
  subject.update_initial_urls
@@ -305,7 +304,22 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
305
304
  end
306
305
 
307
306
  it "does not log a warning" do
308
- expect(subject.license_checker).to_not receive(:log_license_deprecation_warn)
307
+ expect(subject.license_checker).to_not receive(:warn_no_license)
308
+ expect(subject.license_checker).to_not receive(:warn_invalid_license)
309
+ subject.update_initial_urls
310
+ end
311
+ end
312
+
313
+ context "if ES returns an invalid license" do
314
+ let(:license_status) { 'invalid' }
315
+
316
+ it "marks the url as active" do
317
+ subject.update_initial_urls
318
+ expect(subject.alive_urls_count).to eq(1)
319
+ end
320
+
321
+ it "logs a warning" do
322
+ expect(subject.license_checker).to receive(:warn_invalid_license).and_call_original
309
323
  subject.update_initial_urls
310
324
  end
311
325
  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,7 +1,5 @@
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
 
@@ -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,6 +1,7 @@
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
@@ -10,18 +11,30 @@ describe LogStash::Outputs::ElasticSearch do
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)}
@@ -253,8 +272,7 @@ describe LogStash::Outputs::ElasticSearch do
253
272
  before do
254
273
  allow(subject).to receive(:retrying_submit).with(anything)
255
274
  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)
275
+ allow(subject).to receive(:event_action_tuple).with(e).and_return(events_tuples[i])
258
276
  end
259
277
  subject.multi_receive(events)
260
278
  end
@@ -345,7 +363,7 @@ describe LogStash::Outputs::ElasticSearch do
345
363
 
346
364
  it "should log specific error message" do
347
365
  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)'))
366
+ hash_including(:message => 'Sent 2 documents but Elasticsearch returned 3 responses (likely a bug with _bulk endpoint)'))
349
367
 
350
368
  subject.multi_receive(events)
351
369
  end
@@ -402,7 +420,7 @@ describe LogStash::Outputs::ElasticSearch do
402
420
 
403
421
  before do
404
422
  # 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)
423
+ expect(subject.logger).to receive(:error).with(/Attempted to send a bulk request/i, anything).at_least(:once)
406
424
  expect(subject.client).to receive(:bulk).at_least(:twice).and_call_original
407
425
  end
408
426
 
@@ -416,13 +434,14 @@ describe LogStash::Outputs::ElasticSearch do
416
434
  end
417
435
 
418
436
  describe "the action option" do
437
+
419
438
  context "with a sprintf action" do
420
439
  let(:options) { {"action" => "%{myactionfield}" } }
421
440
 
422
441
  let(:event) { LogStash::Event.new("myactionfield" => "update", "message" => "blah") }
423
442
 
424
443
  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")
444
+ expect(subject.send(:event_action_tuple, event).first).to eql("update")
426
445
  end
427
446
  end
428
447
 
@@ -432,7 +451,7 @@ describe LogStash::Outputs::ElasticSearch do
432
451
  let(:event) { LogStash::Event.new("myactionfield" => "update", "message" => "blah") }
433
452
 
434
453
  it "should obtain specific action's params from event_action_tuple" do
435
- expect(subject.event_action_tuple(event)[1]).to include(:_upsert)
454
+ expect(subject.send(:event_action_tuple, event)[1]).to include(:_upsert)
436
455
  end
437
456
  end
438
457
 
@@ -440,6 +459,8 @@ describe LogStash::Outputs::ElasticSearch do
440
459
  let(:options) { {"action" => "SOME Garbaaage"} }
441
460
  let(:do_register) { false } # this is what we want to test, so we disable the before(:each) call
442
461
 
462
+ before { allow(subject).to receive(:finish_register) }
463
+
443
464
  it "should raise a configuration error" do
444
465
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
445
466
  end
@@ -447,13 +468,14 @@ describe LogStash::Outputs::ElasticSearch do
447
468
  end
448
469
 
449
470
  describe "the pipeline option" do
471
+
450
472
  context "with a sprintf and set pipeline" do
451
473
  let(:options) { {"pipeline" => "%{pipeline}" } }
452
474
 
453
475
  let(:event) { LogStash::Event.new("pipeline" => "my-ingest-pipeline") }
454
476
 
455
477
  it "should interpolate the pipeline value and set it" do
456
- expect(subject.event_action_tuple(event)[1]).to include(:pipeline => "my-ingest-pipeline")
478
+ expect(subject.send(:event_action_tuple, event)[1]).to include(:pipeline => "my-ingest-pipeline")
457
479
  end
458
480
  end
459
481
 
@@ -463,7 +485,7 @@ describe LogStash::Outputs::ElasticSearch do
463
485
  let(:event) { LogStash::Event.new("pipeline" => "") }
464
486
 
465
487
  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)
488
+ expect(subject.send(:event_action_tuple, event)[1]).not_to include(:pipeline)
467
489
  end
468
490
  end
469
491
  end
@@ -505,8 +527,8 @@ describe LogStash::Outputs::ElasticSearch do
505
527
 
506
528
  it "should not set the retry_on_conflict parameter when creating an event_action_tuple" do
507
529
  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})
530
+ action, params, event_data = subject.send(:event_action_tuple, event)
531
+ expect(params).not_to include({subject.send(:retry_on_conflict_action_name) => num_retries})
510
532
  end
511
533
  end
512
534
 
@@ -514,8 +536,8 @@ describe LogStash::Outputs::ElasticSearch do
514
536
  let(:options) { super().merge("action" => "update", "retry_on_conflict" => num_retries, "document_id" => 1) }
515
537
 
516
538
  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})
539
+ action, params, event_data = subject.send(:event_action_tuple, event)
540
+ expect(params).to include({subject.send(:retry_on_conflict_action_name) => num_retries})
519
541
  end
520
542
  end
521
543
 
@@ -523,8 +545,8 @@ describe LogStash::Outputs::ElasticSearch do
523
545
  let(:options) { super().merge("action" => "%{myactionfield}", "retry_on_conflict" => num_retries, "document_id" => 1) }
524
546
 
525
547
  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})
548
+ action, params, event_data = subject.send(:event_action_tuple, event)
549
+ expect(params).to include({subject.send(:retry_on_conflict_action_name) => num_retries})
528
550
  expect(action).to eq("update")
529
551
  end
530
552
  end
@@ -554,6 +576,8 @@ describe LogStash::Outputs::ElasticSearch do
554
576
  let(:do_register) { false }
555
577
 
556
578
  before :each do
579
+ allow(subject).to receive(:finish_register)
580
+
557
581
  allow(::Manticore::Client).to receive(:new).with(any_args).and_call_original
558
582
  end
559
583
 
@@ -577,6 +601,12 @@ describe LogStash::Outputs::ElasticSearch do
577
601
  let(:custom_parameters_hash) { { "id" => 1, "name" => "logstash" } }
578
602
  let(:custom_parameters_query) { custom_parameters_hash.map {|k,v| "#{k}=#{v}" }.join("&") }
579
603
 
604
+ let(:stub_http_client_pool!) do
605
+ [:start_resurrectionist, :start_sniffer, :healthcheck!].each do |method|
606
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(method)
607
+ end
608
+ end
609
+
580
610
  context "using non-url hosts" do
581
611
 
582
612
  let(:options) {
@@ -783,7 +813,7 @@ describe LogStash::Outputs::ElasticSearch do
783
813
  end
784
814
 
785
815
  describe "custom headers" do
786
- let(:manticore_options) { subject.client.pool.adapter.manticore.instance_variable_get(:@options) }
816
+ let(:manticore_options) { subject.client.pool.adapter.manticore.instance_variable_get(:@options) }
787
817
 
788
818
  context "when set" do
789
819
  let(:headers) { { "X-Thing" => "Test" } }
@@ -856,6 +886,75 @@ describe LogStash::Outputs::ElasticSearch do
856
886
  end
857
887
  end
858
888
 
889
+ describe "post-register ES setup" do
890
+ let(:do_register) { false }
891
+ let(:es_version) { '7.10.0' } # DS default on LS 8.x
892
+ let(:options) { { 'hosts' => '127.0.0.1:9999' } }
893
+ let(:logger) { subject.logger }
894
+
895
+ before do
896
+ allow(logger).to receive(:error) # expect tracking
897
+
898
+ allow(subject).to receive(:last_es_version).and_return es_version
899
+ # make successful_connection? return true:
900
+ allow(subject).to receive(:maximum_seen_major_version).and_return Integer(es_version.split('.').first)
901
+ allow(subject).to receive(:stop_after_successful_connection_thread)
902
+ end
903
+
904
+ it "logs inability to retrieve uuid" do
905
+ allow(subject).to receive(:install_template)
906
+ allow(subject).to receive(:ilm_in_use?).and_return nil
907
+ subject.register
908
+ subject.send :wait_for_successful_connection
909
+
910
+ expect(logger).to have_received(:error).with(/Unable to retrieve Elasticsearch cluster uuid/i, anything)
911
+ end if LOGSTASH_VERSION >= '7.0.0'
912
+
913
+ it "logs template install failure" do
914
+ allow(subject).to receive(:discover_cluster_uuid)
915
+ allow(subject).to receive(:ilm_in_use?).and_return nil
916
+ subject.register
917
+ subject.send :wait_for_successful_connection
918
+
919
+ expect(logger).to have_received(:error).with(/Failed to install template/i, anything)
920
+ end
921
+
922
+ context 'error raised' do
923
+
924
+ let(:es_version) { '7.8.0' }
925
+ let(:options) { super().merge('data_stream' => 'true') }
926
+ let(:latch) { Concurrent::CountDownLatch.new }
927
+
928
+ before do
929
+ allow(subject).to receive(:install_template)
930
+ allow(subject).to receive(:discover_cluster_uuid)
931
+ allow(subject).to receive(:ilm_in_use?).and_return nil
932
+ # executes from the after_successful_connection thread :
933
+ allow(subject).to receive(:finish_register) { latch.wait }.and_call_original
934
+ subject.register
935
+ end
936
+
937
+ it 'keeps logging on multi_receive' do
938
+ allow(subject).to receive(:retrying_submit)
939
+ latch.count_down; sleep(1.0)
940
+
941
+ expect_logged_error = lambda do |count|
942
+ expect(logger).to have_received(:error).with(
943
+ /Elasticsearch setup did not complete normally, please review previously logged errors/i,
944
+ hash_including(message: 'A data_stream configuration is only supported since Elasticsearch 7.9.0 (detected version 7.8.0), please upgrade your cluster')
945
+ ).exactly(count).times
946
+ end
947
+
948
+ subject.multi_receive [ LogStash::Event.new('foo' => 1) ]
949
+ expect_logged_error.call(1)
950
+
951
+ subject.multi_receive [ LogStash::Event.new('foo' => 2) ]
952
+ expect_logged_error.call(2)
953
+ end
954
+
955
+ end
956
+ end
957
+
859
958
  @private
860
959
 
861
960
  def stub_manticore_client!(manticore_double = nil)
@@ -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
 
5
4
  describe "SSL option" do
6
5
  let(:manticore_double) { double("manticoreSSL #{self.inspect}") }
@@ -12,6 +12,7 @@ describe "whitelisting error types in expected behavior" do
12
12
  before :each do
13
13
  allow(subject.logger).to receive(:warn)
14
14
  allow(subject).to receive(:maximum_seen_major_version).and_return(0)
15
+ allow(subject).to receive(:finish_register)
15
16
 
16
17
  subject.register
17
18
 
@@ -39,7 +40,7 @@ describe "whitelisting error types in expected behavior" do
39
40
 
40
41
  describe "when failure logging is enabled for everything" do
41
42
  it "should log a failure on the action" do
42
- expect(subject.logger).to have_received(:warn).with("Failed action.", anything)
43
+ expect(subject.logger).to have_received(:warn).with("Failed action", anything)
43
44
  end
44
45
  end
45
46
 
@@ -47,7 +48,7 @@ describe "whitelisting error types in expected behavior" do
47
48
  let(:settings) { super().merge("failure_type_logging_whitelist" => ["document_already_exists_exception"]) }
48
49
 
49
50
  it "should log a failure on the action" do
50
- expect(subject.logger).not_to have_received(:warn).with("Failed action.", anything)
51
+ expect(subject.logger).not_to have_received(:warn).with("Failed action", anything)
51
52
  end
52
53
  end
53
54
 
@@ -14,22 +14,6 @@ describe LogStash::Outputs::ElasticSearch::LicenseChecker do
14
14
  end
15
15
  end
16
16
 
17
- context "LicenseChecker API required by Pool specs" do
18
- subject { described_class }
19
-
20
- it "defines the oss? method" do
21
- expect(subject.instance_methods).to include(:oss?)
22
- end
23
-
24
- it "defines the valid_es_license? method" do
25
- expect(subject.instance_methods).to include(:valid_es_license?)
26
- end
27
-
28
- it "defines the log_license_deprecation_warn method" do
29
- expect(subject.instance_methods).to include(:log_license_deprecation_warn)
30
- end
31
- end
32
-
33
17
  context "Pool class API required by the LicenseChecker" do
34
18
  subject { LogStash::Outputs::ElasticSearch::HttpClient::Pool }
35
19