2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-02 08:51:44 +00:00

test(genimage): exercise boot publication and repositories directly

This commit is contained in:
Vinícius Ferrão
2026-09-25 01:38:38 -03:00
parent cc2e3e9635
commit 23e1c2c29c
3 changed files with 280 additions and 210 deletions
@@ -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 $@;
@@ -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"));
}
+93 -210
View File
@@ -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);
}