fluent-plugin-elasticsearch-dext 5.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.editorconfig +9 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  6. data/.github/workflows/issue-auto-closer.yml +12 -0
  7. data/.github/workflows/linux.yml +26 -0
  8. data/.github/workflows/macos.yml +26 -0
  9. data/.github/workflows/windows.yml +26 -0
  10. data/.gitignore +18 -0
  11. data/.travis.yml +40 -0
  12. data/CONTRIBUTING.md +24 -0
  13. data/Gemfile +11 -0
  14. data/History.md +553 -0
  15. data/ISSUE_TEMPLATE.md +30 -0
  16. data/LICENSE.txt +201 -0
  17. data/PULL_REQUEST_TEMPLATE.md +10 -0
  18. data/README.ElasticsearchGenID.md +116 -0
  19. data/README.ElasticsearchInput.md +293 -0
  20. data/README.Troubleshooting.md +601 -0
  21. data/README.md +1467 -0
  22. data/Rakefile +11 -0
  23. data/appveyor.yml +20 -0
  24. data/fluent-plugin-elasticsearch.gemspec +35 -0
  25. data/gemfiles/Gemfile.elasticsearch.v6 +12 -0
  26. data/lib/fluent/log-ext.rb +38 -0
  27. data/lib/fluent/plugin/default-ilm-policy.json +14 -0
  28. data/lib/fluent/plugin/elasticsearch_constants.rb +13 -0
  29. data/lib/fluent/plugin/elasticsearch_error.rb +5 -0
  30. data/lib/fluent/plugin/elasticsearch_error_handler.rb +129 -0
  31. data/lib/fluent/plugin/elasticsearch_fallback_selector.rb +9 -0
  32. data/lib/fluent/plugin/elasticsearch_index_lifecycle_management.rb +67 -0
  33. data/lib/fluent/plugin/elasticsearch_index_template.rb +211 -0
  34. data/lib/fluent/plugin/elasticsearch_simple_sniffer.rb +10 -0
  35. data/lib/fluent/plugin/elasticsearch_tls.rb +70 -0
  36. data/lib/fluent/plugin/filter_elasticsearch_genid.rb +77 -0
  37. data/lib/fluent/plugin/in_elasticsearch.rb +325 -0
  38. data/lib/fluent/plugin/oj_serializer.rb +22 -0
  39. data/lib/fluent/plugin/out_elasticsearch.rb +1108 -0
  40. data/lib/fluent/plugin/out_elasticsearch_data_stream.rb +218 -0
  41. data/lib/fluent/plugin/out_elasticsearch_dynamic.rb +282 -0
  42. data/test/helper.rb +24 -0
  43. data/test/plugin/test_alias_template.json +9 -0
  44. data/test/plugin/test_elasticsearch_error_handler.rb +646 -0
  45. data/test/plugin/test_elasticsearch_fallback_selector.rb +74 -0
  46. data/test/plugin/test_elasticsearch_index_lifecycle_management.rb +66 -0
  47. data/test/plugin/test_elasticsearch_tls.rb +145 -0
  48. data/test/plugin/test_filter_elasticsearch_genid.rb +215 -0
  49. data/test/plugin/test_in_elasticsearch.rb +459 -0
  50. data/test/plugin/test_index_alias_template.json +11 -0
  51. data/test/plugin/test_index_template.json +25 -0
  52. data/test/plugin/test_oj_serializer.rb +19 -0
  53. data/test/plugin/test_out_elasticsearch.rb +5688 -0
  54. data/test/plugin/test_out_elasticsearch_data_stream.rb +337 -0
  55. data/test/plugin/test_out_elasticsearch_dynamic.rb +1134 -0
  56. data/test/plugin/test_template.json +23 -0
  57. data/test/test_log-ext.rb +35 -0
  58. metadata +236 -0
@@ -0,0 +1,459 @@
1
+ require_relative '../helper'
2
+ require 'date'
3
+ require 'fluent/test/helpers'
4
+ require 'json'
5
+ require 'fluent/test/driver/input'
6
+ require 'flexmock/test_unit'
7
+ require 'fluent/plugin/in_elasticsearch'
8
+
9
+ class ElasticsearchInputTest < Test::Unit::TestCase
10
+ include FlexMock::TestCase
11
+ include Fluent::Test::Helpers
12
+
13
+ CONFIG = %[
14
+ tag raw.elasticsearch
15
+ interval 2
16
+ ]
17
+
18
+ def setup
19
+ Fluent::Test.setup
20
+ @driver = nil
21
+ log = Fluent::Engine.log
22
+ log.out.logs.slice!(0, log.out.logs.length)
23
+ @http_method = if Gem::Version.new(Elasticsearch::VERSION) >= Gem::Version.new("7.9.0")
24
+ :post
25
+ else
26
+ :get
27
+ end
28
+ end
29
+
30
+ def driver(conf='')
31
+ @driver ||= Fluent::Test::Driver::Input.new(Fluent::Plugin::ElasticsearchInput).configure(conf)
32
+ end
33
+
34
+ def sample_response(index_name="fluentd")
35
+ {
36
+ "took"=>4,
37
+ "timed_out"=>false,
38
+ "_shards"=>{
39
+ "total"=>2,
40
+ "successful"=>2,
41
+ "skipped"=>0,
42
+ "failed"=>0
43
+ },
44
+ "hits"=>{
45
+ "total"=>{
46
+ "value"=>1,
47
+ "relation"=>"eq"
48
+ },
49
+ "max_score"=>1,
50
+ "hits"=>[
51
+ {
52
+ "_index"=>"#{index_name}-2019.11.14",
53
+ "_type"=>"_doc",
54
+ "_id"=>"MJ_faG4B16RqUMOji_nH",
55
+ "_score"=>1,
56
+ "_source"=>{
57
+ "message"=>"Hi from Fluentd!",
58
+ "@timestamp"=>"2019-11-14T16:45:10.559841000+09:00"
59
+ }
60
+ }
61
+ ]
62
+ }
63
+ }.to_json
64
+ end
65
+
66
+ def sample_scroll_response
67
+ {
68
+ "_scroll_id"=>"WomkoUKG0QPB679Ulo6TqQgh3pIGRUmrl9qXXGK3EeiQh9rbYNasTkspZQcJ01uz",
69
+ "took"=>0,
70
+ "timed_out"=>false,
71
+ "_shards"=>{
72
+ "total"=>1,
73
+ "successful"=>1,
74
+ "skipped"=>0,
75
+ "failed"=>0
76
+ },
77
+ "hits"=>{
78
+ "total"=>{
79
+ "value"=>7,
80
+ "relation"=>"eq"
81
+ },
82
+ "max_score"=>nil,
83
+ "hits"=>[
84
+ {
85
+ "_index"=>"fluentd-2019.11.14",
86
+ "_type"=>"_doc",
87
+ "_id"=>"MJ_faG4B16RqUMOji_nH",
88
+ "_score"=>1,
89
+ "_source"=>{
90
+ "message"=>"Hi from Fluentd!",
91
+ "@timestamp"=>"2019-11-14T16:45:10.559841000+09:00"
92
+ },
93
+ "sort"=>[0]
94
+ }
95
+ ]
96
+ }
97
+ }.to_json
98
+ end
99
+
100
+ def sample_scroll_response_2
101
+ {
102
+ "_scroll_id"=>"WomkoUKG0QPB679Ulo6TqQgh3pIGRUmrl9qXXGK3EeiQh9rbYNasTkspZQcJ01uz",
103
+ "took"=>0,
104
+ "timed_out"=>false,
105
+ "_shards"=>{
106
+ "total"=>1,
107
+ "successful"=>1,
108
+ "skipped"=>0,
109
+ "failed"=>0
110
+ },
111
+ "hits"=>{
112
+ "total"=>{
113
+ "value"=>7,
114
+ "relation"=>"eq"
115
+ },
116
+ "max_score"=>nil,
117
+ "hits"=>[
118
+ {
119
+ "_index"=>"fluentd-2019.11.14",
120
+ "_type"=>"_doc",
121
+ "_id"=>"L5-saG4B16RqUMOjw_kb",
122
+ "_score"=>1,
123
+ "_source"=>{
124
+ "message"=>"Yaaaaaaay from Fluentd!",
125
+ "@timestamp"=>"2019-11-14T15:49:41.112023000+09:00"
126
+ },
127
+ "sort"=>[1]
128
+ }
129
+ ]
130
+ }
131
+ }.to_json
132
+ end
133
+
134
+ def sample_scroll_response_terminate
135
+ {
136
+ "_scroll_id"=>"WomkoUKG0QPB679Ulo6TqQgh3pIGRUmrl9qXXGK3EeiQh9rbYNasTkspZQcJ01uz",
137
+ "took"=>1,
138
+ "timed_out"=>false,
139
+ "terminated_early"=>true,
140
+ "_shards"=>{
141
+ "total"=>1,
142
+ "successful"=>1,
143
+ "skipped"=>0,
144
+ "failed"=>0
145
+ },
146
+ "hits"=>{
147
+ "total"=>{
148
+ "value"=>7,
149
+ "relation"=>"eq"
150
+ },
151
+ "max_score"=>nil,
152
+ "hits"=>[]
153
+ }
154
+ }.to_json
155
+ end
156
+
157
+ def test_configure
158
+ config = %{
159
+ host logs.google.com
160
+ port 777
161
+ scheme https
162
+ path /es/
163
+ user john
164
+ password doe
165
+ tag raw.elasticsearch
166
+ }
167
+ instance = driver(config).instance
168
+
169
+ expected_query = { "sort" => [ "_doc" ]}
170
+ assert_equal 'logs.google.com', instance.host
171
+ assert_equal 777, instance.port
172
+ assert_equal :https, instance.scheme
173
+ assert_equal '/es/', instance.path
174
+ assert_equal 'john', instance.user
175
+ assert_equal 'doe', instance.password
176
+ assert_equal 'raw.elasticsearch', instance.tag
177
+ assert_equal :TLSv1_2, instance.ssl_version
178
+ assert_equal 'fluentd', instance.index_name
179
+ assert_equal expected_query, instance.query
180
+ assert_equal '1m', instance.scroll
181
+ assert_equal 1000, instance.size
182
+ assert_equal 1, instance.num_slices
183
+ assert_equal 5, instance.interval
184
+ assert_true instance.repeat
185
+ assert_nil instance.client_key
186
+ assert_nil instance.client_cert
187
+ assert_nil instance.client_key_pass
188
+ assert_nil instance.ca_file
189
+ assert_false instance.with_transporter_log
190
+ assert_equal :excon, instance.http_backend
191
+ assert_nil instance.sniffer_class_name
192
+ assert_true instance.custom_headers.empty?
193
+ assert_equal ['_index', '_type', '_id'], instance.docinfo_fields
194
+ assert_equal '@metadata', instance.docinfo_target
195
+ assert_false instance.docinfo
196
+ end
197
+
198
+ def test_single_host_params_and_defaults
199
+ config = %{
200
+ host logs.google.com
201
+ user john
202
+ password doe
203
+ tag raw.elasticsearch
204
+ }
205
+ instance = driver(config).instance
206
+
207
+ assert_equal 1, instance.get_connection_options[:hosts].length
208
+ host1 = instance.get_connection_options[:hosts][0]
209
+
210
+ assert_equal 'logs.google.com', host1[:host]
211
+ assert_equal 9200, host1[:port]
212
+ assert_equal 'http', host1[:scheme]
213
+ assert_equal 'john', host1[:user]
214
+ assert_equal 'doe', host1[:password]
215
+ assert_equal nil, host1[:path]
216
+ assert_equal 'raw.elasticsearch', instance.tag
217
+ end
218
+
219
+ def test_single_host_params_and_defaults_with_escape_placeholders
220
+ config = %{
221
+ host logs.google.com
222
+ user %{j+hn}
223
+ password %{d@e}
224
+ tag raw.elasticsearch
225
+ }
226
+ instance = driver(config).instance
227
+
228
+ assert_equal 1, instance.get_connection_options[:hosts].length
229
+ host1 = instance.get_connection_options[:hosts][0]
230
+
231
+ assert_equal 'logs.google.com', host1[:host]
232
+ assert_equal 9200, host1[:port]
233
+ assert_equal 'http', host1[:scheme]
234
+ assert_equal 'j%2Bhn', host1[:user]
235
+ assert_equal 'd%40e', host1[:password]
236
+ assert_equal nil, host1[:path]
237
+ assert_equal 'raw.elasticsearch', instance.tag
238
+ end
239
+
240
+ def test_legacy_hosts_list
241
+ config = %{
242
+ hosts host1:50,host2:100,host3
243
+ scheme https
244
+ path /es/
245
+ port 123
246
+ tag raw.elasticsearch
247
+ }
248
+ instance = driver(config).instance
249
+
250
+ assert_equal 3, instance.get_connection_options[:hosts].length
251
+ host1, host2, host3 = instance.get_connection_options[:hosts]
252
+
253
+ assert_equal 'host1', host1[:host]
254
+ assert_equal 50, host1[:port]
255
+ assert_equal 'https', host1[:scheme]
256
+ assert_equal '/es/', host2[:path]
257
+ assert_equal 'host3', host3[:host]
258
+ assert_equal 123, host3[:port]
259
+ assert_equal 'https', host3[:scheme]
260
+ assert_equal '/es/', host3[:path]
261
+ assert_equal 'raw.elasticsearch', instance.tag
262
+ end
263
+
264
+ def test_hosts_list
265
+ config = %{
266
+ hosts https://john:password@host1:443/elastic/,http://host2
267
+ path /default_path
268
+ user default_user
269
+ password default_password
270
+ tag raw.elasticsearch
271
+ }
272
+ instance = driver(config).instance
273
+
274
+ assert_equal 2, instance.get_connection_options[:hosts].length
275
+ host1, host2 = instance.get_connection_options[:hosts]
276
+
277
+ assert_equal 'host1', host1[:host]
278
+ assert_equal 443, host1[:port]
279
+ assert_equal 'https', host1[:scheme]
280
+ assert_equal 'john', host1[:user]
281
+ assert_equal 'password', host1[:password]
282
+ assert_equal '/elastic/', host1[:path]
283
+
284
+ assert_equal 'host2', host2[:host]
285
+ assert_equal 'http', host2[:scheme]
286
+ assert_equal 'default_user', host2[:user]
287
+ assert_equal 'default_password', host2[:password]
288
+ assert_equal '/default_path', host2[:path]
289
+ assert_equal 'raw.elasticsearch', instance.tag
290
+ end
291
+
292
+ def test_hosts_list_with_escape_placeholders
293
+ config = %{
294
+ hosts https://%{j+hn}:%{passw@rd}@host1:443/elastic/,http://host2
295
+ path /default_path
296
+ user default_user
297
+ password default_password
298
+ tag raw.elasticsearch
299
+ }
300
+ instance = driver(config).instance
301
+
302
+ assert_equal 2, instance.get_connection_options[:hosts].length
303
+ host1, host2 = instance.get_connection_options[:hosts]
304
+
305
+ assert_equal 'host1', host1[:host]
306
+ assert_equal 443, host1[:port]
307
+ assert_equal 'https', host1[:scheme]
308
+ assert_equal 'j%2Bhn', host1[:user]
309
+ assert_equal 'passw%40rd', host1[:password]
310
+ assert_equal '/elastic/', host1[:path]
311
+
312
+ assert_equal 'host2', host2[:host]
313
+ assert_equal 'http', host2[:scheme]
314
+ assert_equal 'default_user', host2[:user]
315
+ assert_equal 'default_password', host2[:password]
316
+ assert_equal '/default_path', host2[:path]
317
+ assert_equal 'raw.elasticsearch', instance.tag
318
+ end
319
+
320
+ def test_emit
321
+ stub_request(@http_method, "http://localhost:9200/fluentd/_search?scroll=1m&size=1000").
322
+ with(body: "{\"sort\":[\"_doc\"]}").
323
+ to_return(status: 200, body: sample_response.to_s,
324
+ headers: {'Content-Type' => 'application/json'})
325
+
326
+ driver(CONFIG)
327
+ driver.run(expect_emits: 1, timeout: 10)
328
+ expected = {"message" => "Hi from Fluentd!",
329
+ "@timestamp" => "2019-11-14T16:45:10.559841000+09:00"}
330
+ event = driver.events.map {|e| e.last}.last
331
+ assert_equal expected, event
332
+ end
333
+
334
+ def test_emit_with_custom_index_name
335
+ index_name = "logstash"
336
+ stub_request(@http_method, "http://localhost:9200/#{index_name}/_search?scroll=1m&size=1000").
337
+ with(body: "{\"sort\":[\"_doc\"]}").
338
+ to_return(status: 200, body: sample_response(index_name).to_s,
339
+ headers: {'Content-Type' => 'application/json'})
340
+
341
+ driver(CONFIG + %[index_name #{index_name}])
342
+ driver.run(expect_emits: 1, timeout: 10)
343
+ expected = {"message" => "Hi from Fluentd!",
344
+ "@timestamp" => "2019-11-14T16:45:10.559841000+09:00"}
345
+ event = driver.events.map {|e| e.last}.last
346
+ assert_equal expected, event
347
+ end
348
+
349
+ def test_emit_with_parse_timestamp
350
+ index_name = "fluentd"
351
+ stub_request(@http_method, "http://localhost:9200/#{index_name}/_search?scroll=1m&size=1000").
352
+ with(body: "{\"sort\":[\"_doc\"]}").
353
+ to_return(status: 200, body: sample_response(index_name).to_s,
354
+ headers: {'Content-Type' => 'application/json'})
355
+
356
+ driver(CONFIG + %[parse_timestamp])
357
+ driver.run(expect_emits: 1, timeout: 10)
358
+ expected = {"message" => "Hi from Fluentd!",
359
+ "@timestamp" => "2019-11-14T16:45:10.559841000+09:00"}
360
+ event = driver.events.map {|e| e.last}.last
361
+ time = driver.events.map {|e| e[1]}.last
362
+ expected_time = event_time("2019-11-14T16:45:10.559841000+09:00")
363
+ assert_equal expected_time.to_time, time.to_time
364
+ assert_equal expected, event
365
+ end
366
+
367
+ def test_emit_with_parse_timestamp_and_timstamp_format
368
+ index_name = "fluentd"
369
+ stub_request(@http_method, "http://localhost:9200/#{index_name}/_search?scroll=1m&size=1000").
370
+ with(body: "{\"sort\":[\"_doc\"]}").
371
+ to_return(status: 200, body: sample_response(index_name).to_s,
372
+ headers: {'Content-Type' => 'application/json'})
373
+
374
+ driver(CONFIG + %[parse_timestamp true
375
+ timestamp_key_format %Y-%m-%dT%H:%M:%S.%N%z
376
+ ])
377
+ driver.run(expect_emits: 1, timeout: 10)
378
+ expected = {"message" => "Hi from Fluentd!",
379
+ "@timestamp" => "2019-11-14T16:45:10.559841000+09:00"}
380
+ event = driver.events.map {|e| e.last}.last
381
+ time = driver.events.map {|e| e[1]}.last
382
+ expected_time = event_time("2019-11-14T16:45:10.559841000+09:00")
383
+ assert_equal expected_time.to_time, time.to_time
384
+ assert_equal expected, event
385
+ end
386
+
387
+ def test_emit_with_docinfo
388
+ stub_request(@http_method, "http://localhost:9200/fluentd/_search?scroll=1m&size=1000").
389
+ with(body: "{\"sort\":[\"_doc\"]}").
390
+ to_return(status: 200, body: sample_response.to_s,
391
+ headers: {'Content-Type' => 'application/json'})
392
+
393
+ driver(CONFIG + %[docinfo true])
394
+ driver.run(expect_emits: 1, timeout: 10)
395
+ expected = {"message" => "Hi from Fluentd!",
396
+ "@timestamp" => "2019-11-14T16:45:10.559841000+09:00"}
397
+ expected.merge!({"@metadata"=>
398
+ {"_id"=>"MJ_faG4B16RqUMOji_nH",
399
+ "_index"=>"fluentd-2019.11.14",
400
+ "_type"=>"_doc"}
401
+ })
402
+ event = driver.events.map {|e| e.last}.last
403
+ assert_equal expected, event
404
+ end
405
+
406
+ def test_emit_with_slices
407
+ stub_request(@http_method, "http://localhost:9200/fluentd/_search?scroll=1m&size=1000").
408
+ with(body: "{\"sort\":[\"_doc\"],\"slice\":{\"id\":0,\"max\":2}}").
409
+ to_return(status: 200, body: sample_response.to_s,
410
+ headers: {'Content-Type' => 'application/json'})
411
+ stub_request(@http_method, "http://localhost:9200/fluentd/_search?scroll=1m&size=1000").
412
+ with(body: "{\"sort\":[\"_doc\"],\"slice\":{\"id\":1,\"max\":2}}").
413
+ to_return(status: 200, body: sample_response.to_s,
414
+ headers: {'Content-Type' => 'application/json'})
415
+
416
+ driver(CONFIG + %[num_slices 2])
417
+ driver.run(expect_emits: 1, timeout: 10)
418
+ expected = [
419
+ {"message"=>"Hi from Fluentd!", "@timestamp"=>"2019-11-14T16:45:10.559841000+09:00"},
420
+ {"message"=>"Hi from Fluentd!", "@timestamp"=>"2019-11-14T16:45:10.559841000+09:00"},
421
+ ]
422
+ events = driver.events.map {|e| e.last}
423
+ assert_equal expected, events
424
+ end
425
+
426
+ def test_emit_with_size
427
+ stub_request(@http_method, "http://localhost:9200/fluentd/_search?scroll=1m&size=1").
428
+ with(body: "{\"sort\":[\"_doc\"]}").
429
+ to_return(status: 200, body: sample_scroll_response.to_s,
430
+ headers: {'Content-Type' => 'application/json'})
431
+ connection = 0
432
+ scroll_request = stub_request(@http_method, "http://localhost:9200/_search/scroll?scroll=1m").
433
+ with(
434
+ body: "{\"scroll_id\":\"WomkoUKG0QPB679Ulo6TqQgh3pIGRUmrl9qXXGK3EeiQh9rbYNasTkspZQcJ01uz\"}") do
435
+ connection += 1
436
+ end
437
+ scroll_request.to_return(lambda do |req|
438
+ if connection <= 1
439
+ {status: 200, body: sample_scroll_response_2.to_s,
440
+ headers: {'Content-Type' => 'application/json'}}
441
+ else
442
+ {status: 200, body: sample_scroll_response_terminate.to_s,
443
+ headers: {'Content-Type' => 'application/json'}}
444
+ end
445
+ end)
446
+ stub_request(:delete, "http://localhost:9200/_search/scroll/WomkoUKG0QPB679Ulo6TqQgh3pIGRUmrl9qXXGK3EeiQh9rbYNasTkspZQcJ01uz").
447
+ to_return(status: 200, body: "", headers: {})
448
+
449
+ driver(CONFIG + %[size 1])
450
+ driver.run(expect_emits: 1, timeout: 10)
451
+ expected = [
452
+ {"message"=>"Hi from Fluentd!", "@timestamp"=>"2019-11-14T16:45:10.559841000+09:00"},
453
+ {"message"=>"Yaaaaaaay from Fluentd!", "@timestamp"=>"2019-11-14T15:49:41.112023000+09:00"}
454
+ ]
455
+ events = driver.events.map{|e| e.last}
456
+ assert_equal expected, events
457
+ end
458
+
459
+ end