From b391007e0e402b97dd6ede273a3cabf0ba8ebf5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Mon, 24 Aug 2026 20:08:04 -0300 Subject: [PATCH] test(genesis): define shared repository activation --- .../unit/genesis_openembedded_activation.t | 93 +++++++++++++++ xCAT-test/unit/go_xcat_common_repository.t | 112 ++++++++++++++++++ xCAT-test/unit/mknb_exported_genesis.t | 52 +++++++- xCAT-test/unit/xcat_release_package.t | 9 ++ .../unit/xcatconfig_genesis_openembedded.t | 43 +++++++ 5 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 xCAT-test/unit/genesis_openembedded_activation.t create mode 100644 xCAT-test/unit/go_xcat_common_repository.t create mode 100644 xCAT-test/unit/xcatconfig_genesis_openembedded.t diff --git a/xCAT-test/unit/genesis_openembedded_activation.t b/xCAT-test/unit/genesis_openembedded_activation.t new file mode 100644 index 000000000..ca8a5e34e --- /dev/null +++ b/xCAT-test/unit/genesis_openembedded_activation.t @@ -0,0 +1,93 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Spec; +use FindBin; +use Test::More; + +my $root = File::Spec->rel2abs(File::Spec->catdir($FindBin::Bin, '..', '..')); + +sub read_file { + my ($relative) = @_; + my $path = File::Spec->catfile($root, split m{/}, $relative); + open(my $fh, '<', $path) or die "open $path: $!"; + my $content = do { local $/; <$fh> }; + close($fh) or die "close $path: $!"; + return $content; +} + +my $rpm_spec = read_file('xCAT/xCAT.spec'); +like( + $rpm_spec, + qr/^Recommends:\s+xCAT-genesis-openembedded-x86_64$/m, + 'RPM installations recommend the first-class x86_64 image', +); +like( + $rpm_spec, + qr/^Recommends:\s+xCAT-genesis-openembedded-ppc64le$/m, + 'RPM installations recommend the first-class ppc64le image', +); +unlike( + $rpm_spec, + qr/^Requires:\s+xCAT-genesis-openembedded-/m, + 'missing OpenEmbedded images do not block an RPM upgrade', +); + +my $deb_control = read_file('xCAT/debian/control'); +like( + $deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-x86-64\b/m, + 'DEB installations recommend the first-class x86_64 image', +); +like( + $deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-ppc64le\b/m, + 'DEB installations recommend the first-class ppc64le image', +); +unlike( + $deb_control, + qr/^Depends:.*\bxcat-genesis-openembedded-/m, + 'missing OpenEmbedded images do not block a DEB upgrade', +); + +my $sn_rpm_spec = read_file('xCATsn/xCATsn.spec'); +like( + $sn_rpm_spec, + qr/^Recommends:\s+xCAT-genesis-openembedded-x86_64$/m, + 'RPM service nodes recommend the first-class x86_64 image', +); +like( + $sn_rpm_spec, + qr/^Recommends:\s+xCAT-genesis-openembedded-ppc64le$/m, + 'RPM service nodes recommend the first-class ppc64le image', +); + +my $sn_deb_control = read_file('xCATsn/debian/control'); +like( + $sn_deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-x86-64\b/m, + 'DEB service nodes recommend the first-class x86_64 image', +); +like( + $sn_deb_control, + qr/^Recommends:.*\bxcat-genesis-openembedded-ppc64le\b/m, + 'DEB service nodes recommend the first-class ppc64le image', +); + +my $go_xcat = read_file('xCAT-server/share/xcat/tools/go-xcat'); +for my $architecture (qw(x86 x86_64 ppc64 ppc64le armv7hf aarch64 riscv64)) { + like( + $go_xcat, + qr/\bxCAT-genesis-openembedded-\Q$architecture\E\b/, + "go-xcat removes the $architecture RPM image", + ); + (my $deb_architecture = $architecture) =~ tr/_/-/; + like( + $go_xcat, + qr/\bxcat-genesis-openembedded-\Q$deb_architecture\E\b/, + "go-xcat removes the $architecture DEB image", + ); +} + +done_testing(); diff --git a/xCAT-test/unit/go_xcat_common_repository.t b/xCAT-test/unit/go_xcat_common_repository.t new file mode 100644 index 000000000..c3aa05428 --- /dev/null +++ b/xCAT-test/unit/go_xcat_common_repository.t @@ -0,0 +1,112 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +my $go_xcat = "$FindBin::Bin/../../xCAT-server/share/xcat/tools/go-xcat"; +my $tmpdir = tempdir(CLEANUP => 1); +my $driver = "$tmpdir/driver.sh"; + +open(my $driver_fh, '>', $driver) or die "open $driver: $!"; +print {$driver_fh} <<'DRIVER'; +#!/bin/bash +set -euo pipefail + +export GO_XCAT_LIBRARY_ONLY=1 +source "$GO_XCAT_SOURCE" + +TMP_DIR=$TEST_TMP +GO_XCAT_DEFAULT_BASE_URL=https://repo.example.invalid +GO_XCAT_DEP_REPOSITORY_IDS=(xcat-dep) + +yum() { :; } + +download_file() { + printf '%s\n' "$1" >>"$DOWNLOAD_LOG" + [[ ${COMMON_PRESENT:-0} == 1 ]] || return 1 + : >"$2" +} + +add_repo_by_url_yum_or_zypper() { + printf '%s %s\n' "$1" "$2" >>"$ADD_LOG" +} + +add_xcat_dep_common_repo_yum_or_zypper "$@" +printf '%s\n' "${GO_XCAT_DEP_REPOSITORY_IDS[*]}" >"$ID_LOG" +DRIVER +close($driver_fh) or die "close $driver: $!"; +chmod(0755, $driver) or die "chmod $driver: $!"; + +sub run_case { + my ($name, $present, @arguments) = @_; + my $case_dir = "$tmpdir/$name"; + make_path($case_dir); + local %ENV = ( + %ENV, + ADD_LOG => "$case_dir/add.log", + COMMON_PRESENT => $present, + DOWNLOAD_LOG => "$case_dir/download.log", + GO_XCAT_SOURCE => $go_xcat, + ID_LOG => "$case_dir/id.log", + TEST_TMP => $case_dir, + ); + my $status = system('bash', $driver, @arguments); + return ($status >> 8, $case_dir); +} + +sub read_file { + my ($path) = @_; + return '' unless -f $path; + open(my $fh, '<', $path) or die "open $path: $!"; + my $content = do { local $/; <$fh> }; + close($fh) or die "close $path: $!"; + return $content; +} + +my ($status, $case_dir) = run_case('remote-present', 1, '', 'latest'); +is($status, 0, 'an available common repository is accepted'); +is( + read_file("$case_dir/download.log"), + "https://repo.example.invalid/yum/latest/xcat-dep/common/repodata/repomd.xml\n", + 'the default repository is probed before it is enabled', +); +is( + read_file("$case_dir/add.log"), + "https://repo.example.invalid/yum/latest/xcat-dep/common xcat-dep-common\n", + 'the common repository uses its own repository ID', +); +is(read_file("$case_dir/id.log"), "xcat-dep xcat-dep-common\n", + 'common packages are included in repository listings'); + +($status, $case_dir) = run_case('remote-missing', 0, '', '2.18'); +is($status, 0, 'a release without the common repository remains usable'); +is(read_file("$case_dir/add.log"), '', 'a missing common repository is not enabled'); +is(read_file("$case_dir/id.log"), "xcat-dep\n", + 'legacy package listings remain unchanged when common is absent'); + +($status, $case_dir) = run_case( + 'repo-file', 1, 'https://repo.example.invalid/custom/xcat-dep.repo', 'latest' +); +is($status, 0, 'a custom repository file remains supported'); +is(read_file("$case_dir/download.log"), '', + 'the common URL is not guessed from a custom repository file'); +is(read_file("$case_dir/add.log"), '', + 'a custom repository file does not enable an unrelated repository'); + +my $local_root = "$tmpdir/local-repository"; +make_path("$local_root/common/repodata"); +open(my $repomd_fh, '>', "$local_root/common/repodata/repomd.xml") or die $!; +close($repomd_fh) or die $!; +($status, $case_dir) = run_case('local-present', 0, $local_root, 'latest'); +is($status, 0, 'a local common repository is accepted'); +is( + read_file("$case_dir/add.log"), + "$local_root/common xcat-dep-common\n", + 'the local common repository is enabled beside the distribution repository', +); + +done_testing(); diff --git a/xCAT-test/unit/mknb_exported_genesis.t b/xCAT-test/unit/mknb_exported_genesis.t index c864b4a2e..51b873f8c 100644 --- a/xCAT-test/unit/mknb_exported_genesis.t +++ b/xCAT-test/unit/mknb_exported_genesis.t @@ -4,7 +4,7 @@ use warnings; ## no critic (Modules::RequireFilenameMatchesPackage, TestingAndDebugging::ProhibitNoStrict, TestingAndDebugging::ProhibitNoWarnings) use Digest::SHA qw(sha256_hex); -use File::Path qw(make_path); +use File::Path qw(make_path remove_tree); use File::Temp qw(tempdir); use FindBin; use Test::More; @@ -332,4 +332,54 @@ is( 'an incomplete marked export keeps the published initramfs', ); +my $selection_root = "$tmpdir/selection-root"; +my $legacy_x86 = "$selection_root/share/xcat/netboot/genesis/x86_64"; +my $openembedded_x86 = + "$selection_root/share/xcat/netboot/genesis-openembedded/x86_64"; +make_path("$legacy_x86/fs", $openembedded_x86); + +my ($selected_dir, $selected_arch, $selected_type) = + xCAT_plugin::mknb::_select_genesis_source($selection_root, 'x86_64'); +is($selected_dir, $openembedded_x86, + 'an installed OpenEmbedded export takes precedence over legacy Genesis'); +is($selected_arch, 'x86_64', 'the OpenEmbedded x86_64 name is unchanged'); +is($selected_type, 'openembedded', 'the selected source is identified'); + +remove_tree($openembedded_x86); +($selected_dir, $selected_arch, $selected_type) = + xCAT_plugin::mknb::_select_genesis_source($selection_root, 'x86_64'); +is($selected_dir, $legacy_x86, 'legacy Genesis remains the fallback'); +is($selected_arch, 'x86_64', 'the legacy x86_64 name is unchanged'); +is($selected_type, 'legacy', 'the fallback source is identified'); + +my $openembedded_ppc64le = + "$selection_root/share/xcat/netboot/genesis-openembedded/ppc64le"; +my $legacy_ppc64 = "$selection_root/share/xcat/netboot/genesis/ppc64"; +make_path($openembedded_ppc64le, "$legacy_ppc64/fs"); +($selected_dir, $selected_arch, $selected_type) = + xCAT_plugin::mknb::_select_genesis_source($selection_root, 'ppc64le'); +is($selected_dir, $openembedded_ppc64le, + 'ppc64le selects its exact OpenEmbedded export'); +is($selected_arch, 'ppc64le', 'ppc64le is not rewritten to ppc64'); +is($selected_type, 'openembedded', 'ppc64le uses the OpenEmbedded source'); + +($selected_dir, $selected_arch, $selected_type) = + xCAT_plugin::mknb::_select_genesis_source($selection_root, 'ppc64el'); +is($selected_dir, $openembedded_ppc64le, + 'the Debian spelling resolves to the canonical ppc64le export'); +is($selected_arch, 'ppc64le', 'the canonical architecture name is returned'); + +remove_tree($openembedded_ppc64le); +($selected_dir, $selected_arch, $selected_type) = + xCAT_plugin::mknb::_select_genesis_source($selection_root, 'ppc64le'); +is($selected_dir, $legacy_ppc64, + 'ppc64le falls back to the old combined POWER image when needed'); +is($selected_arch, 'ppc64', 'only the legacy fallback uses the old ppc64 name'); +is($selected_type, 'legacy', 'the POWER fallback is identified as legacy'); + +my $unsafe = xCAT_plugin::mknb::_select_genesis_source( + $selection_root, '../../outside' +); +is($unsafe, undef, 'unsupported architecture names cannot escape the image root'); + done_testing(); diff --git a/xCAT-test/unit/xcat_release_package.t b/xCAT-test/unit/xcat_release_package.t index 1f4cbcf99..44d369810 100644 --- a/xCAT-test/unit/xcat_release_package.t +++ b/xCAT-test/unit/xcat_release_package.t @@ -16,6 +16,7 @@ like($spec, qr/^BuildArch:\s+noarch$/m, 'package is architecture independent'); like($spec, qr/^Requires:\s+dnf$/m, 'package is limited to DNF-based systems'); like($spec, qr/^%config\(noreplace\) .*xcat-core\.repo$/m, 'core repo preserves local changes'); like($spec, qr/^%config\(noreplace\) .*xcat-dep\.repo$/m, 'dependency repo preserves local changes'); +like($spec, qr/^%config\(noreplace\) .*xcat-dep-common\.repo$/m, 'common dependency repo preserves local changes'); like($spec, qr{RPM-GPG-KEY-xCAT}, 'package installs the signing key'); my $core = read_file('xCAT-release/xcat-core.repo'); @@ -34,6 +35,14 @@ like( 'dependency repo follows the DNF release and architecture variables' ); +my $common_dep = read_file('xCAT-release/xcat-dep-common.repo'); +assert_repo_security($common_dep, 'common dependency'); +like( + $common_dep, + qr{^baseurl=https://xcat\.org/files/xcat/repos/yum/latest/xcat-dep/common$}m, + 'common dependency repo is independent of the management-node distribution' +); + my $key = read_file('xCAT-release/RPM-GPG-KEY-xCAT'); like($key, qr/^-----BEGIN PGP PUBLIC KEY BLOCK-----$/m, 'signing key is ASCII armored'); is( diff --git a/xCAT-test/unit/xcatconfig_genesis_openembedded.t b/xCAT-test/unit/xcatconfig_genesis_openembedded.t new file mode 100644 index 000000000..31ec17c34 --- /dev/null +++ b/xCAT-test/unit/xcatconfig_genesis_openembedded.t @@ -0,0 +1,43 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +my $source = "$FindBin::Bin/../../xCAT-server/sbin/xcatconfig"; +open(my $source_fh, '<', $source) or die "open $source: $!"; +my $content = do { local $/; <$source_fh> }; +close($source_fh) or die "close $source: $!"; + +my ($routine) = $content =~ /^(sub _installed_genesis_architectures\s*\{.*?^\})/ms; +BAIL_OUT('could not extract _installed_genesis_architectures from xcatconfig') + unless $routine; +eval $routine; +BAIL_OUT("could not load _installed_genesis_architectures: $@") if $@; + +my $tmpdir = tempdir(CLEANUP => 1); +make_path( + "$tmpdir/share/xcat/netboot/genesis/ppc64/fs", + "$tmpdir/share/xcat/netboot/genesis/x86_64/fs", + "$tmpdir/share/xcat/netboot/genesis-openembedded/aarch64", + "$tmpdir/share/xcat/netboot/genesis-openembedded/ppc64le", + "$tmpdir/share/xcat/netboot/genesis-openembedded/x86_64", +); + +is_deeply( + [ _installed_genesis_architectures($tmpdir) ], + [ qw(aarch64 ppc64 ppc64le x86_64) ], + 'xcatconfig finds exact OpenEmbedded architectures and installed legacy images', +); + +make_path("$tmpdir/share/xcat/netboot/genesis-openembedded/unsupported"); +is_deeply( + [ _installed_genesis_architectures($tmpdir) ], + [ qw(aarch64 ppc64 ppc64le x86_64) ], + 'unknown directories are not passed to mknb', +); + +done_testing();