2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 16:39:30 +00:00

test(xcat-core): naming util-linux-extra stops the Genesis build on jammy

builddeb-genesis-base names util-linux-extra in REQUIRED_PACKAGES for every
release. Measured on the four Ubuntu management nodes: focal and jammy report
"Candidate: (none)" for that package and carry hwclock in util-linux, which is
essential and already in the build root; noble and resolute carry it in
util-linux-extra. apt-get install with a package it cannot locate exits
non-zero, and the script runs under set -euo pipefail, so the build stops on
two supported targets before dracut runs.

util-linux only Suggests util-linux-extra, and the install passes
--no-install-recommends, so a release that split the package has to name it.

The test asserts the unconditional list does not name it, and drives the
selector that decides, with apt-cache shadowed for a release that has the
package and one that does not.

It also drops the assertion that matched "verify-genesis-payload" against the
text of the build script. That proved the string was present, not that the
verifier ran, ran before packaging, or stopped the build -- and the script it
names does not exist on this branch.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-16 18:02:47 -03:00
parent 3a715962d8
commit ea2b86adef
+36 -7
View File
@@ -20,16 +20,22 @@ my $builder = repo_path('xCAT-genesis-builder/builddeb-genesis-base');
my $module = repo_path('xCAT-genesis-builder/dracut_105/ubuntu/module-setup.sh');
plan skip_all => 'builddeb-genesis-base not found' unless -f $builder;
plan skip_all => 'ubuntu module-setup.sh not found' unless -f $module;
plan tests => 8;
plan tests => 10;
# Mandatory commands a minimal Ubuntu server root does NOT already provide, and the package
# that supplies each one.
# that supplies each one on every release xCAT builds for.
my %PACKAGE_FOR = (
dhclient => 'isc-dhcp-client',
ifenslave => 'ifenslave',
hwclock => 'util-linux-extra',
);
# hwclock is not in that list because the package that carries it moved. Measured on the
# four Ubuntu management nodes: focal and jammy have it in util-linux, which is essential
# and always in the build root, and no util-linux-extra exists to install; noble and
# resolute have it in util-linux-extra. util-linux only Suggests that package, and this
# build passes --no-install-recommends, so the releases that split it must name it and the
# releases that did not must not.
my %mandatory = map { $_ => 1 } mandatory_commands($module);
my @packages = required_packages($builder);
@@ -43,10 +49,22 @@ for my $command (sort keys %PACKAGE_FOR) {
ok($mandatory{dhclient} && scalar(grep { $_ eq 'isc-dhcp-client' } @packages),
'the Genesis image can obtain a DHCP lease');
# dracut_install is silent about a missing command, so the payload needs its own gate.
# xCAT-genesis-base.spec runs the same verifier on the EL path.
my $text = do { open my $fh, '<', $builder or die "$builder: $!"; local $/; <$fh> };
like($text, qr{verify-genesis-payload}, 'builddeb-genesis-base verifies the payload it packages');
ok($mandatory{hwclock}, "the Ubuntu dracut module installs 'hwclock' unconditionally");
# Naming a package apt cannot locate fails the whole install, and the script runs under
# set -e, so an unconditional util-linux-extra stops the build on focal and jammy.
ok(!scalar(grep { $_ eq 'util-linux-extra' } @packages),
'the unconditional list does not name util-linux-extra');
# What the script does instead: keep a package only where apt has a candidate for it.
{
my @present = optional_packages($builder, 'util-linux-extra', 0);
my @absent = optional_packages($builder, 'util-linux-extra', 1);
is_deeply(\@present, ['util-linux-extra'],
'a release that carries util-linux-extra installs it');
is_deeply(\@absent, [],
'a release without it installs nothing in its place');
}
# An absolute path in the install() output is a data file, not a command.
sub mandatory_commands {
@@ -75,6 +93,17 @@ BASH
return @names;
}
# Run the script's own selector with apt-cache shadowed, so the decision is exercised
# rather than read. $rc is what the shadow returns: 0 for a release that has the package.
sub optional_packages {
my ($path, $package, $rc) = @_;
my $text = do { open my $fh, '<', $path or die "$path: $!"; local $/; <$fh> };
my ($block) = $text =~ /^(optional_packages\(\)\s*\{.*?^\})/ms;
BAIL_OUT("no optional_packages() in $path") unless $block;
my $out = qx{bash -c 'set -u; apt-cache() { return $rc; }; $block; optional_packages $package' 2>/dev/null};
return [ grep { length } split /\s+/, ($out // '') ];
}
# Evaluate the assignment rather than parse it, so the list is the value the script uses.
sub required_packages {
my ($path) = @_;