2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-04 12:07:56 +00:00

Merge pull request #7789 from VersatusHPC/refactor/package-manager-discovery

refactor(postscripts): share RPM package manager discovery
This commit is contained in:
Daniel Hilst
2026-08-31 15:33:17 -03:00
committed by GitHub
4 changed files with 264 additions and 10 deletions
+250
View File
@@ -0,0 +1,250 @@
#!/usr/bin/env perl
use strict;
use warnings;
use File::Copy qw(copy);
use File::Path qw(make_path);
use File::Slurper qw(read_text write_text);
use File::Spec;
use File::Temp qw(tempdir);
use FindBin;
use lib "$FindBin::Bin/../lib";
use Test::More;
use XCAT::Test::File qw(repo_path);
my $postscripts = repo_path(File::Spec->catdir('xCAT', 'postscripts'));
my $library = File::Spec->catfile( $postscripts, 'xcatpkgutils.sh' );
my $loader = File::Spec->catfile( $postscripts, 'xcatpkgutils-loader.sh' );
my $tmpdir = tempdir( CLEANUP => 1 );
my $dnf_first = File::Spec->catdir( $tmpdir, 'dnf-first' );
my $yum_only = File::Spec->catdir( $tmpdir, 'yum-only' );
my $neither = File::Spec->catdir( $tmpdir, 'neither' );
make_path( $dnf_first, $yum_only, $neither );
write_executable( File::Spec->catfile( $dnf_first, 'dnf' ), "#!/bin/sh\n" );
write_executable( File::Spec->catfile( $dnf_first, 'yum' ), "#!/bin/sh\n" );
write_executable( File::Spec->catfile( $yum_only, 'yum' ), "#!/bin/sh\n" );
SKIP: {
skip 'the shared discovery helper is introduced by the production commit', 3
unless helper_available();
is_deeply(
[ run_helper($dnf_first) ],
[ 0, "dnf\n" ],
'dnf is preferred when both RPM package managers are executable'
);
is_deeply(
[ run_helper($yum_only) ],
[ 0, "yum\n" ],
'yum is used when dnf is unavailable'
);
is_deeply(
[ run_helper($neither) ],
[ 1, '' ],
'discovery fails without output when neither package manager is executable'
);
}
my $caller_dir = File::Spec->catdir( $tmpdir, 'callers' );
my $test_bin = File::Spec->catdir( $caller_dir, 'bin' );
make_path($test_bin);
stage_callers($caller_dir);
write_executable( File::Spec->catfile( $test_bin, 'logger' ), "#!/bin/sh\nexit 0\n" );
write_executable( File::Spec->catfile( $test_bin, 'mount' ), "#!/bin/sh\nexit 1\n" );
write_executable(
File::Spec->catfile( $test_bin, 'uname' ),
"#!/bin/sh\nprintf '%s\\n' Linux\n"
);
write_executable( File::Spec->catfile( $test_bin, 'dpkg' ), "#!/bin/sh\nexit 1\n" );
write_executable(
File::Spec->catfile( $test_bin, 'rpm' ),
<<'SH'
#!/bin/sh
case "$*" in
--version) exit 0 ;;
-q\ zypper)
[ "$XCAT_PM_SCENARIO" = zypper ] && exit 0
exit 1
;;
esac
if [ -n "${XCAT_RPM_TRACE:-}" ]; then
printf '%s\n' "$*" >> "$XCAT_RPM_TRACE"
exit 0
fi
exit 1
SH
);
write_executable( File::Spec->catfile( $test_bin, 'dnf' ), "#!/bin/sh\nexit 0\n" );
write_executable( File::Spec->catfile( $test_bin, 'yum' ), "#!/bin/sh\nexit 0\n" );
write_executable( File::Spec->catfile( $test_bin, 'zypper' ), "#!/bin/sh\nexit 0\n" );
my $bash_env = File::Spec->catfile( $caller_dir, 'bash-env.sh' );
# Fake only the absolute executable probes; the staged callers and library stay unchanged.
write_text(
$bash_env,
<<'SH'
function [
{
case "$1:$2:$XCAT_PM_SCENARIO" in
-x:/usr/bin/dnf:dnf) return 0 ;;
-x:/usr/bin/dnf:*) return 1 ;;
-x:/usr/bin/yum:dnf|-x:/usr/bin/yum:yum) return 0 ;;
-x:/usr/bin/yum:*) return 1 ;;
esac
builtin [ "$@"
}
exit()
{
if builtin [ -n "${XCAT_PM_STATE_TRACE:-}" ]; then
printf '%s|%s|%s|%s|%s\n' "${yumcmd:-}" "${hasrpm:-}" "${hasyum:-}" \
"${haszypper:-}" "${supdatecommand:-}" \
> "$XCAT_PM_STATE_TRACE"
fi
builtin exit "$@"
}
SH
);
my %common_env = (
ARCH => 'x86_64',
BASH_ENV => $bash_env,
INSTALLDIR => 'INSTALLDIR',
NFSSERVER => 'package-test-server',
NODE => 'node1',
OSVER => 'rocky9',
PATH => "$test_bin:$ENV{PATH}",
);
for my $scenario (
[ dnf => 'dnf||||' ],
[ yum => 'yum||||' ],
)
{
my ( $name, $expected_state ) = @{$scenario};
my ( $status, $output, $state ) = run_caller( 'ospkgs', $name );
is( $status, 0, "ospkgs completes with $name" ) or diag($output);
is( $state, $expected_state, "ospkgs selects $name" );
}
my ( $ospkgs_status, $ospkgs_output, $ospkgs_state ) =
run_caller( 'ospkgs', 'neither' );
is( $ospkgs_status, 1, 'ospkgs still stops when neither dnf nor yum is available' );
is( $ospkgs_state, '||||', 'ospkgs leaves package-manager state empty on failure' );
like(
$ospkgs_output,
qr/^Please install yum or dnf on node1\.$/m,
'ospkgs retains its package-manager installation error'
);
for my $scenario (
[ dnf => 'dnf|1|1|0|rpm -Uvh --replacepkgs' ],
[ yum => 'yum|1|1|0|rpm -Uvh --replacepkgs' ],
[ zypper => '|1|0|1|rpm -Uvh --replacepkgs' ],
[ rpm => '|1|0|0|rpm -Uvh --replacepkgs' ],
)
{
my ( $name, $expected_state ) = @{$scenario};
my ( $status, $output, $state ) = run_caller( 'otherpkgs', $name );
is( $status, 0, "otherpkgs completes with the $name discovery outcome" )
or diag($output);
is( $state, $expected_state, "otherpkgs retains the $name discovery outcome" );
}
like(
read_text( File::Spec->catfile( $caller_dir, 'otherpkgs-rpm-command.trace' ) ),
qr/^-Uvh --replacepkgs package-test\*$/m,
'otherpkgs executes its raw RPM installation fallback'
);
done_testing();
sub run_helper {
my ($directory) = @_;
return run_command(
'/bin/sh', '-c', '. "$1"; xcat_find_rpm_package_manager "$2"',
'package-manager-discovery-test', $library, $directory
);
}
sub helper_available {
return system(
'/bin/sh', '-c',
'. "$1"; command -v xcat_find_rpm_package_manager >/dev/null 2>&1',
'package-manager-discovery-test', $library
) == 0;
}
sub run_caller {
my ( $caller, $scenario ) = @_;
my $trace = File::Spec->catfile( $caller_dir, "$caller-$scenario.trace" );
local %ENV = ( %ENV, %common_env );
$ENV{XCAT_PM_SCENARIO} = $scenario;
$ENV{XCAT_PM_STATE_TRACE} = $trace;
my @arguments;
if ( $caller eq 'ospkgs' ) {
delete @ENV{qw(OTHERPKGS OTHERPKGS_INDEX UPDATENODE)};
$ENV{OSPKGS} = 'package-test';
@arguments = ('--keeprepo');
} else {
delete @ENV{qw(OSPKGS OTHERPKGS)};
$ENV{OSVER} = 'custom9';
if ( $scenario eq 'rpm' ) {
$ENV{OTHERPKGS1} = 'package-test';
$ENV{OTHERPKGS_INDEX} = 1;
$ENV{XCAT_RPM_TRACE} = File::Spec->catfile(
$caller_dir, 'otherpkgs-rpm-command.trace'
);
} else {
delete @ENV{qw(OTHERPKGS1 XCAT_RPM_TRACE)};
$ENV{OTHERPKGS_INDEX} = 0;
}
$ENV{UPDATENODE} = 1;
}
my ( $status, $output ) = run_command(
File::Spec->catfile( $caller_dir, $caller ), @arguments
);
my $state = read_text($trace);
chomp($state);
return ( $status, $output, $state );
}
sub stage_callers {
my ($directory) = @_;
for my $source (
$library, $loader,
map { File::Spec->catfile( $postscripts, $_ ) } qw(ospkgs otherpkgs)
)
{
my $filename = ( File::Spec->splitpath($source) )[2];
my $destination = File::Spec->catfile( $directory, $filename );
copy( $source, $destination )
or die "Unable to stage $source as $destination: $!";
chmod 0755, $destination
or die "Unable to make $destination executable: $!";
}
}
sub write_executable {
my ( $path, $contents ) = @_;
write_text( $path, $contents );
chmod 0755, $path or die "Unable to make $path executable: $!";
}
sub run_command {
my (@command) = @_;
my $pid = open( my $pipe, '-|' );
die "Unable to fork for @command: $!" unless defined($pid);
if ( $pid == 0 ) {
open( STDERR, '>&', STDOUT ) or die "Unable to merge stderr: $!";
exec { $command[0] } @command;
die "Unable to execute @command: $!";
}
my $output = do { local $/; <$pipe> } // '';
close($pipe);
return ( $? >> 8, $output );
}
+1 -5
View File
@@ -892,11 +892,7 @@ elif ( pmatch "$OSVER" "ubuntu*" ); then
fi
else
#check if yum or dnf is installed
if [ -x /usr/bin/dnf ]; then
yumcmd="dnf"
elif [ -x /usr/bin/yum ]; then
yumcmd="yum"
else
if ! yumcmd=$(xcat_find_rpm_package_manager); then
echo "Please install yum or dnf on $NODE."
exit 1;
fi
+1 -5
View File
@@ -491,12 +491,8 @@ else
result=`rpm --version 2>/dev/null`
if [ $? -eq 0 ]; then
hasrpm=1
if [ -x /usr/bin/dnf ]; then
if yumcmd=$(xcat_find_rpm_package_manager); then
hasyum=1
yumcmd="dnf"
elif [ -x /usr/bin/yum ]; then
hasyum=1
yumcmd="yum"
else
result=`rpm -q zypper`
if [ "$?" = "0" ]; then
+12
View File
@@ -2,6 +2,18 @@
# EPL license http://www.eclipse.org/legal/epl-v10.html
# Shared POSIX shell helpers for the ospkgs and otherpkgs postscripts.
xcat_find_rpm_package_manager()
{
if [ -x "${1:-/usr/bin}/dnf" ]; then
printf '%s\n' dnf
elif [ -x "${1:-/usr/bin}/yum" ]; then
printf '%s\n' yum
else
return 1
fi
}
# Keep the marker assignment last so callers know the whole library loaded.
xcat_is_el_modular_pkgdir()