From 23e1c2c29c348b3a1e236912db461cf522631dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Fri, 25 Sep 2026 01:38:38 -0300 Subject: [PATCH] test(genimage): exercise boot publication and repositories directly --- .../fixtures/openeuler-genimage-driver.pl | 67 ++++ .../native/openeuler_genimage_bootfiles.t | 120 +++++++ xCAT-test/unit/openeuler_genimage_bootfiles.t | 303 ++++++------------ 3 files changed, 280 insertions(+), 210 deletions(-) create mode 100644 xCAT-test/native/fixtures/openeuler-genimage-driver.pl create mode 100644 xCAT-test/native/openeuler_genimage_bootfiles.t diff --git a/xCAT-test/native/fixtures/openeuler-genimage-driver.pl b/xCAT-test/native/fixtures/openeuler-genimage-driver.pl new file mode 100644 index 000000000..a216a2a7b --- /dev/null +++ b/xCAT-test/native/fixtures/openeuler-genimage-driver.pl @@ -0,0 +1,67 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use Errno qw(EACCES EIO); +use File::Copy (); +use File::Basename qw(dirname); +use FindBin; +use File::Slurper qw(write_text); +use Storable qw(nstore); +our (@commands, $dracut_calls, $lock_calls); +BEGIN { + *CORE::GLOBAL::readpipe = sub { + return "4.18.2\n" if $_[0] =~ /rpm --version/; + return "059\n" if $_[0] =~ /^rpm --root .* -qi dracut /; + return '' if $_[0] =~ /type -p pigz/; + die "Unexpected command: $_[0]"; + }; + *CORE::GLOBAL::system = sub { + my $command = join(' ', @_); + push @commands, $command; + if ($command =~ /^chroot (\S+) dracut .* -f (\S+) /) { + $dracut_calls++; + write_text("$1$2", "new initrd\n"); + return 29 << 8 if $ENV{GENIMAGE_FAILURE} eq 'dracut'; + } + if ($command =~ m{^sed -i -e '[^']+' \Q$ENV{GENIMAGE_DEST}\E/rootimg/etc/yum\.repos\.d/[A-Za-z0-9.-]+\.repo$}) { + return CORE::system($command); + } + return 0; + }; + *CORE::GLOBAL::rename = sub { + my $failure = $ENV{GENIMAGE_FAILURE}; + if ($failure eq 'publish-initrd' + && $_[1] eq "$ENV{GENIMAGE_DEST}/initrd-stateless.gz") { + $! = EACCES; + return 0; + } + return CORE::rename($_[0], $_[1]); + }; +} +my $move = \&File::Copy::move; +{ + no warnings 'redefine'; + *File::Copy::move = sub { + if ($ENV{GENIMAGE_FAILURE} eq 'move-initrd' && $_[0] =~ m{/tmp/initrd\.\d+\.gz$}) { + $! = EIO; + return 0; + } + return $move->(@_); + }; +} +END { + nstore({ commands => \@commands, dracut_calls => $dracut_calls || 0, lock_calls => $lock_calls || 0 }, + "$ENV{GENIMAGE_CASE}/result") if defined($ENV{GENIMAGE_CASE}); +} +require xCAT::Utils; +{ + no warnings qw(redefine once); + *xCAT::Utils::acquire_lock_imageop = sub { + $lock_calls++; + return ($ENV{GENIMAGE_FAILURE} eq 'lock' ? 1 : 0, undef); + }; +} +$0 = $ENV{GENIMAGE_SCRIPT}; +$FindBin::Bin = dirname($0); +do $0; +die $@ if $@; diff --git a/xCAT-test/native/openeuler_genimage_bootfiles.t b/xCAT-test/native/openeuler_genimage_bootfiles.t new file mode 100644 index 000000000..8ee520a1a --- /dev/null +++ b/xCAT-test/native/openeuler_genimage_bootfiles.t @@ -0,0 +1,120 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use FindBin; +use File::Path qw(make_path); +use File::Spec; +use File::Slurper qw(read_text write_text); +use File::Temp qw(tempdir); +use Storable qw(retrieve); +use Test::More; + +plan skip_all => 'requires Linux filesystem semantics and an unprivileged test user' + unless $^O eq 'linux' && $>; +my $repo = File::Spec->rel2abs("$FindBin::Bin/../.."); +my $dir = tempdir(CLEANUP => 1); +my $driver = "$FindBin::Bin/fixtures/openeuler-genimage-driver.pl"; + +for my $os (qw(openeuler20.03sp4 openeuler24.03sp3)) { + for my $failure (qw(none kernel-copy dracut move-initrd publish-initrd no-dracut)) { + my ($rc, $state, $output, $case) = run_case($os, $failure, 'vmlinuz', 1, $failure eq 'none'); + my $label = "$os $failure"; + if ($failure eq 'none') { + is($rc, 0, "$label completes") or diag($output); + is(read_text("$case/image/kernel"), "new kernel\n", "$label publishes the kernel"); + is(read_text("$case/image/initrd-stateless.gz"), "new initrd\n", "$label publishes the initrd"); + my $repos = "$case/image/rootimg/etc/yum.repos.d"; + is(read_text("$repos/$_"), "[fixture]\nenabled=0\n", "$label disables $_") + for qw(openEuler.repo Rocky-BaseOS.repo); + is(read_text("$repos/administrator.repo"), "[fixture]\nenabled=1\n", + "$label preserves administrator repositories"); + } else { + isnt($rc, 0, "$label fails"); + like($output, qr/Error: failed to (?:copy|generate|move|publish|preserve).*?(?:kernel|ramdisk|initrd)/i, + "$label reports the failed boot file operation"); + is(read_text("$case/image/kernel"), "old kernel\n", "$label preserves the previous kernel"); + is(read_text("$case/image/initrd-stateless.gz"), "old initrd\n", "$label preserves the previous initrd"); + ok(grep({ /^findmnt .*\/dev\// } @{$state->{commands}}), "$label invokes mount cleanup"); + } + is($state->{dracut_calls}, $failure =~ /^(?:kernel-copy|no-dracut)$/ ? 0 : 1, + "$label only reaches dracut after a successful kernel copy"); + is($state->{lock_calls}, 1, "$label acquires the image operation lock"); + is_deeply([glob("$case/image/rootimg/tmp/initrd.*.gz")], [], "$label removes temporary dracut output"); + is_deeply([glob("$case/image/.genimage*")], [], "$label removes boot staging files"); + } +} +for my $kernel (qw(vmlinux image modules)) { + my ($rc, $state, $output, $case) = run_case('openeuler24.03', 'none', $kernel, 0); + is($rc, 0, "$kernel kernel layout completes") or diag($output); + is(read_text("$case/image/kernel"), "new kernel\n", "$kernel layout publishes its kernel"); + is(read_text("$case/image/initrd-stateless.gz"), "new initrd\n", "$kernel layout publishes its initrd"); +} +my ($rc, $state, $output, $case) = run_case('openeuler24.03sp3', 'lock', 'vmlinuz', 1); +isnt($rc, 0, 'lock refusal is fatal'); +like($output, qr/Could not acquire image operation lock/, 'lock refusal reports the contested image'); +is($state->{lock_calls}, 1, 'lock refusal uses the existing lock owner'); +is($state->{dracut_calls}, 0, 'lock refusal does not run dracut'); +is(read_text("$case/image/kernel"), "old kernel\n", 'lock refusal preserves kernel'); +is(read_text("$case/image/initrd-stateless.gz"), "old initrd\n", 'lock refusal preserves initrd'); +is_deeply($state->{commands}, [], 'lock refusal does not run mount cleanup against the active build'); +is_deeply([glob("$case/image/.genimage*")], [], 'lock refusal creates no staging directory'); + +for my $failure (qw(none kernel-copy dracut move-initrd)) { + ($rc, $state, $output, $case) = run_case('rhels9.6', $failure, 'vmlinuz', 1); + is($rc == 0 ? 0 : 1, $failure eq 'dracut' ? 1 : 0, "EL $failure retains its exit behavior") or diag($output); + is(read_text("$case/image/kernel"), $failure eq 'kernel-copy' ? "old kernel\n" : "new kernel\n", + "EL $failure retains kernel copy behavior"); + is($state->{dracut_calls}, $failure eq 'dracut' ? 1 : 2, "EL $failure retains both initrd modes"); + is($state->{lock_calls}, 0, "EL $failure retains its existing onlyinitrd lock behavior"); +} + +done_testing(); + +sub run_case { + my ($os, $failure, $kernel, $old, $repositories) = @_; + my $case = tempdir(DIR => $dir, CLEANUP => 1); + my $root = "$case/image/rootimg"; + make_path(map { "$root/$_" } qw(boot lib/modules/fixture etc dev run tmp + usr/lib/dracut/modules.d/98syslog usr/lib/dracut/modules.d/35network-manager)); + if ($failure eq 'no-dracut') { + require File::Path; + File::Path::remove_tree("$root/usr/lib/dracut"); + } + write_text("$root/lib/modules/fixture/modules.dep", ''); + my $kernel_file = $kernel eq 'modules' ? "$root/lib/modules/fixture/vmlinuz" : "$root/boot/$kernel-fixture"; + write_text($kernel_file, "new kernel\n"); + chmod 0000, $kernel_file if $failure eq 'kernel-copy'; + if ($old) { + write_text("$case/image/kernel", "old kernel\n"); + write_text("$case/image/initrd-stateless.gz", "old initrd\n"); + } + if ($repositories) { + make_path("$root/etc/yum.repos.d"); + write_text("$root/etc/yum.repos.d/$_", "[fixture]\nenabled=1\n") + for qw(openEuler.repo Rocky-BaseOS.repo administrator.repo); + } + my @result = run_owner($os, $failure, $case); + chmod 0644, $kernel_file; + return (@result, $case); +} + +sub run_owner { + my ($os, $failure, $case) = @_; + local %ENV = %ENV; + @ENV{qw(GENIMAGE_SCRIPT GENIMAGE_DEST GENIMAGE_CASE GENIMAGE_FAILURE XCATROOT)} = + ("$repo/xCAT-server/share/xcat/netboot/rh/genimage", "$case/image", $case, $failure, "$repo/xCAT-server"); + my $pid = fork(); + die "fork: $!" unless defined($pid); + if (!$pid) { + open(STDOUT, '>', "$case/output") or die $!; + open(STDERR, '>&', STDOUT) or die $!; + exec $^X, "-I$repo/perl-xCAT", "-I$repo/xCAT-server/lib/perl", + "-I$repo/xCAT-server/share/xcat/netboot/imgutils", $driver, + '-o', $os, '-a', 'x86_64', '-p', 'compute', '-k', 'fixture', + '--onlyinitrd', '--rootimgdir', "$case/image", 'boot-fixture'; + die "exec: $!"; + } + waitpid($pid, 0); + my $status = $?; + return ($status, retrieve("$case/result"), read_text("$case/output")); +} diff --git a/xCAT-test/unit/openeuler_genimage_bootfiles.t b/xCAT-test/unit/openeuler_genimage_bootfiles.t index aaeb95404..a4cbbcaa0 100644 --- a/xCAT-test/unit/openeuler_genimage_bootfiles.t +++ b/xCAT-test/unit/openeuler_genimage_bootfiles.t @@ -1,257 +1,140 @@ #!/usr/bin/env perl use strict; use warnings; +use Errno qw(EACCES EIO); use FindBin; use File::Path qw(make_path); -use File::Spec; +use File::Slurper qw(read_text write_text); use File::Temp qw(tempdir); -use Storable qw(retrieve); +use lib "$FindBin::Bin/../../perl-xCAT"; +use lib "$FindBin::Bin/../../xCAT-server/lib/perl"; +use lib "$FindBin::Bin/../../xCAT-server/share/xcat/netboot/imgutils"; use Test::More; -plan skip_all => 'requires Linux filesystem semantics and an unprivileged test user' - unless $^O eq 'linux' && $>; -my $repo = File::Spec->rel2abs("$FindBin::Bin/../.."); -my $dir = tempdir(CLEANUP => 1); -my $driver = "$dir/driver.pl"; -write_file($driver, <<'PERL'); -use strict; -use warnings; -use Errno qw(EACCES EIO); -use File::Copy (); -use File::Basename qw(dirname); -use FindBin; -use Storable qw(nstore); -our (@commands, $dracut_calls, $lock_calls); BEGIN { - *CORE::GLOBAL::readpipe = sub { - return "4.18.2\n" if $_[0] =~ /rpm --version/; - return "059\n" if $_[0] =~ /^rpm --root .* -qi dracut /; - return '' if $_[0] =~ /type -p pigz/; - die "Unexpected command: $_[0]"; - }; - *CORE::GLOBAL::system = sub { - my $command = join(' ', @_); - push @commands, $command; - if ($command =~ /^chroot (\S+) dracut .* -f (\S+) /) { - $dracut_calls++; - open(my $file, '>', "$1$2") or die $!; - print {$file} "new initrd\n"; - close($file) or die $!; - return 29 << 8 if $ENV{GENIMAGE_FAILURE} eq 'dracut'; - } - if ($command =~ m{^sed -i -e '[^']+' \Q$ENV{GENIMAGE_DEST}\E/rootimg/etc/yum\.repos\.d/[A-Za-z0-9.-]+\.repo$}) { - return CORE::system($command); - } - return 0; - }; - *CORE::GLOBAL::rename = sub { - my $failure = $ENV{GENIMAGE_FAILURE}; - if (($failure =~ /^(?:publish-initrd|rollback)$/ - && $_[1] eq "$ENV{GENIMAGE_DEST}/initrd-stateless.gz") - || ($failure eq 'publish-kernel' && $_[1] eq "$ENV{GENIMAGE_DEST}/kernel") - || ($failure eq 'rollback' && $_[0] =~ m{/previous-kernel$})) { - $! = EACCES; - return 0; - } - return CORE::rename($_[0], $_[1]); - }; - *CORE::GLOBAL::link = sub { - if ($ENV{GENIMAGE_FAILURE} eq 'backup-kernel') { - $! = EACCES; - return 0; - } - return CORE::link($_[0], $_[1]); - }; + *CORE::GLOBAL::rename = sub { return CORE::rename($_[0], $_[1]); }; + *CORE::GLOBAL::link = sub { return CORE::link($_[0], $_[1]); }; } -my $move = \&File::Copy::move; -{ - no warnings 'redefine'; - *File::Copy::move = sub { - if ($ENV{GENIMAGE_FAILURE} eq 'move-initrd' && $_[0] =~ m{/tmp/initrd\.\d+\.gz$}) { - $! = EIO; - return 0; - } - return $move->(@_); - }; -} -END { - nstore({ commands => \@commands, dracut_calls => $dracut_calls || 0, lock_calls => $lock_calls || 0 }, - "$ENV{GENIMAGE_CASE}/result"); -} -require xCAT::Utils; -{ - no warnings qw(redefine once); - *xCAT::Utils::acquire_lock_imageop = sub { - $lock_calls++; - return ($ENV{GENIMAGE_FAILURE} eq 'lock' ? 1 : 0, undef); - }; -} -$0 = $ENV{GENIMAGE_SCRIPT}; -$FindBin::Bin = dirname($0); -do $0; -die $@ if $@; -PERL +use imgutils; -for my $os (qw(openeuler20.03sp4 openeuler24.03sp3)) { - for my $failure (qw(none kernel-copy dracut move-initrd backup-kernel publish-kernel publish-initrd no-dracut)) { - my ($rc, $state, $output, $case) = run_case($os, $failure, 'vmlinuz', 1); - my $label = "$os $failure"; +my $dir = tempdir(CLEANUP => 1); + +for my $mode (qw(stateless statelite)) { + for my $failure (qw(none move-initrd backup-kernel publish-kernel publish-initrd)) { + my ($ok, $error, $case) = publish_case($mode, $failure, 1); + my $label = "$mode $failure"; if ($failure eq 'none') { - is($rc, 0, "$label completes") or diag($output); - is(read_file("$case/image/kernel"), "new kernel\n", "$label publishes the kernel"); - is(read_file("$case/image/initrd-stateless.gz"), "new initrd\n", "$label publishes the initrd"); + ok($ok, "$label completes") or diag($error); + is(read_text("$case/kernel"), "new kernel\n", "$label publishes the kernel"); + is(read_text("$case/initrd-$mode.gz"), "new initrd\n", "$label publishes the initrd"); } else { - isnt($rc, 0, "$label fails"); - like($output, qr/Error: failed to (?:copy|generate|move|publish|preserve).*?(?:kernel|ramdisk|initrd)/i, + ok(!$ok, "$label fails"); + like($error, qr/Error: failed to (?:move|publish|preserve).*?(?:kernel|ramdisk)/i, "$label reports the failed boot file operation"); - is(read_file("$case/image/kernel"), "old kernel\n", "$label preserves the previous kernel"); - is(read_file("$case/image/initrd-stateless.gz"), "old initrd\n", "$label preserves the previous initrd"); - ok(grep({ /^findmnt .*\/dev\// } @{$state->{commands}}), "$label invokes mount cleanup"); + is(read_text("$case/kernel"), "old kernel\n", "$label preserves the previous kernel"); + is(read_text("$case/initrd-$mode.gz"), "old initrd\n", "$label preserves the previous initrd"); } - is($state->{dracut_calls}, $failure =~ /^(?:kernel-copy|no-dracut)$/ ? 0 : 1, - "$label only reaches dracut after a successful kernel copy"); - is($state->{lock_calls}, 1, "$label acquires the image operation lock"); - is_deeply([glob("$case/image/rootimg/tmp/initrd.*.gz")], [], "$label removes temporary dracut output"); - is_deeply([glob("$case/image/.genimage*")], [], "$label removes boot staging files"); + is_deeply([glob("$case/.genimage*")], [], "$label removes boot staging files"); } } -for my $kernel (qw(vmlinux image modules)) { - my ($rc, $state, $output, $case) = run_case('openeuler24.03', 'none', $kernel, 0); - is($rc, 0, "$kernel kernel layout completes") or diag($output); - is(read_file("$case/image/kernel"), "new kernel\n", "$kernel layout publishes its kernel"); - is(read_file("$case/image/initrd-stateless.gz"), "new initrd\n", "$kernel layout publishes its initrd"); -} -my ($rc, $state, $output, $case) = run_case('openeuler24.03sp3', 'publish-initrd', 'vmlinuz', 0); -isnt($rc, 0, 'first build publication failure is fatal'); -ok(!-e "$case/image/kernel", 'first build failure removes the unpaired kernel'); -ok(!-e "$case/image/initrd-stateless.gz", 'first build failure does not expose an initrd'); -($rc, $state, $output, $case) = run_case('openeuler24.03sp3', 'lock', 'vmlinuz', 1); -isnt($rc, 0, 'lock refusal is fatal'); -like($output, qr/Could not acquire image operation lock/, 'lock refusal reports the contested image'); -is($state->{lock_calls}, 1, 'lock refusal uses the existing lock owner'); -is($state->{dracut_calls}, 0, 'lock refusal does not run dracut'); -is(read_file("$case/image/kernel"), "old kernel\n", 'lock refusal preserves kernel'); -is(read_file("$case/image/initrd-stateless.gz"), "old initrd\n", 'lock refusal preserves initrd'); -is_deeply($state->{commands}, [], 'lock refusal does not run mount cleanup against the active build'); -is_deeply([glob("$case/image/.genimage*")], [], 'lock refusal creates no staging directory'); +my ($ok, $error, $case) = publish_case('stateless', 'publish-initrd', 0); +ok(!$ok, 'first build publication failure is fatal'); +ok(!-e "$case/kernel", 'first build failure removes the unpaired kernel'); +ok(!-e "$case/initrd-stateless.gz", 'first build failure does not expose an initrd'); +is_deeply([glob("$case/.genimage*")], [], 'first build failure removes boot staging files'); -($rc, $state, $output, $case) = run_case('openeuler24.03sp3', 'rollback', 'vmlinuz', 1); -isnt($rc, 0, 'rollback failure is fatal'); -my @backups = glob("$case/image/.genimage*/previous-kernel"); +($ok, $error, $case) = publish_case('stateless', 'rollback', 1); +ok(!$ok, 'rollback failure is fatal'); +my @backups = glob("$case/.genimage*/previous-kernel"); is(scalar(@backups), 1, 'rollback failure retains one previous kernel backup'); -is(read_file($backups[0]), "old kernel\n", 'retained backup contains the previous kernel'); -like($output, qr/kernel rollback failed: .*Boot files retained in \Q$case\E\/image\/\.genimage/, +is(read_text($backups[0]), "old kernel\n", 'retained backup contains the previous kernel'); +like($error, qr/kernel rollback failed: .*Boot files retained in \Q$case\E\/\.genimage/, 'rollback failure reports the recoverable backup directory'); -is(read_file("$case/image/initrd-stateless.gz"), "old initrd\n", 'rollback failure preserves previous initrd'); +is(read_text("$case/initrd-stateless.gz"), "old initrd\n", 'rollback failure preserves previous initrd'); -for my $failure (qw(none kernel-copy dracut move-initrd)) { - ($rc, $state, $output, $case) = run_case('rhels9.6', $failure, 'vmlinuz', 1); - is($rc == 0 ? 0 : 1, $failure eq 'dracut' ? 1 : 0, "EL $failure retains its exit behavior") or diag($output); - is(read_file("$case/image/kernel"), $failure eq 'kernel-copy' ? "old kernel\n" : "new kernel\n", - "EL $failure retains kernel copy behavior"); - is($state->{dracut_calls}, $failure eq 'dracut' ? 1 : 2, "EL $failure retains both initrd modes"); - is($state->{lock_calls}, 0, "EL $failure retains its existing onlyinitrd lock behavior"); -} - -my @legacy_repositories = qw(Rocky-BaseOS.repo almalinux.repo CentOS-Base.repo oracle-linux-ol10.repo); -my @custom_repositories = qw(local-repository-0.repo xCAT-custom.repo administrator.repo); -my $enabled = "[fixture]\nenabled=1\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; -my $disabled = "[fixture]\nenabled=0\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; -my $native = "[OS]\nenabled=1\ngpgcheck=1\n[update]\n \tenabled = 1\n[disabled]\nenabled=0\n#enabled=1\n"; -my $native_disabled = "[OS]\nenabled=0\ngpgcheck=1\n[update]\nenabled=0\n[disabled]\nenabled=0\n#enabled=1\n"; -for my $os (qw(openeuler20.03sp4 openeuler24.03sp3)) { +subtest 'vendor repositories' => sub { + plan skip_all => 'requires Linux sed' unless $^O eq 'linux'; + my @legacy_repositories = qw(Rocky-BaseOS.repo almalinux.repo CentOS-Base.repo oracle-linux-ol10.repo); + my @custom_repositories = qw(local-repository-0.repo xCAT-custom.repo administrator.repo); + my $enabled = "[fixture]\nenabled=1\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; + my $disabled = "[fixture]\nenabled=0\ngpgcheck=1\nbaseurl=https://repo.invalid/\n"; + my $native = "[OS]\nenabled=1\ngpgcheck=1\n[update]\n \tenabled = 1\n[disabled]\nenabled=0\n#enabled=1\n"; + my $native_disabled = "[OS]\nenabled=0\ngpgcheck=1\n[update]\nenabled=0\n[disabled]\nenabled=0\n#enabled=1\n"; for my $repository_case ( ['enabled', $native, $native_disabled], ['already-disabled', $native_disabled, $native_disabled], ['absent', undef, undef], ) { my ($name, $input, $expected) = @$repository_case; - subtest "$os repositories $name" => sub { - my %repositories = map { $_ => $enabled } (@legacy_repositories, @custom_repositories); - $repositories{'openEuler.repo'} = $input if defined $input; - my ($status, $result, $log, $fixture) = run_case($os, 'none', 'vmlinuz', 0, \%repositories); - my $repos = "$fixture/image/rootimg/etc/yum.repos.d"; + subtest $name => sub { + my $root = tempdir(DIR => $dir, CLEANUP => 1); + my $repos = "$root/etc/yum.repos.d"; + make_path($repos); + write_text("$repos/$_", $enabled) for (@legacy_repositories, @custom_repositories); + write_text("$repos/openEuler.repo", $input) if defined $input; for my $pass (1, 2) { - ($status, $result, $log) = run_owner($os, 'none', $fixture) if $pass == 2; - is($status, 0, "pass $pass completes the whole genimage owner") or diag($log); + imgutils::disable_vendor_repositories($root); if (defined $expected) { - is(read_file("$repos/openEuler.repo"), $expected, + is(read_text("$repos/openEuler.repo"), $expected, "pass $pass disables only active native repository assignments"); } else { ok(!-e "$repos/openEuler.repo", "pass $pass does not create an absent vendor file"); } - is(read_file("$repos/$_"), $disabled, "pass $pass preserves policy for $_") + is(read_text("$repos/$_"), $disabled, "pass $pass preserves policy for $_") for @legacy_repositories; - is(read_file("$repos/$_"), $enabled, "pass $pass leaves $_ unchanged") + is(read_text("$repos/$_"), $enabled, "pass $pass leaves $_ unchanged") for @custom_repositories; } }; } -} +}; done_testing(); -sub run_case { - my ($os, $failure, $kernel, $old, $repositories) = @_; +sub publish_case { + my ($mode, $failure, $old) = @_; my $case = tempdir(DIR => $dir, CLEANUP => 1); - my $root = "$case/image/rootimg"; - make_path(map { "$root/$_" } qw(boot lib/modules/fixture etc dev run tmp - usr/lib/dracut/modules.d/98syslog usr/lib/dracut/modules.d/35network-manager)); - if ($failure eq 'no-dracut') { - require File::Path; - File::Path::remove_tree("$root/usr/lib/dracut"); - } - write_file("$root/lib/modules/fixture/modules.dep", ''); - my $kernel_file = $kernel eq 'modules' ? "$root/lib/modules/fixture/vmlinuz" : "$root/boot/$kernel-fixture"; - write_file($kernel_file, "new kernel\n"); - chmod 0000, $kernel_file if $failure eq 'kernel-copy'; + make_path("$case/tmp"); + my $staging = File::Temp->newdir('.genimage-boot.XXXXXX', DIR => $case, CLEANUP => 1); + write_text("$staging/kernel", "new kernel\n"); + write_text("$case/tmp/initrd.gz", "new initrd\n"); if ($old) { - write_file("$case/image/kernel", "old kernel\n"); - write_file("$case/image/initrd-stateless.gz", "old initrd\n"); + write_text("$case/kernel", "old kernel\n"); + write_text("$case/initrd-$mode.gz", "old initrd\n"); } - if ($repositories) { - make_path("$root/etc/yum.repos.d"); - write_file("$root/etc/yum.repos.d/$_", $repositories->{$_}) for keys %$repositories; + + my $move = \&imgutils::move; + my ($ok, $error); + { + no warnings 'redefine'; + local *imgutils::move = sub { + if ($failure eq 'move-initrd') { + $! = EIO; + return 0; + } + return $move->(@_); + }; + local *CORE::GLOBAL::link = sub { + if ($failure eq 'backup-kernel') { + $! = EACCES; + return 0; + } + return CORE::link($_[0], $_[1]); + }; + local *CORE::GLOBAL::rename = sub { + if (($failure =~ /^(?:publish-initrd|rollback)$/ + && $_[1] eq "$case/initrd-$mode.gz") + || ($failure eq 'publish-kernel' && $_[1] eq "$case/kernel") + || ($failure eq 'rollback' && $_[0] =~ m{/previous-kernel$})) { + $! = EACCES; + return 0; + } + return CORE::rename($_[0], $_[1]); + }; + $ok = eval { imgutils::publish_boot_files($staging, $case, "$case/tmp/initrd.gz", $mode); }; + $error = $@; } - my @result = run_owner($os, $failure, $case); - chmod 0644, $kernel_file; - return (@result, $case); -} - -sub run_owner { - my ($os, $failure, $case) = @_; - local %ENV = %ENV; - @ENV{qw(GENIMAGE_SCRIPT GENIMAGE_DEST GENIMAGE_CASE GENIMAGE_FAILURE XCATROOT)} = - ("$repo/xCAT-server/share/xcat/netboot/rh/genimage", "$case/image", $case, $failure, "$repo/xCAT-server"); - my $pid = fork(); - die "fork: $!" unless defined($pid); - if (!$pid) { - open(STDOUT, '>', "$case/output") or die $!; - open(STDERR, '>&', STDOUT) or die $!; - exec $^X, "-I$repo/perl-xCAT", "-I$repo/xCAT-server/lib/perl", - "-I$repo/xCAT-server/share/xcat/netboot/imgutils", $driver, - '-o', $os, '-a', 'x86_64', '-p', 'compute', '-k', 'fixture', - '--onlyinitrd', '--rootimgdir', "$case/image", 'boot-fixture'; - die "exec: $!"; - } - waitpid($pid, 0); - my $status = $?; - return ($status, retrieve("$case/result"), read_file("$case/output")); -} - -sub read_file { - my ($path) = @_; - return undef unless -e $path; - open(my $file, '<', $path) or die "read $path: $!"; - my $contents = do { local $/; <$file> }; - close($file) or die $!; - return $contents; -} - -sub write_file { - my ($path, $contents) = @_; - open(my $file, '>', $path) or die "write $path: $!"; - print {$file} $contents or die $!; - close($file) or die $!; + undef $staging; + return ($ok, $error, $case); }