App-karr

 view release on metacpan or  search on metacpan

t/164-foundation-signal-exit.t  view on Meta::CPAN

my \$repo    = \$scratch->child('repo');

my \$f = App::karr::Foundation->new( _config_data => {} );
\$f->_run_command(\$repo, { max_runtime => 30 }, 'exec $^X $wrapper_path');
exit 0;
PERL
    return $driver;
}

sub reap_wait {
    my ( $pid, $max ) = @_;
    $max //= 10;
    my $end = time + $max;
    while ( time < $end ) {
        my $w = waitpid( $pid, WNOHANG );
        return $w if $w > 0 || $w < 0;
        select undef, undef, undef, 0.05;
    }
    diag "driver $pid did not exit within ${max}s; killing";
    kill 'KILL', $pid;
    waitpid( $pid, 0 );
    return -1;
}

sub wait_for_pid_file {
    my ( $pid_file, $timeout ) = @_;
    my $pid;
    my $end = time + $timeout;
    while ( time < $end && !$pid ) {
        if ( -e $pid_file ) {
            my $content = $pid_file->slurp_utf8;
            chomp $content;
            $pid = $content if $content =~ /^\d+$/;
        }
        select undef, undef, undef, 0.05;
    }
    return $pid;
}

subtest 'agent killed by SIGKILL is reported as 128+SIGKILL, not exit 0' => sub {
    my $scratch = path( tempdir( CLEANUP => 1 ) );
    my $repo    = $scratch->child('repo');
    $repo->mkpath;
    $repo->child('.karr.log')->touch;
    my $pid_file = $scratch->child('agent.pid');

    my $wrapper = write_agent_wrapper( $scratch, '/bin/sleep 20' );
    my $driver  = write_driver( $scratch, $wrapper );

    my $dpid = fork;
    die "fork: $!" unless defined $dpid;
    if ( $dpid == 0 ) {
        exec( $^X, "$driver" ) or die;
    }

    my $agent_pid = wait_for_pid_file( $pid_file, 5 );
    ok $agent_pid, 'agent was forked (pid file populated)'
        or BAIL_OUT 'no agent pid recorded -- the runner did not fork';

    select undef, undef, undef, 0.1;
    ok kill( 0, $agent_pid ), 'agent is alive in /proc before the kill';

    # External SIGKILL -- the OOM-killer shape.
    kill 'KILL', $agent_pid;

    reap_wait($dpid);

    my $log = -e $repo->child('.karr.log') ? $repo->child('.karr.log')->slurp_utf8 : '';
    like $log, qr/END elapsed=\d+s exit=(\d+)/, 'log has END line'
        or diag "log was: $log";

    my ($reported) = $log =~ /END elapsed=\d+s exit=(\d+)/;
    is $reported, 128 + SIGKILL,
        "exit code in log is 128+SIGKILL=137 (was: $reported)"
        or diag "runner reported $reported for a SIGKILLed agent -- bug #164 still present";

    isnt $reported, 0,
        'the SIGKILLed agent is NOT booked as exit 0'
        or diag 'agent was killed by SIGKILL but runner said exit 0';
};

subtest 'agent killed by external SIGTERM+SIGKILL is reported as 128+SIGKILL' => sub {
    # The wrapper ignores SIGTERM (so the kill actually escalates), then
    # the test sends SIGKILL to simulate the kernel OOM-killer landing
    # mid-escalation. The runner sees only SIGKILL because SIGTERM was
    # caught by the wrapper.
    my $scratch = path( tempdir( CLEANUP => 1 ) );
    my $repo    = $scratch->child('repo');
    $repo->mkpath;
    $repo->child('.karr.log')->touch;
    my $pid_file = $scratch->child('agent.pid');

    my $wrapper = write_signal_ignoring_wrapper( $scratch, '/bin/sleep 20' );
    my $driver  = write_driver( $scratch, $wrapper );

    my $dpid = fork;
    die "fork: $!" unless defined $dpid;
    if ( $dpid == 0 ) {
        exec( $^X, "$driver" ) or die;
    }

    my $agent_pid = wait_for_pid_file( $pid_file, 5 );
    ok $agent_pid, 'agent was forked';

    select undef, undef, undef, 0.1;
    kill 'TERM', $agent_pid;
    select undef, undef, undef, 0.1;
    kill 'KILL', $agent_pid;

    reap_wait($dpid);

    my $log = -e $repo->child('.karr.log') ? $repo->child('.karr.log')->slurp_utf8 : '';
    like $log, qr/END elapsed=\d+s exit=(\d+)/, 'log has END line'
        or diag "log was: $log";

    my ($reported) = $log =~ /END elapsed=\d+s exit=(\d+)/;
    ok defined $reported, 'log has END line (parsed)';
    isnt $reported, 0, 'killed agent is not booked as exit 0';
    is $reported, 128 + SIGKILL,
        'reported exit is 128+SIGKILL (the actual kill signal)'
        or diag "runner reported $reported for a SIGTERM->SIGKILLed agent";



( run in 1.205 second using v1.01-cache-2.11-cpan-14f38c9f855 )