fluentd 1.12.1-x86-mingw32 → 1.12.2-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.
- checksums.yaml +4 -4
- data/.deepsource.toml +13 -0
- data/.github/ISSUE_TEMPLATE/config.yml +2 -2
- data/.github/workflows/linux-test.yaml +36 -0
- data/.github/workflows/macos-test.yaml +30 -0
- data/.github/workflows/{build.yaml → windows-test.yaml} +7 -6
- data/CHANGELOG.md +47 -0
- data/MAINTAINERS.md +5 -2
- data/fluentd.gemspec +1 -0
- data/lib/fluent/command/bundler_injection.rb +1 -1
- data/lib/fluent/command/cat.rb +0 -1
- data/lib/fluent/command/plugin_config_formatter.rb +2 -1
- data/lib/fluent/compat/parser.rb +2 -2
- data/lib/fluent/config/section.rb +1 -1
- data/lib/fluent/config/types.rb +2 -2
- data/lib/fluent/event.rb +3 -13
- data/lib/fluent/load.rb +0 -1
- data/lib/fluent/plugin/formatter_ltsv.rb +2 -2
- data/lib/fluent/plugin/in_http.rb +1 -1
- data/lib/fluent/plugin/in_monitor_agent.rb +1 -1
- data/lib/fluent/plugin/in_tail.rb +34 -15
- data/lib/fluent/plugin/out_copy.rb +18 -5
- data/lib/fluent/plugin/out_exec_filter.rb +3 -3
- data/lib/fluent/plugin/out_forward.rb +61 -28
- data/lib/fluent/plugin/storage_local.rb +3 -3
- data/lib/fluent/supervisor.rb +1 -1
- data/lib/fluent/time.rb +57 -1
- data/lib/fluent/version.rb +1 -1
- data/test/command/test_fluentd.rb +8 -0
- data/test/config/test_configurable.rb +1 -1
- data/test/plugin/in_tail/test_position_file.rb +4 -4
- data/test/plugin/out_forward/test_connection_manager.rb +6 -0
- data/test/plugin/test_in_exec.rb +1 -1
- data/test/plugin/test_in_tail.rb +54 -16
- data/test/plugin/test_out_copy.rb +87 -0
- data/test/plugin/test_out_forward.rb +74 -0
- data/test/plugin/test_out_http.rb +1 -1
- data/test/plugin_helper/test_child_process.rb +5 -2
- data/test/test_event.rb +16 -0
- data/test/test_formatter.rb +30 -0
- data/test/test_time_parser.rb +109 -0
- metadata +27 -7
- data/.travis.yml +0 -77
- data/appveyor.yml +0 -31
@@ -159,9 +159,9 @@ module Fluent::Plugin
|
|
159
159
|
@added_prefix_string = @add_prefix + '.'
|
160
160
|
end
|
161
161
|
|
162
|
-
@respawns = if @child_respawn.nil?
|
162
|
+
@respawns = if @child_respawn.nil? || (@child_respawn == 'none') || (@child_respawn == '0')
|
163
163
|
0
|
164
|
-
elsif @child_respawn == 'inf'
|
164
|
+
elsif (@child_respawn == 'inf') || (@child_respawn == '-1')
|
165
165
|
-1
|
166
166
|
elsif @child_respawn =~ /^\d+$/
|
167
167
|
@child_respawn.to_i
|
@@ -251,7 +251,7 @@ module Fluent::Plugin
|
|
251
251
|
|
252
252
|
def tag_remove_prefix(tag)
|
253
253
|
if @remove_prefix
|
254
|
-
if (tag[0, @removed_length] == @removed_prefix_string
|
254
|
+
if ((tag[0, @removed_length] == @removed_prefix_string) && (tag.length > @removed_length)) || (tag == @removed_prefix_string)
|
255
255
|
tag = tag[@removed_length..-1] || ''
|
256
256
|
end
|
257
257
|
end
|
@@ -166,6 +166,7 @@ module Fluent::Plugin
|
|
166
166
|
|
167
167
|
@usock = nil
|
168
168
|
@keep_alive_watcher_interval = 5 # TODO
|
169
|
+
@suspend_flush = false
|
169
170
|
end
|
170
171
|
|
171
172
|
def configure(conf)
|
@@ -291,6 +292,15 @@ module Fluent::Plugin
|
|
291
292
|
@require_ack_response
|
292
293
|
end
|
293
294
|
|
295
|
+
def overwrite_delayed_commit_timeout
|
296
|
+
# Output#start sets @delayed_commit_timeout by @buffer_config.delayed_commit_timeout
|
297
|
+
# But it should be overwritten by ack_response_timeout to rollback chunks after timeout
|
298
|
+
if @delayed_commit_timeout != @ack_response_timeout
|
299
|
+
log.info "delayed_commit_timeout is overwritten by ack_response_timeout"
|
300
|
+
@delayed_commit_timeout = @ack_response_timeout + 2 # minimum ack_reader IO.select interval is 1s
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
294
304
|
def start
|
295
305
|
super
|
296
306
|
|
@@ -303,13 +313,7 @@ module Fluent::Plugin
|
|
303
313
|
end
|
304
314
|
|
305
315
|
if @require_ack_response
|
306
|
-
|
307
|
-
# But it should be overwritten by ack_response_timeout to rollback chunks after timeout
|
308
|
-
if @delayed_commit_timeout != @ack_response_timeout
|
309
|
-
log.info "delayed_commit_timeout is overwritten by ack_response_timeout"
|
310
|
-
@delayed_commit_timeout = @ack_response_timeout + 2 # minimum ack_reader IO.select interval is 1s
|
311
|
-
end
|
312
|
-
|
316
|
+
overwrite_delayed_commit_timeout
|
313
317
|
thread_create(:out_forward_receiving_ack, &method(:ack_reader))
|
314
318
|
end
|
315
319
|
|
@@ -346,6 +350,26 @@ module Fluent::Plugin
|
|
346
350
|
end
|
347
351
|
end
|
348
352
|
|
353
|
+
def before_shutdown
|
354
|
+
super
|
355
|
+
@suspend_flush = true
|
356
|
+
end
|
357
|
+
|
358
|
+
def after_shutdown
|
359
|
+
last_ack if @require_ack_response
|
360
|
+
super
|
361
|
+
end
|
362
|
+
|
363
|
+
def try_flush
|
364
|
+
return if @require_ack_response && @suspend_flush
|
365
|
+
super
|
366
|
+
end
|
367
|
+
|
368
|
+
def last_ack
|
369
|
+
overwrite_delayed_commit_timeout
|
370
|
+
ack_check(ack_select_interval)
|
371
|
+
end
|
372
|
+
|
349
373
|
def write(chunk)
|
350
374
|
return if chunk.empty?
|
351
375
|
tag = chunk.metadata.tag
|
@@ -361,6 +385,7 @@ module Fluent::Plugin
|
|
361
385
|
end
|
362
386
|
tag = chunk.metadata.tag
|
363
387
|
discovery_manager.select_service { |node| node.send_data(tag, chunk) }
|
388
|
+
last_ack if @require_ack_response && @suspend_flush
|
364
389
|
end
|
365
390
|
|
366
391
|
def create_transfer_socket(host, port, hostname, &block)
|
@@ -481,31 +506,39 @@ module Fluent::Plugin
|
|
481
506
|
@connection_manager.purge_obsolete_socks
|
482
507
|
end
|
483
508
|
|
509
|
+
def ack_select_interval
|
510
|
+
if @delayed_commit_timeout > 3
|
511
|
+
1
|
512
|
+
else
|
513
|
+
@delayed_commit_timeout / 3.0
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
484
517
|
def ack_reader
|
485
|
-
select_interval =
|
486
|
-
1
|
487
|
-
else
|
488
|
-
@delayed_commit_timeout / 3.0
|
489
|
-
end
|
518
|
+
select_interval = ack_select_interval
|
490
519
|
|
491
520
|
while thread_current_running?
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
case result
|
496
|
-
when AckHandler::Result::SUCCESS
|
497
|
-
commit_write(chunk_id)
|
498
|
-
when AckHandler::Result::FAILED
|
499
|
-
node.disable!
|
500
|
-
rollback_write(chunk_id, update_retry: false)
|
501
|
-
when AckHandler::Result::CHUNKID_UNMATCHED
|
502
|
-
rollback_write(chunk_id, update_retry: false)
|
503
|
-
else
|
504
|
-
log.warn("BUG: invalid status #{result} #{chunk_id}")
|
521
|
+
ack_check(select_interval)
|
522
|
+
end
|
523
|
+
end
|
505
524
|
|
506
|
-
|
507
|
-
|
508
|
-
|
525
|
+
def ack_check(select_interval)
|
526
|
+
@ack_handler.collect_response(select_interval) do |chunk_id, node, sock, result|
|
527
|
+
@connection_manager.close(sock)
|
528
|
+
|
529
|
+
case result
|
530
|
+
when AckHandler::Result::SUCCESS
|
531
|
+
commit_write(chunk_id)
|
532
|
+
when AckHandler::Result::FAILED
|
533
|
+
node.disable!
|
534
|
+
rollback_write(chunk_id, update_retry: false)
|
535
|
+
when AckHandler::Result::CHUNKID_UNMATCHED
|
536
|
+
rollback_write(chunk_id, update_retry: false)
|
537
|
+
else
|
538
|
+
log.warn("BUG: invalid status #{result} #{chunk_id}")
|
539
|
+
|
540
|
+
if chunk_id
|
541
|
+
rollback_write(chunk_id, update_retry: false)
|
509
542
|
end
|
510
543
|
end
|
511
544
|
end
|
@@ -87,7 +87,7 @@ module Fluent
|
|
87
87
|
if File.exist?(@path)
|
88
88
|
raise Fluent::ConfigError, "Plugin storage path '#{@path}' is not readable/writable" unless File.readable?(@path) && File.writable?(@path)
|
89
89
|
begin
|
90
|
-
data = open(@path, 'r:utf-8') { |io| io.read }
|
90
|
+
data = File.open(@path, 'r:utf-8') { |io| io.read }
|
91
91
|
if data.empty?
|
92
92
|
log.warn "detect empty plugin storage file during startup. Ignored: #{@path}"
|
93
93
|
return
|
@@ -115,7 +115,7 @@ module Fluent
|
|
115
115
|
return if @on_memory
|
116
116
|
return unless File.exist?(@path)
|
117
117
|
begin
|
118
|
-
json_string = open(@path, 'r:utf-8'){ |io| io.read }
|
118
|
+
json_string = File.open(@path, 'r:utf-8'){ |io| io.read }
|
119
119
|
json = Yajl::Parser.parse(json_string)
|
120
120
|
unless json.is_a?(Hash)
|
121
121
|
log.error "broken content for plugin storage (Hash required: ignored)", type: json.class
|
@@ -133,7 +133,7 @@ module Fluent
|
|
133
133
|
tmp_path = @path + '.tmp'
|
134
134
|
begin
|
135
135
|
json_string = Yajl::Encoder.encode(@store, pretty: @pretty_print)
|
136
|
-
open(tmp_path, 'w:utf-8', @mode) { |io| io.write json_string; io.fsync }
|
136
|
+
File.open(tmp_path, 'w:utf-8', @mode) { |io| io.write json_string; io.fsync }
|
137
137
|
File.rename(tmp_path, @path)
|
138
138
|
rescue => e
|
139
139
|
log.error "failed to save data for plugin storage to file", path: @path, tmp: tmp_path, error: e
|
data/lib/fluent/supervisor.rb
CHANGED
@@ -385,7 +385,7 @@ module Fluent
|
|
385
385
|
config_mtime = File.mtime(path)
|
386
386
|
|
387
387
|
# reuse previous config if last load time is within 5 seconds and mtime of the config file is not changed
|
388
|
-
if Time.now - Time.at(pre_loadtime) < 5
|
388
|
+
if (Time.now - Time.at(pre_loadtime) < 5) && (config_mtime == pre_config_mtime)
|
389
389
|
return params['pre_conf']
|
390
390
|
end
|
391
391
|
|
data/lib/fluent/time.rb
CHANGED
@@ -132,13 +132,14 @@ module Fluent
|
|
132
132
|
end
|
133
133
|
|
134
134
|
module TimeMixin
|
135
|
-
TIME_TYPES = ['string', 'unixtime', 'float']
|
135
|
+
TIME_TYPES = ['string', 'unixtime', 'float', 'mixed']
|
136
136
|
|
137
137
|
TIME_PARAMETERS = [
|
138
138
|
[:time_format, :string, {default: nil}],
|
139
139
|
[:localtime, :bool, {default: true}], # UTC if :localtime is false and :timezone is nil
|
140
140
|
[:utc, :bool, {default: false}], # to turn :localtime false
|
141
141
|
[:timezone, :string, {default: nil}],
|
142
|
+
[:time_format_fallbacks, :array, {default: []}], # try time_format, then try fallbacks
|
142
143
|
]
|
143
144
|
TIME_FULL_PARAMETERS = [
|
144
145
|
# To avoid to define :time_type twice (in plugin_helper/inject)
|
@@ -170,6 +171,12 @@ module Fluent
|
|
170
171
|
raise Fluent::ConfigError, "both of utc and localtime are specified, use only one of them"
|
171
172
|
end
|
172
173
|
|
174
|
+
if conf.has_key?('time_type') and @time_type == :mixed
|
175
|
+
if @time_format.nil? and @time_format_fallbacks.empty?
|
176
|
+
raise Fluent::ConfigError, "time_type is :mixed but time_format and time_format_fallbacks is empty."
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
173
180
|
Fluent::Timezone.validate!(@timezone) if @timezone
|
174
181
|
end
|
175
182
|
end
|
@@ -180,6 +187,7 @@ module Fluent
|
|
180
187
|
end
|
181
188
|
|
182
189
|
def time_parser_create(type: @time_type, format: @time_format, timezone: @timezone, force_localtime: false)
|
190
|
+
return MixedTimeParser.new(type, format, @localtime, timezone, @utc, force_localtime, @time_format_fallbacks) if type == :mixed
|
183
191
|
return NumericTimeParser.new(type) if type != :string
|
184
192
|
return TimeParser.new(format, true, nil) if force_localtime
|
185
193
|
|
@@ -452,4 +460,52 @@ module Fluent
|
|
452
460
|
end
|
453
461
|
end
|
454
462
|
end
|
463
|
+
|
464
|
+
# MixedTimeParser is available when time_type is set to :mixed
|
465
|
+
#
|
466
|
+
# Use Case 1: primary format is specified explicitly in time_format
|
467
|
+
# time_type mixed
|
468
|
+
# time_format %iso8601
|
469
|
+
# time_format_fallbacks unixtime
|
470
|
+
# Use Case 2: time_format is omitted
|
471
|
+
# time_type mixed
|
472
|
+
# time_format_fallbacks %iso8601, unixtime
|
473
|
+
#
|
474
|
+
class MixedTimeParser < TimeParser # to include TimeParseError
|
475
|
+
def initialize(type, format = nil, localtime = nil, timezone = nil, utc = nil, force_localtime = nil, fallbacks = [])
|
476
|
+
@parsers = []
|
477
|
+
fallbacks.unshift(format).each do |fallback|
|
478
|
+
next unless fallback
|
479
|
+
case fallback
|
480
|
+
when 'unixtime', 'float'
|
481
|
+
@parsers << NumericTimeParser.new(fallback, localtime, timezone)
|
482
|
+
else
|
483
|
+
if force_localtime
|
484
|
+
@parsers << TimeParser.new(fallback, true, nil)
|
485
|
+
else
|
486
|
+
localtime = localtime && (timezone.nil? && !utc)
|
487
|
+
@parsers << TimeParser.new(fallback, localtime, timezone)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
def parse(value)
|
494
|
+
@parsers.each do |parser|
|
495
|
+
begin
|
496
|
+
Float(value) if parser.class == Fluent::NumericTimeParser
|
497
|
+
rescue
|
498
|
+
next
|
499
|
+
end
|
500
|
+
begin
|
501
|
+
return parser.parse(value)
|
502
|
+
rescue
|
503
|
+
# skip TimeParseError
|
504
|
+
end
|
505
|
+
end
|
506
|
+
fallback_class = @parsers.collect do |parser| parser.class end.join(",")
|
507
|
+
raise TimeParseError, "invalid time format: value = #{value}, even though fallbacks: #{fallback_class}"
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
455
511
|
end
|
data/lib/fluent/version.rb
CHANGED
@@ -878,6 +878,8 @@ CONF
|
|
878
878
|
end
|
879
879
|
|
880
880
|
test "without RUBYOPT" do
|
881
|
+
saved_ruby_opt = ENV["RUBYOPT"]
|
882
|
+
ENV["RUBYOPT"] = nil
|
881
883
|
conf = <<CONF
|
882
884
|
<source>
|
883
885
|
@type dummy
|
@@ -889,6 +891,8 @@ CONF
|
|
889
891
|
CONF
|
890
892
|
conf_path = create_conf_file('rubyopt_test.conf', conf)
|
891
893
|
assert_log_matches(create_cmdline(conf_path), '-Eascii-8bit:ascii-8bit')
|
894
|
+
ensure
|
895
|
+
ENV["RUBYOPT"] = saved_ruby_opt
|
892
896
|
end
|
893
897
|
|
894
898
|
test 'invalid values are set to RUBYOPT' do
|
@@ -912,6 +916,8 @@ CONF
|
|
912
916
|
|
913
917
|
# https://github.com/fluent/fluentd/issues/2915
|
914
918
|
test "ruby path contains spaces" do
|
919
|
+
saved_ruby_opt = ENV["RUBYOPT"]
|
920
|
+
ENV["RUBYOPT"] = nil
|
915
921
|
conf = <<CONF
|
916
922
|
<source>
|
917
923
|
@type dummy
|
@@ -940,6 +946,8 @@ CONF
|
|
940
946
|
'spawn command to main:',
|
941
947
|
'-Eascii-8bit:ascii-8bit'
|
942
948
|
)
|
949
|
+
ensure
|
950
|
+
ENV["RUBYOPT"] = saved_ruby_opt
|
943
951
|
end
|
944
952
|
|
945
953
|
test 'success to start workers when file buffer is configured in non-workers way only for specific worker' do
|
@@ -1453,7 +1453,7 @@ module Fluent::Config
|
|
1453
1453
|
@example = ConfigurableSpec::ExampleWithSkipAccessor.new
|
1454
1454
|
@example.configure(config_element('ROOT'))
|
1455
1455
|
assert_equal 'example7', @example.instance_variable_get(:@name)
|
1456
|
-
assert_raise NoMethodError
|
1456
|
+
assert_raise NoMethodError do
|
1457
1457
|
@example.name
|
1458
1458
|
end
|
1459
1459
|
end
|
@@ -147,7 +147,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
147
147
|
write_data(@file, TEST_CONTENT)
|
148
148
|
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, **{logger: $log})
|
149
149
|
|
150
|
-
valid_target_info = Fluent::Plugin::TailInput::TargetInfo.new('valid_path',
|
150
|
+
valid_target_info = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', File.stat(@file).ino)
|
151
151
|
f = pf[valid_target_info]
|
152
152
|
assert_equal Fluent::Plugin::TailInput::FilePositionEntry, f.class
|
153
153
|
assert_equal 2, f.read_pos
|
@@ -177,7 +177,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
177
177
|
assert_equal 0, f.read_inode
|
178
178
|
assert_equal 0, f.read_pos
|
179
179
|
|
180
|
-
pf[Fluent::Plugin::TailInput::TargetInfo.new('valid_path',
|
180
|
+
pf[Fluent::Plugin::TailInput::TargetInfo.new('valid_path', File.stat(@file).ino)].update(1, 2)
|
181
181
|
|
182
182
|
f = pf[Fluent::Plugin::TailInput::TargetInfo.new('nonexist_path', -1)]
|
183
183
|
assert_equal 0, f.read_inode
|
@@ -193,7 +193,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
193
193
|
test 'deletes entry by path' do
|
194
194
|
write_data(@file, TEST_CONTENT)
|
195
195
|
pf = Fluent::Plugin::TailInput::PositionFile.load(@file, false, {}, logger: $log)
|
196
|
-
inode1 =
|
196
|
+
inode1 = File.stat(@file).ino
|
197
197
|
target_info1 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode1)
|
198
198
|
p1 = pf[target_info1]
|
199
199
|
assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p1.class
|
@@ -201,7 +201,7 @@ class IntailPositionFileTest < Test::Unit::TestCase
|
|
201
201
|
pf.unwatch(target_info1)
|
202
202
|
assert_equal p1.read_pos, Fluent::Plugin::TailInput::PositionFile::UNWATCHED_POSITION
|
203
203
|
|
204
|
-
inode2 =
|
204
|
+
inode2 = File.stat(@file).ino
|
205
205
|
target_info2 = Fluent::Plugin::TailInput::TargetInfo.new('valid_path', inode2)
|
206
206
|
p2 = pf[target_info2]
|
207
207
|
assert_equal Fluent::Plugin::TailInput::FilePositionEntry, p2.class
|
@@ -81,6 +81,8 @@ class ConnectionManager < Test::Unit::TestCase
|
|
81
81
|
|
82
82
|
sub_test_case 'when socket_cache exists' do
|
83
83
|
test 'calls connect_keepalive' do
|
84
|
+
omit "Proxy of RR doesn't support kwargs of Ruby 3 yet" if RUBY_VERSION.split('.')[0].to_i >= 3
|
85
|
+
|
84
86
|
cache = Fluent::Plugin::ForwardOutput::SocketCache.new(10, $log)
|
85
87
|
mock(cache).checkin('sock').never
|
86
88
|
|
@@ -99,6 +101,8 @@ class ConnectionManager < Test::Unit::TestCase
|
|
99
101
|
end
|
100
102
|
|
101
103
|
test 'calls connect_keepalive and closes socket with block' do
|
104
|
+
omit "Proxy of RR doesn't support kwargs of Ruby 3 yet" if RUBY_VERSION.split('.')[0].to_i >= 3
|
105
|
+
|
102
106
|
cache = Fluent::Plugin::ForwardOutput::SocketCache.new(10, $log)
|
103
107
|
mock(cache).checkin('sock').once
|
104
108
|
|
@@ -118,6 +122,8 @@ class ConnectionManager < Test::Unit::TestCase
|
|
118
122
|
end
|
119
123
|
|
120
124
|
test 'does not call dec_ref when ack is passed' do
|
125
|
+
omit "Proxy of RR doesn't support kwargs of Ruby 3 yet" if RUBY_VERSION.split('.')[0].to_i >= 3
|
126
|
+
|
121
127
|
cache = Fluent::Plugin::ForwardOutput::SocketCache.new(10, $log)
|
122
128
|
mock(cache).checkin('sock').never
|
123
129
|
sock = 'sock'
|
data/test/plugin/test_in_exec.rb
CHANGED
data/test/plugin/test_in_tail.rb
CHANGED
@@ -6,6 +6,8 @@ require 'fluent/system_config'
|
|
6
6
|
require 'net/http'
|
7
7
|
require 'flexmock/test_unit'
|
8
8
|
require 'timecop'
|
9
|
+
require 'tmpdir'
|
10
|
+
require 'securerandom'
|
9
11
|
|
10
12
|
class TailInputTest < Test::Unit::TestCase
|
11
13
|
include FlexMock::TestCase
|
@@ -22,28 +24,64 @@ class TailInputTest < Test::Unit::TestCase
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def cleanup_directory(path)
|
25
|
-
|
26
|
-
FileUtils.
|
27
|
-
|
28
|
-
FileUtils.rm_f(path) # For Ruby 2.6 or before.
|
27
|
+
unless Dir.exist?(path)
|
28
|
+
FileUtils.mkdir_p(path)
|
29
|
+
return
|
29
30
|
end
|
30
|
-
|
31
|
-
|
31
|
+
|
32
|
+
if Fluent.windows?
|
33
|
+
Dir.glob("*", base: path).each do |name|
|
34
|
+
begin
|
35
|
+
cleanup_file(File.join(path, name))
|
36
|
+
rescue
|
37
|
+
# expect test driver block release already owned file handle.
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
begin
|
42
|
+
FileUtils.rm_f(path, secure:true)
|
43
|
+
rescue ArgumentError
|
44
|
+
FileUtils.rm_f(path) # For Ruby 2.6 or before.
|
45
|
+
end
|
46
|
+
if File.exist?(path)
|
47
|
+
FileUtils.remove_entry_secure(path, true)
|
48
|
+
end
|
32
49
|
end
|
33
50
|
FileUtils.mkdir_p(path)
|
34
51
|
end
|
35
52
|
|
36
53
|
def cleanup_file(path)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
#
|
44
|
-
#
|
45
|
-
|
46
|
-
|
54
|
+
if Fluent.windows?
|
55
|
+
# On Windows, when the file or directory is removed and created
|
56
|
+
# frequently, there is a case that creating file or directory will
|
57
|
+
# fail. This situation is caused by pending file or directory
|
58
|
+
# deletion which is mentioned on win32 API document [1]
|
59
|
+
# As a workaround, execute rename and remove method.
|
60
|
+
#
|
61
|
+
# [1] https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#files
|
62
|
+
#
|
63
|
+
file = File.join(Dir.tmpdir, SecureRandom.hex(10))
|
64
|
+
begin
|
65
|
+
FileUtils.mv(path, file)
|
66
|
+
FileUtils.rm_rf(file, secure: true)
|
67
|
+
rescue ArgumentError
|
68
|
+
FileUtils.rm_rf(file) # For Ruby 2.6 or before.
|
69
|
+
end
|
70
|
+
if File.exist?(file)
|
71
|
+
# ensure files are closed for Windows, on which deleted files
|
72
|
+
# are still visible from filesystem
|
73
|
+
GC.start(full_mark: true, immediate_mark: true, immediate_sweep: true)
|
74
|
+
FileUtils.remove_entry_secure(file, true)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
begin
|
78
|
+
FileUtils.rm_f(path, secure: true)
|
79
|
+
rescue ArgumentError
|
80
|
+
FileUtils.rm_f(path) # For Ruby 2.6 or before.
|
81
|
+
end
|
82
|
+
if File.exist?(path)
|
83
|
+
FileUtils.remove_entry_secure(path, true)
|
84
|
+
end
|
47
85
|
end
|
48
86
|
end
|
49
87
|
|