From 8e04d66f0b9a5fcd9999d41a1df7b2a9d8083fbe Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 5 Jun 2023 10:12:36 +0000 Subject: port t0011-active-unix-socket.sh to Perl 5 Another socat dependency down... I've also started turning FD_CLOEXEC off on a pipe as a mechanism to detect daemonized process death in tests. --- t/active-unix-socket.t | 117 ++++++++++++++++++++++++++++++++++++++++++ t/integration.ru | 1 + t/t0011-active-unix-socket.sh | 79 ---------------------------- 3 files changed, 118 insertions(+), 79 deletions(-) create mode 100644 t/active-unix-socket.t delete mode 100755 t/t0011-active-unix-socket.sh diff --git a/t/active-unix-socket.t b/t/active-unix-socket.t new file mode 100644 index 0000000..6b5c218 --- /dev/null +++ b/t/active-unix-socket.t @@ -0,0 +1,117 @@ +#!perl -w +# Copyright (C) unicorn hackers +# License: GPL-3.0+ + +use v5.14; BEGIN { require './t/lib.perl' }; +use IO::Socket::UNIX; +my %to_kill; +END { kill('TERM', values(%to_kill)) if keys %to_kill } +my $u1 = "$tmpdir/u1.sock"; +my $u2 = "$tmpdir/u2.sock"; +my $unix_req = sub { + my $s = IO::Socket::UNIX->new(Peer => shift, Type => SOCK_STREAM); + print $s @_, "\r\n\r\n" or die $!; + $s; +}; +{ + use autodie; + open my $fh, '>', "$tmpdir/u1.conf.rb"; + print $fh <', "$tmpdir/u2.conf.rb"; + print $fh <', "$tmpdir/u3.conf.rb"; + print $fh <join; +is($?, 0, 'daemonized 1st process'); +chomp($to_kill{u1} = slurp("$tmpdir/u.pid")); +like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file'); + +chomp(my $worker_pid = readline($unix_req->($u1, 'GET /pid'))); +like($worker_pid, qr/\A\d+\z/s, 'captured worker pid'); +ok(kill(0, $worker_pid), 'worker is kill-able'); + + +# 2nd process conflicts on PID +unicorn('-c', "$tmpdir/u2.conf.rb", @uarg)->join; +isnt($?, 0, 'conflicting PID file fails to start'); + +chomp(my $pidf = slurp("$tmpdir/u.pid")); +is($pidf, $to_kill{u1}, 'pid file contents unchanged after start failure'); + +chomp(my $pid2 = readline($unix_req->($u1, 'GET /pid'))); +is($worker_pid, $pid2, 'worker PID unchanged'); + + +# 3rd process conflicts on socket +unicorn('-c', "$tmpdir/u3.conf.rb", @uarg)->join; +isnt($?, 0, 'conflicting UNIX socket fails to start'); + +chomp($pid2 = readline($unix_req->($u1, 'GET /pid'))); +is($worker_pid, $pid2, 'worker PID still unchanged'); + +chomp($pidf = slurp("$tmpdir/u.pid")); +is($pidf, $to_kill{u1}, 'pid file contents unchanged after 2nd start failure'); + +{ # teardown initial process via SIGKILL + ok(kill('KILL', delete $to_kill{u1}), 'SIGKILL initial daemon'); + close $p1; + vec(my $rvec = '', fileno($p0), 1) = 1; + is(select($rvec, undef, undef, 5), 1, 'timeout for pipe HUP'); + is(my $undef = <$p0>, undef, 'process closed pipe writer at exit'); + ok(-f "$tmpdir/u.pid", 'pid file stayed after SIGKILL'); + ok(-S $u1, 'socket stayed after SIGKILL'); + is(IO::Socket::UNIX->new(Peer => $u1, Type => SOCK_STREAM), undef, + 'fail to connect to u1'); + ok(!kill(0, $worker_pid), 'worker gone after parent dies'); +} + +# restart the first instance +{ + pipe(($p0, $p1)) or die "pipe: $!"; + fcntl($p1, POSIX::F_SETFD, 0) or die "fcntl: $!"; # clear FD_CLOEXEC + unicorn('-c', "$tmpdir/u1.conf.rb", @uarg)->join; + is($?, 0, 'daemonized 1st process'); + chomp($to_kill{u1} = slurp("$tmpdir/u.pid")); + like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file'); + + chomp($pid2 = readline($unix_req->($u1, 'GET /pid'))); + like($pid2, qr/\A\d+\z/, 'worker running'); + + ok(kill('TERM', delete $to_kill{u1}), 'SIGTERM restarted daemon'); + close $p1; + vec(my $rvec = '', fileno($p0), 1) = 1; + is(select($rvec, undef, undef, 5), 1, 'timeout for pipe HUP'); + is(my $undef = <$p0>, undef, 'process closed pipe writer at exit'); + ok(!-f "$tmpdir/u.pid", 'pid file gone after SIGTERM'); + ok(-S $u1, 'socket stays after SIGTERM'); +} + +my @log = slurp("$tmpdir/err.log"); +diag("@log") if $ENV{V}; +done_testing; diff --git a/t/integration.ru b/t/integration.ru index c0bef99..21f5449 100644 --- a/t/integration.ru +++ b/t/integration.ru @@ -57,6 +57,7 @@ run(lambda do |env| when '/unknown-status-pass-through'; [ '666 I AM THE BEAST', {}, [] ] when '/env_dump'; [ 200, {}, [ env_dump(env) ] ] when '/write_on_close'; write_on_close + when '/pid'; [ 200, {}, [ "#$$\n" ] ] end # case PATH_INFO (GET) when 'POST' case env['PATH_INFO'] diff --git a/t/t0011-active-unix-socket.sh b/t/t0011-active-unix-socket.sh deleted file mode 100755 index fae0b6c..0000000 --- a/t/t0011-active-unix-socket.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh -. ./test-lib.sh -t_plan 11 "existing UNIX domain socket check" - -read_pid_unix () { - x=$(printf 'GET / HTTP/1.0\r\n\r\n' | \ - socat - UNIX:$unix_socket | \ - tail -1) - test -n "$x" - y="$(expr "$x" : '\([0-9][0-9]*\)')" - test x"$x" = x"$y" - test -n "$y" - echo "$y" -} - -t_begin "setup and start" && { - rtmpfiles unix_socket unix_config - rm -f $unix_socket - unicorn_setup - grep -v ^listen < $unicorn_config > $unix_config - echo "listen '$unix_socket'" >> $unix_config - unicorn -D -c $unix_config pid.ru - unicorn_wait_start - orig_master_pid=$unicorn_pid -} - -t_begin "get pid of worker" && { - worker_pid=$(read_pid_unix) - t_info "worker_pid=$worker_pid" -} - -t_begin "fails to start with existing pid file" && { - rm -f $ok - unicorn -D -c $unix_config pid.ru || echo ok > $ok - test x"$(cat $ok)" = xok -} - -t_begin "worker pid unchanged" && { - test x"$(read_pid_unix)" = x$worker_pid - > $r_err -} - -t_begin "fails to start with listening UNIX domain socket bound" && { - rm $ok $pid - unicorn -D -c $unix_config pid.ru || echo ok > $ok - test x"$(cat $ok)" = xok - > $r_err -} - -t_begin "worker pid unchanged (again)" && { - test x"$(read_pid_unix)" = x$worker_pid -} - -t_begin "nuking the existing Unicorn succeeds" && { - kill -9 $unicorn_pid - while kill -0 $unicorn_pid - do - sleep 1 - done - check_stderr -} - -t_begin "succeeds in starting with leftover UNIX domain socket bound" && { - test -S $unix_socket - unicorn -D -c $unix_config pid.ru - unicorn_wait_start -} - -t_begin "worker pid changed" && { - test x"$(read_pid_unix)" != x$worker_pid -} - -t_begin "killing succeeds" && { - kill $unicorn_pid -} - -t_begin "no errors" && check_stderr - -t_done -- cgit v1.2.3-24-ge0c7