logstash-output-elasticsearch 10.8.6-java → 11.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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