fluentd 1.9.3 → 1.11.1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/issue-auto-closer.yml +12 -0
  3. data/CHANGELOG.md +182 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/README.md +4 -0
  6. data/docs/SECURITY_AUDIT.pdf +0 -0
  7. data/lib/fluent/command/debug.rb +1 -0
  8. data/lib/fluent/command/fluentd.rb +25 -1
  9. data/lib/fluent/config.rb +1 -0
  10. data/lib/fluent/daemonizer.rb +88 -0
  11. data/lib/fluent/log.rb +45 -6
  12. data/lib/fluent/match.rb +1 -1
  13. data/lib/fluent/msgpack_factory.rb +13 -6
  14. data/lib/fluent/plugin/buffer.rb +2 -2
  15. data/lib/fluent/plugin/in_dummy.rb +3 -3
  16. data/lib/fluent/plugin/in_forward.rb +2 -2
  17. data/lib/fluent/plugin/in_gc_stat.rb +16 -0
  18. data/lib/fluent/plugin/in_http.rb +146 -75
  19. data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
  20. data/lib/fluent/plugin/in_syslog.rb +4 -4
  21. data/lib/fluent/plugin/in_tail.rb +40 -31
  22. data/lib/fluent/plugin/in_tail/position_file.rb +23 -6
  23. data/lib/fluent/plugin/in_unix.rb +77 -77
  24. data/lib/fluent/plugin/out_copy.rb +1 -1
  25. data/lib/fluent/plugin/out_file.rb +1 -1
  26. data/lib/fluent/plugin/out_forward.rb +25 -22
  27. data/lib/fluent/plugin/out_forward/handshake_protocol.rb +4 -0
  28. data/lib/fluent/plugin/out_forward/load_balancer.rb +1 -1
  29. data/lib/fluent/plugin/out_http.rb +15 -2
  30. data/lib/fluent/plugin/parser_multiline.rb +1 -1
  31. data/lib/fluent/plugin/parser_syslog.rb +303 -62
  32. data/lib/fluent/plugin/sd_file.rb +1 -0
  33. data/lib/fluent/plugin/sd_srv.rb +135 -0
  34. data/lib/fluent/plugin_helper/cert_option.rb +15 -2
  35. data/lib/fluent/plugin_helper/child_process.rb +3 -2
  36. data/lib/fluent/plugin_helper/record_accessor.rb +14 -0
  37. data/lib/fluent/plugin_helper/server.rb +3 -1
  38. data/lib/fluent/plugin_helper/service_discovery.rb +7 -0
  39. data/lib/fluent/plugin_helper/service_discovery/manager.rb +8 -0
  40. data/lib/fluent/plugin_helper/socket.rb +20 -2
  41. data/lib/fluent/plugin_helper/socket_option.rb +21 -3
  42. data/lib/fluent/supervisor.rb +21 -9
  43. data/lib/fluent/system_config.rb +2 -1
  44. data/lib/fluent/test/filter_test.rb +2 -2
  45. data/lib/fluent/test/output_test.rb +3 -3
  46. data/lib/fluent/version.rb +1 -1
  47. data/test/command/test_fluentd.rb +71 -12
  48. data/test/config/test_system_config.rb +2 -0
  49. data/test/helper.rb +2 -2
  50. data/test/plugin/in_tail/test_fifo.rb +121 -0
  51. data/test/plugin/in_tail/test_io_handler.rb +132 -0
  52. data/test/plugin/in_tail/test_position_file.rb +25 -1
  53. data/test/plugin/out_forward/test_handshake_protocol.rb +10 -1
  54. data/test/plugin/out_forward/test_load_balancer.rb +46 -0
  55. data/test/plugin/test_buf_file.rb +3 -1
  56. data/test/plugin/test_buffer.rb +20 -0
  57. data/test/plugin/test_compressable.rb +7 -4
  58. data/test/plugin/test_in_dummy.rb +12 -14
  59. data/test/plugin/test_in_forward.rb +2 -2
  60. data/test/plugin/test_in_gc_stat.rb +24 -1
  61. data/test/plugin/test_in_http.rb +57 -0
  62. data/test/plugin/test_in_syslog.rb +16 -1
  63. data/test/plugin/test_in_tail.rb +43 -20
  64. data/test/plugin/test_in_unix.rb +128 -73
  65. data/test/plugin/test_out_forward.rb +39 -3
  66. data/test/plugin/test_out_http.rb +38 -0
  67. data/test/plugin/test_out_null.rb +1 -1
  68. data/test/plugin/test_output_as_buffered_retries.rb +12 -4
  69. data/test/plugin/test_output_as_buffered_secondary.rb +9 -1
  70. data/test/plugin/test_parser_syslog.rb +106 -46
  71. data/test/plugin/test_sd_file.rb +17 -0
  72. data/test/plugin/test_sd_srv.rb +230 -0
  73. data/test/plugin_helper/data/cert/cert-with-CRLF.pem +19 -0
  74. data/test/plugin_helper/data/cert/cert_chains/ca-cert-key.pem +27 -0
  75. data/test/plugin_helper/data/cert/cert_chains/ca-cert.pem +20 -0
  76. data/test/plugin_helper/data/cert/cert_chains/cert-key.pem +27 -0
  77. data/test/plugin_helper/data/cert/cert_chains/cert.pem +40 -0
  78. data/test/plugin_helper/data/cert/generate_cert.rb +38 -0
  79. data/test/plugin_helper/http_server/test_app.rb +1 -1
  80. data/test/plugin_helper/http_server/test_route.rb +1 -1
  81. data/test/plugin_helper/test_cert_option.rb +2 -0
  82. data/test/plugin_helper/test_child_process.rb +20 -3
  83. data/test/plugin_helper/test_http_server_helper.rb +2 -2
  84. data/test/plugin_helper/test_record_accessor.rb +41 -0
  85. data/test/plugin_helper/test_server.rb +1 -1
  86. data/test/plugin_helper/test_service_discovery.rb +37 -4
  87. data/test/plugin_helper/test_socket.rb +131 -0
  88. data/test/test_daemonizer.rb +91 -0
  89. data/test/test_log.rb +44 -0
  90. data/test/test_msgpack_factory.rb +18 -0
  91. metadata +28 -2
@@ -1,126 +1,181 @@
1
1
  require_relative '../helper'
2
- require 'fluent/test'
2
+ require 'fluent/test/driver/input'
3
3
  require 'fluent/plugin/in_unix'
4
4
 
5
- module StreamInputTest
5
+ class UnixInputTest < Test::Unit::TestCase
6
6
  def setup
7
7
  Fluent::Test.setup
8
+ @d = nil
8
9
  end
9
10
 
10
- def test_time
11
- d = create_driver
11
+ def teardown
12
+ @d.instance_shutdown if @d
13
+ end
12
14
 
13
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
14
- Fluent::Engine.now = time
15
+ TMP_DIR = File.dirname(__FILE__) + "/../tmp/in_unix#{ENV['TEST_ENV_NUMBER']}"
16
+ CONFIG = %[
17
+ path #{TMP_DIR}/unix
18
+ backlog 1000
19
+ ]
20
+
21
+ def create_driver(conf = CONFIG)
22
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::UnixInput).configure(conf)
23
+ end
15
24
 
16
- d.expect_emit "tag1", time, {"a"=>1}
17
- d.expect_emit "tag2", time, {"a"=>2}
25
+ def packer(*args)
26
+ Fluent::MessagePackFactory.msgpack_packer(*args)
27
+ end
18
28
 
19
- d.run do
20
- d.expected_emits.each {|tag,_time,record|
21
- send_data Fluent::MessagePackFactory.msgpack_packer.write([tag, 0, record]).to_s
29
+ def unpacker
30
+ Fluent::MessagePackFactory.msgpack_unpacker
31
+ end
32
+
33
+ def send_data(data)
34
+ io = UNIXSocket.new("#{TMP_DIR}/unix")
35
+ begin
36
+ io.write data
37
+ ensure
38
+ io.close
39
+ end
40
+ end
41
+
42
+ def test_configure
43
+ @d = create_driver
44
+ assert_equal "#{TMP_DIR}/unix", @d.instance.path
45
+ assert_equal 1000, @d.instance.backlog
46
+ end
47
+
48
+ def test_time
49
+ @d = create_driver
50
+
51
+ time = Fluent::EventTime.now
52
+ records = [
53
+ ["tag1", 0, {"a" => 1}],
54
+ ["tag2", nil, {"a" => 2}],
55
+ ]
56
+
57
+ @d.run(expect_records: records.length, timeout: 5) do
58
+ records.each {|tag, _time, record|
59
+ send_data packer.write([tag, _time, record]).to_s
22
60
  }
23
61
  end
62
+
63
+ @d.events.each_with_index { |e, i|
64
+ orig = records[i]
65
+ assert_equal(orig[0], e[0])
66
+ assert_true(time <= e[1])
67
+ assert_equal(orig[2], e[2])
68
+ }
24
69
  end
25
70
 
26
71
  def test_message
27
- d = create_driver
72
+ @d = create_driver
28
73
 
29
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
30
-
31
- d.expect_emit "tag1", time, {"a"=>1}
32
- d.expect_emit "tag2", time, {"a"=>2}
74
+ time = Fluent::EventTime.now
75
+ records = [
76
+ ["tag1", time, {"a" => 1}],
77
+ ["tag2", time, {"a" => 2}],
78
+ ]
33
79
 
34
- d.run do
35
- d.expected_emits.each {|tag,_time,record|
36
- send_data Fluent::MessagePackFactory.msgpack_packer.write([tag, _time, record]).to_s
80
+ @d.run(expect_records: records.length, timeout: 5) do
81
+ records.each {|tag, _time, record|
82
+ send_data packer.write([tag, _time, record]).to_s
37
83
  }
38
84
  end
85
+
86
+ assert_equal(records, @d.events)
39
87
  end
40
88
 
41
89
  def test_forward
42
- d = create_driver
90
+ @d = create_driver
43
91
 
44
92
  time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
93
+ records = [
94
+ ["tag1", time, {"a" => 1}],
95
+ ["tag1", time, {"a" => 2}]
96
+ ]
45
97
 
46
- d.expect_emit "tag1", time, {"a"=>1}
47
- d.expect_emit "tag1", time, {"a"=>2}
48
-
49
- d.run do
98
+ @d.run(expect_records: records.length, timeout: 20) do
50
99
  entries = []
51
- d.expected_emits.each {|tag,_time,record|
100
+ records.each {|tag, _time, record|
52
101
  entries << [_time, record]
53
102
  }
54
- send_data Fluent::MessagePackFactory.msgpack_packer.write(["tag1", entries]).to_s
103
+ send_data packer.write(["tag1", entries]).to_s
55
104
  end
105
+ assert_equal(records, @d.events)
56
106
  end
57
107
 
58
108
  def test_packed_forward
59
- d = create_driver
109
+ @d = create_driver
60
110
 
61
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
62
-
63
- d.expect_emit "tag1", time, {"a"=>1}
64
- d.expect_emit "tag1", time, {"a"=>2}
111
+ time = Fluent::EventTime.now
112
+ records = [
113
+ ["tag1", time, {"a" => 1}],
114
+ ["tag1", time, {"a" => 2}],
115
+ ]
65
116
 
66
- d.run do
117
+ @d.run(expect_records: records.length, timeout: 20) do
67
118
  entries = ''
68
- d.expected_emits.each {|tag,_time,record|
69
- Fluent::MessagePackFactory.msgpack_packer(entries).write([_time, record]).flush
119
+ records.each {|_tag, _time, record|
120
+ packer(entries).write([_time, record]).flush
70
121
  }
71
- send_data Fluent::MessagePackFactory.msgpack_packer.write(["tag1", entries]).to_s
122
+ send_data packer.write(["tag1", entries]).to_s
72
123
  end
124
+ assert_equal(records, @d.events)
73
125
  end
74
126
 
75
127
  def test_message_json
76
- d = create_driver
128
+ @d = create_driver
129
+
130
+ time = Fluent::EventTime.now
131
+ records = [
132
+ ["tag1", time, {"a" => 1}],
133
+ ["tag2", time, {"a" => 2}],
134
+ ]
135
+
136
+ @d.run(expect_records: records.length, timeout: 5) do
137
+ tag, _time, record = records[0]
138
+ send_data [tag, _time.to_i, record].to_json
139
+ tag, _time, record = records[1]
140
+ send_data [tag, _time.to_f, record].to_json
141
+ end
77
142
 
78
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
143
+ assert_equal(records, @d.events)
144
+ end
145
+
146
+ def test_message_with_tag
147
+ @d = create_driver(CONFIG + "tag new_tag")
79
148
 
80
- d.expect_emit "tag1", time, {"a"=>1}
81
- d.expect_emit "tag2", time, {"a"=>2}
149
+ time = Fluent::EventTime.now
150
+ records = [
151
+ ["tag1", time, {"a" => 1}],
152
+ ["tag2", time, {"a" => 2}],
153
+ ]
82
154
 
83
- d.run do
84
- d.expected_emits.each {|tag,_time,record|
85
- send_data [tag, time, record].to_json
155
+ @d.run(expect_records: records.length, timeout: 5) do
156
+ records.each {|tag, _time, record|
157
+ send_data packer.write([tag, _time, record]).to_s
86
158
  }
87
159
  end
88
- end
89
160
 
90
- def create_driver(klass, conf)
91
- Fluent::Test::InputTestDriver.new(klass).configure(conf)
161
+ @d.events.each { |event|
162
+ assert_equal("new_tag", event[0])
163
+ }
92
164
  end
93
165
 
94
- def send_data(data)
95
- io = connect
96
- begin
97
- io.write data
98
- ensure
99
- io.close
166
+ data('string chunk' => 'broken string',
167
+ 'integer chunk' => 10)
168
+ def test_broken_message(data)
169
+ @d = create_driver
170
+ @d.run(shutdown: false, timeout: 5) do
171
+ @d.instance.__send__(:on_message, data)
100
172
  end
101
- end
102
- end
103
-
104
- class UnixInputTest < Test::Unit::TestCase
105
- include StreamInputTest
106
-
107
- TMP_DIR = File.dirname(__FILE__) + "/../tmp/in_unix#{ENV['TEST_ENV_NUMBER']}"
108
- CONFIG = %[
109
- path #{TMP_DIR}/unix
110
- backlog 1000
111
- ]
112
173
 
113
- def create_driver(conf=CONFIG)
114
- super(Fluent::UnixInput, conf)
115
- end
116
-
117
- def test_configure
118
- d = create_driver
119
- assert_equal "#{TMP_DIR}/unix", d.instance.path
120
- assert_equal 1000, d.instance.backlog
121
- end
174
+ assert_equal 0, @d.events.size
122
175
 
123
- def connect
124
- UNIXSocket.new("#{TMP_DIR}/unix")
176
+ logs = @d.instance.log.logs
177
+ assert_equal 1, logs.select { |line|
178
+ line =~ / \[warn\]: incoming data is broken: msg=#{data.inspect}/
179
+ }.size, "should not accept broken chunk"
125
180
  end
126
181
  end unless Fluent.windows?
@@ -284,6 +284,29 @@ EOL
284
284
  assert_equal 1235, d.instance.discovery_manager.services[1].port
285
285
  end
286
286
 
287
+ test 'pass username and password as empty string to HandshakeProtocol' do
288
+ config_path = File.join(TMP_DIR, "sd_file.conf")
289
+ File.open(config_path, 'w') do |file|
290
+ file.write(%[
291
+ - 'host': 127.0.0.1
292
+ 'port': 1234
293
+ 'weight': 1
294
+ ])
295
+ end
296
+
297
+ mock(Fluent::Plugin::ForwardOutput::HandshakeProtocol).new(log: anything, hostname: nil, shared_key: anything, password: '', username: '')
298
+ @d = d = create_driver(%[
299
+ <service_discovery>
300
+ @type file
301
+ path #{config_path}
302
+ </service_discovery>
303
+ ])
304
+
305
+ assert_equal 1, d.instance.discovery_manager.services.size
306
+ assert_equal '127.0.0.1', d.instance.discovery_manager.services[0].host
307
+ assert_equal 1234, d.instance.discovery_manager.services[0].port
308
+ end
309
+
287
310
  test 'compress_default_value' do
288
311
  @d = d = create_driver
289
312
  assert_equal :text, d.instance.compress
@@ -997,7 +1020,11 @@ EOL
997
1020
  e = assert_raise Fluent::UnrecoverableError do
998
1021
  d.instance_start
999
1022
  end
1000
- assert_match(/Connection refused/, e.message)
1023
+ if Fluent.windows?
1024
+ assert_match(/No connection could be made because the target machine actively refused it/, e.message)
1025
+ else
1026
+ assert_match(/Connection refused/, e.message)
1027
+ end
1001
1028
 
1002
1029
  d.instance_shutdown
1003
1030
  end
@@ -1029,7 +1056,7 @@ EOL
1029
1056
  e = assert_raise Fluent::UnrecoverableError do
1030
1057
  d.instance_start
1031
1058
  end
1032
- assert_match(/Failed to establish connection/, e.message)
1059
+ assert_match(/failed to establish connection/, e.message)
1033
1060
  end
1034
1061
  end
1035
1062
 
@@ -1065,7 +1092,7 @@ EOL
1065
1092
  d.instance_start
1066
1093
  end
1067
1094
 
1068
- assert_match(/Failed to establish connection/, e.message)
1095
+ assert_match(/failed to establish connection/, e.message)
1069
1096
  end
1070
1097
  end
1071
1098
 
@@ -1173,6 +1200,15 @@ EOL
1173
1200
  end
1174
1201
  end
1175
1202
 
1203
+ test 'create timer of purging obsolete sockets' do
1204
+ output_conf = CONFIG + %[keepalive true]
1205
+ d = create_driver(output_conf)
1206
+
1207
+ mock(d.instance).timer_execute(:out_forward_heartbeat_request, 1).once
1208
+ mock(d.instance).timer_execute(:out_forward_keep_alived_socket_watcher, 5).once
1209
+ d.instance_start
1210
+ end
1211
+
1176
1212
  sub_test_case 'with require_ack_response' do
1177
1213
  test 'Create connection per send_data' do
1178
1214
  target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
@@ -80,6 +80,8 @@ class HTTPOutputTest < Test::Unit::TestCase
80
80
  req.body.each_line { |l|
81
81
  data << JSON.parse(l)
82
82
  }
83
+ when 'application/json'
84
+ data = JSON.parse(req.body)
83
85
  when 'text/plain'
84
86
  # Use single_value in this test
85
87
  req.body.each_line { |line|
@@ -181,6 +183,19 @@ class HTTPOutputTest < Test::Unit::TestCase
181
183
  assert_not_match(/Status code 503 is going to be removed/, d.instance.log.out.logs.join)
182
184
  end
183
185
 
186
+ # Check if an exception is raised on not JSON format use
187
+ data('not_json' => 'msgpack')
188
+ def test_configure_with_json_array_err(format_type)
189
+ assert_raise(Fluent::ConfigError) do
190
+ create_driver(config + %[
191
+ json_array true
192
+ <format>
193
+ @type #{format_type}
194
+ </format>
195
+ ])
196
+ end
197
+ end
198
+
184
199
  data('json' => ['json', 'application/x-ndjson'],
185
200
  'ltsv' => ['ltsv', 'text/tab-separated-values'],
186
201
  'msgpack' => ['msgpack', 'application/x-msgpack'],
@@ -195,6 +210,14 @@ class HTTPOutputTest < Test::Unit::TestCase
195
210
  assert_equal content_type, d.instance.content_type
196
211
  end
197
212
 
213
+ # Check that json_array setting sets content_type = application/json
214
+ data('json' => 'application/json')
215
+ def test_configure_content_type_json_array(content_type)
216
+ d = create_driver(config + "json_array true")
217
+
218
+ assert_equal content_type, d.instance.content_type
219
+ end
220
+
198
221
  data('PUT' => 'put', 'POST' => 'post')
199
222
  def test_write_with_method(method)
200
223
  d = create_driver(config + "http_method #{method}")
@@ -211,6 +234,21 @@ class HTTPOutputTest < Test::Unit::TestCase
211
234
  assert_not_empty result.headers
212
235
  end
213
236
 
237
+ # Check that JSON at HTTP request body is valid
238
+ def test_write_with_json_array_setting
239
+ d = create_driver(config + "json_array true")
240
+ d.run(default_tag: 'test.http') do
241
+ test_events.each { |event|
242
+ d.feed(event)
243
+ }
244
+ end
245
+
246
+ result = @@result
247
+ assert_equal 'application/json', result.content_type
248
+ assert_equal test_events, result.data
249
+ assert_not_empty result.headers
250
+ end
251
+
214
252
  def test_write_with_single_value_format
215
253
  d = create_driver(config + %[
216
254
  <format>
@@ -40,7 +40,7 @@ class NullOutputTest < Test::Unit::TestCase
40
40
  assert_equal [], d.instance.chunk_keys
41
41
  end
42
42
 
43
- test 'writes standard formattted chunks' do
43
+ test 'writes standard formatted chunks' do
44
44
  d = create_driver(config_element("ROOT", "", {}, [config_element("buffer")]))
45
45
  t = event_time("2016-05-23 00:22:13 -0800")
46
46
  d.run(default_tag: 'test', flush: true) do
@@ -895,17 +895,25 @@ class BufferedOutputRetryTest < Test::Unit::TestCase
895
895
 
896
896
  @i.flush_thread_wakeup
897
897
  waiting(4){ Thread.pass until @i.write_count > 0 }
898
+ waiting(4) do
899
+ state = @i.instance_variable_get(:@output_flush_threads).first
900
+ state.thread.status == 'sleep'
901
+ end
898
902
 
899
- assert{ @i.write_count > 0 }
900
- assert{ @i.num_errors > 0 }
903
+ assert(@i.write_count > 0)
904
+ assert(@i.num_errors > 0)
901
905
 
902
906
  now = @i.next_flush_time
903
907
  Timecop.freeze( now )
904
908
  @i.flush_thread_wakeup
905
909
  waiting(4){ Thread.pass until @i.write_count > 1 }
910
+ waiting(4) do
911
+ state = @i.instance_variable_get(:@output_flush_threads).first
912
+ state.thread.status == 'sleep'
913
+ end
906
914
 
907
- assert{ @i.write_count > 1 }
908
- assert{ @i.num_errors > 1 }
915
+ assert(@i.write_count > 1)
916
+ assert(@i.num_errors > 1)
909
917
  end
910
918
  end
911
919
  end
@@ -617,7 +617,15 @@ class BufferedOutputSecondaryTest < Test::Unit::TestCase
617
617
  assert{ !chunks[1].empty? }
618
618
 
619
619
  30.times do |i| # large enough
620
- now = first_failure + 60 * 0.8 + 2 + i
620
+ # In https://github.com/fluent/fluentd/blob/c90c024576b3d35f356a55fd33d1232947114a9a/lib/fluent/plugin_helper/retry_state.rb
621
+ # @timeout_at is 2016-04-13 18:34:31, @next_time must be less than 2016-04-13 18:34:30
622
+ #
623
+ # first_failure + 60 * 0.8 + 2 # => 2016-04-13 18:34:21
624
+ # @next_time is not added by 1, but by randomize(@retry_wait) https://github.com/fluent/fluentd/blob/c90c024576b3d35f356a55fd33d1232947114a9a/lib/fluent/plugin_helper/retry_state.rb#L196
625
+ # current_time(=Time.now) + randomize(@retry_wait) < @timeout_at
626
+ # (2016-04-13 18:34:21 + 6) + 3 < 2016-04-13 18:34:31
627
+ # So, current_time must be at most 6
628
+ now = first_failure + 60 * 0.8 + 2 + [i, 6].min
621
629
  Timecop.freeze( now )
622
630
  @i.flush_thread_wakeup
623
631