mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-20 22:55:40 +00:00
test(ubuntu): exercise genimage mirror selection
Read site settings from an isolated database and inspect the sources.list written into the image. Stop at an injected package-metadata failure without downloading packages.
This commit is contained in:
@@ -24,6 +24,7 @@ jobs:
|
||||
run: >-
|
||||
prove xCAT-test/native/ubuntu_live_media_guardrails.t
|
||||
xCAT-test/native/ubuntu_genimage_debootstrap_arch.t
|
||||
xCAT-test/native/ubuntu_genimage_apt_mirror.t
|
||||
- name: Run tests
|
||||
if: ${{ !cancelled() }}
|
||||
run: perl github_action_xcat_test.pl
|
||||
|
||||
@@ -72,10 +72,33 @@ sub bootstrap {
|
||||
my ($self, %options) = @_;
|
||||
$self->prepare_genimage();
|
||||
$self->write('work/packages/Packages.gz', 'package index fixture');
|
||||
$self->write('work/site.json', encode_json({}));
|
||||
$self->write('work/site.json', encode_json({
|
||||
(exists $options{mirror} ? (ubuntu_apt_mirror => $options{mirror}) : ()),
|
||||
}));
|
||||
if ($options{bootstrap_success}) {
|
||||
$self->write('work/bootstrap-success', '');
|
||||
for my $tool (qw(mount umount)) {
|
||||
my $path = $self->write("work/bin/$tool", "#!/bin/sh\nexit 0\n");
|
||||
chmod(0755, $path) or die "chmod $path: $!";
|
||||
}
|
||||
my $chroot = $self->write('work/bin/chroot', <<'SH');
|
||||
#!/bin/sh
|
||||
case "$*" in
|
||||
'/work/image/rootimg apt-get update')
|
||||
printf '%s\n' "$@" > /work/apt-update.args
|
||||
exit 23 ;;
|
||||
*) printf 'Unexpected chroot command: %s\n' "$*" >&2; exit 97 ;;
|
||||
esac
|
||||
SH
|
||||
chmod(0755, $chroot) or die "chmod $chroot: $!";
|
||||
}
|
||||
my $command = $self->write('work/bin/debootstrap', <<'SH');
|
||||
#!/bin/sh
|
||||
printf '%s\n' "$@" > /work/debootstrap.args
|
||||
if [ -f /work/bootstrap-success ]; then
|
||||
mkdir -p "$5/etc/apt" "$5/proc" "$5/usr/sbin" "$5/mnt"
|
||||
exit 0
|
||||
fi
|
||||
exit 23
|
||||
SH
|
||||
chmod(0755, $command) or die "chmod $command: $!";
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Slurper qw(read_text);
|
||||
use JSON::PP qw(decode_json);
|
||||
use File::Slurper qw(read_text write_binary);
|
||||
use JSON::PP qw(decode_json encode_json);
|
||||
use xCAT::Table;
|
||||
|
||||
my %values = (master => '192.0.2.1', httpport => '80',
|
||||
%{ decode_json(read_text('/work/site.json')) });
|
||||
my $site = xCAT::Table->new('site', -create => 1);
|
||||
$site->setAttribs({ key => $_ }, { value => $values{$_} }) for keys %values;
|
||||
for my $key (keys %values) {
|
||||
$site->setAttribs({ key => $key }, { value => 'https://previous.example.invalid/ubuntu' })
|
||||
if $key eq 'ubuntu_apt_mirror' && $values{$key} eq '';
|
||||
$site->setAttribs({ key => $key }, { value => $values{$key} });
|
||||
}
|
||||
my $mirror_row = $site->getAttribs({ key => 'ubuntu_apt_mirror' }, qw(key value));
|
||||
write_binary('/work/site-row.json', encode_json({ mirror => $mirror_row }));
|
||||
$site->close();
|
||||
exec 'perl', @ARGV;
|
||||
die "exec genimage: $!";
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use JSON::PP qw(decode_json);
|
||||
use Test::More;
|
||||
use XCAT::Test::UbuntuImage;
|
||||
|
||||
plan skip_all => 'Linux filesystem namespaces are required' unless $^O eq 'linux';
|
||||
|
||||
my $archive = 'http://archive.ubuntu.com/ubuntu';
|
||||
my $ports = 'http://ports.ubuntu.com/ubuntu-ports';
|
||||
my $site = 'https://site.example.invalid/ubuntu';
|
||||
my $explicit = 'https://image.example.invalid/ubuntu';
|
||||
|
||||
for my $case (
|
||||
['amd64 default', {arch => 'x86_64'}, 'amd64', $archive, 'noble'],
|
||||
['i386 default', {arch => 'x86'}, 'i386', $archive, 'noble'],
|
||||
['POWER default', {arch => 'ppc64el'}, 'ppc64el', $ports, 'noble'],
|
||||
['RISC-V default', {arch => 'riscv64'}, 'riscv64', $ports, 'noble'],
|
||||
['s390x default', {arch => 's390x'}, 's390x', $ports, 'noble'],
|
||||
['site overrides archive', {mirror => $site}, 'amd64', $site, 'noble'],
|
||||
['site overrides ports', {arch => 'riscv64', mirror => $site}, 'riscv64', $site, 'noble'],
|
||||
['cleared site keeps default', {arch => 'riscv64', mirror => ''}, 'riscv64', $ports, 'noble'],
|
||||
['pkgdir overrides site', {mirror => $site,
|
||||
pkgdir => "/work/packages,$explicit jammy main"}, 'amd64', $explicit, 'jammy'],
|
||||
['first pkgdir mirror wins', {pkgdir =>
|
||||
"/work/packages,$explicit noble main,$site jammy universe"}, 'amd64', $explicit, 'noble'],
|
||||
) {
|
||||
my ($name, $options, $arch, $mirror, $suite) = @$case;
|
||||
subtest $name => sub {
|
||||
my $image = XCAT::Test::UbuntuImage->new();
|
||||
my ($status, $output, $args) = $image->bootstrap(%$options, bootstrap_success => 1);
|
||||
if (exists $options->{mirror} && $options->{mirror} eq '') {
|
||||
is_deeply(decode_json($image->read('work/site-row.json'))->{mirror},
|
||||
{ key => 'ubuntu_apt_mirror' }, 'the cleared setting remains as an empty database row');
|
||||
}
|
||||
ok($status > 0 && ($status & 127) == 0,
|
||||
'genimage exits unsuccessfully after the injected package-metadata failure') or diag($output);
|
||||
is_deeply($args, ['--verbose', '--arch', $arch, $suite,
|
||||
'/work/image/rootimg', $mirror], 'the selected mirror reaches debootstrap') or diag($output);
|
||||
like($output, qr/Failed to update package metadata/,
|
||||
'package-metadata failure is reported');
|
||||
unlike($output, qr/Unexpected chroot command/, 'only the expected chroot command runs');
|
||||
is($image->read('work/apt-update.args'), "/work/image/rootimg\napt-get\nupdate\n",
|
||||
'APT uses the generated image root');
|
||||
my @sources = grep { /\S/ } split /\n/,
|
||||
$image->read('work/image/rootimg/etc/apt/sources.list');
|
||||
my @expected = exists $options->{pkgdir}
|
||||
? map { "deb $_" } grep { /^https?:/ } split /,/, $options->{pkgdir}
|
||||
: map { "deb $mirror $_ main universe" } ($suite, "$suite-updates", "$suite-security");
|
||||
unshift @expected, "deb [trusted=yes] http://192.0.2.1:80/work/packages $suite main restricted universe";
|
||||
is_deeply([map { [split ' '] } @sources], [map { [split ' '] } @expected],
|
||||
'image APT sources retain the local mirror and all configured remote pockets');
|
||||
};
|
||||
}
|
||||
|
||||
subtest 'explicit mirror without a suite' => sub {
|
||||
my $image = XCAT::Test::UbuntuImage->new();
|
||||
my ($status, $output, $args) = $image->bootstrap(
|
||||
pkgdir => "/work/packages,$explicit");
|
||||
is($status, 256, 'invalid mirror is rejected');
|
||||
is($args, undef, 'debootstrap is not invoked');
|
||||
like($output, qr/first http mirror path must includes http URL and distribute name/,
|
||||
'rejection identifies the missing suite');
|
||||
};
|
||||
|
||||
done_testing();
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# archive.ubuntu.com publishes amd64 and i386 only. A ppc64el or riscv64 netboot image built
|
||||
# against it finds no package at all, and debootstrap fails before it copies anything, so the
|
||||
# default mirror has to follow the image architecture.
|
||||
#
|
||||
# Driven by the real selection code: the two statements are extracted from genimage and
|
||||
# evaluated here, so this test tracks the script rather than a copy of it.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs( File::Spec->catdir( $FindBin::Bin, '..', '..' ) );
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
require xCAT::Utils;
|
||||
my $genimage_path = File::Spec->catfile(
|
||||
$repo_root, 'xCAT-server', 'share', 'xcat', 'netboot', 'ubuntu', 'genimage'
|
||||
);
|
||||
plan skip_all => "genimage not found at $genimage_path" unless -f $genimage_path;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $genimage_path or die $!; <$fh> };
|
||||
|
||||
my ($default) = $src =~ /^\s*(my \$default = \(\$uarch =~.*?;)\s*$/ms;
|
||||
ok( defined $default, 'found the default mirror selection in genimage' )
|
||||
or done_testing(), exit;
|
||||
|
||||
my ($pick) = $src =~ /^\s*(my \$mirror = \(defined \$aptmirror\[0\].*?;)\s*$/ms;
|
||||
ok( defined $pick, 'found the mirror override in genimage' )
|
||||
or done_testing(), exit;
|
||||
|
||||
# genimage does not read osimage.osarch directly: it converts it first, so the value the selection
|
||||
# sees is whatever xCAT::Utils::debian_arch returns. Driving the xCAT token through that conversion
|
||||
# is what catches a token the map does not know.
|
||||
sub choose_osarch {
|
||||
my ( $osarch, $site ) = @_;
|
||||
return choose( xCAT::Utils->debian_arch($osarch), $site );
|
||||
}
|
||||
|
||||
sub choose {
|
||||
my ( $uarch, $site ) = @_;
|
||||
my $set = defined $site ? "('$site')" : "()";
|
||||
## no critic (BuiltinFunctions::ProhibitStringyEval)
|
||||
my $mirror = eval "my \$uarch = '$uarch'; my \@aptmirror = $set; $default $pick \$mirror";
|
||||
## use critic
|
||||
die "failed to evaluate the genimage mirror selection: $@" if $@;
|
||||
return $mirror;
|
||||
}
|
||||
|
||||
is( choose('riscv64'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'a riscv64 image takes the ports archive' );
|
||||
is( choose('ppc64el'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'a ppc64el image takes the ports archive' );
|
||||
is( choose('amd64'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'an amd64 image keeps the main archive' );
|
||||
is( choose('i386'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'an i386 image keeps the main archive' );
|
||||
|
||||
is( choose( 'riscv64', 'http://mirror.example.invalid/ubuntu' ),
|
||||
'http://mirror.example.invalid/ubuntu',
|
||||
'site.ubuntu_apt_mirror overrides the ports archive' );
|
||||
is( choose( 'amd64', 'http://mirror.example.invalid/ubuntu' ),
|
||||
'http://mirror.example.invalid/ubuntu',
|
||||
'site.ubuntu_apt_mirror overrides the main archive' );
|
||||
is( choose( 'riscv64', '' ), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'an empty site.ubuntu_apt_mirror does not blank the mirror' );
|
||||
|
||||
# The architecture reaches the selection as an xCAT osarch value, not as a Debian one.
|
||||
is( choose_osarch('x86_64'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'osarch x86_64 reaches the archive' );
|
||||
is( choose_osarch('x86'), 'http://archive.ubuntu.com/ubuntu',
|
||||
'osarch x86 reaches the archive, which is where i386 lives' );
|
||||
is( choose_osarch('riscv64'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'osarch riscv64 reaches the ports archive' );
|
||||
is( choose_osarch('ppc64el'), 'http://ports.ubuntu.com/ubuntu-ports',
|
||||
'osarch ppc64el reaches the ports archive' );
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user