mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-02 08:51:44 +00:00
test(openeuler): exercise signed postscript package transactions
Run the complete postscripts in private mount namespaces. Verify generated repository scope, native key import, missing trust and repository failures. Check keeprepo behavior and existing EL upgrade scope. Register both integration tests.
This commit is contained in:
@@ -4,5 +4,5 @@ os:Linux
|
||||
label:mn_only,ci_test,integration
|
||||
cmd:prove -I/opt/xcat/lib/perl -I/opt/xcat/lib/perl/xCAT -r /opt/xcat/share/xcat/tools/autotest/integration
|
||||
check:rc==0
|
||||
check:output=~Files=5,
|
||||
check:output=~Files=7,
|
||||
end
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin;
|
||||
use File::Path qw(make_path);
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use Test::More;
|
||||
|
||||
plan skip_all => 'requires Linux root and a private mount namespace'
|
||||
unless $^O eq 'linux' && $> == 0
|
||||
&& system('unshare', '--mount', '--propagation', 'private', '/bin/true') == 0;
|
||||
plan skip_all => 'requires the native DNF executable'
|
||||
unless -x '/usr/bin/dnf' && -d '/etc/yum.repos.d';
|
||||
|
||||
my $postscripts = File::Spec->rel2abs("$FindBin::Bin/../../xCAT/postscripts");
|
||||
unless (-x "$postscripts/otherpkgs") {
|
||||
require xCAT::TableUtils;
|
||||
$postscripts = xCAT::TableUtils->getInstallDir() . '/postscripts';
|
||||
}
|
||||
|
||||
my $dir = tempdir(CLEANUP => 1);
|
||||
make_path("$dir/bin");
|
||||
write_text("$dir/bin/dnf", <<'SH');
|
||||
#!/bin/sh
|
||||
printf '%s\n' "$*" >> "$DNF_TRACE"
|
||||
case " $* " in
|
||||
*" list "*) exit "$DNF_LIST_RC" ;;
|
||||
*" install "*) exit "$DNF_INSTALL_RC" ;;
|
||||
esac
|
||||
exit 0
|
||||
SH
|
||||
write_text("$dir/bin/rpm", <<'SH');
|
||||
#!/bin/sh
|
||||
case "$1" in
|
||||
--version) echo 'RPM version 4.18.2'; exit 0 ;;
|
||||
esac
|
||||
printf '%s\n' "$*" >> "$RPM_TRACE"
|
||||
exit 1
|
||||
SH
|
||||
for my $name (qw(logger mount)) {
|
||||
write_text("$dir/bin/$name", "#!/bin/sh\nexit 0\n");
|
||||
}
|
||||
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
|
||||
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)) {
|
||||
my ($rc, $output, $repos, $dnf, $rpm) = run_case($caller, $os, 0, 0);
|
||||
is($rc, 0, "$caller $os completes its package operation") or diag($output);
|
||||
like($dnf, qr/\binstall .*native-test/, "$caller $os reaches package installation");
|
||||
if ($os =~ /^openeuler/) {
|
||||
like($repos, qr/baseurl=http:\/\/192\.0\.2\.1:8080\/install\/\Q$os\E\/x86_64\n/,
|
||||
"$caller $os keeps the native package directory");
|
||||
unlike($repos, qr/(?:BaseOS|AppStream)/, "$caller $os does not invent EL subrepositories");
|
||||
unlike($repos, qr/gpgcheck=0|skip_if_unavailable=True/, "$caller $os requires signatures and available repositories");
|
||||
like($dnf, qr/--setopt=strict=1 --setopt=\*\.skip_if_unavailable=False --setopt=\*\.gpgcheck=1/,
|
||||
"$caller $os makes DNF reject missing packages and unavailable repositories");
|
||||
if ($caller eq 'otherpkgs') {
|
||||
like($repos, qr{gpgkey=http://192\.0\.2\.1:8080/install/otherpkgs/native/repodata/repomd\.xml\.key},
|
||||
"$caller $os uses the declared native package repository key");
|
||||
}
|
||||
} else {
|
||||
like($repos, qr/gpgcheck=0\nskip_if_unavailable=True/, "$caller preserves existing EL repository policy");
|
||||
unlike($dnf, qr/--setopt=strict/, "$caller preserves existing EL package command");
|
||||
like($repos, qr{/BaseOS\n}, "$caller preserves the EL BaseOS path");
|
||||
like($repos, qr{/AppStream\n}, "$caller preserves the EL AppStream path");
|
||||
}
|
||||
is($rpm, '', "$caller $os does not invoke direct RPM or recursive package download");
|
||||
}
|
||||
my ($rc, $output) = run_case($caller, 'openeuler24.03sp3', 0, 23);
|
||||
is($rc, 23, "$caller propagates a native DNF install failure") or diag($output);
|
||||
}
|
||||
my ($rc, $output, $repos, $dnf, $rpm) = run_case('otherpkgs', 'openeuler24.03sp3', 1, 0);
|
||||
isnt($rc, 0, 'an unresolved native package fails the complete otherpkgs postscript');
|
||||
like($output, qr/required native package native-test could not be resolved/, 'the unresolved package is identified');
|
||||
unlike($dnf, qr/\b(?:upgrade|install)\b/, 'resolution failure stops before package mutation');
|
||||
is($rpm, '', 'resolution failure does not fall back to unsigned direct RPM installation');
|
||||
|
||||
for my $empty (0, 1) {
|
||||
my ($rc, $output, $repos, $dnf, $rpm) = run_case('otherpkgs', 'openeuler24.03sp3', 0, 0, 1, $empty);
|
||||
my $label = $empty ? 'repository-only without extra packages' : 'repository-only with extra packages';
|
||||
is($rc, 0, "$label completes") or diag($output);
|
||||
like($repos, qr{baseurl=http://192\.0\.2\.1:8080/install/openeuler24\.03sp3/x86_64\n},
|
||||
"$label writes the native OS repository");
|
||||
unlike($repos, qr/gpgcheck=0|skip_if_unavailable=True/, "$label retains native repository policy");
|
||||
unlike($dnf, qr/\b(?:install|upgrade)\b/, "$label does not install or upgrade packages");
|
||||
unlike($output, qr/unary operator expected|integer expression expected/, "$label has no missing-index arithmetic error");
|
||||
is($rpm, '', "$label does not fall back to direct RPM installation");
|
||||
}
|
||||
|
||||
done_testing();
|
||||
|
||||
sub run_case {
|
||||
my ($caller, $os, $list_rc, $install_rc, $repoonly, $empty) = @_;
|
||||
my $run = tempdir(DIR => $dir, CLEANUP => 1);
|
||||
make_path("$run/repos");
|
||||
local %ENV = %ENV;
|
||||
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)} =
|
||||
('native-test', '/install/otherpkgs', 1, 'native/native-test');
|
||||
delete @ENV{qw(OTHERPKGS_INDEX OTHERPKGS1)} if $empty;
|
||||
my $pid = open(my $pipe, '-|');
|
||||
die "fork: $!" unless defined($pid);
|
||||
if (!$pid) {
|
||||
chdir($run) or die "chdir: $!";
|
||||
open(STDERR, '>&', STDOUT) or die "stderr: $!";
|
||||
exec 'unshare', '--mount', '--propagation', 'private', $wrapper,
|
||||
"$postscripts/$caller", ($repoonly ? '--repoonly' : ());
|
||||
die "exec: $!";
|
||||
}
|
||||
my $output = do { local $/; <$pipe> } // '';
|
||||
close($pipe);
|
||||
my $rc = $? >> 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") : '');
|
||||
}
|
||||
|
||||
sub read_text {
|
||||
my ($path) = @_;
|
||||
open(my $file, '<', $path) or die "read $path: $!";
|
||||
return do { local $/; <$file> };
|
||||
}
|
||||
|
||||
sub write_text {
|
||||
my ($path, $text) = @_;
|
||||
open(my $file, '>', $path) or die "write $path: $!";
|
||||
print {$file} $text or die "write $path: $!";
|
||||
close($file) or die "close $path: $!";
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use FindBin;
|
||||
use File::Path qw(make_path);
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use JSON::PP qw(decode_json encode_json);
|
||||
use Test::More;
|
||||
|
||||
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)) {
|
||||
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'
|
||||
unless -f '/etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler';
|
||||
|
||||
my $postscripts = File::Spec->rel2abs("$FindBin::Bin/../../xCAT/postscripts");
|
||||
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 <repo-test@example.invalid>', '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
|
||||
Release: 1
|
||||
Summary: Isolated package repository fixture
|
||||
License: MIT
|
||||
BuildArch: noarch
|
||||
AutoReqProv: no
|
||||
%s
|
||||
%%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
|
||||
write_text("$dir/bin/mount", <<'SH');
|
||||
#!/bin/sh
|
||||
printf '%s on %s type nfs\n' "$NFSSERVER" "$INSTALLDIR"
|
||||
SH
|
||||
write_text("$dir/bin/rpm", <<'SH');
|
||||
#!/bin/sh
|
||||
case "$1" in --version) exec /usr/bin/rpm "$@" ;; esac
|
||||
printf '%s\n' "$*" >> "$RPM_TRACE"
|
||||
exit 79
|
||||
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
|
||||
chmod 0755, glob("$dir/bin/*"), "$dir/run";
|
||||
|
||||
my $os = run_case('ospkgs');
|
||||
is($os->{rc}, 0, 'native ospkgs succeeds with an unreachable enabled publisher repository') or diag($os->{output});
|
||||
is($os->{packages}{'oe-scope-os'}, '2', 'native ospkgs installs from the generated OS repository');
|
||||
is($os->{keys}, 1, 'DNF imports the configured OS key into the initially empty RPM database');
|
||||
ok($os->{vendor_preserved}, 'the publisher repository file is preserved byte for byte');
|
||||
strict_transactions($os, 'ospkgs');
|
||||
|
||||
my $keep = run_case('ospkgs', keeprepo => 1);
|
||||
isnt($keep->{rc}, 0, 'native keeprepo retains the unavailable publisher repository failure');
|
||||
ok(!exists $keep->{packages}{'oe-scope-os'}, 'keeprepo failure does not install the requested package');
|
||||
ok($keep->{vendor_preserved}, 'keeprepo preserves the publisher repository file');
|
||||
my $keep_available = run_case('ospkgs', keeprepo => 1, vendor_available => 1);
|
||||
is($keep_available->{rc}, 0, 'native keeprepo can use an available publisher repository') or diag($keep_available->{output});
|
||||
is($keep_available->{packages}{'oe-scope-os'}, '3', 'keeprepo permits the newer publisher package');
|
||||
|
||||
my $other = run_case('otherpkgs', seed_old => 1);
|
||||
is($other->{rc}, 0, 'native otherpkgs completes with an unreachable publisher repository') or diag($other->{output});
|
||||
is($other->{packages}{'oe-scope-extra'}, '1', 'otherpkgs installs the requested extra package');
|
||||
is($other->{packages}{'oe-scope-dependency'}, '1', 'the actual DNF solver installs its required OS dependency');
|
||||
is($other->{packages}{'oe-scope-os'}, '1', 'otherpkgs upgrade does not apply unrelated generated OS updates');
|
||||
ok($other->{vendor_preserved}, 'otherpkgs preserves the publisher repository file');
|
||||
strict_transactions($other, 'otherpkgs');
|
||||
|
||||
for my $case (['ospkgs', 'xCAT-openeuler*-path*.repo'],
|
||||
['otherpkgs', 'xCAT-openeuler*-path*.repo'],
|
||||
['otherpkgs', 'xCAT-otherpkgs*.repo']) {
|
||||
my ($caller, $pattern) = @$case;
|
||||
my $missing = run_case($caller, remove_repo => $pattern, vendor_available => 1);
|
||||
isnt($missing->{rc}, 0, "$caller rejects missing generated repository $pattern");
|
||||
is(scalar keys %{$missing->{packages}}, 0, "$caller does not substitute publisher packages for $pattern");
|
||||
is($missing->{rpm}, '', "$caller does not bypass DNF after missing $pattern");
|
||||
}
|
||||
my $key_missing = run_case('ospkgs', empty_key => 1);
|
||||
isnt($key_missing->{rc}, 0, 'an unusable declared OS key rejects the signed package');
|
||||
is(scalar keys %{$key_missing->{packages}}, 0, 'missing trust does not disable package signature verification');
|
||||
is($key_missing->{keys}, 0, 'an unusable key does not import another trust source');
|
||||
|
||||
for my $caller (qw(ospkgs otherpkgs)) {
|
||||
my $legacy = run_case($caller, osver => 'rhels9.6', vendor_available => 1);
|
||||
is($legacy->{rc}, 0, "$caller retains existing EL package behavior") or diag($legacy->{output});
|
||||
if ($caller eq 'ospkgs') {
|
||||
is($legacy->{packages}{'oe-scope-os'}, '3', 'EL ospkgs retains its publisher repository selection');
|
||||
} else {
|
||||
is($legacy->{packages}{'oe-scope-dependency'}, '1', 'EL otherpkgs retains distribution dependency resolution');
|
||||
}
|
||||
ok(!grep({ grep { /^--setopt=strict=/ } @$_ } @{$legacy->{dnf}}), "$caller does not impose the native strict wrapper on EL");
|
||||
ok($legacy->{vendor_preserved}, "$caller retains the legacy publisher-file behavior");
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
my $rpm = $ENV{XCAT_NATIVE_KEY_RPM};
|
||||
skip 'set XCAT_NATIVE_KEY_RPM to a verified native openEuler-gpg-keys RPM for publisher-key import', 3
|
||||
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: $!";
|
||||
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);
|
||||
is($native->{rc}, 0, 'DNF installs the real native signed package using the packaged openEuler key') or diag($native->{output});
|
||||
is($native->{keys}, 1, 'the empty RPM database imports only the declared native key');
|
||||
}
|
||||
done_testing();
|
||||
|
||||
sub strict_transactions {
|
||||
my ($run, $caller) = @_;
|
||||
my @transactions = grep { grep { /^(?:list|install|upgrade|remove)$/ } @$_ } @{$run->{dnf}};
|
||||
ok(@transactions > 0, "$caller invokes the real package manager");
|
||||
for my $flag ('--setopt=strict=1', '--setopt=*.gpgcheck=1', '--setopt=*.skip_if_unavailable=False') {
|
||||
ok(!grep({ my $args = $_; !grep { $_ eq $flag } @$args } @transactions), "$caller retains $flag for every transaction");
|
||||
}
|
||||
is($run->{rpm}, '', "$caller avoids direct RPM fallback");
|
||||
}
|
||||
|
||||
sub run_case {
|
||||
my ($caller, %options) = @_;
|
||||
my $run = tempdir(DIR => $dir, CLEANUP => 1);
|
||||
make_path("$run/repos", "$run/root", "$run/media");
|
||||
my $key = $options{native_key} ? '/etc/pki/rpm-gpg/RPM-GPG-KEY-openEuler' : "$dir/key.asc";
|
||||
if ($options{empty_key}) {
|
||||
write_text("$run/empty-key", '');
|
||||
$key = "$run/empty-key";
|
||||
}
|
||||
my $vendor = '[publisher]' . "\nname=publisher\nenabled=1\nskip_if_unavailable=False\ngpgcheck=1\ngpgkey=file://$dir/key.asc\nbaseurl=file://"
|
||||
. ($options{vendor_available} ? "$dir/fixtures/vendor" : "$run/unavailable") . "\n";
|
||||
write_text("$run/repos/openEuler.repo", $vendor);
|
||||
if ($options{seed_old}) {
|
||||
my ($rc, $output) = capture('/usr/bin/rpm', '--root', "$run/root", '--import', "$dir/key.asc");
|
||||
die $output if $rc;
|
||||
($rc, $output) = capture('/usr/bin/rpm', '--root', "$run/root", '-i', "$dir/fixtures/old/oe-scope-os-1-1.noarch.rpm");
|
||||
die $output if $rc;
|
||||
}
|
||||
local %ENV = %ENV;
|
||||
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} // '');
|
||||
@ENV{qw(OSVER ARCH MASTER NFSSERVER HTTPPORT INSTALLDIR UPDATENODE)} =
|
||||
($options{osver} // 'openeuler24.03sp3', 'x86_64', '192.0.2.1', '192.0.2.1', 8080, "$run/media", 1);
|
||||
@ENV{qw(OSPKGDIR OSPKGS OTHERPKGDIR OTHERPKGS_INDEX OTHERPKGS1)} =
|
||||
($options{osrepo} // "$dir/fixtures/os", $options{package} // 'oe-scope-os', "$dir/fixtures/other", 1, 'native/oe-scope-extra');
|
||||
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');
|
||||
my (%packages, $keys);
|
||||
$keys = 0;
|
||||
for my $line (split /\n/, $packages) {
|
||||
next unless $line =~ /^(\S+) (\S+)$/;
|
||||
if ($1 eq 'gpg-pubkey') { $keys++ } else { $packages{$1} = $2 }
|
||||
}
|
||||
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 capture {
|
||||
my (@command) = @_;
|
||||
my $pid = open(my $pipe, '-|');
|
||||
die "fork: $!" unless defined($pid);
|
||||
if (!$pid) {
|
||||
open(STDERR, '>&', STDOUT) or die "stderr: $!";
|
||||
exec @command;
|
||||
die "exec: $!";
|
||||
}
|
||||
my $output = do { local $/; <$pipe> } // '';
|
||||
close($pipe);
|
||||
return ($? >> 8, $output);
|
||||
}
|
||||
|
||||
sub read_text {
|
||||
my ($path) = @_;
|
||||
return '' unless -f $path;
|
||||
open(my $file, '<', $path) or die "read $path: $!";
|
||||
return do { local $/; <$file> };
|
||||
}
|
||||
|
||||
sub write_text {
|
||||
my ($path, $text) = @_;
|
||||
open(my $file, '>', $path) or die "write $path: $!";
|
||||
print {$file} $text or die "write $path: $!";
|
||||
close($file) or die "close $path: $!";
|
||||
}
|
||||
Reference in New Issue
Block a user