Skip to content

Commit 6a27913

Browse files
authored
feat(in_exec): add command_timeout option to limit child process exec… (#5320)
**Which issue(s) this PR fixes**: Fixes #5319 **What this PR does / why we need it**: Adds `command_timeout` option to `in_exec` plugin, mirroring the existing `command_timeout` option in `out_exec`. Currently, `in_exec` calls `child_process_execute` without `wait_timeout`, defaulting to `nil` (infinite wait). When the external command hangs, Fluentd repeatedly shows `previous child process is still running. skipped.` with no indication of the actual cause, making troubleshooting difficult. `out_exec` already exposes `command_timeout` which maps to `wait_timeout` in `child_process_execute`. This PR resolves the inconsistency between the two plugins. **Docs Changes**: fluent/fluentd-docs-gitbook#619 **Release Note**: Add `command_timeout` option to `in_exec` plugin to kill long-running child processes and align behavior with `out_exec`. --------- Signed-off-by: zoklk <bjincheol34@gmail.com>
1 parent c603d04 commit 6a27913

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

lib/fluent/plugin/in_exec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class ExecInput < Fluent::Plugin::Input
4343
config_param :tag, :string, default: nil
4444
desc 'The interval time between periodic program runs.'
4545
config_param :run_interval, :time, default: nil
46+
desc 'Command (program) execution timeout.'
47+
config_param :command_timeout, :time, default: nil
4648
desc 'The default block size to read if parser requires partial read.'
4749
config_param :read_block_size, :size, default: 10240 # 10k
4850
desc 'The encoding to receive the result of the command, especially for non-ascii characters.'
@@ -86,9 +88,9 @@ def start
8688
options[:external_encoding] = @encoding if @encoding
8789

8890
if @run_interval
89-
child_process_execute(:exec_input, @command, interval: @run_interval, **options, &method(:run))
91+
child_process_execute(:exec_input, @command, interval: @run_interval, wait_timeout: @command_timeout, **options, &method(:run))
9092
else
91-
child_process_execute(:exec_input, @command, immediate: true, **options, &method(:run))
93+
child_process_execute(:exec_input, @command, immediate: true, wait_timeout: @command_timeout, **options, &method(:run))
9294
end
9395
end
9496

test/plugin/test_in_exec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,48 @@ def create_driver(conf)
295295
assert_match(/LoadError/, event[2]['message'])
296296
end
297297
end
298+
sub_test_case 'command_timeout' do
299+
test 'configure command_timeout' do
300+
d = create_driver %[
301+
command ruby -e "sleep 10"
302+
tag test
303+
run_interval 1s
304+
command_timeout 1s
305+
<parse>
306+
@type none
307+
</parse>
308+
]
309+
assert_equal 1.0, d.instance.command_timeout
310+
end
311+
312+
test 'command_timeout kills long-running child process' do
313+
d = create_driver %[
314+
command ruby -e "sleep 10"
315+
tag test
316+
run_interval 5s
317+
command_timeout 1s
318+
<parse>
319+
@type none
320+
</parse>
321+
]
322+
start_time = Time.now
323+
d.run(timeout: 5) do
324+
sleep 1 # avoid to return test immediately
325+
end
326+
elapsed = Time.now - start_time
327+
assert (elapsed >= 1.0 and elapsed < 5), "command should have been killed by command_timeout"
328+
end
329+
330+
test 'command_timeout defaults to nil' do
331+
d = create_driver %[
332+
command ruby -e "puts 'hello'"
333+
tag test
334+
run_interval 1s
335+
<parse>
336+
@type none
337+
</parse>
338+
]
339+
assert_nil d.instance.command_timeout
340+
end
341+
end
298342
end

0 commit comments

Comments
 (0)