fluentd 1.12.4 → 1.13.0

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.

@@ -58,6 +58,15 @@ class FileWrapperTest < Test::Unit::TestCase
58
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
59
  Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, "C:\file.txt").inspect)
60
60
  end
61
+
62
+ data('0' => [false, 0],
63
+ '9999' => [false, 9999],
64
+ '10000' => [true, 10000],
65
+ '10001' => [true, 10001])
66
+ test 'wsaerr?' do |data|
67
+ expected, code = data
68
+ assert_equal(expected, Fluent::Win32Error.new(code).wsaerr?)
69
+ end
61
70
  end
62
71
 
63
72
  sub_test_case 'WindowsFile exceptions' do
@@ -92,7 +101,8 @@ class FileWrapperTest < Test::Unit::TestCase
92
101
  path = "#{TMP_DIR}/test_windows_file.txt"
93
102
  file1 = file2 = nil
94
103
  file1 = File.open(path, "wb")
95
- assert_raise(Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, path)) do
104
+ win32err = Fluent::Win32Error.new(ERROR_SHARING_VIOLATION, path)
105
+ assert_raise(Errno::EACCES.new(win32err.message)) do
96
106
  file2 = Fluent::WindowsFile.new(path, 'r', FILE_SHARE_READ)
97
107
  ensure
98
108
  file2.close if file2
@@ -770,6 +770,15 @@ class HttpInputTest < Test::Unit::TestCase
770
770
  end
771
771
  end
772
772
 
773
+ def test_get_request
774
+ d = create_driver(CONFIG)
775
+
776
+ d.run do
777
+ res = get("/cors.test", {}, {})
778
+ assert_equal "200", res.code
779
+ end
780
+ end
781
+
773
782
  def test_cors_preflight
774
783
  d = create_driver(CONFIG + 'cors_allow_origins ["*"]')
775
784
 
@@ -985,6 +994,12 @@ class HttpInputTest < Test::Unit::TestCase
985
994
  assert_equal $test_in_http_connection_object_ids[0], $test_in_http_connection_object_ids[1]
986
995
  end
987
996
 
997
+ def get(path, params, header = {})
998
+ http = Net::HTTP.new("127.0.0.1", PORT)
999
+ req = Net::HTTP::Get.new(path, header)
1000
+ http.request(req)
1001
+ end
1002
+
988
1003
  def options(path, params, header = {})
989
1004
  http = Net::HTTP.new("127.0.0.1", PORT)
990
1005
  req = Net::HTTP::Options.new(path, header)
@@ -156,6 +156,7 @@ class TailInputTest < Test::Unit::TestCase
156
156
  assert_equal 2, d.instance.rotate_wait
157
157
  assert_equal "#{TMP_DIR}/tail.pos", d.instance.pos_file
158
158
  assert_equal 1000, d.instance.read_lines_limit
159
+ assert_equal -1, d.instance.read_bytes_limit_per_second
159
160
  assert_equal false, d.instance.ignore_repeated_permission_error
160
161
  assert_nothing_raised do
161
162
  d.instance.have_read_capability?
@@ -195,6 +196,22 @@ class TailInputTest < Test::Unit::TestCase
195
196
  end
196
197
  end
197
198
 
199
+ sub_test_case "log throttling per file" do
200
+ test "w/o watcher timer is invalid" do
201
+ conf = CONFIG_ENABLE_WATCH_TIMER + config_element("ROOT", "", {"read_bytes_limit_per_second" => "8k"})
202
+ assert_raise(Fluent::ConfigError) do
203
+ create_driver(conf)
204
+ end
205
+ end
206
+
207
+ test "valid" do
208
+ conf = config_element("ROOT", "", {"read_bytes_limit_per_second" => "8k"})
209
+ assert_raise(Fluent::ConfigError) do
210
+ create_driver(conf)
211
+ end
212
+ end
213
+ end
214
+
198
215
  test "both enable_watch_timer and enable_stat_watcher are false" do
199
216
  assert_raise(Fluent::ConfigError) do
200
217
  create_driver(CONFIG_ENABLE_WATCH_TIMER + CONFIG_DISABLE_STAT_WATCHER + PARSE_SINGLE_LINE_CONFIG)
@@ -323,6 +340,138 @@ class TailInputTest < Test::Unit::TestCase
323
340
  assert num_events <= d.emit_count
324
341
  end
325
342
 
343
+ sub_test_case "log throttling per file" do
344
+ teardown do
345
+ cleanup_file("#{TMP_DIR}/tail.txt")
346
+ end
347
+
348
+ sub_test_case "reads_bytes_per_second w/o throttled" do
349
+ data("flat 8192 bytes, 2 events" => [:flat, 100, 8192, 2, false],
350
+ "flat 8192 bytes, 2 events already read limit reached" => [:flat, 100, 8192, 2, true],
351
+ "flat 8192 bytes, 2 events w/o stat watcher" => [:flat_without_stat, 100, 8192, 2, false],
352
+ "flat #{8192*10} bytes, 20 events" => [:flat, 100, (8192 * 10), 20],
353
+ "flat #{8192*10} bytes, 20 events w/o stat watcher" => [:flat_without_stat, 100, (8192 * 10), 20],
354
+ "parse #{8192*4} bytes, 8 events" => [:parse, 100, (8192 * 4), 8],
355
+ "parse #{8192*4} bytes, 8 events w/o stat watcher" => [:parse_without_stat, 100, (8192 * 4), 8],
356
+ "parse #{8192*10} bytes, 20 events" => [:parse, 100, (8192 * 10), 20],
357
+ "parse #{8192*10} bytes, 20 events w/o stat watcher" => [:parse_without_stat, 100, (8192 * 10), 20],
358
+ "flat 8k bytes with unit, 2 events" => [:flat, 100, "8k", 2],
359
+ "flat 8k bytes with unit, 2 events w/o stat watcher" => [:flat_without_stat, 100, "8k", 2],
360
+ "flat #{8*10}k bytes with unit, 20 events" => [:flat, 100, "#{8*10}k", 20],
361
+ "flat #{8*10}k bytes with unit, 20 events w/o stat watcher" => [:flat_without_stat, 100, "#{8*10}k", 20],
362
+ "parse #{8*4}k bytes with unit, 8 events" => [:parse, 100, "#{8*4}k", 8],
363
+ "parse #{8*4}k bytes with unit, 8 events w/o stat watcher" => [:parse_without_stat, 100, "#{8*4}k", 8],
364
+ "parse #{8*10}k bytes with unit, 20 events" => [:parse, 100, "#{8*10}k", 20],
365
+ "parse #{8*10}k bytes with unit, 20 events w/o stat watcher" => [:parse_without_stat, 100, "#{8*10}k", 20])
366
+ def test_emit_with_read_bytes_limit_per_second(data)
367
+ config_style, limit, limit_bytes, num_events = data
368
+ case config_style
369
+ when :flat
370
+ config = CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG + config_element("", "", { "read_lines_limit" => limit, "read_bytes_limit_per_second" => limit_bytes })
371
+ when :parse
372
+ config = CONFIG_READ_FROM_HEAD + config_element("", "", { "read_lines_limit" => limit, "read_bytes_limit_per_second" => limit_bytes }) + PARSE_SINGLE_LINE_CONFIG
373
+ when :flat_without_stat
374
+ config = CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG + CONFIG_DISABLE_STAT_WATCHER + config_element("", "", { "read_lines_limit" => limit, "read_bytes_limit_per_second" => limit_bytes })
375
+ when :parse_without_stat
376
+ config = CONFIG_READ_FROM_HEAD + CONFIG_DISABLE_STAT_WATCHER + config_element("", "", { "read_lines_limit" => limit, "read_bytes_limit_per_second" => limit_bytes }) + PARSE_SINGLE_LINE_CONFIG
377
+ end
378
+
379
+ msg = 'test' * 2000 # in_tail reads 8192 bytes at once.
380
+ start_time = Fluent::Clock.now
381
+
382
+ d = create_driver(config)
383
+ d.run(expect_emits: 2) do
384
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
385
+ 100.times do
386
+ f.puts msg
387
+ end
388
+ }
389
+ end
390
+
391
+ assert_true(Fluent::Clock.now - start_time > 1)
392
+ assert_equal(num_events.times.map { {"message" => msg} },
393
+ d.events.collect { |event| event[2] })
394
+ end
395
+
396
+ def test_read_bytes_limit_precede_read_lines_limit
397
+ config = CONFIG_READ_FROM_HEAD +
398
+ SINGLE_LINE_CONFIG +
399
+ config_element("", "", {
400
+ "read_lines_limit" => 1000,
401
+ "read_bytes_limit_per_second" => 8192
402
+ })
403
+ msg = 'abc'
404
+ start_time = Fluent::Clock.now
405
+ d = create_driver(config)
406
+ d.run(expect_emits: 2) do
407
+ File.open("#{TMP_DIR}/tail.txt", "ab") {|f|
408
+ 8000.times do
409
+ f.puts msg
410
+ end
411
+ }
412
+ end
413
+
414
+ assert_true(Fluent::Clock.now - start_time > 1)
415
+ assert_equal(4096.times.map { {"message" => msg} },
416
+ d.events.collect { |event| event[2] })
417
+ end
418
+ end
419
+
420
+ sub_test_case "reads_bytes_per_second w/ throttled already" do
421
+ data("flat 8192 bytes, 2 events" => [:flat, 100, 8192, 2],
422
+ "flat #{8192*10} bytes, 20 events" => [:flat, 100, (8192 * 10), 20],
423
+ "parse #{8192*4} bytes, 8 events" => [:parse, 100, (8192 * 4), 8],
424
+ "parse #{8192*10} bytes, 20 events" => [:parse, 100, (8192 * 10), 20],
425
+ "flat 8k bytes with unit, 2 events" => [:flat, 100, "8k", 2],
426
+ "flat #{8*10}k bytes with unit, 20 events" => [:flat, 100, "#{8*10}k", 20],
427
+ "parse #{8*4}k bytes with unit, 8 events" => [:parse, 100, "#{8*4}k", 8],
428
+ "parse #{8*10}k bytes with unit, 20 events" => [:parse, 100, "#{8*10}k", 20])
429
+ def test_emit_with_read_bytes_limit_per_second(data)
430
+ config_style, limit, limit_bytes, num_events = data
431
+ case config_style
432
+ when :flat
433
+ config = CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG + config_element("", "", { "read_lines_limit" => limit, "read_bytes_limit_per_second" => limit_bytes })
434
+ when :parse
435
+ config = CONFIG_READ_FROM_HEAD + config_element("", "", { "read_lines_limit" => limit, "read_bytes_limit_per_second" => limit_bytes }) + PARSE_SINGLE_LINE_CONFIG
436
+ end
437
+ d = create_driver(config)
438
+ msg = 'test' * 2000 # in_tail reads 8192 bytes at once.
439
+
440
+ mock.proxy(d.instance).io_handler(anything, anything) do |io_handler|
441
+ require 'fluent/config/types'
442
+ limit_bytes_value = Fluent::Config.size_value(limit_bytes)
443
+ io_handler.instance_variable_set(:@number_bytes_read, limit_bytes_value)
444
+ if Fluent.linux?
445
+ mock.proxy(io_handler).handle_notify.at_least(5)
446
+ else
447
+ mock.proxy(io_handler).handle_notify.twice
448
+ end
449
+ io_handler
450
+ end
451
+
452
+ File.open("#{TMP_DIR}/tail.txt", "ab") do |f|
453
+ 100.times do
454
+ f.puts msg
455
+ end
456
+ end
457
+
458
+ # We should not do shutdown here due to hard timeout.
459
+ d.run do
460
+ start_time = Fluent::Clock.now
461
+ while Fluent::Clock.now - start_time < 0.8 do
462
+ File.open("#{TMP_DIR}/tail.txt", "ab") do |f|
463
+ f.puts msg
464
+ f.flush
465
+ end
466
+ sleep 0.05
467
+ end
468
+ end
469
+
470
+ assert_equal([], d.events)
471
+ end
472
+ end
473
+ end
474
+
326
475
  data(flat: CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG,
327
476
  parse: CONFIG_READ_FROM_HEAD + PARSE_SINGLE_LINE_CONFIG)
328
477
  def test_emit_with_read_from_head(data)
@@ -1935,6 +2084,79 @@ class TailInputTest < Test::Unit::TestCase
1935
2084
  d.run(shutdown: false) {}
1936
2085
  end
1937
2086
  d.instance_shutdown
1938
- assert($log.out.logs.any?{|log| log.include?("stat() for #{path} failed with ENOENT. Drop tail watcher for now.\n") })
2087
+ assert($log.out.logs.any?{|log| log.include?("stat() for #{path} failed with Errno::ENOENT. Drop tail watcher for now.\n") })
2088
+ end
2089
+
2090
+ def test_EACCES_error_after_setup_watcher
2091
+ path = "#{TMP_DIR}/noaccess/tail.txt"
2092
+ begin
2093
+ FileUtils.mkdir_p("#{TMP_DIR}/noaccess")
2094
+ FileUtils.chmod(0755, "#{TMP_DIR}/noaccess")
2095
+ FileUtils.touch(path)
2096
+ config = config_element('', '', {
2097
+ 'tag' => "tail",
2098
+ 'path' => path,
2099
+ 'format' => 'none',
2100
+ })
2101
+ d = create_driver(config, false)
2102
+ mock.proxy(d.instance).setup_watcher(anything, anything) do |tw|
2103
+ FileUtils.chmod(0000, "#{TMP_DIR}/noaccess")
2104
+ tw
2105
+ end
2106
+ assert_nothing_raised do
2107
+ d.run(shutdown: false) {}
2108
+ end
2109
+ d.instance_shutdown
2110
+ assert($log.out.logs.any?{|log| log.include?("stat() for #{path} failed with Errno::EACCES. Drop tail watcher for now.\n") })
2111
+ end
2112
+ ensure
2113
+ FileUtils.chmod(0755, "#{TMP_DIR}/noaccess")
2114
+ FileUtils.rm_rf("#{TMP_DIR}/noaccess")
2115
+ end unless Fluent.windows?
2116
+
2117
+ def test_EACCES
2118
+ path = "#{TMP_DIR}/tail.txt"
2119
+ FileUtils.touch(path)
2120
+ config = config_element('', '', {
2121
+ 'format' => 'none',
2122
+ })
2123
+ d = create_driver(config)
2124
+ mock.proxy(Fluent::FileWrapper).stat(path) do |stat|
2125
+ raise Errno::EACCES
2126
+ end.at_least(1)
2127
+ assert_nothing_raised do
2128
+ d.run(shutdown: false) {}
2129
+ end
2130
+ d.instance_shutdown
2131
+ assert($log.out.logs.any?{|log| log.include?("expand_paths: stat() for #{path} failed with Errno::EACCES. Skip file.\n") })
2132
+ end
2133
+
2134
+ def test_shutdown_timeout
2135
+ path = "#{TMP_DIR}/tail.txt"
2136
+ File.open("#{TMP_DIR}/tail.txt", "wb") do |f|
2137
+ (1024 * 1024 * 5).times do
2138
+ f.puts "{\"test\":\"fizzbuzz\"}"
2139
+ end
2140
+ end
2141
+
2142
+ config =
2143
+ CONFIG_READ_FROM_HEAD +
2144
+ config_element('', '', {
2145
+ 'format' => 'json',
2146
+ 'skip_refresh_on_startup' => true,
2147
+ })
2148
+ d = create_driver(config)
2149
+ mock.proxy(d.instance).io_handler(anything, anything) do |io_handler|
2150
+ io_handler.shutdown_timeout = 0.5
2151
+ io_handler
2152
+ end
2153
+
2154
+ start_time = Fluent::Clock.now
2155
+ assert_nothing_raised do
2156
+ d.run(expect_emits: 1)
2157
+ end
2158
+
2159
+ elapsed = Fluent::Clock.now - start_time
2160
+ assert_true(elapsed > 0.5 && elapsed < 2.5)
1939
2161
  end
1940
2162
  end
@@ -277,11 +277,16 @@ EOL
277
277
  </service_discovery>
278
278
  ])
279
279
 
280
- assert_equal 2, d.instance.discovery_manager.services.size
281
- assert_equal '127.0.0.1', d.instance.discovery_manager.services[0].host
282
- assert_equal 1234, d.instance.discovery_manager.services[0].port
283
- assert_equal '127.0.0.1', d.instance.discovery_manager.services[1].host
284
- assert_equal 1235, d.instance.discovery_manager.services[1].port
280
+
281
+ assert_equal(
282
+ [
283
+ { host: '127.0.0.1', port: 1234 },
284
+ { host: '127.0.0.1', port: 1235 },
285
+ ],
286
+ d.instance.discovery_manager.services.collect do |service|
287
+ { host: service.host, port: service.port }
288
+ end
289
+ )
285
290
  end
286
291
 
287
292
  test 'pass username and password as empty string to HandshakeProtocol' do
@@ -1073,7 +1078,7 @@ EOL
1073
1078
  test 'when out_forward has @id' do
1074
1079
  # cancel https://github.com/fluent/fluentd/blob/077508ac817b7637307434d0c978d7cdc3d1c534/lib/fluent/plugin_id.rb#L43-L53
1075
1080
  # it always return true in test
1076
- mock.proxy(Fluent::Plugin).new_sd(:static, parent: anything) { |v|
1081
+ mock.proxy(Fluent::Plugin).new_sd('static', parent: anything) { |v|
1077
1082
  stub(v).plugin_id_for_test? { false }
1078
1083
  }.once
1079
1084
 
@@ -17,6 +17,24 @@ class ServiceDiscoveryHelper < Test::Unit::TestCase
17
17
  end
18
18
  end
19
19
 
20
+ class DummyPlugin < Fluent::Plugin::TestBase
21
+ helpers :service_discovery
22
+
23
+ def configure(conf)
24
+ super
25
+ service_discovery_configure(:service_discovery_helper_test, static_default_service_directive: 'node')
26
+ end
27
+
28
+ def select_service(&block)
29
+ service_discovery_select_service(&block)
30
+ end
31
+
32
+ # Make these mehtod public
33
+ def discovery_manager
34
+ super
35
+ end
36
+ end
37
+
20
38
  setup do
21
39
  @sd_file_dir = File.expand_path('../plugin/data/sd_file', __dir__)
22
40
 
@@ -33,7 +51,7 @@ class ServiceDiscoveryHelper < Test::Unit::TestCase
33
51
  end
34
52
  end
35
53
 
36
- test 'start discovery manager' do
54
+ test 'support calling #service_discovery_create_manager and #discovery_manager from plugin' do
37
55
  d = @d = Dummy.new
38
56
 
39
57
  d.service_discovery_create_manager(
@@ -55,13 +73,30 @@ class ServiceDiscoveryHelper < Test::Unit::TestCase
55
73
  assert_equal 1234, services[0].port
56
74
  end
57
75
 
58
- test 'call timer_execute if dynamic configuration' do
59
- d = @d = Dummy.new
76
+ test 'start discovery manager' do
77
+ d = @d = DummyPlugin.new
60
78
 
61
- d.service_discovery_create_manager(
62
- :service_discovery_helper_test,
63
- configurations: [{ type: :file, conf: config_element('file_config', '', { 'path' => File.join(@sd_file_dir, 'config.yml') }) }],
64
- )
79
+ services = [config_element('service', '', { 'host' => '127.0.0.1', 'port' => '1234' })]
80
+ d.configure(config_element('root', '', {}, [config_element('service_discovery', '', {'@type' => 'static'}, services)]))
81
+
82
+ assert_true !!d.discovery_manager
83
+
84
+ mock.proxy(d.discovery_manager).start.once
85
+ mock.proxy(d).timer_execute(:service_discovery_helper_test, anything).never
86
+
87
+ d.start
88
+ d.event_loop_wait_until_start
89
+
90
+ assert_equal 1, d.discovery_manager.services.size
91
+ d.select_service do |serv|
92
+ assert_equal "127.0.0.1", serv.host
93
+ assert_equal 1234, serv.port
94
+ end
95
+ end
96
+
97
+ test 'call timer_execute if dynamic configuration' do
98
+ d = @d = DummyPlugin.new
99
+ d.configure(config_element('root', '', {}, [config_element('service_discovery', '', { '@type' => 'file', 'path' => File.join(@sd_file_dir, 'config.yml' )})]))
65
100
 
66
101
  assert_true !!d.discovery_manager
67
102
  mock.proxy(d.discovery_manager).start.once
@@ -71,25 +106,22 @@ class ServiceDiscoveryHelper < Test::Unit::TestCase
71
106
  end
72
107
 
73
108
  test 'exits service discovery instances without any errors' do
74
- d = @d = Dummy.new
109
+ d = @d = DummyPlugin.new
75
110
  mockv = flexmock('dns_resolver', getaddress: '127.0.0.1')
76
111
  .should_receive(:getresources)
77
112
  .and_return([Resolv::DNS::Resource::IN::SRV.new(1, 10, 8081, 'service1.example.com')])
78
113
  .mock
79
114
  mock(Resolv::DNS).new { mockv }
80
115
 
81
- d.service_discovery_create_manager(
82
- :service_discovery_helper_test2,
83
- configurations: [{ type: :srv, conf: config_element('service_discovery', '', { 'service' => 'service1', 'hostname' => 'example.com' }) }],
84
- )
116
+ d.configure(config_element('root', '', {}, [config_element('service_discovery', '', { '@type' => 'srv', 'service' => 'service1', 'hostname' => 'example.com' })]))
85
117
 
86
118
  assert_true !!d.discovery_manager
87
119
  mock.proxy(d.discovery_manager).start.once
88
- mock(d).timer_execute(:service_discovery_helper_test2, anything).once
120
+ mock(d).timer_execute(:service_discovery_helper_test, anything).once
89
121
 
90
122
  # To avoid claring `@logs` during `terminate` step
91
123
  # https://github.com/fluent/fluentd/blob/bc78d889f93dad8c2a4e0ad1ca802546185dacba/lib/fluent/test/log.rb#L33
92
- mock(d.log).reset.twice
124
+ mock(d.log).reset.times(3)
93
125
 
94
126
  d.start
95
127
  d.event_loop_wait_until_start
@@ -102,4 +134,32 @@ class ServiceDiscoveryHelper < Test::Unit::TestCase
102
134
 
103
135
  assert_false(d.log.out.logs.any? { |e| e.match?(/thread doesn't exit correctly/) })
104
136
  end
137
+
138
+ test 'static service discovery will be configured automatically when default service directive is specified' do
139
+ d = @d = DummyPlugin.new
140
+
141
+ nodes = [
142
+ config_element('node', '', { 'host' => '192.168.0.1', 'port' => '24224' }),
143
+ config_element('node', '', { 'host' => '192.168.0.2', 'port' => '24224' })
144
+ ]
145
+ d.configure(config_element('root', '', {}, nodes))
146
+
147
+ assert_true !!d.discovery_manager
148
+
149
+ mock.proxy(d.discovery_manager).start.once
150
+ mock.proxy(d).timer_execute(:service_discovery_helper_test, anything).never
151
+
152
+ d.start
153
+ d.event_loop_wait_until_start
154
+
155
+ assert_equal 2, d.discovery_manager.services.size
156
+ d.select_service do |serv|
157
+ assert_equal "192.168.0.1", serv.host
158
+ assert_equal 24224, serv.port
159
+ end
160
+ d.select_service do |serv|
161
+ assert_equal "192.168.0.2", serv.host
162
+ assert_equal 24224, serv.port
163
+ end
164
+ end
105
165
  end