mirror of
https://github.com/xcat2/xcat-dep.git
synced 2026-09-12 04:26:25 +00:00
fix(xcat-dep): harden finalize peer requirement + rpm version/identity edges
Follow-up self-review hardening on top of the PR #62 review response: - finalize_xcat_dep now treats a missing ppc64le PEER repo (not just missing genesis rpms) as fatal instead of silently skipping the OS -- in the CD both arches build every EL, so a missing peer is an incomplete input that would otherwise leave the x86_64 repo without the ppc64 genesis and still exit 0. - cross_copy_genesis treats an empty SIGMD5 (unreadable rpm) as "cannot confirm identical" and refreshes, rather than risking a false up-to-date match when two unreadable rpms both return an empty digest. - rpm_version fails when a directory holds more than one distinct version of a package (a stale artifact not cleaned before the build) instead of silently returning the first sorted match, which a version pin could pass against while the stale rpm still ships. Both arches share a Version for genesis, so a normal x86_64+ppc64 pair is a single entry. t/mockbuild-all.t: +4 cases (30 total) -- missing-peer fatal, rpm_sigmd5 on a missing rpm, and rpm_version multi-version failure. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
This commit is contained in:
+19
-7
@@ -84,6 +84,7 @@ sub rpm_version {
|
||||
my $glob = ($name eq 'xCAT-genesis-base')
|
||||
? "$dir/xCAT-genesis-base-*.rpm"
|
||||
: "$dir/${name}-*.rpm";
|
||||
my %vers; # distinct %{version}s of the matching binary rpms
|
||||
for my $f (sort glob($glob)) {
|
||||
next if $f =~ /\.src\.rpm$/ || $f =~ /-debug(?:info|source)-/;
|
||||
my $n = `rpm -qp --qf '%{name}' ${\ sh_quote($f)} 2>/dev/null`;
|
||||
@@ -92,9 +93,16 @@ sub rpm_version {
|
||||
next unless $match;
|
||||
my $v = `rpm -qp --qf '%{version}' ${\ sh_quote($f)} 2>/dev/null`;
|
||||
chomp $v;
|
||||
return $v;
|
||||
$vers{$v} = 1 if $v ne '';
|
||||
}
|
||||
return undef;
|
||||
return undef unless %vers;
|
||||
# More than one distinct version present means a stale artifact was not cleaned before the
|
||||
# build -- a version pin could then pass against the wrong rpm and both could be shipped.
|
||||
# (For genesis both arches share the same Version, so a normal x86_64+ppc64 pair is one entry.)
|
||||
die "Multiple versions of $name present in $dir: " . join(', ', sort keys %vers)
|
||||
. " (stale artifact not cleaned before the build)\n" if keys(%vers) > 1;
|
||||
my ($v) = keys %vers;
|
||||
return $v;
|
||||
}
|
||||
|
||||
# read_manifest: parse packages-manifest.conf into %{ target => { package => version|'*' } }.
|
||||
@@ -137,7 +145,10 @@ sub cross_copy_genesis {
|
||||
my $up_to_date = 1;
|
||||
for my $base (keys %want) {
|
||||
my $dst = "$to/$base";
|
||||
if (!-f $dst || rpm_sigmd5($want{$base}) ne rpm_sigmd5($dst)) { $up_to_date = 0; last; }
|
||||
my $src_sig = rpm_sigmd5($want{$base});
|
||||
# An empty SIGMD5 (unreadable rpm) means "cannot confirm identical" -> refresh rather
|
||||
# than risk skipping on a false match (two '' would otherwise compare equal).
|
||||
if (!-f $dst || $src_sig eq '' || $src_sig ne rpm_sigmd5($dst)) { $up_to_date = 0; last; }
|
||||
}
|
||||
return 0 if $up_to_date;
|
||||
}
|
||||
@@ -175,10 +186,11 @@ sub finalize_xcat_dep {
|
||||
my $osdir = basename($p);
|
||||
my $x86dir = "$x86_64_repo/$osdir/x86_64";
|
||||
my $ppcdir = "$ppc64le_repo/$osdir/ppc64le";
|
||||
if (!-d $ppcdir) {
|
||||
print "[finalize] $osdir: no ppc64le peer at $ppcdir -- skipping\n";
|
||||
next;
|
||||
}
|
||||
# Require the peer repo itself: in the CD both arches build every EL, so a missing
|
||||
# ppc64le peer for an x86_64 OS means an incomplete input, not something to skip past
|
||||
# (skipping would leave that OS's x86_64 repo without the ppc64 genesis and still exit 0).
|
||||
die "FATAL: [finalize] $osdir: no ppc64le peer repo at $ppcdir\n"
|
||||
. " (both arches must build every EL before finalize)\n" if !-d $ppcdir;
|
||||
# Require the expected inputs: each arch's build must have produced its OWN genesis rpm
|
||||
# before finalize cross-populates them. Without this, a pair whose builds produced no
|
||||
# genesis rpms would make finalize a silent no-op that still exits 0 (the bug this guards).
|
||||
|
||||
+23
-4
@@ -10,7 +10,7 @@ use lib "$RealBin/..";
|
||||
use File::Temp qw(tempdir);
|
||||
use File::Path qw(make_path);
|
||||
use File::Basename qw(basename);
|
||||
use MockBuildUtils qw(required_pkgs version_matches rpm_sigmd5
|
||||
use MockBuildUtils qw(required_pkgs version_matches rpm_sigmd5 rpm_version
|
||||
cross_copy_genesis finalize_xcat_dep read_manifest);
|
||||
|
||||
# Run a printing sub with STDOUT muted so its progress lines do not pollute TAP.
|
||||
@@ -64,19 +64,24 @@ ok( version_matches('anything', '*'), "'*' matches any version");
|
||||
is_deeply({read_manifest("$dir/nope.conf")}, {}, 'read_manifest: missing file -> empty');
|
||||
}
|
||||
|
||||
# rpm_sigmd5 on a missing/unreadable rpm returns '' (so cross_copy treats it as "not identical").
|
||||
is(rpm_sigmd5('/nonexistent/xCAT-genesis-base-ppc64-9.9.9.noarch.rpm'), '',
|
||||
'rpm_sigmd5 returns empty for a missing rpm');
|
||||
|
||||
# ---- RPM-identity comparison + cross_copy_genesis (needs rpmbuild for real rpms) --------------
|
||||
SKIP: {
|
||||
skip 'rpmbuild not available', 5 if system('command -v rpmbuild >/dev/null 2>&1') != 0;
|
||||
skip 'rpmbuild not available', 6 if system('command -v rpmbuild >/dev/null 2>&1') != 0;
|
||||
my $tmp = tempdir(CLEANUP => 1);
|
||||
my $seq = 0;
|
||||
my $mk = sub { # build a genesis-named rpm with a given marker payload
|
||||
my ($tarch, $content) = @_;
|
||||
my ($tarch, $content, $version) = @_;
|
||||
$version ||= '2.19.0';
|
||||
my $out = "$tmp/out" . (++$seq); # unique dir: same NVR would overwrite in a shared one
|
||||
my $spec = "$tmp/$tarch-$seq.spec";
|
||||
open my $fh, '>', $spec or die;
|
||||
print $fh <<"SPEC";
|
||||
Name: xCAT-genesis-base-$tarch
|
||||
Version: 2.19.0
|
||||
Version: $version
|
||||
Release: snapTEST
|
||||
Summary: test fixture
|
||||
License: EPL
|
||||
@@ -122,6 +127,13 @@ SPEC
|
||||
my @signed;
|
||||
quiet { cross_copy_genesis($from2, $to2, 'ppc64', sub { push @signed, $_[0] }) };
|
||||
is_deeply(\@signed, ["$to2/$base"], 'the sign callback runs on each copied rpm');
|
||||
|
||||
# rpm_version dies when a dir holds two DIFFERENT versions of the same package (stale artifact).
|
||||
my $vdir = "$tmp/vers"; make_path($vdir);
|
||||
system("cp '" . $mk->('ppc64', 'x', '2.19.0') . "' '$vdir/'");
|
||||
system("cp '" . $mk->('ppc64', 'x', '2.18.0') . "' '$vdir/'");
|
||||
my $vdied = !eval { rpm_version($vdir, 'xCAT-genesis-base'); 1 };
|
||||
ok($vdied, 'rpm_version dies when a dir holds multiple distinct versions of a package');
|
||||
}
|
||||
|
||||
# ---- finalize_xcat_dep: require the genesis inputs (no silent no-op) --------------------------
|
||||
@@ -137,6 +149,13 @@ SPEC
|
||||
make_path("$tmp2/x", "$tmp2/p");
|
||||
my $ok2 = eval { quiet { finalize_xcat_dep("$tmp2/x", "$tmp2/p") }; 1 };
|
||||
ok(!$ok2, 'finalize dies when no <os>/x86_64 + <os>/ppc64le pair is found');
|
||||
|
||||
# A missing ppc64le PEER repo (not just missing rpms) is fatal, not a silent skip.
|
||||
my $tmp3 = tempdir(CLEANUP => 1);
|
||||
make_path("$tmp3/x/rh9/x86_64"); # x86_64 OS present, but NO ppc64le peer dir at all
|
||||
my $ok3 = eval { quiet { finalize_xcat_dep("$tmp3/x", "$tmp3/p") }; 1 };
|
||||
ok(!$ok3, 'finalize dies when an x86_64 OS has no ppc64le peer repo (no silent skip)');
|
||||
like($@, qr/no ppc64le peer repo/, 'finalize error names the missing peer');
|
||||
}
|
||||
|
||||
done_testing;
|
||||
|
||||
Reference in New Issue
Block a user