fluentd 1.12.1-x86-mingw32 → 1.13.1-x86-mingw32

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 (87) 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 +46 -0
  7. data/.gitlab-ci.yml +19 -19
  8. data/CHANGELOG.md +163 -0
  9. data/CONTRIBUTING.md +2 -2
  10. data/MAINTAINERS.md +5 -2
  11. data/README.md +6 -3
  12. data/example/counter.conf +1 -1
  13. data/fluentd.gemspec +4 -2
  14. data/lib/fluent/command/bundler_injection.rb +1 -1
  15. data/lib/fluent/command/cat.rb +19 -4
  16. data/lib/fluent/command/fluentd.rb +1 -2
  17. data/lib/fluent/command/plugin_config_formatter.rb +2 -1
  18. data/lib/fluent/command/plugin_generator.rb +31 -1
  19. data/lib/fluent/compat/parser.rb +2 -2
  20. data/lib/fluent/config/section.rb +6 -1
  21. data/lib/fluent/config/types.rb +2 -2
  22. data/lib/fluent/event.rb +3 -13
  23. data/lib/fluent/load.rb +0 -1
  24. data/lib/fluent/log.rb +1 -0
  25. data/lib/fluent/plugin/file_wrapper.rb +49 -4
  26. data/lib/fluent/plugin/formatter_ltsv.rb +2 -2
  27. data/lib/fluent/plugin/in_http.rb +11 -1
  28. data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
  29. data/lib/fluent/plugin/in_tail.rb +140 -41
  30. data/lib/fluent/plugin/in_tail/position_file.rb +15 -1
  31. data/lib/fluent/plugin/out_copy.rb +18 -5
  32. data/lib/fluent/plugin/out_exec_filter.rb +3 -3
  33. data/lib/fluent/plugin/out_forward.rb +75 -61
  34. data/lib/fluent/plugin/output.rb +11 -9
  35. data/lib/fluent/plugin/parser_csv.rb +2 -2
  36. data/lib/fluent/plugin/parser_syslog.rb +2 -2
  37. data/lib/fluent/plugin/service_discovery.rb +0 -15
  38. data/lib/fluent/plugin/storage_local.rb +4 -4
  39. data/lib/fluent/plugin_helper/http_server/router.rb +1 -1
  40. data/lib/fluent/plugin_helper/server.rb +4 -2
  41. data/lib/fluent/plugin_helper/service_discovery.rb +39 -1
  42. data/lib/fluent/plugin_helper/service_discovery/manager.rb +11 -5
  43. data/lib/fluent/plugin_helper/socket_option.rb +2 -2
  44. data/lib/fluent/supervisor.rb +16 -1
  45. data/lib/fluent/system_config.rb +14 -0
  46. data/lib/fluent/time.rb +57 -1
  47. data/lib/fluent/version.rb +1 -1
  48. data/templates/new_gem/fluent-plugin.gemspec.erb +3 -3
  49. data/test/command/test_cat.rb +99 -0
  50. data/test/command/test_fluentd.rb +8 -0
  51. data/test/config/test_configurable.rb +1 -1
  52. data/test/config/test_section.rb +9 -0
  53. data/test/config/test_system_config.rb +46 -0
  54. data/test/plugin/in_tail/test_io_handler.rb +4 -4
  55. data/test/plugin/in_tail/test_position_file.rb +58 -4
  56. data/test/plugin/test_file_wrapper.rb +115 -0
  57. data/test/plugin/test_in_exec.rb +1 -1
  58. data/test/plugin/test_in_forward.rb +59 -83
  59. data/test/plugin/test_in_http.rb +58 -40
  60. data/test/plugin/test_in_syslog.rb +66 -56
  61. data/test/plugin/test_in_tail.rb +298 -26
  62. data/test/plugin/test_in_tcp.rb +45 -32
  63. data/test/plugin/test_in_udp.rb +47 -33
  64. data/test/plugin/test_out_copy.rb +87 -0
  65. data/test/plugin/test_out_forward.rb +198 -91
  66. data/test/plugin/test_out_http.rb +1 -1
  67. data/test/plugin/test_out_stream.rb +18 -8
  68. data/test/plugin/test_output.rb +15 -3
  69. data/test/plugin/test_output_as_buffered_backup.rb +2 -0
  70. data/test/plugin/test_parser_csv.rb +14 -0
  71. data/test/plugin/test_parser_syslog.rb +14 -0
  72. data/test/plugin_helper/http_server/test_route.rb +1 -1
  73. data/test/plugin_helper/service_discovery/test_manager.rb +1 -1
  74. data/test/plugin_helper/test_child_process.rb +6 -3
  75. data/test/plugin_helper/test_http_server_helper.rb +34 -27
  76. data/test/plugin_helper/test_server.rb +144 -139
  77. data/test/plugin_helper/test_service_discovery.rb +74 -14
  78. data/test/plugin_helper/test_socket.rb +16 -9
  79. data/test/test_config.rb +2 -1
  80. data/test/test_event.rb +16 -0
  81. data/test/test_formatter.rb +30 -0
  82. data/test/test_output.rb +2 -2
  83. data/test/test_supervisor.rb +35 -0
  84. data/test/test_time_parser.rb +109 -0
  85. metadata +55 -11
  86. data/.travis.yml +0 -77
  87. data/appveyor.yml +0 -31
@@ -5,21 +5,33 @@ require 'fluent/plugin/in_udp'
5
5
  class UdpInputTest < Test::Unit::TestCase
6
6
  def setup
7
7
  Fluent::Test.setup
8
+ @port = unused_port
8
9
  end
9
10
 
10
- PORT = unused_port
11
- BASE_CONFIG = %[
12
- port #{PORT}
13
- tag udp
14
- ]
15
- CONFIG = BASE_CONFIG + %!
16
- bind 127.0.0.1
17
- format /^\\[(?<time>[^\\]]*)\\] (?<message>.*)/
18
- !
19
- IPv6_CONFIG = BASE_CONFIG + %!
20
- bind ::1
21
- format /^\\[(?<time>[^\\]]*)\\] (?<message>.*)/
22
- !
11
+ def teardown
12
+ @port = nil
13
+ end
14
+
15
+ def base_config
16
+ %[
17
+ port #{@port}
18
+ tag udp
19
+ ]
20
+ end
21
+
22
+ def ipv4_config
23
+ base_config + %!
24
+ bind 127.0.0.1
25
+ format /^\\[(?<time>[^\\]]*)\\] (?<message>.*)/
26
+ !
27
+ end
28
+
29
+ def ipv6_config
30
+ base_config + %!
31
+ bind ::1
32
+ format /^\\[(?<time>[^\\]]*)\\] (?<message>.*)/
33
+ !
34
+ end
23
35
 
24
36
  def create_driver(conf)
25
37
  Fluent::Test::Driver::Input.new(Fluent::Plugin::UdpInput).configure(conf)
@@ -45,15 +57,16 @@ class UdpInputTest < Test::Unit::TestCase
45
57
  end
46
58
 
47
59
  data(
48
- 'ipv4' => [CONFIG, '127.0.0.1', :ipv4],
49
- 'ipv6' => [IPv6_CONFIG, '::1', :ipv6],
60
+ 'ipv4' => ['127.0.0.1', :ipv4],
61
+ 'ipv6' => ['::1', :ipv6],
50
62
  )
51
63
  test 'configure' do |data|
52
- conf, bind, protocol = data
64
+ bind, protocol = data
65
+ conf = send("#{protocol}_config")
53
66
  omit "IPv6 is not supported on this environment" if protocol == :ipv6 && !ipv6_enabled?
54
67
 
55
68
  d = create_driver(conf)
56
- assert_equal PORT, d.instance.port
69
+ assert_equal @port, d.instance.port
57
70
  assert_equal bind, d.instance.bind
58
71
  assert_equal 4096, d.instance.message_length_limit
59
72
  assert_equal nil, d.instance.receive_buffer_size
@@ -61,16 +74,17 @@ class UdpInputTest < Test::Unit::TestCase
61
74
 
62
75
  test ' configure w/o parse section' do
63
76
  assert_raise(Fluent::ConfigError.new("<parse> section is required.")) {
64
- create_driver(BASE_CONFIG)
77
+ create_driver(base_config)
65
78
  }
66
79
  end
67
80
 
68
81
  data(
69
- 'ipv4' => [CONFIG, '127.0.0.1', :ipv4],
70
- 'ipv6' => [IPv6_CONFIG, '::1', :ipv6],
82
+ 'ipv4' => ['127.0.0.1', :ipv4],
83
+ 'ipv6' => ['::1', :ipv6],
71
84
  )
72
85
  test 'time_format' do |data|
73
- conf, bind, protocol = data
86
+ bind, protocol = data
87
+ conf = send("#{protocol}_config")
74
88
  omit "IPv6 is not supported on this environment" if protocol == :ipv6 && !ipv6_enabled?
75
89
 
76
90
  d = create_driver(conf)
@@ -81,7 +95,7 @@ class UdpInputTest < Test::Unit::TestCase
81
95
  ]
82
96
 
83
97
  d.run(expect_records: 2) do
84
- create_udp_socket(bind, PORT) do |u|
98
+ create_udp_socket(bind, @port) do |u|
85
99
  tests.each do |test|
86
100
  u.send(test['msg'], 0)
87
101
  end
@@ -100,7 +114,7 @@ class UdpInputTest < Test::Unit::TestCase
100
114
  )
101
115
  test 'message_length_limit/body_size_limit compatibility' do |param|
102
116
 
103
- d = create_driver(CONFIG + param)
117
+ d = create_driver(ipv4_config + param)
104
118
  assert_equal 2048, d.instance.message_length_limit
105
119
  end
106
120
 
@@ -141,9 +155,9 @@ class UdpInputTest < Test::Unit::TestCase
141
155
  payloads = data['payloads']
142
156
  expecteds = data['expecteds']
143
157
 
144
- d = create_driver(BASE_CONFIG + "format #{format}")
158
+ d = create_driver(base_config + "format #{format}")
145
159
  d.run(expect_records: 2) do
146
- create_udp_socket('127.0.0.1', PORT) do |u|
160
+ create_udp_socket('127.0.0.1', @port) do |u|
147
161
  payloads.each do |payload|
148
162
  u.send(payload, 0)
149
163
  end
@@ -159,13 +173,13 @@ class UdpInputTest < Test::Unit::TestCase
159
173
  end
160
174
 
161
175
  test 'remove_newline' do
162
- d = create_driver(BASE_CONFIG + %!
176
+ d = create_driver(base_config + %!
163
177
  format none
164
178
  remove_newline false
165
179
  !)
166
180
  payloads = ["test1\n", "test2\n"]
167
181
  d.run(expect_records: 2) do
168
- create_udp_socket('127.0.0.1', PORT) do |u|
182
+ create_udp_socket('127.0.0.1', @port) do |u|
169
183
  payloads.each do |payload|
170
184
  u.send(payload, 0)
171
185
  end
@@ -182,13 +196,13 @@ class UdpInputTest < Test::Unit::TestCase
182
196
  end
183
197
 
184
198
  test 'source_hostname_key' do
185
- d = create_driver(BASE_CONFIG + %!
199
+ d = create_driver(base_config + %!
186
200
  format none
187
201
  source_hostname_key host
188
202
  !)
189
203
  hostname = nil
190
204
  d.run(expect_records: 1) do
191
- create_udp_socket('127.0.0.1', PORT) do |u|
205
+ create_udp_socket('127.0.0.1', @port) do |u|
192
206
  u.send("test", 0)
193
207
  hostname = u.peeraddr[2]
194
208
  end
@@ -201,13 +215,13 @@ class UdpInputTest < Test::Unit::TestCase
201
215
  end
202
216
 
203
217
  test 'source_address_key' do
204
- d = create_driver(BASE_CONFIG + %!
218
+ d = create_driver(base_config + %!
205
219
  format none
206
220
  source_address_key addr
207
221
  !)
208
222
  address = nil
209
223
  d.run(expect_records: 1) do
210
- create_udp_socket('127.0.0.1', PORT) do |u|
224
+ create_udp_socket('127.0.0.1', @port) do |u|
211
225
  u.send("test", 0)
212
226
  address = u.peeraddr[3]
213
227
  end
@@ -223,7 +237,7 @@ class UdpInputTest < Test::Unit::TestCase
223
237
  # doesn't check exact value because it depends on platform and condition
224
238
 
225
239
  # check if default socket and in_udp's one without receive_buffer_size have same size buffer
226
- d0 = create_driver(BASE_CONFIG + %!
240
+ d0 = create_driver(base_config + %!
227
241
  format none
228
242
  !)
229
243
  d0.run do
@@ -237,7 +251,7 @@ class UdpInputTest < Test::Unit::TestCase
237
251
  end
238
252
 
239
253
  # check if default socket and in_udp's one with receive_buffer_size have different size buffer
240
- d1 = create_driver(BASE_CONFIG + %!
254
+ d1 = create_driver(base_config + %!
241
255
  format none
242
256
  receive_buffer_size 1001
243
257
  !)
@@ -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
 
@@ -12,32 +12,38 @@ class ForwardOutputTest < Test::Unit::TestCase
12
12
  FileUtils.rm_rf(TMP_DIR)
13
13
  FileUtils.mkdir_p(TMP_DIR)
14
14
  @d = nil
15
+ @target_port = unused_port
15
16
  end
16
17
 
17
18
  def teardown
18
19
  @d.instance_shutdown if @d
20
+ @port = nil
19
21
  end
20
22
 
21
23
  TMP_DIR = File.join(__dir__, "../tmp/out_forward#{ENV['TEST_ENV_NUMBER']}")
22
24
 
23
25
  TARGET_HOST = '127.0.0.1'
24
- TARGET_PORT = unused_port
25
- CONFIG = %[
26
- send_timeout 51
27
- heartbeat_type udp
28
- <server>
29
- name test
30
- host #{TARGET_HOST}
31
- port #{TARGET_PORT}
32
- </server>
33
- ]
34
26
 
35
- TARGET_CONFIG = %[
36
- port #{TARGET_PORT}
37
- bind #{TARGET_HOST}
38
- ]
27
+ def config
28
+ %[
29
+ send_timeout 51
30
+ heartbeat_type udp
31
+ <server>
32
+ name test
33
+ host #{TARGET_HOST}
34
+ port #{@target_port}
35
+ </server>
36
+ ]
37
+ end
39
38
 
40
- def create_driver(conf=CONFIG)
39
+ def target_config
40
+ %[
41
+ port #{@target_port}
42
+ bind #{TARGET_HOST}
43
+ ]
44
+ end
45
+
46
+ def create_driver(conf=config)
41
47
  Fluent::Test::Driver::Output.new(Fluent::Plugin::ForwardOutput) {
42
48
  attr_reader :sent_chunk_ids, :ack_handler, :discovery_manager
43
49
 
@@ -60,7 +66,7 @@ class ForwardOutputTest < Test::Unit::TestCase
60
66
  <server>
61
67
  name test
62
68
  host #{TARGET_HOST}
63
- port #{TARGET_PORT}
69
+ port #{@target_port}
64
70
  </server>
65
71
  ])
66
72
  nodes = d.instance.nodes
@@ -71,7 +77,7 @@ class ForwardOutputTest < Test::Unit::TestCase
71
77
  node = nodes.first
72
78
  assert_equal "test", node.name
73
79
  assert_equal '127.0.0.1', node.host
74
- assert_equal TARGET_PORT, node.port
80
+ assert_equal @target_port, node.port
75
81
  end
76
82
 
77
83
  test 'configure_traditional' do
@@ -80,7 +86,7 @@ class ForwardOutputTest < Test::Unit::TestCase
80
86
  <server>
81
87
  name test
82
88
  host #{TARGET_HOST}
83
- port #{TARGET_PORT}
89
+ port #{@target_port}
84
90
  </server>
85
91
  buffer_chunk_limit 10m
86
92
  EOL
@@ -100,7 +106,7 @@ EOL
100
106
  ack_response_timeout 20
101
107
  <server>
102
108
  host #{TARGET_HOST}
103
- port #{TARGET_PORT}
109
+ port #{@target_port}
104
110
  </server>
105
111
  ])
106
112
  assert_equal 30, d.instance.send_timeout
@@ -110,33 +116,33 @@ EOL
110
116
  end
111
117
 
112
118
  test 'configure_udp_heartbeat' do
113
- @d = d = create_driver(CONFIG + "\nheartbeat_type udp")
119
+ @d = d = create_driver(config + "\nheartbeat_type udp")
114
120
  assert_equal :udp, d.instance.heartbeat_type
115
121
  end
116
122
 
117
123
  test 'configure_none_heartbeat' do
118
- @d = d = create_driver(CONFIG + "\nheartbeat_type none")
124
+ @d = d = create_driver(config + "\nheartbeat_type none")
119
125
  assert_equal :none, d.instance.heartbeat_type
120
126
  end
121
127
 
122
128
  test 'configure_expire_dns_cache' do
123
- @d = d = create_driver(CONFIG + "\nexpire_dns_cache 5")
129
+ @d = d = create_driver(config + "\nexpire_dns_cache 5")
124
130
  assert_equal 5, d.instance.expire_dns_cache
125
131
  end
126
132
 
127
133
  test 'configure_dns_round_robin udp' do
128
134
  assert_raise(Fluent::ConfigError) do
129
- create_driver(CONFIG + "\nheartbeat_type udp\ndns_round_robin true")
135
+ create_driver(config + "\nheartbeat_type udp\ndns_round_robin true")
130
136
  end
131
137
  end
132
138
 
133
139
  test 'configure_dns_round_robin transport' do
134
- @d = d = create_driver(CONFIG + "\nheartbeat_type transport\ndns_round_robin true")
140
+ @d = d = create_driver(config + "\nheartbeat_type transport\ndns_round_robin true")
135
141
  assert_equal true, d.instance.dns_round_robin
136
142
  end
137
143
 
138
144
  test 'configure_dns_round_robin none' do
139
- @d = d = create_driver(CONFIG + "\nheartbeat_type none\ndns_round_robin true")
145
+ @d = d = create_driver(config + "\nheartbeat_type none\ndns_round_robin true")
140
146
  assert_equal true, d.instance.dns_round_robin
141
147
  end
142
148
 
@@ -176,7 +182,7 @@ EOL
176
182
  #{param} #{dummy_cert_path}
177
183
  <server>
178
184
  host #{TARGET_HOST}
179
- port #{TARGET_PORT}
185
+ port #{@target_port}
180
186
  </server>
181
187
  ]
182
188
 
@@ -195,7 +201,7 @@ EOL
195
201
  tls_cert_thumbprint a909502dd82ae41433e6f83886b00d4277a32a7b
196
202
  <server>
197
203
  host #{TARGET_HOST}
198
- port #{TARGET_PORT}
204
+ port #{@target_port}
199
205
  </server>
200
206
  ]
201
207
 
@@ -210,7 +216,7 @@ EOL
210
216
  transport tls
211
217
  <server>
212
218
  host #{TARGET_HOST}
213
- port #{TARGET_PORT}
219
+ port #{@target_port}
214
220
  </server>
215
221
  ]
216
222
 
@@ -232,7 +238,7 @@ EOL
232
238
  tls_cert_logical_store_name Root
233
239
  <server>
234
240
  host #{TARGET_HOST}
235
- port #{TARGET_PORT}
241
+ port #{@target_port}
236
242
  </server>
237
243
  ]
238
244
 
@@ -250,7 +256,7 @@ EOL
250
256
  tls_cert_thumbprint a909502dd82ae41433e6f83886b00d4277a32a7b
251
257
  <server>
252
258
  host #{TARGET_HOST}
253
- port #{TARGET_PORT}
259
+ port #{@target_port}
254
260
  </server>
255
261
  ]
256
262
 
@@ -277,11 +283,16 @@ EOL
277
283
  </service_discovery>
278
284
  ])
279
285
 
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
286
+
287
+ assert_equal(
288
+ [
289
+ { host: '127.0.0.1', port: 1234 },
290
+ { host: '127.0.0.1', port: 1235 },
291
+ ],
292
+ d.instance.discovery_manager.services.collect do |service|
293
+ { host: service.host, port: service.port }
294
+ end
295
+ )
285
296
  end
286
297
 
287
298
  test 'pass username and password as empty string to HandshakeProtocol' do
@@ -316,7 +327,7 @@ EOL
316
327
  end
317
328
 
318
329
  test 'set_compress_is_gzip' do
319
- @d = d = create_driver(CONFIG + %[compress gzip])
330
+ @d = d = create_driver(config + %[compress gzip])
320
331
  assert_equal :gzip, d.instance.compress
321
332
  assert_equal :gzip, d.instance.buffer.compress
322
333
 
@@ -328,7 +339,7 @@ EOL
328
339
  mock = flexmock($log)
329
340
  mock.should_receive(:log).with("buffer is compressed. If you also want to save the bandwidth of a network, Add `compress` configuration in <match>")
330
341
 
331
- @d = d = create_driver(CONFIG + %[
342
+ @d = d = create_driver(config + %[
332
343
  <buffer>
333
344
  type memory
334
345
  compress gzip
@@ -342,7 +353,7 @@ EOL
342
353
  end
343
354
 
344
355
  test 'phi_failure_detector disabled' do
345
- @d = d = create_driver(CONFIG + %[phi_failure_detector false \n phi_threshold 0])
356
+ @d = d = create_driver(config + %[phi_failure_detector false \n phi_threshold 0])
346
357
  node = d.instance.nodes.first
347
358
  stub(node.failure).phi { raise 'Should not be called' }
348
359
  node.tick
@@ -350,34 +361,55 @@ EOL
350
361
  end
351
362
 
352
363
  test 'phi_failure_detector enabled' do
353
- @d = d = create_driver(CONFIG + %[phi_failure_detector true \n phi_threshold 0])
364
+ @d = d = create_driver(config + %[phi_failure_detector true \n phi_threshold 0])
354
365
  node = d.instance.nodes.first
355
366
  node.tick
356
367
  assert_false node.available?
357
368
  end
358
369
 
359
370
  test 'require_ack_response is disabled in default' do
360
- @d = d = create_driver(CONFIG)
371
+ @d = d = create_driver(config)
361
372
  assert_equal false, d.instance.require_ack_response
362
373
  assert_equal 190, d.instance.ack_response_timeout
363
374
  end
364
375
 
365
376
  test 'require_ack_response can be enabled' do
366
- @d = d = create_driver(CONFIG + %[
377
+ @d = d = create_driver(config + %[
367
378
  require_ack_response true
368
379
  ack_response_timeout 2s
369
380
  ])
381
+ d.instance_start
370
382
  assert d.instance.require_ack_response
371
383
  assert_equal 2, d.instance.ack_response_timeout
372
384
  end
373
385
 
386
+ test 'suspend_flush is disable before before_shutdown' do
387
+ @d = d = create_driver(config + %[
388
+ require_ack_response true
389
+ ack_response_timeout 2s
390
+ ])
391
+ d.instance_start
392
+ assert_false d.instance.instance_variable_get(:@suspend_flush)
393
+ end
394
+
395
+ test 'suspend_flush should be enabled and try_flush returns nil after before_shutdown' do
396
+ @d = d = create_driver(config + %[
397
+ require_ack_response true
398
+ ack_response_timeout 2s
399
+ ])
400
+ d.instance_start
401
+ d.instance.before_shutdown
402
+ assert_true d.instance.instance_variable_get(:@suspend_flush)
403
+ assert_nil d.instance.try_flush
404
+ end
405
+
374
406
  test 'verify_connection_at_startup is disabled in default' do
375
- @d = d = create_driver(CONFIG)
407
+ @d = d = create_driver(config)
376
408
  assert_false d.instance.verify_connection_at_startup
377
409
  end
378
410
 
379
411
  test 'verify_connection_at_startup can be enabled' do
380
- @d = d = create_driver(CONFIG + %[
412
+ @d = d = create_driver(config + %[
381
413
  verify_connection_at_startup true
382
414
  ])
383
415
  assert_true d.instance.verify_connection_at_startup
@@ -386,7 +418,7 @@ EOL
386
418
  test 'send tags in str (utf-8 strings)' do
387
419
  target_input_driver = create_target_input_driver
388
420
 
389
- @d = d = create_driver(CONFIG + %[flush_interval 1s])
421
+ @d = d = create_driver(config + %[flush_interval 1s])
390
422
 
391
423
  time = event_time("2011-01-02 13:14:15 UTC")
392
424
 
@@ -419,7 +451,7 @@ EOL
419
451
  test 'send_with_time_as_integer' do
420
452
  target_input_driver = create_target_input_driver
421
453
 
422
- @d = d = create_driver(CONFIG + %[flush_interval 1s])
454
+ @d = d = create_driver(config + %[flush_interval 1s])
423
455
 
424
456
  time = event_time("2011-01-02 13:14:15 UTC")
425
457
 
@@ -447,7 +479,7 @@ EOL
447
479
  test 'send_without_time_as_integer' do
448
480
  target_input_driver = create_target_input_driver
449
481
 
450
- @d = d = create_driver(CONFIG + %[
482
+ @d = d = create_driver(config + %[
451
483
  flush_interval 1s
452
484
  time_as_integer false
453
485
  ])
@@ -477,7 +509,7 @@ EOL
477
509
  test 'send_comprssed_message_pack_stream_if_compress_is_gzip' do
478
510
  target_input_driver = create_target_input_driver
479
511
 
480
- @d = d = create_driver(CONFIG + %[
512
+ @d = d = create_driver(config + %[
481
513
  flush_interval 1s
482
514
  compress gzip
483
515
  ])
@@ -507,7 +539,7 @@ EOL
507
539
  test 'send_to_a_node_supporting_responses' do
508
540
  target_input_driver = create_target_input_driver
509
541
 
510
- @d = d = create_driver(CONFIG + %[flush_interval 1s])
542
+ @d = d = create_driver(config + %[flush_interval 1s])
511
543
 
512
544
  time = event_time("2011-01-02 13:14:15 UTC")
513
545
 
@@ -533,7 +565,7 @@ EOL
533
565
  test 'send_to_a_node_not_supporting_responses' do
534
566
  target_input_driver = create_target_input_driver
535
567
 
536
- @d = d = create_driver(CONFIG + %[flush_interval 1s])
568
+ @d = d = create_driver(config + %[flush_interval 1s])
537
569
 
538
570
  time = event_time("2011-01-02 13:14:15 UTC")
539
571
 
@@ -559,38 +591,95 @@ EOL
559
591
  test 'a node supporting responses' do
560
592
  target_input_driver = create_target_input_driver
561
593
 
562
- @d = d = create_driver(CONFIG + %[
594
+ @d = d = create_driver(config + %[
563
595
  require_ack_response true
564
596
  ack_response_timeout 1s
565
597
  <buffer tag>
566
598
  flush_mode immediate
567
599
  retry_type periodic
568
600
  retry_wait 30s
569
- flush_at_shutdown false # suppress errors in d.instance_shutdown
601
+ flush_at_shutdown true
570
602
  </buffer>
571
603
  ])
572
604
 
573
605
  time = event_time("2011-01-02 13:14:15 UTC")
574
606
 
575
607
  acked_chunk_ids = []
608
+ nacked = false
576
609
  mock.proxy(d.instance.ack_handler).read_ack_from_sock(anything) do |info, success|
577
610
  if success
578
611
  acked_chunk_ids << info.chunk_id
612
+ else
613
+ nacked = true
579
614
  end
580
- [chunk_id, success]
615
+ [info, success]
581
616
  end
582
617
 
583
618
  records = [
584
619
  {"a" => 1},
585
620
  {"a" => 2}
586
621
  ]
587
- target_input_driver.run(expect_records: 2) do
588
- d.end_if { acked_chunk_ids.size > 0 }
622
+ target_input_driver.run(expect_records: 2, timeout: 5) do
623
+ d.end_if { acked_chunk_ids.size > 0 || nacked }
589
624
  d.run(default_tag: 'test', wait_flush_completion: false, shutdown: false) do
590
625
  d.feed([[time, records[0]], [time,records[1]]])
591
626
  end
592
627
  end
593
628
 
629
+ assert(!nacked, d.instance.log.logs.join)
630
+
631
+ events = target_input_driver.events
632
+ assert_equal ['test', time, records[0]], events[0]
633
+ assert_equal ['test', time, records[1]], events[1]
634
+
635
+ assert_equal 1, acked_chunk_ids.size
636
+ assert_equal d.instance.sent_chunk_ids.first, acked_chunk_ids.first
637
+ end
638
+
639
+ test 'a node supporting responses after stop' do
640
+ target_input_driver = create_target_input_driver
641
+
642
+ @d = d = create_driver(config + %[
643
+ require_ack_response true
644
+ ack_response_timeout 10s
645
+ <buffer tag>
646
+ flush_mode immediate
647
+ retry_type periodic
648
+ retry_wait 30s
649
+ flush_at_shutdown true
650
+ </buffer>
651
+ ])
652
+
653
+ time = event_time("2011-01-02 13:14:15 UTC")
654
+
655
+ acked_chunk_ids = []
656
+ nacked = false
657
+ mock.proxy(d.instance.ack_handler).read_ack_from_sock(anything) do |info, success|
658
+ if success
659
+ acked_chunk_ids << info.chunk_id
660
+ else
661
+ nacked = true
662
+ end
663
+ [info, success]
664
+ end
665
+
666
+ records = [
667
+ {"a" => 1},
668
+ {"a" => 2}
669
+ ]
670
+ target_input_driver.run(expect_records: 2, timeout: 5) do
671
+ d.end_if { acked_chunk_ids.size > 0 || nacked }
672
+ d.run(default_tag: 'test', wait_flush_completion: false, shutdown: false) do
673
+ d.instance.stop
674
+ d.feed([[time, records[0]], [time,records[1]]])
675
+ d.instance.before_shutdown
676
+ d.instance.shutdown
677
+ d.instance.after_shutdown
678
+ end
679
+ end
680
+
681
+ assert(!nacked, d.instance.log.logs.join)
682
+
594
683
  events = target_input_driver.events
595
684
  assert_equal ['test', time, records[0]], events[0]
596
685
  assert_equal ['test', time, records[1]], events[1]
@@ -604,7 +693,7 @@ EOL
604
693
  test 'TLS transport and ack parameter combination' do |ack|
605
694
  omit "TLS and 'ack false' always fails on AppVeyor. Need to debug" if Fluent.windows? && !ack
606
695
 
607
- input_conf = TARGET_CONFIG + %[
696
+ input_conf = target_config + %[
608
697
  <transport tls>
609
698
  insecure true
610
699
  </transport>
@@ -618,7 +707,7 @@ EOL
618
707
  tls_insecure_mode true
619
708
  <server>
620
709
  host #{TARGET_HOST}
621
- port #{TARGET_PORT}
710
+ port #{@target_port}
622
711
  </server>
623
712
  <buffer>
624
713
  #flush_mode immediate
@@ -647,7 +736,7 @@ EOL
647
736
  test 'a destination node not supporting responses by just ignoring' do
648
737
  target_input_driver = create_target_input_driver(response_stub: ->(_option) { nil }, disconnect: false)
649
738
 
650
- @d = d = create_driver(CONFIG + %[
739
+ @d = d = create_driver(config + %[
651
740
  require_ack_response true
652
741
  ack_response_timeout 1s
653
742
  <buffer tag>
@@ -692,7 +781,7 @@ EOL
692
781
  test 'a destination node not supporting responses by disconnection' do
693
782
  target_input_driver = create_target_input_driver(response_stub: ->(_option) { nil }, disconnect: true)
694
783
 
695
- @d = d = create_driver(CONFIG + %[
784
+ @d = d = create_driver(config + %[
696
785
  require_ack_response true
697
786
  ack_response_timeout 1s
698
787
  <buffer tag>
@@ -735,7 +824,7 @@ EOL
735
824
  end
736
825
 
737
826
  test 'authentication_with_shared_key' do
738
- input_conf = TARGET_CONFIG + %[
827
+ input_conf = target_config + %[
739
828
  <security>
740
829
  self_hostname in.localhost
741
830
  shared_key fluentd-sharedkey
@@ -755,7 +844,7 @@ EOL
755
844
  <server>
756
845
  name test
757
846
  host #{TARGET_HOST}
758
- port #{TARGET_PORT}
847
+ port #{@target_port}
759
848
  shared_key fluentd-sharedkey
760
849
  </server>
761
850
  ]
@@ -782,7 +871,7 @@ EOL
782
871
  end
783
872
 
784
873
  test 'keepalive + shared_key' do
785
- input_conf = TARGET_CONFIG + %[
874
+ input_conf = target_config + %[
786
875
  <security>
787
876
  self_hostname in.localhost
788
877
  shared_key fluentd-sharedkey
@@ -800,7 +889,7 @@ EOL
800
889
  <server>
801
890
  name test
802
891
  host #{TARGET_HOST}
803
- port #{TARGET_PORT}
892
+ port #{@target_port}
804
893
  </server>
805
894
  ]
806
895
  @d = d = create_driver(output_conf)
@@ -830,7 +919,7 @@ EOL
830
919
  end
831
920
 
832
921
  test 'authentication_with_user_auth' do
833
- input_conf = TARGET_CONFIG + %[
922
+ input_conf = target_config + %[
834
923
  <security>
835
924
  self_hostname in.localhost
836
925
  shared_key fluentd-sharedkey
@@ -855,7 +944,7 @@ EOL
855
944
  <server>
856
945
  name test
857
946
  host #{TARGET_HOST}
858
- port #{TARGET_PORT}
947
+ port #{@target_port}
859
948
  shared_key fluentd-sharedkey
860
949
  username fluentd
861
950
  password fluentd
@@ -885,7 +974,7 @@ EOL
885
974
 
886
975
  # This test is not 100% but test failed with previous Node implementation which has race condition
887
976
  test 'Node with security is thread-safe on multi threads' do
888
- input_conf = TARGET_CONFIG + %[
977
+ input_conf = target_config + %[
889
978
  <security>
890
979
  self_hostname in.localhost
891
980
  shared_key fluentd-sharedkey
@@ -904,7 +993,7 @@ EOL
904
993
  <server>
905
994
  name test
906
995
  host #{TARGET_HOST}
907
- port #{TARGET_PORT}
996
+ port #{@target_port}
908
997
  shared_key fluentd-sharedkey
909
998
  </server>
910
999
  ]
@@ -925,10 +1014,12 @@ EOL
925
1014
  end
926
1015
 
927
1016
  logs = d.logs
928
- assert_false(logs.any? { |log| log.include?("invalid format for PONG message") || log.include?("shared key mismatch") }, "'#{logs.last.strip}' happens")
1017
+ assert_false(logs.any? { |log|
1018
+ log.include?("invalid format for PONG message") || log.include?("shared key mismatch")
1019
+ }, "Actual log:\n#{logs.join}")
929
1020
  end
930
1021
 
931
- def create_target_input_driver(response_stub: nil, disconnect: false, conf: TARGET_CONFIG)
1022
+ def create_target_input_driver(response_stub: nil, disconnect: false, conf: target_config)
932
1023
  require 'fluent/plugin/in_forward'
933
1024
 
934
1025
  # TODO: Support actual TCP heartbeat test
@@ -944,7 +1035,7 @@ EOL
944
1035
  end
945
1036
 
946
1037
  test 'heartbeat_type_none' do
947
- @d = d = create_driver(CONFIG + "\nheartbeat_type none")
1038
+ @d = d = create_driver(config + "\nheartbeat_type none")
948
1039
  node = d.instance.nodes.first
949
1040
  assert_equal Fluent::Plugin::ForwardOutput::NoneHeartbeatNode, node.class
950
1041
 
@@ -958,7 +1049,7 @@ EOL
958
1049
  end
959
1050
 
960
1051
  test 'heartbeat_type_udp' do
961
- @d = d = create_driver(CONFIG + "\nheartbeat_type udp")
1052
+ @d = d = create_driver(config + "\nheartbeat_type udp")
962
1053
 
963
1054
  d.instance_start
964
1055
  usock = d.instance.instance_variable_get(:@usock)
@@ -969,7 +1060,7 @@ EOL
969
1060
  assert servers.find{|s| s.title == :out_forward_heartbeat_receiver }
970
1061
  assert timers.include?(:out_forward_heartbeat_request)
971
1062
 
972
- mock(usock).send("\0", 0, Socket.pack_sockaddr_in(TARGET_PORT, '127.0.0.1')).once
1063
+ mock(usock).send("\0", 0, Socket.pack_sockaddr_in(@target_port, '127.0.0.1')).once
973
1064
  d.instance.send(:on_heartbeat_timer)
974
1065
  end
975
1066
 
@@ -995,7 +1086,7 @@ EOL
995
1086
  test 'when out_forward has @id' do
996
1087
  # cancel https://github.com/fluent/fluentd/blob/077508ac817b7637307434d0c978d7cdc3d1c534/lib/fluent/plugin_id.rb#L43-L53
997
1088
  # it always return true in test
998
- mock.proxy(Fluent::Plugin).new_sd(:static, anything) { |v|
1089
+ mock.proxy(Fluent::Plugin).new_sd('static', parent: anything) { |v|
999
1090
  stub(v).plugin_id_for_test? { false }
1000
1091
  }.once
1001
1092
 
@@ -1006,7 +1097,7 @@ EOL
1006
1097
  }
1007
1098
 
1008
1099
  assert_nothing_raised do
1009
- output.configure(CONFIG + %[
1100
+ output.configure(config + %[
1010
1101
  @id unique_out_forward
1011
1102
  ])
1012
1103
  end
@@ -1014,7 +1105,7 @@ EOL
1014
1105
 
1015
1106
  sub_test_case 'verify_connection_at_startup' do
1016
1107
  test 'nodes are not available' do
1017
- @d = d = create_driver(CONFIG + %[
1108
+ @d = d = create_driver(config + %[
1018
1109
  verify_connection_at_startup true
1019
1110
  ])
1020
1111
  e = assert_raise Fluent::UnrecoverableError do
@@ -1030,7 +1121,7 @@ EOL
1030
1121
  end
1031
1122
 
1032
1123
  test 'nodes_shared_key_miss_match' do
1033
- input_conf = TARGET_CONFIG + %[
1124
+ input_conf = target_config + %[
1034
1125
  <security>
1035
1126
  self_hostname in.localhost
1036
1127
  shared_key fluentd-sharedkey
@@ -1047,7 +1138,7 @@ EOL
1047
1138
 
1048
1139
  <server>
1049
1140
  host #{TARGET_HOST}
1050
- port #{TARGET_PORT}
1141
+ port #{@target_port}
1051
1142
  </server>
1052
1143
  ]
1053
1144
  @d = d = create_driver(output_conf)
@@ -1061,7 +1152,7 @@ EOL
1061
1152
  end
1062
1153
 
1063
1154
  test 'nodes_shared_key_miss_match with TLS' do
1064
- input_conf = TARGET_CONFIG + %[
1155
+ input_conf = target_config + %[
1065
1156
  <security>
1066
1157
  self_hostname in.localhost
1067
1158
  shared_key fluentd-sharedkey
@@ -1082,7 +1173,7 @@ EOL
1082
1173
 
1083
1174
  <server>
1084
1175
  host #{TARGET_HOST}
1085
- port #{TARGET_PORT}
1176
+ port #{@target_port}
1086
1177
  </server>
1087
1178
  ]
1088
1179
  @d = d = create_driver(output_conf)
@@ -1097,7 +1188,7 @@ EOL
1097
1188
  end
1098
1189
 
1099
1190
  test 'nodes_shared_key_match' do
1100
- input_conf = TARGET_CONFIG + %[
1191
+ input_conf = target_config + %[
1101
1192
  <security>
1102
1193
  self_hostname in.localhost
1103
1194
  shared_key fluentd-sharedkey
@@ -1113,7 +1204,7 @@ EOL
1113
1204
  <server>
1114
1205
  name test
1115
1206
  host #{TARGET_HOST}
1116
- port #{TARGET_PORT}
1207
+ port #{@target_port}
1117
1208
  </server>
1118
1209
  ]
1119
1210
  @d = d = create_driver(output_conf)
@@ -1137,14 +1228,19 @@ EOL
1137
1228
  end
1138
1229
 
1139
1230
  test 'Create new connection per send_data' do
1140
- target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
1141
- output_conf = CONFIG
1231
+ target_input_driver = create_target_input_driver(conf: target_config)
1232
+ output_conf = config
1142
1233
  d = create_driver(output_conf)
1143
1234
  d.instance_start
1144
1235
 
1145
1236
  begin
1146
1237
  chunk = Fluent::Plugin::Buffer::MemoryChunk.new(Fluent::Plugin::Buffer::Metadata.new(nil, nil, nil))
1147
- mock.proxy(d.instance).socket_create_tcp(TARGET_HOST, TARGET_PORT, anything) { |sock| mock(sock).close.once; sock }.twice
1238
+ mock.proxy(d.instance).socket_create_tcp(TARGET_HOST, @target_port,
1239
+ linger_timeout: anything,
1240
+ send_timeout: anything,
1241
+ recv_timeout: anything,
1242
+ connect_timeout: anything
1243
+ ) { |sock| mock(sock).close.once; sock }.twice
1148
1244
 
1149
1245
  target_input_driver.run(timeout: 15) do
1150
1246
  d.run(shutdown: false) do
@@ -1166,7 +1262,7 @@ EOL
1166
1262
  name test
1167
1263
  standby
1168
1264
  host #{TARGET_HOST}
1169
- port #{TARGET_PORT}
1265
+ port #{@target_port}
1170
1266
  </server>
1171
1267
  ])
1172
1268
  d.instance_start
@@ -1175,8 +1271,8 @@ EOL
1175
1271
 
1176
1272
  sub_test_case 'keepalive' do
1177
1273
  test 'Do not create connection per send_data' do
1178
- target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
1179
- output_conf = CONFIG + %[
1274
+ target_input_driver = create_target_input_driver(conf: target_config)
1275
+ output_conf = config + %[
1180
1276
  keepalive true
1181
1277
  keepalive_timeout 2
1182
1278
  ]
@@ -1185,7 +1281,12 @@ EOL
1185
1281
 
1186
1282
  begin
1187
1283
  chunk = Fluent::Plugin::Buffer::MemoryChunk.new(Fluent::Plugin::Buffer::Metadata.new(nil, nil, nil))
1188
- mock.proxy(d.instance).socket_create_tcp(TARGET_HOST, TARGET_PORT, anything) { |sock| mock(sock).close.once; sock }.once
1284
+ mock.proxy(d.instance).socket_create_tcp(TARGET_HOST, @target_port,
1285
+ linger_timeout: anything,
1286
+ send_timeout: anything,
1287
+ recv_timeout: anything,
1288
+ connect_timeout: anything
1289
+ ) { |sock| mock(sock).close.once; sock }.once
1189
1290
 
1190
1291
  target_input_driver.run(timeout: 15) do
1191
1292
  d.run(shutdown: false) do
@@ -1201,7 +1302,7 @@ EOL
1201
1302
  end
1202
1303
 
1203
1304
  test 'create timer of purging obsolete sockets' do
1204
- output_conf = CONFIG + %[keepalive true]
1305
+ output_conf = config + %[keepalive true]
1205
1306
  d = create_driver(output_conf)
1206
1307
 
1207
1308
  mock(d.instance).timer_execute(:out_forward_heartbeat_request, 1).once
@@ -1211,8 +1312,8 @@ EOL
1211
1312
 
1212
1313
  sub_test_case 'with require_ack_response' do
1213
1314
  test 'Create connection per send_data' do
1214
- target_input_driver = create_target_input_driver(conf: TARGET_CONFIG)
1215
- output_conf = CONFIG + %[
1315
+ target_input_driver = create_target_input_driver(conf: target_config)
1316
+ output_conf = config + %[
1216
1317
  require_ack_response true
1217
1318
  keepalive true
1218
1319
  keepalive_timeout 2
@@ -1222,7 +1323,13 @@ EOL
1222
1323
 
1223
1324
  begin
1224
1325
  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
1326
+ mock.proxy(d.instance).socket_create_tcp(TARGET_HOST, @target_port,
1327
+ linger_timeout: anything,
1328
+ send_timeout: anything,
1329
+ recv_timeout: anything,
1330
+ connect_timeout: anything) { |sock|
1331
+ mock(sock).close.once; sock
1332
+ }.twice
1226
1333
 
1227
1334
  target_input_driver.run(timeout: 15) do
1228
1335
  d.run(shutdown: false) do