fluentd 0.14.2 → 0.14.5

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +77 -1131
  3. data/Rakefile +15 -0
  4. data/appveyor.yml +2 -2
  5. data/example/in_forward.conf +3 -0
  6. data/example/in_forward_client.conf +37 -0
  7. data/example/in_forward_shared_key.conf +15 -0
  8. data/example/in_forward_users.conf +24 -0
  9. data/example/multi_filters.conf +61 -0
  10. data/example/out_forward.conf +13 -13
  11. data/example/out_forward_client.conf +109 -0
  12. data/example/out_forward_shared_key.conf +36 -0
  13. data/example/out_forward_users.conf +65 -0
  14. data/example/{out_buffered_null.conf → out_null.conf} +10 -6
  15. data/example/secondary_file.conf +41 -0
  16. data/fluentd.gemspec +19 -16
  17. data/lib/fluent/agent.rb +5 -0
  18. data/lib/fluent/command/debug.rb +4 -4
  19. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +2 -0
  20. data/lib/fluent/compat/output.rb +5 -2
  21. data/lib/fluent/config/configure_proxy.rb +26 -5
  22. data/lib/fluent/config/error.rb +3 -0
  23. data/lib/fluent/config/section.rb +15 -0
  24. data/lib/fluent/event_router.rb +77 -4
  25. data/lib/fluent/plugin/base.rb +12 -3
  26. data/lib/fluent/plugin/buffer.rb +5 -1
  27. data/lib/fluent/plugin/filter.rb +48 -6
  28. data/lib/fluent/plugin/filter_record_transformer.rb +3 -1
  29. data/lib/fluent/plugin/filter_stdout.rb +0 -4
  30. data/lib/fluent/plugin/in_debug_agent.rb +5 -5
  31. data/lib/fluent/plugin/in_dummy.rb +9 -1
  32. data/lib/fluent/plugin/in_forward.rb +330 -62
  33. data/lib/fluent/plugin/in_monitor_agent.rb +31 -77
  34. data/lib/fluent/plugin/in_tail.rb +76 -92
  35. data/lib/fluent/plugin/multi_output.rb +4 -0
  36. data/lib/fluent/plugin/out_forward.rb +328 -222
  37. data/lib/fluent/plugin/out_null.rb +37 -0
  38. data/lib/fluent/plugin/out_secondary_file.rb +128 -0
  39. data/lib/fluent/plugin/out_stdout.rb +38 -2
  40. data/lib/fluent/plugin/output.rb +29 -6
  41. data/lib/fluent/plugin/storage_local.rb +16 -0
  42. data/lib/fluent/plugin_helper/timer.rb +6 -1
  43. data/lib/fluent/root_agent.rb +4 -1
  44. data/lib/fluent/supervisor.rb +62 -9
  45. data/lib/fluent/test/base.rb +3 -0
  46. data/lib/fluent/test/driver/base.rb +5 -0
  47. data/lib/fluent/test/formatter_test.rb +2 -0
  48. data/lib/fluent/test/parser_test.rb +2 -0
  49. data/lib/fluent/test/startup_shutdown.rb +33 -0
  50. data/lib/fluent/version.rb +1 -1
  51. data/lib/fluent/winsvc.rb +1 -2
  52. data/test/compat/test_calls_super.rb +2 -0
  53. data/test/config/test_configurable.rb +88 -0
  54. data/test/config/test_types.rb +7 -3
  55. data/test/plugin/test_filter.rb +121 -23
  56. data/test/plugin/test_filter_record_transformer.rb +22 -6
  57. data/test/plugin/test_in_debug_agent.rb +2 -2
  58. data/test/plugin/test_in_forward.rb +920 -407
  59. data/test/plugin/test_in_monitor_agent.rb +333 -0
  60. data/test/plugin/test_in_tail.rb +686 -595
  61. data/test/plugin/test_out_forward.rb +150 -207
  62. data/test/plugin/test_out_null.rb +85 -9
  63. data/test/plugin/test_out_secondary_file.rb +432 -0
  64. data/test/plugin/test_out_stdout.rb +143 -45
  65. data/test/plugin/test_output.rb +53 -0
  66. data/test/plugin/test_output_as_buffered.rb +13 -0
  67. data/test/plugin/test_output_as_buffered_overflow.rb +3 -0
  68. data/test/plugin/test_output_as_buffered_retries.rb +11 -0
  69. data/test/plugin/test_output_as_buffered_secondary.rb +12 -0
  70. data/test/plugin/test_output_as_standard.rb +12 -0
  71. data/test/plugin_helper/test_compat_parameters.rb +2 -0
  72. data/test/plugin_helper/test_timer.rb +31 -0
  73. data/test/test_event_router.rb +87 -0
  74. data/test/test_filter.rb +48 -6
  75. data/test/test_log.rb +5 -2
  76. data/test/test_output.rb +41 -1
  77. data/test/test_plugin_classes.rb +17 -9
  78. data/test/test_root_agent.rb +188 -0
  79. metadata +62 -68
  80. data/lib/fluent/plugin/out_buffered_null.rb +0 -59
  81. data/lib/fluent/plugin/out_buffered_stdout.rb +0 -70
  82. data/test/plugin/test_out_buffered_null.rb +0 -79
  83. data/test/plugin/test_out_buffered_stdout.rb +0 -122
@@ -1,46 +1,73 @@
1
1
  require_relative '../helper'
2
2
 
3
3
  require 'fluent/test'
4
+ require 'fluent/test/startup_shutdown'
4
5
  require 'base64'
5
6
 
6
7
  require 'fluent/env'
7
8
  require 'fluent/plugin/in_forward'
8
9
 
9
10
  class ForwardInputTest < Test::Unit::TestCase
10
- class << self
11
- def startup
12
- socket_manager_path = ServerEngine::SocketManager::Server.generate_path
13
- @server = ServerEngine::SocketManager::Server.open(socket_manager_path)
14
- ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
15
- end
16
-
17
- def shutdown
18
- @server.close
19
- end
20
- end
21
-
22
11
  def setup
23
12
  Fluent::Test.setup
24
13
  @responses = [] # for testing responses after sending data
25
14
  end
26
15
 
27
16
  PORT = unused_port
17
+
18
+ SHARED_KEY = 'foobar1'
19
+ USER_NAME = 'tagomoris'
20
+ USER_PASSWORD = 'fluentd'
21
+
28
22
  CONFIG = %[
29
23
  port #{PORT}
30
24
  bind 127.0.0.1
31
25
  ]
26
+ PEERADDR = ['?', '0000', '127.0.0.1', '127.0.0.1']
27
+ CONFIG_AUTH = %[
28
+ port #{PORT}
29
+ bind 127.0.0.1
30
+ <security>
31
+ self_hostname localhost
32
+ shared_key foobar1
33
+ user_auth true
34
+ <user>
35
+ username #{USER_NAME}
36
+ password #{USER_PASSWORD}
37
+ </user>
38
+ <client>
39
+ network 127.0.0.0/8
40
+ shared_key #{SHARED_KEY}
41
+ users ["#{USER_NAME}"]
42
+ </client>
43
+ </security>
44
+ ]
32
45
 
33
46
  def create_driver(conf=CONFIG)
34
47
  Fluent::Test::InputTestDriver.new(Fluent::ForwardInput).configure(conf)
35
48
  end
36
49
 
37
- def test_configure
38
- d = create_driver
39
- assert_equal PORT, d.instance.port
40
- assert_equal '127.0.0.1', d.instance.bind
41
- assert_equal 0, d.instance.linger_timeout
42
- assert_equal 0.5, d.instance.blocking_timeout
43
- assert !d.instance.backlog
50
+ class Configure < self
51
+ def test_simple
52
+ d = create_driver
53
+ assert_equal PORT, d.instance.port
54
+ assert_equal '127.0.0.1', d.instance.bind
55
+ assert_equal 0, d.instance.linger_timeout
56
+ assert_equal 0.5, d.instance.blocking_timeout
57
+ assert !d.instance.backlog
58
+ end
59
+
60
+ def test_auth
61
+ d = create_driver(CONFIG_AUTH)
62
+ assert_equal PORT, d.instance.port
63
+ assert_equal '127.0.0.1', d.instance.bind
64
+ assert_equal 0, d.instance.linger_timeout
65
+ assert !d.instance.backlog
66
+
67
+ assert d.instance.security
68
+ assert_equal 1, d.instance.security.users.size
69
+ assert_equal 1, d.instance.security.clients.size
70
+ end
44
71
  end
45
72
 
46
73
  # TODO: Will add Loop::run arity check with stub/mock library
@@ -49,538 +76,1024 @@ class ForwardInputTest < Test::Unit::TestCase
49
76
  TCPSocket.new('127.0.0.1', PORT)
50
77
  end
51
78
 
52
- def test_time
53
- d = create_driver
79
+ class Message < self
80
+ extend Fluent::Test::StartupShutdown
54
81
 
55
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
56
- Fluent::Engine.now = time
82
+ def test_time
83
+ d = create_driver
57
84
 
58
- d.expect_emit "tag1", time, {"a"=>1}
59
- d.expect_emit "tag2", time, {"a"=>2}
85
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
86
+ Fluent::Engine.now = time
60
87
 
61
- d.run do
62
- d.expected_emits.each {|tag, _time, record|
63
- send_data Fluent::Engine.msgpack_factory.packer.write([tag, 0, record]).to_s
64
- }
65
- end
66
- end
67
-
68
- def test_message
69
- d = create_driver
88
+ records = [
89
+ ["tag1", time, {"a"=>1}],
90
+ ["tag2", time, {"a"=>2}],
91
+ ]
70
92
 
71
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
72
-
73
- d.expect_emit "tag1", time, {"a"=>1}
74
- d.expect_emit "tag2", time, {"a"=>2}
75
-
76
- d.run do
77
- d.expected_emits.each {|tag, _time, record|
78
- send_data Fluent::Engine.msgpack_factory.packer.write([tag, _time, record]).to_s
79
- }
93
+ d.expected_emits_length = records.length
94
+ d.run_timeout = 2
95
+ d.run do
96
+ records.each {|tag, _time, record|
97
+ send_data packer.write([tag, 0, record]).to_s
98
+ }
99
+ end
100
+ assert_equal(records, d.emits.sort_by {|a| a[0] })
80
101
  end
81
- end
82
102
 
83
- def test_message_with_time_as_integer
84
- d = create_driver
103
+ def test_plain
104
+ d = create_driver
85
105
 
86
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
106
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
87
107
 
88
- d.expect_emit "tag1", time, {"a"=>1}
89
- d.expect_emit "tag2", time, {"a"=>2}
108
+ records = [
109
+ ["tag1", time, {"a"=>1}],
110
+ ["tag2", time, {"a"=>2}],
111
+ ]
90
112
 
91
- d.run do
92
- d.expected_emits.each {|tag, _time, record|
93
- send_data Fluent::Engine.msgpack_factory.packer.write([tag, _time, record]).to_s
94
- }
113
+ d.expected_emits_length = records.length
114
+ d.run_timeout = 2
115
+ d.run do
116
+ records.each {|tag, _time, record|
117
+ send_data packer.write([tag, _time, record]).to_s
118
+ }
119
+ end
120
+ assert_equal(records, d.emits)
95
121
  end
96
- end
97
122
 
98
- def test_message_with_skip_invalid_event
99
- d = create_driver(CONFIG + "skip_invalid_event true")
123
+ def test_time_as_integer
124
+ d = create_driver
100
125
 
101
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
126
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
102
127
 
103
- d.expect_emit "tag1", time, {"a" => 1}
104
- d.expect_emit "tag2", time, {"a" => 2}
128
+ records = [
129
+ ["tag1", time, {"a"=>1}],
130
+ ["tag2", time, {"a"=>2}],
131
+ ]
105
132
 
106
- d.run do
107
- entries = d.expected_emits.map {|tag, _time, record| [tag, _time, record] }
108
- # These entries are skipped
109
- entries << ['tag1', true, {'a' => 3}] << ['tag2', time, 'invalid record']
133
+ d.expected_emits_length = records.length
134
+ d.run_timeout = 2
135
+ d.run do
136
+ records.each {|tag, _time, record|
137
+ send_data packer.write([tag, _time, record]).to_s
138
+ }
139
+ end
110
140
 
111
- entries.each {|tag, _time, record|
112
- # Without ack, logs are sometimes not saved to logs during test.
113
- send_data Fluent::Engine.msgpack_factory.packer.write([tag, _time, record]).to_s, true
114
- }
141
+ assert_equal(records, d.emits)
115
142
  end
116
143
 
117
- assert_equal 2, d.instance.log.logs.count { |line| line =~ /got invalid event and drop it/ }
118
- end
144
+ def test_skip_invalid_event
145
+ d = create_driver(CONFIG + "skip_invalid_event true")
119
146
 
120
- def test_forward
121
- d = create_driver
147
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
122
148
 
123
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
149
+ records = [
150
+ ["tag1", time, {"a" => 1}],
151
+ ["tag2", time, {"a" => 2}],
152
+ ]
124
153
 
125
- d.expect_emit "tag1", time, {"a"=>1}
126
- d.expect_emit "tag1", time, {"a"=>2}
154
+ d.run do
155
+ entries = records.map { |tag, _time, record| [tag, _time, record] }
156
+ # These entries are skipped
157
+ entries << ['tag1', true, {'a' => 3}] << ['tag2', time, 'invalid record']
127
158
 
128
- d.run do
129
- entries = []
130
- d.expected_emits.each {|tag, _time,record|
131
- entries << [time, record]
132
- }
133
- send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
134
- end
135
- end
136
-
137
- def test_forward_with_time_as_integer
138
- d = create_driver
139
-
140
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
141
-
142
- d.expect_emit "tag1", time, {"a"=>1}
143
- d.expect_emit "tag1", time, {"a"=>2}
159
+ entries.each {|tag, _time, record|
160
+ # Without ack, logs are sometimes not saved to logs during test.
161
+ send_data packer.write([tag, _time, record]).to_s, try_to_receive_response: true
162
+ }
163
+ end
144
164
 
145
- d.run do
146
- entries = []
147
- d.expected_emits.each {|_tag, _time, record|
148
- entries << [_time, record]
149
- }
150
- send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
165
+ assert_equal 2, d.instance.log.logs.count { |line| line =~ /got invalid event and drop it/ }
166
+ assert_equal records[0], d.emits[0]
167
+ assert_equal records[1], d.emits[1]
151
168
  end
152
- end
153
169
 
154
- def test_forward_with_skip_invalid_event
155
- d = create_driver(CONFIG + "skip_invalid_event true")
170
+ def test_json
171
+ d = create_driver
156
172
 
157
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
173
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
158
174
 
159
- d.expect_emit "tag1", time, {"a" => 1}
160
- d.expect_emit "tag1", time, {"a" => 2}
175
+ records = [
176
+ ["tag1", time, {"a"=>1}],
177
+ ["tag2", time, {"a"=>2}],
178
+ ]
161
179
 
162
- d.run do
163
- entries = d.expected_emits.map {|_tag, _time, record| [_time, record] }
164
- # These entries are skipped
165
- entries << ['invalid time', {'a' => 3}] << [time, 'invalid record']
180
+ d.expected_emits_length = records.length
181
+ d.run_timeout = 2
182
+ d.run do
183
+ records.each {|tag, _time, record|
184
+ send_data [tag, _time, record].to_json
185
+ }
186
+ end
166
187
 
167
- send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
188
+ assert_equal(records, d.emits.sort_by {|a| a[1] })
168
189
  end
169
190
 
170
- assert_equal 2, d.instance.log.logs.count { |line| line =~ /skip invalid event/ }
171
- end
191
+ def test_json_with_newline
192
+ d = create_driver
172
193
 
173
- def test_packed_forward
174
- d = create_driver
194
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
175
195
 
176
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
196
+ records = [
197
+ ["tag1", time, {"a"=>1}],
198
+ ["tag2", time, {"a"=>2}],
199
+ ]
177
200
 
178
- d.expect_emit "tag1", time, {"a"=>1}
179
- d.expect_emit "tag1", time, {"a"=>2}
201
+ d.expected_emits_length = records.length
202
+ d.run_timeout = 2
203
+ d.run do
204
+ records.each {|tag, _time, record|
205
+ send_data [tag, _time, record].to_json + "\n"
206
+ }
207
+ end
180
208
 
181
- d.run do
182
- entries = ''
183
- d.expected_emits.each {|_tag, _time, record|
184
- Fluent::Engine.msgpack_factory.packer(entries).write([_time, record]).flush
185
- }
186
- send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
209
+ assert_equal(records, d.emits.sort_by {|a| a[1] })
187
210
  end
188
211
  end
189
212
 
190
- def test_packed_forward_with_time_as_integer
191
- d = create_driver
192
-
193
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
194
-
195
- d.expect_emit "tag1", time, {"a"=>1}
196
- d.expect_emit "tag1", time, {"a"=>2}
213
+ class Forward < self
214
+ extend Fluent::Test::StartupShutdown
215
+
216
+ data(tcp: {
217
+ config: CONFIG,
218
+ options: {
219
+ auth: false
220
+ }
221
+ },
222
+ auth: {
223
+ config: CONFIG_AUTH,
224
+ options: {
225
+ auth: true
226
+ }
227
+ })
228
+ def test_plain(data)
229
+ config = data[:config]
230
+ options = data[:options]
231
+ d = create_driver(config)
232
+
233
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
234
+
235
+ records = [
236
+ ["tag1", time, {"a"=>1}],
237
+ ["tag1", time, {"a"=>2}]
238
+ ]
239
+
240
+ d.expected_emits_length = records.length
241
+ d.run_timeout = 2
242
+ d.run do
243
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
244
+
245
+ entries = []
246
+ records.each {|tag, _time, record|
247
+ entries << [_time, record]
248
+ }
249
+ send_data packer.write(["tag1", entries]).to_s, **options
250
+ end
251
+ assert_equal(records, d.emits)
197
252
 
198
- d.run do
199
- entries = ''
200
- d.expected_emits.each {|_tag, _time, record|
201
- Fluent::Engine.msgpack_factory.packer(entries).write([_time, record]).flush
202
- }
203
- send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
253
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
204
254
  end
205
- end
206
-
207
- def test_packed_forward_with_skip_invalid_event
208
- d = create_driver(CONFIG + "skip_invalid_event true")
209
-
210
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
211
255
 
212
- d.expect_emit "tag1", time, {"a" => 1}
213
- d.expect_emit "tag1", time, {"a" => 2}
256
+ data(tcp: {
257
+ config: CONFIG,
258
+ options: {
259
+ auth: false
260
+ }
261
+ },
262
+ auth: {
263
+ config: CONFIG_AUTH,
264
+ options: {
265
+ auth: true
266
+ }
267
+ })
268
+ def test_time_as_integer(data)
269
+ config = data[:config]
270
+ options = data[:options]
271
+ d = create_driver(config)
272
+
273
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
274
+
275
+ records = [
276
+ ["tag1", time, {"a"=>1}],
277
+ ["tag1", time, {"a"=>2}]
278
+ ]
279
+
280
+ d.expected_emits_length = records.length
281
+ d.run_timeout = 2
282
+ d.run do
283
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
284
+
285
+ entries = []
286
+ records.each {|tag, _time, record|
287
+ entries << [_time, record]
288
+ }
289
+ send_data packer.write(["tag1", entries]).to_s, **options
290
+ end
214
291
 
215
- d.run do
216
- entries = d.expected_emits.map {|_tag , _time, record| [_time, record] }
217
- # These entries are skipped
218
- entries << ['invalid time', {'a' => 3}] << [time, 'invalid record']
292
+ assert_equal(records, d.emits)
219
293
 
220
- packed_entries = ''
221
- entries.each {|_time, record|
222
- Fluent::Engine.msgpack_factory.packer(packed_entries).write([_time, record]).flush
223
- }
224
- send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", packed_entries]).to_s
294
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
225
295
  end
226
296
 
227
- assert_equal 2, d.instance.log.logs.count { |line| line =~ /skip invalid event/ }
228
- end
229
-
230
- def test_message_json
231
- d = create_driver
232
-
233
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
297
+ data(tcp: {
298
+ config: CONFIG,
299
+ options: {
300
+ auth: false
301
+ }
302
+ },
303
+ auth: {
304
+ config: CONFIG_AUTH,
305
+ options: {
306
+ auth: true
307
+ }
308
+ })
309
+ def test_skip_invalid_event(data)
310
+ config = data[:config]
311
+ options = data[:options]
312
+ d = create_driver(config + "skip_invalid_event true")
313
+
314
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
315
+
316
+ records = [
317
+ ["tag1", time, {"a" => 1}],
318
+ ["tag1", time, {"a" => 2}],
319
+ ]
320
+
321
+ d.expected_emits_length = records.length
322
+ d.run_timeout = 2
323
+ d.run do
324
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
325
+
326
+ entries = records.map { |tag, _time, record| [_time, record] }
327
+ # These entries are skipped
328
+ entries << ['invalid time', {'a' => 3}] << [time, 'invalid record']
329
+
330
+ send_data packer.write(["tag1", entries]).to_s, **options
331
+ end
234
332
 
235
- d.expect_emit "tag1", time, {"a"=>1}
236
- d.expect_emit "tag2", time, {"a"=>2}
333
+ assert_equal 2, d.instance.log.logs.count { |line| line =~ /skip invalid event/ }
237
334
 
238
- d.run do
239
- d.expected_emits.each {|tag, _time, record|
240
- send_data [tag, _time, record].to_json
241
- }
335
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
242
336
  end
243
337
  end
244
338
 
245
- def test_set_size_to_option
246
- d = create_driver
339
+ class PackedForward < self
340
+ extend Fluent::Test::StartupShutdown
341
+
342
+ data(tcp: {
343
+ config: CONFIG,
344
+ options: {
345
+ auth: false
346
+ }
347
+ },
348
+ auth: {
349
+ config: CONFIG_AUTH,
350
+ options: {
351
+ auth: true
352
+ }
353
+ })
354
+ def test_plain(data)
355
+ config = data[:config]
356
+ options = data[:options]
357
+ d = create_driver(config)
358
+
359
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
360
+
361
+ records = [
362
+ ["tag1", time, {"a"=>1}],
363
+ ["tag1", time, {"a"=>2}],
364
+ ]
365
+
366
+ d.expected_emits_length = records.length
367
+ d.run_timeout = 2
368
+ d.run do
369
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
370
+
371
+ entries = ''
372
+ records.each {|_tag, _time, record|
373
+ packer(entries).write([_time, record]).flush
374
+ }
375
+ send_data packer.write(["tag1", entries]).to_s, **options
376
+ end
377
+ assert_equal(records, d.emits)
247
378
 
248
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
249
- events = [
250
- ["tag1", time, {"a"=>1}],
251
- ["tag1", time, {"a"=>2}]
252
- ]
379
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
380
+ end
253
381
 
254
- entries = ''
255
- events.each {|_tag, _time, record|
256
- [_time, record].to_msgpack(entries)
257
- }
382
+ data(tcp: {
383
+ config: CONFIG,
384
+ options: {
385
+ auth: false
386
+ }
387
+ },
388
+ auth: {
389
+ config: CONFIG_AUTH,
390
+ options: {
391
+ auth: true
392
+ }
393
+ })
394
+ def test_time_as_integer(data)
395
+ config = data[:config]
396
+ options = data[:options]
397
+ d = create_driver(config)
398
+
399
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
400
+
401
+ records = [
402
+ ["tag1", time, {"a"=>1}],
403
+ ["tag1", time, {"a"=>2}],
404
+ ]
405
+
406
+ d.expected_emits_length = records.length
407
+ d.run_timeout = 2
408
+ d.run do
409
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
410
+
411
+ entries = ''
412
+ records.each {|tag, _time, record|
413
+ packer(entries).write([_time, record]).flush
414
+ }
415
+ send_data packer.write(["tag1", entries]).to_s, **options
416
+ end
417
+ assert_equal(records, d.emits)
258
418
 
259
- chunk = ["tag1", entries, { 'size' => events.length }].to_msgpack
419
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
420
+ end
260
421
 
261
- d.run do
262
- Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
263
- option = d.instance.send(:on_message, obj, chunk.size, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
264
- assert_equal option['size'], events.length
422
+ data(tcp: {
423
+ config: CONFIG,
424
+ options: {
425
+ auth: false
426
+ }
427
+ },
428
+ auth: {
429
+ config: CONFIG_AUTH,
430
+ options: {
431
+ auth: true
432
+ }
433
+ })
434
+ def test_skip_invalid_event(data)
435
+ config = data[:config]
436
+ options = data[:options]
437
+ d = create_driver(config + "skip_invalid_event true")
438
+
439
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
440
+
441
+ records = [
442
+ ["tag1", time, {"a" => 1}],
443
+ ["tag1", time, {"a" => 2}],
444
+ ]
445
+
446
+ d.expected_emits_length = records.length
447
+ d.run_timeout = 2
448
+ d.run do
449
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
450
+
451
+ entries = records.map { |tag, _time, record| [_time, record] }
452
+ # These entries are skipped
453
+ entries << ['invalid time', {'a' => 3}] << [time, 'invalid record']
454
+
455
+ packed_entries = ''
456
+ entries.each { |_time, record|
457
+ packer(packed_entries).write([_time, record]).flush
458
+ }
459
+ send_data packer.write(["tag1", packed_entries]).to_s, **options
265
460
  end
461
+
462
+ assert_equal 2, d.instance.log.logs.count { |line| line =~ /skip invalid event/ }
463
+
464
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
266
465
  end
267
466
  end
268
467
 
269
- def test_send_large_chunk_warning
270
- d = create_driver(CONFIG + %[
468
+ class Warning < self
469
+ extend Fluent::Test::StartupShutdown
470
+
471
+ def test_send_large_chunk_warning
472
+ d = create_driver(CONFIG + %[
271
473
  chunk_size_warn_limit 16M
272
474
  chunk_size_limit 32M
273
475
  ])
274
476
 
275
- time = Fluent::EventTime.parse("2014-04-25 13:14:15 UTC")
477
+ time = Fluent::EventTime.parse("2014-04-25 13:14:15 UTC")
276
478
 
277
- # generate over 16M chunk
278
- str = "X" * 1024 * 1024
279
- chunk = [ "test.tag", (0...16).map{|i| [time + i, {"data" => str}] } ].to_msgpack
280
- assert chunk.size > (16 * 1024 * 1024)
281
- assert chunk.size < (32 * 1024 * 1024)
479
+ # generate over 16M chunk
480
+ str = "X" * 1024 * 1024
481
+ chunk = [ "test.tag", (0...16).map{|i| [time + i, {"data" => str}] } ].to_msgpack
482
+ assert chunk.size > (16 * 1024 * 1024)
483
+ assert chunk.size < (32 * 1024 * 1024)
282
484
 
283
- d.run do
284
- Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
285
- d.instance.send(:on_message, obj, chunk.size, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
286
- end
287
- end
485
+ d.run do
486
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
288
487
 
289
- # check emitted data
290
- emits = d.emits
291
- assert_equal 16, emits.size
292
- assert emits.map(&:first).all?{|t| t == "test.tag" }
293
- assert_equal (0...16).to_a, emits.map{|_tag, t, _record| t - time }
488
+ Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
489
+ d.instance.send(:on_message, obj, chunk.size, PEERADDR)
490
+ end
491
+ end
294
492
 
295
- # check log
296
- assert d.instance.log.logs.select{|line|
297
- line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
298
- line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=16777216 size=16777501/
299
- }.size == 1, "large chunk warning is not logged"
300
- end
493
+ # check emitted data
494
+ emits = d.emits
495
+ assert_equal 16, emits.size
496
+ assert emits.map(&:first).all?{|t| t == "test.tag" }
497
+ assert_equal (0...16).to_a, emits.map{|_tag, t, _record| t - time }
498
+
499
+ # check log
500
+ assert d.instance.log.logs.select{|line|
501
+ line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
502
+ line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=16777216 size=16777501/
503
+ }.size == 1, "large chunk warning is not logged"
504
+ end
301
505
 
302
- def test_send_large_chunk_only_warning
303
- d = create_driver(CONFIG + %[
506
+ def test_send_large_chunk_only_warning
507
+ d = create_driver(CONFIG + %[
304
508
  chunk_size_warn_limit 16M
305
509
  ])
306
- time = Fluent::EventTime.parse("2014-04-25 13:14:15 UTC")
510
+ time = Fluent::EventTime.parse("2014-04-25 13:14:15 UTC")
307
511
 
308
- # generate over 16M chunk
309
- str = "X" * 1024 * 1024
310
- chunk = [ "test.tag", (0...16).map{|i| [time + i, {"data" => str}] } ].to_msgpack
512
+ # generate over 16M chunk
513
+ str = "X" * 1024 * 1024
514
+ chunk = [ "test.tag", (0...16).map{|i| [time + i, {"data" => str}] } ].to_msgpack
311
515
 
312
- d.run do
313
- Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
314
- d.instance.send(:on_message, obj, chunk.size, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
516
+ d.run do
517
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
518
+
519
+ Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
520
+ d.instance.send(:on_message, obj, chunk.size, PEERADDR)
521
+ end
315
522
  end
316
- end
317
523
 
318
- # check log
319
- assert d.instance.log.logs.select{ |line|
320
- line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
321
- line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=16777216 size=16777501/
322
- }.size == 1, "large chunk warning is not logged"
323
- end
524
+ # check log
525
+ assert d.instance.log.logs.select{ |line|
526
+ line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
527
+ line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=16777216 size=16777501/
528
+ }.size == 1, "large chunk warning is not logged"
529
+ end
324
530
 
325
- def test_send_large_chunk_limit
326
- d = create_driver(CONFIG + %[
531
+ def test_send_large_chunk_limit
532
+ d = create_driver(CONFIG + %[
327
533
  chunk_size_warn_limit 16M
328
534
  chunk_size_limit 32M
329
535
  ])
330
536
 
331
- time = Fluent::EventTime.parse("2014-04-25 13:14:15 UTC")
537
+ time = Fluent::EventTime.parse("2014-04-25 13:14:15 UTC")
332
538
 
333
- # generate over 32M chunk
334
- str = "X" * 1024 * 1024
335
- chunk = [ "test.tag", (0...32).map{|i| [time + i, {"data" => str}] } ].to_msgpack
336
- assert chunk.size > (32 * 1024 * 1024)
539
+ # generate over 32M chunk
540
+ str = "X" * 1024 * 1024
541
+ chunk = [ "test.tag", (0...32).map{|i| [time + i, {"data" => str}] } ].to_msgpack
542
+ assert chunk.size > (32 * 1024 * 1024)
337
543
 
338
- # d.run => send_data
339
- d.run do
340
- Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
341
- d.instance.send(:on_message, obj, chunk.size, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
342
- end
343
- end
344
-
345
- # check emitted data
346
- emits = d.emits
347
- assert_equal 0, emits.size
544
+ # d.run => send_data
545
+ d.run do
546
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
348
547
 
349
- # check log
350
- assert d.instance.log.logs.select{|line|
351
- line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_limit', dropped:/ &&
352
- line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=33554432 size=33554989/
353
- }.size == 1, "large chunk warning is not logged"
354
- end
548
+ Fluent::Engine.msgpack_factory.unpacker.feed_each(chunk) do |obj|
549
+ d.instance.send(:on_message, obj, chunk.size, PEERADDR)
550
+ end
551
+ end
355
552
 
356
- data('string chunk' => 'broken string',
357
- 'integer chunk' => 10)
358
- def test_send_broken_chunk(data)
359
- d = create_driver
553
+ # check emitted data
554
+ emits = d.emits
555
+ assert_equal 0, emits.size
360
556
 
361
- # d.run => send_data
362
- d.run do
363
- d.instance.send(:on_message, data, 1000000000, "host: 127.0.0.1, addr: 127.0.0.1, port: 0000")
557
+ # check log
558
+ assert d.instance.log.logs.select{|line|
559
+ line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_limit', dropped:/ &&
560
+ line =~ / tag="test.tag" source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" limit=33554432 size=33554989/
561
+ }.size == 1, "large chunk warning is not logged"
364
562
  end
365
563
 
366
- # check emitted data
367
- emits = d.emits
368
- assert_equal 0, emits.size
369
-
370
- # check log
371
- assert d.instance.log.logs.select{|line|
372
- line =~ / \[warn\]: incoming chunk is broken: source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" msg=#{data.inspect}/
373
- }.size == 1, "should not accept broken chunk"
374
- end
564
+ data('string chunk' => 'broken string',
565
+ 'integer chunk' => 10)
566
+ def test_send_broken_chunk(data)
567
+ d = create_driver
375
568
 
376
- def test_respond_to_message_requiring_ack
377
- d = create_driver
569
+ # d.run => send_data
570
+ d.run do
571
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
378
572
 
379
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
380
-
381
- events = [
382
- ["tag1", time, {"a"=>1}],
383
- ["tag2", time, {"a"=>2}]
384
- ]
385
- d.expected_emits_length = events.length
573
+ d.instance.send(:on_message, data, 1000000000, PEERADDR)
574
+ end
386
575
 
387
- expected_acks = []
576
+ # check emitted data
577
+ emits = d.emits
578
+ assert_equal 0, emits.size
388
579
 
389
- d.run do
390
- events.each {|tag, _time, record|
391
- op = { 'chunk' => Base64.encode64(record.object_id.to_s) }
392
- expected_acks << op['chunk']
393
- send_data [tag, _time, record, op].to_msgpack, true
394
- }
580
+ # check log
581
+ assert d.instance.log.logs.select{|line|
582
+ line =~ / \[warn\]: incoming chunk is broken: source="host: 127.0.0.1, addr: 127.0.0.1, port: \d+" msg=#{data.inspect}/
583
+ }.size == 1, "should not accept broken chunk"
395
584
  end
396
-
397
- assert_equal events, d.emits
398
- assert_equal expected_acks, @responses.map { |res| MessagePack.unpack(res)['ack'] }
399
585
  end
400
586
 
401
- # FIX: response is not pushed into @responses because IO.select has been blocked until InputForward shutdowns
402
- def test_respond_to_forward_requiring_ack
403
- d = create_driver
587
+ class RespondToRequiringAck < self
588
+ extend Fluent::Test::StartupShutdown
589
+
590
+ data(tcp: {
591
+ config: CONFIG,
592
+ options: {
593
+ auth: false
594
+ }
595
+ },
596
+ auth: {
597
+ config: CONFIG_AUTH,
598
+ options: {
599
+ auth: true
600
+ }
601
+ })
602
+ def test_message(data)
603
+ config = data[:config]
604
+ options = data[:options]
605
+ d = create_driver(config)
606
+
607
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
608
+
609
+ events = [
610
+ ["tag1", time, {"a"=>1}],
611
+ ["tag2", time, {"a"=>2}]
612
+ ]
613
+ d.expected_emits_length = events.length
614
+
615
+ expected_acks = []
616
+
617
+ d.run do
618
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
619
+
620
+ events.each {|tag, _time, record|
621
+ op = { 'chunk' => Base64.encode64(record.object_id.to_s) }
622
+ expected_acks << op['chunk']
623
+ send_data [tag, _time, record, op].to_msgpack, try_to_receive_response: true, **options
624
+ }
625
+ end
404
626
 
405
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
627
+ assert_equal events, d.emits
628
+ assert_equal expected_acks, @responses.map { |res| MessagePack.unpack(res)['ack'] }
406
629
 
407
- events = [
408
- ["tag1", time, {"a"=>1}],
409
- ["tag1", time, {"a"=>2}]
410
- ]
411
- d.expected_emits_length = events.length
630
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
631
+ end
632
+
633
+ # FIX: response is not pushed into @responses because IO.select has been blocked until InputForward shutdowns
634
+ data(tcp: {
635
+ config: CONFIG,
636
+ options: {
637
+ auth: false
638
+ }
639
+ },
640
+ auth: {
641
+ config: CONFIG_AUTH,
642
+ options: {
643
+ auth: true
644
+ }
645
+ })
646
+ def test_forward(data)
647
+ config = data[:config]
648
+ options = data[:options]
649
+ d = create_driver(config)
650
+
651
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
652
+
653
+ events = [
654
+ ["tag1", time, {"a"=>1}],
655
+ ["tag1", time, {"a"=>2}]
656
+ ]
657
+ d.expected_emits_length = events.length
658
+
659
+ expected_acks = []
660
+
661
+ d.run do
662
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
663
+
664
+ entries = []
665
+ events.each {|_tag, _time, record|
666
+ entries << [_time, record]
667
+ }
668
+ op = { 'chunk' => Base64.encode64(entries.object_id.to_s) }
669
+ expected_acks << op['chunk']
670
+ send_data ["tag1", entries, op].to_msgpack, try_to_receive_response: true, **options
671
+ end
412
672
 
413
- expected_acks = []
673
+ assert_equal events, d.emits
674
+ assert_equal expected_acks, @responses.map { |res| MessagePack.unpack(res)['ack'] }
414
675
 
415
- d.run do
416
- entries = []
417
- events.each {|_tag, _time, record|
418
- entries << [time, record]
419
- }
420
- op = { 'chunk' => Base64.encode64(entries.object_id.to_s) }
421
- expected_acks << op['chunk']
422
- send_data ["tag1", entries, op].to_msgpack, true
676
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
423
677
  end
424
678
 
425
- assert_equal events, d.emits
426
- assert_equal expected_acks, @responses.map { |res| MessagePack.unpack(res)['ack'] }
427
- end
679
+ data(tcp: {
680
+ config: CONFIG,
681
+ options: {
682
+ auth: false
683
+ }
684
+ },
685
+ auth: {
686
+ config: CONFIG_AUTH,
687
+ options: {
688
+ auth: true
689
+ }
690
+ })
691
+ def test_packed_forward
692
+ config = data[:config]
693
+ options = data[:options]
694
+ d = create_driver(config)
695
+
696
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
697
+
698
+ events = [
699
+ ["tag1", time, {"a"=>1}],
700
+ ["tag1", time, {"a"=>2}]
701
+ ]
702
+ d.expected_emits_length = events.length
703
+
704
+ expected_acks = []
705
+
706
+ d.run do
707
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
708
+
709
+ entries = ''
710
+ events.each {|_tag, _time,record|
711
+ [time, record].to_msgpack(entries)
712
+ }
713
+ op = { 'chunk' => Base64.encode64(entries.object_id.to_s) }
714
+ expected_acks << op['chunk']
715
+ send_data ["tag1", entries, op].to_msgpack, try_to_receive_response: true, **options
716
+ end
428
717
 
429
- def test_respond_to_packed_forward_requiring_ack
430
- d = create_driver
718
+ assert_equal events, d.emits
719
+ assert_equal expected_acks, @responses.map { |res| MessagePack.unpack(res)['ack'] }
431
720
 
432
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
721
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
722
+ end
433
723
 
434
- events = [
435
- ["tag1", time, {"a"=>1}],
436
- ["tag1", time, {"a"=>2}]
437
- ]
438
- d.expected_emits_length = events.length
724
+ data(tcp: {
725
+ config: CONFIG,
726
+ options: {
727
+ auth: false
728
+ }
729
+ },
730
+ auth: {
731
+ config: CONFIG_AUTH,
732
+ options: {
733
+ auth: true
734
+ }
735
+ })
736
+ def test_message_json(data)
737
+ config = data[:config]
738
+ options = data[:options]
739
+ omit "with json, auth doen NOT work" if options[:auth]
740
+ d = create_driver(config)
741
+
742
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
743
+
744
+ events = [
745
+ ["tag1", time, {"a"=>1}],
746
+ ["tag2", time, {"a"=>2}]
747
+ ]
748
+ d.expected_emits_length = events.length
749
+
750
+ expected_acks = []
751
+
752
+ d.expected_emits_length = events.length
753
+ d.run_timeout = 2
754
+ d.run do
755
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
756
+
757
+ events.each {|tag, _time, record|
758
+ op = { 'chunk' => Base64.encode64(record.object_id.to_s) }
759
+ expected_acks << op['chunk']
760
+ send_data [tag, _time, record, op].to_json, try_to_receive_response: true, **options
761
+ }
762
+ end
439
763
 
440
- expected_acks = []
764
+ assert_equal events, d.emits
765
+ assert_equal expected_acks, @responses.map { |res| JSON.parse(res)['ack'] }
441
766
 
442
- d.run do
443
- entries = ''
444
- events.each {|_tag, _time, record|
445
- [_time, record].to_msgpack(entries)
446
- }
447
- op = { 'chunk' => Base64.encode64(entries.object_id.to_s) }
448
- expected_acks << op['chunk']
449
- send_data ["tag1", entries, op].to_msgpack, true
767
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
450
768
  end
451
-
452
- assert_equal events, d.emits
453
- assert_equal expected_acks, @responses.map { |res| MessagePack.unpack(res)['ack'] }
454
769
  end
455
770
 
456
- def test_respond_to_message_json_requiring_ack
457
- d = create_driver
771
+ class NotRespondToNotRequiringAck < self
772
+ extend Fluent::Test::StartupShutdown
773
+
774
+ data(tcp: {
775
+ config: CONFIG,
776
+ options: {
777
+ auth: false
778
+ }
779
+ },
780
+ auth: {
781
+ config: CONFIG_AUTH,
782
+ options: {
783
+ auth: true
784
+ }
785
+ })
786
+ def test_message
787
+ config = data[:config]
788
+ options = data[:options]
789
+ d = create_driver(config)
790
+
791
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
792
+
793
+ events = [
794
+ ["tag1", time, {"a"=>1}],
795
+ ["tag2", time, {"a"=>2}]
796
+ ]
797
+ d.expected_emits_length = events.length
798
+
799
+ d.run do
800
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
801
+
802
+ events.each {|tag, _time, record|
803
+ send_data [tag, _time, record].to_msgpack, try_to_receive_response: true, **options
804
+ }
805
+ end
458
806
 
459
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
807
+ assert_equal events, d.emits
808
+ assert_equal ["", ""], @responses
460
809
 
461
- events = [
462
- ["tag1", time, {"a"=>1}],
463
- ["tag2", time, {"a"=>2}]
464
- ]
465
- d.expected_emits_length = events.length
810
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
811
+ end
466
812
 
467
- expected_acks = []
813
+ data(tcp: {
814
+ config: CONFIG,
815
+ options: {
816
+ auth: false
817
+ }
818
+ },
819
+ auth: {
820
+ config: CONFIG_AUTH,
821
+ options: {
822
+ auth: true
823
+ }
824
+ })
825
+ def test_forward(data)
826
+ config = data[:config]
827
+ options = data[:options]
828
+ d = create_driver(config)
829
+
830
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
831
+
832
+ events = [
833
+ ["tag1", time, {"a"=>1}],
834
+ ["tag1", time, {"a"=>2}]
835
+ ]
836
+ d.expected_emits_length = events.length
837
+
838
+ d.run do
839
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
840
+
841
+ entries = []
842
+ events.each {|tag, _time, record|
843
+ entries << [_time, record]
844
+ }
845
+ send_data ["tag1", entries].to_msgpack, try_to_receive_response: true, **options
846
+ end
468
847
 
469
- d.run do
470
- events.each {|tag, _time, record|
471
- op = { 'chunk' => Base64.encode64(record.object_id.to_s) }
472
- expected_acks << op['chunk']
473
- send_data [tag, _time, record, op].to_json, true
474
- }
848
+ assert_equal events, d.emits
849
+ assert_equal [nil], @responses
850
+
851
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
475
852
  end
476
853
 
477
- assert_equal events, d.emits
478
- assert_equal expected_acks, @responses.map { |res| JSON.parse(res)['ack'] }
854
+ data(tcp: {
855
+ config: CONFIG,
856
+ options: {
857
+ auth: false
858
+ }
859
+ },
860
+ auth: {
861
+ config: CONFIG_AUTH,
862
+ options: {
863
+ auth: true
864
+ }
865
+ })
866
+ def test_packed_forward(data)
867
+ config = data[:config]
868
+ options = data[:options]
869
+ d = create_driver(config)
870
+
871
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
872
+
873
+ events = [
874
+ ["tag1", time, {"a"=>1}],
875
+ ["tag1", time, {"a"=>2}]
876
+ ]
877
+ d.expected_emits_length = events.length
878
+
879
+ d.run do
880
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
881
+
882
+ entries = ''
883
+ events.each {|tag, _time, record|
884
+ [_time, record].to_msgpack(entries)
885
+ }
886
+ send_data ["tag1", entries].to_msgpack, try_to_receive_response: true, **options
887
+ end
479
888
 
480
- end
889
+ assert_equal events, d.emits
890
+ assert_equal [nil], @responses
481
891
 
482
- def test_not_respond_to_message_not_requiring_ack
483
- d = create_driver
892
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
893
+ end
484
894
 
485
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
895
+ data(tcp: {
896
+ config: CONFIG,
897
+ options: {
898
+ auth: false
899
+ }
900
+ },
901
+ auth: {
902
+ config: CONFIG_AUTH,
903
+ options: {
904
+ auth: true
905
+ }
906
+ })
907
+ def test_message_json(data)
908
+ config = data[:config]
909
+ options = data[:options]
910
+ omit "with json, auth doen NOT work" if options[:auth]
911
+ d = create_driver(config)
912
+
913
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
914
+
915
+ events = [
916
+ ["tag1", time, {"a"=>1}],
917
+ ["tag2", time, {"a"=>2}]
918
+ ]
919
+ d.expected_emits_length = events.length
920
+
921
+ d.run do
922
+ sleep 0.1 until d.instance.instance_eval{ @thread } && d.instance.instance_eval{ @thread }.status
923
+
924
+ events.each {|tag, _time, record|
925
+ send_data [tag, _time, record].to_json, try_to_receive_response: true, **options
926
+ }
927
+ end
486
928
 
487
- events = [
488
- ["tag1", time, {"a"=>1}],
489
- ["tag2", time, {"a"=>2}]
490
- ]
491
- d.expected_emits_length = events.length
929
+ assert_equal events, d.emits
930
+ assert_equal [nil, nil], @responses
492
931
 
493
- d.run do
494
- events.each {|tag, _time, record|
495
- send_data [tag, _time, record].to_msgpack, true
496
- }
932
+ sleep 0.1 while d.instance.instance_eval{ @thread }.status # to confirm that plugin stopped completely
497
933
  end
934
+ end
498
935
 
499
- assert_equal events, d.emits
500
- assert_equal [nil, nil], @responses
936
+ def packer(*args)
937
+ Fluent::Engine.msgpack_factory.packer(*args)
501
938
  end
502
939
 
503
- def test_not_respond_to_forward_not_requiring_ack
504
- d = create_driver
940
+ def unpacker
941
+ Fluent::Engine.msgpack_factory.unpacker
942
+ end
505
943
 
506
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
944
+ # res
945
+ # '' : socket is disconnected without any data
946
+ # nil: socket read timeout
947
+ def read_data(io, timeout, &block)
948
+ res = ''
949
+ select_timeout = 2
950
+ timeout_at = Time.now + timeout
951
+ begin
952
+ buf = ''
953
+ io_activated = false
954
+ while Time.now < timeout_at
955
+ if IO.select([io], nil, nil, select_timeout)
956
+ io_activated = true
957
+ buf = io.readpartial(2048)
958
+ res ||= ''
959
+ res << buf
960
+
961
+ break if block.call(res)
962
+ end
963
+ end
964
+ res = nil unless io_activated # timeout without no data arrival
965
+ rescue Errno::EAGAIN
966
+ sleep 0.01
967
+ retry if res == ''
968
+ # if res is not empty, all data in socket buffer are read, so do not retry
969
+ rescue IOError, EOFError, Errno::ECONNRESET
970
+ # socket disconnected
971
+ end
972
+ res
973
+ end
507
974
 
508
- events = [
509
- ["tag1", time, {"a"=>1}],
510
- ["tag1", time, {"a"=>2}]
975
+ def simulate_auth_sequence(io, shared_key=SHARED_KEY, username=USER_NAME, password=USER_PASSWORD)
976
+ auth_response_timeout = 30
977
+ shared_key_salt = 'salt'
978
+
979
+ # reading helo
980
+ helo_data = read_data(io, auth_response_timeout){|data| MessagePack.unpack(data) rescue nil }
981
+ raise "Authentication packet timeout" unless helo_data
982
+ raise "Authentication connection closed" if helo_data == ''
983
+ # ['HELO', options(hash)]
984
+ helo = MessagePack.unpack(helo_data)
985
+ raise "Invalid HELO header" unless helo[0] == 'HELO'
986
+ raise "Invalid HELO option object" unless helo[1].is_a?(Hash)
987
+ @options = helo[1]
988
+
989
+ # sending ping
990
+ ping = [
991
+ 'PING',
992
+ 'selfhostname',
993
+ shared_key_salt,
994
+ Digest::SHA512.new
995
+ .update(shared_key_salt)
996
+ .update('selfhostname')
997
+ .update(@options['nonce'])
998
+ .update(shared_key).hexdigest,
511
999
  ]
512
- d.expected_emits_length = events.length
1000
+ if @options['auth'] # auth enabled -> value is auth salt
1001
+ pass_digest = Digest::SHA512.new.update(@options['auth']).update(username).update(password).hexdigest
1002
+ ping.push(username, pass_digest)
1003
+ else
1004
+ ping.push('', '')
1005
+ end
1006
+ io.write ping.to_msgpack
1007
+ io.flush
1008
+
1009
+ # reading pong
1010
+ pong_data = read_data(io, auth_response_timeout){|data| MessagePack.unpack(data) rescue nil }
1011
+ raise "PONG packet timeout" unless pong_data
1012
+ raise "PONG connection closed" if pong_data == ''
1013
+ # ['PING', bool(auth_result), string(reason_if_failed), self_hostname, shared_key_digest]
1014
+ pong = MessagePack.unpack(pong_data)
1015
+ raise "Invalid PONG header" unless pong[0] == 'PONG'
1016
+ raise "Authentication Failure: #{pong[2]}" unless pong[1]
1017
+ clientside_calculated = Digest::SHA512.new
1018
+ .update(shared_key_salt)
1019
+ .update(pong[3])
1020
+ .update(@options['nonce'])
1021
+ .update(shared_key).hexdigest
1022
+ raise "Shared key digest mismatch" unless clientside_calculated == pong[4]
1023
+
1024
+ # authentication success
1025
+ true
1026
+ end
513
1027
 
514
- d.run do
515
- entries = []
516
- events.each {|_tag, _time, record|
517
- entries << [_time, record]
518
- }
519
- send_data ["tag1", entries].to_msgpack, true
1028
+ # Data ordering is not assured:
1029
+ # Records in different sockets are processed on different thread, so its scheduling make effect
1030
+ # on order of emitted records.
1031
+ # So, we MUST sort emitted records in different `send_data` before assertion.
1032
+ def send_data(data, try_to_receive_response: false, response_timeout: 5, auth: false)
1033
+ io = connect
1034
+
1035
+ if auth
1036
+ simulate_auth_sequence(io)
520
1037
  end
521
1038
 
522
- assert_equal events, d.emits
523
- assert_equal [nil], @responses
1039
+ io.write data
1040
+ io.flush
1041
+ if try_to_receive_response
1042
+ @responses << read_data(io, response_timeout){|d| MessagePack.unpack(d) rescue nil }
1043
+ end
1044
+ ensure
1045
+ io.close rescue nil # SSL socket requires any writes to close sockets
524
1046
  end
525
1047
 
526
- def test_not_respond_to_packed_forward_not_requiring_ack
527
- d = create_driver
528
-
529
- time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
1048
+ sub_test_case 'source_hostname_key feature' do
1049
+ extend Fluent::Test::StartupShutdown
530
1050
 
531
- events = [
532
- ["tag1", time, {"a"=>1}],
533
- ["tag1", time, {"a"=>2}]
534
- ]
535
- d.expected_emits_length = events.length
1051
+ test 'message protocol with source_hostname_key' do
1052
+ execute_test { |events|
1053
+ events.each { |tag, time, record|
1054
+ send_data [tag, time, record].to_msgpack
1055
+ }
1056
+ }
1057
+ end
536
1058
 
537
- d.run do
538
- entries = ''
539
- events.each {|_tag, _time, record|
540
- [_time, record].to_msgpack(entries)
1059
+ test 'forward protocol with source_hostname_key' do
1060
+ execute_test { |events|
1061
+ entries = []
1062
+ events.each {|tag,time,record|
1063
+ entries << [time, record]
1064
+ }
1065
+ send_data ['tag1', entries].to_msgpack
541
1066
  }
542
- send_data ["tag1", entries].to_msgpack, true
543
1067
  end
544
1068
 
545
- assert_equal events, d.emits
546
- assert_equal [nil], @responses
1069
+ test 'packed forward protocol with source_hostname_key' do
1070
+ execute_test { |events|
1071
+ entries = ''
1072
+ events.each { |tag, time, record|
1073
+ Fluent::Engine.msgpack_factory.packer(entries).write([time, record]).flush
1074
+ }
1075
+ send_data Fluent::Engine.msgpack_factory.packer.write(["tag1", entries]).to_s
1076
+ }
1077
+ end
547
1078
  end
548
1079
 
549
- def test_not_respond_to_message_json_not_requiring_ack
550
- d = create_driver
551
-
552
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
1080
+ def execute_test(&block)
1081
+ d = create_driver(CONFIG + 'source_hostname_key source')
553
1082
 
1083
+ time = Fluent::EventTime.parse("2011-01-02 13:14:15 UTC")
554
1084
  events = [
555
1085
  ["tag1", time, {"a"=>1}],
556
- ["tag2", time, {"a"=>2}]
1086
+ ["tag1", time, {"a"=>2}]
557
1087
  ]
558
1088
  d.expected_emits_length = events.length
559
1089
 
560
1090
  d.run do
561
- events.each {|tag, _time, record|
562
- send_data [tag, _time, record].to_json, true
563
- }
1091
+ block.call(events)
564
1092
  end
565
1093
 
566
- assert_equal events, d.emits
567
- assert_equal [nil, nil], @responses
568
- end
569
-
570
- def send_data(data, try_to_receive_response=false, response_timeout=1)
571
- io = connect
572
- begin
573
- io.write data
574
- if try_to_receive_response
575
- if IO.select([io], nil, nil, response_timeout)
576
- res = io.recv(1024)
577
- end
578
- # timeout means no response, so push nil to @responses
579
- end
580
- ensure
581
- io.close
582
- end
583
- @responses << res if try_to_receive_response
1094
+ d.emits.each { |tag, _time, record|
1095
+ assert_true record.has_key?('source')
1096
+ }
584
1097
  end
585
1098
 
586
1099
  # TODO heartbeat