mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-21 16:39:30 +00:00
test(mknb): capture a Genesis copy failing without failing mknb
mknb stages the Genesis payload before building a netboot image, and those copies are the only point at which it learns that an installed Genesis image is unusable. The legacy branch runs two of them and keeps only the second exit status, so an unreadable root tree is invisible: mknb exits 0 having built an initramfs from nothing, and the node never boots with no error naming the cause. When the kernel copy is the one that fails, the message blames the root tree instead. Extract the staging decision as stage_genesis_payload, preserving today's behaviour exactly, so the outcome can be driven with an injected runner instead of a real Genesis tree. The test fails on this commit, 2 of 10: 'an unreadable root tree fails the step' and 'the failure names the kernel, not the root tree'. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> (cherry picked from commit 989deaa31eb1f3979a8db030d3ae8c404abda2fb)
This commit is contained in:
@@ -327,6 +327,41 @@ sub genesis_lzma_command {
|
||||
return;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 stage_genesis_payload
|
||||
|
||||
Descriptions:
|
||||
Copy the Genesis payload into place for mknb: for a legacy image the unpacked
|
||||
root tree and then the kernel, for an exported image the nbroot tree.
|
||||
|
||||
Extracted so the outcome can be driven directly. The copies are the only
|
||||
place mknb learns that an installed Genesis image is unusable, and a caller
|
||||
cannot tell WHICH copy failed from a single exit status.
|
||||
|
||||
Arguments:
|
||||
genesis_type, genesis_dir, tftpdir, arch, tempdir, and an optional run
|
||||
coderef used in place of system() by the tests.
|
||||
Returns:
|
||||
(rc, source) -- rc is the exit status of the copy that failed, and source
|
||||
names it, so the caller reports the file it could not read.
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub stage_genesis_payload {
|
||||
my (%a) = @_;
|
||||
my $run = $a{run} || sub { return system($_[0]); };
|
||||
my $rc;
|
||||
if (($a{genesis_type} // '') eq 'legacy') {
|
||||
$rc = $run->("shopt -s dotglob; GLOBIGNORE=\".:..\" cp -a $a{genesis_dir}/fs/* $a{tempdir}");
|
||||
$rc = $run->("cp -a $a{genesis_dir}/kernel $a{tftpdir}/xcat/genesis.kernel.$a{arch}");
|
||||
return ($rc, "$a{genesis_dir}/fs");
|
||||
}
|
||||
$rc = $run->("cp -a $a{genesis_dir}/nbroot/* $a{tempdir}");
|
||||
return ($rc, "$a{genesis_dir}/nbroot");
|
||||
}
|
||||
|
||||
sub process_request {
|
||||
my $request = shift;
|
||||
my $callback = shift;
|
||||
@@ -555,21 +590,13 @@ sub process_request {
|
||||
unless (-e "$tftpdir/xcat") {
|
||||
mkpath("$tftpdir/xcat");
|
||||
}
|
||||
my $rc;
|
||||
if ($genesis_type eq 'legacy') {
|
||||
$rc = system("shopt -s dotglob; GLOBIGNORE=\".:..\" cp -a $genesis_dir/fs/* $tempdir");
|
||||
$rc = system("cp -a $genesis_dir/kernel $tftpdir/xcat/genesis.kernel.$arch");
|
||||
$invisibletouch = 1;
|
||||
} else {
|
||||
$rc = system("cp -a $genesis_dir/nbroot/* $tempdir");
|
||||
}
|
||||
$invisibletouch = 1 if $genesis_type eq 'legacy';
|
||||
my ($rc, $failed_src) = stage_genesis_payload(
|
||||
genesis_type => $genesis_type, genesis_dir => $genesis_dir,
|
||||
tftpdir => $tftpdir, arch => $arch, tempdir => $tempdir);
|
||||
if ($rc) {
|
||||
system("rm -rf $tempdir");
|
||||
if ($invisibletouch) {
|
||||
$callback->({ error => ["Failed to copy $genesis_dir/fs contents"], errorcode => [1] });
|
||||
} else {
|
||||
$callback->({ error => ["Failed to copy $genesis_dir/nbroot contents"], errorcode => [1] });
|
||||
}
|
||||
$callback->({ error => ["Failed to copy $failed_src contents"], errorcode => [1] });
|
||||
return;
|
||||
}
|
||||
my $sshdir;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env perl
|
||||
# mknb stages the Genesis payload before it can build a netboot image. Those copies are the
|
||||
# only point at which mknb learns that an installed Genesis image is unusable, so a copy that
|
||||
# fails silently produces an initramfs built from nothing and an exit status of 0 -- the node
|
||||
# then never boots, with no error anywhere naming the cause.
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use Test::More;
|
||||
|
||||
BEGIN { $INC{'xCAT/Utils.pm'} = 1; $INC{'xCAT/MsgUtils.pm'} = 1;
|
||||
$INC{'xCAT/Table.pm'} = 1; $INC{'xCAT/NetworkUtils.pm'} = 1;
|
||||
$INC{'xCAT/TableUtils.pm'} = 1; $INC{'xCAT_monitoring/monitorctrl.pm'} = 1; }
|
||||
|
||||
require "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/mknb.pm";
|
||||
|
||||
can_ok('xCAT_plugin::mknb', 'stage_genesis_payload')
|
||||
or BAIL_OUT('mknb has no stage_genesis_payload to drive');
|
||||
|
||||
# Drive the routine with a runner that fails exactly one copy, so each assertion names the
|
||||
# copy it is about rather than the pair.
|
||||
sub stage {
|
||||
my (%opt) = @_;
|
||||
my @ran;
|
||||
my ($rc, $src) = xCAT_plugin::mknb::stage_genesis_payload(
|
||||
genesis_type => $opt{type} // 'legacy',
|
||||
genesis_dir => '/opt/xcat/share/xcat/netboot/genesis/x86_64',
|
||||
tftpdir => '/tftpboot',
|
||||
arch => 'x86_64',
|
||||
tempdir => '/tmp/scratch',
|
||||
run => sub {
|
||||
my ($cmd) = @_;
|
||||
push @ran, $cmd;
|
||||
return ($opt{fail} && $cmd =~ /$opt{fail}/) ? 256 : 0;
|
||||
},
|
||||
);
|
||||
return { rc => $rc, src => $src, ran => \@ran };
|
||||
}
|
||||
|
||||
# --- legacy: both copies must be able to fail the step -----------------------
|
||||
my $ok = stage();
|
||||
is($ok->{rc}, 0, 'a legacy image whose copies both succeed stages cleanly');
|
||||
is(scalar @{ $ok->{ran} }, 2, 'the legacy path copies the root tree and the kernel');
|
||||
|
||||
my $nofs = stage(fail => qr{/fs/\*});
|
||||
isnt($nofs->{rc}, 0, 'an unreadable root tree fails the step');
|
||||
like($nofs->{src}, qr{/fs$}, 'and the failure names the root tree');
|
||||
|
||||
my $nokernel = stage(fail => qr{/kernel });
|
||||
isnt($nokernel->{rc}, 0, 'a missing kernel fails the step');
|
||||
like($nokernel->{src}, qr{/kernel$}, 'and the failure names the kernel, not the root tree');
|
||||
|
||||
# --- exported (OpenEmbedded) path -------------------------------------------
|
||||
my $nonb = stage(type => 'exported', fail => qr{/nbroot/\*});
|
||||
isnt($nonb->{rc}, 0, 'an unreadable nbroot fails the step');
|
||||
like($nonb->{src}, qr{/nbroot$}, 'and the failure names nbroot');
|
||||
|
||||
my $oknb = stage(type => 'exported');
|
||||
is($oknb->{rc}, 0, 'an exported image whose copy succeeds stages cleanly');
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user