logstash-input-elasticsearch 5.0.2 → 5.2.0

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.
@@ -1165,7 +1165,7 @@ describe LogStash::Inputs::Elasticsearch, :ecs_compatibility_support do
1165
1165
 
1166
1166
  context "when there's an exception" do
1167
1167
  before(:each) do
1168
- allow(client).to receive(:search).and_raise RuntimeError
1168
+ allow(client).to receive(:search).and_raise RuntimeError.new("test exception")
1169
1169
  end
1170
1170
  it 'produces no events' do
1171
1171
  plugin.run queue
@@ -1310,6 +1310,10 @@ describe LogStash::Inputs::Elasticsearch, :ecs_compatibility_support do
1310
1310
 
1311
1311
  let(:mock_queue) { double('queue', :<< => nil) }
1312
1312
 
1313
+ before(:each) do
1314
+ plugin.send(:setup_cursor_tracker)
1315
+ end
1316
+
1313
1317
  it 'pushes a generated event to the queue' do
1314
1318
  plugin.send(:push_hit, hit, mock_queue)
1315
1319
  expect(mock_queue).to have_received(:<<) do |event|
@@ -1366,4 +1370,129 @@ describe LogStash::Inputs::Elasticsearch, :ecs_compatibility_support do
1366
1370
  client.transport.respond_to?(:transport) ? client.transport.transport : client.transport
1367
1371
  end
1368
1372
 
1373
+ describe "#ESQL" do
1374
+ let(:config) do
1375
+ {
1376
+ "query" => "FROM test-index | STATS count() BY field",
1377
+ "query_type" => "esql",
1378
+ "retries" => 3
1379
+ }
1380
+ end
1381
+ let(:es_version) { LogStash::Inputs::Elasticsearch::ES_ESQL_SUPPORT_VERSION }
1382
+ let(:ls_version) { LogStash::Inputs::Elasticsearch::LS_ESQL_SUPPORT_VERSION }
1383
+
1384
+ before(:each) do
1385
+ stub_const("LOGSTASH_VERSION", ls_version)
1386
+ end
1387
+
1388
+ describe "#initialize" do
1389
+ it "sets up the ESQL client with correct parameters" do
1390
+ expect(plugin.instance_variable_get(:@query_type)).to eq(config["query_type"])
1391
+ expect(plugin.instance_variable_get(:@query)).to eq(config["query"])
1392
+ expect(plugin.instance_variable_get(:@retries)).to eq(config["retries"])
1393
+ end
1394
+ end
1395
+
1396
+ describe "#register" do
1397
+ before(:each) do
1398
+ Elasticsearch::Client.send(:define_method, :ping) { }
1399
+ allow_any_instance_of(Elasticsearch::Client).to receive(:info).and_return(cluster_info)
1400
+ end
1401
+ it "creates ES|QL executor" do
1402
+ plugin.register
1403
+ expect(plugin.instance_variable_get(:@query_executor)).to be_an_instance_of(LogStash::Inputs::Elasticsearch::Esql)
1404
+ end
1405
+ end
1406
+
1407
+ describe "#validation" do
1408
+
1409
+ describe "LS version" do
1410
+ context "when compatible" do
1411
+
1412
+ it "does not raise an error" do
1413
+ expect { plugin.send(:validate_ls_version_for_esql_support!) }.not_to raise_error
1414
+ end
1415
+ end
1416
+
1417
+ context "when incompatible" do
1418
+ before(:each) do
1419
+ stub_const("LOGSTASH_VERSION", "8.10.0")
1420
+ end
1421
+
1422
+ it "raises a runtime error" do
1423
+ expect { plugin.send(:validate_ls_version_for_esql_support!) }
1424
+ .to raise_error(RuntimeError, /Current version of Logstash does not include Elasticsearch client which supports ES|QL. Please upgrade Logstash to at least #{ls_version}/)
1425
+ end
1426
+ end
1427
+ end
1428
+
1429
+ describe "ES version" do
1430
+ before(:each) do
1431
+ allow(plugin).to receive(:es_version).and_return("8.10.5")
1432
+ end
1433
+
1434
+ context "when incompatible" do
1435
+ it "raises a runtime error" do
1436
+ expect { plugin.send(:validate_es_for_esql_support!) }
1437
+ .to raise_error(RuntimeError, /Connected Elasticsearch 8.10.5 version does not supports ES|QL. ES|QL feature requires at least Elasticsearch #{es_version} version./)
1438
+ end
1439
+ end
1440
+ end
1441
+
1442
+ context "ES|QL query and DSL params used together" do
1443
+ let(:config) {
1444
+ super().merge({
1445
+ "index" => "my-index",
1446
+ "size" => 1,
1447
+ "slices" => 1,
1448
+ "search_api" => "auto",
1449
+ "docinfo" => true,
1450
+ "docinfo_target" => "[@metadata][docinfo]",
1451
+ "docinfo_fields" => ["_index"],
1452
+ "response_type" => "hits",
1453
+ "tracking_field" => "[@metadata][tracking]"
1454
+ })}
1455
+
1456
+ it "raises a config error" do
1457
+ mixed_fields = %w[index size slices docinfo_fields response_type tracking_field]
1458
+ expect { plugin.register }.to raise_error(LogStash::ConfigurationError, /Configured #{mixed_fields} params are not allowed while using ES|QL query/)
1459
+ end
1460
+ end
1461
+
1462
+ describe "ES|QL query" do
1463
+ context "when query is valid" do
1464
+ it "does not raise an error" do
1465
+ expect { plugin.send(:validate_esql_query!) }.not_to raise_error
1466
+ end
1467
+ end
1468
+
1469
+ context "when query is empty" do
1470
+ let(:config) do
1471
+ {
1472
+ "query" => " "
1473
+ }
1474
+ end
1475
+
1476
+ it "raises a configuration error" do
1477
+ expect { plugin.send(:validate_esql_query!) }
1478
+ .to raise_error(LogStash::ConfigurationError, /`query` cannot be empty/)
1479
+ end
1480
+ end
1481
+
1482
+ context "when query doesn't align with ES syntax" do
1483
+ let(:config) do
1484
+ {
1485
+ "query" => "RANDOM query"
1486
+ }
1487
+ end
1488
+
1489
+ it "raises a configuration error" do
1490
+ source_commands = %w[FROM ROW SHOW]
1491
+ expect { plugin.send(:validate_esql_query!) }
1492
+ .to raise_error(LogStash::ConfigurationError, "`query` needs to start with any of #{source_commands}")
1493
+ end
1494
+ end
1495
+ end
1496
+ end
1497
+ end
1369
1498
  end
@@ -0,0 +1,150 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/inputs/elasticsearch"
4
+ require "elasticsearch"
5
+ require_relative "../../../spec/es_helper"
6
+
7
+ describe LogStash::Inputs::Elasticsearch, integration: true do
8
+
9
+ SECURE_INTEGRATION = ENV['SECURE_INTEGRATION'].eql? 'true'
10
+ ES_HOSTS = ["http#{SECURE_INTEGRATION ? 's' : nil}://#{ESHelper.get_host_port}"]
11
+
12
+ let(:plugin) { described_class.new(config) }
13
+ let(:es_index) { "logstash-esql-integration-#{rand(1000)}" }
14
+ let(:test_documents) do
15
+ [
16
+ { "message" => "test message 1", "type" => "a", "count" => 1 },
17
+ { "message" => "test message 2", "type" => "a", "count" => 2 },
18
+ { "message" => "test message 3", "type" => "b", "count" => 3 },
19
+ { "message" => "test message 4", "type" => "b", "count" => 4 },
20
+ { "message" => "test message 5", "type" => "c", "count" => 5 }
21
+ ]
22
+ end
23
+ let(:config) do
24
+ {
25
+ "hosts" => ES_HOSTS,
26
+ "query_type" => "esql"
27
+ }
28
+ end
29
+ let(:es_client) do
30
+ Elasticsearch::Client.new(hosts: ES_HOSTS)
31
+ end
32
+
33
+ before(:all) do
34
+ is_ls_with_esql_supported_client = Gem::Version.create(LOGSTASH_VERSION) >= Gem::Version.create(LogStash::Inputs::Elasticsearch::LS_ESQL_SUPPORT_VERSION)
35
+ skip "LS version does not have ES client which supports ES|QL" unless is_ls_with_esql_supported_client
36
+
37
+ # Skip tests if ES version doesn't support ES||QL
38
+ es_client = Elasticsearch::Client.new(hosts: ES_HOSTS) # need to separately create since let isn't allowed in before(:context)
39
+ es_version_info = es_client.info["version"]
40
+ es_gem_version = Gem::Version.create(es_version_info["number"])
41
+ skip "ES version does not support ES|QL" if es_gem_version.nil? || es_gem_version < Gem::Version.create(LogStash::Inputs::Elasticsearch::ES_ESQL_SUPPORT_VERSION)
42
+ end
43
+
44
+ before(:each) do
45
+ # Create index with test documents
46
+ es_client.indices.create(index: es_index, body: {}) unless es_client.indices.exists?(index: es_index)
47
+
48
+ test_documents.each do |doc|
49
+ es_client.index(index: es_index, body: doc, refresh: true)
50
+ end
51
+ end
52
+
53
+ after(:each) do
54
+ es_client.indices.delete(index: es_index) if es_client.indices.exists?(index: es_index)
55
+ end
56
+
57
+ context "#run ES|QL queries" do
58
+
59
+ before do
60
+ stub_const("LOGSTASH_VERSION", LogStash::Inputs::Elasticsearch::LS_ESQL_SUPPORT_VERSION)
61
+ allow_any_instance_of(LogStash::Inputs::Elasticsearch).to receive(:exit_plugin?).and_return false, true
62
+ end
63
+
64
+ before(:each) do
65
+ plugin.register
66
+ end
67
+
68
+ shared_examples "ESQL query execution" do |expected_count|
69
+ it "correctly retrieves documents" do
70
+ queue = Queue.new
71
+ plugin.run(queue)
72
+
73
+ event_count = 0
74
+ expected_count.times do |i|
75
+ event = queue.pop
76
+ expect(event).to be_a(LogStash::Event)
77
+ event_count += 1
78
+ end
79
+ expect(event_count).to eq(expected_count)
80
+ end
81
+ end
82
+
83
+ context "#FROM query" do
84
+ let(:config) do
85
+ super().merge("query" => "FROM #{es_index} | SORT count")
86
+ end
87
+
88
+ include_examples "ESQL query execution", 5
89
+ end
90
+
91
+ context "#FROM query and WHERE clause" do
92
+ let(:config) do
93
+ super().merge("query" => "FROM #{es_index} | WHERE type == \"a\" | SORT count")
94
+ end
95
+
96
+ include_examples "ESQL query execution", 2
97
+ end
98
+
99
+ context "#STATS aggregation" do
100
+ let(:config) do
101
+ super().merge("query" => "FROM #{es_index} | STATS avg(count) BY type")
102
+ end
103
+
104
+ it "retrieves aggregated stats" do
105
+ queue = Queue.new
106
+ plugin.run(queue)
107
+ results = []
108
+ 3.times do
109
+ event = queue.pop
110
+ expect(event).to be_a(LogStash::Event)
111
+ results << event.get("avg(count)")
112
+ end
113
+
114
+ expected_averages = [1.5, 3.5, 5.0]
115
+ expect(results.sort).to eq(expected_averages)
116
+ end
117
+ end
118
+
119
+ context "#METADATA" do
120
+ let(:config) do
121
+ super().merge("query" => "FROM #{es_index} METADATA _index, _id, _version | DROP message.keyword, type.keyword | SORT count")
122
+ end
123
+
124
+ it "includes document metadata" do
125
+ queue = Queue.new
126
+ plugin.run(queue)
127
+
128
+ 5.times do
129
+ event = queue.pop
130
+ expect(event).to be_a(LogStash::Event)
131
+ expect(event.get("_index")).not_to be_nil
132
+ expect(event.get("_id")).not_to be_nil
133
+ expect(event.get("_version")).not_to be_nil
134
+ end
135
+ end
136
+ end
137
+
138
+ context "#invalid ES|QL query" do
139
+ let(:config) do
140
+ super().merge("query" => "FROM undefined index | LIMIT 1")
141
+ end
142
+
143
+ it "doesn't produce events" do
144
+ queue = Queue.new
145
+ plugin.run(queue)
146
+ expect(queue.empty?).to eq(true)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -76,6 +76,14 @@ describe LogStash::Inputs::Elasticsearch do
76
76
  shared_examples 'secured_elasticsearch' do
77
77
  it_behaves_like 'an elasticsearch index plugin'
78
78
 
79
+ let(:unauth_exception_class) do
80
+ begin
81
+ Elasticsearch::Transport::Transport::Errors::Unauthorized
82
+ rescue
83
+ Elastic::Transport::Transport::Errors::Unauthorized
84
+ end
85
+ end
86
+
79
87
  context "incorrect auth credentials" do
80
88
 
81
89
  let(:config) do
@@ -85,7 +93,7 @@ describe LogStash::Inputs::Elasticsearch do
85
93
  let(:queue) { [] }
86
94
 
87
95
  it "fails to run the plugin" do
88
- expect { plugin.register }.to raise_error Elasticsearch::Transport::Transport::Errors::Unauthorized
96
+ expect { plugin.register }.to raise_error unauth_exception_class
89
97
  end
90
98
  end
91
99
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.2
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-17 00:00:00.000000000 Z
11
+ date: 2025-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -278,6 +278,8 @@ files:
278
278
  - lib/logstash/helpers/loggable_try.rb
279
279
  - lib/logstash/inputs/elasticsearch.rb
280
280
  - lib/logstash/inputs/elasticsearch/aggregation.rb
281
+ - lib/logstash/inputs/elasticsearch/cursor_tracker.rb
282
+ - lib/logstash/inputs/elasticsearch/esql.rb
281
283
  - lib/logstash/inputs/elasticsearch/paginated_search.rb
282
284
  - lib/logstash/inputs/elasticsearch/patches/_elasticsearch_transport_connections_selector.rb
283
285
  - lib/logstash/inputs/elasticsearch/patches/_elasticsearch_transport_http_manticore.rb
@@ -291,8 +293,11 @@ files:
291
293
  - spec/fixtures/test_certs/es.crt
292
294
  - spec/fixtures/test_certs/es.key
293
295
  - spec/fixtures/test_certs/renew.sh
296
+ - spec/inputs/cursor_tracker_spec.rb
297
+ - spec/inputs/elasticsearch_esql_spec.rb
294
298
  - spec/inputs/elasticsearch_spec.rb
295
299
  - spec/inputs/elasticsearch_ssl_spec.rb
300
+ - spec/inputs/integration/elasticsearch_esql_spec.rb
296
301
  - spec/inputs/integration/elasticsearch_spec.rb
297
302
  - spec/inputs/paginated_search_spec.rb
298
303
  homepage: https://elastic.co/logstash
@@ -330,7 +335,10 @@ test_files:
330
335
  - spec/fixtures/test_certs/es.crt
331
336
  - spec/fixtures/test_certs/es.key
332
337
  - spec/fixtures/test_certs/renew.sh
338
+ - spec/inputs/cursor_tracker_spec.rb
339
+ - spec/inputs/elasticsearch_esql_spec.rb
333
340
  - spec/inputs/elasticsearch_spec.rb
334
341
  - spec/inputs/elasticsearch_ssl_spec.rb
342
+ - spec/inputs/integration/elasticsearch_esql_spec.rb
335
343
  - spec/inputs/integration/elasticsearch_spec.rb
336
344
  - spec/inputs/paginated_search_spec.rb