mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-20 22:55:40 +00:00
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.
This commit is contained in:
@@ -500,16 +500,14 @@ sub run_bats_tests{
|
||||
|
||||
#--------------------------------------------------------
|
||||
# Fuction name: run_source_integration_tests
|
||||
# Description: Run the integration tests that read a file the Debian
|
||||
# packages do not install. They run against the pre-build copy
|
||||
# of the source tree, after install_xcat() put Apache on the
|
||||
# runner. A skip fails the phase: the runner has every
|
||||
# Description: Run source-only integration and native tests after package
|
||||
# installation. A skip fails the phase: the runner has every
|
||||
# prerequisite.
|
||||
# Attributes:
|
||||
# Return code: 0 all tests passed, 1 otherwise
|
||||
#--------------------------------------------------------
|
||||
sub run_source_integration_tests{
|
||||
my $cmd = "cd $unitsrc && prove -v xCAT-test/integration/wsapi_apache_access.t";
|
||||
my $cmd = "cd $unitsrc && prove -v xCAT-test/integration/wsapi_apache_access.t xCAT-test/native/postinstall_bootstrap.t";
|
||||
print "[run_source_integration_tests] running $cmd\n";
|
||||
my @output = runcmd("$cmd");
|
||||
print Dumper \@output;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../../perl-xCAT", "$FindBin::Bin/../../../xCAT-server/lib/perl";
|
||||
|
||||
$ENV{XCATROOT} = "$FindBin::Bin/../../../xCAT-server";
|
||||
$ENV{XCATCFG} = 'SQLite:/fixture/db';
|
||||
require xCAT::Table;
|
||||
require xCAT::Template;
|
||||
require xCAT::Postage;
|
||||
|
||||
my %site = (installdir => '/install', tftpdir => '/tftpboot', master => '192.0.2.1',
|
||||
xcatiport => 3002, httpport => 80, xcatdebugmode => 0, nodestatus => 0);
|
||||
for my $key (sort keys %site) {
|
||||
my $table = xCAT::Table->new('site', -create => 1) or die 'Cannot create site';
|
||||
$table->setAttribs({key => $key}, {value => $site{$key}});
|
||||
$table->close();
|
||||
}
|
||||
%::XCATSITEVALS = %site;
|
||||
for my $row (
|
||||
['nodelist', {groups => 'all'}],
|
||||
['nodetype', {os => 'rhels8', arch => 'x86_64', provmethod => 'install'}],
|
||||
['noderes', {xcatmaster => '192.0.2.1'}],
|
||||
) {
|
||||
my $table = xCAT::Table->new($row->[0], -create => 1) or die "Cannot create $row->[0]";
|
||||
$table->setAttribs({node => 'node'}, $row->[1]);
|
||||
$table->close();
|
||||
}
|
||||
my $error = xCAT::Template->subvars(
|
||||
"$ENV{XCATROOT}/share/xcat/install/scripts/$ARGV[0]", '/fixture/installer',
|
||||
'node', undef, '/install/media', 'rh', undef, {xcatmaster => '192.0.2.1'});
|
||||
die "Cannot render installer: $error" if $error;
|
||||
@@ -0,0 +1,173 @@
|
||||
#!/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();
|
||||
Reference in New Issue
Block a user