diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 68fb5b890..574127a02 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -9,7 +9,7 @@ jobs: - name: Refresh package indexes run: sudo apt-get update - name: Install dependencies - run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests bats bubblewrap gawk mawk rpm rsyslog build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libyaml-pp-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common grub-common grub-efi-amd64-bin squashfs-tools pigz + run: sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --no-install-suggests bats bubblewrap cpio gawk mawk rpm rsyslog build-essential fakeroot reprepro devscripts debhelper libcapture-tiny-perl libfile-slurper-perl libjson-perl libyaml-pp-perl libparallel-forkmanager-perl libsoap-lite-perl libdbi-perl libdbd-sqlite3-perl libcgi-pm-perl quilt openssh-server dpkg looptools genometools software-properties-common grub-common grub-efi-amd64-bin squashfs-tools pigz - name: Enable postscript test namespaces run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Install lifecycle test dependencies @@ -19,6 +19,9 @@ jobs: - name: Prepare xCAT syslog directory if: ${{ !cancelled() }} run: sudo install -d -o syslog -g adm -m 0750 /var/log/xcat + - name: Run Ubuntu image fixture tests + if: ${{ !cancelled() }} + run: prove xCAT-test/native/ubuntu_live_media_guardrails.t - name: Run tests if: ${{ !cancelled() }} run: perl github_action_xcat_test.pl diff --git a/xCAT-test/lib/XCAT/Test/UbuntuImage.pm b/xCAT-test/lib/XCAT/Test/UbuntuImage.pm new file mode 100644 index 000000000..bf708962f --- /dev/null +++ b/xCAT-test/lib/XCAT/Test/UbuntuImage.pm @@ -0,0 +1,64 @@ +package XCAT::Test::UbuntuImage; + +use strict; +use warnings; + +use Capture::Tiny qw(capture_merged); +use File::Basename qw(dirname); +use File::Path qw(make_path); +use File::Slurper qw(read_binary write_binary); +use File::Temp qw(tempdir); +use XCAT::Test::File qw(repo_path); + +sub new { + my ($class) = @_; + for my $tool (qw(bwrap timeout cpio gzip perl)) { + die "Ubuntu image tests require $tool\n" + unless grep { -x "$_/$tool" } qw(/usr/bin /bin /usr/sbin /sbin); + } + my $self = bless { root => tempdir(CLEANUP => 1) }, $class; + make_path(map { "$self->{root}/$_" } qw(etc install tmp work/bin work/db)); + return $self; +} + +sub write { + my ($self, $file, $content) = @_; + my $path = "$self->{root}/$file"; + make_path(dirname($path)); + write_binary($path, $content); + return $path; +} + +sub read { + my ($self, $file) = @_; + return read_binary("$self->{root}/$file"); +} + +sub run { + my ($self, @command) = @_; + my $root = $self->{root}; + my @sandbox = ( + 'bwrap', '--unshare-all', '--die-with-parent', '--new-session', + '--tmpfs', '/', + (map { ('--ro-bind', $_, $_) } grep { -d $_ } qw(/usr /bin /sbin /lib /lib64)), + '--dev', '/dev', '--proc', '/proc', '--dir', '/run', '--dir', '/sys', + '--dir', '/opt', + '--bind', "$root/etc", '/etc', '--bind', "$root/install", '/install', + '--bind', "$root/tmp", '/tmp', '--bind', "$root/work", '/work', + '--ro-bind', repo_path('.'), '/repo', '--chdir', '/work', + '--setenv', 'PATH', '/work/bin:/usr/bin:/bin:/usr/sbin:/sbin', + '--setenv', 'LC_ALL', 'C', '--setenv', 'XCATROOT', '/repo', + '--setenv', 'XCATCFG', 'SQLite:/work/db', + '--setenv', 'PERL5LIB', '/repo/perl-xCAT:/repo/xCAT-server/lib/perl', + ); + push @sandbox, '--ro-bind', '/etc/alternatives', '/etc/alternatives' + if -d '/etc/alternatives'; + my ($output, $status) = capture_merged { + local %ENV = (PATH => '/usr/bin:/bin:/usr/sbin:/sbin'); + system(@sandbox, 'timeout', '45', @command); + $?; + }; + return ($status, $output); +} + +1; diff --git a/xCAT-test/native/fixtures/ubuntu_image/media.pl b/xCAT-test/native/fixtures/ubuntu_image/media.pl new file mode 100644 index 000000000..97c824062 --- /dev/null +++ b/xCAT-test/native/fixtures/ubuntu_image/media.pl @@ -0,0 +1,26 @@ +use strict; +use warnings; + +use File::Slurper qw(write_binary); +use JSON::PP qw(encode_json); +use xCAT::Table; + +my $plugin = '/repo/xCAT-server/lib/xcat/plugins/debian.pm'; +require $plugin; + +my $site = xCAT::Table->new('site', -create => 1); +$site->setAttribs({ key => 'installdir' }, { value => '/install' }); +$site->close(); + +my @responses; +my $detected = xCAT_plugin::debian::is_ubuntu_live_media('/work/media'); +xCAT_plugin::debian::process_request( + { command => ['copycd'], arg => ['-m', '/work/media', '-o'] }, + sub { push @responses, @_ }, +); +my $osdistro = xCAT::Table->new('osdistro'); +my $row = $osdistro->getAttribs({ osdistroname => 'ubuntu24.04-x86_64' }, + qw(arch dirpaths type)); +$osdistro->close(); +write_binary('/work/result.json', encode_json({ detected => $detected, + responses => \@responses, osdistro => $row })); diff --git a/xCAT-test/native/ubuntu_live_media_guardrails.t b/xCAT-test/native/ubuntu_live_media_guardrails.t new file mode 100644 index 000000000..89b9804b7 --- /dev/null +++ b/xCAT-test/native/ubuntu_live_media_guardrails.t @@ -0,0 +1,56 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin/../lib"; +use JSON::PP qw(decode_json); +use Test::More; +use XCAT::Test::UbuntuImage; + +plan skip_all => 'Linux filesystem namespaces are required' unless $^O eq 'linux'; + +for my $case ( + ['install-source metadata', ['casper/install-sources.yaml'], 1], + ['squashfs payload', ['casper/ubuntu-server.squashfs'], 1], + ['both live markers', ['casper/install-sources.yaml', 'casper/root.squashfs'], 1], + ['ordinary installer', ['install/vmlinuz'], 0], + ['casper without a live marker', ['casper/vmlinuz'], 0], + ['similarly named files', ['casper/root.squashfs.bak', 'casper/install-sources.yml'], 0], + ['marker outside casper', ['casper/vmlinuz', 'install-sources.yaml', 'root.squashfs'], 0], +) { + my ($name, $files, $live) = @$case; + subtest $name => sub { + my $image = XCAT::Test::UbuntuImage->new(); + $image->write('work/media/.disk/info', "Ubuntu-Server 24.04 LTS \"Noble Numbat\" - Release amd64 (20240423)\n"); + $image->write('work/media/README.diskdefines', "#define DISKNUM 1\n"); + $image->write('work/media/dists/noble/Release', "Suite: noble\n"); + $image->write("work/media/$_", "fixture $_\n") for @$files; + my ($status, $output) = $image->run('perl', + '/repo/xCAT-test/native/fixtures/ubuntu_image/media.pl'); + is($status, 0, 'copycd request completes') or diag($output); + return unless $status == 0; + my $result = decode_json($image->read('work/result.json')); + is_deeply([grep { $_->{error} || ($_->{data} || '') =~ /^Error/ } + @{ $result->{responses} }], [], 'import reports no errors'); + is_deeply($result->{osdistro}, { arch => 'x86_64', type => 'Linux', + dirpaths => '/install/ubuntu24.04/x86_64' }, 'import records the copied distribution'); + is($result->{detected}, $live, 'media classification matches its contents'); + my @warnings = map { @{ $_->{warning} || [] } } @{ $result->{responses} }; + is(scalar @warnings, $live, 'only live media produces a package-source warning'); + if ($live) { + like($warnings[0], qr/not a complete Ubuntu apt package mirror/, + 'warning explains the live media limitation'); + like($warnings[0], qr/linuximage\.pkgdir.*linuximage\.otherpkgdir.*HTTP\/HTTPS/, + 'warning identifies configurable package sources'); + } + ok(grep({ ($_->{data} || '') eq 'Media copy operation successful' } + @{ $result->{responses} }), 'request reports a successful import'); + for my $file (@$files) { + is($image->read("install/ubuntu24.04/x86_64/$file"), "fixture $file\n", + "$file reaches the copied media"); + } + }; +} + +done_testing(); diff --git a/xCAT-test/unit/ubuntu_live_media_guardrails.t b/xCAT-test/unit/ubuntu_live_media_guardrails.t deleted file mode 100644 index aeee6936a..000000000 --- a/xCAT-test/unit/ubuntu_live_media_guardrails.t +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env perl -use strict; -use warnings; - -use FindBin; -use lib "$FindBin::Bin/../lib"; -use Test::More; - -use XCAT::Test::File qw(slurp_repo_file); - -my $debian_pm = slurp_repo_file('xCAT-server/lib/xcat/plugins/debian.pm'); -like( $debian_pm, qr/sub is_ubuntu_live_media/, 'copycds can detect Ubuntu live media' ); -like( $debian_pm, qr/casper\/install-sources\.yaml/, 'copycds recognizes Subiquity install source metadata' ); -like( $debian_pm, qr/casper\/\*\.squashfs/, 'copycds recognizes live squashfs media' ); -like( $debian_pm, qr/not a complete Ubuntu apt package mirror/, 'copycds warns that Ubuntu live media is not a complete apt mirror' ); -like( $debian_pm, qr/linuximage\.pkgdir.*linuximage\.otherpkgdir.*HTTP\/HTTPS Ubuntu apt repository/s, 'copycds warning points to explicit package source attributes' ); - -my $genimage = slurp_repo_file('xCAT-server/share/xcat/netboot/ubuntu/genimage'); -unlike( $genimage, qr{http://archive\.ubuntu\.com/ubuntu/}, 'Ubuntu genimage does not implicitly use the public amd64 archive' ); -unlike( $genimage, qr{http://ports\.ubuntu\.com/ubuntu-ports/}, 'Ubuntu genimage does not implicitly use the public ports archive' ); -like( $genimage, qr{\$aptcmd2 = "--verbose --arch \$uarch \$dist \$rootimg_dir file://\$srcdir"}, 'Ubuntu genimage uses copied local media when no explicit mirror is configured' ); -like( $genimage, qr/copied Ubuntu media.*complete local Ubuntu apt mirror.*HTTP\/HTTPS Ubuntu apt repository/s, 'Ubuntu genimage gives an actionable package source error' ); -like( $genimage, qr{\@pkgdir_internet.*?\$aptcmd2 = "--verbose --arch \$uarch \$dist \$rootimg_dir \$mirrorurl"}s, 'Ubuntu genimage still honors an explicit mirror configured in pkgdir' ); - -my $copycds_doc = slurp_repo_file('docs/source/guides/admin-guides/references/man8/copycds.8.rst'); -like( $copycds_doc, qr/Ubuntu live-server media.*not a complete Ubuntu apt package mirror/s, 'copycds documentation explains Ubuntu live media package limits' ); - -my $linuximage_doc = slurp_repo_file('docs/source/guides/admin-guides/references/man5/linuximage.5.rst'); -like( $linuximage_doc, qr/Ubuntu live-server media copied by copycds is not a complete apt package mirror.*HTTP\/HTTPS Ubuntu apt repository/, 'linuximage documentation explains Ubuntu live media package limits' ); - -my $osimage_doc = slurp_repo_file('docs/source/guides/admin-guides/references/man7/osimage.7.rst'); -like( $osimage_doc, qr/Ubuntu live-server media copied by copycds is not a complete apt package mirror.*HTTP\/HTTPS Ubuntu apt repository/, 'osimage documentation explains Ubuntu live media package limits' ); - -done_testing();