2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 15:18:19 +00:00

test(xcat-core): mock cannot read the sources staged under /root

buildrpms.pl stages each build's sources under $HOME/rpmbuild/sources.
mock now builds as the chrootuid of the target, uid 1000 for openEuler
24.03, and HOME of the Jenkins agents is /root, mode 0550. mock --sources
then fails with "Permission denied" on the staging directory.

The unit test asserts that the base directory is under the dist tree of
the checkout and not under $HOME, and that the staging tree is readable
by other users when the umask is 077. A native test, run as root in the
Fedora container, reads the staged files as uid 1000.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-10-08 13:13:34 -03:00
parent 32eb096bfe
commit e5137981ff
5 changed files with 93 additions and 7 deletions
+2 -1
View File
@@ -28,7 +28,8 @@ jobs:
set -o pipefail
docker run --rm -v "$PWD:/src:ro" -w /src fedora:42 bash -ec '
dnf -y -q install mock perl perl-File-Slurper >/dev/null
prove -v xCAT-test/native/mock_resultdir_owner.t' 2>&1 | tee mock.tap
prove -v xCAT-test/native/mock_resultdir_owner.t \
xCAT-test/native/build_sources_other_uid.t' 2>&1 | tee mock.tap
if grep -iE '# skip' mock.tap; then
echo 'a mock result directory test was skipped' >&2
exit 1
+4 -2
View File
@@ -39,10 +39,12 @@ sudo prove xCAT-test/native/ip_forwarding.t \
```
The mock result directory case needs the mock Python library, and root for its
ownership checks. The `xcat_test` workflow runs it as root in a Fedora container:
ownership checks. The staged sources case needs root to read the sources as uid
1000. The `xcat_test` workflow runs both as root in a Fedora container:
```
sudo prove xCAT-test/native/mock_resultdir_owner.t
sudo prove xCAT-test/native/mock_resultdir_owner.t \
xCAT-test/native/build_sources_other_uid.t
```
The PostgreSQL case starts a private server with only a Unix socket. Run it as
@@ -0,0 +1,50 @@
#!/usr/bin/env perl
# mock reads the staged sources as the uid of the target's chrootuid while buildrpms.pl
# runs as root. A process with another uid must read them. CI runs this file as root.
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../build-utils/lib";
use File::Path qw(make_path);
use File::Slurper qw(read_text write_text);
use File::Temp qw(tempdir);
use POSIX ();
use Test::More;
use XCAT::BuildUtils qw(build_sources_base prepare_build_sources_dir share_build_sources_dir);
plan skip_all => 'requires root to switch to another uid' if $> != 0;
# Flush before the fork below, or the child repeats this file's TAP output on exit.
$| = 1;
my $checkout = tempdir(CLEANUP => 1);
chmod 0755, $checkout or die "chmod $checkout: $!\n";
# A root $HOME the build uid cannot enter, as on the build hosts.
make_path("$checkout/root");
chmod 0550, "$checkout/root" or die "chmod: $!\n";
local $ENV{HOME} = "$checkout/root";
my $umask = umask 077;
my $dir = prepare_build_sources_dir(build_sources_base($checkout), $$, 'hosta');
make_path("$dir/sub");
write_text("$dir/xCAT-2.20.0.tar.gz", "payload\n");
write_text("$dir/sub/xcat.conf", "conf\n");
share_build_sources_dir($dir);
umask $umask;
my $pid = fork // die "fork: $!\n";
if ($pid == 0) {
POSIX::setgid(1000);
POSIX::setuid(1000);
POSIX::_exit(2) if $> != 1000;
my $ok = eval {
opendir(my $dh, $dir) or die;
read_text("$dir/xCAT-2.20.0.tar.gz") eq "payload\n"
&& read_text("$dir/sub/xcat.conf") eq "conf\n";
};
POSIX::_exit($ok ? 0 : 1);
}
waitpid($pid, 0);
is($? >> 8, 0, 'uid 1000 lists the staging directory and reads every staged file');
done_testing;
+3 -2
View File
@@ -51,6 +51,7 @@ our @STAGES;
sub createmockconfig { push @main::STAGES, 'createmockconfig'; }
sub prepare_mock_resultdirs { push @main::STAGES, 'prepare_mock_resultdirs'; }
sub buildsources { push @main::STAGES, 'buildsources'; }
sub share_build_sources_dir { push @main::STAGES, 'share_build_sources_dir'; }
sub buildspkgs { push @main::STAGES, 'buildspkgs'; }
sub buildpkgs { push @main::STAGES, 'buildpkgs'; }
# buildall assigns a staging directory per build process.
@@ -129,11 +130,11 @@ sub stages_for {
}
is_deeply( stages_for(0),
[qw(createmockconfig prepare_mock_resultdirs buildsources buildspkgs buildpkgs)],
[qw(createmockconfig prepare_mock_resultdirs buildsources share_build_sources_dir buildspkgs buildpkgs)],
'a normal run builds the source rpm and then rebuilds it into binaries' );
is_deeply( stages_for(1),
[qw(createmockconfig prepare_mock_resultdirs buildsources buildspkgs)],
[qw(createmockconfig prepare_mock_resultdirs buildsources share_build_sources_dir buildspkgs)],
'a source-only run stops once the source rpm exists' );
ok( !grep( { $_ eq 'buildpkgs' } @{ stages_for(1) } ),
+34 -2
View File
@@ -4,6 +4,7 @@
use strict;
use warnings;
use Cwd ();
use File::Path qw(make_path);
use File::Slurper qw(write_text);
use File::Temp qw(tempdir);
@@ -12,11 +13,23 @@ use lib "$FindBin::Bin/../../build-utils/lib";
use Test::More;
use XCAT::BuildUtils qw(build_sources_base build_sources_dir prepare_build_sources_dir
remove_build_sources_dir sweep_build_sources_dirs);
remove_build_sources_dir sweep_build_sources_dirs share_build_sources_dir);
# mock reads the staged sources as the uid of the target's chrootuid, and $HOME of a
# root build is /root, mode 0550. The base belongs to the checkout, not to $HOME.
my $home = tempdir(CLEANUP => 1);
local $ENV{HOME} = "$home/home";
my $base = build_sources_base($home);
is($base, "$home/rpmbuild/sources", 'the base directory is under the rpmbuild tree');
is($base, "$home/dist/sources", 'the base directory is under the dist tree of the checkout');
unlike($base, qr{^\Q$ENV{HOME}\E/}, 'the base directory is not under $HOME');
{
my $cwd = Cwd::getcwd();
chdir $home or die "chdir $home: $!\n";
my $default = build_sources_base();
chdir $cwd or die "chdir $cwd: $!\n";
is($default, Cwd::abs_path($home) . "/dist/sources",
'the base directory defaults to the dist tree of the working directory');
}
# Two live processes on one host never have the same pid. The host name keeps two
# hosts apart when $HOME is on NFS.
@@ -89,4 +102,23 @@ ok(!-e $reaped, 'the default check deletes the directory of a reaped process');
is_deeply([sweep_build_sources_dirs("$home/absent", 'hosta', $is_alive)], [],
'a missing base directory is not an error');
# Another uid must traverse every directory and read every file, whatever the umask.
{
my $root = tempdir(CLEANUP => 1);
my $umask = umask 077;
my $sbase = build_sources_base($root);
my $dir = prepare_build_sources_dir($sbase, 401, 'hosta');
make_path("$dir/sub");
write_text("$dir/xCAT-2.20.0.tar.gz", "payload\n");
write_text("$dir/sub/xcat.conf", "conf\n");
share_build_sources_dir($dir);
umask $umask;
for my $d ("$root/dist", $sbase, $dir, "$dir/sub") {
is((stat $d)[2] & 0055, 0055, "other users can list and enter $d");
}
for my $f ("$dir/xCAT-2.20.0.tar.gz", "$dir/sub/xcat.conf") {
is((stat $f)[2] & 0044, 0044, "other users can read $f");
}
}
done_testing;