diff --git a/t/genesis_openembedded_consumer.t b/t/genesis_openembedded_consumer.t index 36070f2..cdefb70 100644 --- a/t/genesis_openembedded_consumer.t +++ b/t/genesis_openembedded_consumer.t @@ -10,21 +10,23 @@ use FindBin; use Test::More; use lib "$FindBin::Bin/../lib"; -use lib "$FindBin::Bin/../genesis-openembedded/lib"; use lib "$FindBin::Bin/lib"; +use XCAT::BuildUtils qw( + capture_command + command_exists + digest_file + read_binary + write_binary +); use XCAT::GenesisRelease qw( architectures deb_package_name rpm_package_name ); use XCAT::GenesisReleaseTest qw( - command_exists - file_sha make_export - read_file run_capture write_checksums - write_file write_release_manifest ); @@ -74,15 +76,15 @@ sub test_rpm_consumer { my $package = "xCAT-genesis-openembedded-x86_64-$version-$release.noarch.rpm"; my $source_package = "xCAT-genesis-openembedded-x86_64-$version-$release.src.rpm"; my $output = "$tmp/rpm output"; - my $target = 'test+epel-10-' . capture('uname', '-m'); + my $target = 'test+epel-10-' . capture_command('uname', '-m'); my $run = "$target-consumer"; - my $run_repo = "$output/mockbuild-all/$run/repo/" . capture('uname', '-m'); + my $run_repo = "$output/mockbuild-all/$run/repo/" . capture_command('uname', '-m'); my $source_repo = "$output/mockbuild-all/$run/repo-src"; - my $deploy_repo = "$output/xcat-dep/rh10/" . capture('uname', '-m'); + my $deploy_repo = "$output/xcat-dep/rh10/" . capture_command('uname', '-m'); make_path($run_repo, $source_repo, $deploy_repo); - write_file("$run_repo/xCAT-genesis-openembedded-stale.noarch.rpm", 'stale'); - write_file("$source_repo/xCAT-genesis-openembedded-stale.src.rpm", 'stale'); - write_file("$deploy_repo/xCAT-genesis-openembedded-stale.noarch.rpm", 'stale'); + write_binary("$run_repo/xCAT-genesis-openembedded-stale.noarch.rpm", 'stale'); + write_binary("$source_repo/xCAT-genesis-openembedded-stale.src.rpm", 'stale'); + write_binary("$deploy_repo/xCAT-genesis-openembedded-stale.noarch.rpm", 'stale'); my $dependencies = "$tmp/rpm-dependencies"; my $scratch_repo_root = "$tmp/rpm-repo-root"; @@ -95,18 +97,18 @@ sub test_rpm_consumer { copy("$release_root/rpm/$package", "$dependencies/$name-1.noarch.rpm") or die $!; } - write_file( + write_binary( "$dependencies/xCAT-genesis-openembedded-x86_64-$version-old.noarch.rpm", 'stale OpenEmbedded RPM', ); - write_file( + write_binary( "$dependencies/xCAT-genesis-openembedded-x86_64-$version-old.src.rpm", 'stale OpenEmbedded SRPM', ); my $stub = "$tmp/perl-stub/Parallel/ForkManager.pm"; make_path("$tmp/perl-stub/Parallel"); - write_file( + write_binary( $stub, "package Parallel::ForkManager;\n" . "sub new { bless {}, shift }\n" @@ -137,7 +139,7 @@ sub test_rpm_consumer { ); is($status, 0, 'RPM repository accepts a verified Genesis release'); - is(file_sha("$deploy_repo/$package"), file_sha("$release_root/rpm/$package"), + is(digest_file("$deploy_repo/$package"), digest_file("$release_root/rpm/$package"), 'deployed RPM matches the release'); ok(!-e "$run_repo/xCAT-genesis-openembedded-stale.noarch.rpm", 'stale run RPM is removed'); @@ -145,8 +147,8 @@ sub test_rpm_consumer { 'stale source RPM is removed'); ok(!-e "$deploy_repo/xCAT-genesis-openembedded-stale.noarch.rpm", 'stale deployed RPM is removed'); - is(file_sha("$source_repo/$source_package"), - file_sha("$release_root/srpm/$source_package"), + is(digest_file("$source_repo/$source_package"), + digest_file("$release_root/srpm/$source_package"), 'source RPM matches the release'); ok(-f "$deploy_repo/repodata/repomd.xml", 'RPM repository metadata is generated'); ok(-f "$deploy_repo/xCAT-genesis-base-x86_64-1.noarch.rpm", @@ -155,7 +157,7 @@ sub test_rpm_consumer { 'stale OpenEmbedded RPM is not collected'); ok(!-e "$source_repo/xCAT-genesis-openembedded-x86_64-$version-old.src.rpm", 'stale OpenEmbedded SRPM is not collected'); - like(read_file("$output/mockbuild-all/$run/summary.txt"), qr/^copied_rpms=15$/m, + like(read_binary("$output/mockbuild-all/$run/summary.txt"), qr/^copied_rpms=15$/m, 'release RPM is counted alongside required dependencies'); } @@ -166,7 +168,7 @@ sub test_deb_consumer { my $input = "$apt_root/ubuntu24.04"; my $dummy = "$tmp/dummy-deb"; make_path("$dummy/DEBIAN", $input, "$apt_root/pool/main/noble"); - write_file( + write_binary( "$dummy/DEBIAN/control", "Package: xcat-genesis-base-amd64\nVersion: 1\nArchitecture: all\n" . "Maintainer: xCAT \n" @@ -177,8 +179,8 @@ sub test_deb_consumer { "$tmp/dummy-deb.log", 'dpkg-deb', '--root-owner-group', '--build', $dummy, "$input/xcat-genesis-base-amd64_1_all.deb", ); - write_file("$input/xcat-genesis-openembedded-stale.deb", 'stale'); - write_file("$apt_root/pool/main/noble/xcat-genesis-openembedded-old.deb", 'stale'); + write_binary("$input/xcat-genesis-openembedded-stale.deb", 'stale'); + write_binary("$apt_root/pool/main/noble/xcat-genesis-openembedded-old.deb", 'stale'); local $ENV{SOURCE_DATE_EPOCH} = $epoch; my $log = "$tmp/deb-consumer.log"; @@ -196,23 +198,23 @@ sub test_deb_consumer { my $ppc64el = "$apt_root/dists/noble/main/binary-ppc64el/Packages"; is($status, 0, 'APT repository accepts a verified Genesis release'); - is(file_sha($pool_package), file_sha("$release_root/deb/$package"), + is(digest_file($pool_package), digest_file("$release_root/deb/$package"), 'pooled DEB matches the release'); ok(!-e "$apt_root/pool/main/noble/xcat-genesis-openembedded-old.deb", 'stale pooled DEB is removed'); - like(read_file($amd64), qr/^Package: xcat-genesis-openembedded-x86-64$/m, + like(read_binary($amd64), qr/^Package: xcat-genesis-openembedded-x86-64$/m, 'all-architecture DEB is indexed for amd64'); - like(read_file($ppc64el), qr/^Package: xcat-genesis-openembedded-x86-64$/m, + like(read_binary($ppc64el), qr/^Package: xcat-genesis-openembedded-x86-64$/m, 'all-architecture DEB is indexed for ppc64el'); ok(-f "$apt_root/dists/noble/Release", 'APT Release metadata is generated'); - like(read_file($amd64), qr/^Package: xcat-genesis-base-amd64$/m, + like(read_binary($amd64), qr/^Package: xcat-genesis-base-amd64$/m, 'legacy Genesis DEB remains available'); ok(!-e "$apt_root/pool/main/noble/xcat-genesis-openembedded-stale.deb", 'stale OpenEmbedded DEB is not collected'); my $collision = "$tmp/apt-collision"; make_path("$collision/ubuntu24.04"); - write_file("$collision/ubuntu24.04/$package", 'different'); + write_binary("$collision/ubuntu24.04/$package", 'different'); my $collision_log = "$tmp/deb-collision.log"; my $collision_status = run_capture( $collision_log, @@ -224,19 +226,19 @@ sub test_deb_consumer { 'ubuntu24.04', ); is($collision_status, 0, 'verified release replaces a stale source package'); - is(file_sha("$collision/pool/main/noble/$package"), - file_sha("$release_root/deb/$package"), + is(digest_file("$collision/pool/main/noble/$package"), + digest_file("$release_root/deb/$package"), 'pooled package still matches the verified release'); } sub test_partial_rpm_release { my $release_root = make_package_release("$tmp/rpm-partial", 'rpm', 'x86_64'); my $output = "$tmp/partial-output"; - my $target = 'test+epel-10-' . capture('uname', '-m'); - my $deployed = "$output/xcat-dep/rh10/" . capture('uname', '-m'); + my $target = 'test+epel-10-' . capture_command('uname', '-m'); + my $deployed = "$output/xcat-dep/rh10/" . capture_command('uname', '-m'); my $existing = "$deployed/xCAT-genesis-base-existing.noarch.rpm"; make_path($deployed); - write_file($existing, 'existing release'); + write_binary($existing, 'existing release'); my $log = "$tmp/rpm-partial.log"; my $status = run_capture( @@ -262,7 +264,7 @@ sub test_partial_deb_release { my $pool = "$apt_root/pool/main/noble"; my $existing = "$pool/xcat-genesis-base-existing.deb"; make_path($pool); - write_file($existing, 'existing release'); + write_binary($existing, 'existing release'); my $log = "$tmp/deb-partial.log"; my $status = run_capture( @@ -327,12 +329,3 @@ sub make_package_release { write_checksums($release_root); return $release_root; } - -sub capture { - my (@command) = @_; - open(my $fh, '-|', @command) or die $!; - my $output = <$fh>; - close($fh) or die $!; - chomp($output); - return $output; -} diff --git a/t/genesis_openembedded_release.t b/t/genesis_openembedded_release.t index 2c3ba85..46dcab3 100644 --- a/t/genesis_openembedded_release.t +++ b/t/genesis_openembedded_release.t @@ -9,8 +9,13 @@ use FindBin; use Test::More; use lib "$FindBin::Bin/../lib"; -use lib "$FindBin::Bin/../genesis-openembedded/lib"; use lib "$FindBin::Bin/lib"; +use XCAT::BuildUtils qw( + command_exists + digest_file + read_binary + write_binary +); use XCAT::GenesisRelease qw( architectures deb_package_name @@ -23,15 +28,11 @@ use XCAT::GenesisRelease qw( verify_release_file ); use XCAT::GenesisReleaseTest qw( - command_exists copy_tree dies_like - file_sha make_export - read_file run_capture write_checksums - write_file write_release_manifest ); @@ -75,12 +76,12 @@ dies_like(sub { validate_export($missing, 'x86_64') }, qr/missing image\.vex\.js 'missing release evidence fails'); my $corrupt = make_export("$tmp/corrupt", 'x86_64'); -write_file("$corrupt/kernel", 'changed'); +write_binary("$corrupt/kernel", 'changed'); dies_like(sub { validate_export($corrupt, 'x86_64') }, qr/Checksum mismatch for kernel/, 'corrupt payload fails'); my $unexpected = make_export("$tmp/unexpected", 'x86_64'); -write_file("$unexpected/extra", 'not part of the export'); +write_binary("$unexpected/extra", 'not part of the export'); write_checksums($unexpected); dies_like(sub { validate_export($unexpected, 'x86_64') }, qr/Unexpected Genesis export file/, 'unlisted export files fail'); @@ -100,9 +101,9 @@ make_path("$release_dir/rpm", "$release_dir/srpm", "$release_dir/deb"); for my $architecture (qw(x86_64 ppc64le)) { my $rpm = rpm_package_name($architecture); my $deb = deb_package_name($architecture); - write_file("$release_dir/rpm/$rpm-$version-$release.noarch.rpm", "rpm $architecture"); - write_file("$release_dir/srpm/$rpm-$version-$release.src.rpm", "srpm $architecture"); - write_file("$release_dir/deb/${deb}_${version}-${release}_all.deb", "deb $architecture"); + write_binary("$release_dir/rpm/$rpm-$version-$release.noarch.rpm", "rpm $architecture"); + write_binary("$release_dir/srpm/$rpm-$version-$release.src.rpm", "srpm $architecture"); + write_binary("$release_dir/deb/${deb}_${version}-${release}_all.deb", "deb $architecture"); } write_release_manifest( $release_dir, $version, $release, $revision, $epoch, @@ -115,7 +116,7 @@ my $qualified_release = "$tmp/qualified-release"; my $qualified_release_name = '1+deb~1'; my $qualified_deb = deb_package_name('x86_64'); make_path("$qualified_release/deb"); -write_file( +write_binary( "$qualified_release/deb/${qualified_deb}_${version}-${qualified_release_name}_all.deb", 'deb x86_64', ); @@ -131,7 +132,7 @@ my $verified_copy = "$tmp/verified-copy.rpm"; copy("$release_dir/$verified_relative", $verified_copy) or die $!; ok(verify_release_file($verified_checksums, $verified_relative, $verified_copy), 'collected package matches the verified release'); -write_file($verified_copy, 'changed after verification'); +write_binary($verified_copy, 'changed after verification'); dies_like( sub { verify_release_file($verified_checksums, $verified_relative, $verified_copy) }, qr/Collected release file checksum mismatch/, @@ -148,9 +149,9 @@ make_path("$complete_release/rpm", "$complete_release/srpm", "$complete_release/ for my $architecture (architectures()) { my $rpm = rpm_package_name($architecture); my $deb = deb_package_name($architecture); - write_file("$complete_release/rpm/$rpm-$version-$release.noarch.rpm", "rpm $architecture"); - write_file("$complete_release/srpm/$rpm-$version-$release.src.rpm", "srpm $architecture"); - write_file("$complete_release/deb/${deb}_${version}-${release}_all.deb", "deb $architecture"); + write_binary("$complete_release/rpm/$rpm-$version-$release.noarch.rpm", "rpm $architecture"); + write_binary("$complete_release/srpm/$rpm-$version-$release.src.rpm", "srpm $architecture"); + write_binary("$complete_release/deb/${deb}_${version}-${release}_all.deb", "deb $architecture"); } write_release_manifest( $complete_release, $version, $release, $revision, $epoch, @@ -163,7 +164,7 @@ my $deb_only_release = "$tmp/deb-only-release"; make_path("$deb_only_release/deb"); for my $architecture (architectures()) { my $deb = deb_package_name($architecture); - write_file("$deb_only_release/deb/${deb}_${version}-${release}_all.deb", "deb $architecture"); + write_binary("$deb_only_release/deb/${deb}_${version}-${release}_all.deb", "deb $architecture"); } write_release_manifest( $deb_only_release, $version, $release, $revision, $epoch, @@ -173,12 +174,12 @@ write_checksums($deb_only_release); my $verify_all_log = "$tmp/verify-all.log"; isnt(run_capture($verify_all_log, $verifier, $deb_only_release), 0, 'all-format verification rejects a single-format release'); -like(read_file($verify_all_log), qr/Release does not contain rpm packages/, +like(read_binary($verify_all_log), qr/Release does not contain rpm packages/, 'all-format failure names the missing format'); my $bad_release = "$tmp/bad-release"; copy_tree($release_dir, $bad_release); -write_file("$bad_release/rpm/stale.rpm", 'stale'); +write_binary("$bad_release/rpm/stale.rpm", 'stale'); write_checksums($bad_release); dies_like(sub { validate_release($bad_release) }, qr/Unexpected Genesis release artifact/, 'stale package fails'); @@ -194,9 +195,9 @@ SKIP: { skip 'git is not installed', 2 unless command_exists('git'); my $source = "$tmp/dirty-xcat-core"; make_path("$source/xCAT-genesis-builder/oe"); - write_file("$source/Version", "$version\n"); - write_file("$source/xCAT-genesis-builder/oe/build", "#!/bin/sh\nexit 99\n"); - write_file("$source/xCAT-genesis-builder/oe/export", "#!/bin/sh\nexit 99\n"); + 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/export", "#!/bin/sh\nexit 99\n"); for my $command ( [ 'git', '-C', $source, 'init', '-q' ], [ 'git', '-C', $source, 'add', '.' ], @@ -206,7 +207,7 @@ SKIP: { die "Cannot prepare test repository\n" if run_capture("$tmp/git-fixture.log", @{$command}); } - write_file("$source/untracked", "not part of the commit\n"); + write_binary("$source/untracked", "not part of the commit\n"); my $log = "$tmp/dirty-source.log"; isnt( run_capture( @@ -216,7 +217,7 @@ SKIP: { 0, 'release builder rejects untracked source files', ); - like(read_file($log), qr/xcat-core checkout is not clean/, + like(read_binary($log), qr/xcat-core checkout is not clean/, 'dirty checkout failure is explicit'); } @@ -277,11 +278,11 @@ sub exercise_packager { $relative = "deb/xcat-genesis-openembedded-x86-64_${version}-${release}_all.deb"; } ok(-f "$first/$relative", "$format binary exists"); - is(file_sha("$first/$relative"), file_sha("$second/$relative"), + is(digest_file("$first/$relative"), digest_file("$second/$relative"), "$format binary is reproducible across umasks"); if ($format eq 'rpm') { ok(-f "$first/$source_relative", 'source RPM exists'); - is(file_sha("$first/$source_relative"), file_sha("$second/$source_relative"), + is(digest_file("$first/$source_relative"), digest_file("$second/$source_relative"), 'source RPM is reproducible across umasks'); } @@ -299,16 +300,16 @@ sub exercise_packager { if ($format eq 'rpm') { is(run_capture($contents_log, 'rpm', '-qpl', "$first/$relative"), 0, 'RPM payload can be listed'); - like(read_file($contents_log), + like(read_binary($contents_log), qr{/opt/xcat/share/xcat/netboot/genesis-openembedded/x86_64/kernel}, 'RPM uses the OpenEmbedded staging namespace'); is(run_capture($contents_log, 'rpm', '-qp', '--scripts', "$first/$relative"), 0, 'RPM script metadata can be read'); - is(read_file($contents_log), '', 'RPM has no package scripts'); + is(read_binary($contents_log), '', 'RPM has no package scripts'); } else { is(run_capture($contents_log, 'dpkg-deb', '-c', "$first/$relative"), 0, 'DEB payload can be listed'); - like(read_file($contents_log), + like(read_binary($contents_log), qr{/opt/xcat/share/xcat/netboot/genesis-openembedded/x86_64/kernel}, 'DEB uses the OpenEmbedded staging namespace'); my $control = "$tmp/deb-control"; @@ -363,8 +364,8 @@ sub exercise_builder_tmpdir { my $source = "$tmp/tmpdir-xcat-core"; my $oe = "$source/xCAT-genesis-builder/oe"; make_path($oe); - write_file("$source/Version", "$version\n"); - write_file( + write_binary("$source/Version", "$version\n"); + write_binary( "$oe/build", <<'BUILD', #!/bin/sh @@ -374,7 +375,7 @@ expected=$XCAT_GENESIS_WORK_DIR/build/tmp mkdir -p "$TMPDIR/deploy" BUILD ); - write_file( + write_binary( "$oe/export", <<'EXPORT', #!/bin/sh @@ -424,7 +425,7 @@ EXPORT ); } is($status, 0, 'release builder isolates the OpenEmbedded tmpdir'); - unlike(read_file($log), qr/Invalid OpenEmbedded deploy directory/, + unlike(read_binary($log), qr/Invalid OpenEmbedded deploy directory/, 'release builder finds the configured deploy directory'); ok(-d "$persistent_work/openembedded/build/tmp", 'release builder preserves the requested work directory'); diff --git a/t/lib/XCAT/GenesisReleaseTest.pm b/t/lib/XCAT/GenesisReleaseTest.pm index a4f83ee..36716ab 100644 --- a/t/lib/XCAT/GenesisReleaseTest.pm +++ b/t/lib/XCAT/GenesisReleaseTest.pm @@ -3,25 +3,23 @@ package XCAT::GenesisReleaseTest; use strict; use warnings; -use Digest::SHA (); use Exporter qw(import); use File::Basename qw(dirname); use File::Copy qw(copy); -use File::Find qw(find); use File::Path qw(make_path); -use File::Spec; use Test::More (); +use XCAT::BuildUtils qw( + digest_manifest + relative_files + write_binary +); our @EXPORT_OK = qw( - command_exists copy_tree dies_like - file_sha make_export - read_file run_capture write_checksums - write_file write_release_manifest ); @@ -39,7 +37,7 @@ sub make_export { "format=xcat-genesis\nversion=1\narchitecture=$architecture\n", ); $content{'fw_jump.elf'} = 'firmware' if $architecture eq 'riscv64'; - write_file("$directory/$_", $content{$_}) for sort keys %content; + write_binary("$directory/$_", $content{$_}) for sort keys %content; write_checksums($directory); return $directory; } @@ -47,7 +45,7 @@ sub make_export { sub write_release_manifest { my ($directory, $xcat_version, $xcat_release, $revision, $epoch, $architectures, $formats) = @_; - write_file( + write_binary( "$directory/release.manifest", "format=xcat-genesis-packages\n" . "version=1\n" @@ -63,66 +61,21 @@ sub write_release_manifest { sub write_checksums { my ($directory) = @_; unlink("$directory/SHA256SUMS") if -e "$directory/SHA256SUMS"; - my @files; - find( - { - no_chdir => 1, - wanted => sub { - return unless -f $_ && !-l $_; - my $relative = File::Spec->abs2rel($File::Find::name, $directory); - $relative =~ tr{\\}{/}; - push(@files, $relative); - }, - }, - $directory, + my @files = relative_files($directory); + write_binary( + "$directory/SHA256SUMS", + digest_manifest($directory, 'sha256', @files), ); - my $content = join('', map { file_sha("$directory/$_") . " $_\n" } sort @files); - write_file("$directory/SHA256SUMS", $content); -} - -sub file_sha { - my ($path) = @_; - open(my $fh, '<:raw', $path) or die $!; - my $digest = Digest::SHA->new(256)->addfile($fh)->hexdigest; - close($fh) or die $!; - return $digest; } sub copy_tree { my ($source, $destination) = @_; make_path($destination); - find( - { - no_chdir => 1, - wanted => sub { - return if $File::Find::name eq $source; - my $relative = File::Spec->abs2rel($File::Find::name, $source); - my $target = "$destination/$relative"; - if (-d $File::Find::name) { - make_path($target); - } elsif (-f $File::Find::name) { - make_path(dirname($target)); - copy($File::Find::name, $target) or die $!; - } - }, - }, - $source, - ); -} - -sub write_file { - my ($path, $content) = @_; - open(my $fh, '>:raw', $path) or die $!; - print {$fh} $content or die $!; - close($fh) or die $!; -} - -sub command_exists { - my ($command) = @_; - for my $directory (File::Spec->path()) { - return 1 if -x "$directory/$command"; + for my $relative (relative_files($source)) { + my $target = "$destination/$relative"; + make_path(dirname($target)); + copy("$source/$relative", $target) or die $!; } - return 0; } sub run_capture { @@ -140,15 +93,6 @@ sub run_capture { return $? >> 8; } -sub read_file { - my ($path) = @_; - open(my $fh, '<:raw', $path) or die $!; - local $/; - my $content = <$fh> // ''; - close($fh) or die $!; - return $content; -} - sub dies_like { my ($code, $pattern, $name) = @_; my $error = '';