mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-20 22:55:40 +00:00
test(imgport): cover the boot files after a failed definition write
Each failure of the definition write runs over a published image, an image without a checksum, a new image, or a statelite image. The previous root image, checksum, metainfo, kernel and ramdisk must be back, and the files of a new image must be gone. When a row does not go back, also after a changed rootimgdir, the new files must stay. The publication lock must be held during the writes and released after them. A row written before its call failed must keep the new files.
This commit is contained in:
@@ -8,7 +8,7 @@ use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
|
||||
use Digest::SHA qw(sha256_hex);
|
||||
use File::Path qw(make_path);
|
||||
use File::Slurper qw(read_binary write_text);
|
||||
use File::Slurper qw(read_binary write_binary write_text);
|
||||
use File::Temp qw(tempdir);
|
||||
use Test::More;
|
||||
use XCAT::Test::File qw(repo_path);
|
||||
@@ -98,6 +98,15 @@ sub committed {
|
||||
return $value;
|
||||
}
|
||||
|
||||
# The bytes of a file that a failed import can leave missing.
|
||||
sub content {
|
||||
my ($path) = @_;
|
||||
return -e $path ? read_binary($path) : undef;
|
||||
}
|
||||
|
||||
# packimage wrote the previous image with a checksum, unless it predates them, and a BitTorrent metainfo.
|
||||
my $metainfo = 'd4:infod6:pieces20:' . join('', map { chr } 0 .. 19) . 'ee';
|
||||
|
||||
# Imports a bundle over an image while the database refuses the writes to $case->{refuse}, or a call fails the way
|
||||
# $case->{fail} names.
|
||||
sub failed_import {
|
||||
@@ -105,8 +114,17 @@ sub failed_import {
|
||||
my $name = 'failed-' . ++$sequence;
|
||||
my $dest = "$fixture/install/netboot/$name";
|
||||
my $bundle = "$fixture/bundle-$sequence";
|
||||
my @boot = ($case->{statelite} ? 'rootimgtree.gz' : 'rootimg.cpio.gz', qw(kernel initrd-stateless.gz));
|
||||
make_path($dest, $bundle);
|
||||
write_text("$bundle/$_", "$_ from the bundle\n") for qw(rootimg.cpio.gz kernel initrd-stateless.gz);
|
||||
write_text("$bundle/$_", "$_ from the bundle\n") for @boot;
|
||||
if ($case->{previous}) {
|
||||
if (!$case->{statelite}) {
|
||||
write_text("$dest/rootimg.cpio.gz", $published);
|
||||
write_text("$dest/rootimg.cpio.gz.sha256", sha256_hex($published) . " rootimg.cpio.gz\n") if $case->{sums};
|
||||
write_binary("$dest/rootimg.cpio.gz.metainfo", $metainfo);
|
||||
}
|
||||
write_text("$dest/$_", "published $_\n") for qw(kernel initrd-stateless.gz);
|
||||
}
|
||||
xCAT::Table->new('osimage', -create => 1)->setAttribs({ imagename => $name }, { osvers => 'rhels9.5', provmethod => 'netboot', comments => 'previous image', ($case->{disabled} ? (disable => '1') : ()) });
|
||||
xCAT::Table->new('linuximage', -create => 1)->setAttribs({ imagename => $name }, { rootimgdir => $case->{rootimgdir} }) if $case->{rootimgdir};
|
||||
my $fail = $case->{fail} // '';
|
||||
@@ -118,7 +136,7 @@ sub failed_import {
|
||||
$shared = xCAT::Table->new('osimage', -create => 1, -autocommit => 0);
|
||||
$shared->setAttribs({ imagename => "$name-other" }, { osvers => 'other' });
|
||||
}
|
||||
my (@errors, @warnings, @messages, $placed);
|
||||
my (@errors, @warnings, @messages, $placed, $locked);
|
||||
{
|
||||
no warnings 'redefine';
|
||||
my ($set, $commit, $delete, $commit_failed) = (\&xCAT::Table::setAttribs, \&xCAT::Table::commit, \&xCAT::Table::delEntries, 0);
|
||||
@@ -133,20 +151,19 @@ sub failed_import {
|
||||
|
||||
# The database worker passes the exception of a failed call to the caller, and returns undef when it does not answer.
|
||||
local *xCAT::Table::setAttribs = sub {
|
||||
goto &$set if caller() ne 'xCAT_plugin::imgport';
|
||||
|
||||
# After this import wrote them, another import adopts the kit, another host changes the osimage row, or
|
||||
# another writer fills more of the linuximage row.
|
||||
xCAT::Table->new('kit')->setAttribs({ kitname => $kit }, { basename => 'mykit', version => '1.0' })
|
||||
if $case->{other_kit} && $_[0]{tabname} eq 'kitrepo' && caller() eq 'xCAT_plugin::imgport';
|
||||
xCAT::Table->new('osimage')->setAttribs({ imagename => $name }, { osvers => 'rhels9.7' })
|
||||
if $case->{other_host} && $_[0]{tabname} eq 'linuximage' && caller() eq 'xCAT_plugin::imgport';
|
||||
xCAT::Table->new('linuximage')->setAttribs({ imagename => $name }, { pkglist => '/install/custom/other.pkglist' })
|
||||
if $case->{filled} && $_[0]{tabname} eq 'kit' && caller() eq 'xCAT_plugin::imgport';
|
||||
if ($fail eq 'die in the linuximage restore' && $_[0]{tabname} eq 'linuximage' && caller() eq 'xCAT_plugin::imgport') {
|
||||
xCAT::Table->new('kit')->setAttribs({ kitname => $kit }, { basename => 'mykit', version => '1.0' }) if $case->{other_kit} && $_[0]{tabname} eq 'kitrepo';
|
||||
xCAT::Table->new('osimage')->setAttribs({ imagename => $name }, { osvers => 'rhels9.7' }) if $case->{other_host} && $_[0]{tabname} eq 'linuximage';
|
||||
xCAT::Table->new('linuximage')->setAttribs({ imagename => $name }, { pkglist => '/install/custom/other.pkglist' }) if $case->{filled} && $_[0]{tabname} eq 'kit';
|
||||
$locked //= -d "$dest/.packimage-publish";
|
||||
if ($fail eq 'die in the linuximage restore' && $_[0]{tabname} eq 'linuximage') {
|
||||
goto &$set if !$linuximage_writes++;
|
||||
die "the database is gone\n";
|
||||
}
|
||||
goto &$set if caller() ne 'xCAT_plugin::imgport' || $_[0]{tabname} ne 'osimage' || $fail !~ /^(?:die|no reply|die after the write|die in the restore|unreadable after the write)$/;
|
||||
goto &$set if $_[0]{tabname} ne 'osimage' || $fail !~ /^(?:die|no reply|die after the write|die in the restore|unreadable after the write)$/;
|
||||
if ($fail eq 'die in the restore') {
|
||||
goto &$set if !$osimage_writes++;
|
||||
die "the database is gone\n";
|
||||
@@ -168,13 +185,18 @@ sub failed_import {
|
||||
goto &$commit;
|
||||
};
|
||||
local *xCAT::Table::new = sub {
|
||||
return $shared if $shared && $_[1] eq 'osimage' && ((caller(1))[3] // '') eq 'xCAT_plugin::imgport::set_config';
|
||||
my $opener = (caller(1))[3] // '';
|
||||
return $shared if $shared && $_[1] eq 'osimage' && $opener eq 'xCAT_plugin::imgport::set_config';
|
||||
if ($fail eq 'die opening a table' && $_[1] eq 'kit' && $opener eq 'xCAT_plugin::imgport::set_config') {
|
||||
$locked //= -d "$dest/.packimage-publish";
|
||||
die "the database is gone\n";
|
||||
}
|
||||
goto &$new;
|
||||
};
|
||||
$placed = eval {
|
||||
xCAT_plugin::imgport::place_image(
|
||||
{
|
||||
rootimg => "$dest/rootimg.cpio.gz",
|
||||
($case->{statelite} ? (rootimgtree => "$dest/rootimgtree.gz") : (rootimg => "$dest/rootimg.cpio.gz")),
|
||||
kernel => "$dest/kernel",
|
||||
ramdisk => "$dest/initrd-stateless.gz",
|
||||
osimage => { imagename => $name, osvers => 'rhels9.6', osarch => 'x86_64', profile => 'compute', provmethod => 'netboot', comments => "operator's image", ($case->{enabled} ? (disable => '0') : ()) },
|
||||
@@ -189,40 +211,47 @@ sub failed_import {
|
||||
}
|
||||
my $free = table_free($case->{refuse} // 'osimage');
|
||||
$allow->();
|
||||
return { placed => $placed, dest => $dest, errors => join("\n", @errors), warnings => join("\n", @warnings), messages => join("\n", @messages),
|
||||
free => $free, osvers => committed('osimage', 'osvers', $name), rootimgdir => committed('linuximage', 'rootimgdir', $name),
|
||||
return { placed => $placed, errors => join("\n", @errors), warnings => join("\n", @warnings), messages => join("\n", @messages),
|
||||
dest => $dest, free => $free, locked => $locked, osvers => committed('osimage', 'osvers', $name),
|
||||
rootimgdir => committed('linuximage', 'rootimgdir', $name),
|
||||
disable => committed('osimage', 'disable', $name), comments => committed('osimage', 'comments', $name),
|
||||
pkglist => committed('linuximage', 'pkglist', $name), visible => scalar(xCAT::Table->new('osimage')->getAttribs({ imagename => $name }, 'osvers')), kitversion => committed('kit', 'version', "kit-$name", 'kitname'),
|
||||
other => committed('osimage', 'osvers', "$name-other") };
|
||||
pkglist => committed('linuximage', 'pkglist', $name), visible => scalar(xCAT::Table->new('osimage')->getAttribs({ imagename => $name }, 'osvers')), kitversion => committed('kit', 'version', "kit-$name", 'kitname'), other => committed('osimage', 'osvers', "$name-other") };
|
||||
}
|
||||
|
||||
my $refused = qr/Cannot write the (\w+) table: .*the database refuses the write/;
|
||||
for my $case (
|
||||
{ refuse => 'osimage', osvers => 'rhels9.5' },
|
||||
{ refuse => 'linuximage', osvers => 'rhels9.5', rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ refuse => 'kit', kit => 1, osvers => 'rhels9.5', rootimgdir => '/install/netboot/previous', rows => qr/Put back the previous linuximage row of imagename=/ },
|
||||
{ refuse => 'kit', kit => 1, osvers => 'rhels9.5', rows => qr/Removed the new linuximage row of imagename=/ },
|
||||
{ refuse => 'linuximage', disabled => 1, osvers => 'rhels9.5', rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ refuse => 'kitrepo', kit => 1, other_kit => 1, osvers => 'rhels9.5' },
|
||||
{ refuse => 'linuximage', other_host => 1, osvers => 'rhels9.7', changed => qr/The osimage row of imagename=\S+ changed after this import wrote it, so the change stays\./ },
|
||||
{ fail => 'die in the commit', osvers => 'rhels9.5', error => qr/Cannot commit the linuximage table: the database is gone/, rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ fail => 'die', pending => 1, osvers => 'rhels9.5', error => qr/Cannot write the osimage table: the database is gone/ },
|
||||
{ fail => 'no reply', osvers => 'rhels9.5', error => qr/Cannot write the osimage table: no reply from the database/ },
|
||||
{ fail => 'die in the restore', refuse => 'linuximage', osvers => 'rhels9.6', error => qr/The previous osimage row of imagename=\S+ is not back\./ },
|
||||
{ fail => 'change before the removal', refuse => 'kit', kit => 1, osvers => 'rhels9.5', after => '/install/netboot/other', error => qr/The previous linuximage row of imagename=\S+ is not back\./ },
|
||||
{ refuse => 'kit', kit => 1, filled => 1, osvers => 'rhels9.5', changed => qr/The linuximage row of imagename=\S+ changed after this import wrote it, so the change stays\./ },
|
||||
{ refuse => 'linuximage', enabled => 1, osvers => 'rhels9.5', rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ fail => 'die in the linuximage restore', refuse => 'kit', kit => 1, rootimgdir => '/install/netboot/previous', after => 'new', osvers => 'rhels9.5',
|
||||
{ refuse => 'osimage', previous => 1, sums => 1 },
|
||||
{ refuse => 'osimage', previous => 1, sums => 0 },
|
||||
{ refuse => 'osimage', previous => 0 },
|
||||
{ refuse => 'linuximage', previous => 1, sums => 1, rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ refuse => 'linuximage', previous => 1, statelite => 1 },
|
||||
{ refuse => 'kit', kit => 1, previous => 1, sums => 1, rootimgdir => '/install/netboot/previous', rows => qr/Put back the previous linuximage row of imagename=/ },
|
||||
{ refuse => 'kit', kit => 1, previous => 1, sums => 1, rows => qr/Removed the new linuximage row of imagename=/ },
|
||||
{ refuse => 'linuximage', disabled => 1, previous => 1, sums => 1, rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ refuse => 'kitrepo', kit => 1, other_kit => 1, previous => 1, sums => 1 },
|
||||
{ refuse => 'linuximage', other_host => 1, previous => 1, sums => 1, stay => 1, osvers => 'rhels9.7', changed => qr/The osimage row of imagename=\S+ changed after this import wrote it, so the change stays\./ },
|
||||
{ fail => 'die in the commit', previous => 1, sums => 1, error => qr/Cannot commit the linuximage table: the database is gone/, rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ fail => 'die', previous => 1, sums => 1, pending => 1, error => qr/Cannot write the osimage table: the database is gone/ },
|
||||
{ fail => 'no reply', previous => 1, sums => 1, error => qr/Cannot write the osimage table: no reply from the database/ },
|
||||
{ fail => 'die opening a table', previous => 1, sums => 1, error => qr/Cannot import the image: the database is gone/ },
|
||||
{ fail => 'die in the restore', refuse => 'linuximage', previous => 1, sums => 1, stay => 1, error => qr/The previous osimage row of imagename=\S+ is not back\./ },
|
||||
{ fail => 'change before the removal', refuse => 'kit', kit => 1, previous => 1, sums => 1, stay => 1, after => '/install/netboot/other', error => qr/The previous linuximage row of imagename=\S+ is not back\./ },
|
||||
{ refuse => 'kit', kit => 1, filled => 1, previous => 1, sums => 1, stay => 1, changed => qr/The linuximage row of imagename=\S+ changed after this import wrote it, so the change stays\./ },
|
||||
{ refuse => 'linuximage', enabled => 1, previous => 1, sums => 1, rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ fail => 'die in the linuximage restore', refuse => 'kit', kit => 1, rootimgdir => '/install/netboot/previous', after => 'new', previous => 1, sums => 1, stay => 1,
|
||||
error => qr/The previous linuximage row of imagename=\S+ is not back\./ },
|
||||
{ malformed => 1, osvers => 'rhels9.5', error => qr/Cannot write the image definition: /, rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ fail => 'unreadable after the write', osvers => 'rhels9.6', error => qr/The previous osimage row of imagename=\S+ is not back: the table cannot be read\./ },
|
||||
{ fail => 'die after the write', osvers => 'rhels9.6', imported => 1, warning => qr/The osimage table holds the new row, but its write reported: a notification failed/ },
|
||||
{ malformed => 1, previous => 1, sums => 1, error => qr/Cannot write the image definition: /, rows => qr/Put back the previous osimage row of imagename=/ },
|
||||
{ fail => 'unreadable after the write', previous => 1, sums => 1, stay => 1, osvers => 'rhels9.6',
|
||||
error => qr/The previous osimage row of imagename=\S+ is not back: the table cannot be read\./ },
|
||||
{ fail => 'die after the write', previous => 1, sums => 1, imported => 1, warning => qr/The osimage table holds the new row, but its write reported: a notification failed/ },
|
||||
) {
|
||||
my $label = join(', ', grep { $_ } ($case->{fail}, $case->{refuse} && "refused $case->{refuse} write", $case->{rootimgdir} && 'previous linuximage row',
|
||||
$case->{disabled} && 'disabled osimage row', $case->{other_kit} && 'kit of another import', $case->{other_host} && 'osimage row of another host',
|
||||
$case->{malformed} && 'kitrepo of an unexpected shape', $case->{filled} && 'linuximage row that another writer filled',
|
||||
$case->{enabled} && 'disable column that was NULL'));
|
||||
my $label = join(', ', grep { $_ } ($case->{fail}, $case->{refuse} && "refused $case->{refuse} write", $case->{statelite} && 'statelite image',
|
||||
!$case->{previous} && 'new image', $case->{previous} && !$case->{statelite} && !$case->{sums} && 'image without checksum',
|
||||
$case->{rootimgdir} && 'previous linuximage row', $case->{disabled} && 'disabled osimage row', $case->{other_kit} && 'kit of another import',
|
||||
$case->{other_host} && 'osimage row of another host', $case->{malformed} && 'kitrepo of an unexpected shape',
|
||||
$case->{filled} && 'linuximage row that another writer filled', $case->{enabled} && 'disable column that was NULL'));
|
||||
my $result = failed_import($case);
|
||||
my $dest = $result->{dest};
|
||||
if ($case->{imported}) {
|
||||
ok($result->{placed}, "$label: the import succeeds, as the row is written") or diag($result->{errors});
|
||||
like($result->{warnings}, $case->{warning}, "$label: the failure after the write is reported");
|
||||
@@ -237,7 +266,30 @@ for my $case (
|
||||
is($result->{kitversion}, '1.0', "$label: the kit that another import uses stays") if $case->{other_kit};
|
||||
ok($result->{free}, "$label: no transaction of the import stays open");
|
||||
is($result->{other}, 'other', "$label: the pending write of another request stays") if $case->{pending};
|
||||
is($result->{osvers}, $case->{osvers}, "$label: the osimage row is as expected");
|
||||
|
||||
# Another host publishes or imports this image only after the outcome of the definition is known.
|
||||
ok($result->{locked}, "$label: the publication lock is held while the definition is written");
|
||||
ok(!-e "$dest/.packimage-publish", "$label: the publication lock is released");
|
||||
like($result->{errors}, qr/The imported boot files stay, because the image definition has rows that did not go back\./, "$label: the kept boot files are reported") if $case->{stay};
|
||||
if ($case->{imported} || $case->{stay}) {
|
||||
is(content("$dest/$_"), "$_ from the bundle\n", "$label: the new $_ stays with the new image definition") for qw(rootimg.cpio.gz kernel initrd-stateless.gz);
|
||||
} else {
|
||||
if ($case->{statelite}) {
|
||||
ok(!-e "$dest/rootimgtree.gz", "$label: the root tree of the bundle is removed");
|
||||
} elsif ($case->{previous}) {
|
||||
is(content("$dest/rootimg.cpio.gz"), $published, "$label: the previous image is published again");
|
||||
if ($case->{sums}) {
|
||||
is(content("$dest/rootimg.cpio.gz.sha256"), sha256_hex($published) . " rootimg.cpio.gz\n", "$label: the previous checksum is back");
|
||||
} else {
|
||||
ok(!-e "$dest/rootimg.cpio.gz.sha256", "$label: the image has no checksum, as before");
|
||||
}
|
||||
is(content("$dest/rootimg.cpio.gz.metainfo"), $metainfo, "$label: the previous metainfo is back");
|
||||
} else {
|
||||
ok(!-e "$dest/$_", "$label: $_, which had no previous version, is removed") for qw(rootimg.cpio.gz rootimg.cpio.gz.sha256);
|
||||
}
|
||||
is(content("$dest/$_"), $case->{previous} ? "published $_\n" : undef, "$label: $_ is as it was") for qw(kernel initrd-stateless.gz);
|
||||
}
|
||||
is($result->{osvers}, $case->{osvers} // ($case->{imported} || ($case->{fail} // '') eq 'die in the restore' ? 'rhels9.6' : 'rhels9.5'), "$label: the osimage row is as expected");
|
||||
my $rootimgdir = ($case->{after} // '') eq 'new' ? $result->{dest} : $case->{after} // $case->{rootimgdir};
|
||||
is($result->{rootimgdir}, $rootimgdir, "$label: the linuximage row is as expected") if $case->{kit} && !$case->{filled};
|
||||
is($result->{pkglist}, '/install/custom/other.pkglist', "$label: the linuximage row that another writer filled stays") if $case->{filled};
|
||||
|
||||
Reference in New Issue
Block a user