2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-09-12 12:36:23 +00:00

docs(build): correct scrub lock semantics; bound genesis scrub; skip re-exec in build-free modes

Review follow-ups (all low-impact):
- The genesis pre-build scrub and scrub_buildroot comments claimed mock 'refuses
  and we skip' on a held buildroot lock; mock actually BLOCKS until release. Correct
  the wording and wrap the genesis scrub in 'timeout 300' so even a pathological
  stale-lock wait can never hang the build (the CD topology never runs a second
  same-target build concurrently, so it is belt-and-suspenders).
- Drop the inaccurate 'no re-bootstrap' note: --scrub=bootstrap intentionally forces
  a re-bootstrap from the root cache (that bootstrap is the leak).
- The mount-namespace re-exec ran for every invocation, printing a spurious non-root
  warning for the documented no-root, build-free modes (--verify-repo,
  --finalize-xcat-dep). Skip the re-exec for those (no mock, no cgroup exposure).
- Soften the abs_path($0) comment: it resolves a relative $0 against cwd but does
  not search $PATH.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
Daniel Hilst
2026-08-13 11:07:54 -03:00
parent dcd193a4f9
commit 06ffc65944
+18 -9
View File
@@ -31,7 +31,10 @@ use MockBuildUtils qw(sh_quote print_step version_matches required_pkgs
# mount mock leaks when we exit, so an aborted build can no longer leave corpse mounts under
# /var/lib/mock. Best-effort: only as root (needs CAP_SYS_ADMIN) and only if `unshare` exists;
# otherwise warn loudly and continue unisolated. MOCKBUILD_ALL_MOUNTNS guards against a re-exec loop.
unless ($ENV{MOCKBUILD_ALL_MOUNTNS}) {
# Build-free modes (--verify-repo, --finalize-xcat-dep) run no mock and are documented no-root, so they
# skip the re-exec entirely -- no cgroup exposure, and no spurious non-root warning.
my $mountns_build_free = grep { /^--(?:verify-repo|finalize-xcat-dep)(?:=|$)/ } @ARGV;
unless ($ENV{MOCKBUILD_ALL_MOUNTNS} || $mountns_build_free) {
if ($> != 0) {
warn "WARN: not root -- skipping mount-namespace isolation (host-cgroup propagation guard); "
. "run as root in CI so mock chroot teardown cannot unmount the host /sys/fs/cgroup\n";
@@ -40,8 +43,9 @@ unless ($ENV{MOCKBUILD_ALL_MOUNTNS}) {
. "may unmount the host /sys/fs/cgroup on a shared-propagation host\n";
} else {
$ENV{MOCKBUILD_ALL_MOUNTNS} = 1;
# Absolute path to self so the re-exec'd perl finds the script regardless of cwd or a bare
# $PATH invocation (unshare does not change cwd, but $0 may be relative or a bare name).
# Absolute path to self (resolved against cwd, which unshare preserves) so the re-exec'd perl
# finds a relative $0. A bare $PATH-only $0 isn't resolved (abs_path doesn't search $PATH), but
# a shell invocation yields a full $0 there anyway.
my $self = abs_path($0) // $0;
my @reexec = ('unshare', '--mount', '--propagation', 'slave', '--', $^X, $self, @ARGV);
exec { $reexec[0] } @reexec;
@@ -558,10 +562,14 @@ if (!$skip_build) {
# The genesis chroot (xCAT-genesis-base-<target>) is SHARED across runs -- buildrpms.pl builds it
# without a per-run --mock-uniqueext. mock's post-build scrub only runs on SUCCESS, so a killed
# or failFast-interrupted prior run leaves the chroot stunted (missing /bin/sh), and mock REUSES
# the corpse on the next run -> "FileNotFoundError: '/bin/sh'". Scrub it FIRST (lock-safe -- mock
# refuses if a concurrent build holds it -- and best-effort: a no-op on the first run before the
# config exists) so mock recreates the chroot fresh from the cached root (fast, no re-bootstrap).
my $genesis_scrub = "{ mock -r " . sh_quote("xCAT-genesis-base-$target")
# the corpse on the next run -> "FileNotFoundError: '/bin/sh'". Scrub it FIRST (best-effort: a
# no-op on the first run before the config exists) so mock recreates the chroot from the cached
# root; --scrub=bootstrap goes too (the per-uniqueext bootstrap is the leak), so the fresh chroot
# re-bootstraps from the root cache. mock takes the buildroot lock for --scrub and BLOCKS (not
# skips) if a concurrent build holds it, but within a run the scrub is sequential before the
# build and the CD topology never runs a second same-target build at once; `timeout` bounds even
# a pathological wait so a stale lock can never hang the build.
my $genesis_scrub = "{ timeout 300 mock -r " . sh_quote("xCAT-genesis-base-$target")
. " --scrub=chroot --scrub=bootstrap >/dev/null 2>&1 || true; }";
my $cmd = "mkdir -p $mktree && $genesis_scrub && HOME=" . sh_quote($genesis_home) . ' ' . join(' ',
'perl', sh_quote("$xcat_src/buildrpms.pl"),
@@ -1049,8 +1057,9 @@ sub run_step {
}
}
# Scrub a single mock buildroot via mock's own lock-safe --scrub. Never rm: if a concurrent build
# still holds the chroot lock, mock refuses and we skip it. Failures (already scrubbed, locked, or
# Scrub a single mock buildroot via mock's own --scrub (never rm). mock takes the buildroot lock for
# the scrub, so a concurrent build holding it makes mock BLOCK until release rather than corrupt a live
# chroot (this can never rm a chroot out from under a running build). Failures (already scrubbed or
# config missing) are tolerated -- a cleanup hiccup must never fail the build. Scrubs both the
# build chroot and its per-uniqueext bootstrap chroot (each build step gets its own bootstrap, so
# both must go or /var/lib/mock still leaks). The shared root cache under /var/cache/mock is kept,