fluent-plugin-elasticsearch 2.10.3 → 2.10.4

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.
@@ -1,2046 +1,2055 @@
1
- require 'helper'
2
- require 'date'
3
- require 'fluent/test/helpers'
4
- require 'json'
5
- require 'fluent/test/driver/output'
6
- require 'flexmock/test_unit'
7
-
8
- class ElasticsearchOutput < Test::Unit::TestCase
9
- include FlexMock::TestCase
10
- include Fluent::Test::Helpers
11
-
12
- attr_accessor :index_cmds, :index_command_counts
13
-
14
- def setup
15
- Fluent::Test.setup
16
- require 'fluent/plugin/out_elasticsearch'
17
- @driver = nil
18
- log = Fluent::Engine.log
19
- log.out.logs.slice!(0, log.out.logs.length)
20
- end
21
-
22
- def driver(conf='', es_version=5)
23
- # For request stub to detect compatibility.
24
- @es_version ||= es_version
25
- Fluent::Plugin::ElasticsearchOutput.module_eval(<<-CODE)
26
- def detect_es_major_version
27
- #{@es_version}
28
- end
29
- CODE
30
- @driver ||= Fluent::Test::Driver::Output.new(Fluent::Plugin::ElasticsearchOutput) {
31
- # v0.12's test driver assume format definition. This simulates ObjectBufferedOutput format
32
- if !defined?(Fluent::Plugin::Output)
33
- def format(tag, time, record)
34
- [time, record].to_msgpack
35
- end
36
- end
37
- }.configure(conf)
38
- end
39
-
40
- def default_type_name
41
- Fluent::Plugin::ElasticsearchOutput::DEFAULT_TYPE_NAME
42
- end
43
-
44
- def sample_record
45
- {'age' => 26, 'request_id' => '42', 'parent_id' => 'parent', 'routing_id' => 'routing'}
46
- end
47
-
48
- def nested_sample_record
49
- {'nested' =>
50
- {'age' => 26, 'parent_id' => 'parent', 'routing_id' => 'routing', 'request_id' => '42'}
51
- }
52
- end
53
-
54
- def stub_elastic_ping(url="http://localhost:9200")
55
- stub_request(:head, url).to_return(:status => 200, :body => "", :headers => {})
56
- end
57
-
58
- def stub_elastic(url="http://localhost:9200/_bulk")
59
- stub_request(:post, url).with do |req|
60
- @index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
61
- end
62
- end
63
-
64
- def stub_elastic_unavailable(url="http://localhost:9200/_bulk")
65
- stub_request(:post, url).to_return(:status => [503, "Service Unavailable"])
66
- end
67
-
68
- def stub_elastic_with_store_index_command_counts(url="http://localhost:9200/_bulk")
69
- if @index_command_counts == nil
70
- @index_command_counts = {}
71
- @index_command_counts.default = 0
72
- end
73
-
74
- stub_request(:post, url).with do |req|
75
- index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
76
- @index_command_counts[url] += index_cmds.size
77
- end
78
- end
79
-
80
- def make_response_body(req, error_el = nil, error_status = nil, error = nil)
81
- req_index_cmds = req.body.split("\n").map { |r| JSON.parse(r) }
82
- items = []
83
- count = 0
84
- ids = 1
85
- op = nil
86
- index = nil
87
- type = nil
88
- id = nil
89
- req_index_cmds.each do |cmd|
90
- if count.even?
91
- op = cmd.keys[0]
92
- index = cmd[op]['_index']
93
- type = cmd[op]['_type']
94
- if cmd[op].has_key?('_id')
95
- id = cmd[op]['_id']
96
- else
97
- # Note: this appears to be an undocumented feature of Elasticsearch
98
- # https://www.elastic.co/guide/en/elasticsearch/reference/2.4/docs-bulk.html
99
- # When you submit an "index" write_operation, with no "_id" field in the
100
- # metadata header, Elasticsearch will turn this into a "create"
101
- # operation in the response.
102
- if "index" == op
103
- op = "create"
104
- end
105
- id = ids
106
- ids += 1
107
- end
108
- else
109
- item = {
110
- op => {
111
- '_index' => index, '_type' => type, '_id' => id, '_version' => 1,
112
- '_shards' => { 'total' => 1, 'successful' => 1, 'failed' => 0 },
113
- 'status' => op == 'create' ? 201 : 200
114
- }
115
- }
116
- items.push(item)
117
- end
118
- count += 1
119
- end
120
- if !error_el.nil? && !error_status.nil? && !error.nil?
121
- op = items[error_el].keys[0]
122
- items[error_el][op].delete('_version')
123
- items[error_el][op].delete('_shards')
124
- items[error_el][op]['error'] = error
125
- items[error_el][op]['status'] = error_status
126
- errors = true
127
- else
128
- errors = false
129
- end
130
- @index_cmds = items
131
- body = { 'took' => 6, 'errors' => errors, 'items' => items }
132
- return body.to_json
133
- end
134
-
135
- def stub_elastic_bad_argument(url="http://localhost:9200/_bulk")
136
- error = {
137
- "type" => "mapper_parsing_exception",
138
- "reason" => "failed to parse [...]",
139
- "caused_by" => {
140
- "type" => "illegal_argument_exception",
141
- "reason" => "Invalid format: \"...\""
142
- }
143
- }
144
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 400, error), :headers => { 'Content-Type' => 'json' } } })
145
- end
146
-
147
- def stub_elastic_bulk_error(url="http://localhost:9200/_bulk")
148
- error = {
149
- "type" => "some-unrecognized-error",
150
- "reason" => "some message printed here ...",
151
- }
152
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
153
- end
154
-
155
- def stub_elastic_bulk_rejected(url="http://localhost:9200/_bulk")
156
- error = {
157
- "status" => 500,
158
- "type" => "es_rejected_execution_exception",
159
- "reason" => "rejected execution of org.elasticsearch.transport.TransportService$4@1a34d37a on EsThreadPoolExecutor[bulk, queue capacity = 50, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@312a2162[Running, pool size = 32, active threads = 32, queued tasks = 50, completed tasks = 327053]]"
160
- }
161
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 429, error), :headers => { 'Content-Type' => 'json' } } })
162
- end
163
-
164
- def stub_elastic_out_of_memory(url="http://localhost:9200/_bulk")
165
- error = {
166
- "status" => 500,
167
- "type" => "out_of_memory_error",
168
- "reason" => "Java heap space"
169
- }
170
- stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
171
- end
172
-
173
- def stub_elastic_unexpected_response_op(url="http://localhost:9200/_bulk")
174
- error = {
175
- "category" => "some-other-type",
176
- "reason" => "some-other-reason"
177
- }
178
- 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' } } })
179
- end
180
-
181
- def assert_logs_include(logs, msg)
182
- matches = logs.grep /#{msg}/
183
- assert_equal(1, matches.length, "Logs do not contain '#{msg}' '#{logs}'")
184
- end
185
-
186
- def test_configure
187
- config = %{
188
- host logs.google.com
189
- port 777
190
- scheme https
191
- path /es/
192
- user john
193
- password doe
194
- }
195
- instance = driver(config).instance
196
-
197
- assert_equal 'logs.google.com', instance.host
198
- assert_equal 777, instance.port
199
- assert_equal 'https', instance.scheme
200
- assert_equal '/es/', instance.path
201
- assert_equal 'john', instance.user
202
- assert_equal 'doe', instance.password
203
- assert_equal :TLSv1, instance.ssl_version
204
- assert_nil instance.client_key
205
- assert_nil instance.client_cert
206
- assert_nil instance.client_key_pass
207
- assert_false instance.with_transporter_log
208
- assert_equal :"application/json", instance.content_type
209
- assert_equal "fluentd", default_type_name
210
- end
211
-
212
- test 'configure Content-Type' do
213
- config = %{
214
- content_type application/x-ndjson
215
- }
216
- instance = driver(config).instance
217
- assert_equal :"application/x-ndjson", instance.content_type
218
- end
219
-
220
- test 'invalid Content-Type' do
221
- config = %{
222
- content_type nonexistent/invalid
223
- }
224
- assert_raise(Fluent::ConfigError) {
225
- instance = driver(config).instance
226
- }
227
- end
228
-
229
- test 'Detected Elasticsearch 7' do
230
- config = %{
231
- type_name changed
232
- }
233
- instance = driver(config, 7).instance
234
- assert_equal '_doc', instance.type_name
235
- end
236
-
237
- test 'lack of tag in chunk_keys' do
238
- assert_raise_message(/'tag' in chunk_keys is required./) do
239
- driver(Fluent::Config::Element.new(
240
- 'ROOT', '', {
241
- '@type' => 'elasticsearch',
242
- 'host' => 'log.google.com',
243
- 'port' => 777,
244
- 'scheme' => 'https',
245
- 'path' => '/es/',
246
- 'user' => 'john',
247
- 'pasword' => 'doe',
248
- }, [
249
- Fluent::Config::Element.new('buffer', 'mykey', {
250
- 'chunk_keys' => 'mykey'
251
- }, [])
252
- ]
253
- ))
254
- end
255
- end
256
-
257
- def test_template_already_present
258
- config = %{
259
- host logs.google.com
260
- port 777
261
- scheme https
262
- path /es/
263
- user john
264
- password doe
265
- template_name logstash
266
- template_file /abc123
267
- }
268
-
269
- # connection start
270
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
271
- to_return(:status => 200, :body => "", :headers => {})
272
- # check if template exists
273
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
274
- to_return(:status => 200, :body => "", :headers => {})
275
-
276
- driver(config)
277
-
278
- assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash")
279
- end
280
-
281
- def test_template_create
282
- cwd = File.dirname(__FILE__)
283
- template_file = File.join(cwd, 'test_template.json')
284
-
285
- config = %{
286
- host logs.google.com
287
- port 777
288
- scheme https
289
- path /es/
290
- user john
291
- password doe
292
- template_name logstash
293
- template_file #{template_file}
294
- }
295
-
296
- # connection start
297
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
298
- to_return(:status => 200, :body => "", :headers => {})
299
- # check if template exists
300
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
301
- to_return(:status => 404, :body => "", :headers => {})
302
- # creation
303
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash").
304
- to_return(:status => 200, :body => "", :headers => {})
305
-
306
- driver(config)
307
-
308
- assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash", times: 1)
309
- end
310
-
311
- def test_template_overwrite
312
- cwd = File.dirname(__FILE__)
313
- template_file = File.join(cwd, 'test_template.json')
314
-
315
- config = %{
316
- host logs.google.com
317
- port 777
318
- scheme https
319
- path /es/
320
- user john
321
- password doe
322
- template_name logstash
323
- template_file #{template_file}
324
- template_overwrite true
325
- }
326
-
327
- # connection start
328
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
329
- to_return(:status => 200, :body => "", :headers => {})
330
- # check if template exists
331
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
332
- to_return(:status => 200, :body => "", :headers => {})
333
- # creation
334
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash").
335
- to_return(:status => 200, :body => "", :headers => {})
336
-
337
- driver(config)
338
-
339
- assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash", times: 1)
340
- end
341
-
342
-
343
- def test_template_create_invalid_filename
344
- config = %{
345
- host logs.google.com
346
- port 777
347
- scheme https
348
- path /es/
349
- user john
350
- password doe
351
- template_name logstash
352
- template_file /abc123
353
- }
354
-
355
- # connection start
356
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
357
- to_return(:status => 200, :body => "", :headers => {})
358
- # check if template exists
359
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
360
- to_return(:status => 404, :body => "", :headers => {})
361
-
362
- assert_raise(RuntimeError) {
363
- driver(config)
364
- }
365
- end
366
-
367
- def test_template_retry_install
368
- cwd = File.dirname(__FILE__)
369
- template_file = File.join(cwd, 'test_template.json')
370
-
371
- config = %{
372
- host logs.google.com
373
- port 778
374
- scheme https
375
- path /es/
376
- user john
377
- password doe
378
- template_name logstash
379
- template_file #{template_file}
380
- max_retry_putting_template 3
381
- }
382
-
383
- connection_resets = 0
384
- # connection start
385
- stub_request(:head, "https://john:doe@logs.google.com:778/es//").with do |req|
386
- connection_resets += 1
387
- end
388
- # check if template exists
389
- stub_request(:get, "https://john:doe@logs.google.com:778/es//_template/logstash").with do |req|
390
- raise Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, "Test message"
391
- end
392
- # creation
393
- stub_request(:put, "https://john:doe@logs.google.com:778/es//_template/logstash").with do |req|
394
- raise Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, "Test message"
395
- end
396
-
397
- assert_raise(Fluent::Plugin::ElasticsearchOutput::ConnectionFailure) do
398
- driver(config)
399
- end
400
-
401
- assert_equal(connection_resets, 4)
402
- end
403
-
404
- def test_templates_create
405
- cwd = File.dirname(__FILE__)
406
- template_file = File.join(cwd, 'test_template.json')
407
- config = %{
408
- host logs.google.com
409
- port 777
410
- scheme https
411
- path /es/
412
- user john
413
- password doe
414
- templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}","logstash3":"#{template_file}" }
415
- }
416
-
417
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
418
- to_return(:status => 200, :body => "", :headers => {})
419
- # check if template exists
420
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
421
- to_return(:status => 404, :body => "", :headers => {})
422
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
423
- to_return(:status => 404, :body => "", :headers => {})
424
-
425
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash3").
426
- to_return(:status => 200, :body => "", :headers => {}) #exists
427
-
428
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
429
- to_return(:status => 200, :body => "", :headers => {})
430
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
431
- to_return(:status => 200, :body => "", :headers => {})
432
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3").
433
- to_return(:status => 200, :body => "", :headers => {})
434
-
435
- driver(config)
436
-
437
- assert_requested( :put, "https://john:doe@logs.google.com:777/es//_template/logstash1", times: 1)
438
- assert_requested( :put, "https://john:doe@logs.google.com:777/es//_template/logstash2", times: 1)
439
- assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3") #exists
440
- end
441
-
442
- def test_templates_overwrite
443
- cwd = File.dirname(__FILE__)
444
- template_file = File.join(cwd, 'test_template.json')
445
- config = %{
446
- host logs.google.com
447
- port 777
448
- scheme https
449
- path /es/
450
- user john
451
- password doe
452
- templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}","logstash3":"#{template_file}" }
453
- template_overwrite true
454
- }
455
-
456
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
457
- to_return(:status => 200, :body => "", :headers => {})
458
- # check if template exists
459
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
460
- to_return(:status => 200, :body => "", :headers => {})
461
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
462
- to_return(:status => 200, :body => "", :headers => {})
463
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash3").
464
- to_return(:status => 200, :body => "", :headers => {}) #exists
465
-
466
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
467
- to_return(:status => 200, :body => "", :headers => {})
468
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
469
- to_return(:status => 200, :body => "", :headers => {})
470
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3").
471
- to_return(:status => 200, :body => "", :headers => {})
472
-
473
- driver(config)
474
-
475
- assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1", times: 1)
476
- assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2", times: 1)
477
- assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3", times: 1)
478
- end
479
-
480
- def test_templates_not_used
481
- cwd = File.dirname(__FILE__)
482
- template_file = File.join(cwd, 'test_template.json')
483
-
484
- config = %{
485
- host logs.google.com
486
- port 777
487
- scheme https
488
- path /es/
489
- user john
490
- password doe
491
- template_name logstash
492
- template_file #{template_file}
493
- templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}" }
494
- }
495
- # connection start
496
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
497
- to_return(:status => 200, :body => "", :headers => {})
498
- # check if template exists
499
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
500
- to_return(:status => 404, :body => "", :headers => {})
501
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
502
- to_return(:status => 404, :body => "", :headers => {})
503
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
504
- to_return(:status => 404, :body => "", :headers => {})
505
- #creation
506
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash").
507
- to_return(:status => 200, :body => "", :headers => {})
508
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
509
- to_return(:status => 200, :body => "", :headers => {})
510
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
511
- to_return(:status => 200, :body => "", :headers => {})
512
-
513
- driver(config)
514
-
515
- assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash", times: 1)
516
-
517
- assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1")
518
- assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2")
519
- end
520
-
521
- def test_templates_can_be_partially_created_if_error_occurs
522
- cwd = File.dirname(__FILE__)
523
- template_file = File.join(cwd, 'test_template.json')
524
- config = %{
525
- host logs.google.com
526
- port 777
527
- scheme https
528
- path /es/
529
- user john
530
- password doe
531
- templates {"logstash1":"#{template_file}", "logstash2":"/abc" }
532
- }
533
- stub_request(:head, "https://john:doe@logs.google.com:777/es//").
534
- to_return(:status => 200, :body => "", :headers => {})
535
- # check if template exists
536
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
537
- to_return(:status => 404, :body => "", :headers => {})
538
- stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
539
- to_return(:status => 404, :body => "", :headers => {})
540
-
541
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
542
- to_return(:status => 200, :body => "", :headers => {})
543
- stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
544
- to_return(:status => 200, :body => "", :headers => {})
545
-
546
- assert_raise(RuntimeError) {
547
- driver(config)
548
- }
549
-
550
- assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1", times: 1)
551
- assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2")
552
- end
553
-
554
- def test_legacy_hosts_list
555
- config = %{
556
- hosts host1:50,host2:100,host3
557
- scheme https
558
- path /es/
559
- port 123
560
- }
561
- instance = driver(config).instance
562
-
563
- assert_equal 3, instance.get_connection_options[:hosts].length
564
- host1, host2, host3 = instance.get_connection_options[:hosts]
565
-
566
- assert_equal 'host1', host1[:host]
567
- assert_equal 50, host1[:port]
568
- assert_equal 'https', host1[:scheme]
569
- assert_equal '/es/', host2[:path]
570
- assert_equal 'host3', host3[:host]
571
- assert_equal 123, host3[:port]
572
- assert_equal 'https', host3[:scheme]
573
- assert_equal '/es/', host3[:path]
574
- end
575
-
576
- def test_hosts_list
577
- config = %{
578
- hosts https://john:password@host1:443/elastic/,http://host2
579
- path /default_path
580
- user default_user
581
- password default_password
582
- }
583
- instance = driver(config).instance
584
-
585
- assert_equal 2, instance.get_connection_options[:hosts].length
586
- host1, host2 = instance.get_connection_options[:hosts]
587
-
588
- assert_equal 'host1', host1[:host]
589
- assert_equal 443, host1[:port]
590
- assert_equal 'https', host1[:scheme]
591
- assert_equal 'john', host1[:user]
592
- assert_equal 'password', host1[:password]
593
- assert_equal '/elastic/', host1[:path]
594
-
595
- assert_equal 'host2', host2[:host]
596
- assert_equal 'http', host2[:scheme]
597
- assert_equal 'default_user', host2[:user]
598
- assert_equal 'default_password', host2[:password]
599
- assert_equal '/default_path', host2[:path]
600
- end
601
-
602
- def test_hosts_list_with_escape_placeholders
603
- config = %{
604
- hosts https://%{j+hn}:%{passw@rd}@host1:443/elastic/,http://host2
605
- path /default_path
606
- user default_user
607
- password default_password
608
- }
609
- instance = driver(config).instance
610
-
611
- assert_equal 2, instance.get_connection_options[:hosts].length
612
- host1, host2 = instance.get_connection_options[:hosts]
613
-
614
- assert_equal 'host1', host1[:host]
615
- assert_equal 443, host1[:port]
616
- assert_equal 'https', host1[:scheme]
617
- assert_equal 'j%2Bhn', host1[:user]
618
- assert_equal 'passw%40rd', host1[:password]
619
- assert_equal '/elastic/', host1[:path]
620
-
621
- assert_equal 'host2', host2[:host]
622
- assert_equal 'http', host2[:scheme]
623
- assert_equal 'default_user', host2[:user]
624
- assert_equal 'default_password', host2[:password]
625
- assert_equal '/default_path', host2[:path]
626
- end
627
-
628
- def test_single_host_params_and_defaults
629
- config = %{
630
- host logs.google.com
631
- user john
632
- password doe
633
- }
634
- instance = driver(config).instance
635
-
636
- assert_equal 1, instance.get_connection_options[:hosts].length
637
- host1 = instance.get_connection_options[:hosts][0]
638
-
639
- assert_equal 'logs.google.com', host1[:host]
640
- assert_equal 9200, host1[:port]
641
- assert_equal 'http', host1[:scheme]
642
- assert_equal 'john', host1[:user]
643
- assert_equal 'doe', host1[:password]
644
- assert_equal nil, host1[:path]
645
- end
646
-
647
- def test_single_host_params_and_defaults_with_escape_placeholders
648
- config = %{
649
- host logs.google.com
650
- user %{j+hn}
651
- password %{d@e}
652
- }
653
- instance = driver(config).instance
654
-
655
- assert_equal 1, instance.get_connection_options[:hosts].length
656
- host1 = instance.get_connection_options[:hosts][0]
657
-
658
- assert_equal 'logs.google.com', host1[:host]
659
- assert_equal 9200, host1[:port]
660
- assert_equal 'http', host1[:scheme]
661
- assert_equal 'j%2Bhn', host1[:user]
662
- assert_equal 'd%40e', host1[:password]
663
- assert_equal nil, host1[:path]
664
- end
665
-
666
- def test_content_type_header
667
- stub_request(:head, "http://localhost:9200/").
668
- to_return(:status => 200, :body => "", :headers => {})
669
- if Elasticsearch::VERSION >= "6.0.2"
670
- elastic_request = stub_request(:post, "http://localhost:9200/_bulk").
671
- with(headers: { "Content-Type" => "application/x-ndjson" })
672
- else
673
- elastic_request = stub_request(:post, "http://localhost:9200/_bulk").
674
- with(headers: { "Content-Type" => "application/json" })
675
- end
676
- driver.run(default_tag: 'test') do
677
- driver.feed(sample_record)
678
- end
679
- assert_requested(elastic_request)
680
- end
681
-
682
- def test_write_message_with_bad_chunk
683
- driver.configure("target_index_key bad_value\n@log_level debug\n")
684
- stub_elastic_ping
685
- stub_elastic
686
- driver.run(default_tag: 'test') do
687
- driver.feed({'bad_value'=>"\255"})
688
- end
689
- error_log = driver.error_events.map {|e| e.last.message }
690
-
691
- assert_logs_include(error_log, /(input string invalid)|(invalid byte sequence in UTF-8)/)
692
- end
693
-
694
- def test_writes_to_default_index
695
- stub_elastic_ping
696
- stub_elastic
697
- driver.run(default_tag: 'test') do
698
- driver.feed(sample_record)
699
- end
700
- assert_equal('fluentd', index_cmds.first['index']['_index'])
701
- end
702
-
703
- def test_writes_to_default_type
704
- stub_elastic_ping
705
- stub_elastic
706
- driver.run(default_tag: 'test') do
707
- driver.feed(sample_record)
708
- end
709
- assert_equal(default_type_name, index_cmds.first['index']['_type'])
710
- end
711
-
712
- def test_writes_to_speficied_index
713
- driver.configure("index_name myindex\n")
714
- stub_elastic_ping
715
- stub_elastic
716
- driver.run(default_tag: 'test') do
717
- driver.feed(sample_record)
718
- end
719
- assert_equal('myindex', index_cmds.first['index']['_index'])
720
- end
721
-
722
- class IndexNamePlaceholdersTest < self
723
- def test_writes_to_speficied_index_with_tag_placeholder
724
- driver.configure("index_name myindex.${tag}\n")
725
- stub_elastic_ping
726
- stub_elastic
727
- driver.run(default_tag: 'test') do
728
- driver.feed(sample_record)
729
- end
730
- assert_equal('myindex.test', index_cmds.first['index']['_index'])
731
- end
732
-
733
- def test_writes_to_speficied_index_with_time_placeholder
734
- driver.configure(Fluent::Config::Element.new(
735
- 'ROOT', '', {
736
- '@type' => 'elasticsearch',
737
- 'index_name' => 'myindex.%Y.%m.%d',
738
- }, [
739
- Fluent::Config::Element.new('buffer', 'tag,time', {
740
- 'chunk_keys' => ['tag', 'time'],
741
- 'timekey' => 3600,
742
- }, [])
743
- ]
744
- ))
745
- stub_elastic_ping
746
- stub_elastic
747
- time = Time.parse Date.today.iso8601
748
- driver.run(default_tag: 'test') do
749
- driver.feed(time.to_i, sample_record)
750
- end
751
- assert_equal("myindex.#{time.utc.strftime("%Y.%m.%d")}", index_cmds.first['index']['_index'])
752
- end
753
-
754
- def test_writes_to_speficied_index_with_custom_key_placeholder
755
- driver.configure(Fluent::Config::Element.new(
756
- 'ROOT', '', {
757
- '@type' => 'elasticsearch',
758
- 'index_name' => 'myindex.${pipeline_id}',
759
- }, [
760
- Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
761
- ]
762
- ))
763
- time = Time.parse Date.today.iso8601
764
- pipeline_id = "mypipeline"
765
- logstash_index = "myindex.#{pipeline_id}"
766
- stub_elastic_ping
767
- stub_elastic
768
- driver.run(default_tag: 'test') do
769
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
770
- end
771
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
772
- end
773
- end
774
-
775
- def test_writes_to_speficied_index_uppercase
776
- driver.configure("index_name MyIndex\n")
777
- stub_elastic_ping
778
- stub_elastic
779
- driver.run(default_tag: 'test') do
780
- driver.feed(sample_record)
781
- end
782
- # Allthough index_name has upper-case characters,
783
- # it should be set as lower-case when sent to elasticsearch.
784
- assert_equal('myindex', index_cmds.first['index']['_index'])
785
- end
786
-
787
- def test_writes_to_target_index_key
788
- driver.configure("target_index_key @target_index\n")
789
- stub_elastic_ping
790
- stub_elastic
791
- record = sample_record.clone
792
- driver.run(default_tag: 'test') do
793
- driver.feed(sample_record.merge('@target_index' => 'local-override'))
794
- end
795
- assert_equal('local-override', index_cmds.first['index']['_index'])
796
- assert_nil(index_cmds[1]['@target_index'])
797
- end
798
-
799
- def test_writes_to_target_index_key_logstash
800
- driver.configure("target_index_key @target_index
801
- logstash_format true")
802
- time = Time.parse Date.today.iso8601
803
- stub_elastic_ping
804
- stub_elastic
805
- driver.run(default_tag: 'test') do
806
- driver.feed(time.to_i, sample_record.merge('@target_index' => 'local-override'))
807
- end
808
- assert_equal('local-override', index_cmds.first['index']['_index'])
809
- end
810
-
811
- def test_writes_to_target_index_key_logstash_uppercase
812
- driver.configure("target_index_key @target_index
813
- logstash_format true")
814
- time = Time.parse Date.today.iso8601
815
- stub_elastic_ping
816
- stub_elastic
817
- driver.run(default_tag: 'test') do
818
- driver.feed(time.to_i, sample_record.merge('@target_index' => 'local-override'))
819
- end
820
- # Allthough @target_index has upper-case characters,
821
- # it should be set as lower-case when sent to elasticsearch.
822
- assert_equal('local-override', index_cmds.first['index']['_index'])
823
- end
824
-
825
- def test_writes_to_default_index_with_pipeline
826
- pipeline = "fluentd"
827
- driver.configure("pipeline #{pipeline}")
828
- stub_elastic_ping
829
- stub_elastic
830
- driver.run(default_tag: 'test') do
831
- driver.feed(sample_record)
832
- end
833
- assert_equal(pipeline, index_cmds.first['index']['pipeline'])
834
- end
835
-
836
- def test_writes_to_target_index_key_fallack
837
- driver.configure("target_index_key @target_index\n")
838
- stub_elastic_ping
839
- stub_elastic
840
- driver.run(default_tag: 'test') do
841
- driver.feed(sample_record)
842
- end
843
- assert_equal('fluentd', index_cmds.first['index']['_index'])
844
- end
845
-
846
- def test_writes_to_target_index_key_fallack_logstash
847
- driver.configure("target_index_key @target_index\n
848
- logstash_format true")
849
- time = Time.parse Date.today.iso8601
850
- logstash_index = "logstash-#{time.getutc.strftime("%Y.%m.%d")}"
851
- stub_elastic_ping
852
- stub_elastic
853
- driver.run(default_tag: 'test') do
854
- driver.feed(time.to_i, sample_record)
855
- end
856
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
857
- end
858
-
859
- data("border" => {"es_version" => 6, "_type" => "mytype"},
860
- "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
861
- )
862
- def test_writes_to_speficied_type(data)
863
- driver('', data["es_version"]).configure("type_name mytype\n")
864
- stub_elastic_ping
865
- stub_elastic
866
- driver.run(default_tag: 'test') do
867
- driver.feed(sample_record)
868
- end
869
- assert_equal(data['_type'], index_cmds.first['index']['_type'])
870
- end
871
-
872
- data("border" => {"es_version" => 6, "_type" => "mytype.test"},
873
- "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
874
- )
875
- def test_writes_to_speficied_type_with_placeholders(data)
876
- driver('', data["es_version"]).configure("type_name mytype.${tag}\n")
877
- stub_elastic_ping
878
- stub_elastic
879
- driver.run(default_tag: 'test') do
880
- driver.feed(sample_record)
881
- end
882
- assert_equal(data['_type'], index_cmds.first['index']['_type'])
883
- end
884
-
885
- data("old" => {"es_version" => 2, "_type" => "local-override"},
886
- "old_behavior" => {"es_version" => 5, "_type" => "local-override"},
887
- "border" => {"es_version" => 6, "_type" => "fluentd"},
888
- "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
889
- )
890
- def test_writes_to_target_type_key(data)
891
- driver('', data["es_version"]).configure("target_type_key @target_type\n")
892
- stub_elastic_ping
893
- stub_elastic
894
- record = sample_record.clone
895
- driver.run(default_tag: 'test') do
896
- driver.feed(sample_record.merge('@target_type' => 'local-override'))
897
- end
898
- assert_equal(data["_type"], index_cmds.first['index']['_type'])
899
- assert_nil(index_cmds[1]['@target_type'])
900
- end
901
-
902
- def test_writes_to_target_type_key_fallack_to_default
903
- driver.configure("target_type_key @target_type\n")
904
- stub_elastic_ping
905
- stub_elastic
906
- driver.run(default_tag: 'test') do
907
- driver.feed(sample_record)
908
- end
909
- assert_equal(default_type_name, index_cmds.first['index']['_type'])
910
- end
911
-
912
- def test_writes_to_target_type_key_fallack_to_type_name
913
- driver.configure("target_type_key @target_type
914
- type_name mytype")
915
- stub_elastic_ping
916
- stub_elastic
917
- driver.run(default_tag: 'test') do
918
- driver.feed(sample_record)
919
- end
920
- assert_equal('mytype', index_cmds.first['index']['_type'])
921
- end
922
-
923
- data("old" => {"es_version" => 2, "_type" => "local-override"},
924
- "old_behavior" => {"es_version" => 5, "_type" => "local-override"},
925
- "border" => {"es_version" => 6, "_type" => "fluentd"},
926
- "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
927
- )
928
- def test_writes_to_target_type_key_nested(data)
929
- driver('', data["es_version"]).configure("target_type_key kubernetes.labels.log_type\n")
930
- stub_elastic_ping
931
- stub_elastic
932
- driver.run(default_tag: 'test') do
933
- driver.feed(sample_record.merge('kubernetes' => {
934
- 'labels' => {
935
- 'log_type' => 'local-override'
936
- }
937
- }))
938
- end
939
- assert_equal(data["_type"], index_cmds.first['index']['_type'])
940
- assert_nil(index_cmds[1]['kubernetes']['labels']['log_type'])
941
- end
942
-
943
- def test_writes_to_target_type_key_fallack_to_default_nested
944
- driver.configure("target_type_key kubernetes.labels.log_type\n")
945
- stub_elastic_ping
946
- stub_elastic
947
- driver.run(default_tag: 'test') do
948
- driver.feed(sample_record.merge('kubernetes' => {
949
- 'labels' => {
950
- 'other_labels' => 'test'
951
- }
952
- }))
953
- end
954
- assert_equal(default_type_name, index_cmds.first['index']['_type'])
955
- end
956
-
957
- def test_writes_to_speficied_host
958
- driver.configure("host 192.168.33.50\n")
959
- stub_elastic_ping("http://192.168.33.50:9200")
960
- elastic_request = stub_elastic("http://192.168.33.50:9200/_bulk")
961
- driver.run(default_tag: 'test') do
962
- driver.feed(sample_record)
963
- end
964
- assert_requested(elastic_request)
965
- end
966
-
967
- def test_writes_to_speficied_port
968
- driver.configure("port 9201\n")
969
- stub_elastic_ping("http://localhost:9201")
970
- elastic_request = stub_elastic("http://localhost:9201/_bulk")
971
- driver.run(default_tag: 'test') do
972
- driver.feed(sample_record)
973
- end
974
- assert_requested(elastic_request)
975
- end
976
-
977
- def test_writes_to_multi_hosts
978
- hosts = [['192.168.33.50', 9201], ['192.168.33.51', 9201], ['192.168.33.52', 9201]]
979
- hosts_string = hosts.map {|x| "#{x[0]}:#{x[1]}"}.compact.join(',')
980
-
981
- driver.configure("hosts #{hosts_string}")
982
-
983
- hosts.each do |host_info|
984
- host, port = host_info
985
- stub_elastic_ping("http://#{host}:#{port}")
986
- stub_elastic_with_store_index_command_counts("http://#{host}:#{port}/_bulk")
987
- end
988
-
989
- driver.run(default_tag: 'test') do
990
- 1000.times do
991
- driver.feed(sample_record.merge('age'=>rand(100)))
992
- end
993
- end
994
-
995
- # @note: we cannot make multi chunks with options (flush_interval, buffer_chunk_limit)
996
- # it's Fluentd test driver's constraint
997
- # so @index_command_counts.size is always 1
998
-
999
- assert(@index_command_counts.size > 0, "not working with hosts options")
1000
-
1001
- total = 0
1002
- @index_command_counts.each do |url, count|
1003
- total += count
1004
- end
1005
- assert_equal(2000, total)
1006
- end
1007
-
1008
- def test_nested_record_with_flattening_on
1009
- driver.configure("flatten_hashes true
1010
- flatten_hashes_separator |")
1011
-
1012
- original_hash = {"foo" => {"bar" => "baz"}, "people" => [
1013
- {"age" => "25", "height" => "1ft"},
1014
- {"age" => "30", "height" => "2ft"}
1015
- ]}
1016
-
1017
- expected_output = {"foo|bar"=>"baz", "people" => [
1018
- {"age" => "25", "height" => "1ft"},
1019
- {"age" => "30", "height" => "2ft"}
1020
- ]}
1021
-
1022
- stub_elastic_ping
1023
- stub_elastic
1024
- driver.run(default_tag: 'test') do
1025
- driver.feed(original_hash)
1026
- end
1027
- assert_equal expected_output, index_cmds[1]
1028
- end
1029
-
1030
- def test_nested_record_with_flattening_off
1031
- # flattening off by default
1032
-
1033
- original_hash = {"foo" => {"bar" => "baz"}}
1034
- expected_output = {"foo" => {"bar" => "baz"}}
1035
-
1036
- stub_elastic_ping
1037
- stub_elastic
1038
- driver.run(default_tag: 'test') do
1039
- driver.feed(original_hash)
1040
- end
1041
- assert_equal expected_output, index_cmds[1]
1042
- end
1043
-
1044
- def test_makes_bulk_request
1045
- stub_elastic_ping
1046
- stub_elastic
1047
- driver.run(default_tag: 'test') do
1048
- driver.feed(sample_record)
1049
- driver.feed(sample_record.merge('age' => 27))
1050
- end
1051
- assert_equal(4, index_cmds.count)
1052
- end
1053
-
1054
- def test_all_records_are_preserved_in_bulk
1055
- stub_elastic_ping
1056
- stub_elastic
1057
- driver.run(default_tag: 'test') do
1058
- driver.feed(sample_record)
1059
- driver.feed(sample_record.merge('age' => 27))
1060
- end
1061
- assert_equal(26, index_cmds[1]['age'])
1062
- assert_equal(27, index_cmds[3]['age'])
1063
- end
1064
-
1065
- def test_writes_to_logstash_index
1066
- driver.configure("logstash_format true\n")
1067
- #
1068
- # This is 1 second past midnight in BST, so the UTC index should be the day before
1069
- dt = DateTime.new(2015, 6, 1, 0, 0, 1, "+01:00")
1070
- logstash_index = "logstash-2015.05.31"
1071
- stub_elastic_ping
1072
- stub_elastic
1073
- driver.run(default_tag: 'test') do
1074
- driver.feed(dt.to_time.to_i, sample_record)
1075
- end
1076
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1077
- end
1078
-
1079
- def test_writes_to_logstash_non_utc_index
1080
- driver.configure("logstash_format true
1081
- utc_index false")
1082
- # When using `utc_index false` the index time will be the local day of
1083
- # ingestion time
1084
- time = Date.today.to_time
1085
- index = "logstash-#{time.strftime("%Y.%m.%d")}"
1086
- stub_elastic_ping
1087
- stub_elastic
1088
- driver.run(default_tag: 'test') do
1089
- driver.feed(time.to_i, sample_record)
1090
- end
1091
- assert_equal(index, index_cmds.first['index']['_index'])
1092
- end
1093
-
1094
- def test_writes_to_logstash_index_with_specified_prefix
1095
- driver.configure("logstash_format true
1096
- logstash_prefix myprefix")
1097
- time = Time.parse Date.today.iso8601
1098
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
1099
- stub_elastic_ping
1100
- stub_elastic
1101
- driver.run(default_tag: 'test') do
1102
- driver.feed(time.to_i, sample_record)
1103
- end
1104
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1105
- end
1106
-
1107
- def test_writes_to_logstash_index_with_specified_prefix_and_separator
1108
- separator = '_'
1109
- driver.configure("logstash_format true
1110
- logstash_prefix_separator #{separator}
1111
- logstash_prefix myprefix")
1112
- time = Time.parse Date.today.iso8601
1113
- logstash_index = "myprefix#{separator}#{time.getutc.strftime("%Y.%m.%d")}"
1114
- stub_elastic_ping
1115
- stub_elastic
1116
- driver.run(default_tag: 'test') do
1117
- driver.feed(time.to_i, sample_record)
1118
- end
1119
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1120
- end
1121
-
1122
- class LogStashPrefixPlaceholdersTest < self
1123
- def test_writes_to_logstash_index_with_specified_prefix_and_tag_placeholder
1124
- driver.configure("logstash_format true
1125
- logstash_prefix myprefix-${tag}")
1126
- time = Time.parse Date.today.iso8601
1127
- logstash_index = "myprefix-test-#{time.getutc.strftime("%Y.%m.%d")}"
1128
- stub_elastic_ping
1129
- stub_elastic
1130
- driver.run(default_tag: 'test') do
1131
- driver.feed(time.to_i, sample_record)
1132
- end
1133
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1134
- end
1135
-
1136
- def test_writes_to_logstash_index_with_specified_prefix_and_time_placeholder
1137
- driver.configure(Fluent::Config::Element.new(
1138
- 'ROOT', '', {
1139
- '@type' => 'elasticsearch',
1140
- 'logstash_format' => true,
1141
- 'logstash_prefix' => 'myprefix-%H',
1142
- }, [
1143
- Fluent::Config::Element.new('buffer', 'tag,time', {
1144
- 'chunk_keys' => ['tag', 'time'],
1145
- 'timekey' => 3600,
1146
- }, [])
1147
- ]
1148
- ))
1149
- time = Time.parse Date.today.iso8601
1150
- logstash_index = "myprefix-#{time.getutc.strftime("%H")}-#{time.getutc.strftime("%Y.%m.%d")}"
1151
- stub_elastic_ping
1152
- stub_elastic
1153
- driver.run(default_tag: 'test') do
1154
- driver.feed(time.to_i, sample_record)
1155
- end
1156
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1157
- end
1158
-
1159
- def test_writes_to_logstash_index_with_specified_prefix_and_custom_key_placeholder
1160
- driver.configure(Fluent::Config::Element.new(
1161
- 'ROOT', '', {
1162
- '@type' => 'elasticsearch',
1163
- 'logstash_format' => true,
1164
- 'logstash_prefix' => 'myprefix-${pipeline_id}',
1165
- }, [
1166
- Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
1167
- ]
1168
- ))
1169
- time = Time.parse Date.today.iso8601
1170
- pipeline_id = "mypipeline"
1171
- logstash_index = "myprefix-#{pipeline_id}-#{time.getutc.strftime("%Y.%m.%d")}"
1172
- stub_elastic_ping
1173
- stub_elastic
1174
- driver.run(default_tag: 'test') do
1175
- driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
1176
- end
1177
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1178
- end
1179
- end
1180
-
1181
- def test_writes_to_logstash_index_with_specified_prefix_uppercase
1182
- driver.configure("logstash_format true
1183
- logstash_prefix MyPrefix")
1184
- time = Time.parse Date.today.iso8601
1185
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
1186
- stub_elastic_ping
1187
- stub_elastic
1188
- driver.run(default_tag: 'test') do
1189
- driver.feed(time.to_i, sample_record)
1190
- end
1191
- # Allthough logstash_prefix has upper-case characters,
1192
- # it should be set as lower-case when sent to elasticsearch.
1193
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1194
- end
1195
-
1196
- def test_writes_to_logstash_index_with_specified_dateformat
1197
- driver.configure("logstash_format true
1198
- logstash_dateformat %Y.%m")
1199
- time = Time.parse Date.today.iso8601
1200
- logstash_index = "logstash-#{time.getutc.strftime("%Y.%m")}"
1201
- stub_elastic_ping
1202
- stub_elastic
1203
- driver.run(default_tag: 'test') do
1204
- driver.feed(time.to_i, sample_record)
1205
- end
1206
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1207
- end
1208
-
1209
- def test_writes_to_logstash_index_with_specified_prefix_and_dateformat
1210
- driver.configure("logstash_format true
1211
- logstash_prefix myprefix
1212
- logstash_dateformat %Y.%m")
1213
- time = Time.parse Date.today.iso8601
1214
- logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m")}"
1215
- stub_elastic_ping
1216
- stub_elastic
1217
- driver.run(default_tag: 'test') do
1218
- driver.feed(time.to_i, sample_record)
1219
- end
1220
- assert_equal(logstash_index, index_cmds.first['index']['_index'])
1221
- end
1222
-
1223
- def test_error_if_tag_not_in_chunk_keys
1224
- assert_raise(Fluent::ConfigError) {
1225
- config = %{
1226
- <buffer foo>
1227
- </buffer>
1228
- }
1229
- driver.configure(config)
1230
- }
1231
- end
1232
-
1233
- def test_can_use_custom_chunk_along_with_tag
1234
- config = %{
1235
- <buffer tag, foo>
1236
- </buffer>
1237
- }
1238
- driver.configure(config)
1239
- end
1240
-
1241
- def test_doesnt_add_logstash_timestamp_by_default
1242
- stub_elastic_ping
1243
- stub_elastic
1244
- driver.run(default_tag: 'test') do
1245
- driver.feed(sample_record)
1246
- end
1247
- assert_nil(index_cmds[1]['@timestamp'])
1248
- end
1249
-
1250
- def test_adds_timestamp_when_logstash
1251
- driver.configure("logstash_format true\n")
1252
- stub_elastic_ping
1253
- stub_elastic
1254
- ts = DateTime.now
1255
- time = Fluent::EventTime.from_time(ts.to_time)
1256
- driver.run(default_tag: 'test') do
1257
- driver.feed(time, sample_record)
1258
- end
1259
- assert(index_cmds[1].has_key? '@timestamp')
1260
- assert_equal(index_cmds[1]['@timestamp'], ts.iso8601(9))
1261
- end
1262
-
1263
- def test_adds_timestamp_when_include_timestamp
1264
- driver.configure("include_timestamp true\n")
1265
- stub_elastic_ping
1266
- stub_elastic
1267
- ts = DateTime.now
1268
- time = Fluent::EventTime.from_time(ts.to_time)
1269
- driver.run(default_tag: 'test') do
1270
- driver.feed(time, sample_record)
1271
- end
1272
- assert(index_cmds[1].has_key? '@timestamp')
1273
- assert_equal(index_cmds[1]['@timestamp'], ts.iso8601(9))
1274
- end
1275
-
1276
- def test_uses_custom_timestamp_when_included_in_record
1277
- driver.configure("logstash_format true\n")
1278
- stub_elastic_ping
1279
- stub_elastic
1280
- ts = DateTime.new(2001,2,3).iso8601
1281
- driver.run(default_tag: 'test') do
1282
- driver.feed(sample_record.merge!('@timestamp' => ts))
1283
- end
1284
- assert(index_cmds[1].has_key? '@timestamp')
1285
- assert_equal(index_cmds[1]['@timestamp'], ts)
1286
- end
1287
-
1288
- def test_uses_custom_timestamp_when_included_in_record_without_logstash
1289
- driver.configure("include_timestamp true\n")
1290
- stub_elastic_ping
1291
- stub_elastic
1292
- ts = DateTime.new(2001,2,3).iso8601
1293
- driver.run(default_tag: 'test') do
1294
- driver.feed(sample_record.merge!('@timestamp' => ts))
1295
- end
1296
- assert(index_cmds[1].has_key? '@timestamp')
1297
- assert_equal(index_cmds[1]['@timestamp'], ts)
1298
- end
1299
-
1300
- def test_uses_custom_time_key
1301
- driver.configure("logstash_format true
1302
- time_key vtm\n")
1303
- stub_elastic_ping
1304
- stub_elastic
1305
- ts = DateTime.new(2001,2,3).iso8601(9)
1306
- driver.run(default_tag: 'test') do
1307
- driver.feed(sample_record.merge!('vtm' => ts))
1308
- end
1309
- assert(index_cmds[1].has_key? '@timestamp')
1310
- assert_equal(index_cmds[1]['@timestamp'], ts)
1311
- end
1312
-
1313
- def test_uses_custom_time_key_with_format
1314
- driver.configure("logstash_format true
1315
- time_key_format %Y-%m-%d %H:%M:%S.%N%z
1316
- time_key vtm\n")
1317
- stub_elastic_ping
1318
- stub_elastic
1319
- ts = "2001-02-03 13:14:01.673+02:00"
1320
- driver.run(default_tag: 'test') do
1321
- driver.feed(sample_record.merge!('vtm' => ts))
1322
- end
1323
- assert(index_cmds[1].has_key? '@timestamp')
1324
- assert_equal(index_cmds[1]['@timestamp'], DateTime.parse(ts).iso8601(9))
1325
- assert_equal("logstash-2001.02.03", index_cmds[0]['index']['_index'])
1326
- end
1327
-
1328
- def test_uses_custom_time_key_with_format_without_logstash
1329
- driver.configure("include_timestamp true
1330
- index_name test
1331
- time_key_format %Y-%m-%d %H:%M:%S.%N%z
1332
- time_key vtm\n")
1333
- stub_elastic_ping
1334
- stub_elastic
1335
- ts = "2001-02-03 13:14:01.673+02:00"
1336
- driver.run(default_tag: 'test') do
1337
- driver.feed(sample_record.merge!('vtm' => ts))
1338
- end
1339
- assert(index_cmds[1].has_key? '@timestamp')
1340
- assert_equal(index_cmds[1]['@timestamp'], DateTime.parse(ts).iso8601(9))
1341
- assert_equal("test", index_cmds[0]['index']['_index'])
1342
- end
1343
-
1344
- def test_uses_custom_time_key_exclude_timekey
1345
- driver.configure("logstash_format true
1346
- time_key vtm
1347
- time_key_exclude_timestamp true\n")
1348
- stub_elastic_ping
1349
- stub_elastic
1350
- ts = DateTime.new(2001,2,3).iso8601
1351
- driver.run(default_tag: 'test') do
1352
- driver.feed(sample_record.merge!('vtm' => ts))
1353
- end
1354
- assert(!index_cmds[1].key?('@timestamp'), '@timestamp should be messing')
1355
- end
1356
-
1357
- def test_uses_custom_time_key_format
1358
- driver.configure("logstash_format true
1359
- time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n")
1360
- stub_elastic_ping
1361
- stub_elastic
1362
- ts = "2001-02-03T13:14:01.673+02:00"
1363
- driver.run(default_tag: 'test') do
1364
- driver.feed(sample_record.merge!('@timestamp' => ts))
1365
- end
1366
- assert_equal("logstash-2001.02.03", index_cmds[0]['index']['_index'])
1367
- assert(index_cmds[1].has_key? '@timestamp')
1368
- assert_equal(index_cmds[1]['@timestamp'], ts)
1369
- end
1370
-
1371
- def test_uses_custom_time_key_format_without_logstash
1372
- driver.configure("include_timestamp true
1373
- index_name test
1374
- time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n")
1375
- stub_elastic_ping
1376
- stub_elastic
1377
- ts = "2001-02-03T13:14:01.673+02:00"
1378
- driver.run(default_tag: 'test') do
1379
- driver.feed(sample_record.merge!('@timestamp' => ts))
1380
- end
1381
- assert_equal("test", index_cmds[0]['index']['_index'])
1382
- assert(index_cmds[1].has_key? '@timestamp')
1383
- assert_equal(index_cmds[1]['@timestamp'], ts)
1384
- end
1385
-
1386
- data(:default => nil,
1387
- :custom_tag => 'es_plugin.output.time.error')
1388
- def test_uses_custom_time_key_format_logs_an_error(tag_for_error)
1389
- tag_config = tag_for_error ? "time_parse_error_tag #{tag_for_error}" : ''
1390
- tag_for_error = 'Fluent::ElasticsearchOutput::TimeParser.error' if tag_for_error.nil?
1391
- driver.configure("logstash_format true
1392
- time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n#{tag_config}\n")
1393
- stub_elastic_ping
1394
- stub_elastic
1395
-
1396
- ts = "2001/02/03 13:14:01,673+02:00"
1397
- index = "logstash-#{Date.today.strftime("%Y.%m.%d")}"
1398
-
1399
- flexmock(driver.instance.router).should_receive(:emit_error_event)
1400
- .with(tag_for_error, Fluent::EventTime, Hash, ArgumentError).once
1401
- driver.run(default_tag: 'test') do
1402
- driver.feed(sample_record.merge!('@timestamp' => ts))
1403
- end
1404
-
1405
- assert_equal(index, index_cmds[0]['index']['_index'])
1406
- assert(index_cmds[1].has_key? '@timestamp')
1407
- assert_equal(index_cmds[1]['@timestamp'], ts)
1408
- end
1409
-
1410
-
1411
- def test_uses_custom_time_key_format_obscure_format
1412
- driver.configure("logstash_format true
1413
- time_key_format %a %b %d %H:%M:%S %Z %Y\n")
1414
- stub_elastic_ping
1415
- stub_elastic
1416
- ts = "Thu Nov 29 14:33:20 GMT 2001"
1417
- driver.run(default_tag: 'test') do
1418
- driver.feed(sample_record.merge!('@timestamp' => ts))
1419
- end
1420
- assert_equal("logstash-2001.11.29", index_cmds[0]['index']['_index'])
1421
- assert(index_cmds[1].has_key? '@timestamp')
1422
- assert_equal(index_cmds[1]['@timestamp'], ts)
1423
- end
1424
-
1425
- def test_uses_nanosecond_precision_by_default
1426
- driver.configure("logstash_format true\n")
1427
- stub_elastic_ping
1428
- stub_elastic
1429
- time = Fluent::EventTime.new(Time.now.to_i, 123456789)
1430
- driver.run(default_tag: 'test') do
1431
- driver.feed(time, sample_record)
1432
- end
1433
- assert(index_cmds[1].has_key? '@timestamp')
1434
- assert_equal(index_cmds[1]['@timestamp'], Time.at(time).iso8601(9))
1435
- end
1436
-
1437
- def test_uses_subsecond_precision_when_configured
1438
- driver.configure("logstash_format true
1439
- time_precision 3\n")
1440
- stub_elastic_ping
1441
- stub_elastic
1442
- time = Fluent::EventTime.new(Time.now.to_i, 123456789)
1443
- driver.run(default_tag: 'test') do
1444
- driver.feed(time, sample_record)
1445
- end
1446
- assert(index_cmds[1].has_key? '@timestamp')
1447
- assert_equal(index_cmds[1]['@timestamp'], Time.at(time).iso8601(3))
1448
- end
1449
-
1450
- def test_doesnt_add_tag_key_by_default
1451
- stub_elastic_ping
1452
- stub_elastic
1453
- driver.run(default_tag: 'test') do
1454
- driver.feed(sample_record)
1455
- end
1456
- assert_nil(index_cmds[1]['tag'])
1457
- end
1458
-
1459
- def test_adds_tag_key_when_configured
1460
- driver.configure("include_tag_key true\n")
1461
- stub_elastic_ping
1462
- stub_elastic
1463
- driver.run(default_tag: 'mytag') do
1464
- driver.feed(sample_record)
1465
- end
1466
- assert(index_cmds[1].has_key?('tag'))
1467
- assert_equal(index_cmds[1]['tag'], 'mytag')
1468
- end
1469
-
1470
- def test_adds_id_key_when_configured
1471
- driver.configure("id_key request_id\n")
1472
- stub_elastic_ping
1473
- stub_elastic
1474
- driver.run(default_tag: 'test') do
1475
- driver.feed(sample_record)
1476
- end
1477
- assert_equal(index_cmds[0]['index']['_id'], '42')
1478
- end
1479
-
1480
- class NestedIdKeyTest < self
1481
- def test_adds_nested_id_key_with_dot
1482
- driver.configure("id_key nested.request_id\n")
1483
- stub_elastic_ping
1484
- stub_elastic
1485
- driver.run(default_tag: 'test') do
1486
- driver.feed(nested_sample_record)
1487
- end
1488
- assert_equal(index_cmds[0]['index']['_id'], '42')
1489
- end
1490
-
1491
- def test_adds_nested_id_key_with_dollar_dot
1492
- driver.configure("id_key $.nested.request_id\n")
1493
- stub_elastic_ping
1494
- stub_elastic
1495
- driver.run(default_tag: 'test') do
1496
- driver.feed(nested_sample_record)
1497
- end
1498
- assert_equal(index_cmds[0]['index']['_id'], '42')
1499
- end
1500
-
1501
- def test_adds_nested_id_key_with_bracket
1502
- driver.configure("id_key $['nested']['request_id']\n")
1503
- stub_elastic_ping
1504
- stub_elastic
1505
- driver.run(default_tag: 'test') do
1506
- driver.feed(nested_sample_record)
1507
- end
1508
- assert_equal(index_cmds[0]['index']['_id'], '42')
1509
- end
1510
- end
1511
-
1512
- def test_doesnt_add_id_key_if_missing_when_configured
1513
- driver.configure("id_key another_request_id\n")
1514
- stub_elastic_ping
1515
- stub_elastic
1516
- driver.run(default_tag: 'test') do
1517
- driver.feed(sample_record)
1518
- end
1519
- assert(!index_cmds[0]['index'].has_key?('_id'))
1520
- end
1521
-
1522
- def test_adds_id_key_when_not_configured
1523
- stub_elastic_ping
1524
- stub_elastic
1525
- driver.run(default_tag: 'test') do
1526
- driver.feed(sample_record)
1527
- end
1528
- assert(!index_cmds[0]['index'].has_key?('_id'))
1529
- end
1530
-
1531
- def test_adds_parent_key_when_configured
1532
- driver.configure("parent_key parent_id\n")
1533
- stub_elastic_ping
1534
- stub_elastic
1535
- driver.run(default_tag: 'test') do
1536
- driver.feed(sample_record)
1537
- end
1538
- assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1539
- end
1540
-
1541
- class NestedParentKeyTest < self
1542
- def test_adds_nested_parent_key_with_dot
1543
- driver.configure("parent_key nested.parent_id\n")
1544
- stub_elastic_ping
1545
- stub_elastic
1546
- driver.run(default_tag: 'test') do
1547
- driver.feed(nested_sample_record)
1548
- end
1549
- assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1550
- end
1551
-
1552
- def test_adds_nested_parent_key_with_dollar_dot
1553
- driver.configure("parent_key $.nested.parent_id\n")
1554
- stub_elastic_ping
1555
- stub_elastic
1556
- driver.run(default_tag: 'test') do
1557
- driver.feed(nested_sample_record)
1558
- end
1559
- assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1560
- end
1561
-
1562
- def test_adds_nested_parent_key_with_bracket
1563
- driver.configure("parent_key $['nested']['parent_id']\n")
1564
- stub_elastic_ping
1565
- stub_elastic
1566
- driver.run(default_tag: 'test') do
1567
- driver.feed(nested_sample_record)
1568
- end
1569
- assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1570
- end
1571
- end
1572
-
1573
- def test_doesnt_add_parent_key_if_missing_when_configured
1574
- driver.configure("parent_key another_parent_id\n")
1575
- stub_elastic_ping
1576
- stub_elastic
1577
- driver.run(default_tag: 'test') do
1578
- driver.feed(sample_record)
1579
- end
1580
- assert(!index_cmds[0]['index'].has_key?('_parent'))
1581
- end
1582
-
1583
- def test_adds_parent_key_when_not_configured
1584
- stub_elastic_ping
1585
- stub_elastic
1586
- driver.run(default_tag: 'test') do
1587
- driver.feed(sample_record)
1588
- end
1589
- assert(!index_cmds[0]['index'].has_key?('_parent'))
1590
- end
1591
-
1592
- def test_adds_routing_key_when_configured
1593
- driver.configure("routing_key routing_id\n")
1594
- stub_elastic_ping
1595
- stub_elastic
1596
- driver.run(default_tag: 'test') do
1597
- driver.feed(sample_record)
1598
- end
1599
- assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1600
- end
1601
-
1602
- class NestedRoutingKeyTest < self
1603
- def test_adds_nested_routing_key_with_dot
1604
- driver.configure("routing_key nested.routing_id\n")
1605
- stub_elastic_ping
1606
- stub_elastic
1607
- driver.run(default_tag: 'test') do
1608
- driver.feed(nested_sample_record)
1609
- end
1610
- assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1611
- end
1612
-
1613
- def test_adds_nested_routing_key_with_dollar_dot
1614
- driver.configure("routing_key $.nested.routing_id\n")
1615
- stub_elastic_ping
1616
- stub_elastic
1617
- driver.run(default_tag: 'test') do
1618
- driver.feed(nested_sample_record)
1619
- end
1620
- assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1621
- end
1622
-
1623
- def test_adds_nested_routing_key_with_bracket
1624
- driver.configure("routing_key $['nested']['routing_id']\n")
1625
- stub_elastic_ping
1626
- stub_elastic
1627
- driver.run(default_tag: 'test') do
1628
- driver.feed(nested_sample_record)
1629
- end
1630
- assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1631
- end
1632
- end
1633
-
1634
- def test_doesnt_add_routing_key_if_missing_when_configured
1635
- driver.configure("routing_key another_routing_id\n")
1636
- stub_elastic_ping
1637
- stub_elastic
1638
- driver.run(default_tag: 'test') do
1639
- driver.feed(sample_record)
1640
- end
1641
- assert(!index_cmds[0]['index'].has_key?('_routing'))
1642
- end
1643
-
1644
- def test_adds_routing_key_when_not_configured
1645
- stub_elastic_ping
1646
- stub_elastic
1647
- driver.run(default_tag: 'test') do
1648
- driver.feed(sample_record)
1649
- end
1650
- assert(!index_cmds[0]['index'].has_key?('_routing'))
1651
- end
1652
-
1653
- def test_remove_one_key
1654
- driver.configure("remove_keys key1\n")
1655
- stub_elastic_ping
1656
- stub_elastic
1657
- driver.run(default_tag: 'test') do
1658
- driver.feed(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
1659
- end
1660
- assert(!index_cmds[1].has_key?('key1'))
1661
- assert(index_cmds[1].has_key?('key2'))
1662
- end
1663
-
1664
- def test_remove_multi_keys
1665
- driver.configure("remove_keys key1, key2\n")
1666
- stub_elastic_ping
1667
- stub_elastic
1668
- driver.run(default_tag: 'test') do
1669
- driver.feed(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
1670
- end
1671
- assert(!index_cmds[1].has_key?('key1'))
1672
- assert(!index_cmds[1].has_key?('key2'))
1673
- end
1674
-
1675
- def test_request_error
1676
- stub_elastic_ping
1677
- stub_elastic_unavailable
1678
- assert_raise(Elasticsearch::Transport::Transport::Errors::ServiceUnavailable) {
1679
- driver.run(default_tag: 'test') do
1680
- driver.feed(sample_record)
1681
- end
1682
- }
1683
- end
1684
-
1685
- def test_connection_failed_retry
1686
- connection_resets = 0
1687
-
1688
- stub_elastic_ping(url="http://localhost:9200").with do |req|
1689
- connection_resets += 1
1690
- end
1691
-
1692
- stub_request(:post, "http://localhost:9200/_bulk").with do |req|
1693
- raise Faraday::ConnectionFailed, "Test message"
1694
- end
1695
-
1696
- driver.run(default_tag: 'test') do
1697
- driver.feed(sample_record)
1698
- end
1699
- assert_equal(connection_resets, 3)
1700
- end
1701
-
1702
- def test_reconnect_on_error_enabled
1703
- connection_resets = 0
1704
-
1705
- stub_elastic_ping(url="http://localhost:9200").with do |req|
1706
- connection_resets += 1
1707
- end
1708
-
1709
- stub_request(:post, "http://localhost:9200/_bulk").with do |req|
1710
- raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
1711
- end
1712
-
1713
- driver.configure("reconnect_on_error true\n")
1714
-
1715
- assert_raise(ZeroDivisionError) {
1716
- driver.run(default_tag: 'test', shutdown: false) do
1717
- driver.feed(sample_record)
1718
- end
1719
- }
1720
-
1721
- assert_raise(Timeout::Error) {
1722
- driver.run(default_tag: 'test', shutdown: false) do
1723
- driver.feed(sample_record)
1724
- end
1725
- }
1726
- # FIXME: Consider keywords arguments in #run and how to test this later.
1727
- # Because v0.14 test driver does not have 1 to 1 correspondence between #run and #flush in tests.
1728
- assert_equal(connection_resets, 1)
1729
- end
1730
-
1731
- def test_reconnect_on_error_disabled
1732
- connection_resets = 0
1733
-
1734
- stub_elastic_ping(url="http://localhost:9200").with do |req|
1735
- connection_resets += 1
1736
- end
1737
-
1738
- stub_request(:post, "http://localhost:9200/_bulk").with do |req|
1739
- raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
1740
- end
1741
-
1742
- driver.configure("reconnect_on_error false\n")
1743
-
1744
- assert_raise(ZeroDivisionError) {
1745
- driver.run(default_tag: 'test', shutdown: false) do
1746
- driver.feed(sample_record)
1747
- end
1748
- }
1749
-
1750
- assert_raise(Timeout::Error) {
1751
- driver.run(default_tag: 'test', shutdown: false) do
1752
- driver.feed(sample_record)
1753
- end
1754
- }
1755
- assert_equal(connection_resets, 1)
1756
- end
1757
-
1758
- def test_bulk_error_retags_when_configured
1759
- driver.configure("retry_tag retry\n")
1760
- stub_elastic_ping
1761
- stub_request(:post, 'http://localhost:9200/_bulk')
1762
- .to_return(lambda do |req|
1763
- { :status => 200,
1764
- :headers => { 'Content-Type' => 'json' },
1765
- :body => %({
1766
- "took" : 1,
1767
- "errors" : true,
1768
- "items" : [
1769
- {
1770
- "create" : {
1771
- "_index" : "foo",
1772
- "_type" : "bar",
1773
- "_id" : "abc",
1774
- "status" : 500,
1775
- "error" : {
1776
- "type" : "some unrecognized type",
1777
- "reason":"some error to cause version mismatch"
1778
- }
1779
- }
1780
- }
1781
- ]
1782
- })
1783
- }
1784
- end)
1785
-
1786
- driver.run(default_tag: 'test') do
1787
- driver.feed(1, sample_record)
1788
- end
1789
-
1790
- assert_equal [['retry', 1, sample_record]], driver.events
1791
- end
1792
-
1793
- def test_bulk_error
1794
- stub_elastic_ping
1795
- stub_request(:post, 'http://localhost:9200/_bulk')
1796
- .to_return(lambda do |req|
1797
- { :status => 200,
1798
- :headers => { 'Content-Type' => 'json' },
1799
- :body => %({
1800
- "took" : 1,
1801
- "errors" : true,
1802
- "items" : [
1803
- {
1804
- "create" : {
1805
- "_index" : "foo",
1806
- "_type" : "bar",
1807
- "_id" : "abc",
1808
- "status" : 500,
1809
- "error" : {
1810
- "type" : "some unrecognized type",
1811
- "reason":"some error to cause version mismatch"
1812
- }
1813
- }
1814
- },
1815
- {
1816
- "create" : {
1817
- "_index" : "foo",
1818
- "_type" : "bar",
1819
- "_id" : "abc",
1820
- "status" : 201
1821
- }
1822
- },
1823
- {
1824
- "create" : {
1825
- "_index" : "foo",
1826
- "_type" : "bar",
1827
- "_id" : "abc",
1828
- "status" : 500,
1829
- "error" : {
1830
- "type" : "some unrecognized type",
1831
- "reason":"some error to cause version mismatch"
1832
- }
1833
- }
1834
- },
1835
- {
1836
- "create" : {
1837
- "_index" : "foo",
1838
- "_type" : "bar",
1839
- "_id" : "abc",
1840
- "_id" : "abc",
1841
- "status" : 409
1842
- }
1843
- }
1844
- ]
1845
- })
1846
- }
1847
- end)
1848
-
1849
- driver.run(default_tag: 'test') do
1850
- driver.feed(1, sample_record)
1851
- driver.feed(2, sample_record)
1852
- driver.feed(3, sample_record)
1853
- driver.feed(4, sample_record)
1854
- end
1855
-
1856
- expect = [['test', 1, sample_record],
1857
- ['test', 3, sample_record]]
1858
- assert_equal expect, driver.events
1859
- end
1860
-
1861
- def test_update_should_not_write_if_theres_no_id
1862
- driver.configure("write_operation update\n")
1863
- stub_elastic_ping
1864
- stub_elastic
1865
- driver.run(default_tag: 'test') do
1866
- driver.feed(sample_record)
1867
- end
1868
- assert_nil(index_cmds)
1869
- end
1870
-
1871
- def test_upsert_should_not_write_if_theres_no_id
1872
- driver.configure("write_operation upsert\n")
1873
- stub_elastic_ping
1874
- stub_elastic
1875
- driver.run(default_tag: 'test') do
1876
- driver.feed(sample_record)
1877
- end
1878
- assert_nil(index_cmds)
1879
- end
1880
-
1881
- def test_create_should_not_write_if_theres_no_id
1882
- driver.configure("write_operation create\n")
1883
- stub_elastic_ping
1884
- stub_elastic
1885
- driver.run(default_tag: 'test') do
1886
- driver.feed(sample_record)
1887
- end
1888
- assert_nil(index_cmds)
1889
- end
1890
-
1891
- def test_update_should_write_update_op_and_doc_as_upsert_is_false
1892
- driver.configure("write_operation update
1893
- id_key request_id")
1894
- stub_elastic_ping
1895
- stub_elastic
1896
- driver.run(default_tag: 'test') do
1897
- driver.feed(sample_record)
1898
- end
1899
- assert(index_cmds[0].has_key?("update"))
1900
- assert(!index_cmds[1]["doc_as_upsert"])
1901
- assert(!index_cmds[1]["upsert"])
1902
- end
1903
-
1904
- def test_update_should_remove_keys_from_doc_when_keys_are_skipped
1905
- driver.configure("write_operation update
1906
- id_key request_id
1907
- remove_keys_on_update parent_id")
1908
- stub_elastic_ping
1909
- stub_elastic
1910
- driver.run(default_tag: 'test') do
1911
- driver.feed(sample_record)
1912
- end
1913
- assert(index_cmds[1]["doc"])
1914
- assert(!index_cmds[1]["doc"]["parent_id"])
1915
- end
1916
-
1917
- def test_upsert_should_write_update_op_and_doc_as_upsert_is_true
1918
- driver.configure("write_operation upsert
1919
- id_key request_id")
1920
- stub_elastic_ping
1921
- stub_elastic
1922
- driver.run(default_tag: 'test') do
1923
- driver.feed(sample_record)
1924
- end
1925
- assert(index_cmds[0].has_key?("update"))
1926
- assert(index_cmds[1]["doc_as_upsert"])
1927
- assert(!index_cmds[1]["upsert"])
1928
- end
1929
-
1930
- def test_upsert_should_write_update_op_upsert_and_doc_when_keys_are_skipped
1931
- driver.configure("write_operation upsert
1932
- id_key request_id
1933
- remove_keys_on_update parent_id")
1934
- stub_elastic_ping
1935
- stub_elastic
1936
- driver.run(default_tag: 'test') do
1937
- driver.feed(sample_record)
1938
- end
1939
- assert(index_cmds[0].has_key?("update"))
1940
- assert(!index_cmds[1]["doc_as_upsert"])
1941
- assert(index_cmds[1]["upsert"])
1942
- assert(index_cmds[1]["doc"])
1943
- end
1944
-
1945
- def test_upsert_should_remove_keys_from_doc_when_keys_are_skipped
1946
- driver.configure("write_operation upsert
1947
- id_key request_id
1948
- remove_keys_on_update parent_id")
1949
- stub_elastic_ping
1950
- stub_elastic
1951
- driver.run(default_tag: 'test') do
1952
- driver.feed(sample_record)
1953
- end
1954
- assert(index_cmds[1]["upsert"] != index_cmds[1]["doc"])
1955
- assert(!index_cmds[1]["doc"]["parent_id"])
1956
- assert(index_cmds[1]["upsert"]["parent_id"])
1957
- end
1958
-
1959
- def test_upsert_should_remove_multiple_keys_when_keys_are_skipped
1960
- driver.configure("write_operation upsert
1961
- id_key id
1962
- remove_keys_on_update foo,baz")
1963
- stub_elastic_ping
1964
- stub_elastic
1965
- driver.run(default_tag: 'test') do
1966
- driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "zip" => "zam")
1967
- end
1968
- assert(
1969
- index_cmds[1]["doc"] == {
1970
- "id" => 1,
1971
- "zip" => "zam",
1972
- }
1973
- )
1974
- assert(
1975
- index_cmds[1]["upsert"] == {
1976
- "id" => 1,
1977
- "foo" => "bar",
1978
- "baz" => "quix",
1979
- "zip" => "zam",
1980
- }
1981
- )
1982
- end
1983
-
1984
- def test_upsert_should_remove_keys_from_when_the_keys_are_in_the_record
1985
- driver.configure("write_operation upsert
1986
- id_key id
1987
- remove_keys_on_update_key keys_to_skip")
1988
- stub_elastic_ping
1989
- stub_elastic
1990
- driver.run(default_tag: 'test') do
1991
- driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "keys_to_skip" => ["baz"])
1992
- end
1993
- assert(
1994
- index_cmds[1]["doc"] == {
1995
- "id" => 1,
1996
- "foo" => "bar",
1997
- }
1998
- )
1999
- assert(
2000
- index_cmds[1]["upsert"] == {
2001
- "id" => 1,
2002
- "foo" => "bar",
2003
- "baz" => "quix",
2004
- }
2005
- )
2006
- end
2007
-
2008
- def test_upsert_should_remove_keys_from_key_on_record_has_higher_presedence_than_config
2009
- driver.configure("write_operation upsert
2010
- id_key id
2011
- remove_keys_on_update foo,bar
2012
- remove_keys_on_update_key keys_to_skip")
2013
- stub_elastic_ping
2014
- stub_elastic
2015
- driver.run(default_tag: 'test') do
2016
- driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "keys_to_skip" => ["baz"])
2017
- end
2018
- assert(
2019
- index_cmds[1]["doc"] == {
2020
- "id" => 1,
2021
- # we only expect baz to be stripped here, if the config was more important
2022
- # foo would be stripped too.
2023
- "foo" => "bar",
2024
- }
2025
- )
2026
- assert(
2027
- index_cmds[1]["upsert"] == {
2028
- "id" => 1,
2029
- "foo" => "bar",
2030
- "baz" => "quix",
2031
- }
2032
- )
2033
- end
2034
-
2035
- def test_create_should_write_create_op
2036
- driver.configure("write_operation create
2037
- id_key request_id")
2038
- stub_elastic_ping
2039
- stub_elastic
2040
- driver.run(default_tag: 'test') do
2041
- driver.feed(sample_record)
2042
- end
2043
- assert(index_cmds[0].has_key?("create"))
2044
- end
2045
-
2046
- end
1
+ require 'helper'
2
+ require 'date'
3
+ require 'fluent/test/helpers'
4
+ require 'json'
5
+ require 'fluent/test/driver/output'
6
+ require 'flexmock/test_unit'
7
+
8
+ class ElasticsearchOutput < Test::Unit::TestCase
9
+ include FlexMock::TestCase
10
+ include Fluent::Test::Helpers
11
+
12
+ attr_accessor :index_cmds, :index_command_counts
13
+
14
+ def setup
15
+ Fluent::Test.setup
16
+ require 'fluent/plugin/out_elasticsearch'
17
+ @driver = nil
18
+ log = Fluent::Engine.log
19
+ log.out.logs.slice!(0, log.out.logs.length)
20
+ end
21
+
22
+ def driver(conf='', es_version=5)
23
+ # For request stub to detect compatibility.
24
+ @es_version ||= es_version
25
+ Fluent::Plugin::ElasticsearchOutput.module_eval(<<-CODE)
26
+ def detect_es_major_version
27
+ #{@es_version}
28
+ end
29
+ CODE
30
+ @driver ||= Fluent::Test::Driver::Output.new(Fluent::Plugin::ElasticsearchOutput) {
31
+ # v0.12's test driver assume format definition. This simulates ObjectBufferedOutput format
32
+ if !defined?(Fluent::Plugin::Output)
33
+ def format(tag, time, record)
34
+ [time, record].to_msgpack
35
+ end
36
+ end
37
+ }.configure(conf)
38
+ end
39
+
40
+ def default_type_name
41
+ Fluent::Plugin::ElasticsearchOutput::DEFAULT_TYPE_NAME
42
+ end
43
+
44
+ def sample_record
45
+ {'age' => 26, 'request_id' => '42', 'parent_id' => 'parent', 'routing_id' => 'routing'}
46
+ end
47
+
48
+ def nested_sample_record
49
+ {'nested' =>
50
+ {'age' => 26, 'parent_id' => 'parent', 'routing_id' => 'routing', 'request_id' => '42'}
51
+ }
52
+ end
53
+
54
+ def stub_elastic_ping(url="http://localhost:9200")
55
+ stub_request(:head, url).to_return(:status => 200, :body => "", :headers => {})
56
+ end
57
+
58
+ def stub_elastic(url="http://localhost:9200/_bulk")
59
+ stub_request(:post, url).with do |req|
60
+ @index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
61
+ end
62
+ end
63
+
64
+ def stub_elastic_unavailable(url="http://localhost:9200/_bulk")
65
+ stub_request(:post, url).to_return(:status => [503, "Service Unavailable"])
66
+ end
67
+
68
+ def stub_elastic_with_store_index_command_counts(url="http://localhost:9200/_bulk")
69
+ if @index_command_counts == nil
70
+ @index_command_counts = {}
71
+ @index_command_counts.default = 0
72
+ end
73
+
74
+ stub_request(:post, url).with do |req|
75
+ index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
76
+ @index_command_counts[url] += index_cmds.size
77
+ end
78
+ end
79
+
80
+ def make_response_body(req, error_el = nil, error_status = nil, error = nil)
81
+ req_index_cmds = req.body.split("\n").map { |r| JSON.parse(r) }
82
+ items = []
83
+ count = 0
84
+ ids = 1
85
+ op = nil
86
+ index = nil
87
+ type = nil
88
+ id = nil
89
+ req_index_cmds.each do |cmd|
90
+ if count.even?
91
+ op = cmd.keys[0]
92
+ index = cmd[op]['_index']
93
+ type = cmd[op]['_type']
94
+ if cmd[op].has_key?('_id')
95
+ id = cmd[op]['_id']
96
+ else
97
+ # Note: this appears to be an undocumented feature of Elasticsearch
98
+ # https://www.elastic.co/guide/en/elasticsearch/reference/2.4/docs-bulk.html
99
+ # When you submit an "index" write_operation, with no "_id" field in the
100
+ # metadata header, Elasticsearch will turn this into a "create"
101
+ # operation in the response.
102
+ if "index" == op
103
+ op = "create"
104
+ end
105
+ id = ids
106
+ ids += 1
107
+ end
108
+ else
109
+ item = {
110
+ op => {
111
+ '_index' => index, '_type' => type, '_id' => id, '_version' => 1,
112
+ '_shards' => { 'total' => 1, 'successful' => 1, 'failed' => 0 },
113
+ 'status' => op == 'create' ? 201 : 200
114
+ }
115
+ }
116
+ items.push(item)
117
+ end
118
+ count += 1
119
+ end
120
+ if !error_el.nil? && !error_status.nil? && !error.nil?
121
+ op = items[error_el].keys[0]
122
+ items[error_el][op].delete('_version')
123
+ items[error_el][op].delete('_shards')
124
+ items[error_el][op]['error'] = error
125
+ items[error_el][op]['status'] = error_status
126
+ errors = true
127
+ else
128
+ errors = false
129
+ end
130
+ @index_cmds = items
131
+ body = { 'took' => 6, 'errors' => errors, 'items' => items }
132
+ return body.to_json
133
+ end
134
+
135
+ def stub_elastic_bad_argument(url="http://localhost:9200/_bulk")
136
+ error = {
137
+ "type" => "mapper_parsing_exception",
138
+ "reason" => "failed to parse [...]",
139
+ "caused_by" => {
140
+ "type" => "illegal_argument_exception",
141
+ "reason" => "Invalid format: \"...\""
142
+ }
143
+ }
144
+ stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 400, error), :headers => { 'Content-Type' => 'json' } } })
145
+ end
146
+
147
+ def stub_elastic_bulk_error(url="http://localhost:9200/_bulk")
148
+ error = {
149
+ "type" => "some-unrecognized-error",
150
+ "reason" => "some message printed here ...",
151
+ }
152
+ stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
153
+ end
154
+
155
+ def stub_elastic_bulk_rejected(url="http://localhost:9200/_bulk")
156
+ error = {
157
+ "status" => 500,
158
+ "type" => "es_rejected_execution_exception",
159
+ "reason" => "rejected execution of org.elasticsearch.transport.TransportService$4@1a34d37a on EsThreadPoolExecutor[bulk, queue capacity = 50, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@312a2162[Running, pool size = 32, active threads = 32, queued tasks = 50, completed tasks = 327053]]"
160
+ }
161
+ stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 429, error), :headers => { 'Content-Type' => 'json' } } })
162
+ end
163
+
164
+ def stub_elastic_out_of_memory(url="http://localhost:9200/_bulk")
165
+ error = {
166
+ "status" => 500,
167
+ "type" => "out_of_memory_error",
168
+ "reason" => "Java heap space"
169
+ }
170
+ stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
171
+ end
172
+
173
+ def stub_elastic_unexpected_response_op(url="http://localhost:9200/_bulk")
174
+ error = {
175
+ "category" => "some-other-type",
176
+ "reason" => "some-other-reason"
177
+ }
178
+ 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' } } })
179
+ end
180
+
181
+ def assert_logs_include(logs, msg)
182
+ matches = logs.grep /#{msg}/
183
+ assert_equal(1, matches.length, "Logs do not contain '#{msg}' '#{logs}'")
184
+ end
185
+
186
+ def test_configure
187
+ config = %{
188
+ host logs.google.com
189
+ port 777
190
+ scheme https
191
+ path /es/
192
+ user john
193
+ password doe
194
+ }
195
+ instance = driver(config).instance
196
+
197
+ assert_equal 'logs.google.com', instance.host
198
+ assert_equal 777, instance.port
199
+ assert_equal 'https', instance.scheme
200
+ assert_equal '/es/', instance.path
201
+ assert_equal 'john', instance.user
202
+ assert_equal 'doe', instance.password
203
+ assert_equal :TLSv1, instance.ssl_version
204
+ assert_nil instance.client_key
205
+ assert_nil instance.client_cert
206
+ assert_nil instance.client_key_pass
207
+ assert_false instance.with_transporter_log
208
+ assert_equal :"application/json", instance.content_type
209
+ assert_equal "fluentd", default_type_name
210
+ end
211
+
212
+ test 'configure Content-Type' do
213
+ config = %{
214
+ content_type application/x-ndjson
215
+ }
216
+ instance = driver(config).instance
217
+ assert_equal :"application/x-ndjson", instance.content_type
218
+ end
219
+
220
+ test 'invalid Content-Type' do
221
+ config = %{
222
+ content_type nonexistent/invalid
223
+ }
224
+ assert_raise(Fluent::ConfigError) {
225
+ instance = driver(config).instance
226
+ }
227
+ end
228
+
229
+ test 'invalid specification of times of retrying template installation' do
230
+ config = %{
231
+ max_retry_putting_template -3
232
+ }
233
+ assert_raise(Fluent::ConfigError) {
234
+ instance = driver(config).instance
235
+ }
236
+ end
237
+
238
+ test 'Detected Elasticsearch 7' do
239
+ config = %{
240
+ type_name changed
241
+ }
242
+ instance = driver(config, 7).instance
243
+ assert_equal '_doc', instance.type_name
244
+ end
245
+
246
+ test 'lack of tag in chunk_keys' do
247
+ assert_raise_message(/'tag' in chunk_keys is required./) do
248
+ driver(Fluent::Config::Element.new(
249
+ 'ROOT', '', {
250
+ '@type' => 'elasticsearch',
251
+ 'host' => 'log.google.com',
252
+ 'port' => 777,
253
+ 'scheme' => 'https',
254
+ 'path' => '/es/',
255
+ 'user' => 'john',
256
+ 'pasword' => 'doe',
257
+ }, [
258
+ Fluent::Config::Element.new('buffer', 'mykey', {
259
+ 'chunk_keys' => 'mykey'
260
+ }, [])
261
+ ]
262
+ ))
263
+ end
264
+ end
265
+
266
+ def test_template_already_present
267
+ config = %{
268
+ host logs.google.com
269
+ port 777
270
+ scheme https
271
+ path /es/
272
+ user john
273
+ password doe
274
+ template_name logstash
275
+ template_file /abc123
276
+ }
277
+
278
+ # connection start
279
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
280
+ to_return(:status => 200, :body => "", :headers => {})
281
+ # check if template exists
282
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
283
+ to_return(:status => 200, :body => "", :headers => {})
284
+
285
+ driver(config)
286
+
287
+ assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash")
288
+ end
289
+
290
+ def test_template_create
291
+ cwd = File.dirname(__FILE__)
292
+ template_file = File.join(cwd, 'test_template.json')
293
+
294
+ config = %{
295
+ host logs.google.com
296
+ port 777
297
+ scheme https
298
+ path /es/
299
+ user john
300
+ password doe
301
+ template_name logstash
302
+ template_file #{template_file}
303
+ }
304
+
305
+ # connection start
306
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
307
+ to_return(:status => 200, :body => "", :headers => {})
308
+ # check if template exists
309
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
310
+ to_return(:status => 404, :body => "", :headers => {})
311
+ # creation
312
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash").
313
+ to_return(:status => 200, :body => "", :headers => {})
314
+
315
+ driver(config)
316
+
317
+ assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash", times: 1)
318
+ end
319
+
320
+ def test_template_overwrite
321
+ cwd = File.dirname(__FILE__)
322
+ template_file = File.join(cwd, 'test_template.json')
323
+
324
+ config = %{
325
+ host logs.google.com
326
+ port 777
327
+ scheme https
328
+ path /es/
329
+ user john
330
+ password doe
331
+ template_name logstash
332
+ template_file #{template_file}
333
+ template_overwrite true
334
+ }
335
+
336
+ # connection start
337
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
338
+ to_return(:status => 200, :body => "", :headers => {})
339
+ # check if template exists
340
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
341
+ to_return(:status => 200, :body => "", :headers => {})
342
+ # creation
343
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash").
344
+ to_return(:status => 200, :body => "", :headers => {})
345
+
346
+ driver(config)
347
+
348
+ assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash", times: 1)
349
+ end
350
+
351
+
352
+ def test_template_create_invalid_filename
353
+ config = %{
354
+ host logs.google.com
355
+ port 777
356
+ scheme https
357
+ path /es/
358
+ user john
359
+ password doe
360
+ template_name logstash
361
+ template_file /abc123
362
+ }
363
+
364
+ # connection start
365
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
366
+ to_return(:status => 200, :body => "", :headers => {})
367
+ # check if template exists
368
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
369
+ to_return(:status => 404, :body => "", :headers => {})
370
+
371
+ assert_raise(RuntimeError) {
372
+ driver(config)
373
+ }
374
+ end
375
+
376
+ def test_template_retry_install
377
+ cwd = File.dirname(__FILE__)
378
+ template_file = File.join(cwd, 'test_template.json')
379
+
380
+ config = %{
381
+ host logs.google.com
382
+ port 778
383
+ scheme https
384
+ path /es/
385
+ user john
386
+ password doe
387
+ template_name logstash
388
+ template_file #{template_file}
389
+ max_retry_putting_template 3
390
+ }
391
+
392
+ connection_resets = 0
393
+ # connection start
394
+ stub_request(:head, "https://john:doe@logs.google.com:778/es//").with do |req|
395
+ connection_resets += 1
396
+ end
397
+ # check if template exists
398
+ stub_request(:get, "https://john:doe@logs.google.com:778/es//_template/logstash").with do |req|
399
+ raise Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, "Test message"
400
+ end
401
+ # creation
402
+ stub_request(:put, "https://john:doe@logs.google.com:778/es//_template/logstash").with do |req|
403
+ raise Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, "Test message"
404
+ end
405
+
406
+ assert_raise(Fluent::Plugin::ElasticsearchOutput::ConnectionFailure) do
407
+ driver(config)
408
+ end
409
+
410
+ assert_equal(connection_resets, 4)
411
+ end
412
+
413
+ def test_templates_create
414
+ cwd = File.dirname(__FILE__)
415
+ template_file = File.join(cwd, 'test_template.json')
416
+ config = %{
417
+ host logs.google.com
418
+ port 777
419
+ scheme https
420
+ path /es/
421
+ user john
422
+ password doe
423
+ templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}","logstash3":"#{template_file}" }
424
+ }
425
+
426
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
427
+ to_return(:status => 200, :body => "", :headers => {})
428
+ # check if template exists
429
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
430
+ to_return(:status => 404, :body => "", :headers => {})
431
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
432
+ to_return(:status => 404, :body => "", :headers => {})
433
+
434
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash3").
435
+ to_return(:status => 200, :body => "", :headers => {}) #exists
436
+
437
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
438
+ to_return(:status => 200, :body => "", :headers => {})
439
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
440
+ to_return(:status => 200, :body => "", :headers => {})
441
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3").
442
+ to_return(:status => 200, :body => "", :headers => {})
443
+
444
+ driver(config)
445
+
446
+ assert_requested( :put, "https://john:doe@logs.google.com:777/es//_template/logstash1", times: 1)
447
+ assert_requested( :put, "https://john:doe@logs.google.com:777/es//_template/logstash2", times: 1)
448
+ assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3") #exists
449
+ end
450
+
451
+ def test_templates_overwrite
452
+ cwd = File.dirname(__FILE__)
453
+ template_file = File.join(cwd, 'test_template.json')
454
+ config = %{
455
+ host logs.google.com
456
+ port 777
457
+ scheme https
458
+ path /es/
459
+ user john
460
+ password doe
461
+ templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}","logstash3":"#{template_file}" }
462
+ template_overwrite true
463
+ }
464
+
465
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
466
+ to_return(:status => 200, :body => "", :headers => {})
467
+ # check if template exists
468
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
469
+ to_return(:status => 200, :body => "", :headers => {})
470
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
471
+ to_return(:status => 200, :body => "", :headers => {})
472
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash3").
473
+ to_return(:status => 200, :body => "", :headers => {}) #exists
474
+
475
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
476
+ to_return(:status => 200, :body => "", :headers => {})
477
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
478
+ to_return(:status => 200, :body => "", :headers => {})
479
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3").
480
+ to_return(:status => 200, :body => "", :headers => {})
481
+
482
+ driver(config)
483
+
484
+ assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1", times: 1)
485
+ assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2", times: 1)
486
+ assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash3", times: 1)
487
+ end
488
+
489
+ def test_templates_not_used
490
+ cwd = File.dirname(__FILE__)
491
+ template_file = File.join(cwd, 'test_template.json')
492
+
493
+ config = %{
494
+ host logs.google.com
495
+ port 777
496
+ scheme https
497
+ path /es/
498
+ user john
499
+ password doe
500
+ template_name logstash
501
+ template_file #{template_file}
502
+ templates {"logstash1":"#{template_file}", "logstash2":"#{template_file}" }
503
+ }
504
+ # connection start
505
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
506
+ to_return(:status => 200, :body => "", :headers => {})
507
+ # check if template exists
508
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash").
509
+ to_return(:status => 404, :body => "", :headers => {})
510
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
511
+ to_return(:status => 404, :body => "", :headers => {})
512
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
513
+ to_return(:status => 404, :body => "", :headers => {})
514
+ #creation
515
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash").
516
+ to_return(:status => 200, :body => "", :headers => {})
517
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
518
+ to_return(:status => 200, :body => "", :headers => {})
519
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
520
+ to_return(:status => 200, :body => "", :headers => {})
521
+
522
+ driver(config)
523
+
524
+ assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash", times: 1)
525
+
526
+ assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1")
527
+ assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2")
528
+ end
529
+
530
+ def test_templates_can_be_partially_created_if_error_occurs
531
+ cwd = File.dirname(__FILE__)
532
+ template_file = File.join(cwd, 'test_template.json')
533
+ config = %{
534
+ host logs.google.com
535
+ port 777
536
+ scheme https
537
+ path /es/
538
+ user john
539
+ password doe
540
+ templates {"logstash1":"#{template_file}", "logstash2":"/abc" }
541
+ }
542
+ stub_request(:head, "https://john:doe@logs.google.com:777/es//").
543
+ to_return(:status => 200, :body => "", :headers => {})
544
+ # check if template exists
545
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash1").
546
+ to_return(:status => 404, :body => "", :headers => {})
547
+ stub_request(:get, "https://john:doe@logs.google.com:777/es//_template/logstash2").
548
+ to_return(:status => 404, :body => "", :headers => {})
549
+
550
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1").
551
+ to_return(:status => 200, :body => "", :headers => {})
552
+ stub_request(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2").
553
+ to_return(:status => 200, :body => "", :headers => {})
554
+
555
+ assert_raise(RuntimeError) {
556
+ driver(config)
557
+ }
558
+
559
+ assert_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash1", times: 1)
560
+ assert_not_requested(:put, "https://john:doe@logs.google.com:777/es//_template/logstash2")
561
+ end
562
+
563
+ def test_legacy_hosts_list
564
+ config = %{
565
+ hosts host1:50,host2:100,host3
566
+ scheme https
567
+ path /es/
568
+ port 123
569
+ }
570
+ instance = driver(config).instance
571
+
572
+ assert_equal 3, instance.get_connection_options[:hosts].length
573
+ host1, host2, host3 = instance.get_connection_options[:hosts]
574
+
575
+ assert_equal 'host1', host1[:host]
576
+ assert_equal 50, host1[:port]
577
+ assert_equal 'https', host1[:scheme]
578
+ assert_equal '/es/', host2[:path]
579
+ assert_equal 'host3', host3[:host]
580
+ assert_equal 123, host3[:port]
581
+ assert_equal 'https', host3[:scheme]
582
+ assert_equal '/es/', host3[:path]
583
+ end
584
+
585
+ def test_hosts_list
586
+ config = %{
587
+ hosts https://john:password@host1:443/elastic/,http://host2
588
+ path /default_path
589
+ user default_user
590
+ password default_password
591
+ }
592
+ instance = driver(config).instance
593
+
594
+ assert_equal 2, instance.get_connection_options[:hosts].length
595
+ host1, host2 = instance.get_connection_options[:hosts]
596
+
597
+ assert_equal 'host1', host1[:host]
598
+ assert_equal 443, host1[:port]
599
+ assert_equal 'https', host1[:scheme]
600
+ assert_equal 'john', host1[:user]
601
+ assert_equal 'password', host1[:password]
602
+ assert_equal '/elastic/', host1[:path]
603
+
604
+ assert_equal 'host2', host2[:host]
605
+ assert_equal 'http', host2[:scheme]
606
+ assert_equal 'default_user', host2[:user]
607
+ assert_equal 'default_password', host2[:password]
608
+ assert_equal '/default_path', host2[:path]
609
+ end
610
+
611
+ def test_hosts_list_with_escape_placeholders
612
+ config = %{
613
+ hosts https://%{j+hn}:%{passw@rd}@host1:443/elastic/,http://host2
614
+ path /default_path
615
+ user default_user
616
+ password default_password
617
+ }
618
+ instance = driver(config).instance
619
+
620
+ assert_equal 2, instance.get_connection_options[:hosts].length
621
+ host1, host2 = instance.get_connection_options[:hosts]
622
+
623
+ assert_equal 'host1', host1[:host]
624
+ assert_equal 443, host1[:port]
625
+ assert_equal 'https', host1[:scheme]
626
+ assert_equal 'j%2Bhn', host1[:user]
627
+ assert_equal 'passw%40rd', host1[:password]
628
+ assert_equal '/elastic/', host1[:path]
629
+
630
+ assert_equal 'host2', host2[:host]
631
+ assert_equal 'http', host2[:scheme]
632
+ assert_equal 'default_user', host2[:user]
633
+ assert_equal 'default_password', host2[:password]
634
+ assert_equal '/default_path', host2[:path]
635
+ end
636
+
637
+ def test_single_host_params_and_defaults
638
+ config = %{
639
+ host logs.google.com
640
+ user john
641
+ password doe
642
+ }
643
+ instance = driver(config).instance
644
+
645
+ assert_equal 1, instance.get_connection_options[:hosts].length
646
+ host1 = instance.get_connection_options[:hosts][0]
647
+
648
+ assert_equal 'logs.google.com', host1[:host]
649
+ assert_equal 9200, host1[:port]
650
+ assert_equal 'http', host1[:scheme]
651
+ assert_equal 'john', host1[:user]
652
+ assert_equal 'doe', host1[:password]
653
+ assert_equal nil, host1[:path]
654
+ end
655
+
656
+ def test_single_host_params_and_defaults_with_escape_placeholders
657
+ config = %{
658
+ host logs.google.com
659
+ user %{j+hn}
660
+ password %{d@e}
661
+ }
662
+ instance = driver(config).instance
663
+
664
+ assert_equal 1, instance.get_connection_options[:hosts].length
665
+ host1 = instance.get_connection_options[:hosts][0]
666
+
667
+ assert_equal 'logs.google.com', host1[:host]
668
+ assert_equal 9200, host1[:port]
669
+ assert_equal 'http', host1[:scheme]
670
+ assert_equal 'j%2Bhn', host1[:user]
671
+ assert_equal 'd%40e', host1[:password]
672
+ assert_equal nil, host1[:path]
673
+ end
674
+
675
+ def test_content_type_header
676
+ stub_request(:head, "http://localhost:9200/").
677
+ to_return(:status => 200, :body => "", :headers => {})
678
+ if Elasticsearch::VERSION >= "6.0.2"
679
+ elastic_request = stub_request(:post, "http://localhost:9200/_bulk").
680
+ with(headers: { "Content-Type" => "application/x-ndjson" })
681
+ else
682
+ elastic_request = stub_request(:post, "http://localhost:9200/_bulk").
683
+ with(headers: { "Content-Type" => "application/json" })
684
+ end
685
+ driver.run(default_tag: 'test') do
686
+ driver.feed(sample_record)
687
+ end
688
+ assert_requested(elastic_request)
689
+ end
690
+
691
+ def test_write_message_with_bad_chunk
692
+ driver.configure("target_index_key bad_value\n@log_level debug\n")
693
+ stub_elastic_ping
694
+ stub_elastic
695
+ driver.run(default_tag: 'test') do
696
+ driver.feed({'bad_value'=>"\255"})
697
+ end
698
+ error_log = driver.error_events.map {|e| e.last.message }
699
+
700
+ assert_logs_include(error_log, /(input string invalid)|(invalid byte sequence in UTF-8)/)
701
+ end
702
+
703
+ def test_writes_to_default_index
704
+ stub_elastic_ping
705
+ stub_elastic
706
+ driver.run(default_tag: 'test') do
707
+ driver.feed(sample_record)
708
+ end
709
+ assert_equal('fluentd', index_cmds.first['index']['_index'])
710
+ end
711
+
712
+ def test_writes_to_default_type
713
+ stub_elastic_ping
714
+ stub_elastic
715
+ driver.run(default_tag: 'test') do
716
+ driver.feed(sample_record)
717
+ end
718
+ assert_equal(default_type_name, index_cmds.first['index']['_type'])
719
+ end
720
+
721
+ def test_writes_to_speficied_index
722
+ driver.configure("index_name myindex\n")
723
+ stub_elastic_ping
724
+ stub_elastic
725
+ driver.run(default_tag: 'test') do
726
+ driver.feed(sample_record)
727
+ end
728
+ assert_equal('myindex', index_cmds.first['index']['_index'])
729
+ end
730
+
731
+ class IndexNamePlaceholdersTest < self
732
+ def test_writes_to_speficied_index_with_tag_placeholder
733
+ driver.configure("index_name myindex.${tag}\n")
734
+ stub_elastic_ping
735
+ stub_elastic
736
+ driver.run(default_tag: 'test') do
737
+ driver.feed(sample_record)
738
+ end
739
+ assert_equal('myindex.test', index_cmds.first['index']['_index'])
740
+ end
741
+
742
+ def test_writes_to_speficied_index_with_time_placeholder
743
+ driver.configure(Fluent::Config::Element.new(
744
+ 'ROOT', '', {
745
+ '@type' => 'elasticsearch',
746
+ 'index_name' => 'myindex.%Y.%m.%d',
747
+ }, [
748
+ Fluent::Config::Element.new('buffer', 'tag,time', {
749
+ 'chunk_keys' => ['tag', 'time'],
750
+ 'timekey' => 3600,
751
+ }, [])
752
+ ]
753
+ ))
754
+ stub_elastic_ping
755
+ stub_elastic
756
+ time = Time.parse Date.today.iso8601
757
+ driver.run(default_tag: 'test') do
758
+ driver.feed(time.to_i, sample_record)
759
+ end
760
+ assert_equal("myindex.#{time.utc.strftime("%Y.%m.%d")}", index_cmds.first['index']['_index'])
761
+ end
762
+
763
+ def test_writes_to_speficied_index_with_custom_key_placeholder
764
+ driver.configure(Fluent::Config::Element.new(
765
+ 'ROOT', '', {
766
+ '@type' => 'elasticsearch',
767
+ 'index_name' => 'myindex.${pipeline_id}',
768
+ }, [
769
+ Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
770
+ ]
771
+ ))
772
+ time = Time.parse Date.today.iso8601
773
+ pipeline_id = "mypipeline"
774
+ logstash_index = "myindex.#{pipeline_id}"
775
+ stub_elastic_ping
776
+ stub_elastic
777
+ driver.run(default_tag: 'test') do
778
+ driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
779
+ end
780
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
781
+ end
782
+ end
783
+
784
+ def test_writes_to_speficied_index_uppercase
785
+ driver.configure("index_name MyIndex\n")
786
+ stub_elastic_ping
787
+ stub_elastic
788
+ driver.run(default_tag: 'test') do
789
+ driver.feed(sample_record)
790
+ end
791
+ # Allthough index_name has upper-case characters,
792
+ # it should be set as lower-case when sent to elasticsearch.
793
+ assert_equal('myindex', index_cmds.first['index']['_index'])
794
+ end
795
+
796
+ def test_writes_to_target_index_key
797
+ driver.configure("target_index_key @target_index\n")
798
+ stub_elastic_ping
799
+ stub_elastic
800
+ record = sample_record.clone
801
+ driver.run(default_tag: 'test') do
802
+ driver.feed(sample_record.merge('@target_index' => 'local-override'))
803
+ end
804
+ assert_equal('local-override', index_cmds.first['index']['_index'])
805
+ assert_nil(index_cmds[1]['@target_index'])
806
+ end
807
+
808
+ def test_writes_to_target_index_key_logstash
809
+ driver.configure("target_index_key @target_index
810
+ logstash_format true")
811
+ time = Time.parse Date.today.iso8601
812
+ stub_elastic_ping
813
+ stub_elastic
814
+ driver.run(default_tag: 'test') do
815
+ driver.feed(time.to_i, sample_record.merge('@target_index' => 'local-override'))
816
+ end
817
+ assert_equal('local-override', index_cmds.first['index']['_index'])
818
+ end
819
+
820
+ def test_writes_to_target_index_key_logstash_uppercase
821
+ driver.configure("target_index_key @target_index
822
+ logstash_format true")
823
+ time = Time.parse Date.today.iso8601
824
+ stub_elastic_ping
825
+ stub_elastic
826
+ driver.run(default_tag: 'test') do
827
+ driver.feed(time.to_i, sample_record.merge('@target_index' => 'local-override'))
828
+ end
829
+ # Allthough @target_index has upper-case characters,
830
+ # it should be set as lower-case when sent to elasticsearch.
831
+ assert_equal('local-override', index_cmds.first['index']['_index'])
832
+ end
833
+
834
+ def test_writes_to_default_index_with_pipeline
835
+ pipeline = "fluentd"
836
+ driver.configure("pipeline #{pipeline}")
837
+ stub_elastic_ping
838
+ stub_elastic
839
+ driver.run(default_tag: 'test') do
840
+ driver.feed(sample_record)
841
+ end
842
+ assert_equal(pipeline, index_cmds.first['index']['pipeline'])
843
+ end
844
+
845
+ def test_writes_to_target_index_key_fallack
846
+ driver.configure("target_index_key @target_index\n")
847
+ stub_elastic_ping
848
+ stub_elastic
849
+ driver.run(default_tag: 'test') do
850
+ driver.feed(sample_record)
851
+ end
852
+ assert_equal('fluentd', index_cmds.first['index']['_index'])
853
+ end
854
+
855
+ def test_writes_to_target_index_key_fallack_logstash
856
+ driver.configure("target_index_key @target_index\n
857
+ logstash_format true")
858
+ time = Time.parse Date.today.iso8601
859
+ logstash_index = "logstash-#{time.getutc.strftime("%Y.%m.%d")}"
860
+ stub_elastic_ping
861
+ stub_elastic
862
+ driver.run(default_tag: 'test') do
863
+ driver.feed(time.to_i, sample_record)
864
+ end
865
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
866
+ end
867
+
868
+ data("border" => {"es_version" => 6, "_type" => "mytype"},
869
+ "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
870
+ )
871
+ def test_writes_to_speficied_type(data)
872
+ driver('', data["es_version"]).configure("type_name mytype\n")
873
+ stub_elastic_ping
874
+ stub_elastic
875
+ driver.run(default_tag: 'test') do
876
+ driver.feed(sample_record)
877
+ end
878
+ assert_equal(data['_type'], index_cmds.first['index']['_type'])
879
+ end
880
+
881
+ data("border" => {"es_version" => 6, "_type" => "mytype.test"},
882
+ "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
883
+ )
884
+ def test_writes_to_speficied_type_with_placeholders(data)
885
+ driver('', data["es_version"]).configure("type_name mytype.${tag}\n")
886
+ stub_elastic_ping
887
+ stub_elastic
888
+ driver.run(default_tag: 'test') do
889
+ driver.feed(sample_record)
890
+ end
891
+ assert_equal(data['_type'], index_cmds.first['index']['_type'])
892
+ end
893
+
894
+ data("old" => {"es_version" => 2, "_type" => "local-override"},
895
+ "old_behavior" => {"es_version" => 5, "_type" => "local-override"},
896
+ "border" => {"es_version" => 6, "_type" => "fluentd"},
897
+ "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
898
+ )
899
+ def test_writes_to_target_type_key(data)
900
+ driver('', data["es_version"]).configure("target_type_key @target_type\n")
901
+ stub_elastic_ping
902
+ stub_elastic
903
+ record = sample_record.clone
904
+ driver.run(default_tag: 'test') do
905
+ driver.feed(sample_record.merge('@target_type' => 'local-override'))
906
+ end
907
+ assert_equal(data["_type"], index_cmds.first['index']['_type'])
908
+ assert_nil(index_cmds[1]['@target_type'])
909
+ end
910
+
911
+ def test_writes_to_target_type_key_fallack_to_default
912
+ driver.configure("target_type_key @target_type\n")
913
+ stub_elastic_ping
914
+ stub_elastic
915
+ driver.run(default_tag: 'test') do
916
+ driver.feed(sample_record)
917
+ end
918
+ assert_equal(default_type_name, index_cmds.first['index']['_type'])
919
+ end
920
+
921
+ def test_writes_to_target_type_key_fallack_to_type_name
922
+ driver.configure("target_type_key @target_type
923
+ type_name mytype")
924
+ stub_elastic_ping
925
+ stub_elastic
926
+ driver.run(default_tag: 'test') do
927
+ driver.feed(sample_record)
928
+ end
929
+ assert_equal('mytype', index_cmds.first['index']['_type'])
930
+ end
931
+
932
+ data("old" => {"es_version" => 2, "_type" => "local-override"},
933
+ "old_behavior" => {"es_version" => 5, "_type" => "local-override"},
934
+ "border" => {"es_version" => 6, "_type" => "fluentd"},
935
+ "fixed_behavior"=> {"es_version" => 7, "_type" => "_doc"},
936
+ )
937
+ def test_writes_to_target_type_key_nested(data)
938
+ driver('', data["es_version"]).configure("target_type_key kubernetes.labels.log_type\n")
939
+ stub_elastic_ping
940
+ stub_elastic
941
+ driver.run(default_tag: 'test') do
942
+ driver.feed(sample_record.merge('kubernetes' => {
943
+ 'labels' => {
944
+ 'log_type' => 'local-override'
945
+ }
946
+ }))
947
+ end
948
+ assert_equal(data["_type"], index_cmds.first['index']['_type'])
949
+ assert_nil(index_cmds[1]['kubernetes']['labels']['log_type'])
950
+ end
951
+
952
+ def test_writes_to_target_type_key_fallack_to_default_nested
953
+ driver.configure("target_type_key kubernetes.labels.log_type\n")
954
+ stub_elastic_ping
955
+ stub_elastic
956
+ driver.run(default_tag: 'test') do
957
+ driver.feed(sample_record.merge('kubernetes' => {
958
+ 'labels' => {
959
+ 'other_labels' => 'test'
960
+ }
961
+ }))
962
+ end
963
+ assert_equal(default_type_name, index_cmds.first['index']['_type'])
964
+ end
965
+
966
+ def test_writes_to_speficied_host
967
+ driver.configure("host 192.168.33.50\n")
968
+ stub_elastic_ping("http://192.168.33.50:9200")
969
+ elastic_request = stub_elastic("http://192.168.33.50:9200/_bulk")
970
+ driver.run(default_tag: 'test') do
971
+ driver.feed(sample_record)
972
+ end
973
+ assert_requested(elastic_request)
974
+ end
975
+
976
+ def test_writes_to_speficied_port
977
+ driver.configure("port 9201\n")
978
+ stub_elastic_ping("http://localhost:9201")
979
+ elastic_request = stub_elastic("http://localhost:9201/_bulk")
980
+ driver.run(default_tag: 'test') do
981
+ driver.feed(sample_record)
982
+ end
983
+ assert_requested(elastic_request)
984
+ end
985
+
986
+ def test_writes_to_multi_hosts
987
+ hosts = [['192.168.33.50', 9201], ['192.168.33.51', 9201], ['192.168.33.52', 9201]]
988
+ hosts_string = hosts.map {|x| "#{x[0]}:#{x[1]}"}.compact.join(',')
989
+
990
+ driver.configure("hosts #{hosts_string}")
991
+
992
+ hosts.each do |host_info|
993
+ host, port = host_info
994
+ stub_elastic_ping("http://#{host}:#{port}")
995
+ stub_elastic_with_store_index_command_counts("http://#{host}:#{port}/_bulk")
996
+ end
997
+
998
+ driver.run(default_tag: 'test') do
999
+ 1000.times do
1000
+ driver.feed(sample_record.merge('age'=>rand(100)))
1001
+ end
1002
+ end
1003
+
1004
+ # @note: we cannot make multi chunks with options (flush_interval, buffer_chunk_limit)
1005
+ # it's Fluentd test driver's constraint
1006
+ # so @index_command_counts.size is always 1
1007
+
1008
+ assert(@index_command_counts.size > 0, "not working with hosts options")
1009
+
1010
+ total = 0
1011
+ @index_command_counts.each do |url, count|
1012
+ total += count
1013
+ end
1014
+ assert_equal(2000, total)
1015
+ end
1016
+
1017
+ def test_nested_record_with_flattening_on
1018
+ driver.configure("flatten_hashes true
1019
+ flatten_hashes_separator |")
1020
+
1021
+ original_hash = {"foo" => {"bar" => "baz"}, "people" => [
1022
+ {"age" => "25", "height" => "1ft"},
1023
+ {"age" => "30", "height" => "2ft"}
1024
+ ]}
1025
+
1026
+ expected_output = {"foo|bar"=>"baz", "people" => [
1027
+ {"age" => "25", "height" => "1ft"},
1028
+ {"age" => "30", "height" => "2ft"}
1029
+ ]}
1030
+
1031
+ stub_elastic_ping
1032
+ stub_elastic
1033
+ driver.run(default_tag: 'test') do
1034
+ driver.feed(original_hash)
1035
+ end
1036
+ assert_equal expected_output, index_cmds[1]
1037
+ end
1038
+
1039
+ def test_nested_record_with_flattening_off
1040
+ # flattening off by default
1041
+
1042
+ original_hash = {"foo" => {"bar" => "baz"}}
1043
+ expected_output = {"foo" => {"bar" => "baz"}}
1044
+
1045
+ stub_elastic_ping
1046
+ stub_elastic
1047
+ driver.run(default_tag: 'test') do
1048
+ driver.feed(original_hash)
1049
+ end
1050
+ assert_equal expected_output, index_cmds[1]
1051
+ end
1052
+
1053
+ def test_makes_bulk_request
1054
+ stub_elastic_ping
1055
+ stub_elastic
1056
+ driver.run(default_tag: 'test') do
1057
+ driver.feed(sample_record)
1058
+ driver.feed(sample_record.merge('age' => 27))
1059
+ end
1060
+ assert_equal(4, index_cmds.count)
1061
+ end
1062
+
1063
+ def test_all_records_are_preserved_in_bulk
1064
+ stub_elastic_ping
1065
+ stub_elastic
1066
+ driver.run(default_tag: 'test') do
1067
+ driver.feed(sample_record)
1068
+ driver.feed(sample_record.merge('age' => 27))
1069
+ end
1070
+ assert_equal(26, index_cmds[1]['age'])
1071
+ assert_equal(27, index_cmds[3]['age'])
1072
+ end
1073
+
1074
+ def test_writes_to_logstash_index
1075
+ driver.configure("logstash_format true\n")
1076
+ #
1077
+ # This is 1 second past midnight in BST, so the UTC index should be the day before
1078
+ dt = DateTime.new(2015, 6, 1, 0, 0, 1, "+01:00")
1079
+ logstash_index = "logstash-2015.05.31"
1080
+ stub_elastic_ping
1081
+ stub_elastic
1082
+ driver.run(default_tag: 'test') do
1083
+ driver.feed(dt.to_time.to_i, sample_record)
1084
+ end
1085
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1086
+ end
1087
+
1088
+ def test_writes_to_logstash_non_utc_index
1089
+ driver.configure("logstash_format true
1090
+ utc_index false")
1091
+ # When using `utc_index false` the index time will be the local day of
1092
+ # ingestion time
1093
+ time = Date.today.to_time
1094
+ index = "logstash-#{time.strftime("%Y.%m.%d")}"
1095
+ stub_elastic_ping
1096
+ stub_elastic
1097
+ driver.run(default_tag: 'test') do
1098
+ driver.feed(time.to_i, sample_record)
1099
+ end
1100
+ assert_equal(index, index_cmds.first['index']['_index'])
1101
+ end
1102
+
1103
+ def test_writes_to_logstash_index_with_specified_prefix
1104
+ driver.configure("logstash_format true
1105
+ logstash_prefix myprefix")
1106
+ time = Time.parse Date.today.iso8601
1107
+ logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
1108
+ stub_elastic_ping
1109
+ stub_elastic
1110
+ driver.run(default_tag: 'test') do
1111
+ driver.feed(time.to_i, sample_record)
1112
+ end
1113
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1114
+ end
1115
+
1116
+ def test_writes_to_logstash_index_with_specified_prefix_and_separator
1117
+ separator = '_'
1118
+ driver.configure("logstash_format true
1119
+ logstash_prefix_separator #{separator}
1120
+ logstash_prefix myprefix")
1121
+ time = Time.parse Date.today.iso8601
1122
+ logstash_index = "myprefix#{separator}#{time.getutc.strftime("%Y.%m.%d")}"
1123
+ stub_elastic_ping
1124
+ stub_elastic
1125
+ driver.run(default_tag: 'test') do
1126
+ driver.feed(time.to_i, sample_record)
1127
+ end
1128
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1129
+ end
1130
+
1131
+ class LogStashPrefixPlaceholdersTest < self
1132
+ def test_writes_to_logstash_index_with_specified_prefix_and_tag_placeholder
1133
+ driver.configure("logstash_format true
1134
+ logstash_prefix myprefix-${tag}")
1135
+ time = Time.parse Date.today.iso8601
1136
+ logstash_index = "myprefix-test-#{time.getutc.strftime("%Y.%m.%d")}"
1137
+ stub_elastic_ping
1138
+ stub_elastic
1139
+ driver.run(default_tag: 'test') do
1140
+ driver.feed(time.to_i, sample_record)
1141
+ end
1142
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1143
+ end
1144
+
1145
+ def test_writes_to_logstash_index_with_specified_prefix_and_time_placeholder
1146
+ driver.configure(Fluent::Config::Element.new(
1147
+ 'ROOT', '', {
1148
+ '@type' => 'elasticsearch',
1149
+ 'logstash_format' => true,
1150
+ 'logstash_prefix' => 'myprefix-%H',
1151
+ }, [
1152
+ Fluent::Config::Element.new('buffer', 'tag,time', {
1153
+ 'chunk_keys' => ['tag', 'time'],
1154
+ 'timekey' => 3600,
1155
+ }, [])
1156
+ ]
1157
+ ))
1158
+ time = Time.parse Date.today.iso8601
1159
+ logstash_index = "myprefix-#{time.getutc.strftime("%H")}-#{time.getutc.strftime("%Y.%m.%d")}"
1160
+ stub_elastic_ping
1161
+ stub_elastic
1162
+ driver.run(default_tag: 'test') do
1163
+ driver.feed(time.to_i, sample_record)
1164
+ end
1165
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1166
+ end
1167
+
1168
+ def test_writes_to_logstash_index_with_specified_prefix_and_custom_key_placeholder
1169
+ driver.configure(Fluent::Config::Element.new(
1170
+ 'ROOT', '', {
1171
+ '@type' => 'elasticsearch',
1172
+ 'logstash_format' => true,
1173
+ 'logstash_prefix' => 'myprefix-${pipeline_id}',
1174
+ }, [
1175
+ Fluent::Config::Element.new('buffer', 'tag,pipeline_id', {}, [])
1176
+ ]
1177
+ ))
1178
+ time = Time.parse Date.today.iso8601
1179
+ pipeline_id = "mypipeline"
1180
+ logstash_index = "myprefix-#{pipeline_id}-#{time.getutc.strftime("%Y.%m.%d")}"
1181
+ stub_elastic_ping
1182
+ stub_elastic
1183
+ driver.run(default_tag: 'test') do
1184
+ driver.feed(time.to_i, sample_record.merge({"pipeline_id" => pipeline_id}))
1185
+ end
1186
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1187
+ end
1188
+ end
1189
+
1190
+ def test_writes_to_logstash_index_with_specified_prefix_uppercase
1191
+ driver.configure("logstash_format true
1192
+ logstash_prefix MyPrefix")
1193
+ time = Time.parse Date.today.iso8601
1194
+ logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
1195
+ stub_elastic_ping
1196
+ stub_elastic
1197
+ driver.run(default_tag: 'test') do
1198
+ driver.feed(time.to_i, sample_record)
1199
+ end
1200
+ # Allthough logstash_prefix has upper-case characters,
1201
+ # it should be set as lower-case when sent to elasticsearch.
1202
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1203
+ end
1204
+
1205
+ def test_writes_to_logstash_index_with_specified_dateformat
1206
+ driver.configure("logstash_format true
1207
+ logstash_dateformat %Y.%m")
1208
+ time = Time.parse Date.today.iso8601
1209
+ logstash_index = "logstash-#{time.getutc.strftime("%Y.%m")}"
1210
+ stub_elastic_ping
1211
+ stub_elastic
1212
+ driver.run(default_tag: 'test') do
1213
+ driver.feed(time.to_i, sample_record)
1214
+ end
1215
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1216
+ end
1217
+
1218
+ def test_writes_to_logstash_index_with_specified_prefix_and_dateformat
1219
+ driver.configure("logstash_format true
1220
+ logstash_prefix myprefix
1221
+ logstash_dateformat %Y.%m")
1222
+ time = Time.parse Date.today.iso8601
1223
+ logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m")}"
1224
+ stub_elastic_ping
1225
+ stub_elastic
1226
+ driver.run(default_tag: 'test') do
1227
+ driver.feed(time.to_i, sample_record)
1228
+ end
1229
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
1230
+ end
1231
+
1232
+ def test_error_if_tag_not_in_chunk_keys
1233
+ assert_raise(Fluent::ConfigError) {
1234
+ config = %{
1235
+ <buffer foo>
1236
+ </buffer>
1237
+ }
1238
+ driver.configure(config)
1239
+ }
1240
+ end
1241
+
1242
+ def test_can_use_custom_chunk_along_with_tag
1243
+ config = %{
1244
+ <buffer tag, foo>
1245
+ </buffer>
1246
+ }
1247
+ driver.configure(config)
1248
+ end
1249
+
1250
+ def test_doesnt_add_logstash_timestamp_by_default
1251
+ stub_elastic_ping
1252
+ stub_elastic
1253
+ driver.run(default_tag: 'test') do
1254
+ driver.feed(sample_record)
1255
+ end
1256
+ assert_nil(index_cmds[1]['@timestamp'])
1257
+ end
1258
+
1259
+ def test_adds_timestamp_when_logstash
1260
+ driver.configure("logstash_format true\n")
1261
+ stub_elastic_ping
1262
+ stub_elastic
1263
+ ts = DateTime.now
1264
+ time = Fluent::EventTime.from_time(ts.to_time)
1265
+ driver.run(default_tag: 'test') do
1266
+ driver.feed(time, sample_record)
1267
+ end
1268
+ assert(index_cmds[1].has_key? '@timestamp')
1269
+ assert_equal(index_cmds[1]['@timestamp'], ts.iso8601(9))
1270
+ end
1271
+
1272
+ def test_adds_timestamp_when_include_timestamp
1273
+ driver.configure("include_timestamp true\n")
1274
+ stub_elastic_ping
1275
+ stub_elastic
1276
+ ts = DateTime.now
1277
+ time = Fluent::EventTime.from_time(ts.to_time)
1278
+ driver.run(default_tag: 'test') do
1279
+ driver.feed(time, sample_record)
1280
+ end
1281
+ assert(index_cmds[1].has_key? '@timestamp')
1282
+ assert_equal(index_cmds[1]['@timestamp'], ts.iso8601(9))
1283
+ end
1284
+
1285
+ def test_uses_custom_timestamp_when_included_in_record
1286
+ driver.configure("logstash_format true\n")
1287
+ stub_elastic_ping
1288
+ stub_elastic
1289
+ ts = DateTime.new(2001,2,3).iso8601
1290
+ driver.run(default_tag: 'test') do
1291
+ driver.feed(sample_record.merge!('@timestamp' => ts))
1292
+ end
1293
+ assert(index_cmds[1].has_key? '@timestamp')
1294
+ assert_equal(index_cmds[1]['@timestamp'], ts)
1295
+ end
1296
+
1297
+ def test_uses_custom_timestamp_when_included_in_record_without_logstash
1298
+ driver.configure("include_timestamp true\n")
1299
+ stub_elastic_ping
1300
+ stub_elastic
1301
+ ts = DateTime.new(2001,2,3).iso8601
1302
+ driver.run(default_tag: 'test') do
1303
+ driver.feed(sample_record.merge!('@timestamp' => ts))
1304
+ end
1305
+ assert(index_cmds[1].has_key? '@timestamp')
1306
+ assert_equal(index_cmds[1]['@timestamp'], ts)
1307
+ end
1308
+
1309
+ def test_uses_custom_time_key
1310
+ driver.configure("logstash_format true
1311
+ time_key vtm\n")
1312
+ stub_elastic_ping
1313
+ stub_elastic
1314
+ ts = DateTime.new(2001,2,3).iso8601(9)
1315
+ driver.run(default_tag: 'test') do
1316
+ driver.feed(sample_record.merge!('vtm' => ts))
1317
+ end
1318
+ assert(index_cmds[1].has_key? '@timestamp')
1319
+ assert_equal(index_cmds[1]['@timestamp'], ts)
1320
+ end
1321
+
1322
+ def test_uses_custom_time_key_with_format
1323
+ driver.configure("logstash_format true
1324
+ time_key_format %Y-%m-%d %H:%M:%S.%N%z
1325
+ time_key vtm\n")
1326
+ stub_elastic_ping
1327
+ stub_elastic
1328
+ ts = "2001-02-03 13:14:01.673+02:00"
1329
+ driver.run(default_tag: 'test') do
1330
+ driver.feed(sample_record.merge!('vtm' => ts))
1331
+ end
1332
+ assert(index_cmds[1].has_key? '@timestamp')
1333
+ assert_equal(index_cmds[1]['@timestamp'], DateTime.parse(ts).iso8601(9))
1334
+ assert_equal("logstash-2001.02.03", index_cmds[0]['index']['_index'])
1335
+ end
1336
+
1337
+ def test_uses_custom_time_key_with_format_without_logstash
1338
+ driver.configure("include_timestamp true
1339
+ index_name test
1340
+ time_key_format %Y-%m-%d %H:%M:%S.%N%z
1341
+ time_key vtm\n")
1342
+ stub_elastic_ping
1343
+ stub_elastic
1344
+ ts = "2001-02-03 13:14:01.673+02:00"
1345
+ driver.run(default_tag: 'test') do
1346
+ driver.feed(sample_record.merge!('vtm' => ts))
1347
+ end
1348
+ assert(index_cmds[1].has_key? '@timestamp')
1349
+ assert_equal(index_cmds[1]['@timestamp'], DateTime.parse(ts).iso8601(9))
1350
+ assert_equal("test", index_cmds[0]['index']['_index'])
1351
+ end
1352
+
1353
+ def test_uses_custom_time_key_exclude_timekey
1354
+ driver.configure("logstash_format true
1355
+ time_key vtm
1356
+ time_key_exclude_timestamp true\n")
1357
+ stub_elastic_ping
1358
+ stub_elastic
1359
+ ts = DateTime.new(2001,2,3).iso8601
1360
+ driver.run(default_tag: 'test') do
1361
+ driver.feed(sample_record.merge!('vtm' => ts))
1362
+ end
1363
+ assert(!index_cmds[1].key?('@timestamp'), '@timestamp should be messing')
1364
+ end
1365
+
1366
+ def test_uses_custom_time_key_format
1367
+ driver.configure("logstash_format true
1368
+ time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n")
1369
+ stub_elastic_ping
1370
+ stub_elastic
1371
+ ts = "2001-02-03T13:14:01.673+02:00"
1372
+ driver.run(default_tag: 'test') do
1373
+ driver.feed(sample_record.merge!('@timestamp' => ts))
1374
+ end
1375
+ assert_equal("logstash-2001.02.03", index_cmds[0]['index']['_index'])
1376
+ assert(index_cmds[1].has_key? '@timestamp')
1377
+ assert_equal(index_cmds[1]['@timestamp'], ts)
1378
+ end
1379
+
1380
+ def test_uses_custom_time_key_format_without_logstash
1381
+ driver.configure("include_timestamp true
1382
+ index_name test
1383
+ time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n")
1384
+ stub_elastic_ping
1385
+ stub_elastic
1386
+ ts = "2001-02-03T13:14:01.673+02:00"
1387
+ driver.run(default_tag: 'test') do
1388
+ driver.feed(sample_record.merge!('@timestamp' => ts))
1389
+ end
1390
+ assert_equal("test", index_cmds[0]['index']['_index'])
1391
+ assert(index_cmds[1].has_key? '@timestamp')
1392
+ assert_equal(index_cmds[1]['@timestamp'], ts)
1393
+ end
1394
+
1395
+ data(:default => nil,
1396
+ :custom_tag => 'es_plugin.output.time.error')
1397
+ def test_uses_custom_time_key_format_logs_an_error(tag_for_error)
1398
+ tag_config = tag_for_error ? "time_parse_error_tag #{tag_for_error}" : ''
1399
+ tag_for_error = 'Fluent::ElasticsearchOutput::TimeParser.error' if tag_for_error.nil?
1400
+ driver.configure("logstash_format true
1401
+ time_key_format %Y-%m-%dT%H:%M:%S.%N%z\n#{tag_config}\n")
1402
+ stub_elastic_ping
1403
+ stub_elastic
1404
+
1405
+ ts = "2001/02/03 13:14:01,673+02:00"
1406
+ index = "logstash-#{Date.today.strftime("%Y.%m.%d")}"
1407
+
1408
+ flexmock(driver.instance.router).should_receive(:emit_error_event)
1409
+ .with(tag_for_error, Fluent::EventTime, Hash, ArgumentError).once
1410
+ driver.run(default_tag: 'test') do
1411
+ driver.feed(sample_record.merge!('@timestamp' => ts))
1412
+ end
1413
+
1414
+ assert_equal(index, index_cmds[0]['index']['_index'])
1415
+ assert(index_cmds[1].has_key? '@timestamp')
1416
+ assert_equal(index_cmds[1]['@timestamp'], ts)
1417
+ end
1418
+
1419
+
1420
+ def test_uses_custom_time_key_format_obscure_format
1421
+ driver.configure("logstash_format true
1422
+ time_key_format %a %b %d %H:%M:%S %Z %Y\n")
1423
+ stub_elastic_ping
1424
+ stub_elastic
1425
+ ts = "Thu Nov 29 14:33:20 GMT 2001"
1426
+ driver.run(default_tag: 'test') do
1427
+ driver.feed(sample_record.merge!('@timestamp' => ts))
1428
+ end
1429
+ assert_equal("logstash-2001.11.29", index_cmds[0]['index']['_index'])
1430
+ assert(index_cmds[1].has_key? '@timestamp')
1431
+ assert_equal(index_cmds[1]['@timestamp'], ts)
1432
+ end
1433
+
1434
+ def test_uses_nanosecond_precision_by_default
1435
+ driver.configure("logstash_format true\n")
1436
+ stub_elastic_ping
1437
+ stub_elastic
1438
+ time = Fluent::EventTime.new(Time.now.to_i, 123456789)
1439
+ driver.run(default_tag: 'test') do
1440
+ driver.feed(time, sample_record)
1441
+ end
1442
+ assert(index_cmds[1].has_key? '@timestamp')
1443
+ assert_equal(index_cmds[1]['@timestamp'], Time.at(time).iso8601(9))
1444
+ end
1445
+
1446
+ def test_uses_subsecond_precision_when_configured
1447
+ driver.configure("logstash_format true
1448
+ time_precision 3\n")
1449
+ stub_elastic_ping
1450
+ stub_elastic
1451
+ time = Fluent::EventTime.new(Time.now.to_i, 123456789)
1452
+ driver.run(default_tag: 'test') do
1453
+ driver.feed(time, sample_record)
1454
+ end
1455
+ assert(index_cmds[1].has_key? '@timestamp')
1456
+ assert_equal(index_cmds[1]['@timestamp'], Time.at(time).iso8601(3))
1457
+ end
1458
+
1459
+ def test_doesnt_add_tag_key_by_default
1460
+ stub_elastic_ping
1461
+ stub_elastic
1462
+ driver.run(default_tag: 'test') do
1463
+ driver.feed(sample_record)
1464
+ end
1465
+ assert_nil(index_cmds[1]['tag'])
1466
+ end
1467
+
1468
+ def test_adds_tag_key_when_configured
1469
+ driver.configure("include_tag_key true\n")
1470
+ stub_elastic_ping
1471
+ stub_elastic
1472
+ driver.run(default_tag: 'mytag') do
1473
+ driver.feed(sample_record)
1474
+ end
1475
+ assert(index_cmds[1].has_key?('tag'))
1476
+ assert_equal(index_cmds[1]['tag'], 'mytag')
1477
+ end
1478
+
1479
+ def test_adds_id_key_when_configured
1480
+ driver.configure("id_key request_id\n")
1481
+ stub_elastic_ping
1482
+ stub_elastic
1483
+ driver.run(default_tag: 'test') do
1484
+ driver.feed(sample_record)
1485
+ end
1486
+ assert_equal(index_cmds[0]['index']['_id'], '42')
1487
+ end
1488
+
1489
+ class NestedIdKeyTest < self
1490
+ def test_adds_nested_id_key_with_dot
1491
+ driver.configure("id_key nested.request_id\n")
1492
+ stub_elastic_ping
1493
+ stub_elastic
1494
+ driver.run(default_tag: 'test') do
1495
+ driver.feed(nested_sample_record)
1496
+ end
1497
+ assert_equal(index_cmds[0]['index']['_id'], '42')
1498
+ end
1499
+
1500
+ def test_adds_nested_id_key_with_dollar_dot
1501
+ driver.configure("id_key $.nested.request_id\n")
1502
+ stub_elastic_ping
1503
+ stub_elastic
1504
+ driver.run(default_tag: 'test') do
1505
+ driver.feed(nested_sample_record)
1506
+ end
1507
+ assert_equal(index_cmds[0]['index']['_id'], '42')
1508
+ end
1509
+
1510
+ def test_adds_nested_id_key_with_bracket
1511
+ driver.configure("id_key $['nested']['request_id']\n")
1512
+ stub_elastic_ping
1513
+ stub_elastic
1514
+ driver.run(default_tag: 'test') do
1515
+ driver.feed(nested_sample_record)
1516
+ end
1517
+ assert_equal(index_cmds[0]['index']['_id'], '42')
1518
+ end
1519
+ end
1520
+
1521
+ def test_doesnt_add_id_key_if_missing_when_configured
1522
+ driver.configure("id_key another_request_id\n")
1523
+ stub_elastic_ping
1524
+ stub_elastic
1525
+ driver.run(default_tag: 'test') do
1526
+ driver.feed(sample_record)
1527
+ end
1528
+ assert(!index_cmds[0]['index'].has_key?('_id'))
1529
+ end
1530
+
1531
+ def test_adds_id_key_when_not_configured
1532
+ stub_elastic_ping
1533
+ stub_elastic
1534
+ driver.run(default_tag: 'test') do
1535
+ driver.feed(sample_record)
1536
+ end
1537
+ assert(!index_cmds[0]['index'].has_key?('_id'))
1538
+ end
1539
+
1540
+ def test_adds_parent_key_when_configured
1541
+ driver.configure("parent_key parent_id\n")
1542
+ stub_elastic_ping
1543
+ stub_elastic
1544
+ driver.run(default_tag: 'test') do
1545
+ driver.feed(sample_record)
1546
+ end
1547
+ assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1548
+ end
1549
+
1550
+ class NestedParentKeyTest < self
1551
+ def test_adds_nested_parent_key_with_dot
1552
+ driver.configure("parent_key nested.parent_id\n")
1553
+ stub_elastic_ping
1554
+ stub_elastic
1555
+ driver.run(default_tag: 'test') do
1556
+ driver.feed(nested_sample_record)
1557
+ end
1558
+ assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1559
+ end
1560
+
1561
+ def test_adds_nested_parent_key_with_dollar_dot
1562
+ driver.configure("parent_key $.nested.parent_id\n")
1563
+ stub_elastic_ping
1564
+ stub_elastic
1565
+ driver.run(default_tag: 'test') do
1566
+ driver.feed(nested_sample_record)
1567
+ end
1568
+ assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1569
+ end
1570
+
1571
+ def test_adds_nested_parent_key_with_bracket
1572
+ driver.configure("parent_key $['nested']['parent_id']\n")
1573
+ stub_elastic_ping
1574
+ stub_elastic
1575
+ driver.run(default_tag: 'test') do
1576
+ driver.feed(nested_sample_record)
1577
+ end
1578
+ assert_equal(index_cmds[0]['index']['_parent'], 'parent')
1579
+ end
1580
+ end
1581
+
1582
+ def test_doesnt_add_parent_key_if_missing_when_configured
1583
+ driver.configure("parent_key another_parent_id\n")
1584
+ stub_elastic_ping
1585
+ stub_elastic
1586
+ driver.run(default_tag: 'test') do
1587
+ driver.feed(sample_record)
1588
+ end
1589
+ assert(!index_cmds[0]['index'].has_key?('_parent'))
1590
+ end
1591
+
1592
+ def test_adds_parent_key_when_not_configured
1593
+ stub_elastic_ping
1594
+ stub_elastic
1595
+ driver.run(default_tag: 'test') do
1596
+ driver.feed(sample_record)
1597
+ end
1598
+ assert(!index_cmds[0]['index'].has_key?('_parent'))
1599
+ end
1600
+
1601
+ def test_adds_routing_key_when_configured
1602
+ driver.configure("routing_key routing_id\n")
1603
+ stub_elastic_ping
1604
+ stub_elastic
1605
+ driver.run(default_tag: 'test') do
1606
+ driver.feed(sample_record)
1607
+ end
1608
+ assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1609
+ end
1610
+
1611
+ class NestedRoutingKeyTest < self
1612
+ def test_adds_nested_routing_key_with_dot
1613
+ driver.configure("routing_key nested.routing_id\n")
1614
+ stub_elastic_ping
1615
+ stub_elastic
1616
+ driver.run(default_tag: 'test') do
1617
+ driver.feed(nested_sample_record)
1618
+ end
1619
+ assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1620
+ end
1621
+
1622
+ def test_adds_nested_routing_key_with_dollar_dot
1623
+ driver.configure("routing_key $.nested.routing_id\n")
1624
+ stub_elastic_ping
1625
+ stub_elastic
1626
+ driver.run(default_tag: 'test') do
1627
+ driver.feed(nested_sample_record)
1628
+ end
1629
+ assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1630
+ end
1631
+
1632
+ def test_adds_nested_routing_key_with_bracket
1633
+ driver.configure("routing_key $['nested']['routing_id']\n")
1634
+ stub_elastic_ping
1635
+ stub_elastic
1636
+ driver.run(default_tag: 'test') do
1637
+ driver.feed(nested_sample_record)
1638
+ end
1639
+ assert_equal(index_cmds[0]['index']['_routing'], 'routing')
1640
+ end
1641
+ end
1642
+
1643
+ def test_doesnt_add_routing_key_if_missing_when_configured
1644
+ driver.configure("routing_key another_routing_id\n")
1645
+ stub_elastic_ping
1646
+ stub_elastic
1647
+ driver.run(default_tag: 'test') do
1648
+ driver.feed(sample_record)
1649
+ end
1650
+ assert(!index_cmds[0]['index'].has_key?('_routing'))
1651
+ end
1652
+
1653
+ def test_adds_routing_key_when_not_configured
1654
+ stub_elastic_ping
1655
+ stub_elastic
1656
+ driver.run(default_tag: 'test') do
1657
+ driver.feed(sample_record)
1658
+ end
1659
+ assert(!index_cmds[0]['index'].has_key?('_routing'))
1660
+ end
1661
+
1662
+ def test_remove_one_key
1663
+ driver.configure("remove_keys key1\n")
1664
+ stub_elastic_ping
1665
+ stub_elastic
1666
+ driver.run(default_tag: 'test') do
1667
+ driver.feed(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
1668
+ end
1669
+ assert(!index_cmds[1].has_key?('key1'))
1670
+ assert(index_cmds[1].has_key?('key2'))
1671
+ end
1672
+
1673
+ def test_remove_multi_keys
1674
+ driver.configure("remove_keys key1, key2\n")
1675
+ stub_elastic_ping
1676
+ stub_elastic
1677
+ driver.run(default_tag: 'test') do
1678
+ driver.feed(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
1679
+ end
1680
+ assert(!index_cmds[1].has_key?('key1'))
1681
+ assert(!index_cmds[1].has_key?('key2'))
1682
+ end
1683
+
1684
+ def test_request_error
1685
+ stub_elastic_ping
1686
+ stub_elastic_unavailable
1687
+ assert_raise(Elasticsearch::Transport::Transport::Errors::ServiceUnavailable) {
1688
+ driver.run(default_tag: 'test') do
1689
+ driver.feed(sample_record)
1690
+ end
1691
+ }
1692
+ end
1693
+
1694
+ def test_connection_failed_retry
1695
+ connection_resets = 0
1696
+
1697
+ stub_elastic_ping(url="http://localhost:9200").with do |req|
1698
+ connection_resets += 1
1699
+ end
1700
+
1701
+ stub_request(:post, "http://localhost:9200/_bulk").with do |req|
1702
+ raise Faraday::ConnectionFailed, "Test message"
1703
+ end
1704
+
1705
+ driver.run(default_tag: 'test') do
1706
+ driver.feed(sample_record)
1707
+ end
1708
+ assert_equal(connection_resets, 3)
1709
+ end
1710
+
1711
+ def test_reconnect_on_error_enabled
1712
+ connection_resets = 0
1713
+
1714
+ stub_elastic_ping(url="http://localhost:9200").with do |req|
1715
+ connection_resets += 1
1716
+ end
1717
+
1718
+ stub_request(:post, "http://localhost:9200/_bulk").with do |req|
1719
+ raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
1720
+ end
1721
+
1722
+ driver.configure("reconnect_on_error true\n")
1723
+
1724
+ assert_raise(ZeroDivisionError) {
1725
+ driver.run(default_tag: 'test', shutdown: false) do
1726
+ driver.feed(sample_record)
1727
+ end
1728
+ }
1729
+
1730
+ assert_raise(Timeout::Error) {
1731
+ driver.run(default_tag: 'test', shutdown: false) do
1732
+ driver.feed(sample_record)
1733
+ end
1734
+ }
1735
+ # FIXME: Consider keywords arguments in #run and how to test this later.
1736
+ # Because v0.14 test driver does not have 1 to 1 correspondence between #run and #flush in tests.
1737
+ assert_equal(connection_resets, 1)
1738
+ end
1739
+
1740
+ def test_reconnect_on_error_disabled
1741
+ connection_resets = 0
1742
+
1743
+ stub_elastic_ping(url="http://localhost:9200").with do |req|
1744
+ connection_resets += 1
1745
+ end
1746
+
1747
+ stub_request(:post, "http://localhost:9200/_bulk").with do |req|
1748
+ raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
1749
+ end
1750
+
1751
+ driver.configure("reconnect_on_error false\n")
1752
+
1753
+ assert_raise(ZeroDivisionError) {
1754
+ driver.run(default_tag: 'test', shutdown: false) do
1755
+ driver.feed(sample_record)
1756
+ end
1757
+ }
1758
+
1759
+ assert_raise(Timeout::Error) {
1760
+ driver.run(default_tag: 'test', shutdown: false) do
1761
+ driver.feed(sample_record)
1762
+ end
1763
+ }
1764
+ assert_equal(connection_resets, 1)
1765
+ end
1766
+
1767
+ def test_bulk_error_retags_when_configured
1768
+ driver.configure("retry_tag retry\n")
1769
+ stub_elastic_ping
1770
+ stub_request(:post, 'http://localhost:9200/_bulk')
1771
+ .to_return(lambda do |req|
1772
+ { :status => 200,
1773
+ :headers => { 'Content-Type' => 'json' },
1774
+ :body => %({
1775
+ "took" : 1,
1776
+ "errors" : true,
1777
+ "items" : [
1778
+ {
1779
+ "create" : {
1780
+ "_index" : "foo",
1781
+ "_type" : "bar",
1782
+ "_id" : "abc",
1783
+ "status" : 500,
1784
+ "error" : {
1785
+ "type" : "some unrecognized type",
1786
+ "reason":"some error to cause version mismatch"
1787
+ }
1788
+ }
1789
+ }
1790
+ ]
1791
+ })
1792
+ }
1793
+ end)
1794
+
1795
+ driver.run(default_tag: 'test') do
1796
+ driver.feed(1, sample_record)
1797
+ end
1798
+
1799
+ assert_equal [['retry', 1, sample_record]], driver.events
1800
+ end
1801
+
1802
+ def test_bulk_error
1803
+ stub_elastic_ping
1804
+ stub_request(:post, 'http://localhost:9200/_bulk')
1805
+ .to_return(lambda do |req|
1806
+ { :status => 200,
1807
+ :headers => { 'Content-Type' => 'json' },
1808
+ :body => %({
1809
+ "took" : 1,
1810
+ "errors" : true,
1811
+ "items" : [
1812
+ {
1813
+ "create" : {
1814
+ "_index" : "foo",
1815
+ "_type" : "bar",
1816
+ "_id" : "abc",
1817
+ "status" : 500,
1818
+ "error" : {
1819
+ "type" : "some unrecognized type",
1820
+ "reason":"some error to cause version mismatch"
1821
+ }
1822
+ }
1823
+ },
1824
+ {
1825
+ "create" : {
1826
+ "_index" : "foo",
1827
+ "_type" : "bar",
1828
+ "_id" : "abc",
1829
+ "status" : 201
1830
+ }
1831
+ },
1832
+ {
1833
+ "create" : {
1834
+ "_index" : "foo",
1835
+ "_type" : "bar",
1836
+ "_id" : "abc",
1837
+ "status" : 500,
1838
+ "error" : {
1839
+ "type" : "some unrecognized type",
1840
+ "reason":"some error to cause version mismatch"
1841
+ }
1842
+ }
1843
+ },
1844
+ {
1845
+ "create" : {
1846
+ "_index" : "foo",
1847
+ "_type" : "bar",
1848
+ "_id" : "abc",
1849
+ "_id" : "abc",
1850
+ "status" : 409
1851
+ }
1852
+ }
1853
+ ]
1854
+ })
1855
+ }
1856
+ end)
1857
+
1858
+ driver.run(default_tag: 'test') do
1859
+ driver.feed(1, sample_record)
1860
+ driver.feed(2, sample_record)
1861
+ driver.feed(3, sample_record)
1862
+ driver.feed(4, sample_record)
1863
+ end
1864
+
1865
+ expect = [['test', 1, sample_record],
1866
+ ['test', 3, sample_record]]
1867
+ assert_equal expect, driver.events
1868
+ end
1869
+
1870
+ def test_update_should_not_write_if_theres_no_id
1871
+ driver.configure("write_operation update\n")
1872
+ stub_elastic_ping
1873
+ stub_elastic
1874
+ driver.run(default_tag: 'test') do
1875
+ driver.feed(sample_record)
1876
+ end
1877
+ assert_nil(index_cmds)
1878
+ end
1879
+
1880
+ def test_upsert_should_not_write_if_theres_no_id
1881
+ driver.configure("write_operation upsert\n")
1882
+ stub_elastic_ping
1883
+ stub_elastic
1884
+ driver.run(default_tag: 'test') do
1885
+ driver.feed(sample_record)
1886
+ end
1887
+ assert_nil(index_cmds)
1888
+ end
1889
+
1890
+ def test_create_should_not_write_if_theres_no_id
1891
+ driver.configure("write_operation create\n")
1892
+ stub_elastic_ping
1893
+ stub_elastic
1894
+ driver.run(default_tag: 'test') do
1895
+ driver.feed(sample_record)
1896
+ end
1897
+ assert_nil(index_cmds)
1898
+ end
1899
+
1900
+ def test_update_should_write_update_op_and_doc_as_upsert_is_false
1901
+ driver.configure("write_operation update
1902
+ id_key request_id")
1903
+ stub_elastic_ping
1904
+ stub_elastic
1905
+ driver.run(default_tag: 'test') do
1906
+ driver.feed(sample_record)
1907
+ end
1908
+ assert(index_cmds[0].has_key?("update"))
1909
+ assert(!index_cmds[1]["doc_as_upsert"])
1910
+ assert(!index_cmds[1]["upsert"])
1911
+ end
1912
+
1913
+ def test_update_should_remove_keys_from_doc_when_keys_are_skipped
1914
+ driver.configure("write_operation update
1915
+ id_key request_id
1916
+ remove_keys_on_update parent_id")
1917
+ stub_elastic_ping
1918
+ stub_elastic
1919
+ driver.run(default_tag: 'test') do
1920
+ driver.feed(sample_record)
1921
+ end
1922
+ assert(index_cmds[1]["doc"])
1923
+ assert(!index_cmds[1]["doc"]["parent_id"])
1924
+ end
1925
+
1926
+ def test_upsert_should_write_update_op_and_doc_as_upsert_is_true
1927
+ driver.configure("write_operation upsert
1928
+ id_key request_id")
1929
+ stub_elastic_ping
1930
+ stub_elastic
1931
+ driver.run(default_tag: 'test') do
1932
+ driver.feed(sample_record)
1933
+ end
1934
+ assert(index_cmds[0].has_key?("update"))
1935
+ assert(index_cmds[1]["doc_as_upsert"])
1936
+ assert(!index_cmds[1]["upsert"])
1937
+ end
1938
+
1939
+ def test_upsert_should_write_update_op_upsert_and_doc_when_keys_are_skipped
1940
+ driver.configure("write_operation upsert
1941
+ id_key request_id
1942
+ remove_keys_on_update parent_id")
1943
+ stub_elastic_ping
1944
+ stub_elastic
1945
+ driver.run(default_tag: 'test') do
1946
+ driver.feed(sample_record)
1947
+ end
1948
+ assert(index_cmds[0].has_key?("update"))
1949
+ assert(!index_cmds[1]["doc_as_upsert"])
1950
+ assert(index_cmds[1]["upsert"])
1951
+ assert(index_cmds[1]["doc"])
1952
+ end
1953
+
1954
+ def test_upsert_should_remove_keys_from_doc_when_keys_are_skipped
1955
+ driver.configure("write_operation upsert
1956
+ id_key request_id
1957
+ remove_keys_on_update parent_id")
1958
+ stub_elastic_ping
1959
+ stub_elastic
1960
+ driver.run(default_tag: 'test') do
1961
+ driver.feed(sample_record)
1962
+ end
1963
+ assert(index_cmds[1]["upsert"] != index_cmds[1]["doc"])
1964
+ assert(!index_cmds[1]["doc"]["parent_id"])
1965
+ assert(index_cmds[1]["upsert"]["parent_id"])
1966
+ end
1967
+
1968
+ def test_upsert_should_remove_multiple_keys_when_keys_are_skipped
1969
+ driver.configure("write_operation upsert
1970
+ id_key id
1971
+ remove_keys_on_update foo,baz")
1972
+ stub_elastic_ping
1973
+ stub_elastic
1974
+ driver.run(default_tag: 'test') do
1975
+ driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "zip" => "zam")
1976
+ end
1977
+ assert(
1978
+ index_cmds[1]["doc"] == {
1979
+ "id" => 1,
1980
+ "zip" => "zam",
1981
+ }
1982
+ )
1983
+ assert(
1984
+ index_cmds[1]["upsert"] == {
1985
+ "id" => 1,
1986
+ "foo" => "bar",
1987
+ "baz" => "quix",
1988
+ "zip" => "zam",
1989
+ }
1990
+ )
1991
+ end
1992
+
1993
+ def test_upsert_should_remove_keys_from_when_the_keys_are_in_the_record
1994
+ driver.configure("write_operation upsert
1995
+ id_key id
1996
+ remove_keys_on_update_key keys_to_skip")
1997
+ stub_elastic_ping
1998
+ stub_elastic
1999
+ driver.run(default_tag: 'test') do
2000
+ driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "keys_to_skip" => ["baz"])
2001
+ end
2002
+ assert(
2003
+ index_cmds[1]["doc"] == {
2004
+ "id" => 1,
2005
+ "foo" => "bar",
2006
+ }
2007
+ )
2008
+ assert(
2009
+ index_cmds[1]["upsert"] == {
2010
+ "id" => 1,
2011
+ "foo" => "bar",
2012
+ "baz" => "quix",
2013
+ }
2014
+ )
2015
+ end
2016
+
2017
+ def test_upsert_should_remove_keys_from_key_on_record_has_higher_presedence_than_config
2018
+ driver.configure("write_operation upsert
2019
+ id_key id
2020
+ remove_keys_on_update foo,bar
2021
+ remove_keys_on_update_key keys_to_skip")
2022
+ stub_elastic_ping
2023
+ stub_elastic
2024
+ driver.run(default_tag: 'test') do
2025
+ driver.feed("id" => 1, "foo" => "bar", "baz" => "quix", "keys_to_skip" => ["baz"])
2026
+ end
2027
+ assert(
2028
+ index_cmds[1]["doc"] == {
2029
+ "id" => 1,
2030
+ # we only expect baz to be stripped here, if the config was more important
2031
+ # foo would be stripped too.
2032
+ "foo" => "bar",
2033
+ }
2034
+ )
2035
+ assert(
2036
+ index_cmds[1]["upsert"] == {
2037
+ "id" => 1,
2038
+ "foo" => "bar",
2039
+ "baz" => "quix",
2040
+ }
2041
+ )
2042
+ end
2043
+
2044
+ def test_create_should_write_create_op
2045
+ driver.configure("write_operation create
2046
+ id_key request_id")
2047
+ stub_elastic_ping
2048
+ stub_elastic
2049
+ driver.run(default_tag: 'test') do
2050
+ driver.feed(sample_record)
2051
+ end
2052
+ assert(index_cmds[0].has_key?("create"))
2053
+ end
2054
+
2055
+ end