logstash-input-elasticsearch 4.9.0 → 4.12.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +1 -1
- data/docs/index.asciidoc +48 -25
- data/lib/logstash/inputs/{patch.rb → elasticsearch/patches/_elasticsearch_transport_connections_selector.rb} +9 -6
- data/lib/logstash/inputs/elasticsearch/patches/_elasticsearch_transport_http_manticore.rb +43 -0
- data/lib/logstash/inputs/elasticsearch.rb +69 -36
- data/logstash-input-elasticsearch.gemspec +10 -8
- data/spec/es_helper.rb +17 -16
- data/spec/fixtures/test_certs/ca.crt +20 -0
- data/spec/fixtures/test_certs/ca.key +27 -0
- data/spec/fixtures/test_certs/es.crt +20 -0
- data/spec/fixtures/test_certs/es.key +27 -0
- data/spec/inputs/elasticsearch_spec.rb +343 -133
- data/spec/inputs/integration/elasticsearch_spec.rb +31 -13
- metadata +80 -52
- data/spec/fixtures/test_certs/ca/ca.crt +0 -32
- data/spec/fixtures/test_certs/ca/ca.key +0 -51
- data/spec/fixtures/test_certs/test.crt +0 -36
- data/spec/fixtures/test_certs/test.key +0 -51
@@ -7,16 +7,45 @@ require "timecop"
|
|
7
7
|
require "stud/temporary"
|
8
8
|
require "time"
|
9
9
|
require "date"
|
10
|
+
require "cabin"
|
11
|
+
require "webrick"
|
12
|
+
require "uri"
|
10
13
|
|
11
|
-
|
12
|
-
attr_reader :client
|
13
|
-
end
|
14
|
+
require 'logstash/plugin_mixins/ecs_compatibility_support/spec_helper'
|
14
15
|
|
15
|
-
describe LogStash::Inputs::
|
16
|
+
describe LogStash::Inputs::Elasticsearch, :ecs_compatibility_support do
|
16
17
|
|
17
|
-
let(:plugin) {
|
18
|
+
let(:plugin) { described_class.new(config) }
|
18
19
|
let(:queue) { Queue.new }
|
19
20
|
|
21
|
+
before(:each) do
|
22
|
+
Elasticsearch::Client.send(:define_method, :ping) { } # define no-action ping method
|
23
|
+
end
|
24
|
+
|
25
|
+
context "register" do
|
26
|
+
let(:config) do
|
27
|
+
{
|
28
|
+
"schedule" => "* * * * * UTC"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
context "against authentic Elasticsearch" do
|
33
|
+
it "should not raise an exception" do
|
34
|
+
expect { plugin.register }.to_not raise_error
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "against not authentic Elasticsearch" do
|
39
|
+
before(:each) do
|
40
|
+
Elasticsearch::Client.send(:define_method, :ping) { raise Elasticsearch::UnsupportedProductError.new("Fake error") } # define error ping method
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should raise ConfigurationError" do
|
44
|
+
expect { plugin.register }.to raise_error(LogStash::ConfigurationError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
20
49
|
it_behaves_like "an interruptible input plugin" do
|
21
50
|
let(:esclient) { double("elasticsearch-client") }
|
22
51
|
let(:config) do
|
@@ -37,10 +66,17 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
37
66
|
allow(esclient).to receive(:search) { { "hits" => { "hits" => [hit] } } }
|
38
67
|
allow(esclient).to receive(:scroll) { { "hits" => { "hits" => [hit] } } }
|
39
68
|
allow(esclient).to receive(:clear_scroll).and_return(nil)
|
69
|
+
allow(esclient).to receive(:ping)
|
40
70
|
end
|
41
71
|
end
|
42
72
|
|
43
|
-
|
73
|
+
|
74
|
+
ecs_compatibility_matrix(:disabled, :v1, :v8) do |ecs_select|
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
allow_any_instance_of(described_class).to receive(:ecs_compatibility).and_return(ecs_compatibility)
|
78
|
+
end
|
79
|
+
|
44
80
|
let(:config) do
|
45
81
|
%q[
|
46
82
|
input {
|
@@ -89,6 +125,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
89
125
|
expect(client).to receive(:search).with(any_args).and_return(mock_response)
|
90
126
|
expect(client).to receive(:scroll).with({ :body => { :scroll_id => "cXVlcnlUaGVuRmV0Y2g" }, :scroll=> "1m" }).and_return(mock_scroll_response)
|
91
127
|
expect(client).to receive(:clear_scroll).and_return(nil)
|
128
|
+
expect(client).to receive(:ping)
|
92
129
|
end
|
93
130
|
|
94
131
|
it 'creates the events from the hits' do
|
@@ -97,7 +134,6 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
97
134
|
end
|
98
135
|
|
99
136
|
expect(event).to be_a(LogStash::Event)
|
100
|
-
puts event.to_hash_with_metadata
|
101
137
|
expect(event.get("message")).to eql [ "ohayo" ]
|
102
138
|
end
|
103
139
|
|
@@ -120,10 +156,10 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
120
156
|
end
|
121
157
|
|
122
158
|
expect(event).to be_a(LogStash::Event)
|
123
|
-
puts event.to_hash_with_metadata
|
124
159
|
expect(event.get("[@metadata][_source][message]")).to eql [ "ohayo" ]
|
125
160
|
end
|
126
161
|
end
|
162
|
+
|
127
163
|
end
|
128
164
|
|
129
165
|
# This spec is an adapter-spec, ensuring that we send the right sequence of messages to our Elasticsearch Client
|
@@ -135,6 +171,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
135
171
|
'query' => "#{LogStash::Json.dump(query)}",
|
136
172
|
'slices' => slices,
|
137
173
|
'docinfo' => true, # include ids
|
174
|
+
'docinfo_target' => '[@metadata]'
|
138
175
|
}
|
139
176
|
end
|
140
177
|
let(:query) do
|
@@ -166,7 +203,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
166
203
|
end
|
167
204
|
|
168
205
|
context 'without slices directive' do
|
169
|
-
let(:config) { super.tap { |h| h.delete('slices') } }
|
206
|
+
let(:config) { super().tap { |h| h.delete('slices') } }
|
170
207
|
it 'runs just one slice' do
|
171
208
|
expect(plugin).to receive(:do_run_slice).with(duck_type(:<<))
|
172
209
|
expect(Thread).to_not receive(:new)
|
@@ -304,6 +341,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
304
341
|
expect(client).to receive(:search).with(hash_including(:body => slice0_query)).and_return(slice0_response0)
|
305
342
|
expect(client).to receive(:scroll).with(hash_including(:body => { :scroll_id => slice0_scroll1 })).and_return(slice0_response1)
|
306
343
|
expect(client).to receive(:scroll).with(hash_including(:body => { :scroll_id => slice0_scroll2 })).and_return(slice0_response2)
|
344
|
+
allow(client).to receive(:ping)
|
307
345
|
|
308
346
|
# SLICE1 is a two-page scroll in which the last page has no next scroll id
|
309
347
|
slice1_query = LogStash::Json.dump(query.merge('slice' => { 'id' => 1, 'max' => 2}))
|
@@ -403,129 +441,143 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
403
441
|
expect(client).to receive(:search).with(any_args).and_return(response)
|
404
442
|
allow(client).to receive(:scroll).with({ :body => {:scroll_id => "cXVlcnlUaGVuRmV0Y2g"}, :scroll => "1m" }).and_return(scroll_reponse)
|
405
443
|
allow(client).to receive(:clear_scroll).and_return(nil)
|
444
|
+
allow(client).to receive(:ping).and_return(nil)
|
406
445
|
end
|
407
446
|
|
408
|
-
|
409
|
-
let(:config_metadata) do
|
410
|
-
%q[
|
411
|
-
input {
|
412
|
-
elasticsearch {
|
413
|
-
hosts => ["localhost"]
|
414
|
-
query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
|
415
|
-
docinfo => true
|
416
|
-
}
|
417
|
-
}
|
418
|
-
]
|
419
|
-
end
|
447
|
+
ecs_compatibility_matrix(:disabled, :v1, :v8) do |ecs_select|
|
420
448
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
449
|
+
before(:each) do
|
450
|
+
allow_any_instance_of(described_class).to receive(:ecs_compatibility).and_return(ecs_compatibility)
|
451
|
+
end
|
452
|
+
|
453
|
+
context 'with docinfo enabled' do
|
454
|
+
let(:config_metadata) do
|
455
|
+
%q[
|
456
|
+
input {
|
457
|
+
elasticsearch {
|
458
|
+
hosts => ["localhost"]
|
459
|
+
query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
|
460
|
+
docinfo => true
|
461
|
+
}
|
429
462
|
}
|
430
|
-
|
431
|
-
]
|
432
|
-
|
433
|
-
event = input(config_metadata_with_hash) do |pipeline, queue|
|
434
|
-
queue.pop
|
463
|
+
]
|
435
464
|
end
|
436
465
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
"
|
446
|
-
|
447
|
-
"
|
448
|
-
"
|
449
|
-
|
450
|
-
|
451
|
-
expect(client).not_to receive(:clear_scroll)
|
452
|
-
plugin.register
|
453
|
-
expect { plugin.run([]) }.to raise_error(Exception, /incompatible event/)
|
466
|
+
it "provides document info under metadata" do
|
467
|
+
event = input(config_metadata) do |pipeline, queue|
|
468
|
+
queue.pop
|
469
|
+
end
|
470
|
+
|
471
|
+
if ecs_select.active_mode == :disabled
|
472
|
+
expect(event.get("[@metadata][_index]")).to eq('logstash-2014.10.12')
|
473
|
+
expect(event.get("[@metadata][_type]")).to eq('logs')
|
474
|
+
expect(event.get("[@metadata][_id]")).to eq('C5b2xLQwTZa76jBmHIbwHQ')
|
475
|
+
else
|
476
|
+
expect(event.get("[@metadata][input][elasticsearch][_index]")).to eq('logstash-2014.10.12')
|
477
|
+
expect(event.get("[@metadata][input][elasticsearch][_type]")).to eq('logs')
|
478
|
+
expect(event.get("[@metadata][input][elasticsearch][_id]")).to eq('C5b2xLQwTZa76jBmHIbwHQ')
|
479
|
+
end
|
454
480
|
end
|
455
|
-
end
|
456
481
|
|
457
|
-
|
458
|
-
|
459
|
-
|
482
|
+
it 'merges values if the `docinfo_target` already exist in the `_source` document' do
|
483
|
+
config_metadata_with_hash = %Q[
|
484
|
+
input {
|
485
|
+
elasticsearch {
|
486
|
+
hosts => ["localhost"]
|
487
|
+
query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
|
488
|
+
docinfo => true
|
489
|
+
docinfo_target => 'metadata_with_hash'
|
490
|
+
}
|
491
|
+
}
|
492
|
+
]
|
493
|
+
|
494
|
+
event = input(config_metadata_with_hash) do |pipeline, queue|
|
495
|
+
queue.pop
|
496
|
+
end
|
497
|
+
|
498
|
+
expect(event.get("[metadata_with_hash][_index]")).to eq('logstash-2014.10.12')
|
499
|
+
expect(event.get("[metadata_with_hash][_type]")).to eq('logs')
|
500
|
+
expect(event.get("[metadata_with_hash][_id]")).to eq('C5b2xLQwTZa76jBmHIbwHQ')
|
501
|
+
expect(event.get("[metadata_with_hash][awesome]")).to eq("logstash")
|
460
502
|
end
|
461
503
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
504
|
+
context 'if the `docinfo_target` exist but is not of type hash' do
|
505
|
+
let (:config) { {
|
506
|
+
"hosts" => ["localhost"],
|
507
|
+
"query" => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }',
|
508
|
+
"docinfo" => true,
|
509
|
+
"docinfo_target" => 'metadata_with_string'
|
510
|
+
} }
|
511
|
+
it 'thows an exception if the `docinfo_target` exist but is not of type hash' do
|
512
|
+
expect(client).not_to receive(:clear_scroll)
|
513
|
+
plugin.register
|
514
|
+
expect { plugin.run([]) }.to raise_error(Exception, /incompatible event/)
|
515
|
+
end
|
516
|
+
end
|
466
517
|
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
518
|
+
it 'should move the document information to the specified field' do
|
519
|
+
config = %q[
|
520
|
+
input {
|
521
|
+
elasticsearch {
|
522
|
+
hosts => ["localhost"]
|
523
|
+
query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
|
524
|
+
docinfo => true
|
525
|
+
docinfo_target => 'meta'
|
526
|
+
}
|
475
527
|
}
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
528
|
+
]
|
529
|
+
event = input(config) do |pipeline, queue|
|
530
|
+
queue.pop
|
531
|
+
end
|
532
|
+
|
533
|
+
expect(event.get("[meta][_index]")).to eq('logstash-2014.10.12')
|
534
|
+
expect(event.get("[meta][_type]")).to eq('logs')
|
535
|
+
expect(event.get("[meta][_id]")).to eq('C5b2xLQwTZa76jBmHIbwHQ')
|
480
536
|
end
|
481
537
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
538
|
+
it "allows to specify which fields from the document info to save to metadata" do
|
539
|
+
fields = ["_index"]
|
540
|
+
config = %Q[
|
541
|
+
input {
|
542
|
+
elasticsearch {
|
543
|
+
hosts => ["localhost"]
|
544
|
+
query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
|
545
|
+
docinfo => true
|
546
|
+
docinfo_fields => #{fields}
|
547
|
+
}
|
548
|
+
}]
|
549
|
+
|
550
|
+
event = input(config) do |pipeline, queue|
|
551
|
+
queue.pop
|
552
|
+
end
|
553
|
+
|
554
|
+
meta_base = event.get(ecs_select.active_mode == :disabled ? "@metadata" : "[@metadata][input][elasticsearch]")
|
555
|
+
expect(meta_base.keys).to eq(fields)
|
556
|
+
end
|
486
557
|
|
487
|
-
|
488
|
-
|
489
|
-
config = %Q[
|
558
|
+
it 'should be able to reference metadata fields in `add_field` decorations' do
|
559
|
+
config = %q[
|
490
560
|
input {
|
491
561
|
elasticsearch {
|
492
562
|
hosts => ["localhost"]
|
493
563
|
query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
|
494
564
|
docinfo => true
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
event = input(config) do |pipeline, queue|
|
500
|
-
queue.pop
|
501
|
-
end
|
502
|
-
|
503
|
-
expect(event.get("@metadata").keys).to eq(fields)
|
504
|
-
expect(event.get("[@metadata][_type]")).to eq(nil)
|
505
|
-
expect(event.get("[@metadata][_index]")).to eq('logstash-2014.10.12')
|
506
|
-
expect(event.get("[@metadata][_id]")).to eq(nil)
|
507
|
-
end
|
508
|
-
|
509
|
-
it 'should be able to reference metadata fields in `add_field` decorations' do
|
510
|
-
config = %q[
|
511
|
-
input {
|
512
|
-
elasticsearch {
|
513
|
-
hosts => ["localhost"]
|
514
|
-
query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
|
515
|
-
docinfo => true
|
516
|
-
add_field => {
|
517
|
-
'identifier' => "foo:%{[@metadata][_type]}:%{[@metadata][_id]}"
|
565
|
+
add_field => {
|
566
|
+
'identifier' => "foo:%{[@metadata][_type]}:%{[@metadata][_id]}"
|
567
|
+
}
|
518
568
|
}
|
519
569
|
}
|
520
|
-
|
521
|
-
]
|
570
|
+
]
|
522
571
|
|
523
|
-
|
524
|
-
|
525
|
-
|
572
|
+
event = input(config) do |pipeline, queue|
|
573
|
+
queue.pop
|
574
|
+
end
|
575
|
+
|
576
|
+
expect(event.get('identifier')).to eq('foo:logs:C5b2xLQwTZa76jBmHIbwHQ')
|
577
|
+
end if ecs_select.active_mode == :disabled
|
526
578
|
|
527
|
-
expect(event.get('identifier')).to eq('foo:logs:C5b2xLQwTZa76jBmHIbwHQ')
|
528
579
|
end
|
580
|
+
|
529
581
|
end
|
530
582
|
|
531
583
|
context "when not defining the docinfo" do
|
@@ -542,9 +594,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
542
594
|
queue.pop
|
543
595
|
end
|
544
596
|
|
545
|
-
expect(event.get("[@metadata]
|
546
|
-
expect(event.get("[@metadata][_type]")).to eq(nil)
|
547
|
-
expect(event.get("[@metadata][_id]")).to eq(nil)
|
597
|
+
expect(event.get("[@metadata]")).to be_empty
|
548
598
|
end
|
549
599
|
end
|
550
600
|
end
|
@@ -563,22 +613,23 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
563
613
|
'sample:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2OjkyNDMkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA6OTI0NA=='
|
564
614
|
end
|
565
615
|
|
566
|
-
let(:config) { super.merge({ 'cloud_id' => valid_cloud_id }) }
|
616
|
+
let(:config) { super().merge({ 'cloud_id' => valid_cloud_id }) }
|
567
617
|
|
568
618
|
it "should set host(s)" do
|
569
619
|
plugin.register
|
570
620
|
client = plugin.send(:client)
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
621
|
+
|
622
|
+
expect( client.transport.instance_variable_get(:@seeds) ).to eql [{
|
623
|
+
:scheme => "https",
|
624
|
+
:host => "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io",
|
625
|
+
:port => 9243,
|
626
|
+
:path => "",
|
627
|
+
:protocol => "https"
|
628
|
+
}]
|
578
629
|
end
|
579
630
|
|
580
631
|
context 'invalid' do
|
581
|
-
let(:config) { super.merge({ 'cloud_id' => 'invalid:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlv' }) }
|
632
|
+
let(:config) { super().merge({ 'cloud_id' => 'invalid:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlv' }) }
|
582
633
|
|
583
634
|
it "should fail" do
|
584
635
|
expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_id.*? is invalid/
|
@@ -586,7 +637,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
586
637
|
end
|
587
638
|
|
588
639
|
context 'hosts also set' do
|
589
|
-
let(:config) { super.merge({ 'cloud_id' => valid_cloud_id, 'hosts' => [ 'localhost:9200' ] }) }
|
640
|
+
let(:config) { super().merge({ 'cloud_id' => valid_cloud_id, 'hosts' => [ 'localhost:9200' ] }) }
|
590
641
|
|
591
642
|
it "should fail" do
|
592
643
|
expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_id and hosts/
|
@@ -595,18 +646,18 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
595
646
|
end if LOGSTASH_VERSION > '6.0'
|
596
647
|
|
597
648
|
describe "cloud.auth" do
|
598
|
-
let(:config) { super.merge({ 'cloud_auth' => LogStash::Util::Password.new('elastic:my-passwd-00') }) }
|
649
|
+
let(:config) { super().merge({ 'cloud_auth' => LogStash::Util::Password.new('elastic:my-passwd-00') }) }
|
599
650
|
|
600
651
|
it "should set authorization" do
|
601
652
|
plugin.register
|
602
653
|
client = plugin.send(:client)
|
603
|
-
auth_header = client.
|
654
|
+
auth_header = extract_transport(client).options[:transport_options][:headers]['Authorization']
|
604
655
|
|
605
656
|
expect( auth_header ).to eql "Basic #{Base64.encode64('elastic:my-passwd-00').rstrip}"
|
606
657
|
end
|
607
658
|
|
608
659
|
context 'invalid' do
|
609
|
-
let(:config) { super.merge({ 'cloud_auth' => 'invalid-format' }) }
|
660
|
+
let(:config) { super().merge({ 'cloud_auth' => 'invalid-format' }) }
|
610
661
|
|
611
662
|
it "should fail" do
|
612
663
|
expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_auth.*? format/
|
@@ -614,7 +665,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
614
665
|
end
|
615
666
|
|
616
667
|
context 'user also set' do
|
617
|
-
let(:config) { super.merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
|
668
|
+
let(:config) { super().merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
|
618
669
|
|
619
670
|
it "should fail" do
|
620
671
|
expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
|
@@ -624,7 +675,7 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
624
675
|
|
625
676
|
describe "api_key" do
|
626
677
|
context "without ssl" do
|
627
|
-
let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar') }) }
|
678
|
+
let(:config) { super().merge({ 'api_key' => LogStash::Util::Password.new('foo:bar') }) }
|
628
679
|
|
629
680
|
it "should fail" do
|
630
681
|
expect { plugin.register }.to raise_error LogStash::ConfigurationError, /api_key authentication requires SSL\/TLS/
|
@@ -632,18 +683,18 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
632
683
|
end
|
633
684
|
|
634
685
|
context "with ssl" do
|
635
|
-
let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar'), "ssl" => true }) }
|
686
|
+
let(:config) { super().merge({ 'api_key' => LogStash::Util::Password.new('foo:bar'), "ssl" => true }) }
|
636
687
|
|
637
688
|
it "should set authorization" do
|
638
689
|
plugin.register
|
639
690
|
client = plugin.send(:client)
|
640
|
-
auth_header = client.
|
691
|
+
auth_header = extract_transport(client).options[:transport_options][:headers]['Authorization']
|
641
692
|
|
642
693
|
expect( auth_header ).to eql "ApiKey #{Base64.strict_encode64('foo:bar')}"
|
643
694
|
end
|
644
695
|
|
645
696
|
context 'user also set' do
|
646
|
-
let(:config) { super.merge({ 'api_key' => 'foo:bar', 'user' => 'another' }) }
|
697
|
+
let(:config) { super().merge({ 'api_key' => 'foo:bar', 'user' => 'another' }) }
|
647
698
|
|
648
699
|
it "should fail" do
|
649
700
|
expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
|
@@ -653,29 +704,179 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
653
704
|
end if LOGSTASH_VERSION > '6.0'
|
654
705
|
|
655
706
|
describe "proxy" do
|
656
|
-
let(:config) { super.merge({ 'proxy' => 'http://localhost:1234' }) }
|
707
|
+
let(:config) { super().merge({ 'proxy' => 'http://localhost:1234' }) }
|
657
708
|
|
658
709
|
it "should set proxy" do
|
659
710
|
plugin.register
|
660
711
|
client = plugin.send(:client)
|
661
|
-
proxy = client.
|
712
|
+
proxy = extract_transport(client).options[:transport_options][:proxy]
|
662
713
|
|
663
714
|
expect( proxy ).to eql "http://localhost:1234"
|
664
715
|
end
|
665
716
|
|
666
717
|
context 'invalid' do
|
667
|
-
let(:config) { super.merge({ 'proxy' => '${A_MISSING_ENV_VAR:}' }) }
|
718
|
+
let(:config) { super().merge({ 'proxy' => '${A_MISSING_ENV_VAR:}' }) }
|
668
719
|
|
669
720
|
it "should not set proxy" do
|
670
721
|
plugin.register
|
671
722
|
client = plugin.send(:client)
|
672
723
|
|
673
|
-
expect( client.
|
724
|
+
expect( extract_transport(client).options[:transport_options] ).to_not include(:proxy)
|
674
725
|
end
|
675
726
|
end
|
676
727
|
end
|
677
728
|
|
678
|
-
|
729
|
+
class StoppableServer
|
730
|
+
|
731
|
+
attr_reader :port
|
732
|
+
|
733
|
+
def initialize()
|
734
|
+
queue = Queue.new
|
735
|
+
@first_req_waiter = java.util.concurrent.CountDownLatch.new(1)
|
736
|
+
@first_request = nil
|
737
|
+
|
738
|
+
@t = java.lang.Thread.new(
|
739
|
+
proc do
|
740
|
+
begin
|
741
|
+
@server = WEBrick::HTTPServer.new :Port => 0, :DocumentRoot => ".",
|
742
|
+
:Logger => Cabin::Channel.get, # silence WEBrick logging
|
743
|
+
:StartCallback => Proc.new {
|
744
|
+
queue.push("started")
|
745
|
+
}
|
746
|
+
@port = @server.config[:Port]
|
747
|
+
@server.mount_proc '/' do |req, res|
|
748
|
+
res.body = '''
|
749
|
+
{
|
750
|
+
"name": "ce7ccfb438e8",
|
751
|
+
"cluster_name": "docker-cluster",
|
752
|
+
"cluster_uuid": "DyR1hN03QvuCWXRy3jtb0g",
|
753
|
+
"version": {
|
754
|
+
"number": "7.13.1",
|
755
|
+
"build_flavor": "default",
|
756
|
+
"build_type": "docker",
|
757
|
+
"build_hash": "9a7758028e4ea59bcab41c12004603c5a7dd84a9",
|
758
|
+
"build_date": "2021-05-28T17:40:59.346932922Z",
|
759
|
+
"build_snapshot": false,
|
760
|
+
"lucene_version": "8.8.2",
|
761
|
+
"minimum_wire_compatibility_version": "6.8.0",
|
762
|
+
"minimum_index_compatibility_version": "6.0.0-beta1"
|
763
|
+
},
|
764
|
+
"tagline": "You Know, for Search"
|
765
|
+
}
|
766
|
+
'''
|
767
|
+
res.status = 200
|
768
|
+
res['Content-Type'] = 'application/json'
|
769
|
+
@first_request = req
|
770
|
+
@first_req_waiter.countDown()
|
771
|
+
end
|
772
|
+
|
773
|
+
@server.mount_proc '/logstash_unit_test/_search' do |req, res|
|
774
|
+
res.body = '''
|
775
|
+
{
|
776
|
+
"took" : 1,
|
777
|
+
"timed_out" : false,
|
778
|
+
"_shards" : {
|
779
|
+
"total" : 1,
|
780
|
+
"successful" : 1,
|
781
|
+
"skipped" : 0,
|
782
|
+
"failed" : 0
|
783
|
+
},
|
784
|
+
"hits" : {
|
785
|
+
"total" : {
|
786
|
+
"value" : 10000,
|
787
|
+
"relation" : "gte"
|
788
|
+
},
|
789
|
+
"max_score" : 1.0,
|
790
|
+
"hits" : [
|
791
|
+
{
|
792
|
+
"_index" : "test_bulk_index_2",
|
793
|
+
"_type" : "_doc",
|
794
|
+
"_id" : "sHe6A3wBesqF7ydicQvG",
|
795
|
+
"_score" : 1.0,
|
796
|
+
"_source" : {
|
797
|
+
"@timestamp" : "2021-09-20T15:02:02.557Z",
|
798
|
+
"message" : "{\"name\": \"Andrea\"}",
|
799
|
+
"@version" : "1",
|
800
|
+
"host" : "kalispera",
|
801
|
+
"sequence" : 5
|
802
|
+
}
|
803
|
+
}
|
804
|
+
]
|
805
|
+
}
|
806
|
+
}
|
807
|
+
'''
|
808
|
+
res.status = 200
|
809
|
+
res['Content-Type'] = 'application/json'
|
810
|
+
@first_request = req
|
811
|
+
@first_req_waiter.countDown()
|
812
|
+
end
|
813
|
+
|
814
|
+
|
815
|
+
|
816
|
+
@server.start
|
817
|
+
rescue => e
|
818
|
+
puts "Error in webserver thread #{e}"
|
819
|
+
# ignore
|
820
|
+
end
|
821
|
+
end
|
822
|
+
)
|
823
|
+
@t.daemon = true
|
824
|
+
@t.start
|
825
|
+
queue.pop # blocks until the server is up
|
826
|
+
end
|
827
|
+
|
828
|
+
def stop
|
829
|
+
@server.shutdown
|
830
|
+
end
|
831
|
+
|
832
|
+
def wait_receive_request
|
833
|
+
@first_req_waiter.await(2, java.util.concurrent.TimeUnit::SECONDS)
|
834
|
+
@first_request
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
describe "'user-agent' header" do
|
839
|
+
let!(:webserver) { StoppableServer.new } # webserver must be started before the call, so no lazy "let"
|
840
|
+
|
841
|
+
after :each do
|
842
|
+
webserver.stop
|
843
|
+
end
|
844
|
+
|
845
|
+
it "server should be started" do
|
846
|
+
require 'net/http'
|
847
|
+
response = nil
|
848
|
+
Net::HTTP.start('localhost', webserver.port) {|http|
|
849
|
+
response = http.request_get('/')
|
850
|
+
}
|
851
|
+
expect(response.code.to_i).to eq(200)
|
852
|
+
end
|
853
|
+
|
854
|
+
context "used by plugin" do
|
855
|
+
let(:config) do
|
856
|
+
{
|
857
|
+
"hosts" => ["localhost:#{webserver.port}"],
|
858
|
+
"query" => '{ "query": { "match": { "statuscode": 200 } }, "sort": [ "_doc" ] }',
|
859
|
+
"index" => "logstash_unit_test"
|
860
|
+
}
|
861
|
+
end
|
862
|
+
let(:plugin) { described_class.new(config) }
|
863
|
+
let(:event) { LogStash::Event.new({}) }
|
864
|
+
|
865
|
+
it "client should sent the expect user-agent" do
|
866
|
+
plugin.register
|
867
|
+
|
868
|
+
queue = []
|
869
|
+
plugin.run(queue)
|
870
|
+
|
871
|
+
request = webserver.wait_receive_request
|
872
|
+
|
873
|
+
expect(request.header['user-agent'].size).to eq(1)
|
874
|
+
expect(request.header['user-agent'][0]).to match(/logstash\/\d*\.\d*\.\d* \(OS=.*; JVM=.*\) logstash-input-elasticsearch\/\d*\.\d*\.\d*/)
|
875
|
+
end
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
shared_examples 'configurable timeout' do |config_name, manticore_transport_option|
|
679
880
|
let(:config_value) { fail NotImplementedError }
|
680
881
|
let(:config) { super().merge(config_name => config_value) }
|
681
882
|
{
|
@@ -706,6 +907,9 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
706
907
|
transport_options = new_elasticsearch_client_params[:transport_options]
|
707
908
|
expect(transport_options).to include(manticore_transport_option)
|
708
909
|
expect(transport_options[manticore_transport_option]).to eq(config_value.to_i)
|
910
|
+
mock_client = double("fake_client")
|
911
|
+
allow(mock_client).to receive(:ping)
|
912
|
+
mock_client
|
709
913
|
end
|
710
914
|
|
711
915
|
plugin.register
|
@@ -756,4 +960,10 @@ describe LogStash::Inputs::TestableElasticsearch do
|
|
756
960
|
end
|
757
961
|
|
758
962
|
end
|
963
|
+
|
964
|
+
# @note can be removed once we depends on elasticsearch gem >= 6.x
|
965
|
+
def extract_transport(client) # on 7.x client.transport is a ES::Transport::Client
|
966
|
+
client.transport.respond_to?(:transport) ? client.transport.transport : client.transport
|
967
|
+
end
|
968
|
+
|
759
969
|
end
|