mirror of
https://github.com/xcat2/xcat-dep.git
synced 2026-09-12 12:36:23 +00:00
Merge pull request #68 from VersatusHPC/feat/genesis-s390x
feat(genesis): package s390x images
This commit is contained in:
@@ -48,6 +48,7 @@ jobs:
|
||||
lib/XCAT/GenesisRelease.pm \
|
||||
mockbuild-all.pl \
|
||||
t/build_utils.t \
|
||||
t/common-repo-gate.t \
|
||||
t/genesis_openembedded_release.t \
|
||||
t/genesis_openembedded_consumer.t \
|
||||
t/lib/XCAT/GenesisReleaseTest.pm
|
||||
@@ -59,3 +60,4 @@ jobs:
|
||||
prove -v t/sbuild-all.t
|
||||
prove -v -It/lib t/genesis_openembedded_release.t
|
||||
sudo -E prove -v -It/lib t/genesis_openembedded_consumer.t
|
||||
sudo -E prove -v -It/lib t/common-repo-gate.t
|
||||
|
||||
@@ -247,9 +247,10 @@ generation, and signing have succeeded. If a stopped publisher leaves staging
|
||||
or backup directories behind, rerun it with ``--force-unlock`` to recover the
|
||||
previous repository before starting a new publication.
|
||||
|
||||
Repository publication requires a release containing every supported Genesis
|
||||
architecture. The packages are `noarch`, and the common repository contains
|
||||
the full set of target images.
|
||||
Repository publication requires all eight current architectures. Version 1
|
||||
release manifests remain readable, but they cannot replace the current
|
||||
repository because they lack `s390x`. The packages are `noarch`, and each
|
||||
publication contains the full set.
|
||||
|
||||
The release checksums cover the unsigned input packages. If repository signing
|
||||
is enabled, `rpmsign` changes the deployed RPM bytes after collection.
|
||||
|
||||
@@ -185,3 +185,4 @@ xcat-genesis-openembedded-ppc64le=2.*
|
||||
xcat-genesis-openembedded-armv7hf=2.*
|
||||
xcat-genesis-openembedded-aarch64=2.*
|
||||
xcat-genesis-openembedded-riscv64=2.*
|
||||
xcat-genesis-openembedded-s390x=2.*
|
||||
|
||||
@@ -23,7 +23,7 @@ The packaging scripts use `File::Slurper` and `IPC::Cmd`. Install
|
||||
The default format is `all`, which produces RPM, SRPM, and DEB packages. Use
|
||||
`--format rpm` or `--format deb` when only one package family is needed. The
|
||||
supported image architectures are `x86`, `x86_64`, `ppc64`, `ppc64le`,
|
||||
`armv7hf`, `aarch64`, and `riscv64`.
|
||||
`armv7hf`, `aarch64`, `riscv64`, and `s390x`.
|
||||
|
||||
Use `--architecture` for development builds. Repository publication requires a
|
||||
complete release built with `--all`.
|
||||
@@ -88,8 +88,13 @@ APT metadata is assembled in a side tree and swapped onto the published
|
||||
repository with a single rename, under one global publish lock, so a failed or
|
||||
interrupted publication leaves the previous repository exactly as it was.
|
||||
|
||||
Both consumers require all seven architectures and verify package identities
|
||||
and checksums before publication. A management node can install an image for a
|
||||
Both consumers verify package identities and checksums before publication.
|
||||
Releases containing `s390x` use format version 2. Other releases keep version 1
|
||||
and remain readable by older tools. Current publishers require all eight
|
||||
architectures, so a version 1 release cannot replace the shared repository.
|
||||
Building `s390x` requires an xcat-core revision that reports the target. Merge
|
||||
or deploy that xcat-core change first. Builds for the other architectures still
|
||||
accept older xcat-core revisions. A management node can install an image for a
|
||||
different target architecture.
|
||||
|
||||
Without `--genesis-release`, both builders keep their existing behavior. The
|
||||
@@ -113,4 +118,5 @@ Run the package tests on a Linux builder with RPM, DEB, and repository tools:
|
||||
prove t/build_utils.t
|
||||
prove -It/lib t/genesis_openembedded_release.t
|
||||
sudo -E prove -It/lib t/genesis_openembedded_consumer.t
|
||||
sudo -E prove -It/lib t/common-repo-gate.t
|
||||
```
|
||||
|
||||
@@ -39,7 +39,7 @@ genesis_run_mknb() {
|
||||
genesis_activation_main() {
|
||||
architecture=$1
|
||||
case "$architecture" in
|
||||
x86|x86_64|ppc64|ppc64le|armv7hf|aarch64|riscv64) ;;
|
||||
x86|x86_64|ppc64|ppc64le|armv7hf|aarch64|riscv64|s390x) ;;
|
||||
*)
|
||||
echo "Invalid Genesis architecture: $architecture" >&2
|
||||
return 0
|
||||
|
||||
@@ -23,6 +23,7 @@ use XCAT::BuildUtils qw(
|
||||
use XCAT::GenesisRelease qw(
|
||||
architectures
|
||||
deb_package_name
|
||||
minimum_release_version
|
||||
rpm_package_name
|
||||
validate_architecture
|
||||
validate_release
|
||||
@@ -77,6 +78,28 @@ if ($xcat_ref ne '') {
|
||||
die "xcat-core HEAD $revision does not match $xcat_ref ($expected)\n"
|
||||
unless $revision eq $expected;
|
||||
}
|
||||
if ($requested{s390x}) {
|
||||
my $architecture_reporter = "$xcat_source/xCAT-genesis-builder/oe/build";
|
||||
my $probe_work = tempdir('xcat-genesis-capabilities.XXXXXX', TMPDIR => 1, CLEANUP => 1);
|
||||
my $supported_output;
|
||||
{
|
||||
local $ENV{XCAT_GENESIS_WORK_DIR} = $probe_work;
|
||||
local $ENV{TMPDIR} = $probe_work;
|
||||
$supported_output = eval {
|
||||
capture_command($architecture_reporter, '--list-architectures');
|
||||
};
|
||||
}
|
||||
my $report_error = $@;
|
||||
die "xcat-core source does not report supported Genesis architectures: $report_error"
|
||||
if $report_error;
|
||||
if (!defined($supported_output) || !length $supported_output) {
|
||||
die "xcat-core source reported no supported Genesis architectures\n";
|
||||
}
|
||||
my %supported_architecture = map { $_ => 1 } split m{\s+}xms, $supported_output;
|
||||
if (!$supported_architecture{s390x}) {
|
||||
die "xcat-core source does not support Genesis architecture s390x\n";
|
||||
}
|
||||
}
|
||||
|
||||
my $version = read_first_line("$xcat_source/Version");
|
||||
die "Invalid xCAT version: $version\n" unless $version =~ /^\d+(?:\.\d+){1,3}$/;
|
||||
@@ -140,10 +163,11 @@ for my $architecture (@requested_architectures) {
|
||||
}
|
||||
|
||||
my @formats = $format eq 'all' ? qw(deb rpm) : ($format);
|
||||
my $manifest_version = minimum_release_version(@requested_architectures);
|
||||
write_binary(
|
||||
"$staging/release.manifest",
|
||||
"format=xcat-genesis-packages\n"
|
||||
. "version=1\n"
|
||||
. "version=$manifest_version\n"
|
||||
. "xcat_version=$version\n"
|
||||
. "xcat_release=$release\n"
|
||||
. "xcat_revision=$revision\n"
|
||||
|
||||
@@ -8,20 +8,32 @@ use XCAT::BuildUtils qw(digest_file read_lines relative_files);
|
||||
|
||||
our @EXPORT_OK = qw(
|
||||
architectures
|
||||
deb_package_prefix
|
||||
deb_package_name
|
||||
minimum_release_version
|
||||
read_checksum_manifest
|
||||
read_release_manifest
|
||||
rpm_package_prefix
|
||||
rpm_package_name
|
||||
validated_release_checksums
|
||||
validate_architecture
|
||||
validate_complete_release
|
||||
validate_export
|
||||
validate_repository_packages
|
||||
validate_release
|
||||
verify_release_file
|
||||
);
|
||||
|
||||
my @ARCHITECTURES = qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64);
|
||||
my @RELEASE_V1_ARCHITECTURES = qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64);
|
||||
my @RELEASE_V2_ARCHITECTURES = (@RELEASE_V1_ARCHITECTURES, 's390x');
|
||||
my @ARCHITECTURES = @RELEASE_V2_ARCHITECTURES;
|
||||
my $RPM_PACKAGE_PREFIX = 'xCAT-genesis-openembedded-';
|
||||
my $DEB_PACKAGE_PREFIX = 'xcat-genesis-openembedded-';
|
||||
my %ARCHITECTURE = map { $_ => 1 } @ARCHITECTURES;
|
||||
my %RELEASE_ARCHITECTURES = (
|
||||
1 => \@RELEASE_V1_ARCHITECTURES,
|
||||
2 => \@RELEASE_V2_ARCHITECTURES,
|
||||
);
|
||||
|
||||
sub architectures {
|
||||
return @ARCHITECTURES;
|
||||
@@ -34,17 +46,48 @@ sub validate_architecture {
|
||||
return $architecture;
|
||||
}
|
||||
|
||||
sub rpm_package_prefix {
|
||||
return $RPM_PACKAGE_PREFIX;
|
||||
}
|
||||
|
||||
sub rpm_package_name {
|
||||
my ($architecture) = @_;
|
||||
validate_architecture($architecture);
|
||||
return "xCAT-genesis-openembedded-$architecture";
|
||||
return $RPM_PACKAGE_PREFIX . $architecture;
|
||||
}
|
||||
|
||||
sub deb_package_prefix {
|
||||
return $DEB_PACKAGE_PREFIX;
|
||||
}
|
||||
|
||||
sub deb_package_name {
|
||||
my ($architecture) = @_;
|
||||
validate_architecture($architecture);
|
||||
$architecture =~ tr/_/-/;
|
||||
return "xcat-genesis-openembedded-$architecture";
|
||||
return $DEB_PACKAGE_PREFIX . $architecture;
|
||||
}
|
||||
|
||||
sub minimum_release_version {
|
||||
my @architectures = @_;
|
||||
die "Release format selection requires a Genesis architecture\n" unless @architectures;
|
||||
validate_architecture($_) for @architectures;
|
||||
for my $version (sort { $a <=> $b } keys %RELEASE_ARCHITECTURES) {
|
||||
my %supported = map { $_ => 1 } @{ $RELEASE_ARCHITECTURES{$version} };
|
||||
return $version unless grep { !$supported{$_} } @architectures;
|
||||
}
|
||||
die "No release format supports the requested Genesis architectures\n";
|
||||
}
|
||||
|
||||
sub validate_repository_packages {
|
||||
my ($packages, $section, $prefix, @supported_names) = @_;
|
||||
my %supported = map { $_ => 1 } @supported_names;
|
||||
my @missing = grep { !exists $packages->{$_} } @supported_names;
|
||||
my @unknown = grep {
|
||||
index($_, $prefix) == 0 && !$supported{$_}
|
||||
} sort keys %{$packages};
|
||||
die "FATAL: [$section] is missing supported packages: @missing\n" if @missing;
|
||||
die "FATAL: [$section] has unsupported packages: @unknown\n" if @unknown;
|
||||
return $packages;
|
||||
}
|
||||
|
||||
sub _read_key_values {
|
||||
@@ -161,7 +204,7 @@ sub _validate_release {
|
||||
die "Unsupported Genesis package release format\n"
|
||||
unless $manifest->{format} eq 'xcat-genesis-packages';
|
||||
die "Unsupported Genesis package release version\n"
|
||||
unless $manifest->{version} eq '1';
|
||||
unless $RELEASE_ARCHITECTURES{ $manifest->{version} };
|
||||
die "Invalid xCAT version in release manifest\n"
|
||||
unless $manifest->{xcat_version} =~ /^\d+(?:\.\d+){1,3}$/;
|
||||
die "Invalid xCAT release in release manifest\n"
|
||||
@@ -172,9 +215,13 @@ sub _validate_release {
|
||||
unless $manifest->{source_date_epoch} =~ /^\d+$/;
|
||||
|
||||
my @architectures = split(/,/, $manifest->{architectures});
|
||||
my %version_architecture = map { $_ => 1 }
|
||||
@{ $RELEASE_ARCHITECTURES{ $manifest->{version} } };
|
||||
my %seen_arch;
|
||||
for my $architecture (@architectures) {
|
||||
validate_architecture($architecture);
|
||||
die "Genesis architecture $architecture is not valid in release version $manifest->{version}\n"
|
||||
unless $version_architecture{$architecture};
|
||||
die "Duplicate release architecture: $architecture\n" if $seen_arch{$architecture}++;
|
||||
}
|
||||
die "Release manifest has no architectures\n" unless @architectures;
|
||||
@@ -223,7 +270,8 @@ sub validate_complete_release {
|
||||
my $manifest = validate_release($directory);
|
||||
my %present = map { $_ => 1 } split(/,/, $manifest->{architectures});
|
||||
my @missing = grep { !$present{$_} } @ARCHITECTURES;
|
||||
die "Genesis release is missing supported architectures: @missing\n" if @missing;
|
||||
die "Genesis release version $manifest->{version} omits currently supported architectures: @missing\n"
|
||||
if @missing;
|
||||
return $manifest;
|
||||
}
|
||||
|
||||
|
||||
+26
-13
@@ -38,6 +38,10 @@ use XCAT::BuildUtils qw(
|
||||
shell_quote
|
||||
);
|
||||
use XCAT::GenesisRelease qw(
|
||||
architectures
|
||||
rpm_package_prefix
|
||||
rpm_package_name
|
||||
validate_repository_packages
|
||||
validated_release_checksums
|
||||
verify_release_file
|
||||
);
|
||||
@@ -373,6 +377,7 @@ if ($genesis_release ne '') {
|
||||
unless -d $genesis_release;
|
||||
my $verifier = "$script_dir/genesis-openembedded/verify-release";
|
||||
die "Genesis release verifier not found: $verifier\n" unless -x $verifier;
|
||||
common_repository_requirements();
|
||||
# Checksum, verify, checksum again. The verifier reads the tree it validates, so a
|
||||
# release rewritten together with its SHA256SUMS while the verifier runs would satisfy
|
||||
# both the verifier and any single pass taken afterwards; comparing the pass taken
|
||||
@@ -1131,9 +1136,8 @@ sub publish_genesis_common_repo {
|
||||
|
||||
=head3 verify_common_repo
|
||||
|
||||
Assert the shared OpenEmbedded Genesis repository carries every package the manifest's [common]
|
||||
section requires, at a version satisfying its pin. [common] is not a build target: it describes
|
||||
the one repository published beside the per-EL cells, which no [<target>] section covers.
|
||||
Assert the shared repository carries every package required by [common]. [common] must
|
||||
describe every currently supported Genesis architecture.
|
||||
|
||||
Arguments:
|
||||
$dir - the repository to check (the staging directory, before it is swapped into place)
|
||||
@@ -1145,16 +1149,11 @@ sub publish_genesis_common_repo {
|
||||
#--------------------------------------------------------------------------------
|
||||
sub verify_common_repo {
|
||||
my ($dir) = @_;
|
||||
my $manifest = "$repo_root/packages-manifest.conf";
|
||||
my %MAN = read_manifest($manifest);
|
||||
my %req = %{ $MAN{common} // {} };
|
||||
die "FATAL: no [common] section in $manifest -- cannot verify the shared Genesis repository\n"
|
||||
if !%req;
|
||||
|
||||
my @names = sort keys %req;
|
||||
my %present = repo_present_versions($dir, \@names);
|
||||
my %req = %{ common_repository_requirements() };
|
||||
my @names = sort keys %req;
|
||||
my %present = repo_present_versions($dir, \@names);
|
||||
my %present_evr = map { $_ => rpm_evr($dir, $_) } @names;
|
||||
my @problems = verify_repo_packages(\%req, \%present, \%present_evr, \&rpm_vercmp_segment);
|
||||
my @problems = verify_repo_packages(\%req, \%present, \%present_evr, \&rpm_vercmp_segment);
|
||||
if (@problems) {
|
||||
print " - $_\n" for @problems;
|
||||
die "FATAL: shared Genesis repo INCOMPLETE at $dir (" . scalar(@problems) . " problem(s))\n";
|
||||
@@ -1164,6 +1163,21 @@ sub verify_common_repo {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub common_repository_requirements {
|
||||
my $manifest = "$repo_root/packages-manifest.conf";
|
||||
my %MAN = read_manifest($manifest);
|
||||
my %common = %{ $MAN{common} // {} };
|
||||
die "FATAL: no [common] section in $manifest -- cannot verify the shared Genesis repository\n"
|
||||
if !%common;
|
||||
|
||||
return validate_repository_packages(
|
||||
\%common,
|
||||
'common',
|
||||
rpm_package_prefix(),
|
||||
map { rpm_package_name($_) } architectures(),
|
||||
);
|
||||
}
|
||||
|
||||
sub replace_common_repository {
|
||||
my ($staged, $destination) = @_;
|
||||
my $backup = "$repo_dep/.common.previous.$$";
|
||||
@@ -2229,4 +2243,3 @@ sub slurp_chomp {
|
||||
chomp $line if defined $line;
|
||||
return $line // '';
|
||||
}
|
||||
|
||||
|
||||
@@ -189,3 +189,4 @@ xCAT-genesis-openembedded-ppc64le=>= 2.18.0
|
||||
xCAT-genesis-openembedded-armv7hf=>= 2.18.0
|
||||
xCAT-genesis-openembedded-aarch64=>= 2.18.0
|
||||
xCAT-genesis-openembedded-riscv64=>= 2.18.0
|
||||
xCAT-genesis-openembedded-s390x=>= 2.18.0
|
||||
|
||||
+20
-12
@@ -318,6 +318,7 @@ if ($genesis_release ne '') {
|
||||
# the verifier runs would satisfy both the verifier and any single pass taken afterwards.
|
||||
require XCAT::BuildUtils;
|
||||
require XCAT::GenesisRelease;
|
||||
shared_repository_requirements();
|
||||
my $before = XCAT::GenesisRelease::validated_release_checksums($genesis_release);
|
||||
XCAT::BuildUtils::run_command($^X, $verifier, '--complete', '--format', 'deb', $genesis_release);
|
||||
my $after = XCAT::GenesisRelease::validated_release_checksums($genesis_release);
|
||||
@@ -1142,16 +1143,9 @@ sub install_genesis_release_debs {
|
||||
return scalar(@files);
|
||||
}
|
||||
|
||||
# verify_shared_pool($pool): assert the shared Genesis pool carries every package the manifest's
|
||||
# [shared] section requires, at a version satisfying its pin. [shared] is not a build target: it
|
||||
# describes the one pool every suite indexes, which no [<codename>-<arch>] section covers. Run on
|
||||
# the SIDE TREE, before it is swapped into place, so an incomplete pool is never published.
|
||||
# Completeness only -- the release checksums cover the bytes.
|
||||
sub verify_shared_pool {
|
||||
my ($pool) = @_;
|
||||
my %req = %{ $MANIFEST{shared} // {} };
|
||||
die "FATAL: no [shared] section in $manifest -- cannot verify the shared Genesis pool\n"
|
||||
if !%req;
|
||||
my %req = %{ shared_repository_requirements() };
|
||||
my @names = sort keys %req;
|
||||
my %present = map { $_ => deb_version($pool, $_) } @names;
|
||||
my @problems = verify_repo_packages(\%req, \%present);
|
||||
@@ -1163,6 +1157,19 @@ sub verify_shared_pool {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub shared_repository_requirements {
|
||||
my %shared = %{ $MANIFEST{shared} // {} };
|
||||
die "FATAL: no [shared] section in $manifest -- cannot verify the shared Genesis pool\n"
|
||||
if !%shared;
|
||||
return XCAT::GenesisRelease::validate_repository_packages(
|
||||
\%shared,
|
||||
'shared',
|
||||
XCAT::GenesisRelease::deb_package_prefix(),
|
||||
map { XCAT::GenesisRelease::deb_package_name($_) }
|
||||
XCAT::GenesisRelease::architectures(),
|
||||
);
|
||||
}
|
||||
|
||||
sub assemble_into {
|
||||
my ($dir, $expect) = @_;
|
||||
if ($genesis_release ne '') {
|
||||
@@ -1567,10 +1574,11 @@ Publish an B<OpenEmbedded Genesis package release> alongside the packages this r
|
||||
release is produced separately (see F<genesis-openembedded/README.md>); this option only verifies it
|
||||
and copies the verified bytes into every selected suite.
|
||||
|
||||
The release must be B<complete> (every supported Genesis architecture) and must carry C<deb>
|
||||
packages. It is validated before any build or publish: its C<SHA256SUMS> is read, the shared
|
||||
verifier runs, and the checksums are read again -- a release rewritten together with its checksums
|
||||
while the verifier runs is rejected.
|
||||
The release must carry C<deb> packages for every currently supported architecture. Version 1
|
||||
metadata remains readable, but a complete release now requires C<s390x>. The release is validated
|
||||
before any build or publish: its C<SHA256SUMS> is read, the shared verifier runs, and the checksums
|
||||
are read again -- a release rewritten together with its checksums while the verifier runs is
|
||||
rejected.
|
||||
|
||||
The packages are published B<once>, into F<pool/main/xcat-genesis-openembedded>, and every suite's
|
||||
C<Packages> index points at that one copy: they are C<Architecture: all> and identical everywhere,
|
||||
|
||||
+178
-25
@@ -10,34 +10,85 @@ use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use FindBin qw($RealBin);
|
||||
use File::Basename qw(basename);
|
||||
use File::Temp qw(tempdir);
|
||||
use File::Path qw(make_path);
|
||||
use File::Copy qw(copy);
|
||||
use lib "$RealBin/..";
|
||||
use lib "$RealBin/../lib";
|
||||
use lib "$RealBin/lib";
|
||||
use MockBuildUtils qw(read_manifest);
|
||||
use XCAT::BuildUtils qw(command_exists);
|
||||
use XCAT::GenesisRelease qw(architectures rpm_package_name);
|
||||
use XCAT::GenesisReleaseTest qw(
|
||||
build_package_release
|
||||
copy_tree
|
||||
write_checksums
|
||||
write_release_manifest
|
||||
);
|
||||
|
||||
my $SCRIPT = "$RealBin/../mockbuild-all.pl";
|
||||
my $RELEASE = '/opt/xcat-ci-shared/builds/genesis-openembedded-initial-20260825/release';
|
||||
my $PACKAGER = "$RealBin/../genesis-openembedded/package";
|
||||
plan skip_all => 'mockbuild-all.pl not found' unless -f $SCRIPT;
|
||||
plan skip_all => 'no Genesis release fixture' unless -d "$RELEASE/rpm";
|
||||
# root, like every other test that drives mockbuild-all.pl: the script refuses to run otherwise,
|
||||
# and the CI builder (XCAT_GENESIS_CI) is root.
|
||||
plan skip_all => 'rpm tooling and a root Linux builder required'
|
||||
unless $^O eq 'linux'
|
||||
&& $> == 0
|
||||
&& !system('sh', '-c', 'command -v rpm >/dev/null 2>&1')
|
||||
&& !system('sh', '-c', 'command -v createrepo_c >/dev/null 2>&1')
|
||||
&& !system('sh', '-c', 'command -v rpmbuild >/dev/null 2>&1');
|
||||
my @missing_requirements;
|
||||
push @missing_requirements, 'Linux' unless $^O eq 'linux';
|
||||
push @missing_requirements, 'root' unless $> == 0;
|
||||
for my $command (qw(createrepo_c gzip rpm rpmbuild tar)) {
|
||||
push @missing_requirements, $command unless command_exists($command);
|
||||
}
|
||||
if (command_exists('tar')) {
|
||||
my $tar_version = `tar --version 2>/dev/null`;
|
||||
push @missing_requirements, 'GNU tar' unless $tar_version =~ /GNU tar/;
|
||||
}
|
||||
if (@missing_requirements) {
|
||||
my $message = 'requires ' . join(', ', @missing_requirements);
|
||||
BAIL_OUT($message) if $ENV{XCAT_GENESIS_CI};
|
||||
plan skip_all => $message;
|
||||
}
|
||||
|
||||
my $tmp = tempdir(CLEANUP => 1);
|
||||
my $target = 'alma+epel-10-' . do { my $m = `uname -m`; chomp $m; $m };
|
||||
my @architectures = architectures();
|
||||
my $xcat_version = '2.19.0';
|
||||
my $xcat_release = 'snap202609040000';
|
||||
my $xcat_revision = 'c' x 40;
|
||||
my $source_date_epoch = 1788476400;
|
||||
my $RELEASE = build_package_release(
|
||||
root => "$tmp/release-fixture",
|
||||
format => 'rpm',
|
||||
architectures => \@architectures,
|
||||
packager => $PACKAGER,
|
||||
version => $xcat_version,
|
||||
release => $xcat_release,
|
||||
revision => $xcat_revision,
|
||||
epoch => $source_date_epoch,
|
||||
);
|
||||
my @version_1_architectures = grep { $_ ne 's390x' } @architectures;
|
||||
my $VERSION_1_RELEASE = build_package_release(
|
||||
root => "$tmp/version-1-release-fixture",
|
||||
format => 'rpm',
|
||||
architectures => \@version_1_architectures,
|
||||
packager => $PACKAGER,
|
||||
version => $xcat_version,
|
||||
release => $xcat_release,
|
||||
revision => $xcat_revision,
|
||||
epoch => $source_date_epoch,
|
||||
);
|
||||
write_release_manifest(
|
||||
$VERSION_1_RELEASE, $xcat_version, $xcat_release, $xcat_revision,
|
||||
$source_date_epoch, join(',', @version_1_architectures), 'rpm', 1,
|
||||
);
|
||||
write_checksums($VERSION_1_RELEASE);
|
||||
|
||||
# The shipped manifest must describe the shared repo, else nothing can gate it.
|
||||
{
|
||||
my %m = read_manifest("$RealBin/../packages-manifest.conf");
|
||||
ok($m{common} && %{ $m{common} }, 'the shipped manifest has a [common] section');
|
||||
is(scalar(keys %{ $m{common} // {} }), 7,
|
||||
'... naming every architecture the release must carry');
|
||||
my @expected = sort map { rpm_package_name($_) }
|
||||
qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x);
|
||||
my @missing = grep { !exists $m{common}{$_} } @expected;
|
||||
is_deeply(\@missing, [],
|
||||
'the shared RPM manifest lists every Genesis architecture');
|
||||
}
|
||||
|
||||
# fixture_rpm: a minimal noarch rpm, built once, standing in for a compiled dep.
|
||||
@@ -69,9 +120,8 @@ SPEC
|
||||
return $FIXTURE;
|
||||
}
|
||||
|
||||
# run_publish($release_dir) -> ($exit, $output, $common_dir)
|
||||
sub run_publish {
|
||||
my ($release, $tag) = @_;
|
||||
my ($release, $tag, $mutate_common, $skip_repo_verification) = @_;
|
||||
my $out = "$tmp/$tag";
|
||||
make_path("$out/root", "$out/collect");
|
||||
# Something to collect, so the run gets past the "built nothing" guard. It must NOT be an
|
||||
@@ -83,49 +133,152 @@ sub run_publish {
|
||||
print $fh "[$target]\nipmitool-xcat=1.8.18\n";
|
||||
# the shared repo's own section, copied from the shipped manifest so the test uses the real one
|
||||
my %m = read_manifest("$RealBin/../packages-manifest.conf");
|
||||
$mutate_common->($m{common}) if $mutate_common;
|
||||
print $fh "\n[common]\n";
|
||||
print $fh "$_=$m{common}{$_}\n" for sort keys %{ $m{common} // {} };
|
||||
close $fh;
|
||||
my $cmd = join(' ', map { my $x = $_; $x =~ s/'/'"'"'/g; "'$x'" }
|
||||
my @command =
|
||||
($^X, $SCRIPT, '--repo-root', "$out/root", '--output', "$out/build",
|
||||
'--repo-dep', "$out/repo", '--target', $target, '--run-id', $tag,
|
||||
'--build-timestamp', '1787672536',
|
||||
'--skip-build', '--skip-genesis', '--skip-xcat-dep', '--skip-perl',
|
||||
'--skip-tarball',
|
||||
'--collect-dir', "$out/collect", '--genesis-release', $release)) . ' 2>&1';
|
||||
'--collect-dir', "$out/collect", '--genesis-release', $release);
|
||||
push @command, '--no-verify-repo' if $skip_repo_verification;
|
||||
my $cmd = join(' ', map { my $x = $_; $x =~ s/'/'"'"'/g; "'$x'" } @command)
|
||||
. ' 2>&1';
|
||||
my $log = `$cmd`;
|
||||
return ($? >> 8, $log, "$out/repo/common");
|
||||
}
|
||||
|
||||
{
|
||||
my $package = rpm_package_name('s390x');
|
||||
my ($rc, $out, $common) = run_publish(
|
||||
$RELEASE,
|
||||
'unsatisfied-common',
|
||||
sub { $_[0]->{$package} = '>= 99.0.0' },
|
||||
);
|
||||
isnt($rc, 0, 'an unsatisfied shared-repository requirement is refused');
|
||||
like($out, qr/EVR \Q$package\E: repo has .* manifest requires >= 99\.0\.0/,
|
||||
'the shared-repository gate names the unsatisfied requirement');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'a failed shared-repository gate publishes nothing');
|
||||
}
|
||||
|
||||
# ---- a complete release publishes, and says it was gated -----------------------------------------
|
||||
{
|
||||
my ($rc, $out, $common) = run_publish($RELEASE, 'full');
|
||||
is($rc, 0, 'a complete release publishes') or diag($out);
|
||||
is(scalar(grep { !/\.src\.rpm$/ } glob("$common/*.rpm")), 7,
|
||||
is(scalar(grep { !/\.src\.rpm$/ } glob("$common/*.rpm")), 8,
|
||||
'the published shared repo carries every architecture');
|
||||
like($out, qr/\[verify-repo\] common complete/, 'the shared repo is gated against [common]');
|
||||
like($out, qr/\[verify-repo\] common complete: 8 packages present/,
|
||||
'the shared repo is gated against [common]');
|
||||
}
|
||||
|
||||
{
|
||||
my ($rc, $out, $common) = run_publish($VERSION_1_RELEASE, 'version-1');
|
||||
isnt($rc, 0, 'a version 1 release cannot replace the current repository');
|
||||
like($out, qr/Genesis release version 1 omits currently supported architectures: s390x/,
|
||||
'version 1 refusal identifies the missing architecture');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'a version 1 release publishes nothing');
|
||||
}
|
||||
|
||||
{
|
||||
my ($rc, $out, $common) = run_publish(
|
||||
$RELEASE,
|
||||
'missing-non-genesis-package',
|
||||
sub { $_[0]->{'xCAT-release'} = '>= 2.0.0' },
|
||||
);
|
||||
isnt($rc, 0, 'every common manifest package is verified');
|
||||
like($out, qr/MISSING xCAT-release/,
|
||||
'the common gate identifies a missing non-Genesis package');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'a missing non-Genesis package prevents publication');
|
||||
}
|
||||
|
||||
{
|
||||
my ($rc, $out, $common) = run_publish(
|
||||
$RELEASE,
|
||||
'missing-current-package',
|
||||
sub { delete $_[0]->{ rpm_package_name('s390x') } },
|
||||
);
|
||||
isnt($rc, 0, 'a current release does not hide an incomplete manifest');
|
||||
like($out, qr/\[common\] is missing supported packages: .*s390x/,
|
||||
'the manifest failure identifies the missing current package');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'an incomplete current manifest publishes nothing');
|
||||
}
|
||||
|
||||
{
|
||||
my ($rc, $out, $common) = run_publish(
|
||||
$RELEASE,
|
||||
'missing-current-package-without-repo-verification',
|
||||
sub { delete $_[0]->{ rpm_package_name('s390x') } },
|
||||
1,
|
||||
);
|
||||
isnt($rc, 0, 'repository verification cannot disable the common manifest gate');
|
||||
like($out, qr/\[common\] is missing supported packages: .*s390x/,
|
||||
'the mandatory manifest gate identifies the missing package');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'the mandatory manifest gate publishes nothing');
|
||||
}
|
||||
|
||||
{
|
||||
my ($rc, $out, $common) = run_publish(
|
||||
$RELEASE,
|
||||
'unknown-current-package',
|
||||
sub { $_[0]->{'xCAT-genesis-openembedded-unknown'} = '>= 2.18.0' },
|
||||
);
|
||||
isnt($rc, 0, 'an unknown shared manifest package is refused');
|
||||
like($out, qr/\[common\] has unsupported packages: xCAT-genesis-openembedded-unknown/,
|
||||
'the manifest failure identifies the unknown package');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'an unknown shared manifest package publishes nothing');
|
||||
}
|
||||
|
||||
# ---- an incomplete release is refused, and publishes nothing --------------------------------------
|
||||
{
|
||||
my $partial = "$tmp/partial-release";
|
||||
my $missing_architecture = 's390x';
|
||||
my $missing_package = rpm_package_name($missing_architecture);
|
||||
my @partial_architectures = grep { $_ ne $missing_architecture } @architectures;
|
||||
make_path("$partial/rpm", "$partial/srpm");
|
||||
for my $f (glob("$RELEASE/rpm/*.rpm"), glob("$RELEASE/srpm/*.rpm")) {
|
||||
next if $f =~ /riscv64/; # drop one architecture
|
||||
next if basename($f) =~ /^\Q$missing_package-\E/;
|
||||
my ($sub) = $f =~ m{/(rpm|srpm)/[^/]+$};
|
||||
copy($f, "$partial/$sub/") or die $!;
|
||||
}
|
||||
copy("$RELEASE/release.manifest", $partial) or die $!;
|
||||
# SHA256SUMS without the dropped arch, so the release itself still self-describes consistently
|
||||
open my $in, '<', "$RELEASE/SHA256SUMS" or die $!;
|
||||
open my $o, '>', "$partial/SHA256SUMS" or die $!;
|
||||
while (<$in>) { print {$o} $_ unless /riscv64/ }
|
||||
close $in; close $o;
|
||||
write_release_manifest(
|
||||
$partial, $xcat_version, $xcat_release, $xcat_revision, $source_date_epoch,
|
||||
join(',', @partial_architectures), 'rpm',
|
||||
);
|
||||
write_checksums($partial);
|
||||
|
||||
my ($rc, $out, $common) = run_publish($partial, 'partial');
|
||||
isnt($rc, 0, 'a release missing an architecture is refused');
|
||||
like($out, qr/omits currently supported architectures: \Q$missing_architecture\E/,
|
||||
'the completeness gate identifies the missing architecture');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'... and nothing is published into the shared repository');
|
||||
}
|
||||
|
||||
{
|
||||
my $inconsistent = "$tmp/inconsistent-release";
|
||||
my $missing_package = rpm_package_name('s390x');
|
||||
copy_tree($RELEASE, $inconsistent);
|
||||
for my $directory (qw(rpm srpm)) {
|
||||
my @packages = glob("$inconsistent/$directory/$missing_package-*");
|
||||
unlink(@packages) == @packages or die "cannot remove package fixture\n";
|
||||
}
|
||||
write_checksums($inconsistent);
|
||||
|
||||
my ($rc, $out, $common) = run_publish($inconsistent, 'inconsistent');
|
||||
isnt($rc, 0, 'a release inconsistent with its manifest is refused');
|
||||
like($out, qr/Genesis release is missing:/,
|
||||
'the release-layout gate reports the missing package');
|
||||
ok(!-d $common || !glob("$common/*.rpm"),
|
||||
'an inconsistent release publishes nothing');
|
||||
}
|
||||
|
||||
done_testing();
|
||||
|
||||
@@ -12,8 +12,10 @@ use POSIX ();
|
||||
use Test::More;
|
||||
use Time::HiRes qw(sleep);
|
||||
|
||||
use lib "$FindBin::Bin/..";
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use lib "$FindBin::Bin/lib";
|
||||
use BuildUtils qw(read_manifest);
|
||||
use XCAT::BuildUtils qw(
|
||||
capture_command
|
||||
command_exists
|
||||
@@ -27,10 +29,10 @@ use XCAT::GenesisRelease qw(
|
||||
rpm_package_name
|
||||
);
|
||||
use XCAT::GenesisReleaseTest qw(
|
||||
make_export
|
||||
build_package_release
|
||||
run_capture
|
||||
write_forkmanager_stub
|
||||
write_checksums
|
||||
write_forkmanager_stub
|
||||
write_release_manifest
|
||||
);
|
||||
|
||||
@@ -75,11 +77,12 @@ SKIP: {
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip 'APT repository tools are not installed', 59
|
||||
skip 'APT repository tools are not installed', 63
|
||||
unless $^O eq 'linux'
|
||||
&& command_exists('dpkg-deb')
|
||||
&& command_exists('apt-ftparchive');
|
||||
test_deb_consumer();
|
||||
test_version_1_deb_consumer();
|
||||
test_legacy_deb_consumer();
|
||||
test_partial_deb_release();
|
||||
test_publish_lock();
|
||||
@@ -100,9 +103,13 @@ done_testing();
|
||||
sub write_target_manifest {
|
||||
my ($root, $target) = @_;
|
||||
make_path($root);
|
||||
my %manifest = read_manifest("$repo_root/packages-manifest.conf");
|
||||
my $common = join('', map { "$_=$manifest{common}{$_}\n" }
|
||||
sort keys %{ $manifest{common} // {} });
|
||||
write_binary(
|
||||
"$root/packages-manifest.conf",
|
||||
"[$target]\n" . rpm_package_name(capture_command('uname', '-m')) . "=*\n",
|
||||
"[$target]\n" . rpm_package_name(capture_command('uname', '-m'))
|
||||
. "=*\n\n[common]\n$common",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -297,17 +304,11 @@ SH
|
||||
sub run_apt_consumer {
|
||||
my (%args) = @_;
|
||||
my @dists = @{ $args{dists} // \@APT_SUITES };
|
||||
my @build_mode = $args{build} ? ('--dry-run') : ('--skip-build');
|
||||
my $manifest = $args{manifest};
|
||||
unless ($manifest) {
|
||||
$manifest = "$args{output}/manifest.conf";
|
||||
make_path($args{output});
|
||||
# plus the shipped [shared] section verbatim: publishing a release gates the shared pool
|
||||
# against it, and a manifest without it is refused rather than silently ungated.
|
||||
my $shipped = read_binary("$repo_root/debs-manifest.conf");
|
||||
my ($shared) = $shipped =~ /^(\[shared\]\n(?:[^\[]*))/ms;
|
||||
BAIL_OUT('debs-manifest.conf has no [shared] section') unless $shared;
|
||||
write_binary($manifest,
|
||||
join('', map { "[$_-amd64]\nxcat-genesis-base=*\n" } @APT_SUITES) . "\n" . $shared);
|
||||
write_apt_manifest($manifest);
|
||||
}
|
||||
return run_capture(
|
||||
$args{log},
|
||||
@@ -318,13 +319,29 @@ sub run_apt_consumer {
|
||||
'--manifest', $manifest,
|
||||
'--dists', join(' ', @dists),
|
||||
'--arch', 'amd64',
|
||||
'--skip-build', '--skip-genesis', '--skip-tarball',
|
||||
@build_mode, '--skip-genesis', '--skip-tarball',
|
||||
'--publish', '--expect-arch', 'amd64 ppc64el',
|
||||
($args{verify} ? () : ('--no-verify-repo')),
|
||||
@{ $args{extra} // [] },
|
||||
);
|
||||
}
|
||||
|
||||
sub write_apt_manifest {
|
||||
my ($path, $mutate) = @_;
|
||||
my %shipped = read_manifest("$repo_root/debs-manifest.conf");
|
||||
BAIL_OUT('debs-manifest.conf has no [shared] section')
|
||||
unless exists $shipped{shared};
|
||||
my %shared = %{ $shipped{shared} };
|
||||
$mutate->(\%shared) if $mutate;
|
||||
make_path((File::Basename::dirname($path)));
|
||||
write_binary(
|
||||
$path,
|
||||
join('', map { "[$_-amd64]\nxcat-genesis-base=*\n" } @APT_SUITES)
|
||||
. "\n[shared]\n"
|
||||
. join('', map { "$_=$shared{$_}\n" } sort keys %shared),
|
||||
);
|
||||
}
|
||||
|
||||
# Stage one legacy genesis-base deb for the noble suite only -- enough for the runs that publish
|
||||
# no Genesis release.
|
||||
sub stage_legacy_deb {
|
||||
@@ -397,7 +414,8 @@ sub test_deb_consumer {
|
||||
} architectures();
|
||||
is_deeply([ genesis_deb_names($shared_pool) ], \@expected_packages,
|
||||
'shared APT pool contains one complete Genesis release');
|
||||
like(read_binary($log), qr/\[verify-repo\] shared pool complete: 7 packages present/,
|
||||
like(read_binary($log),
|
||||
qr/\[verify-repo\] shared pool complete: 8 packages present/,
|
||||
'the shared pool is gated against the manifest\'s [shared] section');
|
||||
my @suite_packages;
|
||||
for my $codename (@APT_SUITES) {
|
||||
@@ -524,6 +542,103 @@ sub test_deb_consumer {
|
||||
'pooled package still matches the verified release');
|
||||
}
|
||||
|
||||
sub test_version_1_deb_consumer {
|
||||
my @release_architectures = grep { $_ ne 's390x' } architectures();
|
||||
my $release_root = make_package_release(
|
||||
"$tmp/deb-version-1", 'deb', @release_architectures,
|
||||
);
|
||||
write_release_manifest(
|
||||
$release_root, $version, $release, $revision, $epoch,
|
||||
join(',', @release_architectures), 'deb', 1,
|
||||
);
|
||||
write_checksums($release_root);
|
||||
|
||||
my $apt_root = "$tmp/apt-version-1";
|
||||
my $output = "$tmp/deb-version-1-output";
|
||||
stage_apt_suites($output, "$tmp/deb-version-1-legacy");
|
||||
my $log = "$tmp/deb-version-1.log";
|
||||
my $status = run_apt_consumer(
|
||||
log => $log, output => $output, apt_dir => $apt_root,
|
||||
extra => [ '--genesis-release', $release_root ],
|
||||
);
|
||||
my $pool = "$apt_root/pool/main/xcat-genesis-openembedded";
|
||||
|
||||
isnt($status, 0, 'APT refuses a version 1 release for the current repository');
|
||||
like(read_binary($log),
|
||||
qr/Genesis release version 1 omits currently supported architectures: s390x/,
|
||||
'the version 1 refusal identifies the missing architecture');
|
||||
ok(!-d $pool, 'a version 1 release publishes no shared pool');
|
||||
|
||||
my $current_release = make_package_release(
|
||||
"$tmp/deb-current-manifest", 'deb', architectures(),
|
||||
);
|
||||
|
||||
my $missing_manifest = "$tmp/deb-version-1-missing.conf";
|
||||
write_apt_manifest(
|
||||
$missing_manifest,
|
||||
sub { delete $_[0]->{ deb_package_name('s390x') } },
|
||||
);
|
||||
my $missing_apt = "$tmp/apt-version-1-missing";
|
||||
my $missing_output = "$tmp/deb-version-1-missing-output";
|
||||
stage_apt_suites($missing_output, "$tmp/deb-version-1-missing-legacy");
|
||||
my $missing_log = "$tmp/deb-version-1-missing.log";
|
||||
my $missing_status = run_apt_consumer(
|
||||
log => $missing_log, output => $missing_output, apt_dir => $missing_apt,
|
||||
manifest => $missing_manifest,
|
||||
build => 1,
|
||||
extra => [ '--genesis-release', $current_release ],
|
||||
);
|
||||
isnt($missing_status, 0,
|
||||
'a current release does not hide an incomplete shared manifest');
|
||||
like(read_binary($missing_log), qr/\[shared\] is missing supported packages: .*s390x/,
|
||||
'the shared manifest failure identifies the missing current package');
|
||||
unlike(read_binary($missing_log), qr/Ensure sbuild chroots/,
|
||||
'an incomplete shared manifest is rejected before building');
|
||||
|
||||
my $unknown_manifest = "$tmp/deb-version-1-unknown.conf";
|
||||
write_apt_manifest(
|
||||
$unknown_manifest,
|
||||
sub { $_[0]->{'xcat-genesis-openembedded-unknown'} = '2.*' },
|
||||
);
|
||||
my $unknown_apt = "$tmp/apt-version-1-unknown";
|
||||
my $unknown_output = "$tmp/deb-version-1-unknown-output";
|
||||
stage_apt_suites($unknown_output, "$tmp/deb-version-1-unknown-legacy");
|
||||
my $unknown_log = "$tmp/deb-version-1-unknown.log";
|
||||
my $unknown_status = run_apt_consumer(
|
||||
log => $unknown_log, output => $unknown_output, apt_dir => $unknown_apt,
|
||||
manifest => $unknown_manifest,
|
||||
extra => [ '--genesis-release', $current_release ],
|
||||
);
|
||||
isnt($unknown_status, 0, 'an unknown shared manifest package is refused');
|
||||
like(read_binary($unknown_log),
|
||||
qr/\[shared\] has unsupported packages: xcat-genesis-openembedded-unknown/,
|
||||
'the shared manifest failure identifies the unknown package');
|
||||
ok(!-d "$unknown_apt/pool/main/xcat-genesis-openembedded",
|
||||
'an unknown shared manifest package publishes nothing');
|
||||
|
||||
my $non_genesis_manifest = "$tmp/deb-non-genesis-missing.conf";
|
||||
write_apt_manifest(
|
||||
$non_genesis_manifest,
|
||||
sub { $_[0]->{'xcat-release'} = '2.*' },
|
||||
);
|
||||
my $non_genesis_apt = "$tmp/apt-non-genesis-missing";
|
||||
my $non_genesis_output = "$tmp/deb-non-genesis-missing-output";
|
||||
stage_apt_suites($non_genesis_output, "$tmp/deb-non-genesis-missing-legacy");
|
||||
my $non_genesis_log = "$tmp/deb-non-genesis-missing.log";
|
||||
my $non_genesis_status = run_apt_consumer(
|
||||
log => $non_genesis_log,
|
||||
output => $non_genesis_output,
|
||||
apt_dir => $non_genesis_apt,
|
||||
manifest => $non_genesis_manifest,
|
||||
extra => [ '--genesis-release', $current_release ],
|
||||
);
|
||||
isnt($non_genesis_status, 0, 'every shared manifest package is verified');
|
||||
like(read_binary($non_genesis_log), qr/MISSING xcat-release/,
|
||||
'the shared gate identifies a missing non-Genesis package');
|
||||
ok(!-d "$non_genesis_apt/pool/main/xcat-genesis-openembedded",
|
||||
'a missing non-Genesis package prevents APT publication');
|
||||
}
|
||||
|
||||
sub test_signed_common_rpm_repository {
|
||||
my $release_root = make_package_release("$tmp/rpm-signed", 'rpm');
|
||||
my $package = "xCAT-genesis-openembedded-x86_64-$version-$release.noarch.rpm";
|
||||
@@ -679,7 +794,7 @@ sub test_partial_rpm_release {
|
||||
);
|
||||
|
||||
isnt($status, 0, 'RPM repository rejects a partial Genesis release');
|
||||
like(read_binary($log), qr/Genesis release is missing supported architectures/,
|
||||
like(read_binary($log), qr/Genesis release version 2 omits currently supported architectures/,
|
||||
'RPM partial-release failure names the missing architectures');
|
||||
ok(-f $existing, 'partial release does not remove the deployed package');
|
||||
}
|
||||
@@ -701,7 +816,7 @@ sub test_partial_deb_release {
|
||||
);
|
||||
|
||||
isnt($status, 0, 'APT repository rejects a partial Genesis release');
|
||||
like(read_binary($log), qr/Genesis release is missing supported architectures/,
|
||||
like(read_binary($log), qr/Genesis release version 2 omits currently supported architectures/,
|
||||
'APT partial-release failure names the missing architectures');
|
||||
ok(-f $existing, 'partial DEB release does not remove the published package');
|
||||
}
|
||||
@@ -995,6 +1110,18 @@ SH
|
||||
is($status, 0, 'the activation helper accepts a local-TFTP node');
|
||||
is(read_binary($log), "x86_64\n", 'the activation helper runs mknb for one architecture');
|
||||
|
||||
write_binary($log, '');
|
||||
$status = run_capture($output, $driver, 's390x');
|
||||
is($status, 0, 'the activation helper accepts s390x');
|
||||
is(read_binary($log), "s390x\n", 'the activation helper runs mknb for s390x');
|
||||
|
||||
write_binary($log, '');
|
||||
$status = run_capture($output, $driver, 'unsupported');
|
||||
is($status, 0, 'an unsupported architecture does not fail the package transaction');
|
||||
is(read_binary($log), '', 'an unsupported architecture does not run mknb');
|
||||
like(read_binary($output), qr/Invalid Genesis architecture: unsupported/,
|
||||
'the activation helper reports an unsupported architecture');
|
||||
|
||||
write_binary($log, '');
|
||||
local $ENV{XCAT_TEST_SERVICE_NODE} = 1;
|
||||
local $ENV{XCAT_TEST_SHAREDTFTP} = 1;
|
||||
@@ -1011,50 +1138,16 @@ SH
|
||||
sub make_package_release {
|
||||
my ($root, $format, @requested_architectures) = @_;
|
||||
@requested_architectures = architectures() unless @requested_architectures;
|
||||
my $release_root = "$root/release";
|
||||
make_path($release_root);
|
||||
for my $architecture (@requested_architectures) {
|
||||
my $export = make_export("$root/exports/$architecture", $architecture);
|
||||
my $packages = "$root/packages/$architecture";
|
||||
die "Cannot package test release for $architecture\n"
|
||||
if run_capture(
|
||||
"$root/package-$architecture.log",
|
||||
$packager,
|
||||
'--architecture', $architecture,
|
||||
'--export-dir', $export,
|
||||
'--output-dir', $packages,
|
||||
'--version', $version,
|
||||
'--release', $release,
|
||||
'--revision', $revision,
|
||||
'--source-date-epoch', $epoch,
|
||||
'--format', $format,
|
||||
);
|
||||
if ($format eq 'rpm') {
|
||||
my $name = rpm_package_name($architecture);
|
||||
make_path("$release_root/rpm", "$release_root/srpm");
|
||||
copy(
|
||||
"$packages/rpm/$name-$version-$release.noarch.rpm",
|
||||
"$release_root/rpm/$name-$version-$release.noarch.rpm",
|
||||
) or die $!;
|
||||
copy(
|
||||
"$packages/srpm/$name-$version-$release.src.rpm",
|
||||
"$release_root/srpm/$name-$version-$release.src.rpm",
|
||||
) or die $!;
|
||||
} else {
|
||||
my $name = deb_package_name($architecture);
|
||||
make_path("$release_root/deb");
|
||||
copy(
|
||||
"$packages/deb/${name}_${version}-${release}_all.deb",
|
||||
"$release_root/deb/${name}_${version}-${release}_all.deb",
|
||||
) or die $!;
|
||||
}
|
||||
}
|
||||
write_release_manifest(
|
||||
$release_root, $version, $release, $revision, $epoch,
|
||||
join(',', @requested_architectures), $format,
|
||||
return build_package_release(
|
||||
root => $root,
|
||||
format => $format,
|
||||
architectures => \@requested_architectures,
|
||||
packager => $packager,
|
||||
version => $version,
|
||||
release => $release,
|
||||
revision => $revision,
|
||||
epoch => $epoch,
|
||||
);
|
||||
write_checksums($release_root);
|
||||
return $release_root;
|
||||
}
|
||||
|
||||
sub genesis_rpm_names {
|
||||
|
||||
@@ -11,6 +11,7 @@ use Test::More;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use lib "$FindBin::Bin/lib";
|
||||
use XCAT::BuildUtils qw(
|
||||
capture_command
|
||||
command_exists
|
||||
digest_file
|
||||
read_binary
|
||||
@@ -19,6 +20,7 @@ use XCAT::BuildUtils qw(
|
||||
use XCAT::GenesisRelease qw(
|
||||
architectures
|
||||
deb_package_name
|
||||
minimum_release_version
|
||||
rpm_package_name
|
||||
validated_release_checksums
|
||||
validate_architecture
|
||||
@@ -53,13 +55,19 @@ if ($ENV{XCAT_GENESIS_CI}) {
|
||||
|
||||
is_deeply(
|
||||
[ architectures() ],
|
||||
[ qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64) ],
|
||||
[ qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64 s390x) ],
|
||||
'supported architectures keep their exact xCAT names',
|
||||
);
|
||||
is(rpm_package_name('ppc64le'), 'xCAT-genesis-openembedded-ppc64le',
|
||||
'RPM package keeps ppc64le distinct');
|
||||
is(deb_package_name('x86_64'), 'xcat-genesis-openembedded-x86-64',
|
||||
'DEB package uses a legal spelling of x86_64');
|
||||
is(minimum_release_version('x86_64'), 1,
|
||||
'legacy architectures use release format version 1');
|
||||
is(minimum_release_version('x86_64', 's390x'), 2,
|
||||
's390x requires release format version 2');
|
||||
dies_like(sub { minimum_release_version() }, qr/requires a Genesis architecture/,
|
||||
'release format selection requires an architecture');
|
||||
dies_like(sub { validate_architecture('ppc') }, qr/Unsupported Genesis architecture/,
|
||||
'legacy ppc alias is rejected');
|
||||
|
||||
@@ -181,7 +189,7 @@ like(
|
||||
);
|
||||
dies_like(
|
||||
sub { validate_complete_release($release_dir) },
|
||||
qr/Genesis release is missing supported architectures/,
|
||||
qr/Genesis release version 2 omits currently supported architectures/,
|
||||
'partial release cannot be published',
|
||||
);
|
||||
|
||||
@@ -201,6 +209,49 @@ write_release_manifest(
|
||||
write_checksums($complete_release);
|
||||
ok(validate_complete_release($complete_release), 'complete release can be published');
|
||||
|
||||
my $legacy_release = "$tmp/legacy-release";
|
||||
copy_tree($complete_release, $legacy_release);
|
||||
for my $directory (qw(rpm srpm deb)) {
|
||||
my @s390x_packages = glob("$legacy_release/$directory/*s390x*");
|
||||
unlink(@s390x_packages) == @s390x_packages
|
||||
or die "Cannot remove the s390x package fixture: $!\n";
|
||||
}
|
||||
write_release_manifest(
|
||||
$legacy_release, $version, $release, $revision, $epoch,
|
||||
'x86,x86_64,ppc64,ppc64le,armv7hf,aarch64,riscv64', 'deb,rpm', 1,
|
||||
);
|
||||
write_checksums($legacy_release);
|
||||
ok(validate_release($legacy_release), 'version 1 releases remain readable');
|
||||
dies_like(
|
||||
sub { validate_complete_release($legacy_release) },
|
||||
qr/Genesis release version 1 omits currently supported architectures: s390x/,
|
||||
'version 1 releases cannot replace the current repository',
|
||||
);
|
||||
|
||||
my $invalid_legacy_release = "$tmp/invalid-legacy-release";
|
||||
copy_tree($complete_release, $invalid_legacy_release);
|
||||
write_release_manifest(
|
||||
$invalid_legacy_release, $version, $release, $revision, $epoch,
|
||||
join(',', architectures()), 'deb,rpm', 1,
|
||||
);
|
||||
write_checksums($invalid_legacy_release);
|
||||
dies_like(
|
||||
sub { validate_release($invalid_legacy_release) },
|
||||
qr/Genesis architecture s390x is not valid in release version 1/,
|
||||
'version 1 rejects the version 2 architecture vocabulary',
|
||||
);
|
||||
|
||||
my $unknown_release_version = "$tmp/unknown-release-version";
|
||||
copy_tree($complete_release, $unknown_release_version);
|
||||
write_release_manifest(
|
||||
$unknown_release_version, $version, $release, $revision, $epoch,
|
||||
join(',', architectures()), 'deb,rpm', 3,
|
||||
);
|
||||
write_checksums($unknown_release_version);
|
||||
dies_like(sub { validate_release($unknown_release_version) },
|
||||
qr/Unsupported Genesis package release version/,
|
||||
'unknown release manifest versions fail');
|
||||
|
||||
my $deb_only_release = "$tmp/deb-only-release";
|
||||
make_path("$deb_only_release/deb");
|
||||
for my $architecture (architectures()) {
|
||||
@@ -233,11 +284,25 @@ dies_like(sub { validate_release($missing_release) }, qr/Genesis release is miss
|
||||
'incomplete architecture set fails');
|
||||
|
||||
SKIP: {
|
||||
skip 'git is not installed', 2 unless command_exists('git');
|
||||
skip 'git is not installed', 13 unless command_exists('git');
|
||||
my $source = "$tmp/dirty-xcat-core";
|
||||
make_path("$source/xCAT-genesis-builder/oe");
|
||||
my $oe = "$source/xCAT-genesis-builder/oe";
|
||||
my $capability_marker = "$tmp/capability-query-ran";
|
||||
make_path($oe);
|
||||
write_binary("$source/Version", "$version\n");
|
||||
write_binary("$source/xCAT-genesis-builder/oe/build", "#!/bin/sh\nexit 99\n");
|
||||
write_binary(
|
||||
"$source/xCAT-genesis-builder/oe/build",
|
||||
"#!/bin/sh\n"
|
||||
. "if [ \"\${1-}\" = --list-architectures ]; then\n"
|
||||
. " [ -z \"\${XCAT_TEST_CAPABILITY_MARKER-}\" ] || : >\"\$XCAT_TEST_CAPABILITY_MARKER\"\n"
|
||||
. " mkdir -p \"\${XCAT_GENESIS_WORK_DIR:?}\"\n"
|
||||
. " printf '%s\\n' x86_64\n"
|
||||
. " exit 0\n"
|
||||
. "fi\n"
|
||||
. "exit 99\n",
|
||||
);
|
||||
chmod(0755, "$source/xCAT-genesis-builder/oe/build")
|
||||
or die "Cannot make fixture build executable: $!";
|
||||
write_binary("$source/xCAT-genesis-builder/oe/export", "#!/bin/sh\nexit 99\n");
|
||||
for my $command (
|
||||
[ 'git', '-C', $source, 'init', '-q' ],
|
||||
@@ -248,22 +313,111 @@ SKIP: {
|
||||
die "Cannot prepare test repository\n"
|
||||
if run_capture("$tmp/git-fixture.log", @{$command});
|
||||
}
|
||||
my $previous_revision = capture_command('git', '-C', $source, 'rev-parse', 'HEAD');
|
||||
my $commit_source = sub {
|
||||
my ($path, $message) = @_;
|
||||
for my $command (
|
||||
[ 'git', '-C', $source, 'add', $path ],
|
||||
[ 'git', '-C', $source, '-c', 'user.name=xCAT test',
|
||||
'-c', 'user.email=xcat-test@example.invalid', 'commit', '-qm', $message ],
|
||||
) {
|
||||
die "Cannot update test repository\n"
|
||||
if run_capture("$tmp/git-fixture.log", @{$command});
|
||||
}
|
||||
};
|
||||
write_binary("$source/untracked", "not part of the commit\n");
|
||||
my $log = "$tmp/dirty-source.log";
|
||||
isnt(
|
||||
run_capture(
|
||||
$log, $builder, '--xcat-source', $source,
|
||||
'--output-dir', "$tmp/dirty-output",
|
||||
),
|
||||
0,
|
||||
'release builder rejects untracked source files',
|
||||
);
|
||||
{
|
||||
local $ENV{XCAT_TEST_CAPABILITY_MARKER} = $capability_marker;
|
||||
isnt(
|
||||
run_capture(
|
||||
$log, $builder, '--xcat-source', $source,
|
||||
'--architecture', 's390x',
|
||||
'--output-dir', "$tmp/dirty-output",
|
||||
),
|
||||
0,
|
||||
'release builder rejects untracked source files',
|
||||
);
|
||||
}
|
||||
like(read_binary($log), qr/xcat-core checkout is not clean/,
|
||||
'dirty checkout failure is explicit');
|
||||
ok(!-e $capability_marker,
|
||||
'dirty source is rejected before its architecture helper runs');
|
||||
unlink("$source/untracked") or die "Cannot clean the source fixture: $!\n";
|
||||
|
||||
write_binary("$source/revision-marker", "new revision\n");
|
||||
$commit_source->('revision-marker', 'advance source revision');
|
||||
my $ref_log = "$tmp/ref-mismatch.log";
|
||||
{
|
||||
local $ENV{XCAT_TEST_CAPABILITY_MARKER} = $capability_marker;
|
||||
isnt(
|
||||
run_capture(
|
||||
$ref_log, $builder, '--xcat-source', $source,
|
||||
'--xcat-ref', $previous_revision,
|
||||
'--architecture', 's390x',
|
||||
'--output-dir', "$tmp/ref-mismatch-output",
|
||||
),
|
||||
0,
|
||||
'release builder rejects a mismatched xcat-core revision',
|
||||
);
|
||||
}
|
||||
like(read_binary($ref_log), qr/xcat-core HEAD .* does not match \Q$previous_revision\E/,
|
||||
'revision mismatch failure is explicit');
|
||||
ok(!-e $capability_marker,
|
||||
'revision mismatch is rejected before the architecture helper runs');
|
||||
|
||||
my $target_log = "$tmp/missing-target.log";
|
||||
{
|
||||
local $ENV{XCAT_TEST_CAPABILITY_MARKER} = $capability_marker;
|
||||
isnt(
|
||||
run_capture(
|
||||
$target_log, $builder, '--xcat-source', $source,
|
||||
'--architecture', 's390x',
|
||||
'--output-dir', "$tmp/missing-target-output",
|
||||
),
|
||||
0,
|
||||
'release builder rejects an unsupported xcat-core target',
|
||||
);
|
||||
}
|
||||
like(read_binary($target_log), qr/does not support Genesis architecture s390x/,
|
||||
'missing target failure identifies the required xcat-core support');
|
||||
ok(-e $capability_marker, 'the architecture helper records a successful query');
|
||||
|
||||
write_binary("$oe/build", "#!/bin/sh\nexit 23\n");
|
||||
chmod(0755, "$oe/build") or die "Cannot update fixture build executable: $!";
|
||||
$commit_source->('xCAT-genesis-builder/oe/build', 'fail capability query');
|
||||
my $failed_log = "$tmp/failed-query.log";
|
||||
isnt(
|
||||
run_capture(
|
||||
$failed_log, $builder, '--xcat-source', $source,
|
||||
'--architecture', 's390x',
|
||||
'--output-dir', "$tmp/failed-query-output",
|
||||
),
|
||||
0,
|
||||
'release builder rejects a failed architecture query',
|
||||
);
|
||||
like(read_binary($failed_log), qr/does not report supported Genesis architectures/,
|
||||
'failed architecture queries are reported');
|
||||
|
||||
write_binary("$oe/build", "#!/bin/sh\nexit 0\n");
|
||||
chmod(0755, "$oe/build") or die "Cannot update fixture build executable: $!";
|
||||
$commit_source->('xCAT-genesis-builder/oe/build', 'empty capability query');
|
||||
my $empty_log = "$tmp/empty-query.log";
|
||||
isnt(
|
||||
run_capture(
|
||||
$empty_log, $builder, '--xcat-source', $source,
|
||||
'--architecture', 's390x',
|
||||
'--output-dir', "$tmp/empty-query-output",
|
||||
),
|
||||
0,
|
||||
'release builder rejects an empty architecture query',
|
||||
);
|
||||
like(read_binary($empty_log), qr/reported no supported Genesis architectures/,
|
||||
'empty architecture queries are reported');
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip 'rpmbuild and rpm are not installed', 18
|
||||
skip 'rpmbuild and rpm are not installed', 19
|
||||
unless command_exists('rpmbuild') && command_exists('rpm');
|
||||
exercise_packager('rpm');
|
||||
}
|
||||
@@ -276,12 +430,12 @@ SKIP: {
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip 'dpkg-deb is not installed', 12 unless command_exists('dpkg-deb');
|
||||
skip 'dpkg-deb is not installed', 14 unless command_exists('dpkg-deb');
|
||||
exercise_packager('deb');
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip 'git and dpkg-deb are not installed', 6
|
||||
skip 'git and dpkg-deb are not installed', 9
|
||||
unless command_exists('git') && command_exists('dpkg-deb');
|
||||
exercise_builder_tmpdir();
|
||||
}
|
||||
@@ -465,6 +619,10 @@ sub exercise_builder_tmpdir {
|
||||
<<'BUILD',
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
if [ "${1-}" = --list-architectures ]; then
|
||||
printf '%s\n' x86_64 s390x
|
||||
exit 0
|
||||
fi
|
||||
expected=$XCAT_GENESIS_WORK_DIR/build/tmp
|
||||
[ "${TMPDIR:-}" = "$expected" ] || exit 41
|
||||
mkdir -p "$TMPDIR/deploy"
|
||||
@@ -516,7 +674,7 @@ EXPORT
|
||||
$status = run_capture(
|
||||
$log, $builder, '--xcat-source', $source,
|
||||
'--output-dir', $output, '--work-dir', $persistent_work,
|
||||
'--format', 'deb',
|
||||
'--format', 'deb', '--architecture', 's390x',
|
||||
);
|
||||
}
|
||||
is($status, 0, 'release builder isolates the OpenEmbedded tmpdir');
|
||||
@@ -527,6 +685,18 @@ EXPORT
|
||||
is((stat($output))[2] & oct('07777'), oct('0755'),
|
||||
'release directory is readable by other users');
|
||||
my $built = validate_release($output);
|
||||
is($built->{architectures}, 'x86_64', 'isolated build keeps the target architecture');
|
||||
is($built->{architectures}, 's390x', 'isolated build keeps the target architecture');
|
||||
is($built->{formats}, 'deb', 'isolated build keeps the requested format');
|
||||
is($built->{version}, 2, 'a release containing s390x uses format version 2');
|
||||
|
||||
my $legacy_output = "$tmp/tmpdir-legacy-release";
|
||||
my $legacy_log = "$tmp/tmpdir-legacy-builder.log";
|
||||
my $legacy_status = run_capture(
|
||||
$legacy_log, $builder, '--xcat-source', $source,
|
||||
'--output-dir', $legacy_output, '--work-dir', $persistent_work,
|
||||
'--format', 'deb', '--architecture', 'x86_64',
|
||||
);
|
||||
is($legacy_status, 0, 'release builder keeps legacy targets buildable');
|
||||
my $legacy = validate_release($legacy_output);
|
||||
is($legacy->{version}, 1, 'a release without s390x uses format version 1');
|
||||
}
|
||||
|
||||
@@ -13,8 +13,10 @@ use XCAT::BuildUtils qw(
|
||||
relative_files
|
||||
write_binary
|
||||
);
|
||||
use XCAT::GenesisRelease qw(deb_package_name rpm_package_name);
|
||||
|
||||
our @EXPORT_OK = qw(
|
||||
build_package_release
|
||||
copy_tree
|
||||
dies_like
|
||||
make_export
|
||||
@@ -24,6 +26,58 @@ our @EXPORT_OK = qw(
|
||||
write_release_manifest
|
||||
);
|
||||
|
||||
sub build_package_release {
|
||||
my (%args) = @_;
|
||||
my $root = $args{root};
|
||||
my $format = $args{format};
|
||||
my @architectures = @{ $args{architectures} };
|
||||
my $release_root = "$root/release";
|
||||
|
||||
make_path($release_root);
|
||||
for my $architecture (@architectures) {
|
||||
my $export = make_export("$root/exports/$architecture", $architecture);
|
||||
my $packages = "$root/packages/$architecture";
|
||||
die "Cannot package test release for $architecture\n"
|
||||
if run_capture(
|
||||
"$root/package-$architecture.log",
|
||||
$args{packager},
|
||||
'--architecture', $architecture,
|
||||
'--export-dir', $export,
|
||||
'--output-dir', $packages,
|
||||
'--version', $args{version},
|
||||
'--release', $args{release},
|
||||
'--revision', $args{revision},
|
||||
'--source-date-epoch', $args{epoch},
|
||||
'--format', $format,
|
||||
);
|
||||
if ($format eq 'rpm') {
|
||||
my $name = rpm_package_name($architecture);
|
||||
make_path("$release_root/rpm", "$release_root/srpm");
|
||||
copy(
|
||||
"$packages/rpm/$name-$args{version}-$args{release}.noarch.rpm",
|
||||
"$release_root/rpm/$name-$args{version}-$args{release}.noarch.rpm",
|
||||
) or die $!;
|
||||
copy(
|
||||
"$packages/srpm/$name-$args{version}-$args{release}.src.rpm",
|
||||
"$release_root/srpm/$name-$args{version}-$args{release}.src.rpm",
|
||||
) or die $!;
|
||||
} else {
|
||||
my $name = deb_package_name($architecture);
|
||||
make_path("$release_root/deb");
|
||||
copy(
|
||||
"$packages/deb/${name}_$args{version}-$args{release}_all.deb",
|
||||
"$release_root/deb/${name}_$args{version}-$args{release}_all.deb",
|
||||
) or die $!;
|
||||
}
|
||||
}
|
||||
write_release_manifest(
|
||||
$release_root, $args{version}, $args{release}, $args{revision},
|
||||
$args{epoch}, join(',', @architectures), $format,
|
||||
);
|
||||
write_checksums($release_root);
|
||||
return $release_root;
|
||||
}
|
||||
|
||||
sub make_export {
|
||||
my ($directory, $architecture) = @_;
|
||||
make_path($directory);
|
||||
@@ -45,11 +99,12 @@ sub make_export {
|
||||
|
||||
sub write_release_manifest {
|
||||
my ($directory, $xcat_version, $xcat_release, $revision, $epoch,
|
||||
$architectures, $formats) = @_;
|
||||
$architectures, $formats, $manifest_version) = @_;
|
||||
$manifest_version //= 2;
|
||||
write_binary(
|
||||
"$directory/release.manifest",
|
||||
"format=xcat-genesis-packages\n"
|
||||
. "version=1\n"
|
||||
. "version=$manifest_version\n"
|
||||
. "xcat_version=$xcat_version\n"
|
||||
. "xcat_release=$xcat_release\n"
|
||||
. "xcat_revision=$revision\n"
|
||||
|
||||
Reference in New Issue
Block a user