mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 07:08:12 +00:00
test(imgport): cover the image rows after a failed definition write
The database refuses the linuximage, kit or kitrepo write, the linuximage commit dies, or a kitrepo section of an unexpected shape dies, after earlier rows are written. The osimage row, also a disabled one, one with a quote in a value, or one whose disable column was NULL, and a previous linuximage row with its rootimgdir must be back, and a new linuximage row must be gone. A kit that another import uses and a row that another writer changed or filled must stay. A restore that fails, or a row that cannot be read back, must be reported.
This commit is contained in:
@@ -89,11 +89,11 @@ sub table_free {
|
||||
return $free;
|
||||
}
|
||||
|
||||
# A column of the row of an image that the database holds, without the transaction of the test process.
|
||||
# A column of a row that the database holds, also a disabled one, without the transaction of the test process.
|
||||
sub committed {
|
||||
my ($table, $column, $name) = @_;
|
||||
my ($table, $column, $name, $key) = @_;
|
||||
my $dbh = DBI->connect("dbi:SQLite:dbname=$fixture/db/$table.sqlite", '', '', { RaiseError => 1, PrintError => 0, sqlite_busy_timeout => 1000 });
|
||||
my ($value) = $dbh->selectrow_array("SELECT $column FROM $table WHERE imagename = ?", undef, $name);
|
||||
my ($value) = $dbh->selectrow_array("SELECT $column FROM $table WHERE " . ($key // 'imagename') . ' = ?', undef, $name);
|
||||
$dbh->disconnect;
|
||||
return $value;
|
||||
}
|
||||
@@ -107,7 +107,8 @@ sub failed_import {
|
||||
my $bundle = "$fixture/bundle-$sequence";
|
||||
make_path($dest, $bundle);
|
||||
write_text("$bundle/$_", "$_ from the bundle\n") for qw(rootimg.cpio.gz kernel initrd-stateless.gz);
|
||||
xCAT::Table->new('osimage', -create => 1)->setAttribs({ imagename => $name }, { osvers => 'rhels9.5', provmethod => 'netboot' });
|
||||
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} // '';
|
||||
my $allow = $case->{refuse} ? refuse_writes($case->{refuse}) : sub { };
|
||||
|
||||
@@ -120,16 +121,48 @@ sub failed_import {
|
||||
my (@errors, @warnings, @messages, $placed);
|
||||
{
|
||||
no warnings 'redefine';
|
||||
my ($set, $commit, $commit_failed) = (\&xCAT::Table::setAttribs, \&xCAT::Table::commit, 0);
|
||||
my ($set, $commit, $delete, $commit_failed) = (\&xCAT::Table::setAttribs, \&xCAT::Table::commit, \&xCAT::Table::delEntries, 0);
|
||||
my ($osimage_writes, $linuximage_writes, $osimage_written, $kit) = (0, 0, 0, "kit-$name");
|
||||
my $read = \&xCAT::Table::getAllEntries;
|
||||
|
||||
# The database stops answering reads of the osimage table after the write of the import.
|
||||
local *xCAT::Table::getAllEntries = sub {
|
||||
die "the database is gone\n" if $fail eq 'unreadable after the write' && $osimage_written && $_[0]{tabname} eq 'osimage';
|
||||
goto &$read;
|
||||
};
|
||||
|
||||
# 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' || $_[0]{tabname} ne 'osimage' || !$fail || $fail eq 'die in the commit';
|
||||
|
||||
# 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') {
|
||||
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)$/;
|
||||
if ($fail eq 'die in the restore') {
|
||||
goto &$set if !$osimage_writes++;
|
||||
die "the database is gone\n";
|
||||
}
|
||||
die "the database is gone\n" if $fail eq 'die';
|
||||
return undef if $fail eq 'no reply'; ## no critic (Subroutines::ProhibitExplicitReturnUndef)
|
||||
$set->(@_);
|
||||
$osimage_written = 1;
|
||||
die "a notification failed\n";
|
||||
};
|
||||
# Another writer changes the new linuximage row between the check of the restore and its removal.
|
||||
local *xCAT::Table::delEntries = sub {
|
||||
xCAT::Table->new('linuximage')->setAttribs({ imagename => $name }, { rootimgdir => '/install/netboot/other' })
|
||||
if $fail eq 'change before the removal' && $_[0]{tabname} eq 'linuximage' && caller() eq 'xCAT_plugin::imgport';
|
||||
goto &$delete;
|
||||
};
|
||||
local *xCAT::Table::commit = sub {
|
||||
die "the database is gone\n" if $fail eq 'die in the commit' && $_[0]{tabname} eq 'linuximage' && caller() eq 'xCAT_plugin::imgport' && !$commit_failed++;
|
||||
goto &$commit;
|
||||
@@ -144,8 +177,11 @@ sub failed_import {
|
||||
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' },
|
||||
osimage => { imagename => $name, osvers => 'rhels9.6', osarch => 'x86_64', profile => 'compute', provmethod => 'netboot', comments => "operator's image", ($case->{enabled} ? (disable => '0') : ()) },
|
||||
linuximage => { imagename => $name, rootimgdir => $dest },
|
||||
($case->{kit} ? (kit => { $kit => { kitname => $kit, basename => 'mykit', version => '1.0' } }) : ()),
|
||||
($case->{other_kit} ? (kitrepo => { "repo-$name" => { kitreponame => "repo-$name", kitname => $kit } }) : ()),
|
||||
($case->{malformed} ? (kitrepo => 'not a table') : ()),
|
||||
},
|
||||
$bundle, sub { push @errors, @{ $_[0]{error} || [] }; push @warnings, @{ $_[0]{warning} || [] }; push @messages, @{ $_[0]{data} || [] } });
|
||||
};
|
||||
@@ -153,21 +189,39 @@ sub failed_import {
|
||||
}
|
||||
my $free = table_free($case->{refuse} // 'osimage');
|
||||
$allow->();
|
||||
return { placed => $placed, errors => join("\n", @errors), warnings => join("\n", @warnings), messages => join("\n", @messages),
|
||||
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),
|
||||
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") };
|
||||
}
|
||||
|
||||
my $refused = qr/Cannot write the (\w+) table: .*the database refuses the write/;
|
||||
for my $case (
|
||||
{ refuse => 'osimage', osvers => 'rhels9.5' },
|
||||
{ refuse => 'linuximage' },
|
||||
{ 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 commit', error => qr/Cannot commit the linuximage table: the database is gone/ },
|
||||
{ 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',
|
||||
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/ },
|
||||
) {
|
||||
my $label = $case->{fail} // "refused $case->{refuse} write";
|
||||
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 $result = failed_import($case);
|
||||
if ($case->{imported}) {
|
||||
ok($result->{placed}, "$label: the import succeeds, as the row is written") or diag($result->{errors});
|
||||
@@ -175,10 +229,24 @@ for my $case (
|
||||
} else {
|
||||
ok(!$result->{placed}, "$label: the import fails");
|
||||
like($result->{errors}, $case->{error} // $refused, "$label: the failure is reported");
|
||||
like($result->{errors}, $refused, "$label: the refused write is reported") if $case->{refuse} && $case->{error};
|
||||
}
|
||||
like($result->{messages}, $case->{rows}, "$label: the restore of the rows is reported") if $case->{rows};
|
||||
like($result->{warnings}, $case->{changed}, "$label: the row that another import changed is reported") if $case->{changed};
|
||||
is($result->{disable}, '1', "$label: the osimage row stays, disabled") if $case->{disabled};
|
||||
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 holds only what was written") if $case->{osvers};
|
||||
is($result->{osvers}, $case->{osvers}, "$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};
|
||||
if ($case->{enabled}) {
|
||||
is($result->{disable}, undef, "$label: the disable column is NULL again");
|
||||
ok($result->{visible}, "$label: the image stays visible to a read of enabled rows");
|
||||
}
|
||||
my $kept = $case->{imported} || $case->{other_host} || ($case->{fail} // '') =~ /^(?:die in the restore|unreadable after the write)$/;
|
||||
is($result->{comments}, $kept ? "operator's image" : 'previous image', "$label: the osimage comments, with a quote, are as expected");
|
||||
}
|
||||
|
||||
done_testing();
|
||||
|
||||
Reference in New Issue
Block a user