diff options
author | Sean McGivern <sean@mcgivern.me.uk> | 2017-10-16 09:11:31 +0000 |
---|---|---|
committer | Winnie Hellmann <winnie@gitlab.com> | 2017-10-18 09:14:08 +0000 |
commit | 587d0465ae92951594b8d3de044bb83da13fab8b (patch) | |
tree | c2685abf7249132ba73e111b00ba6fc757f0747c /lib | |
parent | 6526efa657e874e286c851f3890ed775c330ab74 (diff) | |
download | gitlab-ce-587d0465ae92951594b8d3de044bb83da13fab8b.tar.gz |
Merge branch 'an/popen-deadline' into 'master'
Popen with a timeout
See merge request gitlab-org/gitlab-ce!14872
(cherry picked from commit 8aa6e7ef030a9e7522c533c7e177a618f37265ec)
b88e8aae Popen with a timeout
f09a7b3c Linter fixes
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/git/popen.rb | 63 | ||||
-rw-r--r-- | lib/gitlab/git/repository.rb | 7 |
2 files changed, 70 insertions, 0 deletions
diff --git a/lib/gitlab/git/popen.rb b/lib/gitlab/git/popen.rb index 3d2fc471d28..b45da6020ee 100644 --- a/lib/gitlab/git/popen.rb +++ b/lib/gitlab/git/popen.rb @@ -5,6 +5,8 @@ require 'open3' module Gitlab module Git module Popen + FAST_GIT_PROCESS_TIMEOUT = 15.seconds + def popen(cmd, path, vars = {}) unless cmd.is_a?(Array) raise "System commands must be given as an array of strings" @@ -27,6 +29,67 @@ module Gitlab [@cmd_output, @cmd_status] end + + def popen_with_timeout(cmd, timeout, path, vars = {}) + unless cmd.is_a?(Array) + raise "System commands must be given as an array of strings" + end + + path ||= Dir.pwd + vars['PWD'] = path + + unless File.directory?(path) + FileUtils.mkdir_p(path) + end + + rout, wout = IO.pipe + rerr, werr = IO.pipe + + pid = Process.spawn(vars, *cmd, out: wout, err: werr, chdir: path, pgroup: true) + + begin + status = process_wait_with_timeout(pid, timeout) + + # close write ends so we could read them + wout.close + werr.close + + cmd_output = rout.readlines.join + cmd_output << rerr.readlines.join # Copying the behaviour of `popen` which merges stderr into output + + [cmd_output, status.exitstatus] + rescue Timeout::Error => e + kill_process_group_for_pid(pid) + + raise e + ensure + wout.close unless wout.closed? + werr.close unless werr.closed? + + rout.close + rerr.close + end + end + + def process_wait_with_timeout(pid, timeout) + deadline = timeout.seconds.from_now + wait_time = 0.01 + + while deadline > Time.now + sleep(wait_time) + _, status = Process.wait2(pid, Process::WNOHANG) + + return status unless status.nil? + end + + raise Timeout::Error, "Timeout waiting for process ##{pid}" + end + + def kill_process_group_for_pid(pid) + Process.kill("KILL", -pid) + Process.wait(pid) + rescue Errno::ESRCH + end end end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 0f059bef808..19cbae070ef 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1058,6 +1058,13 @@ module Gitlab end # Refactoring aid; allows us to copy code from app/models/repository.rb + def run_git_with_timeout(args, timeout, env: {}) + circuit_breaker.perform do + popen_with_timeout([Gitlab.config.git.bin_path, *args], timeout, path, env) + end + end + + # Refactoring aid; allows us to copy code from app/models/repository.rb def commit(ref = 'HEAD') Gitlab::Git::Commit.find(self, ref) end |