From 19a7a41572a3ed18dd861eab1978046ef27136bb Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Sat, 12 Sep 2026 14:28:25 -0300 Subject: [PATCH] test(xcat-core): a failed Ubuntu install waits for someone to collect its logs The Subiquity template offers the installer logs with "nc -l 8080" in error-commands. Subiquity waits for every error command to return, and that listener waits for a collector an unattended install never has, so a failed install stops there until the provisioning timeout resets the node. The test runs the template's own error commands with nc, tar and tail replaced, and requires that they return and that they write the end of the curtin log to the console the caller names. Both assertions fail before the fix: the first times out. Evidence: reg_linux_diskfull_installation_flat on Ubuntu 24.04 ppc64le, cluster xcat25. The node wrote no disk block and sent no packet for 18 minutes with the console at "acquiring and extracting image from cp:///tmp/.../mount". Port 8080 was open; one connection to it released the install and the console then reported the error. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- .../unit/ubuntu_subiquity_error_commands.t | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100755 xCAT-test/unit/ubuntu_subiquity_error_commands.t diff --git a/xCAT-test/unit/ubuntu_subiquity_error_commands.t b/xCAT-test/unit/ubuntu_subiquity_error_commands.t new file mode 100755 index 000000000..ee89356ef --- /dev/null +++ b/xCAT-test/unit/ubuntu_subiquity_error_commands.t @@ -0,0 +1,68 @@ +#!/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. The template +# used to offer the installer logs with "nc -l 8080", which waits for a collector that an +# unattended install never has, so a failed install stopped there: the node answered ping with no +# disk or network activity for as long as the provisioning timeout allowed, and the reason for the +# failure stayed on the node. That is how a missing grub-ieee1275 on ppc64el read as a wedged +# curtin extract. +# +# 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; +BAIL_OUT('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; } +} +BAIL_OUT('no error command found in the block') unless @commands; + +my $root = tempdir(CLEANUP => 1); +my $console = "$root/console"; +my $script = "$root/error-commands.sh"; + +# nc, tar and tail are shadowed: bash resolves a function ahead of PATH, so the commands run as +# written while nothing reaches the host or the network. nc 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();