logstash-output-elasticsearch 10.8.2-java → 11.0.1-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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/docs/index.asciidoc +134 -23
  4. data/lib/logstash/outputs/elasticsearch.rb +137 -63
  5. data/lib/logstash/outputs/elasticsearch/data_stream_support.rb +233 -0
  6. data/lib/logstash/outputs/elasticsearch/http_client.rb +59 -21
  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 +81 -60
  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 +22 -18
  17. data/spec/integration/outputs/ingest_pipeline_spec.rb +4 -2
  18. data/spec/integration/outputs/retry_spec.rb +14 -2
  19. data/spec/integration/outputs/sniffer_spec.rb +0 -1
  20. data/spec/spec_helper.rb +14 -0
  21. data/spec/unit/http_client_builder_spec.rb +9 -9
  22. data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +542 -0
  23. data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +1 -0
  24. data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +27 -13
  25. data/spec/unit/outputs/elasticsearch/http_client_spec.rb +59 -41
  26. data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +1 -3
  27. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +4 -5
  28. data/spec/unit/outputs/elasticsearch_spec.rb +280 -47
  29. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +1 -2
  30. data/spec/unit/outputs/error_whitelist_spec.rb +4 -3
  31. data/spec/unit/outputs/license_check_spec.rb +0 -16
  32. metadata +23 -16
@@ -6,7 +6,8 @@ if ESHelper.es_version_satisfies?(">= 5")
6
6
  require "logstash/outputs/elasticsearch"
7
7
  settings = {
8
8
  "hosts" => "#{get_host_port()}",
9
- "pipeline" => "apache-logs"
9
+ "pipeline" => "apache-logs",
10
+ "data_stream" => 'false'
10
11
  }
11
12
  next LogStash::Outputs::ElasticSearch.new(settings)
12
13
  end
@@ -52,9 +53,10 @@ if ESHelper.es_version_satisfies?(">= 5")
52
53
  @es.indices.refresh
53
54
 
54
55
  #Wait or fail until everything's indexed.
55
- Stud::try(20.times) do
56
+ Stud::try(10.times) do
56
57
  r = @es.search(index: 'logstash-*')
57
58
  expect(r).to have_hits(1)
59
+ sleep(0.1)
58
60
  end
59
61
  end
60
62
 
@@ -4,9 +4,21 @@ require_relative "../../../spec/es_spec_helper"
4
4
  describe "failures in bulk class expected behavior", :integration => true do
5
5
  let(:template) { '{"template" : "not important, will be updated by :index"}' }
6
6
  let(:event1) { LogStash::Event.new("somevalue" => 100, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0}) }
7
- let(:action1) { ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17", :_type=> doc_type }, event1]) }
7
+ let(:action1) do
8
+ if ESHelper.es_version_satisfies?(">= 6", "< 7")
9
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17", :_type=> doc_type }, event1.to_hash])
10
+ else
11
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17" }, event1.to_hash])
12
+ end
13
+ end
8
14
  let(:event2) { LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0] }, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0}) }
9
- let(:action2) { ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17", :_type=> doc_type }, event2]) }
15
+ let(:action2) do
16
+ if ESHelper.es_version_satisfies?(">= 6", "< 7")
17
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17", :_type=> doc_type }, event2.to_hash])
18
+ else
19
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17" }, event2.to_hash])
20
+ end
21
+ end
10
22
  let(:invalid_event) { LogStash::Event.new("geoip" => { "location" => "notlatlon" }, "@timestamp" => "2014-11-17T20:37:17.223Z") }
11
23
 
12
24
  def mock_actions_with_response(*resp)
@@ -1,4 +1,3 @@
1
- require "logstash/devutils/rspec/spec_helper"
2
1
  require_relative "../../../spec/es_spec_helper"
3
2
  require "logstash/outputs/elasticsearch/http_client"
4
3
  require "json"
@@ -0,0 +1,14 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+
3
+ unless defined?(LogStash::OSS)
4
+ LogStash::OSS = ENV['DISTRIBUTION'] != "default"
5
+ end
6
+
7
+ require "logstash/outputs/elasticsearch"
8
+
9
+ module LogStash::Outputs::ElasticSearch::SpecHelper
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ config.include LogStash::Outputs::ElasticSearch::SpecHelper
14
+ end
@@ -40,10 +40,10 @@ describe LogStash::Outputs::ElasticSearch::HttpClientBuilder do
40
40
 
41
41
  context "when setting bulk_path" do
42
42
  let(:bulk_path) { "/meh" }
43
- let(:options) { super.merge("bulk_path" => bulk_path) }
43
+ let(:options) { super().merge("bulk_path" => bulk_path) }
44
44
 
45
45
  context "when using path" do
46
- let(:options) { super.merge("path" => "/path") }
46
+ let(:options) { super().merge("path" => "/path") }
47
47
  it "ignores the path setting" do
48
48
  expect(described_class).to receive(:create_http_client) do |options|
49
49
  expect(options[:bulk_path]).to eq(bulk_path)
@@ -66,7 +66,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClientBuilder do
66
66
 
67
67
  context "when using path" do
68
68
  let(:path) { "/meh" }
69
- let(:options) { super.merge("path" => path) }
69
+ let(:options) { super().merge("path" => path) }
70
70
  it "sets bulk_path to path+_bulk" do
71
71
  expect(described_class).to receive(:create_http_client) do |options|
72
72
  expect(options[:bulk_path]).to eq("#{path}/_bulk")
@@ -88,10 +88,10 @@ describe LogStash::Outputs::ElasticSearch::HttpClientBuilder do
88
88
  describe "healthcheck_path" do
89
89
  context "when setting healthcheck_path" do
90
90
  let(:healthcheck_path) { "/meh" }
91
- let(:options) { super.merge("healthcheck_path" => healthcheck_path) }
91
+ let(:options) { super().merge("healthcheck_path" => healthcheck_path) }
92
92
 
93
93
  context "when using path" do
94
- let(:options) { super.merge("path" => "/path") }
94
+ let(:options) { super().merge("path" => "/path") }
95
95
  it "ignores the path setting" do
96
96
  expect(described_class).to receive(:create_http_client) do |options|
97
97
  expect(options[:healthcheck_path]).to eq(healthcheck_path)
@@ -114,7 +114,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClientBuilder do
114
114
 
115
115
  context "when using path" do
116
116
  let(:path) { "/meh" }
117
- let(:options) { super.merge("path" => path) }
117
+ let(:options) { super().merge("path" => path) }
118
118
  it "sets healthcheck_path to path" do
119
119
  expect(described_class).to receive(:create_http_client) do |options|
120
120
  expect(options[:healthcheck_path]).to eq(path)
@@ -136,10 +136,10 @@ describe LogStash::Outputs::ElasticSearch::HttpClientBuilder do
136
136
  describe "sniffing_path" do
137
137
  context "when setting sniffing_path" do
138
138
  let(:sniffing_path) { "/meh" }
139
- let(:options) { super.merge("sniffing_path" => sniffing_path) }
139
+ let(:options) { super().merge("sniffing_path" => sniffing_path) }
140
140
 
141
141
  context "when using path" do
142
- let(:options) { super.merge("path" => "/path") }
142
+ let(:options) { super().merge("path" => "/path") }
143
143
  it "ignores the path setting" do
144
144
  expect(described_class).to receive(:create_http_client) do |options|
145
145
  expect(options[:sniffing_path]).to eq(sniffing_path)
@@ -162,7 +162,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClientBuilder do
162
162
 
163
163
  context "when using path" do
164
164
  let(:path) { "/meh" }
165
- let(:options) { super.merge("path" => path) }
165
+ let(:options) { super().merge("path" => path) }
166
166
  it "sets sniffing_path to path+_nodes/http" do
167
167
  expect(described_class).to receive(:create_http_client) do |options|
168
168
  expect(options[:sniffing_path]).to eq("#{path}/_nodes/http")
@@ -0,0 +1,542 @@
1
+ require_relative '../../../../spec/spec_helper'
2
+ require "logstash/outputs/elasticsearch/data_stream_support"
3
+
4
+ describe LogStash::Outputs::ElasticSearch::DataStreamSupport do
5
+
6
+ subject { LogStash::Outputs::ElasticSearch.new(options) }
7
+ let(:options) { { 'hosts' => [ 'localhost:12345' ] } }
8
+ let(:es_version) { '7.10.1' }
9
+
10
+ let(:do_register) { false }
11
+ let(:stub_plugin_register!) do
12
+ allow(subject).to receive(:last_es_version).and_return(es_version)
13
+
14
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:start)
15
+
16
+ # stub-out unrelated (finish_register) setup:
17
+ allow(subject).to receive(:discover_cluster_uuid)
18
+ allow(subject).to receive(:install_template)
19
+ allow(subject).to receive(:ilm_in_use?).and_return nil
20
+
21
+ # emulate 'successful' ES connection on the same thread
22
+ allow(subject).to receive(:after_successful_connection) { |&block| block.call }
23
+ allow(subject).to receive(:stop_after_successful_connection_thread)
24
+
25
+ subject.register
26
+
27
+ allow(subject.client).to receive(:maximum_seen_major_version).and_return(Integer(es_version.split('.').first))
28
+
29
+ # allow( subject.logger ).to receive(:info) do |msg|
30
+ # expect(msg).to include "New Elasticsearch output"
31
+ # end
32
+ end
33
+
34
+ @@logstash_oss = LogStash::OSS
35
+
36
+ before(:each) do
37
+ change_constant :OSS, false, target: LogStash # assume non-OSS by default
38
+
39
+ stub_plugin_register! if do_register
40
+ end
41
+
42
+ after(:each) do
43
+ subject.close if do_register
44
+
45
+ change_constant :OSS, @@logstash_oss, target: LogStash
46
+ end
47
+
48
+ context "default configuration" do
49
+
50
+ let(:options) { {} }
51
+
52
+ before { allow(subject).to receive(:last_es_version).and_return(es_version) }
53
+
54
+ it "does not use data-streams on LS 7.x" do
55
+ change_constant :LOGSTASH_VERSION, '7.10.0' do
56
+ expect( subject.data_stream_config? ).to be false
57
+ end
58
+ end
59
+
60
+ it "warns when configuration is data-stream compliant (LS 7.x)" do
61
+ expect( subject.logger ).to receive(:warn) do |msg|
62
+ expect(msg).to include "Configuration is data stream compliant but due backwards compatibility Logstash 7.x"
63
+ end
64
+ change_constant :LOGSTASH_VERSION, '7.11.0' do
65
+ expect( subject.data_stream_config? ).to be false
66
+ end
67
+ end
68
+
69
+ it "defaults to using data-streams on LS 8.0" do
70
+ change_constant :LOGSTASH_VERSION, '8.0.0' do
71
+ expect( subject.data_stream_config? ).to be true
72
+ end
73
+ end
74
+
75
+ context 'non-compatible ES' do
76
+
77
+ let(:es_version) { '7.8.0' }
78
+
79
+ it "does not print an error (from after_successful_connection thread)" do
80
+ change_constant :LOGSTASH_VERSION, '7.8.1' do
81
+ expect( subject.logger ).to_not receive(:error)
82
+ expect( subject ).to receive(:finish_register).once.and_call_original
83
+ stub_plugin_register!
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ context "ds-compatible configuration" do
92
+
93
+ let(:options) do
94
+ {
95
+ 'hosts' => [ 'http://127.0.0.1:12345' ],
96
+ 'http_compression' => 'true', 'bulk_path' => '_bulk', 'timeout' => '30',
97
+ 'user' => 'elastic', 'password' => 'ForSearch!', 'ssl' => 'false'
98
+ }
99
+ end
100
+
101
+ before { allow(subject).to receive(:last_es_version).and_return(es_version) }
102
+
103
+ it "does not use data-streams on LS 7.x" do
104
+ change_constant :LOGSTASH_VERSION, '7.10.0' do
105
+ expect( subject.data_stream_config? ).to be false
106
+ end
107
+ end
108
+
109
+ it "defaults to using data-streams on LS 8.0" do
110
+ change_constant :LOGSTASH_VERSION, '8.0.1' do
111
+ expect( subject.data_stream_config? ).to be true
112
+ end
113
+ change_constant :LOGSTASH_VERSION, '8.1.0' do
114
+ expect( subject.send(:check_data_stream_config!) ).to be true
115
+ end
116
+ end
117
+
118
+ it "uses data-streams on LS 8.0 (OSS)" do
119
+ change_constant :LOGSTASH_VERSION, '8.0.1' do
120
+ change_constant :OSS, true, target: LogStash do
121
+ expect( subject.data_stream_config? ).to be true
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'old ES' do
127
+
128
+ let(:es_version) { '7.8.1' }
129
+
130
+ it "prints an error (from after_successful_connection thread) on LS 8.0" do
131
+ change_constant :LOGSTASH_VERSION, '8.0.0' do
132
+ expect( subject.logger ).to receive(:error).with(/Elasticsearch version does not support data streams/,
133
+ {:es_version=>"7.8.1"})
134
+ stub_plugin_register!
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+
142
+ context "default (non data-stream) configuration (on 7.x)" do
143
+
144
+ let(:options) do
145
+ { 'data_stream_dataset' => 'test', 'data_stream_auto_routing' => 'false', 'user' => 'elastic' }
146
+ end
147
+
148
+ before { allow(subject).to receive(:last_es_version).and_return(es_version) }
149
+
150
+ it "does not default to data-streams" do
151
+ expect( subject.logger ).to receive(:error) do |msg|
152
+ expect(msg).to include "Ambiguous configuration; data stream settings are present, but data streams are not enabled"
153
+ end
154
+ change_constant :LOGSTASH_VERSION, '7.10.2' do
155
+ expect { subject.data_stream_config? }.to raise_error(LogStash::ConfigurationError, /Ambiguous configuration/i)
156
+ end
157
+ end
158
+
159
+ context 'explicit data_stream => false' do
160
+
161
+ let(:options) { super().merge('data_stream' => 'false') }
162
+
163
+ it "raises a configuration error (due ds specific settings)" do
164
+ expect( subject.logger ).to receive(:error).with(/Ambiguous configuration; data stream settings must not be present when data streams is disabled/,
165
+ {"data_stream_auto_routing"=>"false", "data_stream_dataset"=>"test"})
166
+ change_constant :LOGSTASH_VERSION, '7.10.2' do
167
+ expect { subject.data_stream_config? }.to raise_error(LogStash::ConfigurationError, /Ambiguous configuration/i)
168
+ end
169
+ end
170
+
171
+ end
172
+
173
+ end
174
+
175
+ context "(explicit) ds disabled configuration" do
176
+
177
+ let(:options) { super().merge('data_stream' => false.to_s) }
178
+
179
+ before { allow(subject).to receive(:last_es_version).and_return(es_version) }
180
+
181
+ it "does not use data-streams on LS 7.x" do
182
+ change_constant :LOGSTASH_VERSION, '7.10.0' do
183
+ expect( subject.data_stream_config? ).to be false
184
+ end
185
+ end
186
+
187
+ it "does not use data-streams on LS 8.0" do
188
+ change_constant :LOGSTASH_VERSION, '8.0.0' do
189
+ expect( subject.data_stream_config? ).to be false
190
+ end
191
+ end
192
+
193
+ it "does not print a warning" do
194
+ expect( subject.logger ).to_not receive(:warn)
195
+ change_constant :LOGSTASH_VERSION, '7.10.2' do
196
+ expect( subject.data_stream_config? ).to be false
197
+ end
198
+ end
199
+
200
+ end
201
+
202
+ context "(explicit) ds enabled configuration" do
203
+
204
+ let(:options) { super().merge('data_stream' => true.to_s) }
205
+
206
+ before { allow(subject).to receive(:last_es_version).and_return(es_version) }
207
+
208
+ it "does use data-streams on LS 7.x" do
209
+ change_constant :LOGSTASH_VERSION, '7.9.1' do
210
+ expect( subject.data_stream_config? ).to be true
211
+ end
212
+ end
213
+
214
+ it "does use data-streams on LS 8.0" do
215
+ change_constant :LOGSTASH_VERSION, '8.1.0' do
216
+ expect( subject.data_stream_config? ).to be true
217
+ end
218
+ end
219
+
220
+ context 'non-compatible ES' do
221
+
222
+ let(:es_version) { '6.8.11' }
223
+
224
+ it "prints an error (from after_successful_connection thread) on LS 7.x" do
225
+ change_constant :LOGSTASH_VERSION, '7.12.0' do
226
+ expect( subject.logger ).to receive(:error).with(/Elasticsearch version does not support data streams/,
227
+ {:es_version=>"6.8.11"})
228
+ stub_plugin_register!
229
+ end
230
+ end
231
+
232
+ it "prints an error (from after_successful_connection thread) on LS 8.0" do
233
+ change_constant :LOGSTASH_VERSION, '8.0.5' do
234
+ expect( subject.logger ).to receive(:error).with(/Elasticsearch version does not support data streams/,
235
+ {:es_version=>"6.8.11"})
236
+ stub_plugin_register!
237
+ end
238
+ end
239
+
240
+ end
241
+
242
+ end
243
+
244
+ describe "auto routing" do
245
+
246
+ let(:options) { super().merge('data_stream' => 'true') }
247
+ let(:do_register) { true }
248
+
249
+ let(:event) do
250
+ event = LogStash::Event.new
251
+ event.set '[host][hostname]', 'orangutan'
252
+ event
253
+ end
254
+
255
+ context 'with data_stream.* event data' do
256
+
257
+ let(:event) do
258
+ super().tap do |event|
259
+ event.set '[data_stream][type]', 'metrics'
260
+ event.set '[data_stream][dataset]', 'src1'
261
+ event.set '[data_stream][namespace]', 'test'
262
+ end
263
+ end
264
+
265
+ it 'uses event specified target' do
266
+ tuple = subject.map_events([ event ]).first
267
+ expect( tuple.size ).to eql 3
268
+ expect( tuple[0] ).to eql 'create'
269
+ expect( tuple[1] ).to include :_index => 'metrics-src1-test'
270
+ end
271
+
272
+ end
273
+
274
+ context 'with routing turned off' do
275
+
276
+ let(:options) { super().merge('data_stream_auto_routing' => 'false') }
277
+
278
+ let(:event) do
279
+ super().tap do |event|
280
+ event.set '[data_stream][type]', 'metrics'
281
+ event.set '[data_stream][dataset]', 'src1'
282
+ event.set '[data_stream][namespace]', 'test'
283
+ end
284
+ end
285
+
286
+ it 'uses event specified target' do
287
+ tuple = subject.map_events([ event ]).first
288
+ expect( tuple.size ).to eql 3
289
+ expect( tuple[0] ).to eql 'create'
290
+ expect( tuple[1] ).to include :_index => 'logs-generic-default'
291
+ end
292
+
293
+ end
294
+
295
+ context 'with partial data_stream.* data' do
296
+
297
+ let(:options) { super().merge('data_stream_dataset' => 'data') }
298
+
299
+ let(:event) do
300
+ super().tap do |event|
301
+ event.set '[data_stream][type]', 'metrics'
302
+ event.set '[data_stream][dataset]', 'src1'
303
+ end
304
+ end
305
+
306
+ it 'uses event specified target' do
307
+ tuple = subject.map_events([ event ]).first
308
+ expect( tuple.size ).to eql 3
309
+ expect( tuple[0] ).to eql 'create'
310
+ expect( tuple[1] ).to include :_index => 'metrics-src1-default'
311
+ end
312
+
313
+ end
314
+
315
+ context 'with no data_stream.* fields' do
316
+
317
+ let(:options) { super().merge('data_stream_dataset' => 'stats', 'data_stream_type' => 'metrics') }
318
+
319
+ it 'uses configuration target' do
320
+ tuple = subject.map_events([ event ]).first
321
+ expect( tuple.size ).to eql 3
322
+ expect( tuple[0] ).to eql 'create'
323
+ expect( tuple[1] ).to include :_index => 'metrics-stats-default'
324
+ end
325
+
326
+ end
327
+
328
+ context 'with default configuration' do
329
+
330
+ it 'uses default target' do
331
+ tuple = subject.map_events([ event ]).first
332
+ expect( tuple.size ).to eql 3
333
+ expect( tuple[0] ).to eql 'create'
334
+ expect( tuple[1] ).to include :_index => 'logs-generic-default'
335
+ end
336
+
337
+ end
338
+
339
+ end
340
+
341
+ describe "field sync" do
342
+
343
+ let(:options) { super().merge('data_stream' => 'true') }
344
+
345
+ let(:do_register) { true }
346
+
347
+ let(:event) do
348
+ event = LogStash::Event.new
349
+ event.set '[host][hostname]', 'orangutan'
350
+ event
351
+ end
352
+
353
+ context 'enabled and no event data' do
354
+
355
+ let(:options) { super().merge('data_stream_sync_fields' => 'true') }
356
+
357
+ it 'fills in DS fields' do
358
+ tuple = subject.map_events([ event ]).first
359
+ expect( tuple.size ).to eql 3
360
+ expect( tuple[2]['data_stream'] ).to eql({"type" => "logs", "dataset" => "generic", "namespace" => "default"})
361
+ end
362
+
363
+ end
364
+
365
+ context 'enabled and some event data' do
366
+
367
+ let(:options) { super().merge('data_stream_dataset' => 'ds1', 'data_stream_sync_fields' => 'true') }
368
+
369
+ let(:event) do
370
+ super().tap do |event|
371
+ event.set '[data_stream][namespace]', 'custom'
372
+ end
373
+ end
374
+
375
+ it 'fills in missing fields' do
376
+ tuple = subject.map_events([ event ]).first
377
+ expect( tuple.size ).to eql 3
378
+ expect( tuple[2]['data_stream'] ).to eql({"type" => "logs", "dataset" => "ds1", "namespace" => "custom"})
379
+ end
380
+
381
+ it 'does not mutate data_stream hash' do
382
+ data_stream = event.get('data_stream')
383
+ data_stream_dup = data_stream.dup
384
+ subject.map_events([ event ])
385
+ expect( data_stream ).to eql data_stream_dup
386
+ end
387
+
388
+ end
389
+
390
+ context 'enabled with invalid data' do
391
+
392
+ let(:options) { super().merge('data_stream_sync_fields' => 'true') }
393
+
394
+ let(:event) do
395
+ super().tap do |event|
396
+ event.set '[data_stream]', false
397
+ end
398
+ end
399
+
400
+ it 'overwrites invalid data_stream field' do
401
+ tuple = subject.map_events([ event ]).first
402
+ expect( tuple.size ).to eql 3
403
+ expect( tuple[2]['data_stream'] ).to eql({"type" => "logs", "dataset" => "generic", "namespace" => "default"})
404
+ end
405
+
406
+ end
407
+
408
+ context 'enabled having invalid data with routing disabled' do
409
+
410
+ let(:options) do
411
+ super().merge('data_stream_sync_fields' => 'true', 'data_stream_auto_routing' => 'false', 'data_stream_namespace' => 'ns1')
412
+ end
413
+
414
+ let(:event) do
415
+ super().tap do |event|
416
+ event.set '[data_stream][type]', 'foo'
417
+ event.set '[data_stream][dataset]', false
418
+ event.set '[data_stream][extra]', 0
419
+ end
420
+ end
421
+
422
+ it 'overwrites invalid data_stream sub-fields' do
423
+ tuple = subject.map_events([ event ]).first
424
+ expect( tuple.size ).to eql 3
425
+ expect( tuple[2]['data_stream'] ).to eql({"type" => "logs", "dataset" => "generic", "namespace" => "ns1", "extra" => 0})
426
+ end
427
+
428
+ end
429
+
430
+ context 'disabled and no event data' do
431
+
432
+ let(:options) { super().merge('data_stream_dataset' => 'ds1', 'data_stream_sync_fields' => 'false') }
433
+
434
+ it 'does not fill DS fields' do
435
+ tuple = subject.map_events([ event ]).first
436
+ expect( tuple.size ).to eql 3
437
+ expect( tuple[2].keys ).to_not include 'data_stream'
438
+ end
439
+
440
+ end
441
+
442
+ context 'disabled and some event data' do
443
+
444
+ let(:options) { super().merge('data_stream_sync_fields' => 'false') }
445
+
446
+ let(:event) do
447
+ super().tap do |event|
448
+ event.set '[data_stream][type]', 'logs'
449
+ end
450
+ end
451
+
452
+ it 'does not fill DS fields' do
453
+ tuple = subject.map_events([ event ]).first
454
+ expect( tuple.size ).to eql 3
455
+ expect( tuple[2]['data_stream'] ).to eql({ 'type' => 'logs'})
456
+ end
457
+
458
+ end
459
+
460
+ end
461
+
462
+ describe "validation" do
463
+
464
+ context 'with too long dataset name' do
465
+
466
+ let(:options) { super().merge('data_stream' => 'true', 'data_stream_dataset' => 'x' * 120) }
467
+
468
+ it 'fails' do
469
+ expect { LogStash::Outputs::ElasticSearch.new(options) }.to raise_error LogStash::ConfigurationError
470
+ end
471
+
472
+ end
473
+
474
+ context 'with empty dataset name' do
475
+
476
+ let(:options) { super().merge('data_stream' => 'true', 'data_stream_dataset' => '') }
477
+
478
+ it 'fails' do
479
+ expect { LogStash::Outputs::ElasticSearch.new(options) }.to raise_error LogStash::ConfigurationError
480
+ end
481
+
482
+ end
483
+
484
+ context 'with invalid dataset char' do
485
+
486
+ let(:options) { super().merge('data_stream' => 'true', 'data_stream_dataset' => 'foo/bar') }
487
+
488
+ it 'fails' do
489
+ expect { LogStash::Outputs::ElasticSearch.new(options) }.to raise_error LogStash::ConfigurationError
490
+ end
491
+
492
+ end
493
+
494
+ context 'with invalid namespace char' do
495
+
496
+ let(:options) { super().merge('data_stream' => 'true', 'data_stream_namespace' => 'foo*') }
497
+
498
+ it 'fails' do
499
+ expect { LogStash::Outputs::ElasticSearch.new(options) }.to raise_error LogStash::ConfigurationError
500
+ end
501
+
502
+ end
503
+
504
+ context 'with invalid "empty" namespace' do
505
+
506
+ let(:options) { super().merge('data_stream' => 'true', 'data_stream_namespace' => ' ') }
507
+
508
+ it 'fails' do
509
+ expect { LogStash::Outputs::ElasticSearch.new(options) }.to raise_error LogStash::ConfigurationError
510
+ end
511
+
512
+ end
513
+
514
+ context 'with invalid type' do
515
+
516
+ let(:options) { super().merge('data_stream' => 'true', 'data_stream_type' => 'custom') }
517
+
518
+ it 'fails' do
519
+ expect { LogStash::Outputs::ElasticSearch.new(options) }.to raise_error LogStash::ConfigurationError
520
+ end
521
+
522
+ end
523
+
524
+ end
525
+
526
+ private
527
+
528
+ def change_constant(name, new_value, target: Object)
529
+ old_value = target.const_get name
530
+ begin
531
+ target.send :remove_const, name
532
+ target.const_set name, new_value
533
+ yield if block_given?
534
+ ensure
535
+ if block_given?
536
+ target.send :remove_const, name rescue nil
537
+ target.const_set name, old_value
538
+ end
539
+ end
540
+ end
541
+
542
+ end