fluentd 1.12.1 → 1.12.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.deepsource.toml +13 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +2 -2
  4. data/.github/workflows/linux-test.yaml +36 -0
  5. data/.github/workflows/{build.yaml → macos-test.yaml} +8 -7
  6. data/.github/workflows/windows-test.yaml +35 -0
  7. data/.gitlab-ci.yml +41 -19
  8. data/CHANGELOG.md +95 -0
  9. data/MAINTAINERS.md +5 -2
  10. data/README.md +5 -2
  11. data/fluentd.gemspec +4 -2
  12. data/lib/fluent/command/bundler_injection.rb +1 -1
  13. data/lib/fluent/command/cat.rb +0 -1
  14. data/lib/fluent/command/plugin_config_formatter.rb +2 -1
  15. data/lib/fluent/command/plugin_generator.rb +31 -1
  16. data/lib/fluent/compat/parser.rb +2 -2
  17. data/lib/fluent/config/section.rb +1 -1
  18. data/lib/fluent/config/types.rb +2 -2
  19. data/lib/fluent/event.rb +3 -13
  20. data/lib/fluent/load.rb +0 -1
  21. data/lib/fluent/plugin/file_wrapper.rb +39 -3
  22. data/lib/fluent/plugin/formatter_ltsv.rb +2 -2
  23. data/lib/fluent/plugin/in_http.rb +1 -1
  24. data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
  25. data/lib/fluent/plugin/in_tail/position_file.rb +15 -1
  26. data/lib/fluent/plugin/in_tail.rb +35 -15
  27. data/lib/fluent/plugin/out_copy.rb +18 -5
  28. data/lib/fluent/plugin/out_exec_filter.rb +3 -3
  29. data/lib/fluent/plugin/out_forward.rb +61 -28
  30. data/lib/fluent/plugin/output.rb +11 -9
  31. data/lib/fluent/plugin/parser_csv.rb +2 -2
  32. data/lib/fluent/plugin/parser_syslog.rb +2 -2
  33. data/lib/fluent/plugin/storage_local.rb +4 -4
  34. data/lib/fluent/plugin_helper/server.rb +4 -2
  35. data/lib/fluent/plugin_helper/socket_option.rb +2 -2
  36. data/lib/fluent/supervisor.rb +1 -1
  37. data/lib/fluent/time.rb +57 -1
  38. data/lib/fluent/version.rb +1 -1
  39. data/templates/new_gem/fluent-plugin.gemspec.erb +3 -3
  40. data/test/command/test_fluentd.rb +8 -0
  41. data/test/config/test_configurable.rb +1 -1
  42. data/test/plugin/in_tail/test_position_file.rb +58 -4
  43. data/test/plugin/test_file_wrapper.rb +105 -0
  44. data/test/plugin/test_in_exec.rb +1 -1
  45. data/test/plugin/test_in_tail.rb +87 -26
  46. data/test/plugin/test_out_copy.rb +87 -0
  47. data/test/plugin/test_out_forward.rb +94 -6
  48. data/test/plugin/test_out_http.rb +1 -1
  49. data/test/plugin/test_output.rb +15 -3
  50. data/test/plugin/test_output_as_buffered_backup.rb +2 -0
  51. data/test/plugin/test_parser_csv.rb +14 -0
  52. data/test/plugin/test_parser_syslog.rb +14 -0
  53. data/test/plugin_helper/service_discovery/test_manager.rb +1 -1
  54. data/test/plugin_helper/test_child_process.rb +5 -2
  55. data/test/plugin_helper/test_http_server_helper.rb +1 -1
  56. data/test/plugin_helper/test_server.rb +8 -2
  57. data/test/test_event.rb +16 -0
  58. data/test/test_formatter.rb +30 -0
  59. data/test/test_output.rb +2 -2
  60. data/test/test_time_parser.rb +109 -0
  61. metadata +31 -9
  62. data/.travis.yml +0 -77
  63. data/appveyor.yml +0 -31
@@ -147,7 +147,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
147
147
  write_data(@file, TEST_CONTENT)
148
148
  pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
149
149
 
150
- valid_target_info = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', Fluent::FileWrapper.stat(@file).ino)
150
+ valid_target_info = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', File.stat(@file).ino)
151
151
  f = pf[valid_target_info]
152
152
  assert_equal Fluent::Plugin::TailInput::FilePositionEntry, f.class
153
153
  assert_equal 2, f.read_pos
@@ -177,7 +177,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
177
177
  assert_equal 0, f.read_inode
178
178
  assert_equal 0, f.read_pos
179
179
 
180
- pf[Fluent::Plugin::TailInput::TargetInfo.new('valid_path', Fluent::FileWrapper.stat(@file).ino)].update(1, 2)
180
+ pf[Fluent::Plugin::TailInput::TargetInfo.new('valid_path', File.stat(@file).ino)].update(1, 2)
181
181
 
182
182
  f = pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)]
183
183
  assert_equal 0, f.read_inode
@@ -193,7 +193,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
193
193
  test 'deletes entry by path' do
194
194
  write_data(@file, TEST_CONTENT)
195
195
  pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
196
- inode1 = Fluent::FileWrapper.stat(@file).ino
196
+ inode1 = File.stat(@file).ino
197
197
  target_info1 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode1)
198
198
  p1 = pf[target_info1]
199
199
  assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p1.class
@@ -201,7 +201,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
201
201
  pf.unwatch(target_info1)
202
202
  assert_equal p1.read_pos, Fluent::Plugin::TailInput::PositionFile::UNWATCHED_POSITION
203
203
 
204
- inode2 = Fluent::FileWrapper.stat(@file).ino
204
+ inode2 = File.stat(@file).ino
205
205
  target_info2 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode2)
206
206
  p2 = pf[target_info2]
207
207
  assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p2.class
@@ -282,4 +282,58 @@ class IntailPositionFileTest < Test::Unit::TestCase
282
282
  assert_equal 2, f.read_inode
283
283
  end
284
284
  end
285
+
286
+ sub_test_case "TargetInfo equality rules" do
287
+ sub_test_case "== operator" do
288
+ def test_equal
289
+ t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
290
+ t2 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1235)
291
+
292
+ assert_equal t1, t2
293
+ end
294
+
295
+ def test_not_equal
296
+ t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
297
+ t2 = Fluent::Plugin::TailInput::TargetInfo.new("test2", 1234)
298
+
299
+ assert_not_equal t1, t2
300
+ end
301
+ end
302
+
303
+ sub_test_case "eql? method" do
304
+ def test_eql?
305
+ t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
306
+ t2 = Fluent::Plugin::TailInput::TargetInfo.new("test", 5321)
307
+
308
+ assert do
309
+ t1.eql? t2
310
+ end
311
+ end
312
+
313
+ def test_not_eql?
314
+ t1 = Fluent::Plugin::TailInput::TargetInfo.new("test2", 1234)
315
+ t2 = Fluent::Plugin::TailInput::TargetInfo.new("test3", 1234)
316
+
317
+ assert do
318
+ !t1.eql? t2
319
+ end
320
+ end
321
+ end
322
+
323
+ sub_test_case "hash" do
324
+ def test_equal
325
+ t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
326
+ t2 = Fluent::Plugin::TailInput::TargetInfo.new("test", 7321)
327
+
328
+ assert_equal t1.hash, t2.hash
329
+ end
330
+
331
+ def test_not_equal
332
+ t1 = Fluent::Plugin::TailInput::TargetInfo.new("test", 1234)
333
+ t2 = Fluent::Plugin::TailInput::TargetInfo.new("test2", 1234)
334
+
335
+ assert_not_equal t1.hash, t2.hash
336
+ end
337
+ end
338
+ end
285
339
  end
@@ -0,0 +1,105 @@
1
+ require_relative '../helper'
2
+ require 'fluent/plugin/file_wrapper'
3
+
4
+ class FileWrapperTest < Test::Unit::TestCase
5
+ require 'windows/file'
6
+ require 'windows/error'
7
+ include Windows::File
8
+ include Windows::Error
9
+
10
+ TMP_DIR = File.dirname(__FILE__) + "/../tmp/file_wrapper#{ENV['TEST_ENV_NUMBER']}"
11
+
12
+ def setup
13
+ FileUtils.mkdir_p(TMP_DIR)
14
+ end
15
+
16
+ def teardown
17
+ FileUtils.rm_rf(TMP_DIR)
18
+ end
19
+
20
+ sub_test_case 'Win32Error' do
21
+ test 'equal' do
22
+ assert_equal(Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, "message"),
23
+ Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, "message"))
24
+ end
25
+
26
+ test 'different error code' do
27
+ assert_not_equal(Fluent::Win32Error.new(ERROR_FILE_NOT_FOUND),
28
+ Fluent::Win32Error.new(ERROR_SHARING_VIOLATION))
29
+ end
30
+
31
+ test 'different error message' do
32
+ assert_not_equal(Fluent::Win32Error.new(ERROR_FILE_NOT_FOUND, "message1"),
33
+ Fluent::Win32Error.new(ERROR_FILE_NOT_FOUND, "message2"))
34
+ end
35
+
36
+ test 'different class' do
37
+ assert_not_equal(Errno::EPIPE,
38
+ Fluent::Win32Error.new(ERROR_SHARING_VIOLATION))
39
+ end
40
+
41
+ test 'ERROR_SHARING_VIOLATION message' do
42
+ assert_equal(Fluent::Win32Error.new(ERROR_SHARING_VIOLATION).message,
43
+ "Fluent::Win32Error: code: 32, The process cannot access the file because it is being used by another process.")
44
+ end
45
+
46
+ test 'ERROR_SHARING_VIOLATION with a message' do
47
+ assert_equal(Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, "cannot open the file").message,
48
+ "Fluent::Win32Error: code: 32, The process cannot access the file because it is being used by another process." +
49
+ " - cannot open the file")
50
+ end
51
+
52
+ test 'to_s' do
53
+ assert_equal("Fluent::Win32Error: code: 32, The process cannot access the file because it is being used by another process. - C:\file.txt",
54
+ Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, "C:\file.txt").to_s)
55
+ end
56
+
57
+ test 'inspect' do
58
+ assert_equal("#<Fluent::Win32Error: code: 32, The process cannot access the file because it is being used by another process. - C:\file.txt>",
59
+ Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, "C:\file.txt").inspect)
60
+ end
61
+ end
62
+
63
+ sub_test_case 'WindowsFile exceptions' do
64
+ test 'nothing raised' do
65
+ begin
66
+ path = "#{TMP_DIR}/test_windows_file.txt"
67
+ file1 = file2 = nil
68
+ file1 = File.open(path, "wb") do |f|
69
+ end
70
+ assert_nothing_raised do
71
+ file2 = Fluent::WindowsFile.new(path)
72
+ ensure
73
+ file2.close
74
+ end
75
+ ensure
76
+ file1.close if file1
77
+ end
78
+ end
79
+
80
+ test 'Errno::ENOENT raised' do
81
+ path = "#{TMP_DIR}/nofile.txt"
82
+ file = nil
83
+ assert_raise(Errno::ENOENT) do
84
+ file = Fluent::WindowsFile.new(path)
85
+ ensure
86
+ file.close if file
87
+ end
88
+ end
89
+
90
+ test 'ERROR_SHARING_VIOLATION raised' do
91
+ begin
92
+ path = "#{TMP_DIR}/test_windows_file.txt"
93
+ file1 = file2 = nil
94
+ file1 = File.open(path, "wb")
95
+ assert_raise(Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, path)) do
96
+ file2 = Fluent::WindowsFile.new(path, 'r', FILE_SHARE_READ)
97
+ ensure
98
+ file2.close if file2
99
+ end
100
+ ensure
101
+ file1.close if file1
102
+ end
103
+ end
104
+ end
105
+ end if Fluent.windows?
@@ -255,7 +255,7 @@ EOC
255
255
  assert{ d.events.length > 0 }
256
256
  d.events.each do |event|
257
257
  assert_equal 'test', event[0]
258
- assert_match /LoadError/, event[2]['message']
258
+ assert_match(/LoadError/, event[2]['message'])
259
259
  end
260
260
  end
261
261
  end
@@ -6,6 +6,8 @@ require 'fluent/system_config'
6
6
  require 'net/http'
7
7
  require 'flexmock/test_unit'
8
8
  require 'timecop'
9
+ require 'tmpdir'
10
+ require 'securerandom'
9
11
 
10
12
  class TailInputTest < Test::Unit::TestCase
11
13
  include FlexMock::TestCase
@@ -22,28 +24,64 @@ class TailInputTest < Test::Unit::TestCase
22
24
  end
23
25
 
24
26
  def cleanup_directory(path)
25
- begin
26
- FileUtils.rm_f(path, secure: true)
27
- rescue ArgumentError
28
- FileUtils.rm_f(path) # For Ruby 2.6 or before.
27
+ unless Dir.exist?(path)
28
+ FileUtils.mkdir_p(path)
29
+ return
29
30
  end
30
- if File.exist?(path)
31
- FileUtils.remove_entry_secure(path, true)
31
+
32
+ if Fluent.windows?
33
+ Dir.glob("*", base: path).each do |name|
34
+ begin
35
+ cleanup_file(File.join(path, name))
36
+ rescue
37
+ # expect test driver block release already owned file handle.
38
+ end
39
+ end
40
+ else
41
+ begin
42
+ FileUtils.rm_f(path, secure:true)
43
+ rescue ArgumentError
44
+ FileUtils.rm_f(path) # For Ruby 2.6 or before.
45
+ end
46
+ if File.exist?(path)
47
+ FileUtils.remove_entry_secure(path, true)
48
+ end
32
49
  end
33
50
  FileUtils.mkdir_p(path)
34
51
  end
35
52
 
36
53
  def cleanup_file(path)
37
- begin
38
- FileUtils.rm_f(path, secure: true)
39
- rescue ArgumentError
40
- FileUtils.rm_f(path) # For Ruby 2.6 or before.
41
- end
42
- if File.exist?(path)
43
- # ensure files are closed for Windows, on which deleted files
44
- # are still visible from filesystem
45
- GC.start(full_mark: true, immediate_mark: true, immediate_sweep: true)
46
- FileUtils.remove_entry_secure(path, true)
54
+ if Fluent.windows?
55
+ # On Windows, when the file or directory is removed and created
56
+ # frequently, there is a case that creating file or directory will
57
+ # fail. This situation is caused by pending file or directory
58
+ # deletion which is mentioned on win32 API document [1]
59
+ # As a workaround, execute rename and remove method.
60
+ #
61
+ # [1] https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#files
62
+ #
63
+ file = File.join(Dir.tmpdir, SecureRandom.hex(10))
64
+ begin
65
+ FileUtils.mv(path, file)
66
+ FileUtils.rm_rf(file, secure: true)
67
+ rescue ArgumentError
68
+ FileUtils.rm_rf(file) # For Ruby 2.6 or before.
69
+ end
70
+ if File.exist?(file)
71
+ # ensure files are closed for Windows, on which deleted files
72
+ # are still visible from filesystem
73
+ GC.start(full_mark: true, immediate_mark: true, immediate_sweep: true)
74
+ FileUtils.remove_entry_secure(file, true)
75
+ end
76
+ else
77
+ begin
78
+ FileUtils.rm_f(path, secure: true)
79
+ rescue ArgumentError
80
+ FileUtils.rm_f(path) # For Ruby 2.6 or before.
81
+ end
82
+ if File.exist?(path)
83
+ FileUtils.remove_entry_secure(path, true)
84
+ end
47
85
  end
48
86
  end
49
87
 
@@ -551,6 +589,7 @@ class TailInputTest < Test::Unit::TestCase
551
589
  File.open("#{TMP_DIR}/tail.txt", "wb") {|f|
552
590
  f.puts "test1"
553
591
  f.puts "test2"
592
+ f.flush
554
593
  }
555
594
 
556
595
  d = create_driver(config)
@@ -560,19 +599,23 @@ class TailInputTest < Test::Unit::TestCase
560
599
  f.puts "test3\ntest4"
561
600
  f.flush
562
601
  }
563
- sleep 1
602
+ waiting(2) { sleep 0.1 until d.events.length == 2 }
564
603
  File.truncate("#{TMP_DIR}/tail.txt", 6)
565
604
  end
566
605
 
567
- events = d.events
568
- assert_equal(3, events.length)
569
- assert_equal({"message" => "test3"}, events[0][2])
570
- assert_equal({"message" => "test4"}, events[1][2])
571
- assert_equal({"message" => "test1"}, events[2][2])
572
- assert(events[0][1].is_a?(Fluent::EventTime))
573
- assert(events[1][1].is_a?(Fluent::EventTime))
574
- assert(events[2][1].is_a?(Fluent::EventTime))
575
- assert_equal(2, d.emit_count)
606
+ expected = {
607
+ emit_count: 2,
608
+ events: [
609
+ [Fluent::EventTime, {"message" => "test3"}],
610
+ [Fluent::EventTime, {"message" => "test4"}],
611
+ [Fluent::EventTime, {"message" => "test1"}],
612
+ ]
613
+ }
614
+ actual = {
615
+ emit_count: d.emit_count,
616
+ events: d.events.collect{|event| [event[1].class, event[2]]}
617
+ }
618
+ assert_equal(expected, actual)
576
619
  end
577
620
 
578
621
  def test_move_truncate_move_back
@@ -1876,4 +1919,22 @@ class TailInputTest < Test::Unit::TestCase
1876
1919
  waiting(5) { sleep 0.1 until d.instance.instance_variable_get(:@tails).keys.size == 1 }
1877
1920
  d.instance_shutdown
1878
1921
  end
1922
+
1923
+ def test_ENOENT_error_after_setup_watcher
1924
+ path = "#{TMP_DIR}/tail.txt"
1925
+ FileUtils.touch(path)
1926
+ config = config_element('', '', {
1927
+ 'format' => 'none',
1928
+ })
1929
+ d = create_driver(config)
1930
+ mock.proxy(d.instance).setup_watcher(anything, anything) do |tw|
1931
+ cleanup_file(path)
1932
+ tw
1933
+ end
1934
+ assert_nothing_raised do
1935
+ d.run(shutdown: false) {}
1936
+ end
1937
+ d.instance_shutdown
1938
+ assert($log.out.logs.any?{|log| log.include?("stat() for #{path} failed with ENOENT. Drop tail watcher for now.\n") })
1939
+ end
1879
1940
  end
@@ -2,8 +2,11 @@ require_relative '../helper'
2
2
  require 'fluent/test/driver/multi_output'
3
3
  require 'fluent/plugin/out_copy'
4
4
  require 'fluent/event'
5
+ require 'flexmock/test_unit'
5
6
 
6
7
  class CopyOutputTest < Test::Unit::TestCase
8
+ include FlexMock::TestCase
9
+
7
10
  class << self
8
11
  def startup
9
12
  $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'scripts'))
@@ -54,6 +57,48 @@ class CopyOutputTest < Test::Unit::TestCase
54
57
  assert_equal :no_copy, d.instance.copy_mode
55
58
  end
56
59
 
60
+ ERRORNEOUS_IGNORE_IF_PREV_SUCCESS_CONFIG = %[
61
+ <store ignore_if_prev_success ignore_error>
62
+ @type test
63
+ name c0
64
+ </store>
65
+ <store ignore_if_prev_success ignore_error>
66
+ @type test
67
+ name c1
68
+ </store>
69
+ <store ignore_if_prev_success>
70
+ @type test
71
+ name c2
72
+ </store>
73
+ ]
74
+ def test_configure_with_errorneus_ignore_if_prev_success
75
+ assert_raise(Fluent::ConfigError) do
76
+ create_driver(ERRORNEOUS_IGNORE_IF_PREV_SUCCESS_CONFIG)
77
+ end
78
+ end
79
+
80
+ ALL_IGNORE_ERROR_WITHOUT_IGNORE_IF_PREV_SUCCESS_CONFIG = %[
81
+ @log_level info
82
+ <store ignore_error>
83
+ @type test
84
+ name c0
85
+ </store>
86
+ <store ignore_error>
87
+ @type test
88
+ name c1
89
+ </store>
90
+ <store ignore_error>
91
+ @type test
92
+ name c2
93
+ </store>
94
+ ]
95
+ def test_configure_all_ignore_errors_without_ignore_if_prev_success
96
+ d = create_driver(ALL_IGNORE_ERROR_WITHOUT_IGNORE_IF_PREV_SUCCESS_CONFIG)
97
+ expected = /ignore_errors are specified in all <store>, but ignore_if_prev_success is not specified./
98
+ matches = d.logs.grep(expected)
99
+ assert_equal(1, matches.length, "Logs do not contain '#{expected}' '#{d.logs}'")
100
+ end
101
+
57
102
  def test_configure_with_deep_copy_and_use_shallow_copy_mode
58
103
  d = create_driver(%[
59
104
  deep_copy true
@@ -217,5 +262,47 @@ class CopyOutputTest < Test::Unit::TestCase
217
262
  end
218
263
  end
219
264
  end
265
+
266
+ IGNORE_IF_PREV_SUCCESS_CONFIG = %[
267
+ <store ignore_error>
268
+ @type test
269
+ name c0
270
+ </store>
271
+ <store ignore_if_prev_success ignore_error>
272
+ @type test
273
+ name c1
274
+ </store>
275
+ <store ignore_if_prev_success>
276
+ @type test
277
+ name c2
278
+ </store>
279
+ ]
280
+
281
+ def test_ignore_if_prev_success
282
+ d = create_driver(IGNORE_IF_PREV_SUCCESS_CONFIG)
283
+
284
+ # override to raise an error
285
+ d.instance.outputs[0].define_singleton_method(:process) do |tag, es|
286
+ raise ArgumentError, 'Failed'
287
+ end
288
+
289
+ # check ingore_if_prev_success functionality:
290
+ # 1. output 2 is succeeded.
291
+ # 2. output 3 is not called.
292
+ flexstub(d.instance.outputs[1]) do |output|
293
+ output.should_receive(:process).once
294
+ end
295
+ flexstub(d.instance.outputs[2]) do |output|
296
+ output.should_receive(:process).never
297
+ end
298
+
299
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
300
+ assert_nothing_raised do
301
+ d.run(default_tag: 'test') do
302
+ d.feed(time, {"a"=>1})
303
+ end
304
+ end
305
+ end
306
+
220
307
  end
221
308
 
@@ -367,10 +367,31 @@ EOL
367
367
  require_ack_response true
368
368
  ack_response_timeout 2s
369
369
  ])
370
+ d.instance_start
370
371
  assert d.instance.require_ack_response
371
372
  assert_equal 2, d.instance.ack_response_timeout
372
373
  end
373
374
 
375
+ test 'suspend_flush is disable before before_shutdown' do
376
+ @d = d = create_driver(CONFIG + %[
377
+ require_ack_response true
378
+ ack_response_timeout 2s
379
+ ])
380
+ d.instance_start
381
+ assert_false d.instance.instance_variable_get(:@suspend_flush)
382
+ end
383
+
384
+ test 'suspend_flush should be enabled and try_flush returns nil after before_shutdown' do
385
+ @d = d = create_driver(CONFIG + %[
386
+ require_ack_response true
387
+ ack_response_timeout 2s
388
+ ])
389
+ d.instance_start
390
+ d.instance.before_shutdown
391
+ assert_true d.instance.instance_variable_get(:@suspend_flush)
392
+ assert_nil d.instance.try_flush
393
+ end
394
+
374
395
  test 'verify_connection_at_startup is disabled in default' do
375
396
  @d = d = create_driver(CONFIG)
376
397
  assert_false d.instance.verify_connection_at_startup
@@ -566,31 +587,88 @@ EOL
566
587
  flush_mode immediate
567
588
  retry_type periodic
568
589
  retry_wait 30s
569
- flush_at_shutdown false # suppress errors in d.instance_shutdown
590
+ flush_at_shutdown true
570
591
  </buffer>
571
592
  ])
572
593
 
573
594
  time = event_time("2011-01-02 13:14:15 UTC")
574
595
 
575
596
  acked_chunk_ids = []
597
+ nacked = false
576
598
  mock.proxy(d.instance.ack_handler).read_ack_from_sock(anything) do |info, success|
577
599
  if success
578
600
  acked_chunk_ids << info.chunk_id
601
+ else
602
+ nacked = true
579
603
  end
580
- [chunk_id, success]
604
+ [info, success]
581
605
  end
582
606
 
583
607
  records = [
584
608
  {"a" => 1},
585
609
  {"a" => 2}
586
610
  ]
587
- target_input_driver.run(expect_records: 2) do
588
- d.end_if { acked_chunk_ids.size > 0 }
611
+ target_input_driver.run(expect_records: 2, timeout: 5) do
612
+ d.end_if { acked_chunk_ids.size > 0 || nacked }
589
613
  d.run(default_tag: 'test', wait_flush_completion: false, shutdown: false) do
590
614
  d.feed([[time, records[0]], [time,records[1]]])
591
615
  end
592
616
  end
593
617
 
618
+ assert(!nacked, d.instance.log.logs.join)
619
+
620
+ events = target_input_driver.events
621
+ assert_equal ['test', time, records[0]], events[0]
622
+ assert_equal ['test', time, records[1]], events[1]
623
+
624
+ assert_equal 1, acked_chunk_ids.size
625
+ assert_equal d.instance.sent_chunk_ids.first, acked_chunk_ids.first
626
+ end
627
+
628
+ test 'a node supporting responses after stop' do
629
+ target_input_driver = create_target_input_driver
630
+
631
+ @d = d = create_driver(CONFIG + %[
632
+ require_ack_response true
633
+ ack_response_timeout 1s
634
+ <buffer tag>
635
+ flush_mode immediate
636
+ retry_type periodic
637
+ retry_wait 30s
638
+ flush_at_shutdown true
639
+ </buffer>
640
+ ])
641
+
642
+ time = event_time("2011-01-02 13:14:15 UTC")
643
+
644
+ acked_chunk_ids = []
645
+ nacked = false
646
+ mock.proxy(d.instance.ack_handler).read_ack_from_sock(anything) do |info, success|
647
+ if success
648
+ acked_chunk_ids << info.chunk_id
649
+ else
650
+ nacked = true
651
+ end
652
+ [info, success]
653
+ end
654
+
655
+ records = [
656
+ {"a" => 1},
657
+ {"a" => 2}
658
+ ]
659
+ target_input_driver.run(expect_records: 2, timeout: 5) do
660
+ d.end_if { acked_chunk_ids.size > 0 || nacked }
661
+ d.run(default_tag: 'test', wait_flush_completion: false, shutdown: false) do
662
+ d.instance.stop
663
+ d.feed([[time, records[0]], [time,records[1]]])
664
+ d.instance.before_shutdown
665
+ d.instance.shutdown
666
+ d.instance.after_shutdown
667
+ end
668
+ end
669
+
670
+ assert(!nacked, d.instance.log.logs.join)
671
+
594
672
  events = target_input_driver.events
595
673
  assert_equal ['test', time, records[0]], events[0]
596
674
  assert_equal ['test', time, records[1]], events[1]
@@ -995,7 +1073,7 @@ EOL
995
1073
  test 'when out_forward has @id' do
996
1074
  # cancel https://github.com/fluent/fluentd/blob/077508ac817b7637307434d0c978d7cdc3d1c534/lib/fluent/plugin_id.rb#L43-L53
997
1075
  # it always return true in test
998
- mock.proxy(Fluent::Plugin).new_sd(:static, anything) { |v|
1076
+ mock.proxy(Fluent::Plugin).new_sd(:static, parent: anything) { |v|
999
1077
  stub(v).plugin_id_for_test? { false }
1000
1078
  }.once
1001
1079
 
@@ -1137,6 +1215,8 @@ EOL
1137
1215
  end
1138
1216
 
1139
1217
  test 'Create new connection per send_data' do
1218
+ omit "Proxy of RR doesn't support kwargs of Ruby 3 yet" if RUBY_VERSION.split('.')[0].to_i >= 3
1219
+
1140
1220
  target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
1141
1221
  output_conf = CONFIG
1142
1222
  d = create_driver(output_conf)
@@ -1175,6 +1255,8 @@ EOL
1175
1255
 
1176
1256
  sub_test_case 'keepalive' do
1177
1257
  test 'Do not create connection per send_data' do
1258
+ omit "Proxy of RR doesn't support kwargs of Ruby 3 yet" if RUBY_VERSION.split('.')[0].to_i >= 3
1259
+
1178
1260
  target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
1179
1261
  output_conf = CONFIG + %[
1180
1262
  keepalive true
@@ -1222,7 +1304,13 @@ EOL
1222
1304
 
1223
1305
  begin
1224
1306
  chunk = Fluent::Plugin::Buffer::MemoryChunk.new(Fluent::Plugin::Buffer::Metadata.new(nil, nil, nil))
1225
- mock.proxy(d.instance).socket_create_tcp(TARGET_HOST, TARGET_PORT, anything) { |sock| mock(sock).close.once; sock }.twice
1307
+ mock.proxy(d.instance).socket_create_tcp(TARGET_HOST, TARGET_PORT,
1308
+ linger_timeout: anything,
1309
+ send_timeout: anything,
1310
+ recv_timeout: anything,
1311
+ connect_timeout: anything) { |sock|
1312
+ mock(sock).close.once; sock
1313
+ }.twice
1226
1314
 
1227
1315
  target_input_driver.run(timeout: 15) do
1228
1316
  d.run(shutdown: false) do
@@ -127,7 +127,7 @@ class HTTPOutputTest < Test::Unit::TestCase
127
127
  now = Time.now
128
128
  started = false
129
129
  until started
130
- raise "Server not started" if (now - Time.now > 10.0)
130
+ raise "Server not started" if (Time.now - now > 10.0)
131
131
  begin
132
132
  http_client { |c| c.request_get('/') }
133
133
  started = true