From d2fec89988d4318e4c3552a81ed1a64a5fe090b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:01:07 -0300 Subject: [PATCH 1/2] refactor(postscripts): share modular package directory predicate --- xCAT/postscripts/ospkgs | 22 ++-------------------- xCAT/postscripts/otherpkgs | 22 ++-------------------- xCAT/postscripts/xcatpkgutils.sh | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 40 deletions(-) diff --git a/xCAT/postscripts/ospkgs b/xCAT/postscripts/ospkgs index e50b3c4da..20ece81c6 100755 --- a/xCAT/postscripts/ospkgs +++ b/xCAT/postscripts/ospkgs @@ -261,24 +261,6 @@ array_ospkgdirs=($OSPKGDIR) array_empty os_path -is_el_modular_pkgdir() -{ - pmatch "$OSVER" "rhel[89]*" || - pmatch "$OSVER" "rhel1[0-9]*" || - pmatch "$OSVER" "rhels[89]*" || - pmatch "$OSVER" "rhels1[0-9]*" || - pmatch "$OSVER" "centos[89]*" || - pmatch "$OSVER" "centos1[0-9]*" || - pmatch "$OSVER" "rocky[89]*" || - pmatch "$OSVER" "rocky1[0-9]*" || - pmatch "$OSVER" "alma[89]*" || - pmatch "$OSVER" "alma1[0-9]*" || - pmatch "$OSVER" "almalinux[89]*" || - pmatch "$OSVER" "almalinux1[0-9]*" || - pmatch "$OSVER" "ol[89]*" || - pmatch "$OSVER" "ol1[0-9]*" -} - index=0 for dir in ${array_ospkgdirs[@]} do @@ -308,7 +290,7 @@ do ospkgdir="$ospkgdir/SL" fi fi - if ! is_el_modular_pkgdir || \ + if ! xcat_is_el_modular_pkgdir "$OSVER" || \ { [ "$dir" != "$default_pkgdir" ] && [ "$dir" != "$default_pkgdir/" ]; }; then # For EL8+ modular distros, only the base OS pkgdir expands to subrepos. array_set_element os_path $index $ospkgdir @@ -376,7 +358,7 @@ do array_set_element os_path $index $ospkgdir_ok done fi # x86_64 - elif is_el_modular_pkgdir; then + elif xcat_is_el_modular_pkgdir "$OSVER"; then # for EL8+ modular distros the repodata is in ./BaseOS and ./AppStream, not in ./ for arg in "BaseOS" "AppStream" do diff --git a/xCAT/postscripts/otherpkgs b/xCAT/postscripts/otherpkgs index 6dbc9707a..8815e69ca 100755 --- a/xCAT/postscripts/otherpkgs +++ b/xCAT/postscripts/otherpkgs @@ -341,24 +341,6 @@ fi IFS=$OIFS array_empty os_path - is_el_modular_pkgdir() - { - pmatch "$OSVER" "rhel[89]*" || - pmatch "$OSVER" "rhel1[0-9]*" || - pmatch "$OSVER" "rhels[89]*" || - pmatch "$OSVER" "rhels1[0-9]*" || - pmatch "$OSVER" "centos[89]*" || - pmatch "$OSVER" "centos1[0-9]*" || - pmatch "$OSVER" "rocky[89]*" || - pmatch "$OSVER" "rocky1[0-9]*" || - pmatch "$OSVER" "alma[89]*" || - pmatch "$OSVER" "alma1[0-9]*" || - pmatch "$OSVER" "almalinux[89]*" || - pmatch "$OSVER" "almalinux1[0-9]*" || - pmatch "$OSVER" "ol[89]*" || - pmatch "$OSVER" "ol1[0-9]*" - } - is_el_yum_distro() { pmatch "$OSVER" "rhel*" || @@ -398,7 +380,7 @@ fi ospkgdir="$ospkgdir/SL" fi fi - if ! is_el_modular_pkgdir || \ + if ! xcat_is_el_modular_pkgdir "$OSVER" || \ { [ "$dir" != "$default_pkgdir" ] && [ "$dir" != "$default_pkgdir/" ]; }; then # For EL8+ modular distros, only the base OS pkgdir expands to subrepos. array_set_element os_path $index $ospkgdir @@ -441,7 +423,7 @@ fi array_set_element os_path $index $ospkgdir_ok done fi # x86_64 - elif is_el_modular_pkgdir; then + elif xcat_is_el_modular_pkgdir "$OSVER"; then # for EL8+ modular distros the repodata is in ./BaseOS and ./AppStream, not in ./ for arg in "BaseOS" "AppStream" do diff --git a/xCAT/postscripts/xcatpkgutils.sh b/xCAT/postscripts/xcatpkgutils.sh index 8c91b5239..857b1b586 100755 --- a/xCAT/postscripts/xcatpkgutils.sh +++ b/xCAT/postscripts/xcatpkgutils.sh @@ -3,5 +3,20 @@ # Shared POSIX shell helpers for the ospkgs and otherpkgs postscripts. # Keep the marker assignment last so callers know the whole library loaded. + +xcat_is_el_modular_pkgdir() +{ + case "$1" in + rhel[89]*|rhel1[0-9]*|rhels[89]*|rhels1[0-9]*|\ + centos[89]*|centos1[0-9]*|rocky[89]*|rocky1[0-9]*|\ + alma[89]*|alma1[0-9]*|almalinux[89]*|almalinux1[0-9]*|\ + ol[89]*|ol1[0-9]*) + return 0 + ;; + esac + + return 1 +} + # shellcheck disable=SC2034 XCATPKGUTILS_LOADED=1 From 4c889644d5ce1bb96d9d9cf53de7fdc76be0f027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:01:52 -0300 Subject: [PATCH 2/2] test(postscripts): cover modular package directory predicate --- xCAT-test/unit/package_modular_pkgdir.t | 176 ++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 xCAT-test/unit/package_modular_pkgdir.t diff --git a/xCAT-test/unit/package_modular_pkgdir.t b/xCAT-test/unit/package_modular_pkgdir.t new file mode 100644 index 000000000..35e4bd58e --- /dev/null +++ b/xCAT-test/unit/package_modular_pkgdir.t @@ -0,0 +1,176 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Copy qw(copy); +use File::Path qw(make_path); +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 $library = repo_path('xCAT/postscripts/xcatpkgutils.sh'); +my $loader = repo_path('xCAT/postscripts/xcatpkgutils-loader.sh'); + +my @modular_osvers = qw( + rhel8 rhel9.6 rhel10.0 rhels8.10 rhels19 + centos8-stream centos10 rocky9.4 rocky19 + alma8 alma10 almalinux9 almalinux19 ol8 ol10 +); +my @other_osvers = ( '', qw(rhel rhel1 rhel7.9 rhel20 ubuntu24.04 RHEL9 el9) ); + +SKIP: { + skip 'the shared predicate is introduced by the production commit', + scalar( @modular_osvers + @other_osvers ) + unless shared_predicate_available(); + + for my $osver (@modular_osvers) { + is( predicate_status($osver), 0, + "$osver uses modular package directories" ); + } + for my $osver (@other_osvers) { + is( predicate_status($osver), 1, + ( $osver || 'an empty OS version' ) + . ' does not use modular package directories' ); + } +} + +for my $caller (qw(ospkgs otherpkgs)) { + is_deeply( + [ caller_repository_paths( $caller, 'rocky9.4' ) ], + [ + 'package-test-server:INSTALLDIR/rocky9.4/x86_64/BaseOS', + 'package-test-server:INSTALLDIR/rocky9.4/x86_64/AppStream', + ], + "$caller expands a modular OS into BaseOS and AppStream" + ); + is_deeply( + [ caller_repository_paths( $caller, 'rocky7.9' ) ], + ['package-test-server:INSTALLDIR/rocky7.9/x86_64'], + "$caller retains the base directory for a non-modular OS" + ); +} + +done_testing(); + +sub predicate_status { + my ($osver) = @_; + my $status = system( + '/bin/sh', '-c', '. "$1"; xcat_is_el_modular_pkgdir "$2"', + 'package-modular-pkgdir-test', $library, $osver + ); + die "Unable to execute /bin/sh: $!" if $status == -1; + die "/bin/sh terminated by signal " . ( $status & 127 ) + if $status & 127; + return $status >> 8; +} + +sub shared_predicate_available { + return system( + '/bin/sh', '-c', + '. "$1"; command -v xcat_is_el_modular_pkgdir >/dev/null 2>&1', + 'package-modular-pkgdir-test', $library + ) == 0; +} + +sub caller_repository_paths { + my ( $caller, $osver ) = @_; + my $tmpdir = tempdir( CLEANUP => 1 ); + my $bindir = File::Spec->catdir( $tmpdir, 'bin' ); + my $trace = File::Spec->catfile( $tmpdir, 'repository-paths.trace' ); + make_path($bindir); + + my $mount = File::Spec->catfile( $bindir, 'mount' ); + write_fixture( $mount, "#!/bin/sh\nexit 1\n" ); + chmod 0755, $mount or die "Unable to make $mount executable: $!"; + my $uname = File::Spec->catfile( $bindir, 'uname' ); + write_fixture( $uname, "#!/bin/sh\nprintf '%s\\n' Linux\n" ); + chmod 0755, $uname or die "Unable to make $uname executable: $!"; + + for my $source ( + $library, $loader, repo_path("xCAT/postscripts/$caller") + ) + { + my $filename = ( File::Spec->splitpath($source) )[2]; + my $destination = File::Spec->catfile( $tmpdir, $filename ); + copy( $source, $destination ) + or die "Unable to stage $source as $destination: $!"; + chmod 0755, $destination + or die "Unable to make $destination executable: $!"; + } + + my $bash_env = File::Spec->catfile( $tmpdir, 'bash-env.sh' ); + write_fixture( + $bash_env, + <<'SH' +logger() +{ + case " $* " in + *" NFSSERVER="*) + size=$(array_get_size os_path) + index=0 + while [ "$index" -lt "$size" ]; do + array_get_element os_path "$index" + index=$((index + 1)) + done > "$XCAT_REPOSITORY_PATH_TRACE" + exit 73 + ;; + esac + return 0 +} +SH + ); + + local %ENV = %ENV; + delete @ENV{ + qw(HTTPPORT KERNELDIR MASTER NODESETSTATE OTHERPKGDIR OSPKGDIR VERBOSE) + }; + $ENV{ARCH} = 'x86_64'; + $ENV{BASH_ENV} = $bash_env; + $ENV{INSTALLDIR} = 'INSTALLDIR'; + $ENV{NFSSERVER} = 'package-test-server'; + $ENV{OSVER} = $osver; + $ENV{PATH} = "$bindir:$ENV{PATH}"; + $ENV{UPDATENODE} = 1; + $ENV{XCAT_REPOSITORY_PATH_TRACE} = $trace; + if ( $caller eq 'ospkgs' ) { + $ENV{OSPKGS} = 'package-test'; + delete @ENV{qw(OTHERPKGS OTHERPKGS_INDEX)}; + } else { + $ENV{OTHERPKGS_INDEX} = 1; + delete @ENV{qw(OSPKGS)}; + } + + my $script = File::Spec->catfile( $tmpdir, $caller ); + my $status = system($script); + die "Unable to execute $script: $!" if $status == -1; + die "$script terminated by signal " . ( $status & 127 ) + if $status & 127; + die "$script exited unexpectedly with " . ( $status >> 8 ) + unless ( $status >> 8 ) == 73; + + my $contents = read_fixture($trace); + chomp($contents); + return split /\n/, $contents; +} + +sub write_fixture { + my ( $path, $contents ) = @_; + open( my $fh, '>:raw', $path ) + or die "Unable to open $path for writing: $!"; + print {$fh} $contents or die "Unable to write $path: $!"; + close($fh) or die "Unable to close $path: $!"; +} + +sub read_fixture { + my ($path) = @_; + open( my $fh, '<:raw', $path ) + or die "Unable to open $path for reading: $!"; + my $contents = do { local $/; <$fh> }; + die "Unable to read $path: $!" unless defined $contents; + close($fh) or die "Unable to close $path: $!"; + return $contents; +}