From 4bbed9e184b0b4fa05d0dd92157fdb9fc1834dca Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Sat, 12 Sep 2026 08:56:11 -0300 Subject: [PATCH] fix(xcat-core): the Genesis build root is missing nine commands the image needs The first native build stopped on the payload lint. The Ubuntu dracut module installs ping, nc, nslookup, sfdisk, mkfs.btrfs, usb.ids, poweroff, reboot and shutdown unconditionally, and no package in the build root supplies any of them. dracut reported each one with a FAILED: line and exited 0, which is how the image that shipped without dhclient was packaged. Add iputils-ping, netcat-openbsd, fdisk, btrfs-progs, hwdata and systemd-sysv. Two names change between releases. util-linux-extra appeared in 23.04, so the jammy build stopped with "E: Unable to locate package util-linux-extra" where hwclock is still in util-linux; bind9-dnsutils replaced dnsutils in 22.04. add_first_available installs the first name apt knows and stops the build when a release carries none of them. dch reads debian/control from the working directory, not from the file it writes, so the build directory has to be the working directory before it runs. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- xCAT-genesis-builder/builddeb-genesis-base | 25 ++++++++++++++++-- xCAT-test/unit/genesis_ubuntu_build_root.t | 30 ++++++++++++++-------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/xCAT-genesis-builder/builddeb-genesis-base b/xCAT-genesis-builder/builddeb-genesis-base index a6af7e9b1..e1c550ebd 100755 --- a/xCAT-genesis-builder/builddeb-genesis-base +++ b/xCAT-genesis-builder/builddeb-genesis-base @@ -80,7 +80,8 @@ REQUIRED_PACKAGES=" nfs-common rpcbind pciutils usbutils parted dosfstools e2fsprogs lvm2 mdadm net-tools bc psmisc rsync wget cpio - isc-dhcp-client ifenslave util-linux-extra + isc-dhcp-client ifenslave + systemd-sysv hwdata btrfs-progs netcat-openbsd iputils-ping fdisk dpkg-dev debhelper fakeroot devscripts vim-tiny " if [ "$BUILDARCH" = "amd64" ]; then @@ -89,6 +90,24 @@ fi echo "Installing build dependencies..." apt-get update -qq + +# Two commands the image needs changed package between releases: nslookup left dnsutils for +# bind9-dnsutils in 22.04, and hwclock left util-linux for util-linux-extra in 23.04. Ask apt +# which name this release carries rather than branch on the codename. +add_first_available() { + local p + for p in "$@"; do + if apt-cache show "$p" >/dev/null 2>&1; then + REQUIRED_PACKAGES="$REQUIRED_PACKAGES $p" + return 0 + fi + done + echo "ERROR: $CODENAME carries none of these packages: $*" >&2 + exit 1 +} +add_first_available bind9-dnsutils dnsutils +add_first_available util-linux-extra util-linux + apt-get install -y --no-install-recommends $REQUIRED_PACKAGES # dpkg-architecture comes from dpkg-dev, which the line above installs. @@ -237,6 +256,9 @@ rewrite_control "$DIR/debian/control" "$BUILDARCH" # debian/dirs names the image directory, which is the rpm architecture. echo "/opt/xcat/share/xcat/netboot/genesis/$TARCH/" > "$DIR/debian/dirs" +# dch reads debian/control from the current directory, not from the file it writes. +cd "$DIR" + PKG_VERSION="${VERSION}-${RELEASE}~${CODENAME}" rm -f "$DIR/debian/changelog" dch --create --package "xcat-genesis-base-$BUILDARCH" \ @@ -245,7 +267,6 @@ dch --create --package "xcat-genesis-base-$BUILDARCH" \ "Native Ubuntu build on $CODENAME $BUILDARCH" echo "Building .deb package..." -cd "$DIR" dpkg-buildpackage -rfakeroot -uc -us -b if [ -n "$outdir" ]; then diff --git a/xCAT-test/unit/genesis_ubuntu_build_root.t b/xCAT-test/unit/genesis_ubuntu_build_root.t index 1d87f7e63..4510cac3f 100755 --- a/xCAT-test/unit/genesis_ubuntu_build_root.t +++ b/xCAT-test/unit/genesis_ubuntu_build_root.t @@ -24,21 +24,25 @@ plan skip_all => 'ubuntu module-setup.sh not found' unless -f $module; plan tests => 9; # Commands the Ubuntu dracut module marks mandatory that a minimal Ubuntu server root does -# NOT already provide, and the package that supplies each one. Every entry here has to be in -# REQUIRED_PACKAGES or the image ships without the command. -my %PACKAGE_FOR = ( - dhclient => 'isc-dhcp-client', - ifenslave => 'ifenslave', - hwclock => 'util-linux-extra', +# NOT already provide, and the packages that supply each one. Every command here needs one of +# its packages in the build root or the image ships without it. hwclock has two names because +# it left util-linux for util-linux-extra in 23.04, and the build root asks apt which name the +# release it is building for carries. +my %PACKAGES_FOR = ( + dhclient => ['isc-dhcp-client'], + ifenslave => ['ifenslave'], + hwclock => [ 'util-linux-extra', 'util-linux' ], ); my %mandatory = map { $_ => 1 } mandatory_commands($module); my @packages = required_packages($builder); -for my $command (sort keys %PACKAGE_FOR) { +for my $command (sort keys %PACKAGES_FOR) { + my @provider = @{ $PACKAGES_FOR{$command} }; ok($mandatory{$command}, "the Ubuntu dracut module installs '$command' unconditionally"); - ok(scalar(grep { $_ eq $PACKAGE_FOR{$command} } @packages), - "the build root installs $PACKAGE_FOR{$command}, which provides '$command'"); + my @named = grep { my $p = $_; grep { $_ eq $p } @packages } @provider; + ok(scalar @named, + "the build root installs @{[ join ' or ', @provider ]}, which provides '$command'"); } # doxcat asks dhclient for the provisioning lease. An image without it never gets an address, @@ -85,8 +89,10 @@ BASH return @names; } -# required_packages($path): extract the REQUIRED_PACKAGES assignment from the build script and -# evaluate it, so the list comes from the value the script actually uses. +# required_packages($path): the packages the build root installs. The fixed list is the +# REQUIRED_PACKAGES assignment, evaluated so the value comes from the script itself; a command +# whose package name changed between releases is added by add_first_available, whose candidates +# count too -- the script picks whichever one apt knows. sub required_packages { my ($path) = @_; my $text = do { open my $fh, '<', $path or die "$path: $!"; local $/; <$fh> }; @@ -95,5 +101,7 @@ sub required_packages { my $out = qx{bash -c 'set -u; $block; printf "%s\\n" \$REQUIRED_PACKAGES' 2>/dev/null}; my @packages = grep { length } split /\s+/, ($out // ''); BAIL_OUT("REQUIRED_PACKAGES in $path evaluated to nothing") unless @packages; + push @packages, grep { length } split /\s+/, $1 + while $text =~ /^add_first_available\s+(.+)$/mg; return @packages; }