From 878d06e9dd15b1986b8ca39c16fc58be50064000 Mon Sep 17 00:00:00 2001 From: Ioannis Bonatakis Date: Tue, 28 Jan 2025 20:58:58 +0100 Subject: [PATCH] Prevent git from hanging a process * Use of a timeout when IPC::Run invokes * Enhance error message and notify user for likely solution I tried to catch the prompt but I assume that the process is not running in a tty and IPC::Run could not get the output. Signed-off-by: Ioannis Bonatakis --- lib/OpenQA/Git.pm | 13 +++++++----- lib/OpenQA/Task/Needle/Delete.pm | 1 - lib/OpenQA/Utils.pm | 36 ++++++++++++++++++++++---------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/lib/OpenQA/Git.pm b/lib/OpenQA/Git.pm index da4a83d1fdd..24502321185 100644 --- a/lib/OpenQA/Git.pm +++ b/lib/OpenQA/Git.pm @@ -30,9 +30,11 @@ sub _validate_attributes ($self) { sub _run_cmd ($self, $args, $options = {}) { my $include_git_path = $options->{include_git_path} // 1; - my $ssh_batchmode = $options->{ssh_batchmode} // 0; + #warn(">>>> $options"); + my $batchmode = $options->{batchmode} // 0; my @cmd; - push @cmd, 'env', 'GIT_SSH_COMMAND=ssh -oBatchMode=yes' if $ssh_batchmode; + push @cmd, 'env', 'GIT_SSH_COMMAND=ssh -oBatchMode=yes' if $batchmode; + #push @cmd, 'env', $options->{nopass} if $options->{nopass}; push @cmd, $self->_prepare_git_command($include_git_path), @$args; my $result = run_cmd_with_log_return_error(\@cmd); @@ -97,6 +99,7 @@ sub commit ($self, $args = undef) { # push changes if (($self->config->{do_push} || '') eq 'yes') { + #my $options->{nopass} = "GIT_TERMINAL_PROMPT=false"; $res = $self->_run_cmd(['push']); return $self->_format_git_error($res, 'Unable to push Git commit') unless $res->{status}; } @@ -123,14 +126,14 @@ sub check_sha ($self, $sha) { } sub get_remote_default_branch ($self, $url) { - my $r = $self->_run_cmd(['ls-remote', '--symref', $url, 'HEAD'], {include_git_path => 0, ssh_batchmode => 1}); + my $r = $self->_run_cmd(['ls-remote', '--symref', $url, 'HEAD'], {include_git_path => 0, batchmode => 1}); die qq/Error detecting remote default branch name for "$url": $r->{stdout} $r->{stderr}/ unless $r->{status} && $r->{stdout} =~ m{refs/heads/(\S+)\s+HEAD}; return $1; } sub clone_url ($self, $url) { - my $r = $self->_run_cmd(['clone', $url, $self->dir], {include_git_path => 0, ssh_batchmode => 1}); + my $r = $self->_run_cmd(['clone', $url, $self->dir], {include_git_path => 0, batchmode => 1}); die $self->_format_git_error($r, qq/Failed to clone "$url"/) unless $r->{status}; } @@ -141,7 +144,7 @@ sub get_origin_url ($self) { } sub fetch ($self, $branch_arg) { - my $r = $self->_run_cmd(['fetch', 'origin', $branch_arg], {ssh_batchmode => 1}); + my $r = $self->_run_cmd(['fetch', 'origin', $branch_arg], {batchmode => 1}); die $self->_format_git_error($r, "Failed to fetch from '$branch_arg'") unless $r->{status}; } diff --git a/lib/OpenQA/Task/Needle/Delete.pm b/lib/OpenQA/Task/Needle/Delete.pm index 34b6df25b17..7e593b330d2 100644 --- a/lib/OpenQA/Task/Needle/Delete.pm +++ b/lib/OpenQA/Task/Needle/Delete.pm @@ -15,7 +15,6 @@ sub register { sub _delete_needles { my ($app, $minion_job, $args) = @_; - my $schema = $app->schema; my $needles = $schema->resultset('Needles'); my $user = $schema->resultset('Users')->find($args->{user_id}); diff --git a/lib/OpenQA/Utils.pm b/lib/OpenQA/Utils.pm index 0a3a2d1d404..c2afd191ee8 100644 --- a/lib/OpenQA/Utils.pm +++ b/lib/OpenQA/Utils.pm @@ -7,7 +7,7 @@ use Mojo::Base -strict, -signatures; use Carp; use Cwd 'abs_path'; use Filesys::Df qw(df); -use IPC::Run(); +use IPC::Run qw(run timeout); use Mojo::URL; use Regexp::Common 'URI'; use Time::Seconds; @@ -333,13 +333,17 @@ sub run_cmd_with_log { sub run_cmd_with_log_return_error ($cmd, %args) { my $stdout_level = $args{stdout} // 'debug'; my $stderr_level = $args{stderr} // 'debug'; + my $timeout_duration = 30; log_info('Running cmd: ' . join(' ', @$cmd)); + my ($stdin, $stdout_err, $stdout, $stderr) = ('') x 4; try { - my ($stdin, $stdout_err, $stdout, $stderr) = ('') x 4; - my $ipc_run_succeeded = IPC::Run::run($cmd, \$stdin, \$stdout, \$stderr); + my $ipc_run = run($cmd, \$stdin, \$stdout, \$stderr, timeout($timeout_duration)); my $return_code = $?; chomp $stderr; - if ($ipc_run_succeeded) { + #my $return_code = $?; + # use Data::Dumper; + #print "***";print Dumper($@); + if ($ipc_run) { OpenQA::Log->can("log_$stdout_level")->($stdout); OpenQA::Log->can("log_$stderr_level")->($stderr); log_info("cmd returned $return_code"); @@ -349,20 +353,30 @@ sub run_cmd_with_log_return_error ($cmd, %args) { log_error("cmd returned $return_code"); } return { - status => $ipc_run_succeeded, + status => $ipc_run, return_code => $return_code, stdout => $stdout, stderr => $stderr, }; } catch { - return { - status => 0, - return_code => undef, - stderr => 'an internal error occurred', - stdout => '', + my $additional_info = "If you use http for CASEDIR or NEEDLES_DIR is adviced to + configure git with the insteadof option in the .gitconfig file. + Run the following command to: + git config [--global] url.\"git://github.com\".insteadOf https://github.com"; + my $stderr_msg = ($_ =~ /timeout/) + ? join(' ', @$cmd) . " timed out after $timeout_duration seconds \nNote:\n$additional_info" + : 'an internal error occurred'; + #$stderr_msg .= "prompt detected" if $stdout =~ /Username.*:/; + log_error($stderr_msg); + return { + status => 0, + return_code => undef, + stderr => $stderr_msg, + stdout => '', + }; }; - }; + } sub asset_type_from_setting {