2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-20 14:45:41 +00:00

test(xcat-core): the subiquity error-command test is written as a Perl program

ubuntu_subiquity_error_commands.t measures shell. It parses the error-commands
block out of compute.subiquity.tmpl, renders each item, writes a wrapper that
shadows nc, tar and tail, and shells out. The Perl adds nothing the shell
cannot state, and the reader follows two languages to reach two assertions.

The test moves to xCAT-test/bats, which the xcat_test workflow already runs. It
keeps what it proved: the error commands return instead of waiting for a
collector an unattended install does not have, and they write the end of the
curtin log to the console the installer names. An error-commands block that
stops matching fails the test instead of covering nothing.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit d5fd9027df)
This commit is contained in:
Daniel Hilst
2026-09-14 10:11:30 -03:00
committed by github-actions[bot]
parent 51cb875be9
commit b5586111fa
2 changed files with 70 additions and 65 deletions
@@ -0,0 +1,70 @@
#!/usr/bin/env bats
#
# Subiquity waits for every error command to return before it reports the failure. An error
# command that waits for a collector an unattended install does not have therefore holds the
# node until the provisioning timeout.
#
# Run the error commands, with the programs that would reach the host or the network replaced,
# and check that they return and that they write the end of the curtin log where the caller
# points.
load 'helpers/shell_source'
setup()
{
TEMPLATE="$(repo_path 'xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl')"
[ -r "$TEMPLATE" ] || skip "$TEMPLATE is required"
export TEMPLATE
}
# One command per list item of the error-commands block. The list form ['sh', '-c', '...']
# carries the command in its last element; a plain item is the command itself.
error_commands()
{
awk '
/^ error-commands:$/ { copy = 1; next }
copy && !/^ [-#]/ { exit }
copy && /^ - / {
found = 1
line = substr($0, 7)
if (match(line, /^\[[^]]*, .-c., .*\]$/)) {
sub(/^\[[^]]*, .-c., ./, "", line)
sub(/.\]$/, "", line)
}
print line
}
END { if (!found) exit 1 }
' "$TEMPLATE"
}
# bash resolves a function ahead of PATH, so the commands run as written while nothing reaches
# the host or the network. The nc shadow waits the way a listener with no collector waits.
run_error_commands()
{
local script="${BATS_TEST_TMPDIR}/error-commands.sh" command
: >"$script"
{
printf "export XCAT_ERROR_CONSOLE='%s'\n" "$CONSOLE"
printf 'nc() { sleep 300; }\n'
printf 'tar() { :; }\n'
printf 'tail() { echo XCAT_CURTIN_LOG_TAIL; }\n'
} >>"$script"
while IFS= read -r command; do
# Subiquity runs each error command on its own, so a command that ends in "exit 0"
# must not end the others.
printf '( %s )\n' "${command//\#HOSTNAME\#/testnode}" >>"$script"
done < <(error_commands)
timeout 10 bash "$script"
}
@test "the error commands return instead of waiting for someone to collect the logs" {
[ -n "$(error_commands)" ]
CONSOLE="${BATS_TEST_TMPDIR}/console"
run run_error_commands
[ "$status" -ne 124 ]
grep -q XCAT_CURTIN_LOG_TAIL "$CONSOLE"
}
@@ -1,65 +0,0 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use File::Temp qw(tempdir);
use Test::More;
# Subiquity waits for every error command to return before it reports the failure. An error
# command that waits for a collector an unattended install does not have therefore holds the
# node until the provisioning timeout.
#
# Run the error commands, with the programs that would reach the host or the network replaced,
# and check that they return and that they write the end of the curtin log where the caller
# points.
my $tmpl = "$FindBin::Bin/../../xCAT-server/share/xcat/install/ubuntu/compute.subiquity.tmpl";
plan skip_all => 'compute.subiquity.tmpl not found' unless -r $tmpl;
open(my $fh, '<', $tmpl) or die "open $tmpl: $!";
my $source = do { local $/; <$fh> };
close $fh;
my ($block) = $source =~ m{^ error-commands:\n((?: [-#].*\n)+)}m;
die('the template declares no error-commands') unless $block;
# One command per list item. The list form ['sh', '-c', '...'] carries the command in its last
# element; a plain item is the command itself.
my @commands;
foreach my $line (split /\n/, $block) {
next unless $line =~ m{^ - (.*)$};
my $item = $1;
if ($item =~ m{^\['[^']+', '-c', '(.*)'\]$}) { push @commands, $1; }
else { push @commands, $item; }
}
die('no error command found in the block') unless @commands;
my $root = tempdir(CLEANUP => 1);
my $console = "$root/console";
my $script = "$root/error-commands.sh";
# bash resolves a function ahead of PATH, so the commands run as written while nothing reaches
# the host or the network. The nc shadow waits the way a listener with no collector waits.
open(my $out, '>', $script) or die "open $script: $!";
print {$out} "export XCAT_ERROR_CONSOLE='$console'\n";
print {$out} "nc() { sleep 300; }\n";
print {$out} "tar() { :; }\n";
print {$out} "tail() { echo XCAT_CURTIN_LOG_TAIL; }\n";
foreach my $command (@commands) {
( my $rendered = $command ) =~ s/#HOSTNAME#/testnode/g;
# Subiquity runs each error command on its own, so a command that ends in "exit 0" must not
# end the others.
print {$out} "( $rendered )\n";
}
close $out;
my $rc = system('timeout', '10', 'bash', $script);
my $status = $rc == -1 ? -1 : $rc >> 8;
isnt( $status, 124, 'the error commands return instead of waiting for someone to collect the logs' );
my $written = '';
if (open(my $log, '<', $console)) { local $/; $written = <$log>; close $log; }
like( $written, qr/XCAT_CURTIN_LOG_TAIL/, 'and write the end of the curtin log to the console the installer names' );
done_testing();