diff --git a/xCAT-test/bats/ubuntu_subiquity_error_commands.bats b/xCAT-test/bats/ubuntu_subiquity_error_commands.bats new file mode 100644 index 000000000..6c9ffd74a --- /dev/null +++ b/xCAT-test/bats/ubuntu_subiquity_error_commands.bats @@ -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" +} diff --git a/xCAT-test/unit/ubuntu_subiquity_error_commands.t b/xCAT-test/unit/ubuntu_subiquity_error_commands.t deleted file mode 100755 index 1c1acb067..000000000 --- a/xCAT-test/unit/ubuntu_subiquity_error_commands.t +++ /dev/null @@ -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();