From dbbdcddcb2e8972596e2e99ef1d88d063ba82ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Thu, 8 Oct 2026 22:48:51 -0300 Subject: [PATCH] test(packaging): query resolved RPM dependencies --- .github/workflows/xcat_test.yml | 4 +- xCAT-test/native/package_relations.t | 100 ++++++++++++++++++ .../unit/genesis_openembedded_activation.t | 29 ----- xCAT-test/unit/riscv64_packaging.t | 59 +---------- .../unit/xcat_server_digest_sha_dependency.t | 40 ++----- 5 files changed, 110 insertions(+), 122 deletions(-) create mode 100644 xCAT-test/native/package_relations.t diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 9f9ab5258..6a480ce54 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -17,12 +17,14 @@ jobs: - name: Test Debian package lifecycle run: prove -v xCAT-test/native/xcatd_debian_lifecycle.t xCAT-test/native/xcatd_restart_callers.t xCAT-test/native/apache_package_lifecycle.t xCAT-test/native/package_upgrade_logging.t - name: Install package test dependencies - run: sudo apt-get install -y --no-install-recommends cpio createrepo-c rsync gnupg libxml-libxml-perl + run: sudo apt-get install -y --no-install-recommends cpio createrepo-c rsync gnupg libxml-libxml-perl python3-rpm - name: Exercise probe package payloads if: ${{ !cancelled() }} run: prove xCAT-test/native/probe_package_payload.t - name: Exercise release repositories run: prove xCAT-test/native/release_repository.t + - name: Check parsed package relations + run: prove xCAT-test/native/package_relations.t - name: Prepare xCAT syslog directory if: ${{ !cancelled() }} run: sudo install -d -o syslog -g adm -m 0750 /var/log/xcat diff --git a/xCAT-test/native/package_relations.t b/xCAT-test/native/package_relations.t new file mode 100644 index 000000000..1a2b83d40 --- /dev/null +++ b/xCAT-test/native/package_relations.t @@ -0,0 +1,100 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use File::Temp qw(tempdir); +use FindBin; +use lib "$FindBin::Bin/../lib"; +use Test::More; +use XCAT::Test::File qw(repo_path); +use XCAT::Test::Package qw(run_in); + +plan skip_all => 'requires Linux RPM tooling' unless $^O eq 'linux'; +local %ENV = (%ENV, HOME => tempdir(CLEANUP => 1)); +delete @ENV{qw(pcm fsm s390x)}; +my @platforms = ( + ['EL7', 'rhel 7', 0], ['EL8', 'rhel 8', 1], ['EL9', 'rhel 9', 1], + ['EL10', 'rhel 10', 1], ['Fedora', 'fedora 44', 1], + ['SLES12', 'suse_version 1315', 0], ['SLES15', 'suse_version 1500', 1], + ['openEuler', 'openEuler 2', 0], +); +my @images = map { "xCAT-genesis-openembedded-$_" } qw(x86_64 ppc64le riscv64 s390x); +my %legacy = (x86_64 => 'x86_64', ppc64le => 'ppc64', aarch64 => 'aarch64'); +for my $platform (@platforms) { + my ($name, $macro, $weak) = @$platform; + for my $arch (qw(x86_64 ppc64le aarch64 riscv64 s390x)) { + for my $pkg (qw(xCAT xCATsn)) { + my $requires = query($pkg, $arch, $macro, '--requires'); + my $recommends = query($pkg, $arch, $macro, '--recommends'); + unlike($requires, qr/xCAT-genesis-openembedded-/, "$name $arch $pkg never requires optional images"); + my @actual = sort grep { /^xCAT-genesis-openembedded-/ } split /\n/, $recommends; + is_deeply(\@actual, $weak ? [sort @images] : [], "$name $arch $pkg optional image recommendations"); + my @scripts = grep { /^xCAT-genesis-scripts-/ } split /\n/, $requires; + is_deeply(\@scripts, $legacy{$arch} + ? ["xCAT-genesis-scripts-$legacy{$arch} = 1:9.9.9-1"] : [], + "$name $arch $pkg required legacy Genesis scripts"); + if ($arch eq 'riscv64') { + like($requires, $pkg eq 'xCAT' ? qr/^ipmitool-xcat >= 1\.8\.18-4$/m : qr/^ipmitool-xcat >= 1\.8\.17-1$/m, + "$name $pkg RISC-V IPMI dependency"); + unlike($requires, qr/xCAT-genesis-scripts-|xnba-undi|syslinux-xcat|elilo-xcat|%\{/, + "$name $pkg RISC-V has no unavailable legacy payload or unresolved macro"); + } + } + } + for my $s390 (0, 1) { + local $ENV{s390x} = $s390; + my $requires = query('xCAT-server', 'x86_64', $macro, '--requires'); + like($requires, qr/^perl\(Digest::SHA\)$/m, "$name server s390x=$s390 requires Digest::SHA"); + next if $s390; + my $recommends = query('xCAT-server', 'x86_64', $macro, '--recommends'); + is(scalar(grep { $_ eq 'perl-DB_File' } split /\n/, $requires), $name eq 'EL10' ? 0 : 1, + "$name server hard DB_File dependency"); + is(scalar(grep { $_ eq 'perl-DB_File' } split /\n/, $recommends), $name eq 'EL10' ? 1 : 0, + "$name server weak DB_File dependency"); + like($requires, qr/^\Q$_\E$/m, "$name server keeps $_") + for qw(perl-Net-Telnet perl-Net-DNS perl-Crypt-CBC perl-Crypt-Rijndael); + } +} + +for my $platform (@platforms) { + my ($name, $macro) = @$platform; + my ($rc, $filter, $err) = run_in(repo_path('.'), 'python3', '-c', <<'PY', repo_path('xCAT-server/xCAT-server.spec'), $macro); +import rpm, sys +for name in ('rhel', 'fedora', 'suse_version', 'openEuler'): + rpm.delMacro(name) +name, value = sys.argv[2].split() +rpm.addMacro(name, value) +rpm.addMacro('version', '9.9.9') +rpm.addMacro('release', '1') +rpm.addMacro('__requires_exclude', '^retained$') +rpm.spec(sys.argv[1]) +print(rpm.expandMacro('%{?__requires_exclude}')) +PY + is($rc, 0, "$name parses server dependency generator policy") or BAIL_OUT($err); + chomp $filter; + ok('retained' =~ /$filter/, "$name keeps an inherited dependency filter"); + is('perl(DB_File)' =~ /$filter/ ? 1 : 0, $name eq 'EL10' ? 1 : 0, + "$name automatic DB_File dependency exclusion"); + unlike('perl(Digest::SHA)', qr/$filter/, "$name keeps automatic Digest::SHA dependencies"); +} + +for my $case (['Linux', 'rhel 8', 'iproute'], ['SUSE', 'suse_version 1500', 'iproute2']) { + my ($name, $macro, $provider) = @$case; + my $requires = query('xCAT-probe', 'x86_64', $macro, '--requires'); + my @providers = grep { /^iproute2?$/ } split /\n/, $requires; + is_deeply(\@providers, [$provider], "$name probe socket tool provider"); +} +done_testing(); + +sub query { + my ($package, $arch, $macro, $relation) = @_; + my $format = $relation eq '--requires' + ? '[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]' + : '[%{RECOMMENDNAME}\n]'; + my ($rc, $out, $err) = run_in(repo_path('.'), 'rpmspec', '-q', '--target', $arch, + (map { ('--undefine', $_) } qw(rhel fedora suse_version openEuler)), + '--define', $macro, '--define', 'version 9.9.9', '--define', 'release 1', + '--qf', $format, repo_path("$package/$package.spec")); + BAIL_OUT("$package $arch $macro: $err") if $rc; + $out =~ s/[^\S\n]+$//mg; + return $out; +} diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index a550cbe09..743eac141 100644 --- a/xCAT-test/unit/genesis_openembedded_activation.t +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -8,28 +8,6 @@ use Test::More; use XCAT::Test::File qw(slurp_repo_file); -my $rpm_weak_dependencies = join( - "\n", - '%if 0%{?fedora} || 0%{?rhel} >= 8 || 0%{?suse_version} >= 1500', - 'Recommends: xCAT-genesis-openembedded-x86_64', - 'Recommends: xCAT-genesis-openembedded-ppc64le', - 'Recommends: xCAT-genesis-openembedded-riscv64', - 'Recommends: xCAT-genesis-openembedded-s390x', - '%endif', -); - -my $rpm_spec = slurp_repo_file('xCAT/xCAT.spec'); -like( - $rpm_spec, - qr/^\Q$rpm_weak_dependencies\E$/m, - 'RPM weak dependencies stay inside their compatibility guard', -); -unlike( - $rpm_spec, - qr/^Requires:\s+xCAT-genesis-openembedded-/m, - 'missing OpenEmbedded images do not block an RPM upgrade', -); - my $deb_control = slurp_repo_file('xCAT/debian/control'); like( $deb_control, @@ -57,13 +35,6 @@ unlike( 'missing OpenEmbedded images do not block a DEB upgrade', ); -my $sn_rpm_spec = slurp_repo_file('xCATsn/xCATsn.spec'); -like( - $sn_rpm_spec, - qr/^\Q$rpm_weak_dependencies\E$/m, - 'service-node weak dependencies stay inside their compatibility guard', -); - my $sn_deb_control = slurp_repo_file('xCATsn/debian/control'); like( $sn_deb_control, diff --git a/xCAT-test/unit/riscv64_packaging.t b/xCAT-test/unit/riscv64_packaging.t index f21bbfc88..8fb6ec0e1 100644 --- a/xCAT-test/unit/riscv64_packaging.t +++ b/xCAT-test/unit/riscv64_packaging.t @@ -7,66 +7,9 @@ use lib "$FindBin::Bin/../lib"; use lib "$FindBin::Bin/../../build-utils/lib"; use Test::More; -use XCAT::Test::File qw(repo_path slurp_repo_file); +use XCAT::Test::File qw(repo_path); use XCAT::BuildUtils qw(targetarch_from_target); -# riscv64 packaging: the arch-named packages (xCAT, xCATsn) must resolve their -# architecture token and dependencies for riscv64, and the build scripts must -# know the riscv64 package names. The Genesis image comes from the OpenEmbedded -# packages, so there is no arch-named Genesis package to resolve here. - -my $xcat = slurp_repo_file('xCAT/xCAT.spec'); -my ($xcat_rv) = $xcat =~ /^%ifarch riscv64\n((?:#[^\n]*\n|Requires:[^\n]*\n)*)%endif$/m; -like( $xcat_rv || '', qr/^Requires: ipmitool-xcat >= 1\.8\.18-4$/m, 'xCAT.spec requires ipmitool-xcat on riscv64' ); -unlike( $xcat_rv || '', qr/xnba-undi|syslinux-xcat|elilo-xcat/, 'xCAT.spec does not require the x86 PXE loaders on riscv64' ); - -# riscv64 has no legacy Genesis package: the requirement must disappear rather than -# resolve to an unsatisfiable name, and the OpenEmbedded image is recommended instead. -like( $xcat, qr/^%\{\?genesistarch:Requires: xCAT-genesis-scripts-%\{genesistarch\} = 1:%\{version\}-%\{release\}\}$/m, 'xCAT.spec asks for the legacy Genesis package only where the architecture has one' ); -like( $xcat, qr/^Recommends: xCAT-genesis-openembedded-riscv64$/m, 'xCAT.spec recommends the RISC-V OpenEmbedded Genesis image' ); - -SKIP: { - my $rpmspec = qx(command -v rpmspec 2>/dev/null); - chomp($rpmspec); - skip 'rpmspec is not installed', 2 unless $rpmspec && -x $rpmspec; - - my $spec = repo_path('xCAT/xCAT.spec'); - open( my $requires_fh, '-|', - $rpmspec, '-q', '--target', 'riscv64', '--requires', $spec ) - or BAIL_OUT("unable to run $rpmspec: $!"); - my $requires = do { local $/; <$requires_fh> }; - close($requires_fh) - or BAIL_OUT("rpmspec failed for $spec with status " . ($? >> 8)); - - unlike( $requires, qr/genesis-scripts/, 'a riscv64 build requires no legacy Genesis package' ); - unlike( $requires, qr/\Q%{genesistarch}\E/, 'a riscv64 build leaves no unexpanded architecture macro' ); -} - -my $xcatsn = slurp_repo_file('xCATsn/xCATsn.spec'); -my ($xcatsn_rv) = $xcatsn =~ /^%ifarch [^\n]*\briscv64\b[^\n]*\n((?:#[^\n]*\n|Requires:[^\n]*\n)*)%endif$/m; -like( $xcatsn_rv || '', qr/^Requires: ipmitool-xcat >= 1\.8\.17-1$/m, 'xCATsn.spec requires ipmitool-xcat on riscv64' ); -like( $xcatsn, qr/^Recommends: xCAT-genesis-openembedded-riscv64$/m, 'xCATsn.spec recommends the RISC-V OpenEmbedded Genesis image' ); - -my $server = slurp_repo_file('xCAT-server/xCAT-server.spec'); -like( $server, qr/^Recommends: perl-DB_File$/m, 'xCAT-server.spec recommends perl-DB_File on EL10 (riscv64 has no EPEL to provide it)' ); -like( - $server, - qr/^%if 0%\{\?rhel\} >= 10\nRecommends: perl-DB_File\n/m, - 'the weak perl-DB_File dependency is limited to EL10, where riscv64 has no EPEL', -); -like( - $server, - qr/^%else\nRequires: perl-DB_File\n%endif$/m, - 'build hosts without weak dependencies keep the hard perl-DB_File requirement', -); -like( - $server, - qr/^%global __requires_exclude %\{\?__requires_exclude:%\{__requires_exclude\}\|\}\^perl\\\\\(DB_File\\\\\)\$$/m, - 'xCAT-server.spec appends the generated perl(DB_File) requirement to the build root filter', -); -like( $server, qr/^Requires: perl-Net-Telnet perl-Net-DNS perl-Crypt-CBC perl-Crypt-Rijndael$/m, 'xCAT-server.spec keeps the other EL perl requires' ); - - my $architectures = repo_path('build-utils/rpm-architectures.sh'); is(system('sh', '-n', $architectures), 0, 'the shared build architecture data parses as POSIX shell'); open( diff --git a/xCAT-test/unit/xcat_server_digest_sha_dependency.t b/xCAT-test/unit/xcat_server_digest_sha_dependency.t index 417668eb0..93af6e0fc 100644 --- a/xCAT-test/unit/xcat_server_digest_sha_dependency.t +++ b/xCAT-test/unit/xcat_server_digest_sha_dependency.t @@ -1,42 +1,14 @@ #!/usr/bin/env perl use strict; use warnings; - -use File::Spec; use FindBin; +use lib "$FindBin::Bin/../lib"; use Test::More; +use XCAT::Test::File qw(slurp_repo_file); -my $repo_root = File::Spec->rel2abs( - File::Spec->catdir( $FindBin::Bin, '..', '..' ) -); - -sub read_file { - my ($filename) = @_; - open( my $fh, '<', $filename ) - or die "Unable to read $filename: $!"; - my $content = do { local $/; <$fh> }; - close($fh); - return $content; -} - -my $rpm_spec = read_file( - File::Spec->catfile( $repo_root, 'xCAT-server', 'xCAT-server.spec' ) -); -my @rpm_sha_requirements = - $rpm_spec =~ /^Requires:.*\bperl\(Digest::SHA\)(?:\s|$)/mg; -is( - scalar(@rpm_sha_requirements), - 2, - 'both RPM architecture branches require Digest::SHA', -); - -my $debian_control = read_file( - File::Spec->catfile( $repo_root, 'xCAT-server', 'debian', 'control' ) -); -like( - $debian_control, - qr/^Depends:.*\blibdigest-sha-perl\b/m, - 'the Debian package requires Digest::SHA', -); +my $control = slurp_repo_file('xCAT-server/debian/control'); +my ($server) = $control =~ /^Package: xcat-server\n(.*?)(?:\n\n|\z)/ms; +like($server // '', qr/^Depends:.*\blibdigest-sha-perl\b/m, + 'the Debian server package requires Digest::SHA'); done_testing();