2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-24 16:54:03 +00:00

test(xcat-core): a Genesis image with no /usr/bin/awk passes verification

verify-genesis-payload reads the names the dracut module installs back out of
module-setup.sh and checks each one against the extracted payload. It drops
every name that starts with "/", so the 609 absolute paths the EL module names
are checked by nothing. An image built without /usr/bin/awk, /etc/services or
/lib64/libnss_dns.so.2 passes.

The Genesis debs carry the architecture in the package name. 2.19 renames the
ppc64 debs to ppc64el, and neither builddeb-genesis-base nor
debuild-xcat-genesis-base names the deb the new package supersedes. dpkg keeps
xcat-genesis-base-ppc64 installed beside xcat-genesis-base-ppc64el, and the old
package owns the same files under /opt/xcat/share/xcat/netboot/genesis.

genesis_payload_verification.t drives the verifier against a payload missing
/usr/bin/awk and one missing /etc/services. genesis_base_deb_arch.t asserts the
Replaces and Breaks the alien path writes. genesis_base_deb_control_rewrite.t
lifts rewrite_control() out of builddeb-genesis-base and runs it over the
control file in the tree.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-09-11 16:00:32 -03:00
parent 24e22671b4
commit b3ed5e6d24
3 changed files with 105 additions and 3 deletions
+14 -2
View File
@@ -2,7 +2,8 @@
# debuild-xcat-genesis-base converts the EL Genesis base rpm to a deb. The rpm name carries the
# Genesis target architecture, and the deb must carry the Debian architecture: ppc64 becomes
# ppc64el, x86_64 becomes amd64. An unmapped architecture leaves the deb named after the rpm and
# makes it break a genesis-scripts package that no repository publishes.
# makes it break a genesis-scripts package that no repository publishes. The rename also has to
# name the deb it supersedes, or an upgraded ppc node keeps xcat-genesis-base-ppc64 as well.
#
# The script is driven here with alien and dpkg-buildpackage shadowed by shell functions.
use strict;
@@ -75,6 +76,13 @@ my %expected = (
'xCAT-genesis-base-ppc64-2.13.10-snap202601010000.noarch.rpm' => 'ppc64el',
);
# The package the new deb takes over from. On ppc that is the deb this rename leaves behind:
# without the relation dpkg keeps xcat-genesis-base-ppc64 and its copy of the same files.
my %superseded = (
'amd64' => 'xcat-genesis-amd64',
'ppc64el' => 'xcat-genesis-ppc64, xcat-genesis-base-ppc64',
);
for my $rpm (sort keys %expected) {
my $arch = $expected{$rpm};
my ($dir, $control) = convert($arch, $rpm);
@@ -84,8 +92,12 @@ for my $rpm (sort keys %expected) {
like($dir, qr/\Q-$arch-\E/, "$rpm builds in a $arch source tree");
like($control, qr/^Package:\s*xcat-genesis-base-\Q$arch\E$/m,
"$rpm builds the package xcat-genesis-base-$arch");
like($control, qr/^Breaks:\s*xcat-genesis-scripts-\Q$arch\E\b/m,
like($control, qr/^Breaks:.*\bxcat-genesis-scripts-\Q$arch\E\b/m,
"xcat-genesis-base-$arch breaks the genesis scripts of its own architecture");
like($control, qr/^Replaces:\s*\Q$superseded{$arch}\E\s*$/m,
"xcat-genesis-base-$arch replaces $superseded{$arch}");
like($control, qr/^Breaks:\s*\Q$superseded{$arch}\E\b/m,
"xcat-genesis-base-$arch breaks $superseded{$arch}");
}
done_testing();
@@ -0,0 +1,64 @@
#!/usr/bin/env perl
# builddeb-genesis-base builds the Genesis base deb natively on Ubuntu. It writes the target
# architecture into debian/control, which is held in the amd64 form in the tree. 2.19 renames
# the ppc64 debs to ppc64el, so the ppc control must also name the deb it supersedes: without
# the relation dpkg keeps xcat-genesis-base-ppc64 installed beside the new package, and that
# old package owns the same files under /opt/xcat/share/xcat/netboot/genesis.
#
# The script needs dracut and root, so rewrite_control() is lifted out of it and run alone
# against the control file the tree ships.
use strict;
use warnings;
use File::Slurper qw(read_text write_text);
use File::Temp qw(tempdir);
use FindBin;
use lib "$FindBin::Bin/../lib";
use Test::More;
use XCAT::Test::File qw(repo_path slurp_repo_file);
my $script = repo_path('xCAT-genesis-builder/builddeb-genesis-base');
my $control = repo_path('xCAT-genesis-builder/debian/control');
plan skip_all => 'builddeb-genesis-base not found' unless -f $script;
plan tests => 8;
my $text = slurp_repo_file('xCAT-genesis-builder/builddeb-genesis-base');
my ($function) = $text =~ /^(rewrite_control\(\)\s*\{.*?^\})/ms;
BAIL_OUT('rewrite_control() no longer matches in builddeb-genesis-base')
unless defined $function;
my $tmpdir = tempdir(CLEANUP => 1);
# What the ppc64el package has to take over from, and what amd64 already took over from.
my %superseded = (
'amd64' => 'xcat-genesis-amd64',
'ppc64el' => 'xcat-genesis-ppc64, xcat-genesis-base-ppc64',
);
for my $arch (sort keys %superseded) {
my $out = rewrite($arch);
like($out, qr/^Package:\s*xcat-genesis-base-\Q$arch\E$/m,
"$arch control names the package xcat-genesis-base-$arch");
like($out, qr/^Replaces:\s*\Q$superseded{$arch}\E\s*$/m,
"$arch control replaces $superseded{$arch}");
like($out, qr/^Breaks:\s*\Q$superseded{$arch}\E\b/m,
"$arch control breaks $superseded{$arch}");
like($out, qr/^Breaks:.*\bxcat-genesis-scripts-\Q$arch\E \(<< 2\.13\.10\)/m,
"$arch control breaks the genesis scripts of its own architecture");
}
#---
# rewrite: run the lifted rewrite_control() over a copy of the control file in the tree.
#---
sub rewrite {
my ($arch) = @_;
my $copy = "$tmpdir/control.$arch";
write_text($copy, read_text($control));
my $driver = "$tmpdir/driver.$arch.sh";
write_text($driver, "#!/bin/bash\nset -eu\n$function\nrewrite_control \"\$1\" \"\$2\"\n");
system('bash', $driver, $copy, $arch) == 0
or BAIL_OUT("rewrite_control failed for $arch");
return read_text($copy);
}
+27 -1
View File
@@ -15,7 +15,7 @@ use XCAT::Test::File qw(repo_path);
my $verifier = repo_path('xCAT-genesis-builder/verify-genesis-payload');
plan skip_all => 'verify-genesis-payload not found' unless -f $verifier;
plan tests => 18;
plan tests => 22;
my $tmpdir = tempdir(CLEANUP => 1);
my $module_seq = 0;
@@ -71,6 +71,23 @@ my $noopenssl = build_payload(sshd_execs_session => 1, session_helper => 1, tmux
isnt($rc, 0, 'a payload without openssl fails');
like($err, qr/openssl/, 'the missing openssl is named');
# dracut_install installs an absolute path at that same path. The verifier used to drop every
# name that started with "/", so an image with no /usr/bin/awk passed. doxcat, getdestiny and
# the firmware wrappers all run awk.
my $noawk = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1,
dhclient => 1, mktemp => 1, commands => [qw(openssl wget tar)], absent => ['usr/bin/awk']);
($rc, $err) = run_with_commands($module, $noawk);
isnt($rc, 0, 'a payload without the absolute path /usr/bin/awk fails');
like($err, qr{/usr/bin/awk}, 'the missing /usr/bin/awk is named');
# The module names data files by absolute path too. Genesis resolves service names with
# /etc/services.
my $noservices = build_payload(sshd_execs_session => 1, session_helper => 1, tmux => 1, locale => 1,
dhclient => 1, mktemp => 1, commands => [qw(openssl wget tar)], absent => ['etc/services']);
($rc, $err) = run_with_commands($module, $noservices);
isnt($rc, 0, 'a payload without the absolute path /etc/services fails');
like($err, qr{/etc/services}, 'the missing /etc/services is named');
# The DHCP client is release-dependent, so the module installs it inside a conditional. Those
# names are not the contract; the spec passes the one it wants as a required path.
my $conditional = write_module_setup(['wget'], ['dhclient']);
@@ -112,6 +129,15 @@ sub build_payload {
write_text("$root/usr/sbin/dhclient", "dhclient\n") if $opt{dhclient};
write_text("$root/usr/bin/mktemp", "mktemp\n") if $opt{mktemp};
write_text("$root/usr/bin/$_", "$_\n") for @{ $opt{commands} || [] };
# The module written by write_module_setup names these two by absolute path.
my %absent = map { $_ => 1 } @{ $opt{absent} || [] };
for my $path (qw(usr/bin/awk etc/services)) {
next if $absent{$path};
my ($dir) = $path =~ m{^(.*)/};
make_path("$root/$dir");
write_text("$root/$path", "$path\n");
}
return $root;
}