diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 6df29182a..fc30f70a8 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -39,6 +39,19 @@ jobs: - name: Run tests if: ${{ !cancelled() }} run: perl github_action_xcat_test.pl + - name: Install package test dependencies + id: package-deps + if: ${{ !cancelled() }} + run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests cpio createrepo-c zstd rsync gnupg libxml-libxml-perl python3-rpm + - name: Exercise probe package payloads + if: ${{ !cancelled() && steps.package-deps.outcome == 'success' }} + run: prove xCAT-test/native/probe_package_payload.t + - name: Exercise release repositories + if: ${{ !cancelled() && steps.package-deps.outcome == 'success' }} + run: prove xCAT-test/native/release_repository.t + - name: Check parsed package relations + if: ${{ !cancelled() && steps.package-deps.outcome == 'success' }} + run: prove xCAT-test/native/package_relations.t rpm_lifecycle_test: runs-on: ubuntu-24.04 timeout-minutes: 30 diff --git a/xCAT-test/bats/README.md b/xCAT-test/bats/README.md index c0c27de93..256cf8544 100644 --- a/xCAT-test/bats/README.md +++ b/xCAT-test/bats/README.md @@ -32,7 +32,7 @@ Missing prerequisites fail these tests without stopping unrelated test files. Non-Linux hosts report a skip. Set `TMPDIR` to a writable, executable filesystem if the default temporary directory is mounted with `noexec`. -`go_xcat_os_release.bats` and the full-script cases in +`go_xcat_os_release.bats`, `go_xcat_activation.bats` and the full-script cases in `postscripts_apt_get.bats` use the same namespace isolation. They require bats-core 1.4 or newer. OS detection also requires GNU awk for the legacy Red Hat release parser. Postscript cases require GNU diffutils. diff --git a/xCAT-test/bats/go_xcat_activation.bats b/xCAT-test/bats/go_xcat_activation.bats new file mode 100644 index 000000000..22621e168 --- /dev/null +++ b/xCAT-test/bats/go_xcat_activation.bats @@ -0,0 +1,116 @@ +#!/usr/bin/env bats + +load helpers/script_sandbox + +setup() { + setup_script_sandbox awk cat comm cp cut dirname grep head id ln mkdir mktemp mv rm sed sleep sort tr wc xargs + mkdir -p "$fixture/etc/yum.repos.d" "$fixture/tools" + sandbox+=(--uid 0 --gid 0 + --ro-bind "$(repo_path xCAT-server/share/xcat/tools/go-xcat)" /run/go-xcat) + # An inherited library-only setting must not disable the command. + sandbox+=(--setenv GO_XCAT_LIBRARY_ONLY 1) + cat >"$fixture/tools/manager" <<'SH' +#!/bin/sh +operation= +for arg do + case "$arg" in install|remove) operation=$arg; continue ;; esac + [ -n "$operation" ] || continue + case "$arg" in -*) continue ;; esac + printf '%s %s\n' "$operation" "$arg" >> /run/fixture/transactions +done +[ "$operation" != install ] || exit 17 +case "$*" in *repoquery*) printf '%s\n' fixture-package ;; esac +exit 0 +SH + cat >"$fixture/tools/rpm" <<'SH' +#!/bin/sh +case "$*" in + '-q --quiet '*) grep -Fxq "$3" /run/fixture/installed ;; + *) exit 0 ;; +esac +SH + cat >"$fixture/tools/dpkg-query" <<'SH' +#!/bin/sh +if grep -Fxq "$3" /run/fixture/installed; then printf 'install ok installed'; else exit 1; fi +SH + printf '#!/bin/sh\nexit 0\n' >"$fixture/tools/apt-cache" + printf '#!/bin/sh\nexit 22\n' >"$fixture/bin/curl" + chmod +x "$fixture/bin/curl" "$fixture/tools/"* + : >"$fixture/installed" + : >"$fixture/transactions" +} + +invoke() { + local format=$1 architecture=$2 action=$3 + local debian_arch=$architecture + if [ "$format" = rpm ]; then + printf 'ID=almalinux\nVERSION_ID=8.10\n' >"$fixture/etc/os-release" + cp "$fixture/tools/rpm" "$fixture/bin/rpm" + cp "$fixture/tools/manager" "$fixture/bin/dnf" + else + [ "$architecture" != x86_64 ] || debian_arch=amd64 + printf '#!/bin/sh\nprintf "%%s\\n" "%s"\n' "$debian_arch" >"$fixture/bin/dpkg" + chmod +x "$fixture/bin/dpkg" + mkdir -p "$fixture/etc/apt/sources.list.d" "$fixture/var/lib/apt/lists" + printf 'ID=ubuntu\nVERSION_ID=24.04\n' >"$fixture/etc/os-release" + printf 'DISTRIB_CODENAME=noble\n' >"$fixture/etc/lsb-release" + printf 'Package: fixture-package\nVersion: 1\n' >"$fixture/var/lib/apt/lists/test_xcat-core_dists_noble_main_binary-${debian_arch}_Packages" + cp "$fixture/var/lib/apt/lists/test_xcat-core_dists_noble_main_binary-${debian_arch}_Packages" "$fixture/var/lib/apt/lists/test_xcat-dep_dists_noble_main_binary-${debian_arch}_Packages" + cp "$fixture/tools/dpkg-query" "$fixture/bin/dpkg-query" + cp "$fixture/tools/apt-cache" "$fixture/bin/apt-cache" + cp "$fixture/tools/manager" "$fixture/bin/apt-get" + fi + run "${sandbox[@]}" --setenv HOSTTYPE "$architecture" \ + timeout 30 bash /run/go-xcat -y \ + --xcat-core=https://packages.example.test/core \ + --xcat-dep=https://packages.example.test/dep "$action" +} + +check_uninstall() { + local format=$1 selection=$2 name prefix=xCAT + [ "$format" != deb ] || prefix=xcat + local architectures=(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x) + [ "$selection" != subset ] || architectures=(ppc64le riscv64) + for name in "${architectures[@]}"; do + [ "$format" != deb ] || name=${name//_/-} + printf '%s-genesis-openembedded-%s\n' "$prefix" "$name" + done >"$fixture/installed" + printf 'unrelated-package\n' >>"$fixture/installed" + invoke "$format" x86_64 uninstall + [ "$status" -eq 0 ] || { echo "$output" >&2; return 1; } + grep -v '^unrelated-package$' "$fixture/installed" | sed 's/^/remove /' | sort >"$fixture/expected" + sort "$fixture/transactions" >"$fixture/actual" + diff -u "$fixture/expected" "$fixture/actual" +} + +check_install() { + local format=$1 architecture=$2 prefix=xCAT legacy=ppc64 x86=x86_64 + if [ "$format" = deb ]; then prefix=xcat; legacy=ppc64el; x86=amd64; fi + invoke "$format" "$architecture" install + [ "$status" -eq 17 ] || { echo "$output" >&2; return 1; } + local packages=("perl-$prefix" "$prefix-client" "$prefix" "$prefix-buildkit" "$prefix-server" + elilo-xcat grub2-xcat ipmitool-xcat syslinux-xcat ipxe-xcat xnba-undi) + if [ "$architecture" = riscv64 ]; then + packages+=("$prefix-genesis-openembedded-riscv64") + else + packages+=("$prefix-genesis-scripts-$legacy" "$prefix-genesis-scripts-$x86" + "$prefix-genesis-base-$legacy" "$prefix-genesis-base-$x86") + fi + [ "$format" != rpm ] || packages+=(initscripts) + printf 'install %s\n' "${packages[@]}" | sort >"$fixture/expected" + sort "$fixture/transactions" >"$fixture/actual" + diff -u "$fixture/expected" "$fixture/actual" || { + echo "$output" >&2 + [ ! -f "$fixture/tmp/go-xcat.log" ] || cat "$fixture/tmp/go-xcat.log" >&2 + return 1 + } +} + +@test "RPM uninstall removes all installed OpenEmbedded architectures" { check_uninstall rpm all; } +@test "DEB uninstall removes all installed OpenEmbedded architectures" { check_uninstall deb all; } +@test "RPM uninstall excludes absent and unrelated packages" { check_uninstall rpm subset; } +@test "DEB uninstall excludes absent and unrelated packages" { check_uninstall deb subset; } +@test "RPM RISC-V installation selects OpenEmbedded and keeps other packages" { check_install rpm riscv64; } +@test "DEB RISC-V installation selects OpenEmbedded and keeps other packages" { check_install deb riscv64; } +@test "RPM x86 installation retains its legacy Genesis packages" { check_install rpm x86_64; } +@test "DEB x86 installation retains its legacy Genesis packages" { check_install deb x86_64; } diff --git a/xCAT-test/lib/XCAT/Test/Package.pm b/xCAT-test/lib/XCAT/Test/Package.pm new file mode 100644 index 000000000..23186bc13 --- /dev/null +++ b/xCAT-test/lib/XCAT/Test/Package.pm @@ -0,0 +1,26 @@ +package XCAT::Test::Package; + +use strict; +use warnings; +use Cwd qw(getcwd); +use Exporter qw(import); +use XCAT::Test::Lifecycle (); + +our @EXPORT_OK = qw(run_in); + +sub run_in { + my ($directory, @command) = @_; + my $previous = getcwd(); + chdir($directory) or die "chdir $directory: $!"; + my ($stdout, $stderr, $status); + my $ok = eval { + ($status, $stdout, $stderr) = XCAT::Test::Lifecycle::command(@command); + 1; + }; + my $error = $@; + chdir($previous) or die "chdir $previous: $!"; + die $error unless $ok; + return ($status, $stdout, $stderr); +} + +1; diff --git a/xCAT-test/native/README.md b/xCAT-test/native/README.md index 511d56ff9..b1dc9fa05 100644 --- a/xCAT-test/native/README.md +++ b/xCAT-test/native/README.md @@ -44,6 +44,23 @@ packages, execute target-architecture binaries or prove that the generated image ## Other native tests +The pull-request workflow runs these packaging cases explicitly as an ordinary +user: + +``` +prove xCAT-test/native/probe_package_payload.t \ + xCAT-test/native/release_repository.t \ + xCAT-test/native/package_relations.t +``` + +They need RPM build/query tools, cpio, createrepo_c, zstd, rsync, GnuPG, Python 3 +with RPM bindings, user/mount namespaces, and the Perl modules Capture::Tiny, +File::Slurper, Parallel::ForkManager and XML::LibXML. The probe command also +needs `which`. The release test uses an ephemeral signing key and removes its +agent. These checks inspect real packages and repository output; they do not +qualify package installation on every target architecture. On EL8, install both +`python36` and `python3-rpm` to provide the interpreter and its RPM bindings. + Run the package and unprivileged namespace cases as an ordinary user: ``` diff --git a/xCAT-test/native/package_relations.t b/xCAT-test/native/package_relations.t new file mode 100644 index 000000000..a49b6670f --- /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 die($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")); + die("$package $arch $macro: $err") if $rc; + $out =~ s/[^\S\n]+$//mg; + return $out; +} diff --git a/xCAT-test/native/probe_package_payload.t b/xCAT-test/native/probe_package_payload.t new file mode 100644 index 000000000..15dba12c4 --- /dev/null +++ b/xCAT-test/native/probe_package_payload.t @@ -0,0 +1,70 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use File::Path qw(make_path); +use File::Slurper qw(read_binary write_text); +use File::Temp qw(tempdir); +use FindBin; +use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../../build-utils/lib"; +use Test::More; +use XCAT::BuildUtils qw(stage_xcat_probe_sources); +use XCAT::Test::File qw(repo_path slurp_repo_file); +use XCAT::Test::Package qw(run_in); + +plan skip_all => 'requires Linux package tools' unless $^O eq 'linux'; +die('run package builds as an unprivileged user') unless $>; +my $root = tempdir(CLEANUP => 1); +make_path(map { "$root/$_" } qw(SOURCES BUILD BUILDROOT RPMS SRPMS SPECS home)); +local %ENV = (%ENV, HOME => "$root/home", LC_ALL => 'C'); +delete @ENV{qw(PERL5LIB PERL5OPT PERLLIB XCATROOT XCATCFG)}; +stage_xcat_probe_sources(repo_path('.'), "$root/SOURCES", '9.9.9', 1600000000); +my @helpers = qw(CommandUtils.pm GlobalDef.pm NetworkUtils.pm PacketCapture.pm ServiceNodeUtils.pm); +my @commands = qw(code_template detect_dhcpd discovery osdeploy xcatmn); + +sub command { + my ($directory, @args) = @_; + my ($rc, $out, $err) = run_in($directory, @args); + unless (is($rc, 0, "$args[0] completes")) { + diag($out, $err); + die("@args failed"); + } + return $out; +} + +command($root, 'rpmbuild', '-bb', '--define', "_topdir $root", + '--define', 'version 9.9.9', '--define', 'release 1', repo_path('xCAT-probe/xCAT-probe.spec')); +my @rpms = glob("$root/RPMS/noarch/xCAT-probe-*.rpm"); +is(scalar(@rpms), 1, 'build produces one probe RPM') or die('missing probe RPM'); +my $requires = command($root, 'rpm', '-qp', '--requires', $rpms[0]); +like($requires, qr/^iproute$/m, 'RPM requires the Linux socket tool provider'); +make_path("$root/rpm"); +command($root, 'bash', '-o', 'pipefail', '-c', + 'cd "$1" && rpm2cpio "$2" | cpio -idm --quiet', 'bash', "$root/rpm", $rpms[0]); +check_payload("$root/rpm", 'RPM'); + +sub check_payload { + my ($destination, $label) = @_; + my $prefix = "$destination/opt/xcat"; + my $helper_dir = "$prefix/probe/lib/perl/xCAT"; + ok(-x $helper_dir, "$label helper directory is searchable") + or die(sprintf('%s has mode %04o', $helper_dir, (stat($helper_dir))[2] & 0777)); + for my $helper (@helpers) { + my $path = "$prefix/probe/lib/perl/xCAT/$helper"; + ok(-f $path, "$label carries $helper") or die("missing $path"); + is(read_binary($path), slurp_repo_file("perl-xCAT/xCAT/$helper"), + "$label carries the canonical $helper bytes"); + is((stat($path))[2] & 0777, 0644, "$label helper is readable without execute permission"); + } + write_text("$prefix/bin/xcatclient", "#!/bin/sh\nprintf '[ok]:fixture client\\n'\n"); + chmod 0755, "$prefix/bin/xcatclient" or die $!; + local $ENV{XCATROOT} = $prefix; + local $ENV{PATH} = "$prefix/bin:$ENV{PATH}"; + for my $name (@commands) { + my $out = command($root, "$prefix/probe/subcmds/$name", '-T'); + like($out, qr/^\[ok\]\s*:/m, "$label $name self-test is ready"); + } + my $list = command($root, "$prefix/bin/xcatprobe", '-l'); + like($list, qr/^\Q$_\E\s/m, "$label lists $_") for @commands; +} + +done_testing(); diff --git a/xCAT-test/native/release_repository.t b/xCAT-test/native/release_repository.t new file mode 100644 index 000000000..fca0c8ae2 --- /dev/null +++ b/xCAT-test/native/release_repository.t @@ -0,0 +1,153 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use File::Copy qw(copy); +use File::Path qw(make_path); +use File::Slurper qw(read_binary write_text); +use File::Temp qw(tempdir); +use FindBin; +use IO::Uncompress::Gunzip qw(gunzip $GunzipError); +use XML::LibXML; +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 package tools' unless $^O eq 'linux'; +die('run package builds as an unprivileged user') unless $>; +my $root = tempdir(CLEANUP => 1); +make_path(map { "$root/$_" } qw(home home/keys SOURCES BUILD BUILDROOT RPMS SRPMS SPECS work)); +chmod 0700, "$root/home/keys" or die $!; +local %ENV = (%ENV, HOME => "$root/home", GNUPGHOME => "$root/home/keys", LC_ALL => 'C'); +delete @ENV{qw(PERL5LIB PERL5OPT PERLLIB)}; +write_text("$root/home/.rpmmacros", "%__gpg /usr/bin/gpg\n"); +command($root, 'gpg', '--batch', '--pinentry-mode', 'loopback', '--passphrase', '', + '--quick-generate-key', 'Package Test ', 'rsa2048', 'sign', '0'); +END { + local $?; + system('gpgconf', '--homedir', "$root/home/keys", '--kill', 'gpg-agent') if defined $root; +} +command($root, 'tar', '-czf', "$root/SOURCES/xCAT-release-9.9.9.tar.gz", + '-C', repo_path('.'), 'xCAT-release'); +command($root, 'rpmbuild', '-ba', '--define', "_topdir $root", + '--define', 'version 9.9.9', '--define', 'release 1', repo_path('xCAT-release/xCAT-release.spec')); +my $rpm = 'xCAT-release-9.9.9-1.noarch.rpm'; +my $srpm = 'xCAT-release-9.9.9-1.src.rpm'; +ok(-f "$root/RPMS/noarch/$rpm", 'real release RPM was built') or die('missing release RPM'); +my $requirements = command($root, 'rpm', '-qp', '--requires', "$root/RPMS/noarch/$rpm"); +like($requirements, qr/^dnf$/m, 'release package requires DNF'); +my $files = command($root, 'rpm', '-qp', '--qf', '[%{FILENAMES}\t%{FILEFLAGS}\n]', "$root/RPMS/noarch/$rpm"); +for my $repo (qw(xcat-core xcat-dep xcat-dep-common)) { + like($files, qr{^/etc/yum\.repos\.d/\Q$repo\E\.repo\t17$}m, "$repo remains config(noreplace)"); +} +make_path("$root/work/build-utils/lib/XCAT"); +copy(repo_path('buildrpms.pl'), "$root/work/buildrpms.pl") or die $!; +copy(repo_path('build-utils/lib/XCAT/BuildUtils.pm'), "$root/work/build-utils/lib/XCAT/BuildUtils.pm") or die $!; +write_text("$root/work/Version", "9.9.9\n"); +write_text("$root/work/Gitepoch", "1600000000\n"); +write_text("$root/work/Gitinfo", ('a' x 40) . "\n"); +make_path("$root/input/SRPMS"); +copy("$root/RPMS/noarch/$rpm", "$root/input/$rpm") or die $!; +copy("$root/SRPMS/$srpm", "$root/input/SRPMS/$srpm") or die $!; +copy("$root/RPMS/noarch/$rpm", "$root/input/xCAT-release-latest.noarch.rpm") or die $!; + +for my $signed (0, 1) { + for my $attempt (1, 2) { + command("$root/work", $^X, 'buildrpms.pl', '--merge-core-repos', + '--target', 'alma+epel-10-x86_64', '--release', '1', + '--output-dir', "$root/output", '--input-core-repos', "$root/input", + $signed ? ('--gpg-sign', '--gpg-key-name', 'package@example.test') : ()); + check_repository("$root/output", $signed, "merge signed=$signed run=$attempt"); + } +} + +my $target = 'alma+epel-10-x86_64'; +my $cached = "$root/work/dist/$target/rpms"; +make_path("$cached/SRPMS", "$root/mock", "$root/locks"); +copy("$root/RPMS/noarch/$rpm", "$cached/$rpm") or die $!; +copy("$root/SRPMS/$srpm", "$cached/SRPMS/$srpm") or die $!; +command($root, 'cp', '-a', repo_path('xCAT-release'), "$root/work/xCAT-release"); +write_text("$root/mock/xCAT-release-$target.cfg", "# cached build\n"); +write_text("$root/os-release", "ID=almalinux\nVERSION_ID=10\n"); +local $ENV{BUILD_FIXTURE} = $root; +command("$root/work", 'unshare', '--user', '--map-root-user', '--mount', + 'bash', repo_path('xCAT-test/native/fixtures/buildrpms-namespace.sh'), + 'sh', '-c', 'trap \'gpgconf --homedir "$GNUPGHOME" --kill gpg-agent\' EXIT; "$@"', 'sign-test', + $^X, 'buildrpms.pl', '--package', 'xCAT-release', '--target', $target, + '--release', '1', '--nproc', '1', '--gpg-sign', '--gpg-key-name', 'package@example.test'); +check_repository($cached, 1, 'cached build finalization'); + +write_text("$root/work/schedule.pl", <<'PERL'); +BEGIN { + require Parallel::ForkManager; + no warnings 'redefine'; + *Parallel::ForkManager::start = sub { + print "scheduled $_[1]\n" if defined $_[1]; + return 1; + }; + *Parallel::ForkManager::wait_all_children = sub { }; +} +do './buildrpms.pl'; +die $@ if $@; +PERL +my $schedule = command("$root/work", 'unshare', '--user', '--map-root-user', '--mount', + 'bash', repo_path('xCAT-test/native/fixtures/buildrpms-namespace.sh'), + $^X, 'schedule.pl', '--target', $target, '--release', '1', '--nproc', '1'); +is(scalar(grep { $_ eq "scheduled xCAT-release-$target" } split /\n/, $schedule), 1, + 'default build schedules the release package once'); + +unlink "$root/input/$rpm" or die $!; +command("$root/work", $^X, 'buildrpms.pl', '--merge-core-repos', + '--target', $target, '--release', '1', '--output-dir', "$root/output", + '--input-core-repos', "$root/input"); +ok(!-e "$root/output/xCAT-release-latest.noarch.rpm", 'missing release package leaves no stale alias'); +is_deeply([primary_packages("$root/output")], [], 'missing release package leaves an empty binary index'); +done_testing(); + +sub command { + my ($directory, @args) = @_; + my ($rc, $out, $err) = run_in($directory, @args); + is($rc, 0, "$args[0] completes") or do { diag($out, $err); die("@args failed"); }; + return $out; +} + +sub primary_packages { + my ($directory) = @_; + my $repomd = XML::LibXML->load_xml(location => "$directory/repodata/repomd.xml"); + my ($location) = $repomd->findnodes('//*[local-name()="data"][@type="primary"]/*[local-name()="location"]'); + die 'missing primary metadata' unless $location; + my $path = "$directory/" . $location->getAttribute('href'); + my $xml; + if ($path =~ /\.gz$/) { + gunzip $path => \$xml, Transparent => 0 or die $GunzipError; + } elsif ($path =~ /\.zst$/) { + my ($rc, $out, $err) = run_in($directory, 'zstd', '-dc', $path); + die "Cannot decode primary metadata: $err" if $rc; + $xml = $out; + } else { + die "Unsupported primary metadata format: $path"; + } + return XML::LibXML->load_xml(string => $xml)->findnodes('//*[local-name()="package"]'); +} + +sub check_repository { + my ($directory, $signed, $label) = @_; + my $alias = "$directory/xCAT-release-latest.noarch.rpm"; + ok(-f $alias, "$label publishes the alias") or die('missing bootstrap alias'); + is(read_binary($alias), read_binary("$directory/$rpm"), "$label alias has final RPM bytes"); + is((stat($alias))[2] & 0777, 0644, "$label alias is readable"); + my @packages = primary_packages($directory); + is(scalar(@packages), 1, "$label indexes the release package once"); + for my $package (@packages) { + my ($location) = $package->findnodes('./*[local-name()="location"]'); + is($location->getAttribute('href'), $rpm, "$label indexes the versioned filename only"); + } + if ($signed) { + for my $repo ($directory, "$directory/SRPMS") { + command($root, 'gpg', '--verify', "$repo/repodata/repomd.xml.asc", "$repo/repodata/repomd.xml"); + } + my $signature = command($root, 'rpm', '-qp', '--qf', '%{RSAHEADER:pgpsig}', $alias); + like($signature, qr/RSA.*Key ID/i, "$label alias carries the package signature"); + isnt(read_binary($alias), read_binary("$root/RPMS/noarch/$rpm"), "$label signing changed the unsigned RPM"); + } +} diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t index a550cbe09..5a694aa5b 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, @@ -86,26 +57,6 @@ like( 'DEB service nodes recommend the s390x image', ); -my $go_xcat = slurp_repo_file('xCAT-server/share/xcat/tools/go-xcat'); -unlike( - $go_xcat, - qr/GO_XCAT_LIBRARY_ONLY/, - 'go-xcat cannot be disabled by an inherited test environment variable', -); -for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x)) { - like( - $go_xcat, - qr/\bxCAT-genesis-openembedded-\Q$architecture\E\b/, - "go-xcat removes the $architecture RPM image", - ); - (my $deb_architecture = $architecture) =~ tr/_/-/; - like( - $go_xcat, - qr/\bxcat-genesis-openembedded-\Q$deb_architecture\E\b/, - "go-xcat removes the $architecture DEB image", - ); -} - my $mknb_pod = slurp_repo_file('xCAT-client/pods/man8/mknb.8.pod'); like( $mknb_pod, diff --git a/xCAT-test/unit/riscv64_packaging.t b/xCAT-test/unit/riscv64_packaging.t index f21bbfc88..6fa3d5a14 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( @@ -75,9 +18,9 @@ open( 'sh', '-c', '. "$1"; printf "%s\n%s\n%s\n" "$XCAT_CORE_RPM_ARCHES" "$XCAT_LOCAL_RPM_ARCHES" "$XCAT_LOCAL_COLLECT_ARCHES"', 'sh', $architectures, -) or BAIL_OUT("unable to load $architectures: $!"); +) or die("unable to load $architectures: $!"); my @architecture_sets = <$arch_fh>; -close($arch_fh) or BAIL_OUT("unable to read architecture data from $architectures"); +close($arch_fh) or die("unable to read architecture data from $architectures"); chomp @architecture_sets; is( diff --git a/xCAT-test/unit/xcat_probe_package_payload.t b/xCAT-test/unit/xcat_probe_package_payload.t index e690b8740..bac1f371b 100644 --- a/xCAT-test/unit/xcat_probe_package_payload.t +++ b/xCAT-test/unit/xcat_probe_package_payload.t @@ -1,143 +1,14 @@ #!/usr/bin/env perl use strict; use warnings; - -use File::Copy qw(copy); -use File::Path qw(make_path); -use File::Slurper qw(write_text); -use File::Spec; -use File::Temp qw(tempdir); use FindBin; use lib "$FindBin::Bin/../lib"; -use lib "$FindBin::Bin/../../build-utils/lib"; use Test::More; +use XCAT::Test::File qw(slurp_repo_file); -use XCAT::BuildUtils qw(XCAT_PROBE_HELPERS stage_probe_helpers); -use XCAT::Test::File qw(repo_path slurp_repo_file); - -my @helpers = qw( - CommandUtils.pm - GlobalDef.pm - NetworkUtils.pm - PacketCapture.pm - ServiceNodeUtils.pm -); -my @affected_subcommands = qw( - code_template - detect_dhcpd - discovery - osdeploy - xcatmn -); - -# How buildrpms.pl stages the xCAT-probe sources is asserted by -# xcat_probe_sources_staged.t, which runs the staging and reads the archive. Seven assertions -# here matched the text of buildrpms.pl instead, and four of them could not fail: three matched -# the xCAT-release routine, which stages the same way, and one spanned the rest of the file. All -# seven stayed green while the staging directory was unset and every RPM target died. -my $installed_probe_test = - slurp_repo_file('xCAT-test/autotest/testcase/probe/xcatproble_list'); -my $rpm_spec = slurp_repo_file('xCAT-probe/xCAT-probe.spec'); -my $debian_control = slurp_repo_file('xCAT-probe/debian/control'); -like( - $rpm_spec, - qr/%if 0%\{\?suse_version\}\s+Requires: iproute2\s+%else\s+Requires: iproute\s+%endif/s, - 'RPM package requires the distro-specific provider of ss' -); -like( - $debian_control, - qr/^Depends:.*\biproute2\s*\|\s*net-tools\b/m, - 'Debian package requires ss or the legacy netstat provider' -); - -# The Debian builder stages the helpers by calling stage_probe_helpers, so run it and -# look at what it produced. The predecessor matched a `cp -f` line in build-ubunturepo, -# which passed whenever that text was reformatted and failed whenever it moved. -my $staged_probe_dir = File::Spec->catdir(tempdir(CLEANUP => 1), 'lib', 'perl', 'xCAT'); -stage_probe_helpers(repo_path(File::Spec->catdir('perl-xCAT', 'xCAT')), $staged_probe_dir); - -for my $helper (@helpers) { - my $source = repo_path(File::Spec->catfile('perl-xCAT', 'xCAT', $helper)); - ok(-f $source, "$helper source exists"); - ok( - scalar(grep { $_ eq $helper } XCAT_PROBE_HELPERS), - "the shared builder helper list carries $helper" - ); - ok( - -f File::Spec->catfile($staged_probe_dir, $helper), - "Debian builder stages $helper" - ); - like( - $installed_probe_test, - qr/cmd:for module in [^;]*\b\Q$helper\E\b[^;]*; do test -r/, - "installed probe payload checks $helper" - ); -} - -my $tmpdir = tempdir(CLEANUP => 1); -my $xcatroot = File::Spec->catdir($tmpdir, 'opt', 'xcat'); -my $probe_root = File::Spec->catdir($xcatroot, 'probe'); -my $bin_dir = File::Spec->catdir($xcatroot, 'bin'); -my $subcmd_dir = File::Spec->catdir($probe_root, 'subcmds'); -my $helper_dir = File::Spec->catdir($probe_root, 'lib', 'perl', 'xCAT'); - -make_path($probe_root, $bin_dir); -copy_tree(repo_path(File::Spec->catdir('xCAT-probe', 'lib')), File::Spec->catdir($probe_root, 'lib')); -copy_tree(repo_path(File::Spec->catdir('xCAT-probe', 'subcmds')), $subcmd_dir); - -my $xcatprobe_source = repo_path(File::Spec->catfile('xCAT-probe', 'xcatprobe')); -my $xcatprobe = File::Spec->catfile($bin_dir, 'xcatprobe'); -copy($xcatprobe_source, $xcatprobe) or die "copy $xcatprobe_source: $!"; -chmod 0755, $xcatprobe or die "chmod $xcatprobe: $!"; - -make_path($helper_dir, File::Spec->catdir($subcmd_dir, 'bin')); -for my $helper (@helpers) { - my $source = repo_path(File::Spec->catfile('perl-xCAT', 'xCAT', $helper)); - my $destination = File::Spec->catfile($helper_dir, $helper); - copy($source, $destination) or die "copy $source: $!"; - chmod 0644, $destination or die "chmod $destination: $!"; -} - -my $xcatclient = File::Spec->catfile($bin_dir, 'xcatclient'); -write_text($xcatclient, "#!/bin/sh\nprintf '[ok]:dummy xcatclient\\n'\n"); -chmod 0755, $xcatclient or die "chmod $xcatclient: $!"; - -local $ENV{XCATROOT} = $xcatroot; -local $ENV{PATH} = "$bin_dir:$ENV{PATH}"; -local $ENV{PERL5LIB}; -local $ENV{PERL5OPT}; -local $ENV{PERLLIB}; -delete $ENV{PERL5LIB}; -delete $ENV{PERL5OPT}; -delete $ENV{PERLLIB}; - -for my $subcommand (@affected_subcommands) { - my $command = File::Spec->catfile($subcmd_dir, $subcommand); - my ($rc, $output) = run_command($command, '-T'); - is($rc, 0, "$subcommand self-test exits successfully") or diag($output); - like($output, qr/^\[ok\]\s*:/m, "$subcommand self-test reports ready"); -} - -my ($list_rc, $list_output) = run_command($xcatprobe, '-l'); -is($list_rc, 0, 'xcatprobe list exits successfully') or diag($list_output); -my %listed = map { /^([^\s].*?)\s/ ? ($1 => 1) : () } split /\n/, $list_output; -for my $subcommand (@affected_subcommands) { - ok($listed{$subcommand}, "xcatprobe lists $subcommand") or diag($list_output); -} +my $control = slurp_repo_file('xCAT-probe/debian/control'); +my ($probe) = $control =~ /^Package: xcat-probe\n(.*?)(?:\n\n|\z)/ms; +like($probe // '', qr/^Depends:.*\biproute2\s*\|\s*net-tools\b/m, + 'Debian probe keeps the socket-tool alternatives'); done_testing(); - -sub copy_tree { - my ($source, $destination) = @_; - my $rc = system('cp', '-R', $source, $destination); - is($rc, 0, "copied $source into the package fixture") - or die("unable to create package fixture from $source"); -} - -sub run_command { - my (@command) = @_; - open(my $fh, '-|', @command) or die "run @command: $!"; - my $output = do { local $/; <$fh> }; - close($fh); - return ($? >> 8, $output // ''); -} diff --git a/xCAT-test/unit/xcat_release_package.t b/xCAT-test/unit/xcat_release_package.t index b8e1f55e4..61925b88d 100644 --- a/xCAT-test/unit/xcat_release_package.t +++ b/xCAT-test/unit/xcat_release_package.t @@ -3,13 +3,12 @@ use strict; use warnings; use Digest::SHA qw(sha256_hex); -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, '..', '..')); - -my $spec = read_file('xCAT-release/xCAT-release.spec'); +my $spec = slurp_repo_file('xCAT-release/xCAT-release.spec'); like($spec, qr/^Name:\s+xCAT-release$/m, 'package has the expected name'); like($spec, qr/^Source0:\s+xCAT-release-%\{version\}\.tar\.gz$/m, 'source archive follows the package name'); like($spec, qr/^BuildArch:\s+noarch$/m, 'package is architecture independent'); @@ -19,7 +18,7 @@ like($spec, qr/^%config\(noreplace\) .*xcat-dep\.repo$/m, 'dependency repo prese like($spec, qr/^%config\(noreplace\) .*xcat-dep-common\.repo$/m, 'common dependency repo preserves local changes'); like($spec, qr{RPM-GPG-KEY-xCAT}, 'package installs the signing key'); -my $core = read_file('xCAT-release/xcat-core.repo'); +my $core = slurp_repo_file('xCAT-release/xcat-core.repo'); assert_repo_security($core, 'core'); like( $core, @@ -27,7 +26,7 @@ like( 'core repo uses the stable HTTPS endpoint' ); -my $dep = read_file('xCAT-release/xcat-dep.repo'); +my $dep = slurp_repo_file('xCAT-release/xcat-dep.repo'); assert_repo_security($dep, 'dependency'); like( $dep, @@ -35,7 +34,7 @@ like( 'dependency repo follows the DNF release and architecture variables' ); -my $common_dep = read_file('xCAT-release/xcat-dep-common.repo'); +my $common_dep = slurp_repo_file('xCAT-release/xcat-dep-common.repo'); assert_repo_security($common_dep, 'common dependency'); like( $common_dep, @@ -48,7 +47,7 @@ like( 'common dependency repo is independent of the management-node distribution' ); -my $key = read_file('xCAT-release/RPM-GPG-KEY-xCAT'); +my $key = slurp_repo_file('xCAT-release/RPM-GPG-KEY-xCAT'); like($key, qr/^-----BEGIN PGP PUBLIC KEY BLOCK-----$/m, 'signing key is ASCII armored'); is( sha256_hex($key), @@ -56,40 +55,6 @@ is( 'packaged key matches the published xCAT signing key' ); -my $builder = read_file('buildrpms.pl'); -like($builder, qr/^\s+xCAT-release\s*$/m, 'default RPM build includes xCAT-release'); -like( - $builder, - qr{\$repodir/xCAT-release-latest\.noarch\.rpm}, - 'stable bootstrap alias follows the package name' -); -like( - $builder, - qr{\$repodir/xCAT-release-\$VERSION-\$RELEASE\.noarch\.rpm}, - 'stable bootstrap alias selects the xCAT-release RPM' -); -like( - $builder, - qr/unlink \$alias.*?createrepo_dir\(\$repodir/s, - 'stable bootstrap alias is excluded from repository metadata' -); -like( - $builder, - qr/cp \$release_rpms\[0\], \$alias/, - 'repository export creates the stable bootstrap filename' -); -my $sign_call = rindex($builder, 'sign_rpms($target)'); -my $alias_call = rindex($builder, 'write_release_alias("dist/$target/rpms")'); -ok( - $sign_call >= 0 && $alias_call > $sign_call, - 'stable bootstrap alias is created after signed metadata is finalized' -); -like( - $builder, - qr/sub merge_core_repos \{.*?write_repo_metadata_dir\(\$out\);.*?write_release_alias\(\$out\);/s, - 'assembled core repository creates the stable alias after final metadata' -); - done_testing(); sub assert_repo_security { @@ -103,12 +68,3 @@ sub assert_repo_security { "$label repo uses the packaged signing key" ); } - -sub read_file { - my ($file) = @_; - my $path = File::Spec->catfile($repo_root, split m{/}, $file); - open(my $fh, '<', $path) or die "open $path: $!"; - my $contents = do { local $/; <$fh> }; - close($fh) or die "close $path: $!"; - return $contents; -} 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();