From f49941fb62761b3ec78f0c5b9e6bc531f46ec073 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, 22 Sep 2026 23:15:42 -0300 Subject: [PATCH] test: separate native openEuler checks from unit tests --- .github/workflows/xcat_test.yml | 2 +- xCAT-test/bats/go_xcat_openeuler.bats | 162 ++++++++++++++++ xCAT-test/integration/fixtures/dnf.pl | 16 ++ .../integration/fixtures/postscript-root.sh | 33 ++++ .../openeuler_package_postscripts.t | 26 +-- .../openeuler_postscript_repositories.t | 131 +++++++------ xCAT-test/native/buildrpms_openeuler.t | 142 ++++++++++++++ .../buildrpms_release_openeuler.t | 30 +-- .../native/fixtures/buildrpms-namespace.sh | 18 ++ .../{unit => native}/genesis_openeuler.t | 4 +- .../genesis_openeuler_release.t | 1 + xCAT-test/{unit => native}/ip_forwarding.t | 2 +- .../openeuler_genimage_transactions.t | 8 +- .../openeuler_install_repositories.t | 40 ++-- .../{unit => native}/openeuler_networking.t | 2 +- .../openeuler_package_policy.t | 12 +- .../{unit => native}/openeuler_xcatroot.t | 0 .../pgsqlsetup_native_schema.t | 8 +- .../remoteshell_hostkeys_openeuler.t | 4 +- xCAT-test/{unit => native}/syslog_openeuler.t | 1 + .../syslog_openeuler_chroot.t | 4 +- xCAT-test/unit/anaconda_openeuler_media.t | 6 +- xCAT-test/unit/buildrpms_openeuler.t | 149 --------------- xCAT-test/unit/go_xcat_openeuler.t | 174 ------------------ xCAT-test/unit/mysqlsetup_openeuler_reboot.t | 2 +- xCAT-test/unit/openeuler_genimage_bootfiles.t | 8 +- xCAT-test/unit/openeuler_pre_partition.t | 39 ++-- xCAT-test/unit/openeuler_profiles.t | 17 +- xCAT-test/unit/packimage_openeuler_archive.t | 1 + xCAT-test/unit/pgsqlsetup_openeuler.t | 2 +- xCAT-test/unit/xcat_conf_selection.t | 32 ++++ .../unit/xcat_conf_upgrade_preservation.t | 17 -- 32 files changed, 569 insertions(+), 524 deletions(-) create mode 100644 xCAT-test/bats/go_xcat_openeuler.bats create mode 100644 xCAT-test/integration/fixtures/dnf.pl create mode 100644 xCAT-test/integration/fixtures/postscript-root.sh create mode 100644 xCAT-test/native/buildrpms_openeuler.t rename xCAT-test/{unit => native}/buildrpms_release_openeuler.t (90%) create mode 100644 xCAT-test/native/fixtures/buildrpms-namespace.sh rename xCAT-test/{unit => native}/genesis_openeuler.t (98%) rename xCAT-test/{unit => native}/genesis_openeuler_release.t (99%) rename xCAT-test/{unit => native}/ip_forwarding.t (99%) rename xCAT-test/{unit => native}/openeuler_genimage_transactions.t (94%) rename xCAT-test/{unit => native}/openeuler_install_repositories.t (80%) rename xCAT-test/{unit => native}/openeuler_networking.t (98%) rename xCAT-test/{unit => native}/openeuler_package_policy.t (94%) rename xCAT-test/{unit => native}/openeuler_xcatroot.t (100%) rename xCAT-test/{unit => native}/pgsqlsetup_native_schema.t (94%) rename xCAT-test/{unit => native}/remoteshell_hostkeys_openeuler.t (98%) rename xCAT-test/{unit => native}/syslog_openeuler.t (99%) rename xCAT-test/{unit => native}/syslog_openeuler_chroot.t (96%) delete mode 100644 xCAT-test/unit/go_xcat_openeuler.t create mode 100644 xCAT-test/unit/xcat_conf_selection.t diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 04d50aff0..655eb7b9b 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -7,7 +7,7 @@ jobs: steps: - uses: actions/checkout@v6 - name: Install dependencies - run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests bats build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common + run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests bats rpm build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common - name: Run tests run: perl github_action_xcat_test.pl diff --git a/xCAT-test/bats/go_xcat_openeuler.bats b/xCAT-test/bats/go_xcat_openeuler.bats new file mode 100644 index 000000000..ac0224a2b --- /dev/null +++ b/xCAT-test/bats/go_xcat_openeuler.bats @@ -0,0 +1,162 @@ +#!/usr/bin/env bats + +load helpers/go_xcat + +setup() { + go_xcat_require_source + source "$GO_XCAT_SOURCE" + FIXTURE="$BATS_TEST_TMPDIR/repos" + mkdir -p "$FIXTURE" + TMP_DIR="$FIXTURE" + GO_XCAT_DEFAULT_BASE_URL=https://packages.example.test/xcat + GO_XCAT_ARCH=x86_64 + TEST_ID=openEuler + TEST_VERSION='24.03 (LTS-SP3)' + TEST_VERSION_ID=24.03 + PACKAGE_STATUS=0 + os_release_value() { + case "$1" in + ID) printf '%s\n' "$TEST_ID" ;; + VERSION) printf '%s\n' "$TEST_VERSION" ;; + VERSION_ID) printf '%s\n' "$TEST_VERSION_ID" ;; + *) return 64 ;; + esac + } + dnf() { + if [[ $1 == repoquery ]]; then printf '%s\n' "${@: -1}" + else printf 'dnf'; printf ' <%s>' "$@"; printf '\n'; fi + return "$PACKAGE_STATUS" + } + yum() { printf 'yum'; printf ' <%s>' "$@"; printf '\n'; return "$PACKAGE_STATUS"; } + download_file() { return 1; } + add_repo_by_file() { cp "$1" "$FIXTURE/$2.repo"; } + select_platform +} + +select_platform() { + GO_XCAT_LINUX_DISTRO="$(check_linux_distro)" + GO_XCAT_LINUX_VERSION="$(check_linux_version)" +} + +repositories() { + select_platform || return + add_xcat_core_repo_yum_or_zypper "${CORE_URL:-}" "${REPO_VERSION:-latest}" || return + add_xcat_dep_repo_yum_or_zypper "${DEP_URL:-}" "${REPO_VERSION:-latest}" +} + +@test "native repositories retain each release, service pack and architecture" { + while IFS='|' read -r TEST_VERSION expected; do + for GO_XCAT_ARCH in x86_64 ppc64le; do + run repositories + [ "$status" -eq 0 ] + grep -Fx "baseurl=https://packages.example.test/xcat/yum/latest/xcat-core/openeuler$expected/$GO_XCAT_ARCH" "$FIXTURE/xcat-core.repo" + grep -Fx "baseurl=https://packages.example.test/xcat/yum/latest/xcat-dep/openeuler$expected/$GO_XCAT_ARCH" "$FIXTURE/xcat-dep.repo" + done + done <<'CASES' +20.03 (LTS-SP4)|20.03sp4 +22.03 (LTS-SP4)|22.03sp4 +24.03 (LTS-SP1)|24.03sp1 +24.03 (LTS-SP3)|24.03sp3 +24.03 (LTS-SP4)|24.03sp4 +24.03 (LTS)|24.03 +CASES +} + +@test "native identity and GA fallback" { + for TEST_ID in openEuler openeuler "'openEuler'"; do + run check_linux_distro + [ "$status" -eq 0 ] + [ "$output" = openeuler ] + done + TEST_VERSION="'24.03 (LTS-SP3)'" + run check_linux_version + [ "$status" -eq 0 ] + [ "$output" = 24.03sp3 ] + TEST_VERSION='' + run check_linux_version + [ "$status" -eq 0 ] + [ "$output" = 24.03 ] +} + +@test "invalid releases and architectures create no repositories" { + for TEST_VERSION in 24.09 '25.03 (LTS)' '24.03 (LTS-SP0)' '24.03 (LTS-SP04)' '24.03 (LTS-SP3) trailing'; do + run repositories + [ "$status" -ne 0 ] + [ ! -e "$FIXTURE/xcat-core.repo" ] + done + TEST_VERSION='24.03 (LTS-SP3)' + GO_XCAT_ARCH=ppc64 + run repositories + [ "$status" -ne 0 ] + [ ! -e "$FIXTURE/xcat-core.repo" ] +} + +@test "explicit URLs and development paths retain native layout" { + CORE_URL=https://custom.example.test/native-core + DEP_URL=https://custom.example.test/native-dep + run repositories + [ "$status" -eq 0 ] + grep -Fx "baseurl=$CORE_URL" "$FIXTURE/xcat-core.repo" + grep -Fx "baseurl=$DEP_URL/openeuler24.03sp3/x86_64" "$FIXTURE/xcat-dep.repo" + unset CORE_URL DEP_URL + REPO_VERSION=devel + run repositories + [ "$status" -eq 0 ] + grep -Fx 'baseurl=https://packages.example.test/xcat/yum/devel/core-snap/openeuler24.03sp3/x86_64' "$FIXTURE/xcat-core.repo" +} + +@test "offline native repositories still enforce package signatures" { + mkdir -p "$FIXTURE/offline/core" "$FIXTURE/offline/dep/openeuler24.03sp3/x86_64" + CORE_URL="file://$FIXTURE/offline/core" + DEP_URL="file://$FIXTURE/offline/dep" + run repositories + [ "$status" -eq 0 ] + grep -Fx gpgcheck=1 "$FIXTURE/xcat-core.repo" + grep -Fx gpgcheck=1 "$FIXTURE/xcat-dep.repo" +} + +@test "native package operations enforce signatures and propagate failure" { + for command in install_packages_dnf install_packages_yum update_repo_dnf; do + run "$command" -y xCAT + [ "$status" -eq 0 ] + [[ "$output" == *'<--setopt=*.gpgcheck=1>'* ]] + [[ "$output" != *nogpgcheck* && "$output" != *strict=0* ]] + if [[ $command != update_repo_dnf ]]; then + [[ "$output" == *' '* ]] + [[ $command != install_packages_dnf || "$output" == *'<--setopt=strict=1>'* ]] + PACKAGE_STATUS=37 + run "$command" -y xCAT + [ "$status" -eq 37 ] + PACKAGE_STATUS=0 + fi + done +} + +@test "EL and SLES repository selection retains existing behavior" { + while read -r TEST_ID TEST_VERSION_ID expected; do + run repositories + [ "$status" -eq 0 ] + grep -Fx 'baseurl=https://packages.example.test/xcat/yum/latest/xcat-core' "$FIXTURE/xcat-core.repo" + grep -Fx "baseurl=https://packages.example.test/xcat/yum/latest/xcat-dep/$expected/x86_64" "$FIXTURE/xcat-dep.repo" + done <<'CASES' +rocky 9.6 rh9 +rhel 10.1 rh10 +sles 15.6 sles15 +CASES + TEST_ID=rhel TEST_VERSION_ID=10.1 + select_platform + run install_packages_dnf -y xCAT + [ "$status" -eq 0 ] + [[ "$output" == *'<--nogpgcheck> <--setopt=strict=0>'* ]] +} + +@test "repository replacement selects DNF when yum is absent" { + unset -f yum + type() { [[ $1 != yum ]] && builtin type "$@"; } + grep() { return 1; } + xargs() { cat >/dev/null; } + mv() { return 0; } + run remove_repo_yum xcat-core + [ "$status" -eq 0 ] + [ "$output" = 'dnf ' ] +} diff --git a/xCAT-test/integration/fixtures/dnf.pl b/xCAT-test/integration/fixtures/dnf.pl new file mode 100644 index 000000000..f5ecb9178 --- /dev/null +++ b/xCAT-test/integration/fixtures/dnf.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use File::Glob qw(bsd_glob); +use JSON::PP qw(encode_json); + +open(my $trace, '>>', $ENV{DNF_TRACE}) or die $!; +print {$trace} encode_json(\@ARGV) . "\n" or die $!; +close($trace) or die $!; +if (!grep({ $_ eq 'clean' } @ARGV) && $ENV{REMOVE_REPO}) { + unlink($_) or die $! for bsd_glob("/etc/yum.repos.d/$ENV{REMOVE_REPO}"); +} +exec '/usr/bin/dnf', "--installroot=$ENV{TEST_ROOT}", '--releasever=1', + '--setopt=reposdir=/etc/yum.repos.d', '--setopt=install_weak_deps=False', + '--setopt=tsflags=noscripts', '--noplugins', @ARGV; +die "exec dnf: $!"; diff --git a/xCAT-test/integration/fixtures/postscript-root.sh b/xCAT-test/integration/fixtures/postscript-root.sh new file mode 100644 index 000000000..62b0047bf --- /dev/null +++ b/xCAT-test/integration/fixtures/postscript-root.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -eu +root=$TEST_SANDBOX +mkdir -p "$root/etc/yum.repos.d" "$root/tmp" "$root/xcatpost" "$root/dev" \ + "$root$TEST_FIXTURES" "$root$TEST_POSTSCRIPTS" +for path in /usr /bin /sbin /lib /lib64; do + [ -d "$path" ] || continue + mkdir -p "$root$path" + /bin/mount --bind "$path" "$root$path" + /bin/mount -o remount,bind,ro "$root$path" +done +for file in passwd group nsswitch.conf ld.so.cache os-release; do + [ ! -f "/etc/$file" ] || cp "/etc/$file" "$root/etc/$file" +done +for path in /etc/dnf /etc/rpm; do + [ -d "$path" ] || continue + mkdir -p "$root$path" + /bin/mount --bind "$path" "$root$path" + /bin/mount -o remount,bind,ro "$root$path" +done +for device in null zero random urandom; do + touch "$root/dev/$device" + /bin/mount --bind "/dev/$device" "$root/dev/$device" +done +/bin/mount --bind "$TEST_FIXTURES" "$root$TEST_FIXTURES" +/bin/mount --bind "$TEST_POSTSCRIPTS" "$root$TEST_POSTSCRIPTS" +/bin/mount -o remount,bind,ro "$root$TEST_POSTSCRIPTS" +/bin/mount --bind "$TEST_REPOS" "$root/etc/yum.repos.d" +if [ -n "${TEST_KEY:-}" ]; then + mkdir -p "$root/etc/pki/rpm-gpg" + cp "$TEST_KEY" "$root/etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler" +fi +exec /usr/sbin/chroot "$root" "$@" diff --git a/xCAT-test/integration/openeuler_package_postscripts.t b/xCAT-test/integration/openeuler_package_postscripts.t index 7599600f8..eaf372829 100644 --- a/xCAT-test/integration/openeuler_package_postscripts.t +++ b/xCAT-test/integration/openeuler_package_postscripts.t @@ -2,6 +2,7 @@ use strict; use warnings; use FindBin; +use File::Copy qw(copy); use File::Path qw(make_path); use File::Spec; use File::Temp qw(tempdir); @@ -45,26 +46,8 @@ write_text("$dir/bin/dpkg", "#!/bin/sh\nexit 1\n"); write_text("$dir/bin/wget", "#!/bin/sh\nprintf 'wget\\n' >> \"\$RPM_TRACE\"\nexit 1\n"); chmod 0755, glob("$dir/bin/*"); my $wrapper = "$dir/run"; -write_text($wrapper, <<'SH'); -#!/bin/bash -set -e -/bin/mount --bind "$TEST_REPOS" /etc/yum.repos.d -exec "$@" -SH +copy("$FindBin::Bin/fixtures/postscript-root.sh", $wrapper) or die "copy isolation wrapper: $!"; chmod 0755, $wrapper; -write_text("$dir/bash-env", <<'SH'); -echo() -{ - case "$*" in - 'Warning: the packages '*fallback*|'Warning: the packages '*falling\ back*) - printf 'direct RPM fallback\n' >> "$RPM_TRACE" - builtin echo "$@" - exit 81 - ;; - esac - builtin echo "$@" -} -SH for my $caller (qw(ospkgs otherpkgs)) { for my $os (qw(openeuler20.03sp4 openeuler22.03sp4 openeuler24.03sp1 openeuler24.03sp3 openeuler24.03sp4 openeuler24.03 rhels9.6)) { @@ -117,11 +100,12 @@ sub run_case { my ($caller, $os, $list_rc, $install_rc, $repoonly, $empty) = @_; my $run = tempdir(DIR => $dir, CLEANUP => 1); make_path("$run/repos"); + my $sandbox = tempdir(CLEANUP => 1); local %ENV = %ENV; + @ENV{qw(TEST_SANDBOX TEST_FIXTURES TEST_POSTSCRIPTS)} = ($sandbox, $dir, $postscripts); delete @ENV{qw(BASH_ENV ENV ENVLIST OTHERPKGDIR_INTERNET KERNELDIR SDKDIR NODESETSTATE OSPKGDIR VERBOSE)}; @ENV{qw(PATH TEST_REPOS DNF_TRACE RPM_TRACE DNF_LIST_RC DNF_INSTALL_RC)} = ("$dir/bin:$ENV{PATH}", "$run/repos", "$run/dnf", "$run/rpm", $list_rc, $install_rc); - $ENV{BASH_ENV} = "$dir/bash-env"; @ENV{qw(OSVER ARCH MASTER NFSSERVER HTTPPORT INSTALLDIR UPDATENODE)} = ($os, 'x86_64', '192.0.2.1', '192.0.2.1', 8080, '/install', 1); @ENV{qw(OSPKGS OTHERPKGDIR OTHERPKGS_INDEX OTHERPKGS1)} = @@ -138,7 +122,7 @@ sub run_case { } my $output = do { local $/; <$pipe> } // ''; close($pipe); - my $rc = $? >> 8; + my $rc = (($? & 127) ? 128 + ($? & 127) : $? >> 8); my $repos = join('', map { read_text($_) } sort glob("$run/repos/*.repo")); return ($rc, $output, $repos, -f "$run/dnf" ? read_text("$run/dnf") : '', -f "$run/rpm" ? read_text("$run/rpm") : ''); diff --git a/xCAT-test/integration/openeuler_postscript_repositories.t b/xCAT-test/integration/openeuler_postscript_repositories.t index 290341030..05761010e 100644 --- a/xCAT-test/integration/openeuler_postscript_repositories.t +++ b/xCAT-test/integration/openeuler_postscript_repositories.t @@ -2,16 +2,20 @@ use strict; use warnings; use FindBin; +use File::Copy qw(copy); use File::Path qw(make_path); use File::Spec; use File::Temp qw(tempdir); -use JSON::PP qw(decode_json encode_json); +use JSON::PP qw(decode_json); use Test::More; +plan skip_all => 'set XCAT_NATIVE_REPOSITORIES=1 to build and transact signed RPM fixtures' + unless $ENV{XCAT_NATIVE_REPOSITORIES}; + plan skip_all => 'requires Linux root and a private mount namespace' unless $^O eq 'linux' && $> == 0 && system('unshare', '--mount', '--propagation', 'private', '/bin/true') == 0; -for my $tool (qw(dnf rpm rpmbuild rpmsign createrepo_c gpg python3 runuser)) { +for my $tool (qw(dnf rpm rpmbuild rpmsign createrepo_c gpg gpgconf runuser)) { plan skip_all => "requires $tool" unless system('sh', '-c', 'command -v "$1" >/dev/null', 'sh', $tool) == 0; } plan skip_all => 'requires the packaged openEuler key path' @@ -22,69 +26,61 @@ unless (-x "$postscripts/otherpkgs") { require xCAT::TableUtils; $postscripts = xCAT::TableUtils->getInstallDir() . '/postscripts'; } +my $parent_pid = $$; my $dir = tempdir(CLEANUP => 1); -my $case_index = 0; + chmod 0755, $dir; make_path("$dir/bin", "$dir/fixtures", "$dir/gnupg"); chmod 0700, "$dir/gnupg"; -write_text("$dir/build-fixtures.py", <<'PY'); -from pathlib import Path -import os, shutil, subprocess, sys -b = Path(sys.argv[1]); home = b / 'gnupg'; top = b / 'build' -top.mkdir(); top.chmod(0o777) -subprocess.run(['gpg', '--homedir', str(home), '--batch', '--passphrase', '', '--quick-generate-key', - 'xCAT repository test ', 'rsa2048', 'sign', '0'], check=True) -key = b / 'key.asc' -key.write_bytes(subprocess.check_output(['gpg', '--homedir', str(home), '--armor', '--export'])) -specs = [('oe-scope-os', '1', '', 'old'), ('oe-scope-os', '2', '', 'os'), - ('oe-scope-os', '3', '', 'vendor'), ('oe-scope-dependency', '1', '', 'os'), - ('oe-scope-extra', '1', 'Requires: oe-scope-dependency = 1', 'other/native')] -for name, version, requires, repo in specs: - spec = b / (name + '-' + version + '.spec') - spec.write_text('''Name: %s -Version: %s +END { + local $?; + capture('gpgconf', '--homedir', "$dir/gnupg", '--kill', 'gpg-agent') + if defined($parent_pid) && $$ == $parent_pid && defined($dir) && -d "$dir/gnupg"; +} +my $top = "$dir/build"; +make_path($top); +chmod 0777, $top; +checked('gpg', '--homedir', "$dir/gnupg", '--batch', '--passphrase', '', + '--quick-generate-key', 'xCAT repository test ', 'rsa2048', 'sign', '0'); +checked('gpg', '--homedir', "$dir/gnupg", '--armor', '--output', "$dir/key.asc", '--export'); +for my $package ( + ['oe-scope-os', '1', '', 'old'], + ['oe-scope-os', '2', '', 'os'], + ['oe-scope-os', '3', '', 'vendor'], + ['oe-scope-dependency', '1', '', 'os'], + ['oe-scope-extra', '1', 'Requires: oe-scope-dependency = 1', 'other/native'], +) { + my ($name, $version, $requires, $repo) = @$package; + my $spec = "$dir/$name-$version.spec"; + write_text($spec, <<"SPEC"); +Name: $name +Version: $version Release: 1 Summary: Isolated package repository fixture License: MIT BuildArch: noarch AutoReqProv: no -%s -%%description +$requires +%description Isolated package repository fixture. -%%install -mkdir -p %%{buildroot}/usr/share/oe-scope -echo %%{version} > %%{buildroot}/usr/share/oe-scope/%%{name} -%%files -/usr/share/oe-scope/%%{name} -''' % (name, version, requires)) - subprocess.run(['runuser', '-u', 'nobody', '--', 'rpmbuild', '--define', '_topdir ' + str(top), '-bb', str(spec)], check=True) - rpm = top / 'RPMS/noarch' / (name + '-' + version + '-1.noarch.rpm') - subprocess.run(['rpmsign', '--define', '_gpg_name repo-test@example.invalid', - '--define', '_gpg_path ' + str(home), '--addsign', str(rpm)], check=True) - dest = b / 'fixtures' / repo; dest.mkdir(parents=True, exist_ok=True) - shutil.copyfile(rpm, dest / rpm.name) -for repo in ('old', 'os', 'vendor', 'other/native'): - dest = b / 'fixtures' / repo - subprocess.run(['createrepo_c', str(dest)], check=True) - shutil.copyfile(key, dest / 'repodata/repomd.xml.key') -PY -my ($fixture_rc, $fixture_output) = capture('python3', "$dir/build-fixtures.py", $dir); -is($fixture_rc, 0, 'signed RPM fixtures are built by an unprivileged user') or BAIL_OUT($fixture_output); - -write_text("$dir/bin/dnf", <<'PY'); -#!/usr/bin/python3 -import json, os, pathlib, sys -import dnf.rpm -args = sys.argv[1:] -with open(os.environ['DNF_TRACE'], 'a') as out: - out.write(json.dumps(args) + '\n') -if 'clean' not in args and os.environ.get('REMOVE_REPO'): - for path in pathlib.Path('/etc/yum.repos.d').glob(os.environ['REMOVE_REPO']): - path.unlink() -os.execv('/usr/bin/dnf', ['dnf', '--installroot=' + os.environ['TEST_ROOT'], - '--releasever=' + dnf.rpm.detect_releasever('/'), '--setopt=reposdir=/etc/yum.repos.d', - '--setopt=install_weak_deps=False', '--setopt=tsflags=noscripts', '--noplugins'] + args) -PY +%install +mkdir -p %{buildroot}/usr/share/oe-scope +echo %{version} > %{buildroot}/usr/share/oe-scope/%{name} +%files +/usr/share/oe-scope/%{name} +SPEC + checked('runuser', '-u', 'nobody', '--', 'rpmbuild', '--define', "_topdir $top", '-bb', $spec); + my $rpm = "$name-$version-1.noarch.rpm"; + checked('rpmsign', '--define', '_gpg_name repo-test@example.invalid', + '--define', "_gpg_path $dir/gnupg", '--addsign', "$top/RPMS/noarch/$rpm"); + make_path("$dir/fixtures/$repo"); + copy("$top/RPMS/noarch/$rpm", "$dir/fixtures/$repo/$rpm") or die "copy $rpm: $!"; +} +for my $repo (qw(old os vendor other/native)) { + checked('createrepo_c', "$dir/fixtures/$repo"); + copy("$dir/key.asc", "$dir/fixtures/$repo/repodata/repomd.xml.key") or die "copy key: $!"; +} +copy("$FindBin::Bin/fixtures/dnf.pl", "$dir/bin/dnf") or die "copy DNF adapter: $!"; write_text("$dir/bin/mount", <<'SH'); #!/bin/sh printf '%s on %s type nfs\n' "$NFSSERVER" "$INSTALLDIR" @@ -98,13 +94,7 @@ SH for my $name (qw(logger dpkg wget)) { write_text("$dir/bin/$name", "#!/bin/sh\nexit 1\n"); } -write_text("$dir/run", <<'SH'); -#!/bin/bash -set -e -/bin/mount --bind "$TEST_REPOS" /etc/yum.repos.d -/bin/mount --bind "$TEST_KEY" /etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler -exec "$@" -SH +copy("$FindBin::Bin/fixtures/postscript-root.sh", "$dir/run") or die "copy isolation wrapper: $!"; chmod 0755, glob("$dir/bin/*"), "$dir/run"; my $os = run_case('ospkgs'); @@ -162,8 +152,7 @@ SKIP: { unless defined($rpm) && -f $rpm; my $repo = "$dir/native-key-repo"; make_path($repo); - require File::Copy; - File::Copy::copy($rpm, "$repo/native-key.rpm") or die "copy native key RPM: $!"; + copy($rpm, "$repo/native-key.rpm") or die "copy native key RPM: $!"; my ($rc, $out) = capture('createrepo_c', $repo); is($rc, 0, 'the official native key package fixture has repository metadata') or diag($out); my $native = run_case('ospkgs', osrepo => $repo, package => 'openEuler-gpg-keys', native_key => 1); @@ -200,7 +189,9 @@ sub run_case { ($rc, $output) = capture('/usr/bin/rpm', '--root', "$run/root", '-i', "$dir/fixtures/old/oe-scope-os-1-1.noarch.rpm"); die $output if $rc; } + my $sandbox = tempdir(CLEANUP => 1); local %ENV = %ENV; + @ENV{qw(TEST_SANDBOX TEST_FIXTURES TEST_POSTSCRIPTS)} = ($sandbox, $dir, $postscripts); delete @ENV{qw(BASH_ENV ENV ENVLIST OTHERPKGDIR_INTERNET KERNELDIR SDKDIR NODESETSTATE VERBOSE)}; @ENV{qw(PATH TEST_REPOS TEST_ROOT TEST_KEY DNF_TRACE RPM_TRACE REMOVE_REPO)} = ("$dir/bin:$ENV{PATH}", "$run/repos", "$run/root", $key, "$run/dnf", "$run/rpm", $options{remove_repo} // ''); @@ -211,6 +202,7 @@ sub run_case { my ($rc, $output) = capture('unshare', '--mount', '--propagation', 'private', "$dir/run", "$postscripts/$caller", ($options{keeprepo} ? '--keeprepo' : ())); my ($query_rc, $packages) = capture('/usr/bin/rpm', '--root', "$run/root", '-qa', '--qf', '%{NAME} %{VERSION}\n'); + die "RPM query failed: $packages" if $query_rc; my (%packages, $keys); $keys = 0; for my $line (split /\n/, $packages) { @@ -220,13 +212,14 @@ sub run_case { my $result = { rc => $rc, output => $output, packages => \%packages, keys => $keys, vendor_preserved => read_text("$run/repos/openEuler.repo") eq $vendor, dnf => [map { decode_json($_) } split /\n/, read_text("$run/dnf")], rpm => read_text("$run/rpm") }; - if (my $evidence = $ENV{XCAT_TEST_EVIDENCE}) { - make_path($evidence); - write_text("$evidence/" . ++$case_index . "-$caller.json", encode_json({ %$result, options => \%options }) . "\n"); - } return $result; } +sub checked { + my ($rc, $output) = capture(@_); + die "@_ failed ($rc): $output" if $rc; +} + sub capture { my (@command) = @_; my $pid = open(my $pipe, '-|'); @@ -238,7 +231,7 @@ sub capture { } my $output = do { local $/; <$pipe> } // ''; close($pipe); - return ($? >> 8, $output); + return ((($? & 127) ? 128 + ($? & 127) : $? >> 8), $output); } sub read_text { diff --git a/xCAT-test/native/buildrpms_openeuler.t b/xCAT-test/native/buildrpms_openeuler.t new file mode 100644 index 000000000..f87bba011 --- /dev/null +++ b/xCAT-test/native/buildrpms_openeuler.t @@ -0,0 +1,142 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use File::Copy qw(copy); +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../../build-utils/lib"; +use Test::More; +use XCAT::Test::File qw(repo_path slurp_repo_file); +use XCAT::BuildUtils qw(openeuler_build_target openeuler_repo_subdir targetarch_from_target); + +plan skip_all => 'requires Linux user and mount namespaces' + unless $^O eq 'linux' && system('unshare', '--user', '--map-root-user', '--mount', '/bin/true') == 0; +my $wrapper = "$FindBin::Bin/fixtures/buildrpms-namespace.sh"; + +my $root = tempdir(CLEANUP => 1); +my $driver = "$root/driver.pl"; +write_file($driver, <<'PERL'); +BEGIN { + require Parallel::ForkManager; + no warnings 'redefine'; + *Parallel::ForkManager::new = sub { die "BUILD_BOUNDARY\n" } if $ENV{BUILD_STOP}; + *CORE::GLOBAL::readpipe = sub { + return "$ENV{BUILD_ARCH}\n" if $_[0] eq 'uname -m'; + return CORE::readpipe($_[0]); + }; +} +do './buildrpms.pl'; +die $@ if $@; +PERL + +my $native = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--setup_local_repos'); +is($native->{status}, 0, 'full builder creates local repositories for the native default target'); +like($native->{core}, qr{/dist/openeuler-24\.03sp3-x86_64/rpms$}m, 'full builder selects the exact native mock target'); +like($native->{dep}, qr{/openeuler24\.03sp3/x86_64$}m, 'full builder selects matching dependency artifacts'); +like($native->{core}, qr/^gpgcheck=1$/m, 'native core repository requires signatures'); +like($native->{dep}, qr/^gpgcheck=1$/m, 'native dependency repository requires signatures'); +my $explicit = run_builder('rocky', '9.6', '9.6', 'x86_64', '--setup_local_repos', '--target', 'openeuler-24.03-ppc64le'); +is($explicit->{status}, 0, 'an explicit native target overrides the host default for repository setup'); +like($explicit->{dep}, qr{/openeuler24\.03/ppc64le$}m, 'explicit native target chooses its own release and architecture'); +my $el = run_builder('rocky', '9.6', '9.6', 'x86_64', '--setup_local_repos'); +is($el->{status}, 0, 'existing EL local repository setup completes'); +like($el->{core}, qr{/dist/rocky\+epel-10-x86_64/rpms$}m, 'existing EL default target is unchanged'); +like($el->{dep}, qr{/el9/x86_64$}m, 'existing EL dependency path is unchanged'); +for my $missing (qw(core dep)) { + local $ENV{BUILD_MISSING_KEY} = $missing; + my $result = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--setup_local_repos'); + isnt($result->{status}, 0, "setup rejects the missing $missing exported key"); + like($result->{stderr}, qr/Missing openEuler repository signing key/, 'setup identifies the absent trust input'); + is($result->{core} . $result->{dep}, '', 'setup fails before writing either repository configuration'); +} +{ + local $ENV{BUILD_STOP} = 1; + my $unsigned = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--package', 'xCAT-client'); + isnt($unsigned->{status}, 0, 'unsigned native binary builds are rejected'); + like($unsigned->{stderr}, qr/openEuler binary repository builds require --gpg-sign/, 'the CLI reports the signing requirement'); + unlike($unsigned->{stderr}, qr/BUILD_BOUNDARY/, 'unsigned native builds stop before the build scheduler'); + for my $case (['native signed', 'openEuler', '--gpg-sign'], ['native source only', 'openEuler', '--source-only'], + ['legacy unsigned', 'rocky']) { + my ($label, $id, @args) = @$case; + my $result = run_builder($id, '24.03 (LTS-SP3)', '24.03', 'x86_64', '--package', 'xCAT-client', @args); + like($result->{stderr}, qr/BUILD_BOUNDARY/, "$label reaches the existing build scheduler"); + unlike($result->{stderr}, qr/repository builds require --gpg-sign/, "$label preserves its signing policy"); + } +} +my $install = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--install_deps'); +is($install->{status}, 0, 'full native prerequisite entry point completes through process boundaries'); +unlike($install->{commands}, qr/epel|crb|codeready/i, 'native prerequisites do not enable EL repositories'); +unlike($install->{commands}, qr/\bpodman\b/, 'native prerequisites do not request an unrelated container bootstrap'); +like($install->{commands}, qr/--setopt=install_weak_deps=False/, 'native prerequisites do not pull optional container dependencies'); +like($install->{commands}, qr/--setopt=\*\.gpgcheck=1/, 'native prerequisite installation enforces signatures'); +like($install->{commands}, qr/--setopt=strict=1/, 'native prerequisite installation does not skip missing packages'); +like($install->{commands}, qr/\bsystemd-nspawn\b/, 'native prerequisites provide the mock isolation executable'); +my $el_install = run_builder('rocky', '9.6', '9.6', 'x86_64', '--install_deps'); +like($el_install->{commands}, qr/epel-release-latest-10/, 'existing EL prerequisite repository choice is preserved'); +my $merge = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--merge-core-repos'); +isnt($merge->{status}, 0, 'native flat repository assembly is rejected'); +like($merge->{stderr}, qr/openEuler repositories must retain their release and architecture/, 'native assembly error preserves the per-cell output contract'); +my $el_merge = run_builder('rocky', '9.6', '9.6', 'x86_64', '--merge-core-repos', '--output-dir', 'merged', '--input-core-repos', 'native-input'); +isnt($el_merge->{status}, 0, 'an EL host cannot flatten copied native inputs'); +like($el_merge->{stderr}, qr/openEuler repositories must retain their release and architecture/, 'native build provenance survives copying to an unrelated path'); +{ + local $ENV{BUILD_DNF_FAILURE} = 37; + my $failed = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--install_deps'); + is($failed->{status}, 37, 'native prerequisite failure terminates the entry point'); +} +done_testing(); + +sub run_builder { + my ($id, $version, $version_id, $arch, @args) = @_; + my $fixture = tempdir(DIR => $root, CLEANUP => 1); + make_path("$fixture/build-utils/lib/XCAT", "$fixture/bin", "$fixture/repos", "$fixture/native-input", "$fixture/locks", "$fixture/home", + "$fixture/dep/openeuler24.03sp3/x86_64", "$fixture/dep/openeuler24.03/ppc64le", "$fixture/dep/el9/x86_64"); + copy($ENV{XCAT_TEST_BUILDRPMS} || repo_path('buildrpms.pl'), "$fixture/buildrpms.pl") or die $!; + copy(repo_path('build-utils/lib/XCAT/BuildUtils.pm'), "$fixture/build-utils/lib/XCAT/BuildUtils.pm") or die $!; + copy($driver, "$fixture/driver.pl") or die $!; + write_file("$fixture/Version", "2.18.0\n"); + write_file("$fixture/Gitepoch", "1756000000\n"); + write_file("$fixture/native-input/buildinfo.txt", "BUILD_TARGET=openeuler-24.03sp3-x86_64\n"); + write_file("$fixture/os-release", "ID=$id\nVERSION=\"$version\"\nVERSION_ID=$version_id\n"); + for my $target ('openeuler-24.03sp3-x86_64', 'openeuler-24.03-ppc64le') { + my $subdir = openeuler_repo_subdir($target); + for my $key (['core', "$fixture/dist/$target/rpms"], ['dep', "$fixture/dep/$subdir"]) { + make_path("$key->[1]/repodata"); + write_file("$key->[1]/repodata/repomd.xml.key", 'existing exported key') + unless ($ENV{BUILD_MISSING_KEY} || '') eq $key->[0]; + } + } + write_file("$fixture/bin/dnf", "#!/bin/sh\nprintf '%s\\n' \"\$*\" >> \"\$BUILD_FIXTURE/commands\"\nexit \"\${BUILD_DNF_FAILURE:-0}\"\n"); + write_file("$fixture/bin/systemctl", "#!/bin/sh\nexit 0\n"); + write_file("$fixture/bin/rpmdev-setuptree", "#!/bin/sh\nexit 0\n"); + chmod 0755, map {"$fixture/bin/$_"} qw(dnf systemctl rpmdev-setuptree); + local %ENV = (%ENV, HOME => "$fixture/home", BUILD_FIXTURE => $fixture, BUILD_ARCH => $arch, PATH => "$fixture/bin:$ENV{PATH}"); + my $pid = fork(); + die $! unless defined $pid; + if (!$pid) { + chdir($fixture) or die $!; + open(STDOUT, '>', 'stdout') or die $!; + open(STDERR, '>', 'stderr') or die $!; + exec('unshare', '--user', '--map-root-user', '--mount', 'bash', $wrapper, $^X, 'driver.pl', @args, '--xcat_dep_path', "$fixture/dep") or die $!; + } + waitpid($pid, 0); + my $status = (($? & 127) ? 128 + ($? & 127) : $? >> 8); + my $result = {status => $status, core => read_file("$fixture/repos/xcat-core-local.repo"), + dep => read_file("$fixture/repos/xcat-dep.repo"), commands => read_file("$fixture/commands"), stderr => read_file("$fixture/stderr")}; + diag(read_file("$fixture/stderr")) if $status && !$ENV{BUILD_DNF_FAILURE} && !$ENV{BUILD_MISSING_KEY} + && !$ENV{BUILD_STOP} && !grep {$_ eq '--merge-core-repos'} @args; + return $result; +} +sub write_file { + my ($path, $contents) = @_; + open(my $fh, '>', $path) or die "$path: $!"; + print {$fh} $contents; + close($fh) or die $!; +} +sub read_file { + my ($path) = @_; + return '' unless -f $path; + open(my $fh, '<', $path) or die $!; + return do {local $/; scalar <$fh>} || ''; +} diff --git a/xCAT-test/unit/buildrpms_release_openeuler.t b/xCAT-test/native/buildrpms_release_openeuler.t similarity index 90% rename from xCAT-test/unit/buildrpms_release_openeuler.t rename to xCAT-test/native/buildrpms_release_openeuler.t index 980335b99..0438e5080 100644 --- a/xCAT-test/unit/buildrpms_release_openeuler.t +++ b/xCAT-test/native/buildrpms_release_openeuler.t @@ -13,6 +13,10 @@ plan skip_all => 'requires unprivileged Linux RPM tooling' unless $^O eq 'linux' && $> && system('sh', '-c', 'command -v rpmbuild >/dev/null && command -v rpm2cpio >/dev/null && command -v cpio >/dev/null') == 0; +plan skip_all => 'requires user and mount namespaces' + if system('unshare', '--user', '--map-root-user', '--mount', '/bin/true'); +my $wrapper = "$FindBin::Bin/fixtures/buildrpms-namespace.sh"; + my $root = tempdir(CLEANUP => !$ENV{XCAT_RELEASE_TEST_KEEP}); diag("Release contract fixtures: $root") if $ENV{XCAT_RELEASE_TEST_KEEP}; my $builder = $ENV{XCAT_RELEASE_TEST_BUILDER} || repo_path('buildrpms.pl'); @@ -83,6 +87,8 @@ for my $case (@cases) { for my $failure (qw(missing-core duplicate-dep copy tar)) { my $result = run_builder('openeuler-24.03sp3-x86_64', 'openeuler24.03sp3/x86_64', undef, $failure); isnt($result->{status}, 0, "$failure stops the native builder"); + like($result->{output}, qr/Error staging xCAT-release sources/, + 'the copy fault reaches production source staging') if $failure eq 'copy'; my $fixture = $result->{fixture}; is(read_file("$fixture/home/rpmbuild/SOURCES/xCAT-release-2.19.0.tar.gz"), 'prior archive', "$failure preserves the preceding source archive"); @@ -110,12 +116,7 @@ sub run_builder { my ($target, $subdir, $default, $failure) = @_; my $fixture = tempdir(DIR => $root, CLEANUP => !$ENV{XCAT_RELEASE_TEST_KEEP}); make_path(map {"$fixture/$_"} qw(build-utils/lib/XCAT bin mock locks tmp xCAT-release home/rpmbuild/SOURCES)); - my $body = read_file($builder); - $body =~ s{'/etc/os-release'}{'$fixture/os-release'}g; - $body =~ s{/etc/mock/}{$fixture/mock/}g; - $body =~ s{/var/lock/}{$fixture/locks/}g; - $body =~ s/\$ENV\{HOME\}/\$ENV{XCAT_RELEASE_TEST_HOME}/g; - write_file("$fixture/buildrpms.pl", $body); + copy($builder, "$fixture/buildrpms.pl") or die $!; copy(repo_path('build-utils/lib/XCAT/BuildUtils.pm'), "$fixture/build-utils/lib/XCAT/BuildUtils.pm") or die $!; copy($spec, "$fixture/xCAT-release/xCAT-release.spec") or die $!; write_file("$fixture/xCAT-release/$_", $original{$_}) for @files; @@ -153,11 +154,11 @@ SH "baseurl=https://xcat.org/files/xcat/repos/yum/latest/xcat-dep/rh\$releasever/\$basearch\n") if $failure eq 'duplicate-dep'; } - local %ENV = (%ENV, PATH => "$fixture/bin:$ENV{PATH}", TMPDIR => "$fixture/tmp", XCAT_RELEASE_TEST_FIXTURE => $fixture, - XCAT_RELEASE_TEST_HOME => "$fixture/home", XCAT_RELEASE_TEST_NATIVE => defined($subdir) ? 1 : 0, + local %ENV = (%ENV, PATH => "$fixture/bin:$ENV{PATH}", TMPDIR => "$fixture/tmp", XCAT_RELEASE_TEST_FIXTURE => $fixture, BUILD_FIXTURE => $fixture, + HOME => "$fixture/home", LC_ALL => 'C', XCAT_RELEASE_TEST_FAILURE => ($failure && $failure eq 'copy' ? 'cp' : $failure || '')); my @target = $default ? () : ('--target', $target); - my ($status, $output) = run($fixture, $^X, 'buildrpms.pl', '--package', 'xCAT-release', @target, + my ($status, $output) = run($fixture, 'unshare', '--user', '--map-root-user', '--mount', 'bash', $wrapper, $^X, 'buildrpms.pl', '--package', 'xCAT-release', @target, (defined($subdir) ? '--gpg-sign' : ()), '--release', 'releasecontract', '--mock-uniqueext', 'release-contract', '--nproc', '1'); write_file("$fixture/builder-output", $output); @@ -182,11 +183,13 @@ while (@ARGV) { elsif ($arg =~ /^(?:-r|--spec|--sources|--resultdir|--rebuild)$/) {$args{$arg} = shift;} else {$args{$arg} = 1;} } +die "Missing selected mock configuration\n" unless -f "/etc/mock/$args{'-r'}.cfg"; exit 0 if $args{'--init'}; my $top = "$fixture/mock-rpm"; make_path(map {"$top/$_"} qw(BUILD BUILDROOT RPMS SOURCES SPECS SRPMS)); -my @command = ('rpmbuild', @defines, '--define', "_topdir $top", '--undefine', 'openEuler'); -push @command, '--define', 'openEuler 2' if $ENV{XCAT_RELEASE_TEST_NATIVE}; +my @command = ('rpmbuild', @defines, '--define', "_topdir $top"); +push @command, ($args{'-r'} // '') =~ m{(?:^|[-/])openeuler-} + ? ('--define', 'openEuler 2') : ('--undefine', 'openEuler'); my @built; if ($args{'--buildsrpm'}) { push @command, '--define', "_sourcedir $args{'--sources'}", '-bs', $args{'--spec'}; @@ -195,7 +198,7 @@ if ($args{'--buildsrpm'}) { push @command, '--rebuild', $args{'--rebuild'}; @built = ("$top/RPMS/noarch/*.rpm"); } else {die "Unexpected mock operation\n";} -system(@command) == 0 or exit(($? >> 8) || 1); +system(@command) == 0 or exit(($? & 127) ? 128 + ($? & 127) : ($? >> 8) || 1); make_path($args{'--resultdir'}); for my $file (map {glob($_)} @built) {copy($file, $args{'--resultdir'}) or die $!;} PERL @@ -213,7 +216,8 @@ sub run { exec(@command) or die "exec @command: $!"; } waitpid($pid, 0); - return ($? ? (($? >> 8) || 1) : 0, read_file($log)); + my $status = ($? & 127) ? 128 + ($? & 127) : $? >> 8; + return ($status, read_file($log)); } sub write_file { my ($path, $contents) = @_; diff --git a/xCAT-test/native/fixtures/buildrpms-namespace.sh b/xCAT-test/native/fixtures/buildrpms-namespace.sh new file mode 100644 index 000000000..bd64f49bb --- /dev/null +++ b/xCAT-test/native/fixtures/buildrpms-namespace.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -eu +mount --make-rslave / +etc="$BUILD_FIXTURE/etc" +mkdir -p "$etc/mock" "$etc/yum.repos.d" +for file in passwd group nsswitch.conf ld.so.cache; do + [ ! -f "/etc/$file" ] || /bin/cp "/etc/$file" "$etc/$file" +done +/bin/cp "$BUILD_FIXTURE/os-release" "$etc/os-release" +mount --bind "$etc" /etc +mount --bind "$BUILD_FIXTURE/locks" /var/lock +if [ -d "$BUILD_FIXTURE/repos" ]; then + mount --bind "$BUILD_FIXTURE/repos" /etc/yum.repos.d +fi +if [ -d "$BUILD_FIXTURE/mock" ]; then + mount --bind "$BUILD_FIXTURE/mock" /etc/mock +fi +exec "$@" diff --git a/xCAT-test/unit/genesis_openeuler.t b/xCAT-test/native/genesis_openeuler.t similarity index 98% rename from xCAT-test/unit/genesis_openeuler.t rename to xCAT-test/native/genesis_openeuler.t index f5a3b5371..80ae19508 100644 --- a/xCAT-test/unit/genesis_openeuler.t +++ b/xCAT-test/native/genesis_openeuler.t @@ -182,11 +182,11 @@ sub run { die $! unless defined($pid); if (!$pid) { open(STDOUT, '>', $log) or die $!; - open(STDERR, '>', "$log.stderr") or die $!; + open(STDERR, '>&', STDOUT) or die $!; exec(@command) or die $!; } waitpid($pid, 0); - my $status = $? >> 8; + my $status = (($? & 127) ? 128 + ($? & 127) : $? >> 8); open(my $fh, '<', $log) or die $!; my $output = do { local $/; <$fh> } // ''; close($fh); diff --git a/xCAT-test/unit/genesis_openeuler_release.t b/xCAT-test/native/genesis_openeuler_release.t similarity index 99% rename from xCAT-test/unit/genesis_openeuler_release.t rename to xCAT-test/native/genesis_openeuler_release.t index b3173f5b6..cdcda7910 100644 --- a/xCAT-test/unit/genesis_openeuler_release.t +++ b/xCAT-test/native/genesis_openeuler_release.t @@ -14,6 +14,7 @@ plan skip_all => 'requires native openEuler, an unprivileged user, rpmbuild and unless $^O eq 'linux' && $> && -f '/etc/openEuler-release' && -d '/usr/lib/dracut' && system('sh', '-c', 'command -v rpmbuild >/dev/null && unshare -Ur -m true') == 0; +local $ENV{PATH} = "$ENV{PATH}:/usr/sbin:/sbin"; my $root = abs_path("$RealBin/../.."); my $spec = $ENV{XCAT_GENESIS_RELEASE_SPEC} // "$root/xCAT-genesis-builder/xCAT-genesis-base.spec"; my $tmp = tempdir(CLEANUP => 1); diff --git a/xCAT-test/unit/ip_forwarding.t b/xCAT-test/native/ip_forwarding.t similarity index 99% rename from xCAT-test/unit/ip_forwarding.t rename to xCAT-test/native/ip_forwarding.t index f030e9d25..ead23c668 100644 --- a/xCAT-test/unit/ip_forwarding.t +++ b/xCAT-test/native/ip_forwarding.t @@ -127,7 +127,7 @@ for my $case (@cases) { exec('unshare', '-mn', '--', "$tmp/namespace") or die $!; } waitpid($pid, 0); - my $status = $? >> 8; + my $status = (($? & 127) ? 128 + ($? & 127) : $? >> 8); is($status, 0, "pass $pass invokes the full production module in isolation"); if ($status) { diag(read_file("$fixture/stderr-$pass.log")); next; } my $result = decode_json(read_file("$fixture/result-$pass.json")); diff --git a/xCAT-test/unit/openeuler_genimage_transactions.t b/xCAT-test/native/openeuler_genimage_transactions.t similarity index 94% rename from xCAT-test/unit/openeuler_genimage_transactions.t rename to xCAT-test/native/openeuler_genimage_transactions.t index a9f80d160..f9f7d6408 100644 --- a/xCAT-test/unit/openeuler_genimage_transactions.t +++ b/xCAT-test/native/openeuler_genimage_transactions.t @@ -17,6 +17,7 @@ write_file($driver, <<'PERL'); use strict; use warnings; use Storable qw(nstore); +use Text::ParseWords qw(shellwords); our (@commands, $reached, $config, $restored); BEGIN { *CORE::GLOBAL::readpipe = sub { @@ -28,7 +29,10 @@ BEGIN { my $command = join(' ', @_); push @commands, $command; if ($command =~ /^\s*(?:dnf|yum) /) { - open(my $file, '<', "/tmp/genimage.$$.yum.conf") or die $!; + my @args = shellwords($command); + my ($index) = grep { $args[$_] eq '-c' } 0 .. $#args - 1; + die 'Package command omitted its configuration' unless defined($index); + open(my $file, '<', $args[$index + 1]) or die $!; $config = do { local $/; <$file> }; close($file); my $phase = $command =~ / erase .*pre-remove/ ? 'pre-remove' @@ -126,7 +130,7 @@ sub run_case { die "exec: $!"; } waitpid($pid, 0); - my $status = $? >> 8; + my $status = (($? & 127) ? 128 + ($? & 127) : $? >> 8); open(my $file, '<', "$case/output") or die $!; my $output = do { local $/; <$file> }; close($file); diff --git a/xCAT-test/unit/openeuler_install_repositories.t b/xCAT-test/native/openeuler_install_repositories.t similarity index 80% rename from xCAT-test/unit/openeuler_install_repositories.t rename to xCAT-test/native/openeuler_install_repositories.t index ebbe23df2..613dfac78 100644 --- a/xCAT-test/unit/openeuler_install_repositories.t +++ b/xCAT-test/native/openeuler_install_repositories.t @@ -6,7 +6,7 @@ use File::Copy qw(copy); use File::Path qw(make_path); use File::Temp qw(tempdir); use FindBin; -use lib "$FindBin::Bin/../lib"; +use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../../perl-xCAT", "$FindBin::Bin/../../xCAT-server/lib/perl"; use Test::More; use XCAT::Test::File qw(repo_path slurp_repo_file); @@ -17,19 +17,21 @@ plan skip_all => 'mount and network namespaces unavailable' my $tmp = tempdir(DIR => '/var/tmp', CLEANUP => !$ENV{XCAT_POST_REPOS_KEEP}); diag("fixtures: $tmp") if $ENV{XCAT_POST_REPOS_KEEP}; -my $scriptdir = 'xCAT-server/share/xcat/install/scripts'; -my $postscript = slurp_repo_file("$scriptdir/post.rhels8"); -my $library = slurp_repo_file("$scriptdir/scriptlib"); -my $include = '#INCLUDE:#ENV:XCATROOT#/share/xcat/install/scripts/scriptlib#'; -$postscript =~ s/^\Q$include\E$/$library/m - or BAIL_OUT('Unable to render the scriptlib include'); -my $preamble = <<'SH'; -compgen() { return 1; } -nmcli() { return 0; } -SH -$postscript =~ s/\A(#![^\n]*\n)/$1$preamble/ - or BAIL_OUT('Unable to stage the network fixture'); -write_file("$tmp/post.rhels8", $postscript); +make_path("$tmp/db", "$tmp/bin"); +$ENV{XCATROOT} = repo_path('xCAT-server'); +$ENV{XCATCFG} = "SQLite:$tmp/db"; +$ENV{MASTER_IP} = '192.0.2.1'; +require xCAT::Table; +require xCAT::Template; +my $site = xCAT::Table->new('site', -create => 1); +$site->setAttribs({key => 'xcatdebugmode'}, {value => '0'}); +$site->close(); +my $error = xCAT::Template->subvars(repo_path('xCAT-server/share/xcat/install/scripts/post.rhels8'), + "$tmp/post.rhels8", 'node', undef, '/install/media', 'openeuler', undef, + {xcatmaster => '192.0.2.1'}); +die "Cannot render post.rhels8: $error" if $error; +write_file("$tmp/bin/nmcli", "#!/bin/sh\nexit 0\n"); +chmod 0755, "$tmp/bin/nmcli" or die $!; is(system('/bin/bash', '-n', "$tmp/post.rhels8"), 0, 'the complete rendered post owner has valid Bash syntax'); write_file("$tmp/namespace", <<'SH'); @@ -42,13 +44,7 @@ mount --bind "$XCAT_POST_REPOS_FIXTURE/etc" /etc exec /bin/bash "$XCAT_POST_REPOS_OWNER" SH chmod 0755, "$tmp/namespace" or die $!; -my @legacy = qw( - oracle-linux-ol8.repo oracle-linux-ol9.repo uek-ol8.repo uek-ol9.repo - Rocky-AppStream.repo Rocky-BaseOS.repo Rocky-Extras.repo rocky.repo - rocky-extras.repo CentOS-Base.repo centos.repo centos-addons.repo - almalinux-ha.repo almalinux-nfv.repo almalinux-powertools.repo - almalinux.repo almalinux-resilientstorage.repo almalinux-rt.repo -); +my @legacy = qw(Rocky-BaseOS.repo almalinux.repo CentOS-Base.repo oracle-linux-ol8.repo); my $enabled = "[fixture]\nenabled=1\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; my $disabled = "[fixture]\nenabled=0\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; my $native = "[OS]\nenabled=1\ngpgcheck=1\n[update]\n \tenabled = 1\n[disabled]\nenabled=0\n#enabled=1\n"; @@ -77,7 +73,7 @@ for my $case ( local %ENV = (%ENV, XCATDEBUGMODE => '0', MASTER_IP => '192.0.2.1', XCAT_POST_REPOS_FIXTURE => $fixture, XCAT_POST_REPOS_OWNER => "$tmp/post.rhels8", XCAT_POST_HOST_MOUNT => $host_mount, XCAT_POST_HOST_NET => $host_net, - PATH => '/usr/sbin:/usr/bin:/sbin:/bin', LC_ALL => 'C'); + PATH => "$tmp/bin:/usr/sbin:/usr/bin:/sbin:/bin", LC_ALL => 'C'); for my $pass (1, 2) { my $pid = fork(); die $! unless defined $pid; if (!$pid) { diff --git a/xCAT-test/unit/openeuler_networking.t b/xCAT-test/native/openeuler_networking.t similarity index 98% rename from xCAT-test/unit/openeuler_networking.t rename to xCAT-test/native/openeuler_networking.t index 5ba27de87..ba9aea2fa 100644 --- a/xCAT-test/unit/openeuler_networking.t +++ b/xCAT-test/native/openeuler_networking.t @@ -149,7 +149,7 @@ sub run_case { die $!; } waitpid($pid, 0); - return ($? >> 8, read_file("$fixture/commands"), read_file("$fixture/output"), $fixture); + return ((($? & 127) ? 128 + ($? & 127) : $? >> 8), read_file("$fixture/commands"), read_file("$fixture/output"), $fixture); } for my $case ( diff --git a/xCAT-test/unit/openeuler_package_policy.t b/xCAT-test/native/openeuler_package_policy.t similarity index 94% rename from xCAT-test/unit/openeuler_package_policy.t rename to xCAT-test/native/openeuler_package_policy.t index 8e2fbc81b..e68baf8ba 100644 --- a/xCAT-test/unit/openeuler_package_policy.t +++ b/xCAT-test/native/openeuler_package_policy.t @@ -26,7 +26,7 @@ foreach my $role (@roles) { my ($rc, $requires) = run('rpmspec', '-q', '--requires', '--target', 'x86_64', @macros, '--define', 'openEuler 2', $spec); is($rc, 0, "$name native dependencies parse through RPM"); - BAIL_OUT("Unable to parse $spec: $requires") if $rc; + die("Unable to parse $spec: $requires") if $rc; my @dhcp = grep { /\bkea(?:-hooks)?\b|\/usr\/sbin\/dhcpd/ } split(/\n/, $requires); ok(@dhcp, "$name emits DHCP policy requirements"); $consumer{$name} = build_fixture("policy-$name", join('', map {"Requires: $_\n"} @dhcp)); @@ -84,9 +84,9 @@ foreach my $scenario (@scenarios) { my ($label, $success, @providers) = @$scenario; my $db = tempdir(DIR => $root, CLEANUP => 1); my ($init_rc, $init_output) = run('rpm', '--dbpath', $db, '--initdb'); - BAIL_OUT("Cannot initialize test RPM database: $init_output") if $init_rc; + die("Cannot initialize test RPM database: $init_output") if $init_rc; my ($install_rc, $install_output) = run('rpm', '--dbpath', $db, '--justdb', '--nodeps', '-i', map {$provider{$_}} @providers); - BAIL_OUT("Cannot seed test RPM database: $install_output") if $install_rc; + die("Cannot seed test RPM database: $install_output") if $install_rc; foreach my $role (@roles) { my ($rc, $output) = run('rpm', '--dbpath', $db, '--test', '-i', $consumer{$role->[0]}); is($rc == 0 ? 1 : 0, $success, "$role->[0] $label has the expected dependency result") @@ -108,9 +108,9 @@ sub build_fixture { print {$fh} "\n%files\n", $file || '', "\n"; close($fh) or die "$spec: $!"; my ($rc, $output) = run('rpmbuild', '-bb', '--define', "_topdir $root", '--define', '_build_id_links none', $spec); - BAIL_OUT("Cannot build RPM fixture $name: $output") if $rc; + die("Cannot build RPM fixture $name: $output") if $rc; my $rpm = "$root/RPMS/noarch/$name-1-1.noarch.rpm"; - BAIL_OUT("RPM fixture missing: $rpm") unless -f $rpm; + die("RPM fixture missing: $rpm") unless -f $rpm; return $rpm; } sub run { @@ -124,7 +124,7 @@ sub run { exec(@command) or die "@command: $!"; } waitpid($pid, 0); - my $status = $? >> 8; + my $status = (($? & 127) ? 128 + ($? & 127) : $? >> 8); open(my $fh, '<', $log) or die "$log: $!"; return ($status, do {local $/; scalar <$fh>} || ''); } diff --git a/xCAT-test/unit/openeuler_xcatroot.t b/xCAT-test/native/openeuler_xcatroot.t similarity index 100% rename from xCAT-test/unit/openeuler_xcatroot.t rename to xCAT-test/native/openeuler_xcatroot.t diff --git a/xCAT-test/unit/pgsqlsetup_native_schema.t b/xCAT-test/native/pgsqlsetup_native_schema.t similarity index 94% rename from xCAT-test/unit/pgsqlsetup_native_schema.t rename to xCAT-test/native/pgsqlsetup_native_schema.t index 0109b7b07..913b77624 100644 --- a/xCAT-test/unit/pgsqlsetup_native_schema.t +++ b/xCAT-test/native/pgsqlsetup_native_schema.t @@ -13,9 +13,9 @@ use DBI; my $bindir = $ENV{XCAT_TEST_PG_BINDIR}; plan skip_all => 'Set XCAT_TEST_PG_BINDIR to native PostgreSQL binaries' unless $bindir; -BAIL_OUT('The private PostgreSQL fixture must run without root privileges') if $< == 0; +die('The private PostgreSQL fixture must run without root privileges') if $< == 0; for my $command (qw(initdb pg_ctl postgres psql createdb)) { - BAIL_OUT("Missing native PostgreSQL program $command") unless -x "$bindir/$command"; + die("Missing native PostgreSQL program $command") unless -x "$bindir/$command"; } BEGIN { *CORE::GLOBAL::exit = sub { die bless { status => $_[0] || 0 }, 'PgSchemaTestExit' }; @@ -47,7 +47,7 @@ push @init, ('-L', $ENV{XCAT_TEST_PG_SHAREDIR}) if $ENV{XCAT_TEST_PG_SHAREDIR}; is(quiet_system(@init), 0, 'native initdb creates a private cluster'); my $start = quiet_system("$bindir/pg_ctl", '-D', "$tmp/data", '-l', "$tmp/server.log", '-o', "-k $tmp/socket -h '' -p $ENV{PGPORT}", '-w', 'start'); is($start, 0, 'private PostgreSQL starts with only a private Unix socket'); -BAIL_OUT('Unable to start the private PostgreSQL fixture') if $start != 0; +die('Unable to start the private PostgreSQL fixture') if $start != 0; $started = 1; my $admin = DBI->connect("dbi:Pg:dbname=postgres;host=$ENV{PGHOST};port=$ENV{PGPORT}", 'postgres', '', { RaiseError => 1, PrintError => 0, AutoCommit => 1 }); my ($version) = $admin->selectrow_array('SHOW server_version_num'); @@ -64,7 +64,7 @@ my ($help, $load_error); $load_error = $@; } is(ref($load_error), 'PgSchemaTestExit', 'complete pgsqlsetup loads through its help path'); -BAIL_OUT("Unable to load pgsqlsetup: $load_error") unless ref($load_error) eq 'PgSchemaTestExit' && $load_error->{status} == 0; +die("Unable to load pgsqlsetup: $load_error") unless ref($load_error) eq 'PgSchemaTestExit' && $load_error->{status} == 0; { local $::osname = 'Linux'; local $::linuxos = 'openeuler24.03sp3'; diff --git a/xCAT-test/unit/remoteshell_hostkeys_openeuler.t b/xCAT-test/native/remoteshell_hostkeys_openeuler.t similarity index 98% rename from xCAT-test/unit/remoteshell_hostkeys_openeuler.t rename to xCAT-test/native/remoteshell_hostkeys_openeuler.t index 87c8c83ce..c149e730c 100644 --- a/xCAT-test/unit/remoteshell_hostkeys_openeuler.t +++ b/xCAT-test/native/remoteshell_hostkeys_openeuler.t @@ -40,7 +40,7 @@ sub public_identity { my @types = qw(dsa rsa ecdsa ed25519); for my $type (@types) { my $rc = system('/usr/bin/ssh-keygen', '-q', '-t', $type, '-f', "$tmp/keys/$type", '-N', '', '-C', ''); - BAIL_OUT("native ssh-keygen cannot generate disposable $type fixture") if $rc; + die("native ssh-keygen cannot generate disposable $type fixture") if $rc; } for my $name (qw(remoteshell xcatlib.sh remoteshell-sshd-config)) { copy("$source/xCAT/postscripts/$name", "$tmp/bin/$name") or die $!; @@ -119,7 +119,7 @@ for my $case ( 'unshare', '-m', '--', "$tmp/bin/namespace", "$tmp/bin/remoteshell") or die $!; $output = do { local $/; <$pipe> }; close($pipe); - $rc = $? >> 8; + $rc = (($? & 127) ? 128 + ($? & 127) : $? >> 8); } if ($fail_command) { is($rc, 1, "$label run $run reports failed key protection"); diff --git a/xCAT-test/unit/syslog_openeuler.t b/xCAT-test/native/syslog_openeuler.t similarity index 99% rename from xCAT-test/unit/syslog_openeuler.t rename to xCAT-test/native/syslog_openeuler.t index 57a64b513..9fac95d08 100644 --- a/xCAT-test/unit/syslog_openeuler.t +++ b/xCAT-test/native/syslog_openeuler.t @@ -140,6 +140,7 @@ for my $case (@cases) { is(system('unshare', '-mn', '--', "$tmp/bin/namespace"), 0, "$name: isolated driver completed"); my $rc = read_file("$fixture/postscript-1.rc"); chomp $rc; + die "Missing postscript status for $name" unless $rc =~ /^\d+$/; if ($failure) { isnt($rc, 0, "$name: failure reaches postscript exit"); is(read_file("$fixture/logger.log"), '', "$name: no success log"); diff --git a/xCAT-test/unit/syslog_openeuler_chroot.t b/xCAT-test/native/syslog_openeuler_chroot.t similarity index 96% rename from xCAT-test/unit/syslog_openeuler_chroot.t rename to xCAT-test/native/syslog_openeuler_chroot.t index 98bf97ea5..ca48672c9 100644 --- a/xCAT-test/unit/syslog_openeuler_chroot.t +++ b/xCAT-test/native/syslog_openeuler_chroot.t @@ -103,8 +103,8 @@ for my $case (['mn', 0], ['snlocal', 0], ['snforward', 0], ['cn', 0], ['mn', 1], is(read_file("$evidence/helper.rc"), "1\n", "$name: shared restart helper retains its existing contract"); is(read_file("$evidence/services-postscript.log"), '', "$name: postscript does not attempt service activation"); if ($invalid) { - isnt(read_file("$evidence/postscript-1.rc"), "0\n", "$name: parser failure reaches the postscript exit"); - isnt(read_file("$evidence/parser.rc"), "0\n", "$name: actual native parser rejects the configuration"); + is(read_file("$evidence/postscript-1.rc"), "1\n", "$name: parser failure reaches the postscript exit"); + is(read_file("$evidence/parser.rc"), "1\n", "$name: actual native parser rejects the configuration"); is(read_file("$evidence/logger.log"), '', "$name: no success log follows invalid configuration"); } else { is(read_file("$evidence/postscript-1.rc"), "0\n", "$name: configuration completes successfully"); diff --git a/xCAT-test/unit/anaconda_openeuler_media.t b/xCAT-test/unit/anaconda_openeuler_media.t index d752b90a2..71036c0a3 100644 --- a/xCAT-test/unit/anaconda_openeuler_media.t +++ b/xCAT-test/unit/anaconda_openeuler_media.t @@ -59,9 +59,9 @@ for my $case ( "$fixture retains the exact release and architecture"); } -for my $version ('20.03-LTS-SP4', '22.03-LTS-SP4', '24.03-LTS-SP1', '24.03-LTS-SP3') { - (my $os = lc($version)) =~ s/-lts//; - $os =~ s/-sp/sp/; +for my $case (['20.03-LTS-SP4', '20.03sp4'], ['22.03-LTS-SP4', '22.03sp4'], + ['24.03-LTS-SP1', '24.03sp1'], ['24.03-LTS-SP3', '24.03sp3']) { + my ($version, $os) = @$case; my $responses = inspect_media(media_tree($version, 'x86_64')); like($responses->[0]{info} || '', qr/^DISTNAME:openeuler\Q$os\E\n/, "$version keeps its service pack"); diff --git a/xCAT-test/unit/buildrpms_openeuler.t b/xCAT-test/unit/buildrpms_openeuler.t index 78d79bbcc..915de3410 100644 --- a/xCAT-test/unit/buildrpms_openeuler.t +++ b/xCAT-test/unit/buildrpms_openeuler.t @@ -1,13 +1,9 @@ #!/usr/bin/env perl use strict; use warnings; -use File::Copy qw(copy); -use File::Path qw(make_path); -use File::Temp qw(tempdir); use FindBin; use lib "$FindBin::Bin/../lib", "$FindBin::Bin/../../build-utils/lib"; use Test::More; -use XCAT::Test::File qw(repo_path slurp_repo_file); use XCAT::BuildUtils qw(openeuler_build_target openeuler_repo_subdir targetarch_from_target); foreach my $version (['20.03 (LTS-SP4)', '20.03sp4'], ['22.03 (LTS-SP4)', '22.03sp4'], @@ -30,149 +26,4 @@ foreach my $invalid ('openeuler-24.09-x86_64', 'openeuler-25.03-x86_64', 'openeu like($@, qr/Unsupported openEuler build target/, "$invalid is rejected"); } -my $root = tempdir(CLEANUP => 1); -my $driver = "$root/driver.pl"; -write_file($driver, <<'PERL'); -BEGIN { - require File::Slurper; - require Parallel::ForkManager; - no warnings 'redefine'; - *Parallel::ForkManager::new = sub { die "BUILD_BOUNDARY\n" } if $ENV{BUILD_STOP}; - my $write = \&File::Slurper::write_text; - *File::Slurper::write_text = sub { - my ($path, @rest) = @_; - $path =~ s{^/etc/yum\.repos\.d/}{$ENV{BUILD_FIXTURE}/repos/}; - return $write->($path, @rest); - }; - *CORE::GLOBAL::readpipe = sub { - return "$ENV{BUILD_ARCH}\n" if $_[0] eq 'uname -m'; - return CORE::readpipe($_[0]); - }; - *CORE::GLOBAL::system = sub { - if (@_ == 1 && $_[0] =~ /source \/etc\/os-release/) { - my $shell = "$ENV{BUILD_FIXTURE}/install.sh"; - CORE::open(my $fh, '>', $shell) or die $!; - print {$fh} 'source() { . "$BUILD_FIXTURE/os-release"; }', "\n", $_[0]; - close($fh); - return CORE::system('bash', $shell); - } - return 0; - }; -} -do './buildrpms.pl'; -die $@ if $@; -PERL - -my $native = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--setup_local_repos'); -is($native->{status}, 0, 'full builder creates local repositories for the native default target'); -like($native->{core}, qr{/dist/openeuler-24\.03sp3-x86_64/rpms$}m, 'full builder selects the exact native mock target'); -like($native->{dep}, qr{/openeuler24\.03sp3/x86_64$}m, 'full builder selects matching dependency artifacts'); -like($native->{core}, qr/^gpgcheck=1$/m, 'native core repository requires signatures'); -like($native->{dep}, qr/^gpgcheck=1$/m, 'native dependency repository requires signatures'); -my $explicit = run_builder('rocky', '9.6', '9.6', 'x86_64', '--setup_local_repos', '--target', 'openeuler-24.03-ppc64le'); -is($explicit->{status}, 0, 'an explicit native target overrides the host default for repository setup'); -like($explicit->{dep}, qr{/openeuler24\.03/ppc64le$}m, 'explicit native target chooses its own release and architecture'); -my $el = run_builder('rocky', '9.6', '9.6', 'x86_64', '--setup_local_repos'); -is($el->{status}, 0, 'existing EL local repository setup completes'); -like($el->{core}, qr{/dist/rocky\+epel-10-x86_64/rpms$}m, 'existing EL default target is unchanged'); -like($el->{dep}, qr{/el9/x86_64$}m, 'existing EL dependency path is unchanged'); -for my $missing (qw(core dep)) { - local $ENV{BUILD_MISSING_KEY} = $missing; - my $result = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--setup_local_repos'); - isnt($result->{status}, 0, "setup rejects the missing $missing exported key"); - like($result->{stderr}, qr/Missing openEuler repository signing key/, 'setup identifies the absent trust input'); - is($result->{core} . $result->{dep}, '', 'setup fails before writing either repository configuration'); -} -{ - local $ENV{BUILD_STOP} = 1; - my $unsigned = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--package', 'xCAT-client'); - isnt($unsigned->{status}, 0, 'unsigned native binary builds are rejected'); - like($unsigned->{stderr}, qr/openEuler binary repository builds require --gpg-sign/, 'the CLI reports the signing requirement'); - unlike($unsigned->{stderr}, qr/BUILD_BOUNDARY/, 'unsigned native builds stop before the build scheduler'); - for my $case (['native signed', 'openEuler', '--gpg-sign'], ['native source only', 'openEuler', '--source-only'], - ['legacy unsigned', 'rocky']) { - my ($label, $id, @args) = @$case; - my $result = run_builder($id, '24.03 (LTS-SP3)', '24.03', 'x86_64', '--package', 'xCAT-client', @args); - like($result->{stderr}, qr/BUILD_BOUNDARY/, "$label reaches the existing build scheduler"); - unlike($result->{stderr}, qr/repository builds require --gpg-sign/, "$label preserves its signing policy"); - } -} -my $install = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--install_deps'); -is($install->{status}, 0, 'full native prerequisite entry point completes through process boundaries'); -unlike($install->{commands}, qr/epel|crb|codeready/i, 'native prerequisites do not enable EL repositories'); -unlike($install->{commands}, qr/\bpodman\b/, 'native prerequisites do not request an unrelated container bootstrap'); -like($install->{commands}, qr/--setopt=install_weak_deps=False/, 'native prerequisites do not pull optional container dependencies'); -like($install->{commands}, qr/--setopt=\*\.gpgcheck=1/, 'native prerequisite installation enforces signatures'); -like($install->{commands}, qr/--setopt=strict=1/, 'native prerequisite installation does not skip missing packages'); -like($install->{commands}, qr/\bsystemd-nspawn\b/, 'native prerequisites provide the mock isolation executable'); -my $el_install = run_builder('rocky', '9.6', '9.6', 'x86_64', '--install_deps'); -like($el_install->{commands}, qr/epel-release-latest-10/, 'existing EL prerequisite repository choice is preserved'); -my $merge = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--merge-core-repos'); -isnt($merge->{status}, 0, 'native flat repository assembly is rejected'); -like($merge->{stderr}, qr/openEuler repositories must retain their release and architecture/, 'native assembly error preserves the per-cell output contract'); -my $el_merge = run_builder('rocky', '9.6', '9.6', 'x86_64', '--merge-core-repos', '--output-dir', 'merged', '--input-core-repos', 'native-input'); -isnt($el_merge->{status}, 0, 'an EL host cannot flatten copied native inputs'); -like($el_merge->{stderr}, qr/openEuler repositories must retain their release and architecture/, 'native build provenance survives copying to an unrelated path'); -{ - local $ENV{BUILD_DNF_FAILURE} = 37; - my $failed = run_builder('openEuler', '24.03 (LTS-SP3)', '24.03', 'x86_64', '--install_deps'); - is($failed->{status}, 37, 'native prerequisite failure terminates the entry point'); -} done_testing(); - -sub run_builder { - my ($id, $version, $version_id, $arch, @args) = @_; - my $fixture = tempdir(DIR => $root, CLEANUP => 1); - make_path("$fixture/build-utils/lib/XCAT", "$fixture/bin", "$fixture/repos", "$fixture/native-input", "$fixture/locks", "$fixture/home", - "$fixture/dep/openeuler24.03sp3/x86_64", "$fixture/dep/openeuler24.03/ppc64le", "$fixture/dep/el9/x86_64"); - my $body = read_file($ENV{XCAT_TEST_BUILDRPMS} || repo_path('buildrpms.pl')); - $body =~ s{'/etc/os-release'}{'$fixture/os-release'}g; - $body =~ s{/var/lock/}{$fixture/locks/}g; - write_file("$fixture/buildrpms.pl", $body); - copy(repo_path('build-utils/lib/XCAT/BuildUtils.pm'), "$fixture/build-utils/lib/XCAT/BuildUtils.pm") or die $!; - copy($driver, "$fixture/driver.pl") or die $!; - write_file("$fixture/Version", "2.18.0\n"); - write_file("$fixture/Gitepoch", "1756000000\n"); - write_file("$fixture/native-input/buildinfo.txt", "BUILD_TARGET=openeuler-24.03sp3-x86_64\n"); - write_file("$fixture/os-release", "ID=$id\nVERSION=\"$version\"\nVERSION_ID=$version_id\n"); - for my $target ('openeuler-24.03sp3-x86_64', 'openeuler-24.03-ppc64le') { - my $subdir = openeuler_repo_subdir($target); - for my $key (['core', "$fixture/dist/$target/rpms"], ['dep', "$fixture/dep/$subdir"]) { - make_path("$key->[1]/repodata"); - write_file("$key->[1]/repodata/repomd.xml.key", 'existing exported key') - unless ($ENV{BUILD_MISSING_KEY} || '') eq $key->[0]; - } - } - write_file("$fixture/bin/dnf", "#!/bin/sh\nprintf '%s\\n' \"\$*\" >> \"\$BUILD_FIXTURE/commands\"\nexit \"\${BUILD_DNF_FAILURE:-0}\"\n"); - write_file("$fixture/bin/systemctl", "#!/bin/sh\nexit 0\n"); - write_file("$fixture/bin/rpmdev-setuptree", "#!/bin/sh\nexit 0\n"); - chmod 0755, map {"$fixture/bin/$_"} qw(dnf systemctl rpmdev-setuptree); - local %ENV = (%ENV, HOME => "$fixture/home", BUILD_FIXTURE => $fixture, BUILD_ARCH => $arch, PATH => "$fixture/bin:$ENV{PATH}"); - my $pid = fork(); - die $! unless defined $pid; - if (!$pid) { - chdir($fixture) or die $!; - open(STDOUT, '>', 'stdout') or die $!; - open(STDERR, '>', 'stderr') or die $!; - exec($^X, 'driver.pl', @args, '--xcat_dep_path', "$fixture/dep") or die $!; - } - waitpid($pid, 0); - my $status = $? >> 8; - my $result = {status => $status, core => read_file("$fixture/repos/xcat-core-local.repo"), - dep => read_file("$fixture/repos/xcat-dep.repo"), commands => read_file("$fixture/commands"), stderr => read_file("$fixture/stderr")}; - diag(read_file("$fixture/stderr")) if $status && !$ENV{BUILD_DNF_FAILURE} && !$ENV{BUILD_MISSING_KEY} - && !$ENV{BUILD_STOP} && !grep {$_ eq '--merge-core-repos'} @args; - return $result; -} -sub write_file { - my ($path, $contents) = @_; - open(my $fh, '>', $path) or die "$path: $!"; - print {$fh} $contents; - close($fh) or die $!; -} -sub read_file { - my ($path) = @_; - return '' unless -f $path; - open(my $fh, '<', $path) or die $!; - return do {local $/; scalar <$fh>} || ''; -} diff --git a/xCAT-test/unit/go_xcat_openeuler.t b/xCAT-test/unit/go_xcat_openeuler.t deleted file mode 100644 index 17bfa7704..000000000 --- a/xCAT-test/unit/go_xcat_openeuler.t +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; -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 $script = $ENV{XCAT_TEST_GO_XCAT} || repo_path('xCAT-server/share/xcat/tools/go-xcat'); -my $root = tempdir(CLEANUP => 1); -my $driver = "$root/driver.sh"; -write_file($driver, <<'SH'); -source "$GO_XCAT_SOURCE" -awk() { - local -a args=("$@") - local i - for i in "${!args[@]}"; do - [[ "${args[i]}" != /etc/os-release ]] || args[i]="$FIXTURE/os-release" - done - command awk "${args[@]}" -} -dnf() { - if [[ $1 == repoquery ]]; then - printf '%s\n' "${@: -1}" - else - printf 'dnf'; printf ' <%s>' "$@"; printf '\n' - fi - return "${PACKAGE_STATUS:-0}" -} -yum() { printf 'yum'; printf ' <%s>' "$@"; printf '\n'; return "${PACKAGE_STATUS:-0}"; } -download_file() { return 1; } -add_repo_by_file() { cp "$1" "$FIXTURE/$2.repo"; } -TMP_DIR="$FIXTURE" -GO_XCAT_LINUX_DISTRO="$(check_linux_distro)" -GO_XCAT_LINUX_VERSION="$(check_linux_version)" -detected=$? -GO_XCAT_ARCH="$TEST_ARCH" -case "$1" in -detect) - printf '%s\n%s\n' "$GO_XCAT_LINUX_DISTRO" "$GO_XCAT_LINUX_VERSION" - exit "$detected" - ;; -repos) - [[ "$detected" = 0 ]] || exit "$detected" - GO_XCAT_DEFAULT_BASE_URL=https://packages.example.test/xcat - add_xcat_core_repo_yum_or_zypper "${CORE_URL:-}" "${REPO_VERSION:-latest}" || exit $? - add_xcat_dep_repo_yum_or_zypper "${DEP_URL:-}" "${REPO_VERSION:-latest}" - ;; -install-dnf) install_packages_dnf -y xCAT ;; -install-yum) install_packages_yum -y xCAT ;; -refresh) update_repo_dnf ;; -cleanup-dnf) - type() { [[ "$1" != yum ]] && builtin type "$@"; } - grep() { return 1; } - xargs() { return 0; } - mv() { return 0; } - remove_repo_yum xcat-core - ;; -*) exit 64 ;; -esac -SH - -my @versions = ( - ['20.03 (LTS-SP4)', '20.03sp4'], - ['22.03 (LTS-SP4)', '22.03sp4'], - ['24.03 (LTS-SP1)', '24.03sp1'], - ['24.03 (LTS-SP3)', '24.03sp3'], - ['24.03 (LTS-SP4)', '24.03sp4'], - ['24.03 (LTS)', '24.03'], -); -foreach my $version (@versions) { - foreach my $arch ('x86_64', 'ppc64le') { - my $r = run_case(version => $version->[0], arch => $arch, action => 'repos'); - is($r->{status}, 0, "$version->[1] $arch repo creation completes"); - like($r->{core}, qr{^baseurl=https://packages\.example\.test/xcat/yum/latest/xcat-core/openeuler\Q$version->[1]\E/\Q$arch\E$}m, - 'core repository retains native release, service pack and architecture'); - like($r->{dep}, qr{^baseurl=https://packages\.example\.test/xcat/yum/latest/xcat-dep/openeuler\Q$version->[1]\E/\Q$arch\E$}m, - 'dependency repository retains native release, service pack and architecture'); - } -} - -foreach my $id ('openEuler', 'openeuler', "'openEuler'") { - my $r = run_case(id => $id, version => '24.03 (LTS-SP3)', action => 'detect'); - is($r->{stdout}, "openeuler\n24.03sp3\n", "$id selects native identity"); -} -my $single = run_case(version_line => "VERSION='24.03 (LTS-SP3)'\n", action => 'detect'); -is($single->{stdout}, "openeuler\n24.03sp3\n", 'single-quoted VERSION retains SP'); -my $ga = run_case(version_line => '', action => 'detect'); -is($ga->{stdout}, "openeuler\n24.03\n", 'missing VERSION uses VERSION_ID for GA'); -foreach my $version ('24.09', '25.03 (LTS)', '24.03 (LTS-SP0)', '24.03 (LTS-SP04)', '24.03 (LTS-SP3) trailing') { - my $r = run_case(version => $version, action => 'repos'); - isnt($r->{status}, 0, "$version is rejected before repo creation"); - is($r->{core}, '', 'invalid release creates no core repository'); -} -my $wrong_arch = run_case(arch => 'ppc64', action => 'repos'); -isnt($wrong_arch->{status}, 0, 'big-endian POWER is not silently admitted'); - -my $override = run_case(action => 'repos', env => {CORE_URL => 'https://custom.example.test/native-core', DEP_URL => 'https://custom.example.test/native-dep'}); -like($override->{core}, qr{^baseurl=https://custom\.example\.test/native-core$}m, 'explicit core URL is authoritative'); -like($override->{dep}, qr{^baseurl=https://custom\.example\.test/native-dep/openeuler24\.03sp3/x86_64$}m, 'explicit dependency root preserves the exact native suffix'); -my $devel = run_case(action => 'repos', env => {REPO_VERSION => 'devel'}); -like($devel->{core}, qr{/yum/devel/core-snap/openeuler24\.03sp3/x86_64$}m, 'development core uses its native subdirectory'); - -my $offline = "$root/offline"; -make_path("$offline/core", "$offline/dep/openeuler24.03sp3/x86_64"); -my $local = run_case(action => 'repos', env => {CORE_URL => "file://$offline/core", DEP_URL => "file://$offline/dep"}); -is($local->{status}, 0, 'offline native directories are usable'); -like($local->{core}, qr/^gpgcheck=1$/m, 'offline native core still requires signed packages'); -like($local->{dep}, qr/^gpgcheck=1$/m, 'offline native dependencies still require signed packages'); -unlike($local->{dep}, qr/^gpgcheck=0$/m, 'missing metadata signature cannot disable package verification'); - -foreach my $action ('install-dnf', 'install-yum', 'refresh') { - my $r = run_case(action => $action); - is($r->{status}, 0, "$action completes through its command boundary"); - like($r->{stdout}, qr/<--setopt=\*\.gpgcheck=1>/, "$action enforces repository package signatures"); - unlike($r->{stdout}, qr/nogpgcheck|strict=0|epel|crb/i, "$action has no native signature or dependency bypass"); - if ($action ne 'refresh') { - like($r->{stdout}, qr/ /, 'native packages share one transaction'); - like($r->{stdout}, qr/<--setopt=strict=1>/, 'native DNF cannot inherit skip-broken configuration') if $action eq 'install-dnf'; - my $failed = run_case(action => $action, env => {PACKAGE_STATUS => 37}); - is($failed->{status}, 37, 'native dependency/signature failure is returned to the caller'); - } -} -foreach my $distro (['rocky', '9.6', 'rh9'], ['rhel', '10.1', 'rh10'], ['sles', '15.6', 'sles15']) { - my $r = run_case(id => $distro->[0], version_id => $distro->[1], action => 'repos'); - is($r->{status}, 0, "$distro->[0] repo mapping still completes"); - like($r->{core}, qr{/yum/latest/xcat-core$}m, 'existing core layout remains flat'); - like($r->{dep}, qr{/xcat-dep/\Q$distro->[2]\E/x86_64$}m, 'existing dependency mapping is preserved'); -} -my $el_install = run_case(id => 'rhel', version_id => '10.1', action => 'install-dnf'); -is($el_install->{status}, 0, 'EL installation completes after repository checks'); -like($el_install->{stdout}, qr/<--nogpgcheck> <--setopt=strict=0>/, 'existing EL install options are unchanged'); -my $cleanup = run_case(action => 'cleanup-dnf'); -is($cleanup->{status}, 0, 'repository replacement works with DNF and no yum command'); -is($cleanup->{stdout}, "dnf \n", 'DNF clears metadata during repository replacement'); - -done_testing(); - -sub run_case { - my (%option) = @_; - my $fixture = tempdir(DIR => $root, CLEANUP => 1); - my $version_line = exists($option{version_line}) ? $option{version_line} - : 'VERSION="' . ($option{version} || '24.03 (LTS-SP3)') . "\"\n"; - write_file("$fixture/os-release", 'ID=' . ($option{id} || 'openEuler') . "\n" . $version_line . - 'VERSION_ID="' . ($option{version_id} || '24.03') . "\"\n"); - local %ENV = (%ENV, GO_XCAT_SOURCE => $script, FIXTURE => $fixture, - TEST_ARCH => $option{arch} || 'x86_64', %{$option{env} || {}}); - my $pid = fork(); - die "fork: $!" unless defined($pid); - if (!$pid) { - open(STDOUT, '>', "$fixture/stdout") or die $!; - open(STDERR, '>', "$fixture/stderr") or die $!; - exec('bash', $driver, $option{action}) or die $!; - } - waitpid($pid, 0); - my $status = $? >> 8; - return {status => $status, stdout => read_file("$fixture/stdout"), stderr => read_file("$fixture/stderr"), - core => read_file("$fixture/xcat-core.repo"), dep => read_file("$fixture/xcat-dep.repo")}; -} -sub write_file { - my ($path, $contents) = @_; - open(my $fh, '>', $path) or die "$path: $!"; - print {$fh} $contents; - close($fh) or die "$path: $!"; -} -sub read_file { - my ($path) = @_; - return '' unless -f $path; - open(my $fh, '<', $path) or die "$path: $!"; - return do {local $/; scalar <$fh>} || ''; -} diff --git a/xCAT-test/unit/mysqlsetup_openeuler_reboot.t b/xCAT-test/unit/mysqlsetup_openeuler_reboot.t index 16adec9a5..8cd19f4d7 100644 --- a/xCAT-test/unit/mysqlsetup_openeuler_reboot.t +++ b/xCAT-test/unit/mysqlsetup_openeuler_reboot.t @@ -32,7 +32,7 @@ my ($help, $load_error); is(ref($load_error), 'MysqlSetupTestExit', 'the complete CLI reaches its help exit'); is(ref($load_error) ? $load_error->{status} : undef, 0, 'help exits successfully'); like($help, qr/mysqlsetup/, 'help prints the CLI usage'); -BAIL_OUT("Unable to load complete mysqlsetup: $load_error") +die("Unable to load complete mysqlsetup: $load_error") unless ref($load_error) eq 'MysqlSetupTestExit' && $load_error->{status} == 0; my $service_map = \&xCAT::Utils::servicemap; diff --git a/xCAT-test/unit/openeuler_genimage_bootfiles.t b/xCAT-test/unit/openeuler_genimage_bootfiles.t index 067e869ed..aaeb95404 100644 --- a/xCAT-test/unit/openeuler_genimage_bootfiles.t +++ b/xCAT-test/unit/openeuler_genimage_bootfiles.t @@ -154,13 +154,7 @@ for my $failure (qw(none kernel-copy dracut move-initrd)) { is($state->{lock_calls}, 0, "EL $failure retains its existing onlyinitrd lock behavior"); } -my @legacy_repositories = qw( - oracle-linux-ol8.repo oracle-linux-ol9.repo oracle-linux-ol10.repo - uek-ol8.repo uek-ol9.repo uek-ol10.repo Rocky-AppStream.repo Rocky-BaseOS.repo - Rocky-Extras.repo rocky.repo rocky-extras.repo CentOS-Base.repo centos.repo - centos-addons.repo almalinux-ha.repo almalinux-nfv.repo almalinux-plus.repo - almalinux-powertools.repo almalinux.repo almalinux-resilientstorage.repo almalinux-rt.repo -); +my @legacy_repositories = qw(Rocky-BaseOS.repo almalinux.repo CentOS-Base.repo oracle-linux-ol10.repo); my @custom_repositories = qw(local-repository-0.repo xCAT-custom.repo administrator.repo); my $enabled = "[fixture]\nenabled=1\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; my $disabled = "[fixture]\nenabled=0\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; diff --git a/xCAT-test/unit/openeuler_pre_partition.t b/xCAT-test/unit/openeuler_pre_partition.t index 92d0cb89f..d7a7803f1 100644 --- a/xCAT-test/unit/openeuler_pre_partition.t +++ b/xCAT-test/unit/openeuler_pre_partition.t @@ -57,25 +57,25 @@ for my $name (qw(uname blockdev udevadm mknod chown vgs vgchange vgremove logger my $small = 62914560; my $large = 8589934592; my @cases = ( - ['native BIOS small', 'openeuler', 'x86_64', 0, $small, 1, 0, 0], - ['native BIOS large', 'openeuler', 'x86_64', 0, $large, 1, 0, 0], - ['native UEFI small', 'openeuler', 'x86_64', 1, $small, 0, 1, 0], - ['native UEFI large', 'openeuler', 'x86_64', 1, $large, 0, 1, 0], - ['native POWER small', 'openeuler', 'ppc64le', 0, $small, 0, 0, 1], - ['native POWER large', 'openeuler', 'ppc64le', 0, $large, 0, 0, 1], - ['legacy BIOS small', 'rhels8', 'x86_64', 0, $small, 0, 0, 0], - ['legacy BIOS large', 'rhels8', 'x86_64', 0, $large, 1, 0, 0], - ['legacy UEFI small', 'rhels8', 'x86_64', 1, $small, 0, 1, 0], - ['legacy UEFI large', 'rhels8', 'x86_64', 1, $large, 1, 1, 0], - ['legacy POWER large', 'rhels8', 'ppc64le', 0, $large, 1, 0, 1], - ['legacy POWER big endian', 'rhels8', 'ppc64', 0, $small, 0, 0, 1], - ['native BIOS static custom', 'openeuler', 'x86_64', 0, $small, 0, 0, 0, 'static'], - ['native BIOS script custom', 'openeuler', 'x86_64', 0, $small, 0, 0, 0, 'script'], - ['native UEFI custom', 'openeuler', 'x86_64', 1, $small, 0, 0, 0, 'static'], - ['native POWER custom', 'openeuler', 'ppc64le', 0, $large, 0, 0, 0, 'static'], + ['native BIOS small', 'openeuler', 'x86_64', 0, $small, 1, 0, 0, 'xfs'], + ['native BIOS large', 'openeuler', 'x86_64', 0, $large, 1, 0, 0, 'xfs'], + ['native UEFI small', 'openeuler', 'x86_64', 1, $small, 0, 1, 0, 'xfs'], + ['native UEFI large', 'openeuler', 'x86_64', 1, $large, 0, 1, 0, 'xfs'], + ['native POWER small', 'openeuler', 'ppc64le', 0, $small, 0, 0, 1, 'ext4'], + ['native POWER large', 'openeuler', 'ppc64le', 0, $large, 0, 0, 1, 'ext4'], + ['legacy BIOS small', 'rhels8', 'x86_64', 0, $small, 0, 0, 0, 'xfs'], + ['legacy BIOS large', 'rhels8', 'x86_64', 0, $large, 1, 0, 0, 'xfs'], + ['legacy UEFI small', 'rhels8', 'x86_64', 1, $small, 0, 1, 0, 'xfs'], + ['legacy UEFI large', 'rhels8', 'x86_64', 1, $large, 1, 1, 0, 'xfs'], + ['legacy POWER large', 'rhels8', 'ppc64le', 0, $large, 1, 0, 1, 'xfs'], + ['legacy POWER big endian', 'rhels8', 'ppc64', 0, $small, 0, 0, 1, 'xfs'], + ['native BIOS static custom', 'openeuler', 'x86_64', 0, $small, 0, 0, 0, 'xfs', 'static'], + ['native BIOS script custom', 'openeuler', 'x86_64', 0, $small, 0, 0, 0, 'xfs', 'script'], + ['native UEFI custom', 'openeuler', 'x86_64', 1, $small, 0, 0, 0, 'xfs', 'static'], + ['native POWER custom', 'openeuler', 'ppc64le', 0, $large, 0, 0, 0, 'ext4', 'static'], ); for my $case (@cases) { - my ($label, $platform, $arch, $efi, $sectors, $bios, $esp, $prep, $custom) = @$case; + my ($label, $platform, $arch, $efi, $sectors, $bios, $esp, $prep, $fstype, $custom) = @$case; my $fixture = tempdir(DIR => $tmp, CLEANUP => 1); make_path(map { "$fixture/$_" } qw(tmp dev proc sys/firmware var/log/xcat etc)); make_path("$fixture/sys/firmware/efi") if $efi; @@ -103,7 +103,7 @@ for my $case (@cases) { my $rendered = read_file("$fixture/output.ks"); my ($pre) = $rendered =~ /^%pre[^\n]*\n(.*?)^%end/ms; defined($pre) or die 'rendered Kickstart did not contain a pre-install script'; - $pre =~ s{(/(?:tmp|dev|proc|sys|etc|var)(?=/|\b)|/foo\.log)}{$fixture$1}g; + $pre =~ s{(?&1', 'sh', '/bin/bash', "$fixture/pre.sh") or die $!; $output = do {local $/; <$pipe>}; close($pipe); - $rc = $? >> 8; + $rc = (($? & 127) ? 128 + ($? & 127) : $? >> 8); } is($rc, 0, "$label executes the complete generated pre-install script") or diag($output); my $layout = read_file("$fixture/tmp/partitionfile"); @@ -129,7 +129,6 @@ for my $case (@cases) { my $disk = "$fixture/dev/vda"; like($layout, qr/^ignoredisk --only-use=\Q$disk\E$/m, "$label retains actual selected disk"); like($layout, qr/^part biosboot --ondisk=\Q$disk\E --size=1$/m, "$label reserves one MiB on the selected disk") if $bios; - my $fstype = $platform eq 'openeuler' && $arch eq 'ppc64le' ? 'ext4' : 'xfs'; like($layout, qr{^part /boot --fstype=$fstype }m, "$label retains boot filesystem policy"); like($layout, qr{^logvol / --fstype=$fstype }m, "$label retains root filesystem policy"); like($layout, qr/^bootloader --boot-drive=vda$/m, "$label retains selected boot drive"); diff --git a/xCAT-test/unit/openeuler_profiles.t b/xCAT-test/unit/openeuler_profiles.t index 7976dd20c..a76beef3a 100644 --- a/xCAT-test/unit/openeuler_profiles.t +++ b/xCAT-test/unit/openeuler_profiles.t @@ -21,13 +21,18 @@ sub asset { return $path; } -foreach my $version (qw(20.03sp4 22.03sp4 24.03sp1 24.03sp3 24.03sp4 24.03)) { - my $os = "openeuler$version"; - my ($base) = $os =~ /^(openeuler[0-9]+\.03)/; - my @expected = $os eq $base ? ($os, 'openeuler') : ($os, $base, 'openeuler'); - is_deeply([xCAT::SvrUtils::get_os_search_list($os)], \@expected, +for my $case ( + ['openeuler20.03sp4', [qw(openeuler20.03sp4 openeuler20.03 openeuler)], '20', '03sp4'], + ['openeuler22.03sp4', [qw(openeuler22.03sp4 openeuler22.03 openeuler)], '22', '03sp4'], + ['openeuler24.03sp1', [qw(openeuler24.03sp1 openeuler24.03 openeuler)], '24', '03sp1'], + ['openeuler24.03sp3', [qw(openeuler24.03sp3 openeuler24.03 openeuler)], '24', '03sp3'], + ['openeuler24.03sp4', [qw(openeuler24.03sp4 openeuler24.03 openeuler)], '24', '03sp4'], + ['openeuler24.03', [qw(openeuler24.03 openeuler)], '24', '03'], +) { + my ($os, $expected, $major, $minor) = @$case; + my $base = "openeuler$major.03"; + is_deeply([xCAT::SvrUtils::get_os_search_list($os)], $expected, "$os falls back within its LTS family without an earlier SP"); - my ($major, $minor) = split /\./, $version, 2; is_deeply([xCAT::SvrUtils::parseosver($os)], ['openeuler', $major, $minor], "$os keeps the service pack in osdistro version fields"); is(xCAT::SvrUtils->getplatform($os), 'openeuler', "$os uses native platform assets"); diff --git a/xCAT-test/unit/packimage_openeuler_archive.t b/xCAT-test/unit/packimage_openeuler_archive.t index 8f4111a93..95f472c85 100644 --- a/xCAT-test/unit/packimage_openeuler_archive.t +++ b/xCAT-test/unit/packimage_openeuler_archive.t @@ -15,6 +15,7 @@ use XCAT::Test::File qw(repo_path); plan skip_all => 'requires Linux cpio, gzip and tar' unless $^O eq 'linux' && -x '/usr/bin/cpio' && -x '/usr/bin/gzip' && -x '/usr/bin/tar'; +local $ENV{PATH} = "$ENV{PATH}:/usr/sbin:/sbin"; my $dir = tempdir(CLEANUP => 1); make_path("$dir/db", "$dir/root/lib/perl", "$dir/root/share", "$dir/install/postscripts"); symlink(repo_path('perl-xCAT/xCAT'), "$dir/root/lib/perl/xCAT") or die $!; diff --git a/xCAT-test/unit/pgsqlsetup_openeuler.t b/xCAT-test/unit/pgsqlsetup_openeuler.t index bdceb3ea9..e6571df69 100644 --- a/xCAT-test/unit/pgsqlsetup_openeuler.t +++ b/xCAT-test/unit/pgsqlsetup_openeuler.t @@ -43,7 +43,7 @@ my ($help, $load_error); is(ref($load_error), 'PgSetupTestExit', 'the complete CLI reaches its help exit'); is(ref($load_error) ? $load_error->{status} : undef, 0, 'help exits successfully'); like($help, qr/pgsqlsetup/, 'help prints usage'); -BAIL_OUT("Unable to load complete pgsqlsetup: $load_error") +die("Unable to load complete pgsqlsetup: $load_error") unless ref($load_error) eq 'PgSetupTestExit' && $load_error->{status} == 0; sub invoke { diff --git a/xCAT-test/unit/xcat_conf_selection.t b/xCAT-test/unit/xcat_conf_selection.t new file mode 100644 index 000000000..c1d5649f2 --- /dev/null +++ b/xCAT-test/unit/xcat_conf_selection.t @@ -0,0 +1,32 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use FindBin; +use lib "$FindBin::Bin/../lib"; +use Test::More; +use XCAT::Test::File qw(repo_path); + +plan skip_all => 'requires rpmspec to render package conditionals' + if system('sh', '-c', 'command -v rpmspec >/dev/null 2>&1'); +my @macros = ('--define', 'version 2.18.0', '--define', 'release 1', + '--undefine', 'rhel', '--undefine', 'fedora', '--undefine', 'suse_version', '--undefine', 'openEuler'); +my @roles = (['mn', repo_path('xCAT/xCAT.spec')], ['sn', repo_path('xCATsn/xCATsn.spec')]); +for my $platform (['openEuler', 2, 'apach24'], ['rhel', 9, 'apach24'], + ['fedora', 40, 'apach24'], ['suse_version', 1500, 'apach24'], + ['rhel', 6, 'apach22']) { + my ($macro, $value, $generation) = @$platform; + for my $role (@roles) { + open(my $pipe, '-|', 'rpmspec', '-P', @macros, + '--define', "$macro $value", $role->[1]) or die "rpmspec: $!"; + my $rendered = do { local $/; <$pipe> }; + close($pipe) or die "Cannot render $role->[0] for $macro: $?"; + my ($install) = $rendered =~ /^%install\s*\n(.*?)(?=^%[a-z]|\z)/msg; + die 'Rendered spec has no install section' unless defined($install); + my $source = $generation eq 'apach24' ? 'xcat.conf.apach24' : 'xcat.conf'; + for my $directory (qw(httpd apache2)) { + like($install, qr{^cp\s+\S*/\Q$source\E\s+\$RPM_BUILD_ROOT/etc/$directory/conf\.d/xcat\.conf\s*$}m, + "$role->[0] $macro $value installs $generation for $directory"); + } + } +} +done_testing(); diff --git a/xCAT-test/unit/xcat_conf_upgrade_preservation.t b/xCAT-test/unit/xcat_conf_upgrade_preservation.t index 33269ab41..73f2a2c06 100644 --- a/xCAT-test/unit/xcat_conf_upgrade_preservation.t +++ b/xCAT-test/unit/xcat_conf_upgrade_preservation.t @@ -72,23 +72,6 @@ for my $pkg (@packages) { "$label package still takes the Apache 2.4 configuration from Source$apache24" ); my $install = section( $spec, 'install' ); - like( - $install, - qr{ - ^%if \s+ 0%\{\?fedora\} \s* \|\| \s* - 0%\{\?rhel\} \s* >= \s* 7 \s* \|\| \s* - 0%\{\?suse_version\} \s* >= \s* 1200 \s* \|\| \s* - 0%\{\?openEuler\} \s* $ \n - ^cp \s+ %\{SOURCE$apache24\} \s+ \$RPM_BUILD_ROOT/etc/$first/conf\.d/xcat\.conf \s* $ \n - ^cp \s+ %\{SOURCE$apache24\} \s+ \$RPM_BUILD_ROOT/etc/$second/conf\.d/xcat\.conf \s* $ \n - ^%else \s* $ \n - ^cp \s+ %\{SOURCE1\} \s+ \$RPM_BUILD_ROOT/etc/$first/conf\.d/xcat\.conf \s* $ \n - ^cp \s+ %\{SOURCE1\} \s+ \$RPM_BUILD_ROOT/etc/$second/conf\.d/xcat\.conf \s* $ \n - ^%endif \s* $ - }mx, - "$label package selects Apache 2.4 at build time on Fedora, EL7+, SLES 12+ and openEuler" - ); - # The next upgrade's %pretrans compares against these, so they must stay shipped. like( $install, qr{^cp \s+ %\{SOURCE$apache24\} \s+ \$RPM_BUILD_ROOT$templates/xcat\.conf\.apach24 \s* $}mx, "$label package still saves the Apache 2.4 template under conf.orig" );