2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 15:18:19 +00:00
Files
xcat-core/xCAT-test/native/postinstall_bootstrap.t
T
Vinícius Ferrão f4f596537e test(postscripts): exercise rendered postinit service policy
Run complete rendered installers and their emitted postboot scripts in
private Linux filesystems. Cover service selection, flag aliases, defaults,
debug output, ordering and failures, and select the test in CI.
2026-10-10 02:41:02 -03:00

174 lines
8.8 KiB
Perl

#!/usr/bin/env perl
use strict;
use warnings;
use Capture::Tiny qw(capture);
use File::Copy qw(copy);
use File::Path qw(make_path);
use File::Slurper qw(read_binary write_binary);
use File::Temp qw(tempdir);
use FindBin;
use lib "$FindBin::Bin/../lib";
use Test::More;
use XCAT::Test::File qw(repo_path);
plan skip_all => 'installer execution requires Linux' unless $^O eq 'linux';
die 'Install bubblewrap to run this test' unless -x '/usr/bin/bwrap';
sub executable {
my ($path, $contents) = @_;
write_binary($path, "#!/bin/sh\n$contents");
chmod 0755, $path or die "Cannot chmod $path: $!";
return;
}
sub fixture {
my $root = tempdir(CLEANUP => 1);
make_path(map { "$root/$_" } qw(bin db opt/xcat etc/systemd/system/multi-user.target.wants
etc/init.d etc/rc2.d etc/rc.d/rc3.d etc/rc.d/rc4.d etc/rc.d/rc5.d
xcatpost log/xcat boot));
write_binary("$root/etc/hosts", "127.0.0.1 localhost\n");
executable("$root/bin/service-control", <<'SH');
name=${0##*/}
[ "$*" != --version ] || exit 0
printf '%s|%s\n' "$name" "$*" >>/fixture/events
case "$name:$*" in
systemctl:disable*|chkconfig:*off|update-rc.d:*remove)
[ "${SERVICE_RC:-0}" -ne 0 ] || touch /fixture/disabled
exit "${SERVICE_RC:-0}"
;;
esac
SH
symlink('service-control', "$root/bin/$_") or die $!
for qw(systemctl chkconfig update-rc.d);
executable("$root/bin/download", <<'SH');
case "$*" in
*mypostscript.node*) cp /fixture/downloaded /xcatpost/mypostscript.node ;;
esac
SH
symlink('download', "$root/bin/$_") or die $! for qw(wget curl);
executable("$root/bin/quiet", 'exit 0' . "\n");
symlink('quiet', "$root/bin/$_") or die $!
for qw(logger sleep ping updateflag.awk);
executable("$root/bin/ip", "printf '1: eth0: link/ether 52:54:00:12:34:56\\n'\n");
write_binary("$root/xcatpost/xcatlib.sh", <<'SH');
msgutil_r() { printf 'message|%s|%s|%s\n' "$1" "$2" "$3" >>/fixture/events; }
SH
write_binary("$root/downloaded", <<'SH');
MASTER=192.0.2.1
MASTER_IP='192.0.2.1'
OSVER='rhels8'
RUNBOOTSCRIPTS='no'
NODESTATUS='no'
XCATDEBUGMODE='0'
SH
return $root;
}
sub isolated {
my ($root, $stage, $environment, @command) = @_;
my @args = ('/usr/bin/bwrap', '--unshare-all', '--die-with-parent', '--new-session',
'--tmpfs', '/', '--proc', '/proc', '--dev', '/dev', '--tmpfs', '/tmp',
'--tmpfs', '/run', '--bind', $root, '/fixture', '--chdir', '/fixture',
'--setenv', 'PATH', '/fixture/bin:/usr/bin:/bin:/usr/sbin:/sbin',
'--setenv', 'LC_ALL', 'C');
if ($stage eq 'render') {
push @args, '--ro-bind', repo_path('xCAT/postscripts'), '/install/postscripts',
'--ro-bind', repo_path('.'), repo_path('.');
}
for my $directory (qw(/usr /bin /sbin /lib /lib64)) {
if (-l $directory) {
push @args, '--symlink', readlink($directory), $directory;
} elsif (-d $directory) {
push @args, '--ro-bind', $directory, $directory;
}
}
push @args, '--bind', "$root/$_", "/$_" for qw(opt etc xcatpost boot);
push @args, '--bind', "$root/log", '/var/log';
push @args, '--ro-bind', '/etc/alternatives', '/etc/alternatives' if -d '/etc/alternatives';
push @args, '--setenv', $_, $environment->{$_} for sort keys %$environment;
local %ENV = (PATH => '/usr/bin:/bin', LC_ALL => 'C');
my ($out, $err, $status) = capture { system(@args, @command) };
return ($status, $out . $err);
}
my @cases = (
{name => 'disabled', run => 'no', status => 'no', disable => 1},
{name => 'node reporting', run => 'no', status => 'yes', ubuntu_only => 1},
{name => 'empty status defaults enabled', run => 'no', status => '', ubuntu_only => 1},
{name => 'run enabled', run => 'yes', status => 'no'},
{name => 'numeric run enabled', run => '1', status => 'no'},
{name => 'short run enabled', run => 'Y', status => 'no'},
{name => 'numeric node enabled', run => 'no', status => '1', ubuntu_only => 1},
{name => 'short node enabled', run => 'no', status => 'Y', ubuntu_only => 1},
{name => 'uppercase flags', run => 'NO', status => 'NO', disable => 1},
{name => 'debug one', run => 'no', status => 'no', disable => 1, debug => 1},
{name => 'debug two', run => 'no', status => 'no', disable => 1, debug => 2},
{name => 'debug while retained', run => 'yes', status => 'no', debug => 1},
{name => 'service failure', run => 'no', status => 'no', disable => 1, service_rc => 5},
{name => 'postscript failure', run => 'no', status => 'no', disable => 1, post_rc => 7},
{name => 'missing node file', run => 'no', status => 'no', no_node => 1, ubuntu_only => 1},
{name => 'missing postboot file', no_post => 1, disable => 1},
);
for my $owner (qw(post.xcat post.xcat.ng post.xcat.rhels10)) {
my $build = fixture();
my ($status, $output) = isolated($build, 'render', {}, $^X,
repo_path('xCAT-test/native/fixtures/postinstall-render.pl'), $owner);
is($status, 0, "$owner renders with the production renderer") or diag($output);
($status, $output) = isolated($build, 'client', {}, '/bin/bash', '-n', '/fixture/installer');
is($status, 0, "$owner rendered installer parses") or diag($output);
($status, $output) = isolated($build, 'client', {}, '/bin/bash', '/fixture/installer');
is($status, 0, "$owner executes the complete installer script") or diag($output);
ok(-x "$build/opt/xcat/xcatinstallpost", "$owner emits the executable postboot script");
($status, $output) = isolated($build, 'client', {}, '/bin/bash', '-n', '/opt/xcat/xcatinstallpost');
is($status, 0, "$owner emitted script parses") or diag($output);
for my $os (qw(rhels8 ubuntu20.04)) {
for my $case (@cases) {
subtest "$owner $os $case->{name}" => sub {
my $root = fixture();
copy("$build/opt/xcat/xcatinstallpost", "$root/opt/xcat/xcatinstallpost") or die $!;
my $run = $case->{run} // 'no';
my $node = $case->{status} // 'no';
my $debug = $case->{debug} // 0;
my $post_rc = $case->{post_rc} // 0;
write_binary("$root/xcatpost/mypostscript", "NODESTATUS='$node'\n") unless $case->{no_node};
unless ($case->{no_post}) {
write_binary("$root/xcatpost/mypostscript.post",
"OSVER='$os'\nRUNBOOTSCRIPTS='$run'\nXCATDEBUGMODE='$debug'\n"
. "MASTER_IP='192.0.2.2'\nMASTER='192.0.2.2'\nNODE='node'\n"
. "MACADDRESS='52:54:00:12:34:56'\n"
. "printf 'postboot|%s\\n' \"\$(test -f /fixture/disabled && echo disabled || echo enabled)\" >>/fixture/events\n"
. "exit $post_rc\n");
}
write_binary("$root/opt/xcat/xcatinfo", "XCATSERVER=192.0.2.1\n");
my ($rc, $text) = isolated($root, 'client', {SERVICE_RC => $case->{service_rc} // 0},
'/bin/bash', '/opt/xcat/xcatinstallpost');
is($rc, 0, 'preserves the postboot runner exit status') or diag($text);
my $events = -e "$root/events" ? scalar read_binary("$root/events") : '';
note($events) if $ENV{XCAT_POST_TEST_TRACE};
my $ubuntu = !$case->{no_post} && $owner eq 'post.xcat' && $os =~ /^ubuntu/;
my $disable = $case->{disable} || ($ubuntu && $case->{ubuntu_only});
my $command = $owner ne 'post.xcat' ? 'systemctl|disable xcatpostinit1.service'
: $ubuntu ? 'update-rc.d|-f xcatpostinit1 remove' : 'chkconfig|xcatpostinit1 off';
my @commands = grep { /^(?:systemctl|chkconfig|update-rc.d)\|/ } split /\n/, $events;
is_deeply(\@commands, $disable ? [$command] : [], 'uses the caller-specific service policy');
unless ($case->{no_post}) {
my $state = $disable && !$case->{service_rc} ? 'disabled' : 'enabled';
like($events, qr/^postboot\|$state$/m, 'postscript observes the service state');
if ($disable) {
ok(index($events, $command) < index($events, 'postboot|'),
'disables before executing the postscript');
}
my @messages = grep { /^message\|.*\|debug\|(?:service xcatpostinit1 disabled|systemctl disable|update-rc.d)/ }
split /\n/, $events;
my $message = $owner ne 'post.xcat' ? 'systemctl disable xcatpostinit1.service'
: $ubuntu ? 'update-rc.d -f xcatpostinit1 remove' : 'service xcatpostinit1 disabled';
is_deeply(\@messages, $debug && ($ubuntu || $disable)
? ["message|192.0.2.2|debug|$message"] : [], 'preserves debug output and master selection');
}
};
}
}
}
done_testing();