fluent-plugin-input-opensearch 1.2.4 → 1.2.5

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/fluent/plugin/in_opensearch.rb +3 -3
  4. data/lib/fluent/plugin/out_opensearch.rb +5 -1
  5. metadata +5 -55
  6. data/.coveralls.yml +0 -1
  7. data/.editorconfig +0 -9
  8. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -29
  9. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
  10. data/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -9
  11. data/.github/workflows/coverage.yaml +0 -22
  12. data/.github/workflows/issue-auto-closer.yml +0 -12
  13. data/.github/workflows/linux.yml +0 -26
  14. data/.github/workflows/macos.yml +0 -26
  15. data/.github/workflows/windows.yml +0 -26
  16. data/.gitignore +0 -18
  17. data/CONTRIBUTING.md +0 -24
  18. data/Gemfile +0 -10
  19. data/History.md +0 -67
  20. data/README.OpenSearchGenID.md +0 -116
  21. data/README.OpenSearchInput.md +0 -396
  22. data/README.Troubleshooting.md +0 -482
  23. data/Rakefile +0 -37
  24. data/fluent-plugin-opensearch.gemspec +0 -39
  25. data/gemfiles/Gemfile.elasticsearch.v6 +0 -12
  26. data/test/helper.rb +0 -60
  27. data/test/plugin/datastream_template.json +0 -4
  28. data/test/plugin/test_alias_template.json +0 -9
  29. data/test/plugin/test_filter_opensearch_genid.rb +0 -241
  30. data/test/plugin/test_in_opensearch.rb +0 -500
  31. data/test/plugin/test_index_alias_template.json +0 -11
  32. data/test/plugin/test_index_template.json +0 -25
  33. data/test/plugin/test_oj_serializer.rb +0 -45
  34. data/test/plugin/test_opensearch_error_handler.rb +0 -770
  35. data/test/plugin/test_opensearch_fallback_selector.rb +0 -100
  36. data/test/plugin/test_opensearch_tls.rb +0 -171
  37. data/test/plugin/test_out_opensearch.rb +0 -3980
  38. data/test/plugin/test_out_opensearch_data_stream.rb +0 -746
  39. data/test/plugin/test_template.json +0 -23
  40. data/test/test_log-ext.rb +0 -61
@@ -1,3980 +0,0 @@
1
- # SPDX-License-Identifier: Apache-2.0
2
- #
3
- # The fluent-plugin-opensearch Contributors require contributions made to
4
- # this file be licensed under the Apache-2.0 license or a
5
- # compatible open source license.
6
- #
7
- # Modifications Copyright fluent-plugin-opensearch Contributors. See
8
- # GitHub history for details.
9
- #
10
- # Licensed to Uken Inc. under one or more contributor
11
- # license agreements. See the NOTICE file distributed with
12
- # this work for additional information regarding copyright
13
- # ownership. Uken Inc. licenses this file to you under
14
- # the Apache License, Version 2.0 (the "License"); you may
15
- # not use this file except in compliance with the License.
16
- # You may obtain a copy of the License at
17
- #
18
- # http://www.apache.org/licenses/LICENSE-2.0
19
- #
20
- # Unless required by applicable law or agreed to in writing,
21
- # software distributed under the License is distributed on an
22
- # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
- # KIND, either express or implied. See the License for the
24
- # specific language governing permissions and limitations
25
- # under the License.
26
-
27
- require_relative '../helper'
28
- require 'date'
29
- require 'fluent/test/helpers'
30
- require 'json'
31
- require 'fluent/test/driver/output'
32
- require 'flexmock/test_unit'
33
- require 'fluent/plugin/out_opensearch'
34
-
35
- class OpenSearchOutputTest < Test::Unit::TestCase
36
- include FlexMock::TestCase
37
- include Fluent::Test::Helpers
38
-
39
- attr_accessor :index_cmds, :index_command_counts, :index_cmds_all_requests
40
-
41
- def setup
42
- Fluent::Test.setup
43
- @driver = nil
44
- log = Fluent::Engine.log
45
- log.out.logs.slice!(0, log.out.logs.length)
46
- end
47
-
48
- def driver(conf='', os_version=1, client_version="\"1.2\"")
49
- # For request stub to detect compatibility.
50
- @os_version ||= os_version
51
- @client_version ||= client_version
52
- if @os_version
53
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
54
- def detect_os_major_version
55
- #{@os_version}
56
- end
57
- CODE
58
- end
59
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
60
- def client_library_version
61
- #{@client_version}
62
- end
63
- CODE
64
- @driver ||= Fluent::Test::Driver::Output.new(Fluent::Plugin::OpenSearchOutput) {
65
- # v0.12's test driver assume format definition. This simulates ObjectBufferedOutput format
66
- if !defined?(Fluent::Plugin::Output)
67
- def format(tag, time, record)
68
- [time, record].to_msgpack
69
- end
70
- end
71
- }.configure(conf)
72
- end
73
-
74
- def default_type_name
75
- Fluent::Plugin::OpenSearchOutput::DEFAULT_TYPE_NAME
76
- end
77
-
78
- def sample_record(content={})
79
- {'age' => 26, 'request_id' => '42', 'parent_id' => 'parent', 'routing_id' => 'routing'}.merge(content)
80
- end
81
-
82
- def nested_sample_record
83
- {'nested' =>
84
- {'age' => 26, 'parent_id' => 'parent', 'routing_id' => 'routing', 'request_id' => '42'}
85
- }
86
- end
87
-
88
- def stub_opensearch_info(url="http://localhost:9200/", version="1.2.2")
89
- body ="{\"version\":{\"number\":\"#{version}\", \"distribution\":\"opensearch\"},\"tagline\":\"The OpenSearch Project: https://opensearch.org/\"}"
90
- stub_request(:get, url).to_return({:status => 200, :body => body, :headers => { 'Content-Type' => 'json' } })
91
- end
92
-
93
- def stub_opensearch(url="http://localhost:9200/_bulk")
94
- stub_request(:post, url).with do |req|
95
- @index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
96
- end
97
- end
98
-
99
- def stub_opensearch_all_requests(url="http://localhost:9200/_bulk")
100
- @index_cmds_all_requests = Array.new
101
- stub_request(:post, url).with do |req|
102
- @index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
103
- @index_cmds_all_requests << @index_cmds
104
- end
105
- end
106
-
107
- def stub_opensearch_unavailable(url="http://localhost:9200/_bulk")
108
- stub_request(:post, url).to_return(:status => [503, "Service Unavailable"])
109
- end
110
-
111
- def stub_opensearch_timeout(url="http://localhost:9200/_bulk")
112
- stub_request(:post, url).to_timeout
113
- end
114
-
115
- def stub_opensearch_with_store_index_command_counts(url="http://localhost:9200/_bulk")
116
- if @index_command_counts == nil
117
- @index_command_counts = {}
118
- @index_command_counts.default = 0
119
- end
120
-
121
- stub_request(:post, url).with do |req|
122
- index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
123
- @index_command_counts[url] += index_cmds.size
124
- end
125
- end
126
-
127
- def make_response_body(req, error_el = nil, error_status = nil, error = nil)
128
- req_index_cmds = req.body.split("\n").map { |r| JSON.parse(r) }
129
- items = []
130
- count = 0
131
- ids = 1
132
- op = nil
133
- index = nil
134
- type = nil
135
- id = nil
136
- req_index_cmds.each do |cmd|
137
- if count.even?
138
- op = cmd.keys[0]
139
- index = cmd[op]['_index']
140
- type = cmd[op]['_type']
141
- if cmd[op].has_key?('_id')
142
- id = cmd[op]['_id']
143
- else
144
- # Note: this appears to be an undocumented feature of OpenSearch (and Elasticsearch)
145
- # https://www.elastic.co/guide/en/elasticsearch/reference/2.4/docs-bulk.html
146
- # When you submit an "index" write_operation, with no "_id" field in the
147
- # metadata header, OpenSearch will turn this into a "create"
148
- # operation in the response.
149
- if "index" == op
150
- op = "create"
151
- end
152
- id = ids
153
- ids += 1
154
- end
155
- else
156
- item = {
157
- op => {
158
- '_index' => index, '_type' => type, '_id' => id, '_version' => 1,
159
- '_shards' => { 'total' => 1, 'successful' => 1, 'failed' => 0 },
160
- 'status' => op == 'create' ? 201 : 200
161
- }
162
- }
163
- items.push(item)
164
- end
165
- count += 1
166
- end
167
- if !error_el.nil? && !error_status.nil? && !error.nil?
168
- op = items[error_el].keys[0]
169
- items[error_el][op].delete('_version')
170
- items[error_el][op].delete('_shards')
171
- items[error_el][op]['error'] = error
172
- items[error_el][op]['status'] = error_status
173
- errors = true
174
- else
175
- errors = false
176
- end
177
- @index_cmds = items
178
- body = { 'took' => 6, 'errors' => errors, 'items' => items }
179
- return body.to_json
180
- end
181
-
182
- def stub_opensearch_bad_argument(url="http://localhost:9200/_bulk")
183
- error = {
184
- "type" => "mapper_parsing_exception",
185
- "reason" => "failed to parse [...]",
186
- "caused_by" => {
187
- "type" => "illegal_argument_exception",
188
- "reason" => "Invalid format: \"...\""
189
- }
190
- }
191
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 400, error), :headers => { 'Content-Type' => 'json' } } })
192
- end
193
-
194
- def stub_opensearch_bulk_error(url="http://localhost:9200/_bulk")
195
- error = {
196
- "type" => "some-unrecognized-error",
197
- "reason" => "some message printed here ...",
198
- }
199
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
200
- end
201
-
202
- def stub_opensearch_bulk_rejected(url="http://localhost:9200/_bulk")
203
- error = {
204
- "status" => 500,
205
- "type" => "rejected_execution_exception",
206
- "reason" => "rejected execution of org.opensearch.transport.TransportService$4@1a34d37a on OpenSearchThreadPoolExecutor[bulk, queue capacity = 50, org.opensearch.common.util.concurrent.OpenSearchThreadPoolExecutor@312a2162[Running, pool size = 32, active threads = 32, queued tasks = 50, completed tasks = 327053]]"
207
- }
208
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 429, error), :headers => { 'Content-Type' => 'json' } } })
209
- end
210
-
211
- def stub_opensearch_out_of_memory(url="http://localhost:9200/_bulk")
212
- error = {
213
- "status" => 500,
214
- "type" => "out_of_memory_error",
215
- "reason" => "Java heap space"
216
- }
217
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
218
- end
219
-
220
- def stub_opensearch_unexpected_response_op(url="http://localhost:9200/_bulk")
221
- error = {
222
- "category" => "some-other-type",
223
- "reason" => "some-other-reason"
224
- }
225
- stub_request(:post, url).to_return(lambda { |req| bodystr = make_response_body(req, 0, 500, error); body = JSON.parse(bodystr); body['items'][0]['unknown'] = body['items'][0].delete('create'); { :status => 200, :body => body.to_json, :headers => { 'Content-Type' => 'json' } } })
226
- end
227
-
228
- def assert_logs_include(logs, msg, exp_matches=1)
229
- matches = logs.grep(/#{msg}/)
230
- assert_equal(exp_matches, matches.length, "Logs do not contain '#{msg}' '#{logs}'")
231
- end
232
-
233
- def assert_logs_include_compare_size(exp_matches=1, operator="<=", logs="", msg="")
234
- matches = logs.grep(/#{msg}/)
235
- assert_compare(exp_matches, operator, matches.length, "Logs do not contain '#{msg}' '#{logs}'")
236
- end
237
-
238
- def alias_endpoint
239
- "_aliases"
240
- end
241
-
242
- def test_configure
243
- config = %{
244
- host logs.google.com
245
- port 777
246
- scheme https
247
- path /os/
248
- user john
249
- password doe
250
- }
251
- instance = driver(config).instance
252
-
253
- assert_equal 'logs.google.com', instance.host
254
- assert_equal 777, instance.port
255
- assert_equal :https, instance.scheme
256
- assert_equal '/os/', instance.path
257
- assert_equal 'john', instance.user
258
- assert_equal 'doe', instance.password
259
- assert_equal Fluent::Plugin::OpenSearchTLS::DEFAULT_VERSION, instance.ssl_version
260
- assert_nil instance.ssl_max_version
261
- assert_nil instance.ssl_min_version
262
- if Fluent::Plugin::OpenSearchTLS::USE_TLS_MINMAX_VERSION
263
- if defined?(OpenSSL::SSL::TLS1_3_VERSION)
264
- assert_equal({max_version: OpenSSL::SSL::TLS1_3_VERSION, min_version: OpenSSL::SSL::TLS1_2_VERSION},
265
- instance.ssl_version_options)
266
- else
267
- assert_equal({max_version: nil, min_version: OpenSSL::SSL::TLS1_2_VERSION},
268
- instance.ssl_version_options)
269
- end
270
- else
271
- assert_equal({version: Fluent::Plugin::OpensearchTLS::DEFAULT_VERSION},
272
- instance.ssl_version_options)
273
- end
274
- assert_nil instance.client_key
275
- assert_nil instance.client_cert
276
- assert_nil instance.client_key_pass
277
- assert_false instance.with_transporter_log
278
- assert_equal "_doc", default_type_name
279
- assert_equal :excon, instance.http_backend
280
- assert_false instance.prefer_oj_serializer
281
- assert_equal ["out_of_memory_error", "rejected_execution_exception"], instance.unrecoverable_error_types
282
- assert_true instance.verify_os_version_at_startup
283
- assert_equal Fluent::Plugin::OpenSearchOutput::DEFAULT_OPENSEARCH_VERSION, instance.default_opensearch_version
284
- assert_false instance.log_os_400_reason
285
- assert_equal(-1, Fluent::Plugin::OpenSearchOutput::DEFAULT_TARGET_BULK_BYTES)
286
- assert_false instance.compression
287
- assert_equal :no_compression, instance.compression_level
288
- assert_true instance.http_backend_excon_nonblock
289
-
290
- assert_nil instance.endpoint
291
- end
292
-
293
- test 'configure endpoint section' do
294
- config = Fluent::Config::Element.new(
295
- 'ROOT', '', {
296
- '@type' => 'opensearch',
297
- }, [
298
- Fluent::Config::Element.new('endpoint', '', {
299
- 'url' => "https://search-opensearch.aws.example.com/",
300
- 'region' => "local",
301
- 'access_key_id' => 'YOUR_AWESOME_KEY',
302
- 'secret_access_key' => 'YOUR_AWESOME_SECRET',
303
- }, []),
304
- Fluent::Config::Element.new('buffer', 'tag', {}, [])
305
-
306
- ])
307
- instance = driver(config).instance
308
-
309
- assert_equal "https://search-opensearch.aws.example.com", instance.endpoint.url
310
- assert_equal "local", instance.endpoint.region
311
- assert_equal "YOUR_AWESOME_KEY", instance.endpoint.access_key_id
312
- assert_equal "YOUR_AWESOME_SECRET", instance.endpoint.secret_access_key
313
- assert_nil instance.endpoint.assume_role_arn
314
- assert_nil instance.endpoint.ecs_container_credentials_relative_uri
315
- assert_equal "fluentd", instance.endpoint.assume_role_session_name
316
- assert_nil instance.endpoint.assume_role_web_identity_token_file
317
- assert_nil instance.endpoint.sts_credentials_region
318
- assert_equal :es, instance.endpoint.aws_service_name
319
- end
320
-
321
- data("OpenSearch Service" => [:es, 'es'],
322
- "OpenSearch Serverless" => [:aoss, 'aoss'])
323
- test 'configure endpoint section w/ aws_service_name' do |data|
324
- expected, conf = data
325
- config = Fluent::Config::Element.new(
326
- 'ROOT', '', {
327
- '@type' => 'opensearch',
328
- }, [
329
- Fluent::Config::Element.new('endpoint', '', {
330
- 'url' => "https://search-opensearch.aws.example.com/",
331
- 'region' => "local",
332
- 'access_key_id' => 'YOUR_AWESOME_KEY',
333
- 'secret_access_key' => 'YOUR_AWESOME_SECRET',
334
- 'aws_service_name' => conf,
335
- }, []),
336
- Fluent::Config::Element.new('buffer', 'tag', {}, [])
337
-
338
- ])
339
- instance = driver(config).instance
340
-
341
- assert_equal "https://search-opensearch.aws.example.com", instance.endpoint.url
342
- assert_equal "local", instance.endpoint.region
343
- assert_equal "YOUR_AWESOME_KEY", instance.endpoint.access_key_id
344
- assert_equal "YOUR_AWESOME_SECRET", instance.endpoint.secret_access_key
345
- assert_nil instance.endpoint.assume_role_arn
346
- assert_nil instance.endpoint.ecs_container_credentials_relative_uri
347
- assert_equal "fluentd", instance.endpoint.assume_role_session_name
348
- assert_nil instance.endpoint.assume_role_web_identity_token_file
349
- assert_nil instance.endpoint.sts_credentials_region
350
- assert_equal expected, instance.endpoint.aws_service_name
351
- end
352
-
353
- test 'configure compression' do
354
- config = %{
355
- compression_level best_compression
356
- }
357
- instance = driver(config).instance
358
-
359
- assert_equal true, instance.compression
360
- end
361
-
362
- test 'check compression strategy' do
363
- config = %{
364
- compression_level best_speed
365
- }
366
- instance = driver(config).instance
367
-
368
- assert_equal Zlib::BEST_SPEED, instance.compression_strategy
369
- end
370
-
371
- test 'check content-encoding header with compression' do
372
- config = %{
373
- compression_level best_compression
374
- }
375
- instance = driver(config).instance
376
-
377
- assert_equal nil, instance.client.transport.transport.options[:transport_options][:headers]["Content-Encoding"]
378
-
379
- stub_request(:post, "http://localhost:9200/_bulk").
380
- to_return(status: 200, body: "", headers: {})
381
- stub_opensearch_info
382
- driver.run(default_tag: 'test') do
383
- driver.feed(sample_record)
384
- end
385
- compressable = instance.compressable_connection
386
-
387
- assert_equal "gzip", instance.client(nil, compressable).transport.transport.options[:transport_options][:headers]["Content-Encoding"]
388
- end
389
-
390
- test 'check compression option is passed to transport' do
391
- config = %{
392
- compression_level best_compression
393
- }
394
- instance = driver(config).instance
395
-
396
- assert_equal false, instance.client.transport.transport.options[:compression]
397
-
398
- stub_request(:post, "http://localhost:9200/_bulk").
399
- to_return(status: 200, body: "", headers: {})
400
- stub_opensearch_info
401
- driver.run(default_tag: 'test') do
402
- driver.feed(sample_record)
403
- end
404
- compressable = instance.compressable_connection
405
-
406
- assert_equal true, instance.client(nil, compressable).transport.transport.options[:compression]
407
- end
408
-
409
- test 'invalid specification of times of retrying template installation' do
410
- config = %{
411
- max_retry_putting_template -3
412
- }
413
- assert_raise(Fluent::ConfigError) {
414
- driver(config)
415
- }
416
- end
417
-
418
- test 'invalid specification of times of retrying get es version' do
419
- config = %{
420
- max_retry_get_os_version -3
421
- }
422
- assert_raise(Fluent::ConfigError) {
423
- driver(config)
424
- }
425
- end
426
-
427
- sub_test_case 'Check client.info response' do
428
- def create_driver(conf='', os_version=1, client_version="\"1.20\"")
429
- # For request stub to detect compatibility.
430
- @client_version ||= client_version
431
- @default_opensearch_version ||= os_version
432
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
433
- def detect_os_major_version
434
- @_os_info ||= client.info
435
- begin
436
- unless version = @_os_info.dig("version", "number")
437
- version = @default_opensearch_version
438
- end
439
- rescue NoMethodError => e
440
- log.warn "#{@_os_info} can not dig version information. Assuming OpenSearch #{@default_opensearch_version}", error: e
441
- version = @default_opensearch_version
442
- end
443
- version.to_i
444
- end
445
- CODE
446
-
447
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
448
- def client_library_version
449
- #{@client_version}
450
- end
451
- CODE
452
- @driver ||= Fluent::Test::Driver::Output.new(Fluent::Plugin::OpenSearchOutput) {
453
- # v0.12's test driver assume format definition. This simulates ObjectBufferedOutput format
454
- if !defined?(Fluent::Plugin::Output)
455
- def format(tag, time, record)
456
- [time, record].to_msgpack
457
- end
458
- end
459
- }.configure(conf)
460
- end
461
-
462
- def stub_opensearch_info_bad(url="http://localhost:9200/", version="6.4.2")
463
- body ="{\"version\":{\"number\":\"#{version}\",\"build_flavor\":\"default\"},\"tagline\":\"You Know, for Search\"}"
464
- stub_request(:get, url).to_return({:status => 200, :body => body, :headers => { 'Content-Type' => 'text/plain' } })
465
- end
466
-
467
- test 'handle invalid client.info' do
468
- stub_opensearch_info_bad("https://logs.fluentd.com:24225/es//", "7.7.1")
469
- config = %{
470
- host logs.fluentd.com
471
- port 24225
472
- scheme https
473
- path /es/
474
- user john
475
- password doe
476
- default_elasticsearch_version 7
477
- scheme https
478
- @log_level info
479
- }
480
- assert_raise(NoMethodError) do
481
- _d = create_driver(config, 1, "\"1.2.2\"")
482
- end
483
- end
484
- end
485
-
486
- sub_test_case 'Check TLS handshake stuck warning log' do
487
- test 'warning TLS log' do
488
- config = %{
489
- scheme https
490
- http_backend_excon_nonblock false
491
- ssl_version TLSv1_2
492
- @log_level info
493
- }
494
- driver(config)
495
- logs = driver.logs
496
- assert_logs_include(logs, /TLS handshake will be stucked with block connection.\n Consider to set `http_backend_excon_nonblock` as true\n/)
497
- end
498
- end
499
-
500
- test 'Detected insecure security' do
501
- config = %{
502
- ssl_version TLSv1_1
503
- @log_level warn
504
- scheme https
505
- }
506
- driver(config, 6)
507
- logs = driver.logs
508
- assert_logs_include(logs, /Detected OpenSearch 1.x or above and enabled insecure security/, 1)
509
- end
510
-
511
- test 'Detected Elasticsearch 7 and secure security' do
512
- config = %{
513
- ssl_version TLSv1_2
514
- @log_level warn
515
- scheme https
516
- }
517
- driver(config, 7)
518
- logs = driver.logs
519
- assert_logs_include(logs, /Detected ES 6.x or above and enabled insecure security/, 0)
520
- end
521
-
522
- test 'Pass OpenSearch and client library are same' do
523
- config = %{
524
- @log_level warn
525
- validate_client_version true
526
- }
527
- assert_nothing_raised do
528
- driver(config, 1, "\"1.2.2\"")
529
- end
530
- end
531
-
532
- test 'Detected Elasticsearch and client library mismatch' do
533
- config = %{
534
- @log_level warn
535
- validate_client_version true
536
- }
537
- assert_raise_message(/Detected OpenSearch 1 but you use OpenSearch client 2.0/) do
538
- driver(config, 1, "\"2.0.0\"")
539
- end
540
- end
541
-
542
- sub_test_case "placeholder substitution needed?" do
543
- data("host placeholder" => ["host", "host-${tag}.google.com"],
544
- "index_name_placeholder" => ["index_name", "logstash-${tag}"],
545
- "template_name_placeholder" => ["template_name", "logstash-${tag}"],
546
- "customize_template" => ["customize_template", '{"<<TAG>>":"${tag}"}'],
547
- "logstash_prefix_placeholder" => ["logstash_prefix", "fluentd-${tag}"],
548
- "application_name_placeholder" => ["application_name", "fluentd-${tag}"],
549
- )
550
- test 'tag placeholder' do |data|
551
- param, value = data
552
- config = Fluent::Config::Element.new(
553
- 'ROOT', '', {
554
- '@type' => 'opensearch',
555
- param => value
556
- }, [
557
- Fluent::Config::Element.new('buffer', 'tag', {}, [])
558
- ])
559
- driver(config)
560
-
561
- assert_true driver.instance.placeholder_substitution_needed_for_template?
562
- end
563
-
564
-
565
- data("host placeholder" => ["host", "host-%Y%m%d.google.com"],
566
- "index_name_placeholder" => ["index_name", "logstash-%Y%m%d"],
567
- "template_name_placeholder" => ["template_name", "logstash-%Y%m%d"],
568
- "customize_template" => ["customize_template", '{"<<TAG>>":"fluentd-%Y%m%d"}'],
569
- "logstash_prefix_placeholder" => ["logstash_prefix", "fluentd-%Y%m%d"],
570
- "application_name_placeholder" => ["application_name", "fluentd-%Y%m%d"],
571
- )
572
- test 'time placeholder' do |data|
573
- param, value = data
574
- config = Fluent::Config::Element.new(
575
- 'ROOT', '', {
576
- '@type' => 'opensearch',
577
- param => value
578
- }, [
579
- Fluent::Config::Element.new('buffer', 'time', {
580
- 'timekey' => '1d'
581
- }, [])
582
- ])
583
- driver(config)
584
-
585
- assert_true driver.instance.placeholder_substitution_needed_for_template?
586
- end
587
-
588
- data("host placeholder" => ["host", "host-${mykey}.google.com"],
589
- "index_name_placeholder" => ["index_name", "logstash-${mykey}"],
590
- "template_name_placeholder" => ["template_name", "logstash-${mykey}"],
591
- "customize_template" => ["customize_template", '{"<<TAG>>":"${mykey}"}'],
592
- "logstash_prefix_placeholder" => ["logstash_prefix", "fluentd-${mykey}"],
593
- "logstash_dateformat_placeholder" => ["logstash_dateformat", "${mykey}"],
594
- "application_name_placeholder" => ["application_name", "fluentd-${mykey}"],
595
- )
596
- test 'custom placeholder' do |data|
597
- param, value = data
598
- config = Fluent::Config::Element.new(
599
- 'ROOT', '', {
600
- '@type' => 'elasticsearch',
601
- param => value
602
- }, [
603
- Fluent::Config::Element.new('buffer', 'mykey', {
604
- 'chunk_keys' => 'mykey',
605
- 'timekey' => '1d',
606
- }, [])
607
- ])
608
- driver(config)
609
-
610
- assert_true driver.instance.placeholder_substitution_needed_for_template?
611
- end
612
-
613
- data("host placeholder" => ["host", "host-${tag}.google.com"],
614
- "index_name_placeholder" => ["index_name", "logstash-${es_index}-%Y%m%d"],
615
- "template_name_placeholder" => ["template_name", "logstash-${tag}-%Y%m%d"],
616
- "customize_template" => ["customize_template", '{"<<TAG>>":"${os_index}"}'],
617
- "logstash_prefix_placeholder" => ["logstash_prefix", "fluentd-${os_index}-%Y%m%d"],
618
- "logstash_dateformat_placeholder" => ["logstash_dateformat", "${os_index}"],
619
- "application_name_placeholder" => ["application_name", "fluentd-${tag}-${os_index}-%Y%m%d"],
620
- )
621
- test 'mixed placeholder' do |data|
622
- param, value = data
623
- config = Fluent::Config::Element.new(
624
- 'ROOT', '', {
625
- '@type' => 'opensearch',
626
- param => value
627
- }, [
628
- Fluent::Config::Element.new('buffer', 'tag,time,os_index', {
629
- 'chunk_keys' => 'os_index',
630
- 'timekey' => '1d',
631
- }, [])
632
- ])
633
- driver(config)
634
-
635
- assert_true driver.instance.placeholder_substitution_needed_for_template?
636
- end
637
- end
638
-
639
- sub_test_case 'chunk_keys requirement' do
640
- test 'tag in chunk_keys' do
641
- assert_nothing_raised do
642
- driver(Fluent::Config::Element.new(
643
- 'ROOT', '', {
644
- '@type' => 'opensearch',
645
- 'host' => 'log.google.com',
646
- 'port' => 777,
647
- 'scheme' => 'https',
648
- 'path' => '/os/',
649
- 'user' => 'john',
650
- 'password' => 'doe',
651
- }, [
652
- Fluent::Config::Element.new('buffer', 'tag', {
653
- 'chunk_keys' => 'tag'
654
- }, [])
655
- ]
656
- ))
657
- end
658
- end
659
-
660
- test '_index in chunk_keys' do
661
- assert_nothing_raised do
662
- driver(Fluent::Config::Element.new(
663
- 'ROOT', '', {
664
- '@type' => 'opensearch',
665
- 'host' => 'log.google.com',
666
- 'port' => 777,
667
- 'scheme' => 'https',
668
- 'path' => '/os/',
669
- 'user' => 'john',
670
- 'password' => 'doe',
671
- }, [
672
- Fluent::Config::Element.new('buffer', '_index', {
673
- 'chunk_keys' => '_index'
674
- }, [])
675
- ]
676
- ))
677
- end
678
- end
679
-
680
- test 'lack of tag and _index in chunk_keys' do
681
- assert_raise_message(/'tag' or '_index' in chunk_keys is required./) do
682
- driver(Fluent::Config::Element.new(
683
- 'ROOT', '', {
684
- '@type' => 'opensearch',
685
- 'host' => 'log.google.com',
686
- 'port' => 777,
687
- 'scheme' => 'https',
688
- 'path' => '/os/',
689
- 'user' => 'john',
690
- 'password' => 'doe',
691
- }, [
692
- Fluent::Config::Element.new('buffer', 'mykey', {
693
- 'chunk_keys' => 'mykey'
694
- }, [])
695
- ]
696
- ))
697
- end
698
- end
699
- end
700
-
701
- test 'Detected exclusive features which are host placeholder, template installation, and verify OpenSearch version at startup' do
702
- cwd = File.dirname(__FILE__)
703
- template_file = File.join(cwd, 'test_template.json')
704
-
705
- assert_raise_message(/host placeholder, template installation, and verify OpenSearch version at startup are exclusive feature at same time./) do
706
- config = %{
707
- host logs-${tag}.google.com
708
- port 777
709
- scheme https
710
- path /os/
711
- user john
712
- password doe
713
- template_name logstash
714
- template_file #{template_file}
715
- verify_os_version_at_startup true
716
- default_opensearch_version 1
717
- }
718
- driver(config)
719
- end
720
- end
721
-
722
- class GetOpenSearchVersionTest < self
723
- def create_driver(conf='', client_version="\"1.0\"")
724
- # For request stub to detect compatibility.
725
- @client_version ||= client_version
726
- # Ensure original implementation existence.
727
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
728
- def detect_os_major_version
729
- @_os_info ||= client.info
730
- unless version = @_os_info.dig("version", "number")
731
- version = @default_opensearch_version
732
- end
733
- version.to_i
734
- end
735
- CODE
736
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
737
- def client_library_version
738
- #{@client_version}
739
- end
740
- CODE
741
- Fluent::Test::Driver::Output.new(Fluent::Plugin::OpenSearchOutput).configure(conf)
742
- end
743
-
744
- def test_retry_get_os_version
745
- config = %{
746
- host logs.google.com
747
- port 778
748
- scheme https
749
- path /os/
750
- user john
751
- password doe
752
- verify_os_version_at_startup true
753
- max_retry_get_os_version 3
754
- }
755
-
756
- connection_resets = 0
757
- stub_request(:get, "https://logs.google.com:778/os//").
758
- with(basic_auth: ['john', 'doe']) do |req|
759
- connection_resets += 1
760
- raise Faraday::ConnectionFailed, "Test message"
761
- end
762
-
763
- assert_raise(Fluent::Plugin::OpenSearchError::RetryableOperationExhaustedFailure) do
764
- create_driver(config)
765
- end
766
-
767
- assert_equal(4, connection_resets)
768
- end
769
- end
770
-
771
- class GetOpenSearchVersionWithFallbackTest < self
772
- def create_driver(conf='', client_version="\"1.2\"")
773
- # For request stub to detect compatibility.
774
- @client_version ||= client_version
775
- # Ensure original implementation existence.
776
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
777
- def detect_os_major_version
778
- @_os_info ||= client.info
779
- unless version = @_os_info.dig("version", "number")
780
- version = @default_opensearch_version
781
- end
782
- version.to_i
783
- end
784
- CODE
785
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
786
- def client_library_version
787
- #{@client_version}
788
- end
789
- CODE
790
- Fluent::Test::Driver::Output.new(Fluent::Plugin::OpenSearchOutput).configure(conf)
791
- end
792
-
793
- data("OpenSearch 1" => ["1.2", 1])
794
- def test_retry_get_os_version_without_fail_on_detecting_os_version_retry_exceeded(data)
795
- client_version, os_major_version = data
796
- config = %{
797
- host logs.google.com
798
- port 778
799
- scheme https
800
- path /os/
801
- user john
802
- password doe
803
- verify_os_version_at_startup true
804
- max_retry_get_os_version 2
805
- fail_on_detecting_os_version_retry_exceed false
806
- default_opensearch_version #{os_major_version}
807
- @log_level info
808
- }
809
-
810
- connection_resets = 0
811
- stub_request(:get, "https://logs.google.com:778/os//").
812
- with(basic_auth: ['john', 'doe']) do |req|
813
- connection_resets += 1
814
- raise Faraday::ConnectionFailed, "Test message"
815
- end
816
-
817
- d = create_driver(config, client_version)
818
-
819
- assert_equal os_major_version, d.instance.default_opensearch_version
820
- assert_equal 3, connection_resets
821
- assert_equal os_major_version, d.instance.instance_variable_get(:@last_seen_major_version)
822
- end
823
- end
824
-
825
- data("legacy_template" => [true, "_template"],
826
- "new_template" => [false, "_index_template"])
827
- def test_template_already_present(data)
828
- use_legacy_template_flag, endpoint = data
829
- config = %{
830
- host logs.google.com
831
- port 777
832
- scheme https
833
- path /os/
834
- user john
835
- password doe
836
- template_name logstash
837
- template_file /abc123
838
- use_legacy_template #{use_legacy_template_flag}
839
- }
840
-
841
- # connection start
842
- stub_request(:head, "https://logs.google.com:777/os//").
843
- with(basic_auth: ['john', 'doe']).
844
- to_return(:status => 200, :body => "", :headers => {})
845
- # check if template exists
846
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash").
847
- with(basic_auth: ['john', 'doe']).
848
- to_return(:status => 200, :body => "", :headers => {})
849
- stub_opensearch_info("https://logs.google.com:777/os//")
850
-
851
- driver(config)
852
-
853
- assert_not_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash")
854
- end
855
-
856
- data("legacy_template" => [true, "_template"],
857
- "new_template" => [false, "_index_template"])
858
- def test_template_create(data)
859
- use_legacy_template_flag, endpoint = data
860
- cwd = File.dirname(__FILE__)
861
- template_file = if use_legacy_template_flag
862
- File.join(cwd, 'test_template.json')
863
- else
864
- File.join(cwd, 'test_index_template.json')
865
- end
866
-
867
- config = %{
868
- host logs.google.com
869
- port 777
870
- scheme https
871
- path /os/
872
- user john
873
- password doe
874
- template_name logstash
875
- template_file #{template_file}
876
- use_legacy_template #{use_legacy_template_flag}
877
- }
878
-
879
- # connection start
880
- stub_request(:head, "https://logs.google.com:777/os//").
881
- with(basic_auth: ['john', 'doe']).
882
- to_return(:status => 200, :body => "", :headers => {})
883
- # check if template exists
884
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash").
885
- with(basic_auth: ['john', 'doe']).
886
- to_return(:status => 404, :body => "", :headers => {})
887
- # creation
888
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash").
889
- with(basic_auth: ['john', 'doe']).
890
- to_return(:status => 200, :body => "", :headers => {})
891
- stub_opensearch_info("https://logs.google.com:777/os//")
892
-
893
- driver(config)
894
-
895
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash", times: 1)
896
- end
897
-
898
- data("legacy_template" => [true, "_template"],
899
- "new_template" => [false, "_index_template"])
900
- def test_custom_template_create(data)
901
- use_legacy_template_flag, endpoint = data
902
- cwd = File.dirname(__FILE__)
903
- template_file = if use_legacy_template_flag
904
- File.join(cwd, 'test_alias_template.json')
905
- else
906
- File.join(cwd, 'test_index_alias_template.json')
907
- end
908
-
909
- config = %{
910
- host logs.google.com
911
- port 777
912
- scheme https
913
- path /os/
914
- user john
915
- password doe
916
- template_name myapp_alias_template
917
- template_file #{template_file}
918
- customize_template {"--appid--": "myapp-logs","--index_prefix--":"mylogs"}
919
- use_legacy_template #{use_legacy_template_flag}
920
- }
921
-
922
- # connection start
923
- stub_request(:head, "https://logs.google.com:777/os//").
924
- with(basic_auth: ['john', 'doe']).
925
- to_return(:status => 200, :body => "", :headers => {})
926
- # check if template exists
927
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template").
928
- with(basic_auth: ['john', 'doe']).
929
- to_return(:status => 404, :body => "", :headers => {})
930
- # creation
931
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template").
932
- with(basic_auth: ['john', 'doe']).
933
- to_return(:status => 200, :body => "", :headers => {})
934
- stub_opensearch_info("https://logs.google.com:777/os//")
935
-
936
- driver(config)
937
-
938
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template", times: 1)
939
- end
940
-
941
- data("legacy_template" => [true, "_template"],
942
- "new_template" => [false, "_index_template"])
943
- def test_custom_template_create_with_customize_template_related_placeholders(data)
944
- use_legacy_template_flag, endpoint = data
945
- cwd = File.dirname(__FILE__)
946
- template_file = if use_legacy_template_flag
947
- File.join(cwd, 'test_alias_template.json')
948
- else
949
- File.join(cwd, 'test_index_alias_template.json')
950
- end
951
-
952
- config = %{
953
- host logs.google.com
954
- port 777
955
- scheme https
956
- path /os/
957
- user john
958
- password doe
959
- template_name myapp_alias_template-${tag}
960
- template_file #{template_file}
961
- customize_template {"--appid--": "${tag}-logs","--index_prefix--":"${tag}"}
962
- use_legacy_template #{use_legacy_template_flag}
963
- }
964
-
965
- # connection start
966
- stub_request(:head, "https://logs.google.com:777/os//").
967
- with(basic_auth: ['john', 'doe']).
968
- to_return(:status => 200, :body => "", :headers => {})
969
- # check if template exists
970
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template-test.template").
971
- with(basic_auth: ['john', 'doe']).
972
- to_return(:status => 404, :body => "", :headers => {})
973
- # creation
974
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template-test.template").
975
- with(basic_auth: ['john', 'doe']).
976
- to_return(:status => 200, :body => "", :headers => {})
977
-
978
- stub_request(:put, "https://logs.google.com:777/os//%3Cfluentd-test-default-000001%3E").
979
- to_return(status: 200, body: "", headers: {})
980
-
981
- driver(config)
982
-
983
- stub_opensearch("https://logs.google.com:777/os//_bulk")
984
- stub_opensearch_info("https://logs.google.com:777/os//")
985
- driver.run(default_tag: 'test.template') do
986
- driver.feed(sample_record)
987
- end
988
-
989
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template-test.template", times: 1)
990
- end
991
-
992
- data("legacy_template" => [true, "_template"],
993
- "new_template" => [false, "_index_template"])
994
- def test_custom_template_installation_for_host_placeholder(data)
995
- use_legacy_template_flag, endpoint = data
996
- cwd = File.dirname(__FILE__)
997
- template_file = if use_legacy_template_flag
998
- File.join(cwd, 'test_template.json')
999
- else
1000
- File.join(cwd, 'test_index_template.json')
1001
- end
1002
-
1003
- config = %{
1004
- host logs-${tag}.google.com
1005
- port 777
1006
- scheme https
1007
- path /os/
1008
- user john
1009
- password doe
1010
- template_name logstash
1011
- template_file #{template_file}
1012
- verify_os_version_at_startup false
1013
- default_elasticsearch_version 6
1014
- customize_template {"--appid--": "myapp-logs","--index_prefix--":"mylogs"}
1015
- use_legacy_template #{use_legacy_template_flag}
1016
- }
1017
-
1018
- # connection start
1019
- stub_request(:head, "https://logs-test.google.com:777/os//").
1020
- with(basic_auth: ['john', 'doe']).
1021
- to_return(:status => 200, :body => "", :headers => {})
1022
- # check if template exists
1023
- stub_request(:get, "https://logs-test.google.com:777/os//#{endpoint}/logstash").
1024
- with(basic_auth: ['john', 'doe']).
1025
- to_return(:status => 404, :body => "", :headers => {})
1026
- stub_request(:put, "https://logs-test.google.com:777/os//#{endpoint}/logstash").
1027
- with(basic_auth: ['john', 'doe']).
1028
- to_return(status: 200, body: "", headers: {})
1029
-
1030
- driver(config)
1031
-
1032
- stub_opensearch("https://logs-test.google.com:777/os//_bulk")
1033
- stub_opensearch_info("https://logs-test.google.com:777/os//")
1034
- driver.run(default_tag: 'test') do
1035
- driver.feed(sample_record)
1036
- end
1037
- end
1038
-
1039
- data("legacy_template" => [true, "_template"],
1040
- "new_template" => [false, "_index_template"])
1041
- def test_template_overwrite(data)
1042
- use_legacy_template_flag, endpoint = data
1043
- cwd = File.dirname(__FILE__)
1044
- template_file = if use_legacy_template_flag
1045
- File.join(cwd, 'test_template.json')
1046
- else
1047
- File.join(cwd, 'test_index_template.json')
1048
- end
1049
-
1050
- config = %{
1051
- host logs.google.com
1052
- port 777
1053
- scheme https
1054
- path /os/
1055
- user john
1056
- password doe
1057
- template_name logstash
1058
- template_file #{template_file}
1059
- template_overwrite true
1060
- use_legacy_template #{use_legacy_template_flag}
1061
- }
1062
-
1063
- # connection start
1064
- stub_request(:head, "https://logs.google.com:777/os//").
1065
- with(basic_auth: ['john', 'doe']).
1066
- to_return(:status => 200, :body => "", :headers => {})
1067
- # check if template exists
1068
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash").
1069
- with(basic_auth: ['john', 'doe']).
1070
- to_return(:status => 200, :body => "", :headers => {})
1071
- # creation
1072
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash").
1073
- with(basic_auth: ['john', 'doe']).
1074
- to_return(:status => 200, :body => "", :headers => {})
1075
- stub_opensearch_info("https://logs.google.com:777/os//")
1076
-
1077
- driver(config)
1078
-
1079
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash", times: 1)
1080
- end
1081
-
1082
- data("legacy_template" => [true, "_template"],
1083
- "new_template" => [false, "_index_template"])
1084
- def test_custom_template_overwrite(data)
1085
- use_legacy_template_flag, endpoint = data
1086
- cwd = File.dirname(__FILE__)
1087
- template_file = if use_legacy_template_flag
1088
- File.join(cwd, 'test_template.json')
1089
- else
1090
- File.join(cwd, 'test_index_template.json')
1091
- end
1092
-
1093
- config = %{
1094
- host logs.google.com
1095
- port 777
1096
- scheme https
1097
- path /os/
1098
- user john
1099
- password doe
1100
- template_name myapp_alias_template
1101
- template_file #{template_file}
1102
- template_overwrite true
1103
- customize_template {"--appid--": "myapp-logs","--index_prefix--":"mylogs"}
1104
- use_legacy_template #{use_legacy_template_flag}
1105
- }
1106
-
1107
- # connection start
1108
- stub_request(:head, "https://logs.google.com:777/os//").
1109
- with(basic_auth: ['john', 'doe']).
1110
- to_return(:status => 200, :body => "", :headers => {})
1111
- # check if template exists
1112
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template").
1113
- with(basic_auth: ['john', 'doe']).
1114
- to_return(:status => 200, :body => "", :headers => {})
1115
- # creation
1116
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template").
1117
- with(basic_auth: ['john', 'doe']).
1118
- to_return(:status => 200, :body => "", :headers => {})
1119
- stub_opensearch_info("https://logs.google.com:777/os//")
1120
-
1121
- driver(config)
1122
-
1123
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/myapp_alias_template", times: 1)
1124
- end
1125
-
1126
- def test_template_create_invalid_filename
1127
- config = %{
1128
- host logs.google.com
1129
- port 777
1130
- scheme https
1131
- path /os/
1132
- user john
1133
- password doe
1134
- template_name logstash
1135
- template_file /abc123
1136
- }
1137
-
1138
- # connection start
1139
- stub_request(:head, "https://logs.google.com:777/os//").
1140
- with(basic_auth: ['john', 'doe']).
1141
- to_return(:status => 200, :body => "", :headers => {})
1142
- # check if template exists
1143
- stub_request(:get, "https://logs.google.com:777/os//_template/logstash").
1144
- with(basic_auth: ['john', 'doe']).
1145
- to_return(:status => 404, :body => "", :headers => {})
1146
- stub_opensearch_info("https://logs.google.com:777/os//")
1147
-
1148
- assert_raise(RuntimeError) {
1149
- driver(config)
1150
- }
1151
- end
1152
-
1153
- data("legacy_template" => [true, "_template"],
1154
- "new_template" => [false, "_index_template"])
1155
- def test_template_create_for_host_placeholder(data)
1156
- use_legacy_template_flag, endpoint = data
1157
- cwd = File.dirname(__FILE__)
1158
- template_file = if use_legacy_template_flag
1159
- File.join(cwd, 'test_template.json')
1160
- else
1161
- File.join(cwd, 'test_index_template.json')
1162
- end
1163
-
1164
- config = %{
1165
- host logs-${tag}.google.com
1166
- port 777
1167
- scheme https
1168
- path /os/
1169
- user john
1170
- password doe
1171
- template_name logstash
1172
- template_file #{template_file}
1173
- verify_os_version_at_startup false
1174
- default_elasticsearch_version 6
1175
- use_legacy_template #{use_legacy_template_flag}
1176
- }
1177
-
1178
- # connection start
1179
- stub_request(:head, "https://logs-test.google.com:777/os//").
1180
- with(basic_auth: ['john', 'doe']).
1181
- to_return(:status => 200, :body => "", :headers => {})
1182
- # check if template exists
1183
- stub_request(:get, "https://logs-test.google.com:777/os//#{endpoint}/logstash").
1184
- with(basic_auth: ['john', 'doe']).
1185
- to_return(:status => 404, :body => "", :headers => {})
1186
- stub_request(:put, "https://logs-test.google.com:777/os//#{endpoint}/logstash").
1187
- with(basic_auth: ['john', 'doe']).
1188
- to_return(status: 200, body: "", headers: {})
1189
- stub_request(:post, "https://logs-test.google.com:777/os//_bulk").
1190
- with(basic_auth: ['john', 'doe']).
1191
- to_return(status: 200, body: "", headers: {})
1192
-
1193
- driver(config)
1194
-
1195
- stub_opensearch("https://logs-test.google.com:777/os//_bulk")
1196
- stub_opensearch_info("https://logs-test.google.com:777/os//")
1197
- driver.run(default_tag: 'test') do
1198
- driver.feed(sample_record)
1199
- end
1200
- end
1201
-
1202
- data("legacy_template" => [true, "_template"],
1203
- "new_template" => [false, "_index_template"])
1204
- def test_template_retry_install_fails(data)
1205
- use_legacy_template_flag, endpoint = data
1206
- cwd = File.dirname(__FILE__)
1207
- template_file = if use_legacy_template_flag
1208
- File.join(cwd, 'test_template.json')
1209
- else
1210
- File.join(cwd, 'test_index_template.json')
1211
- end
1212
-
1213
- config = %{
1214
- host logs.google.com
1215
- port 778
1216
- scheme https
1217
- path /os/
1218
- user john
1219
- password doe
1220
- template_name logstash
1221
- template_file #{template_file}
1222
- max_retry_putting_template 3
1223
- use_legacy_template #{use_legacy_template_flag}
1224
- }
1225
-
1226
- connection_resets = 0
1227
- # check if template exists
1228
- stub_request(:get, "https://logs.google.com:778/os//#{endpoint}/logstash")
1229
- .with(basic_auth: ['john', 'doe']) do |req|
1230
- connection_resets += 1
1231
- raise Faraday::ConnectionFailed, "Test message"
1232
- end
1233
- stub_opensearch_info("https://logs.google.com:778/os//")
1234
-
1235
- assert_raise(Fluent::Plugin::OpenSearchError::RetryableOperationExhaustedFailure) do
1236
- driver(config)
1237
- end
1238
-
1239
- assert_equal(4, connection_resets)
1240
- end
1241
-
1242
- transport_errors_handled_separately = [OpenSearch::Transport::Transport::Errors::NotFound]
1243
- transport_errors = OpenSearch::Transport::Transport::Errors.constants.map { |err| [err, OpenSearch::Transport::Transport::Errors.const_get(err)] }
1244
- transport_errors_hash = Hash[transport_errors.select { |err| !transport_errors_handled_separately.include?(err[1]) } ]
1245
-
1246
- data(transport_errors_hash)
1247
- def test_template_retry_transport_errors(error)
1248
- endpoint, use_legacy_template_flag = ["_index_template".freeze, false]
1249
- cwd = File.dirname(__FILE__)
1250
- template_file = File.join(cwd, 'test_index_template.json')
1251
-
1252
- config = %{
1253
- host logs.google.com
1254
- port 778
1255
- scheme https
1256
- path /os/
1257
- user john
1258
- password doe
1259
- template_name logstash
1260
- template_file #{template_file}
1261
- max_retry_putting_template 0
1262
- use_legacy_template #{use_legacy_template_flag}
1263
- }
1264
-
1265
- retries = 0
1266
- stub_request(:get, "https://logs.google.com:778/os//#{endpoint}/logstash")
1267
- .with(basic_auth: ['john', 'doe']) do |req|
1268
- retries += 1
1269
- raise error
1270
- end
1271
- stub_opensearch_info("https://logs.google.com:778/os//")
1272
-
1273
- assert_raise(Fluent::Plugin::OpenSearchError::RetryableOperationExhaustedFailure) do
1274
- driver(config)
1275
- end
1276
-
1277
- assert_equal(1, retries)
1278
- end
1279
-
1280
- data("legacy_template" => [true, "_template"],
1281
- "new_template" => [false, "_index_template"])
1282
- def test_template_retry_install_does_not_fail(data)
1283
- use_legacy_template_flag, endpoint = data
1284
- cwd = File.dirname(__FILE__)
1285
- template_file = if use_legacy_template_flag
1286
- File.join(cwd, 'test_template.json')
1287
- else
1288
- File.join(cwd, 'test_index_template.json')
1289
- end
1290
-
1291
- config = %{
1292
- host logs.google.com
1293
- port 778
1294
- scheme https
1295
- path /os/
1296
- user john
1297
- password doe
1298
- template_name logstash
1299
- template_file #{template_file}
1300
- max_retry_putting_template 3
1301
- fail_on_putting_template_retry_exceed false
1302
- use_legacy_template #{use_legacy_template_flag}
1303
- }
1304
-
1305
- connection_resets = 0
1306
- # check if template exists
1307
- stub_request(:get, "https://logs.google.com:778/os//#{endpoint}/logstash")
1308
- .with(basic_auth: ['john', 'doe']) do |req|
1309
- connection_resets += 1
1310
- raise Faraday::ConnectionFailed, "Test message"
1311
- end
1312
- stub_opensearch_info("https://logs.google.com:778/os//")
1313
-
1314
- driver(config)
1315
-
1316
- assert_equal(4, connection_resets)
1317
- end
1318
-
1319
- data("legacy_template" => [true, "_template"],
1320
- "new_template" => [false, "_index_template"])
1321
- def test_templates_create(data)
1322
- use_legacy_template_flag, endpoint = data
1323
- cwd = File.dirname(__FILE__)
1324
- template_file = if use_legacy_template_flag
1325
- File.join(cwd, 'test_template.json')
1326
- else
1327
- File.join(cwd, 'test_index_template.json')
1328
- end
1329
-
1330
- config = %{
1331
- host logs.google.com
1332
- port 777
1333
- scheme https
1334
- path /os/
1335
- user john
1336
- password doe
1337
- templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}","logstash3":"#{template_file}" }
1338
- use_legacy_template #{use_legacy_template_flag}
1339
- }
1340
-
1341
- stub_request(:head, "https://logs.google.com:777/os//").
1342
- with(basic_auth: ['john', 'doe']).
1343
- to_return(:status => 200, :body => "", :headers => {})
1344
- # check if template exists
1345
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1346
- with(basic_auth: ['john', 'doe']).
1347
- to_return(:status => 404, :body => "", :headers => {})
1348
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1349
- with(basic_auth: ['john', 'doe']).
1350
- to_return(:status => 404, :body => "", :headers => {})
1351
-
1352
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash3").
1353
- with(basic_auth: ['john', 'doe']).
1354
- to_return(:status => 200, :body => "", :headers => {}) #exists
1355
-
1356
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1357
- with(basic_auth: ['john', 'doe']).
1358
- to_return(:status => 200, :body => "", :headers => {})
1359
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1360
- with(basic_auth: ['john', 'doe']).
1361
- to_return(:status => 200, :body => "", :headers => {})
1362
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash3").
1363
- with(basic_auth: ['john', 'doe']).
1364
- to_return(:status => 200, :body => "", :headers => {})
1365
- stub_opensearch_info("https://logs.google.com:777/os//")
1366
-
1367
- driver(config)
1368
-
1369
- assert_requested( :put, "https://logs.google.com:777/os//#{endpoint}/logstash1", times: 1)
1370
- assert_requested( :put, "https://logs.google.com:777/os//#{endpoint}/logstash2", times: 1)
1371
- assert_not_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash3") #exists
1372
- end
1373
-
1374
- data("legacy_template" => [true, "_template"],
1375
- "new_template" => [false, "_index_template"])
1376
- def test_templates_overwrite(data)
1377
- use_legacy_template_flag, endpoint = data
1378
- cwd = File.dirname(__FILE__)
1379
- template_file = if use_legacy_template_flag
1380
- File.join(cwd, 'test_template.json')
1381
- else
1382
- File.join(cwd, 'test_index_template.json')
1383
- end
1384
-
1385
- config = %{
1386
- host logs.google.com
1387
- port 777
1388
- scheme https
1389
- path /os/
1390
- user john
1391
- password doe
1392
- templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}","logstash3":"#{template_file}" }
1393
- template_overwrite true
1394
- use_legacy_template #{use_legacy_template_flag}
1395
- }
1396
-
1397
- stub_request(:head, "https://logs.google.com:777/os//").
1398
- with(basic_auth: ['john', 'doe']).
1399
- to_return(:status => 200, :body => "", :headers => {})
1400
- # check if template exists
1401
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1402
- with(basic_auth: ['john', 'doe']).
1403
- to_return(:status => 200, :body => "", :headers => {})
1404
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1405
- with(basic_auth: ['john', 'doe']).
1406
- to_return(:status => 200, :body => "", :headers => {})
1407
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash3").
1408
- with(basic_auth: ['john', 'doe']).
1409
- to_return(:status => 200, :body => "", :headers => {}) #exists
1410
-
1411
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1412
- with(basic_auth: ['john', 'doe']).
1413
- to_return(:status => 200, :body => "", :headers => {})
1414
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1415
- with(basic_auth: ['john', 'doe']).
1416
- to_return(:status => 200, :body => "", :headers => {})
1417
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash3").
1418
- with(basic_auth: ['john', 'doe']).
1419
- to_return(:status => 200, :body => "", :headers => {})
1420
- stub_opensearch_info("https://logs.google.com:777/os//")
1421
-
1422
- driver(config)
1423
-
1424
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash1", times: 1)
1425
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash2", times: 1)
1426
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash3", times: 1)
1427
- end
1428
-
1429
- data("legacy_template" => [true, "_template"],
1430
- "new_template" => [false, "_index_template"])
1431
- def test_templates_are_also_used(data)
1432
- use_legacy_template_flag, endpoint = data
1433
- cwd = File.dirname(__FILE__)
1434
- template_file = if use_legacy_template_flag
1435
- File.join(cwd, 'test_template.json')
1436
- else
1437
- File.join(cwd, 'test_index_template.json')
1438
- end
1439
-
1440
- config = %{
1441
- host logs.google.com
1442
- port 777
1443
- scheme https
1444
- path /os/
1445
- user john
1446
- password doe
1447
- template_name logstash
1448
- template_file #{template_file}
1449
- templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}" }
1450
- use_legacy_template #{use_legacy_template_flag}
1451
- }
1452
- # connection start
1453
- stub_request(:head, "https://logs.google.com:777/os//").
1454
- with(basic_auth: ['john', 'doe']).
1455
- to_return(:status => 200, :body => "", :headers => {})
1456
- # check if template exists
1457
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash").
1458
- with(basic_auth: ['john', 'doe']).
1459
- to_return(:status => 404, :body => "", :headers => {})
1460
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1461
- with(basic_auth: ['john', 'doe']).
1462
- to_return(:status => 404, :body => "", :headers => {})
1463
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1464
- with(basic_auth: ['john', 'doe']).
1465
- to_return(:status => 404, :body => "", :headers => {})
1466
- #creation
1467
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash").
1468
- with(basic_auth: ['john', 'doe']).
1469
- to_return(:status => 200, :body => "", :headers => {})
1470
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1471
- with(basic_auth: ['john', 'doe']).
1472
- to_return(:status => 200, :body => "", :headers => {})
1473
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1474
- with(basic_auth: ['john', 'doe']).
1475
- to_return(:status => 200, :body => "", :headers => {})
1476
- stub_opensearch_info("https://logs.google.com:777/os//")
1477
-
1478
- driver(config)
1479
-
1480
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash", times: 1)
1481
-
1482
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash1")
1483
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash2")
1484
- end
1485
-
1486
- data("legacy_template" => [true, "_template"],
1487
- "new_template" => [false, "_index_template"])
1488
- def test_templates_can_be_partially_created_if_error_occurs(data)
1489
- use_legacy_template_flag, endpoint = data
1490
- cwd = File.dirname(__FILE__)
1491
- template_file = if use_legacy_template_flag
1492
- File.join(cwd, 'test_template.json')
1493
- else
1494
- File.join(cwd, 'test_index_template.json')
1495
- end
1496
-
1497
- config = %{
1498
- host logs.google.com
1499
- port 777
1500
- scheme https
1501
- path /os/
1502
- user john
1503
- password doe
1504
- templates {"logstash1":"#{template_file}", "logstash2":"/abc" }
1505
- use_legacy_template #{use_legacy_template_flag}
1506
- }
1507
- stub_request(:head, "https://logs.google.com:777/os//").
1508
- with(basic_auth: ['john', 'doe']).
1509
- to_return(:status => 200, :body => "", :headers => {})
1510
- # check if template exists
1511
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1512
- with(basic_auth: ['john', 'doe']).
1513
- to_return(:status => 404, :body => "", :headers => {})
1514
- stub_request(:get, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1515
- with(basic_auth: ['john', 'doe']).
1516
- to_return(:status => 404, :body => "", :headers => {})
1517
-
1518
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash1").
1519
- with(basic_auth: ['john', 'doe']).
1520
- to_return(:status => 200, :body => "", :headers => {})
1521
- stub_request(:put, "https://logs.google.com:777/os//#{endpoint}/logstash2").
1522
- with(basic_auth: ['john', 'doe']).
1523
- to_return(:status => 200, :body => "", :headers => {})
1524
- stub_opensearch_info("https://logs.google.com:777/os//")
1525
-
1526
- assert_raise(RuntimeError) {
1527
- driver(config)
1528
- }
1529
-
1530
- assert_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash1", times: 1)
1531
- assert_not_requested(:put, "https://logs.google.com:777/os//#{endpoint}/logstash2")
1532
- end
1533
-
1534
- def test_legacy_hosts_list
1535
- config = %{
1536
- hosts host1:50,host2:100,host3
1537
- scheme https
1538
- path /os/
1539
- port 123
1540
- }
1541
- stub_opensearch_info("https://host1:50")
1542
- stub_opensearch_info("https://host2:100")
1543
- stub_opensearch_info("https://host3:123")
1544
- instance = driver(config).instance
1545
-
1546
- assert_equal 3, instance.get_connection_options[:hosts].length
1547
- host1, host2, host3 = instance.get_connection_options[:hosts]
1548
-
1549
- assert_equal 'host1', host1[:host]
1550
- assert_equal 50, host1[:port]
1551
- assert_equal 'https', host1[:scheme]
1552
- assert_equal '/os/', host2[:path]
1553
- assert_equal 'host3', host3[:host]
1554
- assert_equal 123, host3[:port]
1555
- assert_equal 'https', host3[:scheme]
1556
- assert_equal '/os/', host3[:path]
1557
- end
1558
-
1559
- def test_hosts_list
1560
- config = %{
1561
- hosts https://john:password@host1:443/elastic/,http://host2
1562
- path /default_path
1563
- user default_user
1564
- password default_password
1565
- }
1566
- stub_opensearch_info("https://john:password@host1:443/elastic/")
1567
- stub_opensearch_info("http://host2")
1568
- instance = driver(config).instance
1569
-
1570
- assert_equal 2, instance.get_connection_options[:hosts].length
1571
- host1, host2 = instance.get_connection_options[:hosts]
1572
-
1573
- assert_equal 'host1', host1[:host]
1574
- assert_equal 443, host1[:port]
1575
- assert_equal 'https', host1[:scheme]
1576
- assert_equal 'john', host1[:user]
1577
- assert_equal 'password', host1[:password]
1578
- assert_equal '/elastic/', host1[:path]
1579
-
1580
- assert_equal 'host2', host2[:host]
1581
- assert_equal 'http', host2[:scheme]
1582
- assert_equal 'default_user', host2[:user]
1583
- assert_equal 'default_password', host2[:password]
1584
- assert_equal '/default_path', host2[:path]
1585
- end
1586
-
1587
- def test_hosts_list_with_escape_placeholders
1588
- config = %{
1589
- hosts https://%{j+hn}:%{passw@rd}@host1:443/elastic/,http://host2
1590
- path /default_path
1591
- user default_user
1592
- password default_password
1593
- }
1594
- stub_opensearch_info("https://j%2Bhn:passw%40rd@host1:443/elastic/")
1595
- stub_opensearch_info("http://host2")
1596
-
1597
- instance = driver(config).instance
1598
-
1599
- assert_equal 2, instance.get_connection_options[:hosts].length
1600
- host1, host2 = instance.get_connection_options[:hosts]
1601
-
1602
- assert_equal 'host1', host1[:host]
1603
- assert_equal 443, host1[:port]
1604
- assert_equal 'https', host1[:scheme]
1605
- assert_equal 'j%2Bhn', host1[:user]
1606
- assert_equal 'passw%40rd', host1[:password]
1607
- assert_equal '/elastic/', host1[:path]
1608
-
1609
- assert_equal 'host2', host2[:host]
1610
- assert_equal 'http', host2[:scheme]
1611
- assert_equal 'default_user', host2[:user]
1612
- assert_equal 'default_password', host2[:password]
1613
- assert_equal '/default_path', host2[:path]
1614
- end
1615
-
1616
- class IPv6AdressStringHostsTest < self
1617
- def test_legacy_hosts_list
1618
- config = %{
1619
- hosts "[2404:7a80:d440:3000:192a:a292:bd7f:ca19]:50,host2:100,host3"
1620
- scheme https
1621
- path /os/
1622
- port 123
1623
- }
1624
- instance = driver(config).instance
1625
-
1626
- assert_raise(URI::InvalidURIError) do
1627
- instance.get_connection_options[:hosts].length
1628
- end
1629
- end
1630
-
1631
- def test_hosts_list
1632
- config = %{
1633
- hosts https://john:password@[2404:7a80:d440:3000:192a:a292:bd7f:ca19]:443/opensearch/,http://host2
1634
- path /default_path
1635
- user default_user
1636
- password default_password
1637
- }
1638
- instance = driver(config).instance
1639
-
1640
- assert_equal 2, instance.get_connection_options[:hosts].length
1641
- host1, host2 = instance.get_connection_options[:hosts]
1642
-
1643
- assert_equal '[2404:7a80:d440:3000:192a:a292:bd7f:ca19]', host1[:host]
1644
- assert_equal 443, host1[:port]
1645
- assert_equal 'https', host1[:scheme]
1646
- assert_equal 'john', host1[:user]
1647
- assert_equal 'password', host1[:password]
1648
- assert_equal '/opensearch/', host1[:path]
1649
-
1650
- assert_equal 'host2', host2[:host]
1651
- assert_equal 'http', host2[:scheme]
1652
- assert_equal 'default_user', host2[:user]
1653
- assert_equal 'default_password', host2[:password]
1654
- assert_equal '/default_path', host2[:path]
1655
- end
1656
-
1657
- def test_hosts_list_with_escape_placeholders
1658
- config = %{
1659
- hosts https://%{j+hn}:%{passw@rd}@[2404:7a80:d440:3000:192a:a292:bd7f:ca19]:443/opensearch/,http://host2
1660
- path /default_path
1661
- user default_user
1662
- password default_password
1663
- }
1664
- instance = driver(config).instance
1665
-
1666
- assert_equal 2, instance.get_connection_options[:hosts].length
1667
- host1, host2 = instance.get_connection_options[:hosts]
1668
-
1669
- assert_equal '[2404:7a80:d440:3000:192a:a292:bd7f:ca19]', host1[:host]
1670
- assert_equal 443, host1[:port]
1671
- assert_equal 'https', host1[:scheme]
1672
- assert_equal 'j%2Bhn', host1[:user]
1673
- assert_equal 'passw%40rd', host1[:password]
1674
- assert_equal '/opensearch/', host1[:path]
1675
-
1676
- assert_equal 'host2', host2[:host]
1677
- assert_equal 'http', host2[:scheme]
1678
- assert_equal 'default_user', host2[:user]
1679
- assert_equal 'default_password', host2[:password]
1680
- assert_equal '/default_path', host2[:path]
1681
- end
1682
- end
1683
-
1684
- def test_single_host_params_and_defaults
1685
- config = %{
1686
- host logs.google.com
1687
- user john
1688
- password doe
1689
- }
1690
- instance = driver(config).instance
1691
-
1692
- assert_equal 1, instance.get_connection_options[:hosts].length
1693
- host1 = instance.get_connection_options[:hosts][0]
1694
-
1695
- assert_equal 'logs.google.com', host1[:host]
1696
- assert_equal 9200, host1[:port]
1697
- assert_equal 'http', host1[:scheme]
1698
- assert_equal 'john', host1[:user]
1699
- assert_equal 'doe', host1[:password]
1700
- assert_equal nil, host1[:path]
1701
- end
1702
-
1703
- def test_single_host_params_and_defaults_with_escape_placeholders
1704
- config = %{
1705
- host logs.google.com
1706
- user %{j+hn}
1707
- password %{d@e}
1708
- }
1709
- instance = driver(config).instance
1710
-
1711
- assert_equal 1, instance.get_connection_options[:hosts].length
1712
- host1 = instance.get_connection_options[:hosts][0]
1713
-
1714
- assert_equal 'logs.google.com', host1[:host]
1715
- assert_equal 9200, host1[:port]
1716
- assert_equal 'http', host1[:scheme]
1717
- assert_equal 'j%2Bhn', host1[:user]
1718
- assert_equal 'd%40e', host1[:password]
1719
- assert_equal nil, host1[:path]
1720
- end
1721
-
1722
- def test_host_and_port_are_ignored_if_specify_hosts
1723
- config = %{
1724
- host logs.google.com
1725
- port 9200
1726
- hosts host1:50,host2:100
1727
- }
1728
- instance = driver(config).instance
1729
-
1730
- params = instance.get_connection_options[:hosts]
1731
- hosts = params.map { |p| p[:host] }
1732
- ports = params.map { |p| p[:port] }
1733
- assert(hosts.none? { |h| h == 'logs.google.com' })
1734
- assert(ports.none? { |p| p == 9200 })
1735
- end
1736
-
1737
- class IPv6AdressStringHostTest < self
1738
- def test_single_host_params_and_defaults
1739
- config = %{
1740
- host 2404:7a80:d440:3000:192a:a292:bd7f:ca19
1741
- user john
1742
- password doe
1743
- }
1744
- instance = driver(config).instance
1745
-
1746
- assert_equal 1, instance.get_connection_options[:hosts].length
1747
- host1 = instance.get_connection_options[:hosts][0]
1748
-
1749
- assert_equal '[2404:7a80:d440:3000:192a:a292:bd7f:ca19]', host1[:host]
1750
- assert_equal 9200, host1[:port]
1751
- assert_equal 'http', host1[:scheme]
1752
- assert_equal 'john', host1[:user]
1753
- assert_equal 'doe', host1[:password]
1754
- assert_equal nil, host1[:path]
1755
- end
1756
-
1757
- def test_single_host_params_and_defaults_with_escape_placeholders
1758
- config = %{
1759
- host 2404:7a80:d440:3000:192a:a292:bd7f:ca19
1760
- user %{j+hn}
1761
- password %{d@e}
1762
- }
1763
- instance = driver(config).instance
1764
-
1765
- assert_equal 1, instance.get_connection_options[:hosts].length
1766
- host1 = instance.get_connection_options[:hosts][0]
1767
-
1768
- assert_equal '[2404:7a80:d440:3000:192a:a292:bd7f:ca19]', host1[:host]
1769
- assert_equal 9200, host1[:port]
1770
- assert_equal 'http', host1[:scheme]
1771
- assert_equal 'j%2Bhn', host1[:user]
1772
- assert_equal 'd%40e', host1[:password]
1773
- assert_equal nil, host1[:path]
1774
- end
1775
- end
1776
-
1777
- def test_password_is_required_if_specify_user
1778
- config = %{
1779
- user john
1780
- }
1781
-
1782
- assert_raise(Fluent::ConfigError) do
1783
- driver(config)
1784
- end
1785
- end
1786
-
1787
- def test_content_type_header
1788
- stub_request(:head, "http://localhost:9200/").
1789
- to_return(:status => 200, :body => "", :headers => {})
1790
- elastic_request = stub_request(:post, "http://localhost:9200/_bulk").
1791
- with(headers: { "Content-Type" => "application/x-ndjson" })
1792
- stub_opensearch_info
1793
- driver.run(default_tag: 'test') do
1794
- driver.feed(sample_record)
1795
- end
1796
- assert_requested(elastic_request)
1797
- end
1798
-
1799
- def test_custom_headers
1800
- stub_request(:head, "http://localhost:9200/").
1801
- to_return(:status => 200, :body => "", :headers => {})
1802
- elastic_request = stub_request(:post, "http://localhost:9200/_bulk").
1803
- with(headers: {'custom' => 'header1','and_others' => 'header2' })
1804
- stub_opensearch_info
1805
- driver.configure(%[custom_headers {"custom":"header1", "and_others":"header2"}])
1806
- driver.run(default_tag: 'test') do
1807
- driver.feed(sample_record)
1808
- end
1809
- assert_requested(elastic_request)
1810
- end
1811
-
1812
- def test_write_message_with_bad_chunk
1813
- driver.configure("target_index_key bad_value\n@log_level debug\n")
1814
- stub_opensearch
1815
- stub_opensearch_info
1816
- driver.run(default_tag: 'test') do
1817
- driver.feed({'bad_value'=>"\255"})
1818
- end
1819
- error_log = driver.error_events.map {|e| e.last.message }
1820
-
1821
- assert_logs_include(error_log, /(input string invalid)|(invalid byte sequence in UTF-8)/)
1822
- end
1823
-
1824
- data('OpenSearch 1' => [1, 'fluentd'],
1825
- )
1826
- def test_writes_to_default_index(data)
1827
- version, index_name = data
1828
- stub_opensearch
1829
- stub_opensearch_info
1830
- driver("", version)
1831
- driver.run(default_tag: 'test') do
1832
- driver.feed(sample_record)
1833
- end
1834
- assert_equal(index_name, index_cmds.first['index']['_index'])
1835
- end
1836
-
1837
- # gzip compress data
1838
- def gzip(string, strategy)
1839
- wio = StringIO.new("w")
1840
- w_gz = Zlib::GzipWriter.new(wio, strategy = strategy)
1841
- w_gz.write(string)
1842
- w_gz.close
1843
- wio.string
1844
- end
1845
-
1846
-
1847
- def test_writes_to_default_index_with_compression
1848
- config = %[
1849
- compression_level default_compression
1850
- ]
1851
-
1852
- bodystr = %({
1853
- "took" : 500,
1854
- "errors" : false,
1855
- "items" : [
1856
- {
1857
- "create": {
1858
- "_index" : "fluentd",
1859
- "_type" : "fluentd"
1860
- }
1861
- }
1862
- ]
1863
- })
1864
-
1865
- compressed_body = gzip(bodystr, Zlib::DEFAULT_COMPRESSION)
1866
-
1867
- elastic_request = stub_request(:post, "http://localhost:9200/_bulk").
1868
- to_return(:status => 200, :headers => {'Content-Type' => 'Application/json'}, :body => compressed_body)
1869
- stub_opensearch_info("http://localhost:9200/")
1870
-
1871
- driver(config)
1872
- driver.run(default_tag: 'test') do
1873
- driver.feed(sample_record)
1874
- end
1875
-
1876
- assert_requested(elastic_request)
1877
- end
1878
-
1879
- data('OpenSearch 1' => [1, Fluent::Plugin::OpenSearchOutput::DEFAULT_TYPE_NAME],
1880
- )
1881
- def test_writes_to_default_type(data)
1882
- version, index_type = data
1883
- stub_opensearch
1884
- stub_opensearch_info
1885
- driver("", version)
1886
- driver.run(default_tag: 'test') do
1887
- driver.feed(sample_record)
1888
- end
1889
- assert_equal(index_type, index_cmds.first['index']['_type'])
1890
- end
1891
-
1892
- def test_writes_to_speficied_index
1893
- driver.configure("index_name myindex\n")
1894
- stub_opensearch
1895
- stub_opensearch_info
1896
- driver.run(default_tag: 'test') do
1897
- driver.feed(sample_record)
1898
- end
1899
- assert_equal('myindex', index_cmds.first['index']['_index'])
1900
- end
1901
-
1902
- def test_writes_with_huge_records
1903
- driver.configure(Fluent::Config::Element.new(
1904
- 'ROOT', '', {
1905
- '@type' => 'opensearch',
1906
- 'bulk_message_request_threshold' => 20 * 1024 * 1024,
1907
- }, [
1908
- Fluent::Config::Element.new('buffer', 'tag', {
1909
- 'chunk_keys' => ['tag', 'time'],
1910
- 'chunk_limit_size' => '64MB',
1911
- }, [])
1912
- ]
1913
- ))
1914
- request = stub_opensearch
1915
- stub_opensearch_info
1916
- driver.run(default_tag: 'test') do
1917
- driver.feed(sample_record('huge_record' => ("a" * 20 * 1024 * 1024)))
1918
- driver.feed(sample_record('huge_record' => ("a" * 20 * 1024 * 1024)))
1919
- end
1920
- assert_requested(request, times: 2)
1921
- end
1922
-
1923
- def test_writes_with_record_metadata
1924
- chunk_id_key = "metadata_key".freeze
1925
- driver.configure(Fluent::Config::Element.new(
1926
- 'ROOT', '', {
1927
- '@type' => 'opensearch',
1928
- }, [
1929
- Fluent::Config::Element.new('metadata', '', {
1930
- 'include_chunk_id' => true,
1931
- 'chunk_id_key' => chunk_id_key,
1932
- }, [])
1933
- ]
1934
- ))
1935
- stub_request(:post, "http://localhost:9200/_bulk").
1936
- with(
1937
- body: /{"index":{"_index":"fluentd","_type":"_doc"}}\n{"age":26,"request_id":"42","parent_id":"parent","routing_id":"routing","#{chunk_id_key}":".*"}\n/) do |req|
1938
- @index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
1939
- end
1940
- stub_opensearch_info
1941
- driver.run(default_tag: 'test', shutdown: false) do
1942
- driver.feed(sample_record)
1943
- end
1944
- assert_true index_cmds[1].has_key?(chunk_id_key)
1945
- first_chunk_id = index_cmds[1].fetch(chunk_id_key)
1946
-
1947
- driver.run(default_tag: 'test') do
1948
- driver.feed(sample_record)
1949
- end
1950
- assert_true index_cmds[1].has_key?(chunk_id_key)
1951
- second_chunk_id = index_cmds[1].fetch(chunk_id_key)
1952
- assert do
1953
- first_chunk_id != second_chunk_id
1954
- end
1955
- end
1956
-
1957
- def test_writes_with_huge_records_but_uncheck
1958
- driver.configure(Fluent::Config::Element.new(
1959
- 'ROOT', '', {
1960
- '@type' => 'opensearch',
1961
- 'bulk_message_request_threshold' => -1,
1962
- }, [
1963
- Fluent::Config::Element.new('buffer', 'tag', {
1964
- 'chunk_keys' => ['tag', 'time'],
1965
- 'chunk_limit_size' => '64MB',
1966
- }, [])
1967
- ]
1968
- ))
1969
- request = stub_opensearch
1970
- stub_opensearch_info
1971
- driver.run(default_tag: 'test') do
1972
- driver.feed(sample_record('huge_record' => ("a" * 20 * 1024 * 1024)))
1973
- driver.feed(sample_record('huge_record' => ("a" * 20 * 1024 * 1024)))
1974
- end
1975
- assert_false(driver.instance.split_request?({}, nil))
1976
- assert_requested(request, times: 1)
1977
- end
1978
-
1979
- class IndexNamePlaceholdersTest < self
1980
- def test_writes_to_speficied_index_with_tag_placeholder
1981
- driver.configure("index_name myindex.${tag}\n")
1982
- stub_opensearch
1983
- stub_opensearch_info
1984
- driver.run(default_tag: 'test') do
1985
- driver.feed(sample_record)
1986
- end
1987
- assert_equal('myindex.test', index_cmds.first['index']['_index'])
1988
- end
1989
-
1990
- def test_writes_to_speficied_index_with_time_placeholder
1991
- driver.configure(Fluent::Config::Element.new(
1992
- 'ROOT', '', {
1993
- '@type' => 'opensearch',
1994
- 'index_name' => 'myindex.%Y.%m.%d',
1995
- }, [
1996
- Fluent::Config::Element.new('buffer', 'tag,time', {
1997
- 'chunk_keys' => ['tag', 'time'],
1998
- 'timekey' => 3600,
1999
- }, [])
2000
- ]
2001
- ))
2002
- stub_opensearch
2003
- stub_opensearch_info
2004
- time = Time.parse Date.today.iso8601
2005
- driver.run(default_tag: 'test') do
2006
- driver.feed(time.to_i, sample_record)
2007
- end
2008
- assert_equal("myindex.#{time.utc.strftime("%Y.%m.%d")}", index_cmds.first['index']['_index'])
2009
- end
2010
-
2011
- def test_writes_to_speficied_index_with_custom_key_placeholder
2012
- driver.configure(Fluent::Config::Element.new(
2013
- 'ROOT', '', {
2014
- '@type' => 'opensearch',
2015
- 'index_name' => 'myindex.${pipeline_id}',
2016
- }, [
2017
- Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
2018
- ]
2019
- ))
2020
- time = Time.parse Date.today.iso8601
2021
- pipeline_id = "mypipeline"
2022
- logstash_index = "myindex.#{pipeline_id}"
2023
- stub_opensearch
2024
- stub_opensearch_info
2025
- driver.run(default_tag: 'test') do
2026
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
2027
- end
2028
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2029
- end
2030
- end
2031
-
2032
- def test_writes_to_speficied_index_uppercase
2033
- driver.configure("index_name MyIndex\n")
2034
- stub_opensearch
2035
- stub_opensearch_info
2036
- driver.run(default_tag: 'test') do
2037
- driver.feed(sample_record)
2038
- end
2039
- # Allthough index_name has upper-case characters,
2040
- # it should be set as lower-case when sent to elasticsearch.
2041
- assert_equal('myindex', index_cmds.first['index']['_index'])
2042
- end
2043
-
2044
- def test_writes_to_target_index_key
2045
- driver.configure("target_index_key @target_index\n")
2046
- stub_opensearch
2047
- stub_opensearch_info
2048
- record = sample_record.clone
2049
- driver.run(default_tag: 'test') do
2050
- driver.feed(sample_record.merge('@target_index' => 'local-override'))
2051
- end
2052
- assert_equal('local-override', index_cmds.first['index']['_index'])
2053
- assert_nil(index_cmds[1]['@target_index'])
2054
- end
2055
-
2056
- def test_writes_to_target_index_key_logstash
2057
- driver.configure("target_index_key @target_index
2058
- logstash_format true")
2059
- time = Time.parse Date.today.iso8601
2060
- stub_opensearch
2061
- stub_opensearch_info
2062
- driver.run(default_tag: 'test') do
2063
- driver.feed(time.to_i, sample_record.merge('@target_index' => 'local-override'))
2064
- end
2065
- assert_equal('local-override', index_cmds.first['index']['_index'])
2066
- end
2067
-
2068
- def test_writes_to_target_index_key_logstash_uppercase
2069
- driver.configure("target_index_key @target_index
2070
- logstash_format true")
2071
- time = Time.parse Date.today.iso8601
2072
- stub_opensearch
2073
- stub_opensearch_info
2074
- driver.run(default_tag: 'test') do
2075
- driver.feed(time.to_i, sample_record.merge('@target_index' => 'LOCAL-OVERRIDE'))
2076
- end
2077
- # Allthough @target_index has upper-case characters,
2078
- # it should be set as lower-case when sent to elasticsearch.
2079
- assert_equal('local-override', index_cmds.first['index']['_index'])
2080
- end
2081
-
2082
- def test_writes_to_default_index_with_pipeline
2083
- pipeline = "fluentd"
2084
- driver.configure("pipeline #{pipeline}")
2085
- stub_opensearch
2086
- stub_opensearch_info
2087
- driver.run(default_tag: 'test') do
2088
- driver.feed(sample_record)
2089
- end
2090
- assert_equal(pipeline, index_cmds.first['index']['pipeline'])
2091
- end
2092
-
2093
- def stub_opensearch_affinity_target_index_search_with_body(url="http://localhost:9200/logstash-*/_search", ids, return_body_str)
2094
- # Note: ids used in query is unique list of ids
2095
- stub_request(:post, url)
2096
- .with(
2097
- body: "{\"query\":{\"ids\":{\"values\":#{ids.uniq.to_json}}},\"_source\":false,\"sort\":[{\"_index\":{\"order\":\"desc\"}}]}",
2098
- )
2099
- .to_return(lambda do |req|
2100
- { :status => 200,
2101
- :headers => { 'Content-Type' => 'json' },
2102
- :body => return_body_str
2103
- }
2104
- end)
2105
- end
2106
-
2107
- def stub_opensearch_affinity_target_index_search(url="http://localhost:9200/logstash-*/_search", ids, indices)
2108
- # Example ids and indices arrays.
2109
- # [ "3408a2c8eecd4fbfb82e45012b54fa82", "2816fc6ef4524b3f8f7e869002005433"]
2110
- # [ "logstash-2021.04.28", "logstash-2021.04.29"]
2111
- body = %({
2112
- "took" : 31,
2113
- "timed_out" : false,
2114
- "_shards" : {
2115
- "total" : 52,
2116
- "successful" : 52,
2117
- "skipped" : 48,
2118
- "failed" : 0
2119
- },
2120
- "hits" : {
2121
- "total" : {
2122
- "value" : 356,
2123
- "relation" : "eq"
2124
- },
2125
- "max_score" : null,
2126
- "hits" : [
2127
- {
2128
- "_index" : "#{indices[0]}",
2129
- "_type" : "_doc",
2130
- "_id" : "#{ids[0]}",
2131
- "_score" : null,
2132
- "sort" : [
2133
- "#{indices[0]}"
2134
- ]
2135
- },
2136
- {
2137
- "_index" : "#{indices[1]}",
2138
- "_type" : "_doc",
2139
- "_id" : "#{ids[1]}",
2140
- "_score" : null,
2141
- "sort" : [
2142
- "#{indices[1]}"
2143
- ]
2144
- }
2145
- ]
2146
- }
2147
- })
2148
- stub_opensearch_affinity_target_index_search_with_body(ids, body)
2149
- end
2150
-
2151
- def stub_opensearch_affinity_target_index_search_return_empty(url="http://localhost:9200/logstash-*/_search", ids)
2152
- empty_body = %({
2153
- "took" : 5,
2154
- "timed_out" : false,
2155
- "_shards" : {
2156
- "total" : 54,
2157
- "successful" : 54,
2158
- "skipped" : 53,
2159
- "failed" : 0
2160
- },
2161
- "hits" : {
2162
- "total" : {
2163
- "value" : 0,
2164
- "relation" : "eq"
2165
- },
2166
- "max_score" : null,
2167
- "hits" : [ ]
2168
- }
2169
- })
2170
- stub_opensearch_affinity_target_index_search_with_body(ids, empty_body)
2171
- end
2172
-
2173
- def test_writes_to_affinity_target_index
2174
- driver.configure("target_index_affinity true
2175
- logstash_format true
2176
- id_key my_id
2177
- write_operation update")
2178
-
2179
- my_id_value = "3408a2c8eecd4fbfb82e45012b54fa82"
2180
- ids = [my_id_value]
2181
- indices = ["logstash-2021.04.28"]
2182
- stub_opensearch
2183
- stub_opensearch_info
2184
- stub_opensearch_affinity_target_index_search(ids, indices)
2185
- driver.run(default_tag: 'test') do
2186
- driver.feed(sample_record('my_id' => my_id_value))
2187
- end
2188
- assert_equal('logstash-2021.04.28', index_cmds.first['update']['_index'])
2189
- end
2190
-
2191
- def test_writes_to_affinity_target_index_write_operation_upsert
2192
- driver.configure("target_index_affinity true
2193
- logstash_format true
2194
- id_key my_id
2195
- write_operation upsert")
2196
-
2197
- my_id_value = "3408a2c8eecd4fbfb82e45012b54fa82"
2198
- ids = [my_id_value]
2199
- indices = ["logstash-2021.04.28"]
2200
- stub_opensearch
2201
- stub_opensearch_info
2202
- stub_opensearch_affinity_target_index_search(ids, indices)
2203
- driver.run(default_tag: 'test') do
2204
- driver.feed(sample_record('my_id' => my_id_value))
2205
- end
2206
- assert_equal('logstash-2021.04.28', index_cmds.first['update']['_index'])
2207
- end
2208
-
2209
- def test_writes_to_affinity_target_index_index_not_exists_yet
2210
- driver.configure("target_index_affinity true
2211
- logstash_format true
2212
- id_key my_id
2213
- write_operation update")
2214
-
2215
- my_id_value = "3408a2c8eecd4fbfb82e45012b54fa82"
2216
- ids = [my_id_value]
2217
- stub_opensearch
2218
- stub_opensearch_info
2219
- stub_opensearch_affinity_target_index_search_return_empty(ids)
2220
- time = Time.parse Date.today.iso8601
2221
- driver.run(default_tag: 'test') do
2222
- driver.feed(time.to_i, sample_record('my_id' => my_id_value))
2223
- end
2224
- assert_equal("logstash-#{time.utc.strftime("%Y.%m.%d")}", index_cmds.first['update']['_index'])
2225
- end
2226
-
2227
- def test_writes_to_affinity_target_index_multiple_indices
2228
- driver.configure("target_index_affinity true
2229
- logstash_format true
2230
- id_key my_id
2231
- write_operation update")
2232
-
2233
- my_id_value = "2816fc6ef4524b3f8f7e869002005433"
2234
- my_id_value2 = "3408a2c8eecd4fbfb82e45012b54fa82"
2235
- ids = [my_id_value, my_id_value2]
2236
- indices = ["logstash-2021.04.29", "logstash-2021.04.28"]
2237
- stub_opensearch_info
2238
- stub_opensearch_all_requests
2239
- stub_opensearch_affinity_target_index_search(ids, indices)
2240
- driver.run(default_tag: 'test') do
2241
- driver.feed(sample_record('my_id' => my_id_value))
2242
- driver.feed(sample_record('my_id' => my_id_value2))
2243
- end
2244
- assert_equal(2, index_cmds_all_requests.count)
2245
- assert_equal('logstash-2021.04.29', index_cmds_all_requests[0].first['update']['_index'])
2246
- assert_equal(my_id_value, index_cmds_all_requests[0].first['update']['_id'])
2247
- assert_equal('logstash-2021.04.28', index_cmds_all_requests[1].first['update']['_index'])
2248
- assert_equal(my_id_value2, index_cmds_all_requests[1].first['update']['_id'])
2249
- end
2250
-
2251
- def test_writes_to_affinity_target_index_same_id_dublicated_write_to_oldest_index
2252
- driver.configure("target_index_affinity true
2253
- logstash_format true
2254
- id_key my_id
2255
- write_operation update")
2256
-
2257
- my_id_value = "2816fc6ef4524b3f8f7e869002005433"
2258
- # It may happen than same id has inserted to two index while data inserted during rollover period
2259
- ids = [my_id_value, my_id_value]
2260
- # Simulate the used sorting here, as search sorts indices in DESC order to pick only oldest index per single _id
2261
- indices = ["logstash-2021.04.29", "logstash-2021.04.28"]
2262
-
2263
- stub_opensearch_info
2264
- stub_opensearch_all_requests
2265
- stub_opensearch_affinity_target_index_search(ids, indices)
2266
- driver.run(default_tag: 'test') do
2267
- driver.feed(sample_record('my_id' => my_id_value))
2268
- driver.feed(sample_record('my_id' => my_id_value))
2269
- end
2270
- assert_equal('logstash-2021.04.28', index_cmds.first['update']['_index'])
2271
-
2272
- assert_equal(1, index_cmds_all_requests.count)
2273
- assert_equal('logstash-2021.04.28', index_cmds_all_requests[0].first['update']['_index'])
2274
- assert_equal(my_id_value, index_cmds_all_requests[0].first['update']['_id'])
2275
- end
2276
-
2277
- class PipelinePlaceholdersTest < self
2278
- def test_writes_to_default_index_with_pipeline_tag_placeholder
2279
- pipeline = "fluentd-${tag}"
2280
- driver.configure("pipeline #{pipeline}")
2281
- stub_opensearch
2282
- stub_opensearch_info
2283
- driver.run(default_tag: 'test.builtin.placeholder') do
2284
- driver.feed(sample_record)
2285
- end
2286
- assert_equal("fluentd-test.builtin.placeholder", index_cmds.first['index']['pipeline'])
2287
- end
2288
-
2289
- def test_writes_to_default_index_with_pipeline_time_placeholder
2290
- driver.configure(Fluent::Config::Element.new(
2291
- 'ROOT', '', {
2292
- '@type' => 'elasticsearch',
2293
- 'pipeline' => 'fluentd-%Y%m%d',
2294
- }, [
2295
- Fluent::Config::Element.new('buffer', 'tag,time', {
2296
- 'chunk_keys' => ['tag', 'time'],
2297
- 'timekey' => 3600,
2298
- }, [])
2299
- ]
2300
- ))
2301
- time = Time.parse Date.today.iso8601
2302
- pipeline = "fluentd-#{time.getutc.strftime("%Y%m%d")}"
2303
- stub_opensearch
2304
- stub_opensearch_info
2305
- driver.run(default_tag: 'test') do
2306
- driver.feed(time.to_i, sample_record)
2307
- end
2308
- assert_equal(pipeline, index_cmds.first['index']['pipeline'])
2309
- end
2310
-
2311
- def test_writes_to_default_index_with_pipeline_custom_key_placeholder
2312
- driver.configure(Fluent::Config::Element.new(
2313
- 'ROOT', '', {
2314
- '@type' => 'elasticsearch',
2315
- 'pipeline' => 'fluentd-${pipeline_id}',
2316
- }, [
2317
- Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
2318
- ]
2319
- ))
2320
- time = Time.parse Date.today.iso8601
2321
- pipeline_id = "mypipeline"
2322
- logstash_index = "fluentd-#{pipeline_id}"
2323
- stub_opensearch
2324
- stub_opensearch_info
2325
- driver.run(default_tag: 'test') do
2326
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
2327
- end
2328
- assert_equal(logstash_index, index_cmds.first['index']['pipeline'])
2329
- end
2330
- end
2331
-
2332
- def test_writes_to_target_index_key_fallack
2333
- driver.configure("target_index_key @target_index\n")
2334
- stub_opensearch
2335
- stub_opensearch_info
2336
- driver.run(default_tag: 'test') do
2337
- driver.feed(sample_record)
2338
- end
2339
- assert_equal('fluentd', index_cmds.first['index']['_index'])
2340
- end
2341
-
2342
- def test_writes_to_target_index_key_fallack_logstash
2343
- driver.configure("target_index_key @target_index\n
2344
- logstash_format true")
2345
- time = Time.parse Date.today.iso8601
2346
- logstash_index = "logstash-#{time.getutc.strftime("%Y.%m.%d")}"
2347
- stub_opensearch
2348
- stub_opensearch_info
2349
- driver.run(default_tag: 'test') do
2350
- driver.feed(time.to_i, sample_record)
2351
- end
2352
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2353
- end
2354
-
2355
- data(
2356
- "OpenSearch default" => {"os_version" => 1, "_type" => "_doc", "suppress_type" => false},
2357
- "Suppressed type" => {"os_version" => 1, "_type" => nil, "suppress_type" => true},
2358
- "OpenSearch 2" => {"os_version" => 2, "_type" => nil, "suppress_type" => true},
2359
- )
2360
- def test_writes_to_speficied_type(data)
2361
- driver('', data["os_version"]).configure("suppress_type_name #{data['suppress_type']}")
2362
- stub_opensearch
2363
- stub_opensearch_info
2364
- driver.run(default_tag: 'test') do
2365
- driver.feed(sample_record)
2366
- end
2367
- if data["suppress_type"] || data["os_version"] >= 2
2368
- assert_false(index_cmds.first['index'].has_key?("_type"))
2369
- else
2370
- assert_true(index_cmds.first['index'].has_key?("_type"))
2371
- assert_equal(data['_type'], index_cmds.first['index']['_type'])
2372
- end
2373
- end
2374
-
2375
- def test_writes_to_speficied_host
2376
- driver.configure("host 192.168.33.50\n")
2377
- elastic_request = stub_opensearch("http://192.168.33.50:9200/_bulk")
2378
- stub_opensearch_info("http://192.168.33.50:9200/")
2379
- driver.run(default_tag: 'test') do
2380
- driver.feed(sample_record)
2381
- end
2382
- assert_requested(elastic_request)
2383
- end
2384
-
2385
- def test_writes_to_speficied_port
2386
- driver.configure("port 9201\n")
2387
- elastic_request = stub_opensearch("http://localhost:9201/_bulk")
2388
- stub_opensearch_info("http://localhost:9201")
2389
- driver.run(default_tag: 'test') do
2390
- driver.feed(sample_record)
2391
- end
2392
- assert_requested(elastic_request)
2393
- end
2394
-
2395
- def test_writes_to_multi_hosts
2396
- hosts = [['192.168.33.50', 9201], ['192.168.33.51', 9201], ['192.168.33.52', 9201]]
2397
- hosts_string = hosts.map {|x| "#{x[0]}:#{x[1]}"}.compact.join(',')
2398
-
2399
- driver.configure("hosts #{hosts_string}")
2400
-
2401
- hosts.each do |host_info|
2402
- host, port = host_info
2403
- stub_opensearch_with_store_index_command_counts("http://#{host}:#{port}/_bulk")
2404
- stub_opensearch_info("http://#{host}:#{port}/")
2405
- end
2406
-
2407
- driver.run(default_tag: 'test') do
2408
- 1000.times do
2409
- driver.feed(sample_record.merge('age'=>rand(100)))
2410
- end
2411
- end
2412
-
2413
- # @note: we cannot make multi chunks with options (flush_interval, buffer_chunk_limit)
2414
- # it's Fluentd test driver's constraint
2415
- # so @index_command_counts.size is always 1
2416
-
2417
- assert(@index_command_counts.size > 0, "not working with hosts options")
2418
-
2419
- total = 0
2420
- @index_command_counts.each do |url, count|
2421
- total += count
2422
- end
2423
- assert_equal(2000, total)
2424
- end
2425
-
2426
- def test_nested_record_with_flattening_on
2427
- driver.configure("flatten_hashes true
2428
- flatten_hashes_separator |")
2429
-
2430
- original_hash = {"foo" => {"bar" => "baz"}, "people" => [
2431
- {"age" => "25", "height" => "1ft"},
2432
- {"age" => "30", "height" => "2ft"}
2433
- ]}
2434
-
2435
- expected_output = {"foo|bar"=>"baz", "people" => [
2436
- {"age" => "25", "height" => "1ft"},
2437
- {"age" => "30", "height" => "2ft"}
2438
- ]}
2439
-
2440
- stub_opensearch
2441
- stub_opensearch_info
2442
- driver.run(default_tag: 'test') do
2443
- driver.feed(original_hash)
2444
- end
2445
- assert_equal expected_output, index_cmds[1]
2446
- end
2447
-
2448
- def test_nested_record_with_flattening_off
2449
- # flattening off by default
2450
-
2451
- original_hash = {"foo" => {"bar" => "baz"}}
2452
- expected_output = {"foo" => {"bar" => "baz"}}
2453
-
2454
- stub_opensearch
2455
- stub_opensearch_info
2456
- driver.run(default_tag: 'test') do
2457
- driver.feed(original_hash)
2458
- end
2459
- assert_equal expected_output, index_cmds[1]
2460
- end
2461
-
2462
- def test_makes_bulk_request
2463
- stub_opensearch
2464
- stub_opensearch_info
2465
- driver.run(default_tag: 'test') do
2466
- driver.feed(sample_record)
2467
- driver.feed(sample_record.merge('age' => 27))
2468
- end
2469
- assert_equal(4, index_cmds.count)
2470
- end
2471
-
2472
- def test_all_re
2473
- stub_opensearch
2474
- stub_opensearch_info
2475
- driver.run(default_tag: 'test') do
2476
- driver.feed(sample_record)
2477
- driver.feed(sample_record.merge('age' => 27))
2478
- end
2479
- assert_equal(26, index_cmds[1]['age'])
2480
- assert_equal(27, index_cmds[3]['age'])
2481
- end
2482
-
2483
- def test_writes_to_logstash_index
2484
- driver.configure("logstash_format true\n")
2485
- #
2486
- # This is 1 second past midnight in BST, so the UTC index should be the day before
2487
- dt = DateTime.new(2015, 6, 1, 0, 0, 1, "+01:00")
2488
- logstash_index = "logstash-2015.05.31"
2489
- stub_opensearch
2490
- stub_opensearch_info
2491
- driver.run(default_tag: 'test') do
2492
- driver.feed(dt.to_time.to_i, sample_record)
2493
- end
2494
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2495
- end
2496
-
2497
- def test_writes_to_logstash_non_utc_index
2498
- driver.configure("logstash_format true
2499
- utc_index false")
2500
- # When using `utc_index false` the index time will be the local day of
2501
- # ingestion time
2502
- time = Date.today.to_time
2503
- index = "logstash-#{time.strftime("%Y.%m.%d")}"
2504
- stub_opensearch
2505
- stub_opensearch_info
2506
- driver.run(default_tag: 'test') do
2507
- driver.feed(time.to_i, sample_record)
2508
- end
2509
- assert_equal(index, index_cmds.first['index']['_index'])
2510
- end
2511
-
2512
- def test_writes_to_logstash_index_with_specified_prefix
2513
- driver.configure("logstash_format true
2514
- logstash_prefix myprefix")
2515
- time = Time.parse Date.today.iso8601
2516
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
2517
- stub_opensearch
2518
- stub_opensearch_info
2519
- driver.run(default_tag: 'test') do
2520
- driver.feed(time.to_i, sample_record)
2521
- end
2522
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2523
- end
2524
-
2525
- def test_writes_to_logstash_index_with_specified_prefix_and_separator
2526
- separator = '_'
2527
- driver.configure("logstash_format true
2528
- logstash_prefix_separator #{separator}
2529
- logstash_prefix myprefix")
2530
- time = Time.parse Date.today.iso8601
2531
- logstash_index = "myprefix#{separator}#{time.getutc.strftime("%Y.%m.%d")}"
2532
- stub_opensearch
2533
- stub_opensearch_info
2534
- driver.run(default_tag: 'test') do
2535
- driver.feed(time.to_i, sample_record)
2536
- end
2537
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2538
- end
2539
-
2540
- class LogStashPrefixPlaceholdersTest < self
2541
- def test_writes_to_logstash_index_with_specified_prefix_and_tag_placeholder
2542
- driver.configure("logstash_format true
2543
- logstash_prefix myprefix-${tag}")
2544
- time = Time.parse Date.today.iso8601
2545
- logstash_index = "myprefix-test-#{time.getutc.strftime("%Y.%m.%d")}"
2546
- stub_opensearch
2547
- stub_opensearch_info
2548
- driver.run(default_tag: 'test') do
2549
- driver.feed(time.to_i, sample_record)
2550
- end
2551
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2552
- end
2553
-
2554
- def test_writes_to_logstash_index_with_specified_prefix_and_time_placeholder
2555
- driver.configure(Fluent::Config::Element.new(
2556
- 'ROOT', '', {
2557
- '@type' => 'opensearch',
2558
- 'logstash_format' => true,
2559
- 'logstash_prefix' => 'myprefix-%H',
2560
- }, [
2561
- Fluent::Config::Element.new('buffer', 'tag,time', {
2562
- 'chunk_keys' => ['tag', 'time'],
2563
- 'timekey' => 3600,
2564
- }, [])
2565
- ]
2566
- ))
2567
- time = Time.parse Date.today.iso8601
2568
- logstash_index = "myprefix-#{time.getutc.strftime("%H")}-#{time.getutc.strftime("%Y.%m.%d")}"
2569
- stub_opensearch
2570
- stub_opensearch_info
2571
- driver.run(default_tag: 'test') do
2572
- driver.feed(time.to_i, sample_record)
2573
- end
2574
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2575
- end
2576
-
2577
- def test_writes_to_logstash_index_with_specified_prefix_and_custom_key_placeholder
2578
- driver.configure(Fluent::Config::Element.new(
2579
- 'ROOT', '', {
2580
- '@type' => 'opensearch',
2581
- 'logstash_format' => true,
2582
- 'logstash_prefix' => 'myprefix-${pipeline_id}',
2583
- }, [
2584
- Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
2585
- ]
2586
- ))
2587
- time = Time.parse Date.today.iso8601
2588
- pipeline_id = "mypipeline"
2589
- logstash_index = "myprefix-#{pipeline_id}-#{time.getutc.strftime("%Y.%m.%d")}"
2590
- stub_opensearch
2591
- stub_opensearch_info
2592
- driver.run(default_tag: 'test') do
2593
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
2594
- end
2595
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2596
- end
2597
- end
2598
-
2599
- class LogStashDateformatPlaceholdersTest < self
2600
- def test_writes_to_logstash_index_with_specified_prefix_and_dateformat_placeholder_pattern_1
2601
- driver.configure(Fluent::Config::Element.new(
2602
- 'ROOT', '', {
2603
- '@type' => 'opensearch',
2604
- 'logstash_format' => true,
2605
- 'logstash_dateformat' => '${indexformat}',
2606
- 'logstash_prefix' => 'myprefix',
2607
- }, [
2608
- Fluent::Config::Element.new('buffer', 'tag,time,indexformat', {
2609
- 'chunk_keys' => ['tag', 'time', 'indexformat'],
2610
- 'timekey' => 3600,
2611
- }, [])
2612
- ]
2613
- ))
2614
- time = Time.parse Date.today.iso8601
2615
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
2616
- stub_opensearch
2617
- stub_opensearch_info
2618
- driver.run(default_tag: 'test') do
2619
- driver.feed(time.to_i, sample_record.merge('indexformat' => '%Y.%m.%d'))
2620
- end
2621
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2622
- end
2623
-
2624
- def test_writes_to_logstash_index_with_specified_prefix_and_dateformat_placeholder_pattern_2
2625
- driver.configure(Fluent::Config::Element.new(
2626
- 'ROOT', '', {
2627
- '@type' => 'opensearch',
2628
- 'logstash_format' => true,
2629
- 'logstash_dateformat' => '${indexformat}',
2630
- 'logstash_prefix' => 'myprefix',
2631
- }, [
2632
- Fluent::Config::Element.new('buffer', 'tag,time,indexformat', {
2633
- 'chunk_keys' => ['tag', 'time', 'indexformat'],
2634
- 'timekey' => 3600,
2635
- }, [])
2636
- ]
2637
- ))
2638
- time = Time.parse Date.today.iso8601
2639
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m")}"
2640
- stub_opensearch
2641
- stub_opensearch_info
2642
- driver.run(default_tag: 'test') do
2643
- driver.feed(time.to_i, sample_record.merge('indexformat' => '%Y.%m'))
2644
- end
2645
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2646
- end
2647
- end
2648
-
2649
- class HostnamePlaceholders < self
2650
- def test_writes_to_extracted_host
2651
- driver.configure("host ${tag}\n")
2652
- time = Time.parse Date.today.iso8601
2653
- elastic_request = stub_opensearch("http://extracted-host:9200/_bulk")
2654
- stub_opensearch_info("http://extracted-host:9200/")
2655
- driver.run(default_tag: 'extracted-host') do
2656
- driver.feed(time.to_i, sample_record)
2657
- end
2658
- assert_requested(elastic_request)
2659
- end
2660
-
2661
- def test_writes_to_multi_hosts_with_placeholders
2662
- hosts = [['${tag}', 9201], ['192.168.33.51', 9201], ['192.168.33.52', 9201]]
2663
- hosts_string = hosts.map {|x| "#{x[0]}:#{x[1]}"}.compact.join(',')
2664
-
2665
- driver.configure("hosts #{hosts_string}")
2666
-
2667
- hosts.each do |host_info|
2668
- host, port = host_info
2669
- host = "extracted-host" if host == '${tag}'
2670
- stub_opensearch_with_store_index_command_counts("http://#{host}:#{port}/_bulk")
2671
- stub_opensearch_info("http://#{host}:#{port}")
2672
- end
2673
-
2674
- driver.run(default_tag: 'extracted-host') do
2675
- 1000.times do
2676
- driver.feed(sample_record.merge('age'=>rand(100)))
2677
- end
2678
- end
2679
-
2680
- # @note: we cannot make multi chunks with options (flush_interval, buffer_chunk_limit)
2681
- # it's Fluentd test driver's constraint
2682
- # so @index_command_counts.size is always 1
2683
-
2684
- assert(@index_command_counts.size > 0, "not working with hosts options")
2685
-
2686
- total = 0
2687
- @index_command_counts.each do |url, count|
2688
- total += count
2689
- end
2690
- assert_equal(2000, total)
2691
- end
2692
-
2693
- def test_writes_to_extracted_host_with_time_placeholder
2694
- driver.configure(Fluent::Config::Element.new(
2695
- 'ROOT', '', {
2696
- '@type' => 'elasticsearch',
2697
- 'host' => 'host-%Y%m%d',
2698
- }, [
2699
- Fluent::Config::Element.new('buffer', 'tag,time', {
2700
- 'chunk_keys' => ['tag', 'time'],
2701
- 'timekey' => 3600,
2702
- }, [])
2703
- ]
2704
- ))
2705
- stub_opensearch
2706
- stub_opensearch_info
2707
- time = Time.parse Date.today.iso8601
2708
- elastic_request = stub_opensearch("http://host-#{time.utc.strftime('%Y%m%d')}:9200/_bulk")
2709
- stub_opensearch_info("http://host-#{time.utc.strftime('%Y%m%d')}:9200/")
2710
- driver.run(default_tag: 'test') do
2711
- driver.feed(time.to_i, sample_record)
2712
- end
2713
- assert_requested(elastic_request)
2714
- end
2715
-
2716
- def test_writes_to_extracted_host_with_custom_key_placeholder
2717
- driver.configure(Fluent::Config::Element.new(
2718
- 'ROOT', '', {
2719
- '@type' => 'opensearch',
2720
- 'host' => 'myhost-${pipeline_id}',
2721
- }, [
2722
- Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
2723
- ]
2724
- ))
2725
- time = Time.parse Date.today.iso8601
2726
- first_pipeline_id = "1"
2727
- second_pipeline_id = "2"
2728
- first_request = stub_opensearch("http://myhost-1:9200/_bulk")
2729
- second_request = stub_opensearch("http://myhost-2:9200/_bulk")
2730
- stub_opensearch_info("http://myhost-1:9200/")
2731
- stub_opensearch_info("http://myhost-2:9200/")
2732
- driver.run(default_tag: 'test') do
2733
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => first_pipeline_id}))
2734
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => second_pipeline_id}))
2735
- end
2736
- assert_requested(first_request)
2737
- assert_requested(second_request)
2738
- end
2739
-
2740
- def test_writes_to_extracted_host_with_placeholder_replaced_in_exception_message
2741
- driver.configure(Fluent::Config::Element.new(
2742
- 'ROOT', '', {
2743
- '@type' => 'opensearch',
2744
- 'host' => 'myhost-${pipeline_id}',
2745
- }, [
2746
- Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
2747
- ]
2748
- ))
2749
- time = Time.parse Date.today.iso8601
2750
- pipeline_id = "1"
2751
- request = stub_opensearch_unavailable("http://myhost-1:9200/_bulk")
2752
- stub_opensearch_info("http://myhost-1:9200/")
2753
- exception = assert_raise(Fluent::Plugin::OpenSearchOutput::RecoverableRequestFailure) {
2754
- driver.run(default_tag: 'test') do
2755
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
2756
- end
2757
- }
2758
- assert_equal("could not push logs to OpenSearch cluster ({:host=>\"myhost-1\", :port=>9200, :scheme=>\"http\"}): [503] ", exception.message)
2759
- end
2760
- end
2761
-
2762
- def test_writes_to_logstash_index_with_specified_prefix_uppercase
2763
- driver.configure("logstash_format true
2764
- logstash_prefix MyPrefix")
2765
- time = Time.parse Date.today.iso8601
2766
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
2767
- stub_opensearch
2768
- stub_opensearch_info
2769
- driver.run(default_tag: 'test') do
2770
- driver.feed(time.to_i, sample_record)
2771
- end
2772
- # Allthough logstash_prefix has upper-case characters,
2773
- # it should be set as lower-case when sent to elasticsearch.
2774
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2775
- end
2776
-
2777
- def test_writes_to_logstash_index_with_specified_dateformat
2778
- driver.configure("logstash_format true
2779
- logstash_dateformat %Y.%m")
2780
- time = Time.parse Date.today.iso8601
2781
- logstash_index = "logstash-#{time.getutc.strftime("%Y.%m")}"
2782
- stub_opensearch
2783
- stub_opensearch_info
2784
- driver.run(default_tag: 'test') do
2785
- driver.feed(time.to_i, sample_record)
2786
- end
2787
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2788
- end
2789
-
2790
- def test_writes_to_logstash_index_with_specified_prefix_and_dateformat
2791
- driver.configure("logstash_format true
2792
- logstash_prefix myprefix
2793
- logstash_dateformat %Y.%m")
2794
- time = Time.parse Date.today.iso8601
2795
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m")}"
2796
- stub_opensearch
2797
- stub_opensearch_info
2798
- driver.run(default_tag: 'test') do
2799
- driver.feed(time.to_i, sample_record)
2800
- end
2801
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
2802
- end
2803
-
2804
- def test_error_if_tag_not_in_chunk_keys
2805
- assert_raise(Fluent::ConfigError) {
2806
- config = %{
2807
- <buffer foo>
2808
- </buffer>
2809
- }
2810
- driver.configure(config)
2811
- }
2812
- end
2813
-
2814
- def test_can_use_custom_chunk_along_with_tag
2815
- config = %{
2816
- <buffer tag, foo>
2817
- </buffer>
2818
- }
2819
- driver.configure(config)
2820
- end
2821
-
2822
- def test_doesnt_add_logstash_timestamp_by_default
2823
- stub_opensearch
2824
- stub_opensearch_info
2825
- driver.run(default_tag: 'test') do
2826
- driver.feed(sample_record)
2827
- end
2828
- assert_nil(index_cmds[1]['@timestamp'])
2829
- end
2830
-
2831
- def test_adds_timestamp_when_logstash
2832
- driver.configure("logstash_format true\n")
2833
- stub_opensearch
2834
- stub_opensearch_info
2835
- ts = DateTime.now
2836
- time = Fluent::EventTime.from_time(ts.to_time)
2837
- driver.run(default_tag: 'test') do
2838
- driver.feed(time, sample_record)
2839
- end
2840
- assert(index_cmds[1].has_key? '@timestamp')
2841
- assert_equal(ts.iso8601(9), index_cmds[1]['@timestamp'])
2842
- end
2843
-
2844
- def test_adds_timestamp_when_include_timestamp
2845
- driver.configure("include_timestamp true\n")
2846
- stub_opensearch
2847
- stub_opensearch_info
2848
- ts = DateTime.now
2849
- time = Fluent::EventTime.from_time(ts.to_time)
2850
- driver.run(default_tag: 'test') do
2851
- driver.feed(time, sample_record)
2852
- end
2853
- assert(index_cmds[1].has_key? '@timestamp')
2854
- assert_equal(ts.iso8601(9), index_cmds[1]['@timestamp'])
2855
- end
2856
-
2857
- def test_uses_custom_timestamp_when_included_in_record
2858
- driver.configure("logstash_format true\n")
2859
- stub_opensearch
2860
- stub_opensearch_info
2861
- ts = DateTime.new(2001,2,3).iso8601
2862
- driver.run(default_tag: 'test') do
2863
- driver.feed(sample_record.merge!('@timestamp' => ts))
2864
- end
2865
- assert(index_cmds[1].has_key? '@timestamp')
2866
- assert_equal(ts, index_cmds[1]['@timestamp'])
2867
- end
2868
-
2869
- def test_uses_custom_timestamp_when_included_in_record_without_logstash
2870
- driver.configure("include_timestamp true\n")
2871
- stub_opensearch
2872
- stub_opensearch_info
2873
- ts = DateTime.new(2001,2,3).iso8601
2874
- driver.run(default_tag: 'test') do
2875
- driver.feed(sample_record.merge!('@timestamp' => ts))
2876
- end
2877
- assert(index_cmds[1].has_key? '@timestamp')
2878
- assert_equal(ts, index_cmds[1]['@timestamp'])
2879
- end
2880
-
2881
- def test_uses_custom_time_key
2882
- driver.configure("logstash_format true
2883
- time_key vtm\n")
2884
- stub_opensearch
2885
- stub_opensearch_info
2886
- ts = DateTime.new(2001,2,3).iso8601(9)
2887
- driver.run(default_tag: 'test') do
2888
- driver.feed(sample_record.merge!('vtm' => ts))
2889
- end
2890
- assert(index_cmds[1].has_key? '@timestamp')
2891
- assert_equal(ts, index_cmds[1]['@timestamp'])
2892
- end
2893
-
2894
- def test_uses_custom_time_key_with_float_record
2895
- driver.configure("logstash_format true
2896
- time_precision 3
2897
- time_key vtm\n")
2898
- stub_opensearch
2899
- stub_opensearch_info
2900
- time = Time.now
2901
- float_time = time.to_f
2902
- driver.run(default_tag: 'test') do
2903
- driver.feed(sample_record.merge!('vtm' => float_time))
2904
- end
2905
- assert(index_cmds[1].has_key? '@timestamp')
2906
- assert_equal(time.to_datetime.iso8601(3), index_cmds[1]['@timestamp'])
2907
- end
2908
-
2909
- def test_uses_custom_time_key_with_format
2910
- driver.configure("logstash_format true
2911
- time_key_format %Y-%m-%d %H:%M:%S.%N%z
2912
- time_key vtm\n")
2913
- stub_opensearch
2914
- stub_opensearch_info
2915
- ts = "2001-02-03 13:14:01.673+02:00"
2916
- driver.run(default_tag: 'test') do
2917
- driver.feed(sample_record.merge!('vtm' => ts))
2918
- end
2919
- assert(index_cmds[1].has_key? '@timestamp')
2920
- assert_equal(DateTime.parse(ts).iso8601(9), index_cmds[1]['@timestamp'])
2921
- assert_equal("logstash-2001.02.03", index_cmds[0]['index']['_index'])
2922
- end
2923
-
2924
- def test_uses_custom_time_key_with_float_record_and_format
2925
- driver.configure("logstash_format true
2926
- time_key_format %Y-%m-%d %H:%M:%S.%N%z
2927
- time_key vtm\n")
2928
- stub_opensearch
2929
- stub_opensearch_info
2930
- ts = "2001-02-03 13:14:01.673+02:00"
2931
- time = Time.parse(ts)
2932
- current_zone_offset = Time.new(2001, 02, 03).to_datetime.offset
2933
- float_time = time.to_f
2934
- driver.run(default_tag: 'test') do
2935
- driver.feed(sample_record.merge!('vtm' => float_time))
2936
- end
2937
- assert(index_cmds[1].has_key? '@timestamp')
2938
- assert_equal(DateTime.parse(ts).new_offset(current_zone_offset).iso8601(9), index_cmds[1]['@timestamp'])
2939
- end
2940
-
2941
- def test_uses_custom_time_key_with_format_without_logstash
2942
- driver.configure("include_timestamp true
2943
- index_name test
2944
- time_key_format %Y-%m-%d %H:%M:%S.%N%z
2945
- time_key vtm\n")
2946
- stub_opensearch
2947
- stub_opensearch_info
2948
- ts = "2001-02-03 13:14:01.673+02:00"
2949
- driver.run(default_tag: 'test') do
2950
- driver.feed(sample_record.merge!('vtm' => ts))
2951
- end
2952
- assert(index_cmds[1].has_key? '@timestamp')
2953
- assert_equal(DateTime.parse(ts).iso8601(9), index_cmds[1]['@timestamp'])
2954
- assert_equal("test", index_cmds[0]['index']['_index'])
2955
- end
2956
-
2957
- def test_uses_custom_time_key_exclude_timekey
2958
- driver.configure("logstash_format true
2959
- time_key vtm
2960
- time_key_exclude_timestamp true\n")
2961
- stub_opensearch
2962
- stub_opensearch_info
2963
- ts = DateTime.new(2001,2,3).iso8601
2964
- driver.run(default_tag: 'test') do
2965
- driver.feed(sample_record.merge!('vtm' => ts))
2966
- end
2967
- assert(!index_cmds[1].key?('@timestamp'), '@timestamp should be messing')
2968
- end
2969
-
2970
- def test_uses_custom_time_key_format
2971
- driver.configure("logstash_format true
2972
- time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n")
2973
- stub_opensearch
2974
- stub_opensearch_info
2975
- ts = "2001-02-03T13:14:01.673+02:00"
2976
- driver.run(default_tag: 'test') do
2977
- driver.feed(sample_record.merge!('@timestamp' => ts))
2978
- end
2979
- assert_equal("logstash-2001.02.03", index_cmds[0]['index']['_index'])
2980
- assert(index_cmds[1].has_key? '@timestamp')
2981
- assert_equal(ts, index_cmds[1]['@timestamp'])
2982
- end
2983
-
2984
- def test_uses_custom_time_key_format_without_logstash
2985
- driver.configure("include_timestamp true
2986
- index_name test
2987
- time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n")
2988
- stub_opensearch
2989
- stub_opensearch_info
2990
- ts = "2001-02-03T13:14:01.673+02:00"
2991
- driver.run(default_tag: 'test') do
2992
- driver.feed(sample_record.merge!('@timestamp' => ts))
2993
- end
2994
- assert_equal("test", index_cmds[0]['index']['_index'])
2995
- assert(index_cmds[1].has_key? '@timestamp')
2996
- assert_equal(ts, index_cmds[1]['@timestamp'])
2997
- end
2998
-
2999
- data(:default => nil,
3000
- :custom_tag => 'es_plugin.output.time.error')
3001
- def test_uses_custom_time_key_format_logs_an_error(tag_for_error)
3002
- tag_config = tag_for_error ? "time_parse_error_tag #{tag_for_error}" : ''
3003
- tag_for_error = 'opensearch_plugin.output.time.error' if tag_for_error.nil?
3004
- driver.configure("logstash_format true
3005
- time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n#{tag_config}\n")
3006
- stub_opensearch
3007
- stub_opensearch_info
3008
-
3009
- ts = "2001/02/03 13:14:01,673+02:00"
3010
- index = "logstash-#{Time.now.getutc.strftime("%Y.%m.%d")}"
3011
-
3012
- flexmock(driver.instance.router).should_receive(:emit_error_event)
3013
- .with(tag_for_error, Fluent::EventTime, Hash, ArgumentError).once
3014
- driver.run(default_tag: 'test') do
3015
- driver.feed(sample_record.merge!('@timestamp' => ts))
3016
- end
3017
-
3018
- assert_equal(index, index_cmds[0]['index']['_index'])
3019
- assert(index_cmds[1].has_key? '@timestamp')
3020
- assert_equal(ts, index_cmds[1]['@timestamp'])
3021
- end
3022
-
3023
-
3024
- def test_uses_custom_time_key_format_obscure_format
3025
- driver.configure("logstash_format true
3026
- time_key_format %a %b %d %H:%M:%S %Z %Y\n")
3027
- stub_opensearch
3028
- stub_opensearch_info
3029
- ts = "Thu Nov 29 14:33:20 GMT 2001"
3030
- driver.run(default_tag: 'test') do
3031
- driver.feed(sample_record.merge!('@timestamp' => ts))
3032
- end
3033
- assert_equal("logstash-2001.11.29", index_cmds[0]['index']['_index'])
3034
- assert(index_cmds[1].has_key? '@timestamp')
3035
- assert_equal(ts, index_cmds[1]['@timestamp'])
3036
- end
3037
-
3038
- def test_uses_nanosecond_precision_by_default
3039
- driver.configure("logstash_format true\n")
3040
- stub_opensearch
3041
- stub_opensearch_info
3042
- time = Fluent::EventTime.new(Time.now.to_i, 123456789)
3043
- driver.run(default_tag: 'test') do
3044
- driver.feed(time, sample_record)
3045
- end
3046
- assert(index_cmds[1].has_key? '@timestamp')
3047
- assert_equal(Time.at(time).iso8601(9), index_cmds[1]['@timestamp'])
3048
- end
3049
-
3050
- def test_uses_subsecond_precision_when_configured
3051
- driver.configure("logstash_format true
3052
- time_precision 3\n")
3053
- stub_opensearch
3054
- stub_opensearch_info
3055
- time = Fluent::EventTime.new(Time.now.to_i, 123456789)
3056
- driver.run(default_tag: 'test') do
3057
- driver.feed(time, sample_record)
3058
- end
3059
- assert(index_cmds[1].has_key? '@timestamp')
3060
- assert_equal(Time.at(time).iso8601(3), index_cmds[1]['@timestamp'])
3061
- end
3062
-
3063
- def test_doesnt_add_tag_key_by_default
3064
- stub_opensearch
3065
- stub_opensearch_info
3066
- driver.run(default_tag: 'test') do
3067
- driver.feed(sample_record)
3068
- end
3069
- assert_nil(index_cmds[1]['tag'])
3070
- end
3071
-
3072
- def test_adds_tag_key_when_configured
3073
- driver.configure("include_tag_key true\n")
3074
- stub_opensearch
3075
- stub_opensearch_info
3076
- driver.run(default_tag: 'mytag') do
3077
- driver.feed(sample_record)
3078
- end
3079
- assert(index_cmds[1].has_key?('tag'))
3080
- assert_equal('mytag', index_cmds[1]['tag'])
3081
- end
3082
-
3083
- def test_adds_id_key_when_configured
3084
- driver.configure("id_key request_id\n")
3085
- stub_opensearch
3086
- stub_opensearch_info
3087
- driver.run(default_tag: 'test') do
3088
- driver.feed(sample_record)
3089
- end
3090
- assert_equal('42', index_cmds[0]['index']['_id'])
3091
- end
3092
-
3093
- class NestedIdKeyTest < self
3094
- def test_adds_nested_id_key_with_dot
3095
- driver.configure("id_key nested.request_id\n")
3096
- stub_opensearch
3097
- stub_opensearch_info
3098
- driver.run(default_tag: 'test') do
3099
- driver.feed(nested_sample_record)
3100
- end
3101
- assert_equal('42', index_cmds[0]['index']['_id'])
3102
- end
3103
-
3104
- def test_adds_nested_id_key_with_dollar_dot
3105
- driver.configure("id_key $.nested.request_id\n")
3106
- stub_opensearch
3107
- stub_opensearch_info
3108
- driver.run(default_tag: 'test') do
3109
- driver.feed(nested_sample_record)
3110
- end
3111
- assert_equal('42', index_cmds[0]['index']['_id'])
3112
- end
3113
-
3114
- def test_adds_nested_id_key_with_bracket
3115
- driver.configure("id_key $['nested']['request_id']\n")
3116
- stub_opensearch
3117
- stub_opensearch_info
3118
- driver.run(default_tag: 'test') do
3119
- driver.feed(nested_sample_record)
3120
- end
3121
- assert_equal('42', index_cmds[0]['index']['_id'])
3122
- end
3123
- end
3124
-
3125
- def test_doesnt_add_id_key_if_missing_when_configured
3126
- driver.configure("id_key another_request_id\n")
3127
- stub_opensearch
3128
- stub_opensearch_info
3129
- driver.run(default_tag: 'test') do
3130
- driver.feed(sample_record)
3131
- end
3132
- assert(!index_cmds[0]['index'].has_key?('_id'))
3133
- end
3134
-
3135
- def test_adds_id_key_when_not_configured
3136
- stub_opensearch
3137
- stub_opensearch_info
3138
- driver.run(default_tag: 'test') do
3139
- driver.feed(sample_record)
3140
- end
3141
- assert(!index_cmds[0]['index'].has_key?('_id'))
3142
- end
3143
-
3144
- def test_adds_parent_key_when_configured
3145
- driver.configure("parent_key parent_id\n")
3146
- stub_opensearch
3147
- stub_opensearch_info
3148
- driver.run(default_tag: 'test') do
3149
- driver.feed(sample_record)
3150
- end
3151
- assert_equal('parent', index_cmds[0]['index']['_parent'])
3152
- end
3153
-
3154
- class NestedParentKeyTest < self
3155
- def test_adds_nested_parent_key_with_dot
3156
- driver.configure("parent_key nested.parent_id\n")
3157
- stub_opensearch
3158
- stub_opensearch_info
3159
- driver.run(default_tag: 'test') do
3160
- driver.feed(nested_sample_record)
3161
- end
3162
- assert_equal('parent', index_cmds[0]['index']['_parent'])
3163
- end
3164
-
3165
- def test_adds_nested_parent_key_with_dollar_dot
3166
- driver.configure("parent_key $.nested.parent_id\n")
3167
- stub_opensearch
3168
- stub_opensearch_info
3169
- driver.run(default_tag: 'test') do
3170
- driver.feed(nested_sample_record)
3171
- end
3172
- assert_equal('parent', index_cmds[0]['index']['_parent'])
3173
- end
3174
-
3175
- def test_adds_nested_parent_key_with_bracket
3176
- driver.configure("parent_key $['nested']['parent_id']\n")
3177
- stub_opensearch
3178
- stub_opensearch_info
3179
- driver.run(default_tag: 'test') do
3180
- driver.feed(nested_sample_record)
3181
- end
3182
- assert_equal('parent', index_cmds[0]['index']['_parent'])
3183
- end
3184
- end
3185
-
3186
- def test_doesnt_add_parent_key_if_missing_when_configured
3187
- driver.configure("parent_key another_parent_id\n")
3188
- stub_opensearch
3189
- stub_opensearch_info
3190
- driver.run(default_tag: 'test') do
3191
- driver.feed(sample_record)
3192
- end
3193
- assert(!index_cmds[0]['index'].has_key?('_parent'))
3194
- end
3195
-
3196
- def test_adds_parent_key_when_not_configured
3197
- stub_opensearch
3198
- stub_opensearch_info
3199
- driver.run(default_tag: 'test') do
3200
- driver.feed(sample_record)
3201
- end
3202
- assert(!index_cmds[0]['index'].has_key?('_parent'))
3203
- end
3204
-
3205
- class AddsRoutingKeyWhenConfiguredTest < self
3206
- def test_os1
3207
- driver("routing_key routing_id\n", 1)
3208
- stub_opensearch
3209
- stub_opensearch_info
3210
- driver.run(default_tag: 'test') do
3211
- driver.feed(sample_record)
3212
- end
3213
- assert_equal('routing', index_cmds[0]['index']['routing'])
3214
- end
3215
- end
3216
-
3217
- class NestedRoutingKeyTest < self
3218
- def test_adds_nested_routing_key_with_dot
3219
- driver.configure("routing_key nested.routing_id\n")
3220
- stub_opensearch
3221
- stub_opensearch_info
3222
- driver.run(default_tag: 'test') do
3223
- driver.feed(nested_sample_record)
3224
- end
3225
- assert_equal('routing', index_cmds[0]['index']['routing'])
3226
- end
3227
-
3228
- def test_adds_nested_routing_key_with_dollar_dot
3229
- driver.configure("routing_key $.nested.routing_id\n")
3230
- stub_opensearch
3231
- stub_opensearch_info
3232
- driver.run(default_tag: 'test') do
3233
- driver.feed(nested_sample_record)
3234
- end
3235
- assert_equal('routing', index_cmds[0]['index']['routing'])
3236
- end
3237
-
3238
- def test_adds_nested_routing_key_with_bracket
3239
- driver.configure("routing_key $['nested']['routing_id']\n")
3240
- stub_opensearch
3241
- stub_opensearch_info
3242
- driver.run(default_tag: 'test') do
3243
- driver.feed(nested_sample_record)
3244
- end
3245
- assert_equal('routing', index_cmds[0]['index']['routing'])
3246
- end
3247
- end
3248
-
3249
- def test_doesnt_add_routing_key_if_missing_when_configured
3250
- driver.configure("routing_key another_routing_id\n")
3251
- stub_opensearch
3252
- stub_opensearch_info
3253
- driver.run(default_tag: 'test') do
3254
- driver.feed(sample_record)
3255
- end
3256
- assert(!index_cmds[0]['index'].has_key?('_routing'))
3257
- end
3258
-
3259
- def test_adds_routing_key_when_not_configured
3260
- stub_opensearch
3261
- stub_opensearch_info
3262
- driver.run(default_tag: 'test') do
3263
- driver.feed(sample_record)
3264
- end
3265
- assert(!index_cmds[0]['index'].has_key?('_routing'))
3266
- end
3267
-
3268
- def test_remove_one_key
3269
- driver.configure("remove_keys key1\n")
3270
- stub_opensearch
3271
- stub_opensearch_info
3272
- driver.run(default_tag: 'test') do
3273
- driver.feed(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
3274
- end
3275
- assert(!index_cmds[1].has_key?('key1'))
3276
- assert(index_cmds[1].has_key?('key2'))
3277
- end
3278
-
3279
- def test_remove_multi_keys
3280
- driver.configure("remove_keys key1, key2\n")
3281
- stub_opensearch
3282
- stub_opensearch_info
3283
- driver.run(default_tag: 'test') do
3284
- driver.feed(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
3285
- end
3286
- assert(!index_cmds[1].has_key?('key1'))
3287
- assert(!index_cmds[1].has_key?('key2'))
3288
- end
3289
-
3290
- def test_request_error
3291
- stub_opensearch_info
3292
- stub_opensearch_unavailable
3293
- assert_raise(Fluent::Plugin::OpenSearchOutput::RecoverableRequestFailure) {
3294
- driver.run(default_tag: 'test', shutdown: false) do
3295
- driver.feed(sample_record)
3296
- end
3297
- }
3298
- end
3299
-
3300
- def test_request_forever
3301
- omit("retry_forever test is unstable.") if ENV["CI"]
3302
- stub_opensearch
3303
- stub_opensearch_info
3304
- driver.configure(Fluent::Config::Element.new(
3305
- 'ROOT', '', {
3306
- '@type' => 'opensearch',
3307
- }, [
3308
- Fluent::Config::Element.new('buffer', '', {
3309
- 'retry_forever' => true
3310
- }, [])
3311
- ]
3312
- ))
3313
- stub_opensearch_timeout
3314
- assert_raise(Timeout::Error) {
3315
- driver.run(default_tag: 'test', timeout: 10, force_flush_retry: true) do
3316
- driver.feed(sample_record)
3317
- end
3318
- }
3319
- end
3320
-
3321
- def test_connection_failed
3322
- connection_resets = 0
3323
-
3324
- stub_request(:post, "http://localhost:9200/_bulk").with do |req|
3325
- connection_resets += 1
3326
- raise Faraday::ConnectionFailed, "Test message"
3327
- end
3328
- stub_opensearch_info
3329
-
3330
- assert_raise(Fluent::Plugin::OpenSearchOutput::RecoverableRequestFailure) {
3331
- driver.run(default_tag: 'test', shutdown: false) do
3332
- driver.feed(sample_record)
3333
- end
3334
- }
3335
- assert_equal(1, connection_resets)
3336
- end
3337
-
3338
- def test_reconnect_on_error_enabled
3339
- connection_resets = 0
3340
-
3341
- stub_request(:post, "http://localhost:9200/_bulk").with do |req|
3342
- connection_resets += 1
3343
- raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
3344
- end
3345
- stub_opensearch_info
3346
-
3347
- driver.configure("reconnect_on_error true\n")
3348
-
3349
- assert_raise(Fluent::Plugin::OpenSearchOutput::RecoverableRequestFailure) {
3350
- driver.run(default_tag: 'test', shutdown: false) do
3351
- driver.feed(sample_record)
3352
- end
3353
- }
3354
-
3355
- assert_raise(Timeout::Error) {
3356
- driver.run(default_tag: 'test', shutdown: false) do
3357
- driver.feed(sample_record)
3358
- end
3359
- }
3360
- # FIXME: Consider keywords arguments in #run and how to test this later.
3361
- # Because v0.14 test driver does not have 1 to 1 correspondence between #run and #flush in tests.
3362
- assert_equal(1, connection_resets)
3363
- end
3364
-
3365
- def test_reconnect_on_error_disabled
3366
- connection_resets = 0
3367
-
3368
- stub_request(:post, "http://localhost:9200/_bulk").with do |req|
3369
- connection_resets += 1
3370
- raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
3371
- end
3372
- stub_opensearch_info
3373
-
3374
- driver.configure("reconnect_on_error false\n")
3375
-
3376
- assert_raise(Fluent::Plugin::OpenSearchOutput::RecoverableRequestFailure) {
3377
- driver.run(default_tag: 'test', shutdown: false) do
3378
- driver.feed(sample_record)
3379
- end
3380
- }
3381
-
3382
- assert_raise(Timeout::Error) {
3383
- driver.run(default_tag: 'test', shutdown: false) do
3384
- driver.feed(sample_record)
3385
- end
3386
- }
3387
- assert_equal(1, connection_resets)
3388
- end
3389
-
3390
- def test_bulk_error_retags_when_configured
3391
- driver.configure("retry_tag retry\n")
3392
- stub_request(:post, 'http://localhost:9200/_bulk')
3393
- .to_return(lambda do |req|
3394
- { :status => 200,
3395
- :headers => { 'Content-Type' => 'json' },
3396
- :body => %({
3397
- "took" : 1,
3398
- "errors" : true,
3399
- "items" : [
3400
- {
3401
- "create" : {
3402
- "_index" : "foo",
3403
- "_type" : "bar",
3404
- "_id" : "abc",
3405
- "status" : 500,
3406
- "error" : {
3407
- "type" : "some unrecognized type",
3408
- "reason":"some error to cause version mismatch"
3409
- }
3410
- }
3411
- }
3412
- ]
3413
- })
3414
- }
3415
- end)
3416
- stub_opensearch_info
3417
-
3418
- driver.run(default_tag: 'test') do
3419
- driver.feed(1, sample_record)
3420
- end
3421
-
3422
- assert_equal [['retry', 1, sample_record]], driver.events
3423
- end
3424
-
3425
- class FulfilledBufferRetryStreamTest < self
3426
- def test_bulk_error_retags_with_error_when_configured_and_fullfilled_buffer
3427
- def create_driver(conf='', os_version=1, client_version="\"1.0\"")
3428
- @client_version ||= client_version
3429
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
3430
- def retry_stream_retryable?
3431
- false
3432
- end
3433
- CODE
3434
- # For request stub to detect compatibility.
3435
- @os_version ||= os_version
3436
- @client_version ||= client_version
3437
- if @os_version
3438
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
3439
- def detect_os_major_version
3440
- #{@os_version}
3441
- end
3442
- CODE
3443
- end
3444
- Fluent::Plugin::OpenSearchOutput.module_eval(<<-CODE)
3445
- def client_library_version
3446
- #{@client_version}
3447
- end
3448
- CODE
3449
- Fluent::Test::Driver::Output.new(Fluent::Plugin::OpenSearchOutput).configure(conf)
3450
- end
3451
- driver = create_driver("retry_tag retry\n")
3452
- stub_request(:post, 'http://localhost:9200/_bulk')
3453
- .to_return(lambda do |req|
3454
- { :status => 200,
3455
- :headers => { 'Content-Type' => 'json' },
3456
- :body => %({
3457
- "took" : 1,
3458
- "errors" : true,
3459
- "items" : [
3460
- {
3461
- "create" : {
3462
- "_index" : "foo",
3463
- "_type" : "bar",
3464
- "_id" : "abc1",
3465
- "status" : 403,
3466
- "error" : {
3467
- "type" : "cluster_block_exception",
3468
- "reason":"index [foo] blocked by: [FORBIDDEN/8/index write (api)]"
3469
- }
3470
- }
3471
- },
3472
- {
3473
- "create" : {
3474
- "_index" : "foo",
3475
- "_type" : "bar",
3476
- "_id" : "abc2",
3477
- "status" : 403,
3478
- "error" : {
3479
- "type" : "cluster_block_exception",
3480
- "reason":"index [foo] blocked by: [FORBIDDEN/8/index write (api)]"
3481
- }
3482
- }
3483
- }
3484
- ]
3485
- })
3486
- }
3487
- end)
3488
- stub_opensearch_info
3489
-
3490
- # Check buffer fulfillment condition
3491
- assert_raise(Fluent::Plugin::OpenSearchOutput::RetryStreamEmitFailure) do
3492
- driver.run(default_tag: 'test') do
3493
- driver.feed(1, sample_record)
3494
- driver.feed(1, sample_record)
3495
- end
3496
- end
3497
-
3498
- assert_equal [], driver.events
3499
- end
3500
- end
3501
-
3502
- def test_create_should_write_records_with_ids_and_skip_those_without
3503
- driver.configure("write_operation create\nid_key my_id\n@log_level debug")
3504
- stub_request(:post, 'http://localhost:9200/_bulk')
3505
- .to_return(lambda do |req|
3506
- { :status => 200,
3507
- :headers => { 'Content-Type' => 'json' },
3508
- :body => %({
3509
- "took" : 1,
3510
- "errors" : true,
3511
- "items" : [
3512
- {
3513
- "create" : {
3514
- "_index" : "foo",
3515
- "_type" : "bar",
3516
- "_id" : "abc"
3517
- }
3518
- },
3519
- {
3520
- "create" : {
3521
- "_index" : "foo",
3522
- "_type" : "bar",
3523
- "_id" : "xyz",
3524
- "status" : 500,
3525
- "error" : {
3526
- "type" : "some unrecognized type",
3527
- "reason":"some error to cause version mismatch"
3528
- }
3529
- }
3530
- }
3531
- ]
3532
- })
3533
- }
3534
- end)
3535
- stub_opensearch_info
3536
-
3537
- sample_record1 = sample_record('my_id' => 'abc')
3538
- sample_record4 = sample_record('my_id' => 'xyz')
3539
-
3540
- driver.run(default_tag: 'test') do
3541
- driver.feed(1, sample_record1)
3542
- driver.feed(2, sample_record)
3543
- driver.feed(3, sample_record)
3544
- driver.feed(4, sample_record4)
3545
- end
3546
-
3547
- logs = driver.logs
3548
- # one record succeeded while the other should be 'retried'
3549
- assert_equal [['test', 4, sample_record4]], driver.events
3550
- assert_logs_include(logs, /(Dropping record)/, 2)
3551
- end
3552
-
3553
- def test_create_should_write_records_with_ids_and_emit_those_without
3554
- driver.configure("write_operation create\nid_key my_id\nemit_error_for_missing_id true\n@log_level debug")
3555
- stub_request(:post, 'http://localhost:9200/_bulk')
3556
- .to_return(lambda do |req|
3557
- { :status => 200,
3558
- :headers => { 'Content-Type' => 'json' },
3559
- :body => %({
3560
- "took" : 1,
3561
- "errors" : true,
3562
- "items" : [
3563
- {
3564
- "create" : {
3565
- "_index" : "foo",
3566
- "_type" : "bar",
3567
- "_id" : "abc"
3568
- }
3569
- },
3570
- {
3571
- "create" : {
3572
- "_index" : "foo",
3573
- "_type" : "bar",
3574
- "_id" : "xyz",
3575
- "status" : 500,
3576
- "error" : {
3577
- "type" : "some unrecognized type",
3578
- "reason":"some error to cause version mismatch"
3579
- }
3580
- }
3581
- }
3582
- ]
3583
- })
3584
- }
3585
- end)
3586
- stub_opensearch_info
3587
-
3588
- sample_record1 = sample_record('my_id' => 'abc')
3589
- sample_record4 = sample_record('my_id' => 'xyz')
3590
-
3591
- driver.run(default_tag: 'test') do
3592
- driver.feed(1, sample_record1)
3593
- driver.feed(2, sample_record)
3594
- driver.feed(3, sample_record)
3595
- driver.feed(4, sample_record4)
3596
- end
3597
-
3598
- error_log = driver.error_events.map {|e| e.last.message }
3599
- # one record succeeded while the other should be 'retried'
3600
- assert_equal [['test', 4, sample_record4]], driver.events
3601
- assert_logs_include(error_log, /(Missing '_id' field)/, 2)
3602
- end
3603
-
3604
- def test_bulk_error
3605
- stub_request(:post, 'http://localhost:9200/_bulk')
3606
- .to_return(lambda do |req|
3607
- { :status => 200,
3608
- :headers => { 'Content-Type' => 'json' },
3609
- :body => %({
3610
- "took" : 1,
3611
- "errors" : true,
3612
- "items" : [
3613
- {
3614
- "create" : {
3615
- "_index" : "foo",
3616
- "_type" : "bar",
3617
- "_id" : "abc",
3618
- "status" : 500,
3619
- "error" : {
3620
- "type" : "some unrecognized type",
3621
- "reason":"some error to cause version mismatch"
3622
- }
3623
- }
3624
- },
3625
- {
3626
- "create" : {
3627
- "_index" : "foo",
3628
- "_type" : "bar",
3629
- "_id" : "abc",
3630
- "status" : 201
3631
- }
3632
- },
3633
- {
3634
- "create" : {
3635
- "_index" : "foo",
3636
- "_type" : "bar",
3637
- "_id" : "abc",
3638
- "status" : 500,
3639
- "error" : {
3640
- "type" : "some unrecognized type",
3641
- "reason":"some error to cause version mismatch"
3642
- }
3643
- }
3644
- },
3645
- {
3646
- "create" : {
3647
- "_index" : "foo",
3648
- "_type" : "bar",
3649
- "_id" : "abc",
3650
- "_id" : "abc",
3651
- "status" : 409
3652
- }
3653
- }
3654
- ]
3655
- })
3656
- }
3657
- end)
3658
- stub_opensearch_info
3659
-
3660
- driver.run(default_tag: 'test') do
3661
- driver.feed(1, sample_record)
3662
- driver.feed(2, sample_record)
3663
- driver.feed(3, sample_record)
3664
- driver.feed(4, sample_record)
3665
- end
3666
-
3667
- expect = [['test', 1, sample_record],
3668
- ['test', 3, sample_record]]
3669
- assert_equal expect, driver.events
3670
- end
3671
-
3672
- def test_update_should_not_write_if_theres_no_id
3673
- driver.configure("write_operation update\n")
3674
- stub_opensearch
3675
- stub_opensearch_info
3676
- driver.run(default_tag: 'test') do
3677
- driver.feed(sample_record)
3678
- end
3679
- assert_nil(index_cmds)
3680
- end
3681
-
3682
- def test_upsert_should_not_write_if_theres_no_id
3683
- driver.configure("write_operation upsert\n")
3684
- stub_opensearch
3685
- stub_opensearch_info
3686
- driver.run(default_tag: 'test') do
3687
- driver.feed(sample_record)
3688
- end
3689
- assert_nil(index_cmds)
3690
- end
3691
-
3692
- def test_create_should_not_write_if_theres_no_id
3693
- driver.configure("write_operation create\n")
3694
- stub_opensearch
3695
- stub_opensearch_info
3696
- driver.run(default_tag: 'test') do
3697
- driver.feed(sample_record)
3698
- end
3699
- assert_nil(index_cmds)
3700
- end
3701
-
3702
- def test_update_should_write_update_op_and_doc_as_upsert_is_false
3703
- driver.configure("write_operation update
3704
- id_key request_id")
3705
- stub_opensearch
3706
- stub_opensearch_info
3707
- driver.run(default_tag: 'test') do
3708
- driver.feed(sample_record)
3709
- end
3710
- assert(index_cmds[0].has_key?("update"))
3711
- assert(!index_cmds[1]["doc_as_upsert"])
3712
- assert(!index_cmds[1]["upsert"])
3713
- end
3714
-
3715
- def test_update_should_remove_keys_from_doc_when_keys_are_skipped
3716
- driver.configure("write_operation update
3717
- id_key request_id
3718
- remove_keys_on_update parent_id")
3719
- stub_opensearch
3720
- stub_opensearch_info
3721
- driver.run(default_tag: 'test') do
3722
- driver.feed(sample_record)
3723
- end
3724
- assert(index_cmds[1]["doc"])
3725
- assert(!index_cmds[1]["doc"]["parent_id"])
3726
- end
3727
-
3728
- def test_upsert_should_write_update_op_and_doc_as_upsert_is_true
3729
- driver.configure("write_operation upsert
3730
- id_key request_id")
3731
- stub_opensearch
3732
- stub_opensearch_info
3733
- driver.run(default_tag: 'test') do
3734
- driver.feed(sample_record)
3735
- end
3736
- assert(index_cmds[0].has_key?("update"))
3737
- assert(index_cmds[1]["doc_as_upsert"])
3738
- assert(!index_cmds[1]["upsert"])
3739
- end
3740
-
3741
- def test_upsert_should_write_update_op_upsert_and_doc_when_keys_are_skipped
3742
- driver.configure("write_operation upsert
3743
- id_key request_id
3744
- remove_keys_on_update parent_id")
3745
- stub_opensearch
3746
- stub_opensearch_info
3747
- driver.run(default_tag: 'test') do
3748
- driver.feed(sample_record)
3749
- end
3750
- assert(index_cmds[0].has_key?("update"))
3751
- assert(!index_cmds[1]["doc_as_upsert"])
3752
- assert(index_cmds[1]["upsert"])
3753
- assert(index_cmds[1]["doc"])
3754
- end
3755
-
3756
- def test_upsert_should_remove_keys_from_doc_when_keys_are_skipped
3757
- driver.configure("write_operation upsert
3758
- id_key request_id
3759
- remove_keys_on_update parent_id")
3760
- stub_opensearch
3761
- stub_opensearch_info
3762
- driver.run(default_tag: 'test') do
3763
- driver.feed(sample_record)
3764
- end
3765
- assert(index_cmds[1]["upsert"] != index_cmds[1]["doc"])
3766
- assert(!index_cmds[1]["doc"]["parent_id"])
3767
- assert(index_cmds[1]["upsert"]["parent_id"])
3768
- end
3769
-
3770
- def test_upsert_should_remove_multiple_keys_when_keys_are_skipped
3771
- driver.configure("write_operation upsert
3772
- id_key id
3773
- remove_keys_on_update foo,baz")
3774
- stub_opensearch
3775
- stub_opensearch_info
3776
- driver.run(default_tag: 'test') do
3777
- driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "zip" => "zam")
3778
- end
3779
- assert(
3780
- index_cmds[1]["doc"] == {
3781
- "id" => 1,
3782
- "zip" => "zam",
3783
- }
3784
- )
3785
- assert(
3786
- index_cmds[1]["upsert"] == {
3787
- "id" => 1,
3788
- "foo" => "bar",
3789
- "baz" => "quix",
3790
- "zip" => "zam",
3791
- }
3792
- )
3793
- end
3794
-
3795
- def test_upsert_should_remove_keys_from_when_the_keys_are_in_the_record
3796
- driver.configure("write_operation upsert
3797
- id_key id
3798
- remove_keys_on_update_key keys_to_skip")
3799
- stub_opensearch
3800
- stub_opensearch_info
3801
- driver.run(default_tag: 'test') do
3802
- driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "keys_to_skip" => ["baz"])
3803
- end
3804
- assert(
3805
- index_cmds[1]["doc"] == {
3806
- "id" => 1,
3807
- "foo" => "bar",
3808
- }
3809
- )
3810
- assert(
3811
- index_cmds[1]["upsert"] == {
3812
- "id" => 1,
3813
- "foo" => "bar",
3814
- "baz" => "quix",
3815
- }
3816
- )
3817
- end
3818
-
3819
- def test_upsert_should_remove_keys_from_key_on_record_has_higher_presedence_than_config
3820
- driver.configure("write_operation upsert
3821
- id_key id
3822
- remove_keys_on_update foo,bar
3823
- remove_keys_on_update_key keys_to_skip")
3824
- stub_opensearch
3825
- stub_opensearch_info
3826
- driver.run(default_tag: 'test') do
3827
- driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "keys_to_skip" => ["baz"])
3828
- end
3829
- assert(
3830
- index_cmds[1]["doc"] == {
3831
- "id" => 1,
3832
- # we only expect baz to be stripped here, if the config was more important
3833
- # foo would be stripped too.
3834
- "foo" => "bar",
3835
- }
3836
- )
3837
- assert(
3838
- index_cmds[1]["upsert"] == {
3839
- "id" => 1,
3840
- "foo" => "bar",
3841
- "baz" => "quix",
3842
- }
3843
- )
3844
- end
3845
-
3846
- def test_create_should_write_create_op
3847
- driver.configure("write_operation create
3848
- id_key request_id")
3849
- stub_opensearch
3850
- stub_opensearch_info
3851
- driver.run(default_tag: 'test') do
3852
- driver.feed(sample_record)
3853
- end
3854
- assert(index_cmds[0].has_key?("create"))
3855
- end
3856
-
3857
- def test_include_index_in_url
3858
- stub_opensearch('http://localhost:9200/logstash-2018.01.01/_bulk')
3859
- stub_opensearch_info('http://localhost:9200/')
3860
-
3861
- driver.configure("index_name logstash-2018.01.01
3862
- include_index_in_url true")
3863
- driver.run(default_tag: 'test') do
3864
- driver.feed(sample_record)
3865
- end
3866
-
3867
- assert_equal(2, index_cmds.length)
3868
- assert_equal(nil, index_cmds.first['index']['_index'])
3869
- end
3870
-
3871
- def test_use_simple_sniffer
3872
- require 'fluent/plugin/opensearch_simple_sniffer'
3873
- stub_opensearch
3874
- stub_opensearch_info
3875
- config = %[
3876
- sniffer_class_name Fluent::Plugin::OpenSearchSimpleSniffer
3877
- log_level debug
3878
- with_transporter_log true
3879
- reload_connections true
3880
- reload_after 1
3881
- ]
3882
- driver(config, nil)
3883
- driver.run(default_tag: 'test') do
3884
- driver.feed(sample_record)
3885
- end
3886
- log = driver.logs
3887
- # 2 or 3 - one for the ping, one for the _bulk, (and client.info)
3888
- assert_logs_include_compare_size(3, ">", log, /In Fluent::Plugin::OpenSearchSimpleSniffer hosts/)
3889
- assert_logs_include_compare_size(1, "<=", log, /In Fluent::Plugin::OpenSearchSimpleSniffer hosts/)
3890
- end
3891
-
3892
- def test_suppress_doc_wrap
3893
- driver.configure('write_operation update
3894
- id_key id
3895
- remove_keys id
3896
- suppress_doc_wrap true')
3897
- stub_opensearch
3898
- stub_opensearch_info
3899
- doc_body = {'field' => 'value'}
3900
- script_body = {'source' => 'ctx._source.counter += params.param1',
3901
- 'lang' => 'painless',
3902
- 'params' => {'param1' => 1}}
3903
- upsert_body = {'counter' => 1}
3904
- driver.run(default_tag: 'test') do
3905
- driver.feed('id' => 1, 'doc' => doc_body)
3906
- driver.feed('id' => 2, 'script' => script_body, 'upsert' => upsert_body)
3907
- end
3908
- assert(
3909
- index_cmds[1] == {'doc' => doc_body}
3910
- )
3911
- assert(
3912
- index_cmds[3] == {
3913
- 'script' => script_body,
3914
- 'upsert' => upsert_body
3915
- }
3916
- )
3917
- end
3918
-
3919
- def test_suppress_doc_wrap_should_handle_record_as_is_at_upsert
3920
- driver.configure('write_operation upsert
3921
- id_key id
3922
- remove_keys id
3923
- suppress_doc_wrap true')
3924
- stub_opensearch
3925
- stub_opensearch_info
3926
- doc_body = {'field' => 'value'}
3927
- script_body = {'source' => 'ctx._source.counter += params.param1',
3928
- 'lang' => 'painless',
3929
- 'params' => {'param1' => 1}}
3930
- upsert_body = {'counter' => 1}
3931
- driver.run(default_tag: 'test') do
3932
- driver.feed('id' => 1, 'doc' => doc_body, 'doc_as_upsert' => true)
3933
- driver.feed('id' => 2, 'script' => script_body, 'upsert' => upsert_body)
3934
- end
3935
- assert(
3936
- index_cmds[1] == {
3937
- 'doc' => doc_body,
3938
- 'doc_as_upsert' => true
3939
- }
3940
- )
3941
- assert(
3942
- index_cmds[3] == {
3943
- 'script' => script_body,
3944
- 'upsert' => upsert_body
3945
- }
3946
- )
3947
- end
3948
-
3949
- def test_ignore_exception
3950
- driver.configure('ignore_exceptions ["OpenSearch::Transport::Transport::Errors::ServiceUnavailable"]')
3951
- stub_opensearch_unavailable
3952
- stub_opensearch_info
3953
-
3954
- driver.run(default_tag: 'test') do
3955
- driver.feed(sample_record)
3956
- end
3957
- end
3958
-
3959
- def test_ignore_exception_with_superclass
3960
- driver.configure('ignore_exceptions ["OpenSearch::Transport::Transport::ServerError"]')
3961
- stub_opensearch_unavailable
3962
- stub_opensearch_info
3963
-
3964
- driver.run(default_tag: 'test') do
3965
- driver.feed(sample_record)
3966
- end
3967
- end
3968
-
3969
- def test_ignore_excetion_handles_appropriate_ones
3970
- driver.configure('ignore_exceptions ["Faraday::ConnectionFailed"]')
3971
- stub_opensearch_unavailable
3972
- stub_opensearch_info
3973
-
3974
- assert_raise(Fluent::Plugin::OpenSearchOutput::RecoverableRequestFailure) {
3975
- driver.run(default_tag: 'test', shutdown: false) do
3976
- driver.feed(sample_record)
3977
- end
3978
- }
3979
- end
3980
- end