mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-02 08:51:44 +00:00
test(openeuler): exercise logging and host-key postscripts
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Cwd qw(abs_path);
|
||||
use Digest::SHA qw(sha256_hex);
|
||||
use File::Copy qw(copy);
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
plan skip_all => 'requires Linux root and mount namespaces' unless $^O eq 'linux' && $> == 0;
|
||||
plan skip_all => 'mount namespaces unavailable' if system('unshare -m -- true >/dev/null 2>&1');
|
||||
plan skip_all => 'requires native ssh-keygen' unless -x '/usr/bin/ssh-keygen';
|
||||
my $source = $ENV{XCAT_HOSTKEY_SOURCE_ROOT} || abs_path("$FindBin::Bin/../..");
|
||||
my $tmp = tempdir(DIR => '/var/tmp', CLEANUP => 1);
|
||||
chmod 0700, $tmp;
|
||||
make_path("$tmp/keys", "$tmp/bin");
|
||||
|
||||
sub write_file {
|
||||
my ($path, $contents) = @_;
|
||||
open(my $fh, '>', $path) or die "$path: $!";
|
||||
print {$fh} $contents;
|
||||
close($fh) or die "$path: $!";
|
||||
}
|
||||
sub read_file {
|
||||
my ($path) = @_;
|
||||
return '' unless -f $path;
|
||||
open(my $fh, '<', $path) or die "$path: $!";
|
||||
local $/;
|
||||
return <$fh> // '';
|
||||
}
|
||||
sub public_identity {
|
||||
my ($path) = @_;
|
||||
my @fields = split /\s+/, read_file($path);
|
||||
return join(' ', @fields[0, 1]) if @fields >= 2;
|
||||
return '';
|
||||
}
|
||||
|
||||
my @types = qw(dsa rsa ecdsa ed25519);
|
||||
for my $type (@types) {
|
||||
my $rc = system('/usr/bin/ssh-keygen', '-q', '-t', $type, '-f', "$tmp/keys/$type", '-N', '', '-C', '');
|
||||
BAIL_OUT("native ssh-keygen cannot generate disposable $type fixture") if $rc;
|
||||
}
|
||||
for my $name (qw(remoteshell xcatlib.sh remoteshell-sshd-config)) {
|
||||
copy("$source/xCAT/postscripts/$name", "$tmp/bin/$name") or die $!;
|
||||
chmod 0755, "$tmp/bin/$name";
|
||||
}
|
||||
write_file("$tmp/bin/namespace", <<'SH');
|
||||
#!/bin/bash
|
||||
set -e
|
||||
mount --make-rprivate /
|
||||
mount --bind "$XCAT_KEY_FIXTURE/etc" /etc
|
||||
mount --bind "$XCAT_KEY_FIXTURE/root" /root
|
||||
mount --bind "$XCAT_KEY_FIXTURE/tmp" /tmp
|
||||
exec "$@"
|
||||
SH
|
||||
write_file("$tmp/bin/getcredentials.awk", <<'SH');
|
||||
#!/bin/bash
|
||||
case "$1" in
|
||||
ssh_dsa_hostkey) cat "$XCAT_KEY_INPUT/dsa" ;;
|
||||
ssh_rsa_hostkey) cat "$XCAT_KEY_INPUT/rsa" ;;
|
||||
ssh_ecdsa_hostkey) cat "$XCAT_KEY_INPUT/ecdsa" ;;
|
||||
ssh_ed25519_hostkey) cat "$XCAT_KEY_INPUT/ed25519" ;;
|
||||
ssh_root_pub_key) cat "$XCAT_KEY_INPUT/rsa.pub" ;;
|
||||
*) printf '<error>unexpected credential request</error>\n'; exit 1 ;;
|
||||
esac
|
||||
SH
|
||||
write_file("$tmp/bin/allowcred.awk", "#!/bin/sh\nexec /bin/sleep 60\n");
|
||||
write_file("$tmp/bin/logger", "#!/bin/sh\nprintf '%s\\n' \"\$*\" >> \"\$XCAT_KEY_FIXTURE/logger.log\"\n");
|
||||
write_file("$tmp/bin/systemctl", "#!/bin/sh\nprintf '%s\\n' \"\$*\" >> \"\$XCAT_KEY_FIXTURE/services.log\"\nexit 0\n");
|
||||
write_file("$tmp/bin/sleep", "#!/bin/sh\nexit 0\n");
|
||||
for my $command (qw(chown chmod)) {
|
||||
write_file("$tmp/bin/$command", "#!/bin/bash\n" .
|
||||
'if [ "$XCAT_KEY_FAIL_COMMAND" = "${0##*/}" ] && [[ "${!#}" = "/etc/ssh/ssh_host_${XCAT_KEY_FAIL_TYPE}_key" ]]; then exit 42; fi' . "\n" .
|
||||
'exec /usr/bin/' . $command . ' "$@"' . "\n");
|
||||
}
|
||||
for my $name (qw(namespace getcredentials.awk allowcred.awk logger systemctl sleep chown chmod)) {
|
||||
chmod 0755, "$tmp/bin/$name";
|
||||
}
|
||||
|
||||
for my $case (
|
||||
['native fresh', 'openeuler24.03sp3', 'openEuler', 1, 0],
|
||||
['native existing', 'openeuler20.03sp4', 'openEuler', 1, 1],
|
||||
['native no ssh_keys group', 'openeuler24.03', 'openEuler', 0, 1],
|
||||
['native os-release fallback', '', 'openEuler', 1, 1],
|
||||
['legacy with ssh_keys group', 'rhels9.6', 'rhel', 1, 0],
|
||||
['legacy without ssh_keys group', 'rhels9.6', 'rhel', 0, 0],
|
||||
['native chmod failure', 'openeuler24.03sp3', 'openEuler', 1, 1, 'chmod', 'dsa'],
|
||||
['native chown failure', 'openeuler24.03sp3', 'openEuler', 1, 1, 'chown', 'ed25519'],
|
||||
) {
|
||||
my ($label, $osver, $id, $group, $existing, $fail_command, $fail_type) = @$case;
|
||||
my $fixture = tempdir(DIR => $tmp, CLEANUP => 1);
|
||||
make_path(map { "$fixture/$_" } qw(etc/ssh root tmp));
|
||||
write_file("$fixture/etc/passwd", "root:x:0:0:root:/root:/bin/bash\n");
|
||||
write_file("$fixture/etc/group", "root:x:0:\n" . ($group ? "ssh_keys:x:4242:\n" : ''));
|
||||
write_file("$fixture/etc/nsswitch.conf", "passwd: files\ngroup: files\n");
|
||||
write_file("$fixture/etc/os-release", "ID=$id\n");
|
||||
write_file("$fixture/etc/ssh/sshd_config", "Port 22\n");
|
||||
write_file("$fixture/etc/ssh/ssh_config", "Host *\n");
|
||||
for my $type (@types) {
|
||||
next unless $existing;
|
||||
my $key = "$fixture/etc/ssh/ssh_host_${type}_key";
|
||||
copy("$tmp/keys/$type", $key) or die $!;
|
||||
chmod 0640, $key;
|
||||
chown 0, 4242, $key;
|
||||
}
|
||||
for my $run (1, 2) {
|
||||
my $output;
|
||||
my $rc;
|
||||
{
|
||||
local %ENV = (%ENV, PATH => "$tmp/bin:/usr/bin:/bin:/usr/sbin:/sbin",
|
||||
OSVER => $osver, MASTER => '192.0.2.1', USEFLOWCONTROL => 'NO',
|
||||
NTYPE => 'compute', ENABLESSHBETWEENNODES => 'NO', NODESETSTATE => 'netboot',
|
||||
SECUREROOT => '0', ZONENAME => '', XCAT_KEY_FIXTURE => $fixture,
|
||||
XCAT_KEY_INPUT => "$tmp/keys", XCAT_SSH_ETC => '/etc/ssh',
|
||||
XCAT_KEY_FAIL_COMMAND => $fail_command || '', XCAT_KEY_FAIL_TYPE => $fail_type || '');
|
||||
open(my $pipe, '-|', 'sh', '-c', 'exec "$@" 2>&1', 'sh',
|
||||
'unshare', '-m', '--', "$tmp/bin/namespace", "$tmp/bin/remoteshell") or die $!;
|
||||
$output = do { local $/; <$pipe> };
|
||||
close($pipe);
|
||||
$rc = $? >> 8;
|
||||
}
|
||||
if ($fail_command) {
|
||||
is($rc, 1, "$label run $run reports failed key protection");
|
||||
like(read_file("$fixture/logger.log"), qr/failed to secure .*ssh_host_${fail_type}_key/, "$label run $run identifies the failed key");
|
||||
ok(!-e "$fixture/etc/ssh/ssh_host_${fail_type}_key.pub", "$label run $run stops before public key derivation");
|
||||
is(read_file("$fixture/services.log"), '', "$label run $run stops before service restart");
|
||||
next;
|
||||
}
|
||||
is($rc, 0, "$label run $run completes the full postscript");
|
||||
my $native = $id eq 'openEuler';
|
||||
if ($native || !$group) {
|
||||
unlike($output, qr/UNPROTECTED PRIVATE KEY|bad permissions/, "$label run $run has no permission rejection");
|
||||
for my $type (@types) {
|
||||
my $key = "$fixture/etc/ssh/ssh_host_${type}_key";
|
||||
is(sha256_hex(read_file($key)), sha256_hex(read_file("$tmp/keys/$type")), "$label run $run preserves provisioned $type identity");
|
||||
is(sha256_hex(public_identity("$key.pub")), sha256_hex(public_identity("$tmp/keys/$type.pub")), "$label run $run derives the matching $type public key");
|
||||
my @private = stat($key);
|
||||
my @public = stat("$key.pub");
|
||||
is_deeply([defined($private[2]) ? $private[2] & 0777 : undef, @private[4,5]], [0600, 0, 0], "$label run $run $type private ownership and mode");
|
||||
is_deeply([defined($public[2]) ? $public[2] & 0777 : undef, $public[4]], [0644, 0], "$label run $run $type public ownership and mode");
|
||||
}
|
||||
} else {
|
||||
my @rsa = stat("$fixture/etc/ssh/ssh_host_rsa_key");
|
||||
is_deeply([$rsa[2] & 0777, @rsa[4,5]], [0640, 0, 4242], "$label run $run retains the legacy permission chain");
|
||||
}
|
||||
like(read_file("$fixture/services.log"), qr/^restart sshd(?:\.service)?$/m, "$label run $run reaches the service command boundary");
|
||||
}
|
||||
}
|
||||
done_testing();
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Cwd qw(abs_path);
|
||||
use File::Copy qw(copy);
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
plan skip_all => 'requires Linux root and mount/network namespaces' unless $^O eq 'linux' && $> == 0;
|
||||
plan skip_all => 'namespaces unavailable' if system('unshare -mn -- true >/dev/null 2>&1');
|
||||
plan skip_all => 'requires native rsyslogd, logger, ip and ss'
|
||||
if system('command -v rsyslogd logger ip ss >/dev/null');
|
||||
my $source = $ENV{XCAT_SYSLOG_SOURCE_ROOT} || abs_path("$FindBin::Bin/../..");
|
||||
my $tmp = tempdir(DIR => '/var/tmp', CLEANUP => !$ENV{XCAT_SYSLOG_KEEP});
|
||||
diag("fixtures: $tmp") if $ENV{XCAT_SYSLOG_KEEP};
|
||||
make_path("$tmp/bin");
|
||||
sub write_file {
|
||||
my ($path, $contents) = @_;
|
||||
open(my $fh, '>', $path) or die "$path: $!";
|
||||
print {$fh} $contents;
|
||||
close($fh) or die "$path: $!";
|
||||
}
|
||||
sub read_file {
|
||||
my ($path) = @_;
|
||||
return '' unless -f $path;
|
||||
open(my $fh, '<', $path) or die "$path: $!";
|
||||
local $/;
|
||||
return <$fh> // '';
|
||||
}
|
||||
for my $name (qw(syslog xcatlib.sh)) {
|
||||
copy("$source/xCAT/postscripts/$name", "$tmp/bin/$name") or die $!;
|
||||
}
|
||||
write_file("$tmp/bin/systemctl", <<'SH');
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >> "$XCAT_SYSLOG_FIXTURE/services.log"
|
||||
[ "$XCAT_SYSLOG_FAIL" = restart ] && exit 42
|
||||
[ "$1" = restart ] || exit 43
|
||||
if [ -s /run/rsyslog-test.pid ]; then
|
||||
kill "$(cat /run/rsyslog-test.pid)"
|
||||
for i in {1..50}; do [ -f /run/rsyslog-test.pid ] || break; sleep .1; done
|
||||
fi
|
||||
exec /usr/sbin/rsyslogd -i /run/rsyslog-test.pid -f /etc/rsyslog.conf
|
||||
SH
|
||||
write_file("$tmp/bin/logger", <<'SH');
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >> "$XCAT_SYSLOG_FIXTURE/logger.log"
|
||||
SH
|
||||
write_file("$tmp/bin/namespace", <<'SH');
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
mount --make-rprivate /
|
||||
mount --bind "$XCAT_SYSLOG_FIXTURE/etc" /etc
|
||||
mount --bind "$XCAT_SYSLOG_FIXTURE/log" /var/log
|
||||
mount --bind "$XCAT_SYSLOG_FIXTURE/run" /run
|
||||
mount --bind "$XCAT_SYSLOG_FIXTURE/state" /var/lib/rsyslog
|
||||
ip link set lo up
|
||||
trap 'for f in /run/*pid; do [ ! -f "$f" ] || kill "$(cat "$f")" 2>/dev/null || :; done' EXIT
|
||||
/usr/sbin/rsyslogd -i /run/upstream.pid -f "$XCAT_SYSLOG_FIXTURE/upstream.conf"
|
||||
for pass in 1 2; do
|
||||
set +e
|
||||
bash "$XCAT_SYSLOG_BIN/syslog" > "$XCAT_SYSLOG_FIXTURE/postscript-$pass.log" 2>&1
|
||||
rc=$?
|
||||
set -e
|
||||
echo "$rc" > "$XCAT_SYSLOG_FIXTURE/postscript-$pass.rc"
|
||||
cp /etc/rsyslog.conf "$XCAT_SYSLOG_FIXTURE/config-$pass"
|
||||
cp /etc/rsyslog.d/remote.conf "$XCAT_SYSLOG_FIXTURE/remote-$pass" 2>/dev/null || :
|
||||
[ "$rc" -eq 0 ] || break
|
||||
done
|
||||
set +e
|
||||
/usr/sbin/rsyslogd -N1 -f /etc/rsyslog.conf > "$XCAT_SYSLOG_FIXTURE/parser.log" 2>&1
|
||||
echo "$?" > "$XCAT_SYSLOG_FIXTURE/parser.rc"
|
||||
set -e
|
||||
ss -H -lun 'sport = :514' > "$XCAT_SYSLOG_FIXTURE/udp-514"
|
||||
ss -H -ltn 'sport = :514' > "$XCAT_SYSLOG_FIXTURE/tcp-514"
|
||||
if [ -s "$XCAT_SYSLOG_FIXTURE/udp-514" ]; then
|
||||
/usr/bin/logger -n 127.0.0.1 -P 514 -d -t xcat-syslog-test 'xcat-native-udp-proof'
|
||||
fi
|
||||
if [ -s "$XCAT_SYSLOG_FIXTURE/tcp-514" ]; then
|
||||
/usr/bin/logger -n 127.0.0.1 -P 514 -T -t xcat-syslog-test 'xcat-native-tcp-proof'
|
||||
fi
|
||||
if [ "$XCAT_SYSLOG_ROLE" = cn ]; then
|
||||
/usr/bin/logger -n 127.0.0.1 -P 1515 -d -t xcat-syslog-test 'xcat-native-cn-proof'
|
||||
fi
|
||||
for i in {1..30}; do
|
||||
if [ "$XCAT_SYSLOG_ROLE" = cn ]; then
|
||||
grep -q xcat-native-cn-proof /var/log/upstream 2>/dev/null && break
|
||||
else
|
||||
grep -q xcat-native-tcp-proof /var/log/messages /var/log/upstream 2>/dev/null && break
|
||||
fi
|
||||
sleep .1
|
||||
done
|
||||
SH
|
||||
chmod 0755, "$tmp/bin/$_" for qw(systemctl logger namespace);
|
||||
my $default = read_file('/etc/rsyslog.conf');
|
||||
my $minimal = "global(workDirectory=\"/var/lib/rsyslog\")\ninclude(file=\"/etc/rsyslog.d/*.conf\" mode=\"optional\")\n*.info /var/log/messages\n";
|
||||
my $modern = "module(\n load=\"imudp\"\n)\ninput(\n address=\"127.0.0.1\"\n port=\"514\"\n type=\"imudp\"\n)\nmodule(load=\"imtcp\")\ninput(type=\"imtcp\" address=\"127.0.0.1\" port=\"514\")\n";
|
||||
my $legacy = "\$ModLoad imudp\n\$UDPServerRun 514\n\$ModLoad imtcp\n\$InputTCPServerRun 514\n";
|
||||
my @cases = (
|
||||
['native default MN', 'mn', 'openeuler24.03', $default],
|
||||
['native default SN local', 'snlocal', 'openeuler24.03', $default],
|
||||
['native default SN forwarding', 'snforward', 'openeuler24.03', $default],
|
||||
['native CN forwarding', 'cn', 'openeuler24.03', $minimal],
|
||||
['native os-release fallback', 'mn', '', $minimal],
|
||||
['native nested modern admin', 'mn', 'openeuler24.03', $minimal, $modern],
|
||||
['native same-line admin', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\") input(type=\"imudp\" port=\"514\")\nmodule(load=\"imtcp\") input(type=\"imtcp\" port=\"514\")\n"],
|
||||
['native quoted close admin', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\")\ninput(type=\"imudp\" name=\"admin)receiver\" port=\"514\")\nmodule(load=\"imtcp\")\ninput(type=\"imtcp\" port=\"514\")\n"],
|
||||
['native quoted attribute data', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\")\ninput(type=\"imudp\" name=\"type='imudp',port='514'\" port=\"1515\")\nmodule(load=\"imtcp\")\ninput(type=\"imtcp\" port=\"514\")\n"],
|
||||
['native legacy admin', 'snlocal', 'openeuler24.03', $minimal, $legacy],
|
||||
['native module without listener', 'mn', 'openeuler24.03', $minimal, "module(load=\"imudp\")\nmodule(load=\"imtcp\")\n"],
|
||||
['native commented admin', 'mn', 'openeuler24.03', $minimal, join('', map { "#$_\n" } split /\n/, $modern)],
|
||||
['native block comments', 'mn', 'openeuler24.03', $minimal, "/*\n$modern*/\n"],
|
||||
['legacy commented examples', 'mn', 'rhels9.6', $minimal . join('', map { "#$_\n" } split /\n/, $legacy)],
|
||||
['legacy no examples', 'mn', 'rhels9.6', $minimal],
|
||||
['native invalid configuration', 'mn', 'openeuler24.03', $minimal . "invalid_rsyslog_directive()\n", '', 'parser'],
|
||||
['native restart failure', 'mn', 'openeuler24.03', $minimal, '', 'restart'],
|
||||
);
|
||||
my $number = 0;
|
||||
for my $case (@cases) {
|
||||
my ($name, $role, $osver, $config, $admin, $failure) = @$case;
|
||||
$admin //= ''; $failure //= '';
|
||||
my $fixture = "$tmp/" . ++$number;
|
||||
make_path(map { "$fixture/$_" } qw(etc/rsyslog.d etc/admin log run state));
|
||||
write_file("$fixture/etc/os-release", 'ID="' . ($osver =~ /^rhels/ ? 'rhel' : 'openEuler') . "\"\n");
|
||||
write_file("$fixture/etc/xCATMN", '') if $role eq 'mn';
|
||||
if ($admin ne '') {
|
||||
write_file("$fixture/etc/rsyslog.d/admin.conf", "include(file=\"/etc/admin/receiver.conf\")\n");
|
||||
write_file("$fixture/etc/admin/receiver.conf", $admin);
|
||||
}
|
||||
if ($role eq 'cn') {
|
||||
write_file("$fixture/etc/rsyslog.d/admin.conf", "module(load=\"imudp\")\ninput(type=\"imudp\" port=\"1515\")\n");
|
||||
}
|
||||
write_file("$fixture/etc/rsyslog.conf", $config);
|
||||
write_file("$fixture/upstream.conf", "module(load=\"imudp\")\ninput(type=\"imudp\" port=\"1514\")\n*.* /var/log/upstream\n");
|
||||
local %ENV = (%ENV, PATH => "$tmp/bin:$ENV{PATH}", XCAT_SYSLOG_FIXTURE => $fixture,
|
||||
XCAT_SYSLOG_BIN => "$tmp/bin", XCAT_SYSLOG_FAIL => $failure, XCAT_SYSLOG_ROLE => $role,
|
||||
OSVER => $osver, NTYPE => ($role =~ /^sn/ ? 'service' : 'compute'),
|
||||
SVLOGLOCAL => ($role eq 'snlocal' ? 1 : 0), MASTER => '127.0.0.1:1514', SYSLOG => '');
|
||||
is(system('unshare', '-mn', '--', "$tmp/bin/namespace"), 0, "$name: isolated driver completed");
|
||||
my $rc = read_file("$fixture/postscript-1.rc");
|
||||
chomp $rc;
|
||||
if ($failure) {
|
||||
isnt($rc, 0, "$name: failure reaches postscript exit");
|
||||
is(read_file("$fixture/logger.log"), '', "$name: no success log");
|
||||
is(read_file("$fixture/services.log"), '', "$name: invalid configuration is not restarted") if $failure eq 'parser';
|
||||
next;
|
||||
}
|
||||
is($rc, 0, "$name: first postscript succeeds");
|
||||
my $repeat = read_file("$fixture/postscript-2.rc");
|
||||
chomp $repeat;
|
||||
is($repeat, 0, "$name: repeat succeeds");
|
||||
is(read_file("$fixture/config-2"), read_file("$fixture/config-1"), "$name: main configuration is repeatable");
|
||||
is(read_file("$fixture/remote-2"), read_file("$fixture/remote-1"), "$name: forwarding configuration is repeatable");
|
||||
is(0 + read_file("$fixture/parser.rc"), 0, "$name: native parser accepts result");
|
||||
is(read_file("$fixture/etc/admin/receiver.conf"), $admin, "$name: administrator include preserved");
|
||||
if ($name =~ /^native (nested modern|same-line|quoted close|legacy) admin$/) {
|
||||
unlike(read_file("$fixture/config-2"), qr/^(?:module|input)\(/m,
|
||||
"$name: existing administrator receivers need no duplicate declarations");
|
||||
}
|
||||
my $receives = $role ne 'cn' && $name ne 'legacy no examples';
|
||||
for my $protocol (qw(udp tcp)) {
|
||||
is(!!length(read_file("$fixture/$protocol-514")), !!$receives, "$name: $protocol listener matches role");
|
||||
if ($receives) {
|
||||
my $destination = $role eq 'snforward' ? 'upstream' : 'messages';
|
||||
like(read_file("$fixture/log/$destination"), qr/xcat-native-$protocol-proof/, "$name: real $protocol traffic reaches $destination");
|
||||
}
|
||||
}
|
||||
if ($role eq 'cn') {
|
||||
like(read_file("$fixture/log/upstream"), qr/xcat-native-cn-proof/, "$name: real CN traffic forwards");
|
||||
}
|
||||
my $remote = read_file("$fixture/etc/rsyslog.d/remote.conf");
|
||||
if ($role eq 'cn' || $role eq 'snforward') {
|
||||
like($remote, qr/^\*\.\* \@127\.0\.0\.1:1514$/m, "$name: master forwarding retained");
|
||||
} else {
|
||||
unlike($remote, qr/^\*\.\* \@/m, "$name: local logs are not forwarded");
|
||||
}
|
||||
}
|
||||
done_testing();
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Cwd qw(abs_path);
|
||||
use File::Copy qw(copy);
|
||||
use File::Path qw(make_path);
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
plan skip_all => 'requires Linux root and mount/network namespaces' unless $^O eq 'linux' && $> == 0;
|
||||
plan skip_all => 'namespaces unavailable' if system('unshare -mn -- true >/dev/null 2>&1');
|
||||
plan skip_all => 'requires native rsyslogd, systemctl, systemd-detect-virt and chroot'
|
||||
if system('command -v rsyslogd systemctl systemd-detect-virt chroot >/dev/null');
|
||||
my $source = $ENV{XCAT_SYSLOG_SOURCE_ROOT} || abs_path("$FindBin::Bin/../..");
|
||||
my $tmp = tempdir(DIR => '/var/tmp', CLEANUP => !$ENV{XCAT_SYSLOG_KEEP});
|
||||
diag("fixtures: $tmp") if $ENV{XCAT_SYSLOG_KEEP};
|
||||
make_path("$tmp/bin");
|
||||
sub write_file {
|
||||
my ($path, $text) = @_;
|
||||
open(my $fh, '>', $path) or die "$path: $!";
|
||||
print {$fh} $text;
|
||||
close($fh) or die "$path: $!";
|
||||
}
|
||||
sub read_file {
|
||||
my ($path) = @_;
|
||||
return '' unless -f $path;
|
||||
open(my $fh, '<', $path) or die "$path: $!";
|
||||
local $/;
|
||||
return <$fh> // '';
|
||||
}
|
||||
for my $name (qw(syslog xcatlib.sh)) {
|
||||
copy("$source/xCAT/postscripts/$name", "$tmp/bin/$name") or die $!;
|
||||
}
|
||||
write_file("$tmp/bin/systemctl", <<'SH');
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >> "/evidence/services-$XCAT_SYSLOG_PHASE.log"
|
||||
/usr/bin/systemctl "$@" > "/evidence/systemctl-$XCAT_SYSLOG_PHASE.log" 2>&1
|
||||
rc=$?
|
||||
echo "$rc" > "/evidence/systemctl-$XCAT_SYSLOG_PHASE.rc"
|
||||
cat "/evidence/systemctl-$XCAT_SYSLOG_PHASE.log"
|
||||
exit "$rc"
|
||||
SH
|
||||
write_file("$tmp/bin/logger", <<'SH');
|
||||
#!/bin/bash
|
||||
printf '%s\n' "$*" >> /evidence/logger.log
|
||||
SH
|
||||
write_file("$tmp/bin/run", <<'SH');
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
mount --make-rprivate /
|
||||
root="$XCAT_SYSLOG_FIXTURE/root"
|
||||
for path in usr bin sbin lib lib64; do
|
||||
[ -d "/$path" ] || continue
|
||||
mount --bind "/$path" "$root/$path"
|
||||
mount -o remount,bind,ro "$root/$path"
|
||||
done
|
||||
mount --bind "$XCAT_SYSLOG_BIN" "$root/postscripts"
|
||||
mount -o remount,bind,ro "$root/postscripts"
|
||||
mount --bind "$XCAT_SYSLOG_FIXTURE/evidence" "$root/evidence"
|
||||
mount -t proc -o ro proc "$root/proc"
|
||||
mount -t tmpfs -o mode=755,nosuid tmpfs "$root/dev"
|
||||
touch "$root/dev/null"
|
||||
mount --bind /dev/null "$root/dev/null"
|
||||
set +e
|
||||
chroot "$root" /usr/bin/systemd-detect-virt --quiet --chroot > "$XCAT_SYSLOG_FIXTURE/evidence/detect.log" 2>&1
|
||||
echo "$?" > "$XCAT_SYSLOG_FIXTURE/evidence/detect.rc"
|
||||
chroot "$root" /usr/bin/env PATH=/postscripts:/usr/sbin:/usr/bin:/sbin:/bin XCAT_SYSLOG_PHASE=baseline \
|
||||
/bin/bash -c '. /postscripts/xcatlib.sh; restartservice syslog' > "$XCAT_SYSLOG_FIXTURE/evidence/helper.log" 2>&1
|
||||
echo "$?" > "$XCAT_SYSLOG_FIXTURE/evidence/helper.rc"
|
||||
for pass in 1 2; do
|
||||
chroot "$root" /usr/bin/env PATH=/postscripts:/usr/sbin:/usr/bin:/sbin:/bin XCAT_SYSLOG_PHASE=postscript \
|
||||
/bin/bash /postscripts/syslog > "$XCAT_SYSLOG_FIXTURE/evidence/postscript-$pass.log" 2>&1
|
||||
rc=$?
|
||||
echo "$rc" > "$XCAT_SYSLOG_FIXTURE/evidence/postscript-$pass.rc"
|
||||
[ "$rc" -eq 0 ] || break
|
||||
done
|
||||
chroot "$root" /usr/sbin/rsyslogd -N1 -f /etc/rsyslog.conf > "$XCAT_SYSLOG_FIXTURE/evidence/parser.log" 2>&1
|
||||
echo "$?" > "$XCAT_SYSLOG_FIXTURE/evidence/parser.rc"
|
||||
exit 0
|
||||
SH
|
||||
chmod 0755, "$tmp/bin/$_" for qw(systemctl logger run);
|
||||
my $minimal = "global(workDirectory=\"/var/lib/rsyslog\")\ninclude(file=\"/etc/rsyslog.d/*.conf\" mode=\"optional\")\n*.info /var/log/messages\n";
|
||||
for my $case (['mn', 0], ['snlocal', 0], ['snforward', 0], ['cn', 0], ['mn', 1], ['cn', 1]) {
|
||||
my ($role, $invalid) = @$case;
|
||||
my $name = "$role " . ($invalid ? 'invalid' : 'valid') . ' native chroot';
|
||||
my $fixture = "$tmp/$role-$invalid";
|
||||
make_path("$fixture/evidence", map { "$fixture/root/$_" }
|
||||
qw(usr bin sbin lib lib64 postscripts evidence proc dev run tmp etc/rsyslog.d var/log var/lib/rsyslog));
|
||||
copy('/etc/ld.so.cache', "$fixture/root/etc/ld.so.cache") or die $!;
|
||||
write_file("$fixture/root/etc/os-release", "ID=openEuler\n");
|
||||
write_file("$fixture/root/etc/xCATMN", '') if $role eq 'mn';
|
||||
write_file("$fixture/root/etc/rsyslog.conf", $minimal . ($invalid ? "invalid_rsyslog_directive()\n" : ''));
|
||||
local %ENV = (%ENV, XCAT_SYSLOG_FIXTURE => $fixture, XCAT_SYSLOG_BIN => "$tmp/bin",
|
||||
OSVER => ($ENV{XCAT_SYSLOG_OSVER} || 'openeuler24.03'), NTYPE => ($role =~ /^sn/ ? 'service' : 'compute'),
|
||||
SVLOGLOCAL => ($role eq 'snlocal' ? 1 : 0), MASTER => '127.0.0.1:1514', SYSLOG => '');
|
||||
is(system('unshare', '-mn', '--', "$tmp/bin/run"), 0, "$name: isolated driver completed");
|
||||
my $evidence = "$fixture/evidence";
|
||||
is(read_file("$evidence/detect.rc"), "0\n", "$name: native helper detects the actual chroot");
|
||||
is(read_file("$evidence/systemctl-baseline.rc"), "0\n", "$name: actual systemctl defers successfully");
|
||||
like(read_file("$evidence/systemctl-baseline.log"), qr/Running in chroot,\s*ignoring/i,
|
||||
"$name: native systemctl identifies service deferral");
|
||||
is(read_file("$evidence/helper.rc"), "1\n", "$name: shared restart helper retains its existing contract");
|
||||
is(read_file("$evidence/services-postscript.log"), '', "$name: postscript does not attempt service activation");
|
||||
if ($invalid) {
|
||||
isnt(read_file("$evidence/postscript-1.rc"), "0\n", "$name: parser failure reaches the postscript exit");
|
||||
isnt(read_file("$evidence/parser.rc"), "0\n", "$name: actual native parser rejects the configuration");
|
||||
is(read_file("$evidence/logger.log"), '', "$name: no success log follows invalid configuration");
|
||||
} else {
|
||||
is(read_file("$evidence/postscript-1.rc"), "0\n", "$name: configuration completes successfully");
|
||||
is(read_file("$evidence/postscript-2.rc"), "0\n", "$name: repeated configuration succeeds");
|
||||
is(read_file("$evidence/parser.rc"), "0\n", "$name: actual native parser accepts the result");
|
||||
like(read_file("$evidence/logger.log"), qr/rsyslog version 8 setup/, "$name: completed configuration is logged");
|
||||
}
|
||||
}
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user