App-karr

 view release on metacpan or  search on metacpan

t/163-foundation-sigterm-cleanup.t  view on Meta::CPAN

use strict;
use warnings;

# Ticket #163. Foundation installed no SIGTERM handler. Stop it
# mid-drain -- systemd TimeoutStopSec, a deploy, an operator's kill --
# and the agent survived, reparented to init, while .karr.lock named the
# foundation's now-dead pid and the next tick started a second agent.
#
# The fix has two halves:
#   - Foundation::run installs SIGTERM/INT/HUP handlers that kill the
#     live agent's process group and force-release the lock.
#   - the lock now records the work (an open fd, plus pid/pgid as
#     evidence), so the handler can find and clean it up.
#
# This test exercises the handler end-to-end. We fork a driver child
# that imports Foundation, installs the SIGTERM handler, drives an
# agent through _run_command (so the agent is in its own process
# group, the same shape production leaves), then sends SIGTERM to
# itself. We then check from the parent:
#   1. the agent is gone (it would have been reparented to init without
#      the handler, and would still be running);
#   2. .karr.lock is gone (the handler force-released it);
#   3. a fresh foundation instance reports the lock free.

use Test::More;
use POSIX qw( WNOHANG SIGTERM );
use File::Temp qw( tempdir );
use Path::Tiny qw( path );

sub reap_or_kill {
    my ($pid) = @_;
    # waitpid(2) with WNOHANG: if the child has exited, $w is the pid
    # (or 0 if still alive, -1 on error). A handler that called
    # POSIX::_exit has already produced a status for us; we just want
    # to collect it without the WNOHANG-vs-blocking race that would
    # otherwise be the test's own failure mode.
    my $w = waitpid( $pid, WNOHANG );
    return 1 if $w > 0 || $w < 0;
    kill 'KILL', $pid;
    waitpid( $pid, 0 );
    return 1;
}

sub cmdline_of {
    my ($pid) = @_;
    my $cmdline = path("/proc/$pid/cmdline")->slurp_utf8 // return '';
    $cmdline =~ s/\0/ /g;
    return $cmdline;
}

# A script the driver child runs. We write it to a tempdir and exec it
# so that the test process itself is not exposed to the SIGTERM -- the
# handler runs in the driver only, and the test process just observes.
sub write_driver_script {
    my ( $dir, $repo_str ) = @_;
    my $lib = path('lib')->absolute->stringify;
    my $script = path($dir)->child('driver.pl');
    $script->spew_utf8(<<PERL);
use strict;
use warnings;
use lib '$lib';
use App::karr::Foundation;
use Path::Tiny qw( path );
my \$repo = path("$repo_str");
my \$f    = App::karr::Foundation->new( _config_data => {} );



( run in 1.771 second using v1.01-cache-2.11-cpan-85d3896f969 )