2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 07:08:12 +00:00

fix(xcat-core): the mock result directory owner ignores include() and stale logs

mock_build_owner parsed chrootuid and chrootgid from the text of the
rendered configuration. A value set in an included file, an assignment
with an inline comment or a double-quoted key, and an include after an
assignment all gave a uid that mock does not build as. Also, a root
build that stopped part way left root-owned 0644 logs in the result
directories, and the next build, as uid 1000, could not append to them.

mock_build_owner now asks mock's own loader, mockbuild.config.load_config,
for chrootuid and chrootgid of the chroot that buildrpms.pl passes to
mock -r. It fails with a clear message when mock is not installed.
prepare_mock_resultdirs moves to XCAT::BuildUtils. It also gives the
files already in both result directories to that owner, so a retry can
append to the old logs and replace a package of the same version. When
mock builds as the caller, nothing changes owner.

native/mock_resultdir_owner.t gave the wrong uid for four of its
configuration cases, and failed the 11 retry checks, before this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-10-08 11:31:07 -03:00
parent 926c076533
commit 32eb096bfe
2 changed files with 69 additions and 35 deletions
+65 -20
View File
@@ -42,7 +42,7 @@ our @EXPORT_OK = qw(
buildinfo_text
targetarch_from_target
openeuler_build_target openeuler_repo_subdir
mock_config_text mock_build_owner
mock_config_text mock_build_owner prepare_mock_resultdirs
genesis_chroot_name genesis_target_arch genesis_build_plan
genesis_log_errors genesis_log_deny_rules deb_belongs_to_dist
genesis_dists genesis_dist_reason
@@ -1160,32 +1160,77 @@ sub mock_config_text {
=head3 mock_build_owner
Descriptions: Report the uid and gid mock creates its --resultdir as.
mock creates the result directory and its logs as chrootuid, and buildrpms.pl
passes a relative one under a tree it owns as root. The caller gives the
directory to this owner before the build starts.
No mock template declares chrootuid or chrootgid (56 on the ppc64le builder,
none), so the target configuration carries the whole answer.
Descriptions: Report the uid and gid mock builds as, from mock's own
configuration loader. The loader resolves include() and
the assignments around it as a build does.
Arguments:
$text - the rendered mock configuration
$chroot - the name passed to mock -r
$configdir - the mock configuration directory; mock's default when undef
Returns: ($uid, $gid), each undef when the configuration declares none. mock
then builds as the calling user, and the directory needs no change.
Returns: ($uid, $gid). With no chrootuid, $uid is the caller, as in mock.
$gid is undef when the loader reports none.
=cut
#-------------------------------------------------------------------------------
my $MOCK_OWNER_LOADER = <<'PYTHON';
import sys
try:
from mockbuild.config import MOCKCONFDIR, load_config
except ImportError:
sys.exit('mock is not installed: cannot import mockbuild.config')
opts = load_config(sys.argv[2] or MOCKCONFDIR, sys.argv[1])
print(opts['chrootuid'], opts.get('chrootgid', ''))
PYTHON
sub mock_build_owner {
my ($text) = @_;
my ($uid, $gid);
# mock takes the last assignment.
for my $line (split /\n/, $text) {
$uid = $1 if $line =~ /\Aconfig_opts\['chrootuid'\]\s*=\s*(\d+)\s*\z/;
$gid = $1 if $line =~ /\Aconfig_opts\['chrootgid'\]\s*=\s*(\d+)\s*\z/;
}
return ($uid, $gid);
my ($chroot, $configdir) = @_;
open my $pipe, '-|', 'python3', '-c', $MOCK_OWNER_LOADER, $chroot, $configdir // ''
or die "Cannot run python3 to load the mock configuration $chroot: $!\n";
my $out = do { local $/; <$pipe> } // '';
close $pipe or die "mock cannot load the configuration $chroot\n";
my ($uid, $gid) = $out =~ /\A(\d+) (\d*)\n\z/
or die "mock gives no chrootuid for $chroot\n";
return ($uid, length $gid ? $gid : undef);
}
#-------------------------------------------------------------------------------
=head3 prepare_mock_resultdirs
Descriptions: Give the --resultdir directories, and the files a previous
build left in them, to the uid and gid mock builds as.
mock writes its logs and packages there as chrootuid. A
root build that stopped part way leaves root-owned 0644
files that a non-root build cannot append to or replace.
Arguments:
$chroot - the name passed to mock -r
$configdir - the mock configuration directory; mock's default when undef
@dirs - the result directories, created when missing
Returns: nothing. Dies when a chown fails.
=cut
#-------------------------------------------------------------------------------
sub prepare_mock_resultdirs {
my ($chroot, $configdir, @dirs) = @_;
my ($uid, $gid) = mock_build_owner($chroot, $configdir);
make_path(@dirs);
return if $uid == $>;
$gid //= -1;
for my $dir (@dirs) {
opendir(my $dh, $dir) or die "Cannot read $dir: $!\n";
my @files = grep { -f $_ && !-l $_ } map { "$dir/$_" } readdir $dh;
closedir $dh;
for my $path ($dir, @files) {
chown($uid, $gid, $path) == 1
or die "FATAL: cannot give $path to uid $uid: $!\n";
}
# mock writes the packages as chrootuid:chrootgid.
chmod 0775, $dir;
}
}
+4 -15
View File
@@ -60,7 +60,7 @@ use XCAT::BuildUtils qw(git_revision source_date_epoch sh sh_or_die usage buildi
stage_genesis_base_sources
write_script read_line targetarch_from_target
openeuler_build_target openeuler_repo_subdir
mock_config_text mock_build_owner);
mock_config_text);
use POSIX (); # EEXIST, for the directory build lock (see main())
use Getopt::Long qw(GetOptions);
use POSIX qw(strftime);
@@ -330,23 +330,12 @@ sub createmockconfig {
mock_config_text($pkg, "$target$ext", read_text("/etc/mock/$target.cfg"), $SOURCE_DATE_EPOCH));
}
# mock creates --resultdir and the logs under it as chrootuid, and the two mock calls below
# pass a relative directory under dist/, which this script owns as root. Give the directory
# to the build uid before mock runs, so the build does not need to be root to write its own
# results. openeuler-24.03-ppc64le is the only target that declares a uid; everywhere else
# mock builds as the caller and there is nothing to change.
# mock writes --resultdir as chrootuid, and this script runs as root.
sub prepare_mock_resultdirs {
my ($pkg, $target) = @_;
my $ext = $opts{mock_uniqueext} ? "-$opts{mock_uniqueext}" : "";
my ($uid, $gid) = mock_build_owner(read_text("/etc/mock/$pkg-$target$ext.cfg"));
return unless defined $uid || defined $gid;
for my $dir ("dist/$target/rpms", "dist/$target/rpms/SRPMS") {
make_path($dir);
chown(defined $uid ? $uid : -1, defined $gid ? $gid : -1, $dir) == 1
or die "FATAL: cannot give $dir to the build uid: $!\n";
# The group needs the write bit: mock writes the rpms as chrootuid:chrootgid.
chmod 0775, $dir;
}
XCAT::BuildUtils::prepare_mock_resultdirs("$pkg-$target$ext", undef,
"dist/$target/rpms", "dist/$target/rpms/SRPMS");
}
sub buildsources_genesis_base($) {