2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-09 14:36:43 +00:00
Files
xcat-core/xCAT-test/unit/debian_subiquity_nfsroot_server.t
T
Daniel Hilst 2558e84736 test(debian): nothing catches !myipfn! being treated as a hostname
A node whose noderes.xcatmaster is unset gets $instserver = '!myipfn!'. That is
a placeholder, not a name: pxe.pm:176 and grub2.pm:129 substitute it with
my_ip_facing($node) -- an address -- when they write the boot config. The
subiquity path resolves $instserver with getipaddr, which returns undef for the
placeholder, so it reports "Could not resolve the install server" and `next`s
past the node. Before this branch it produced exactly the numeric nfsroot the
change is trying to guarantee.

anaconda.pm and sles.pm both guard the same placeholder with
`unless ($instserver eq '!myipfn!')`. noderes.5.rst:125 documents an unset
xcatmaster as supported.

No pipeline can catch it: reg_linux_diskfull_installation_flat chdefs
xcatmaster=$$MN and no ci/conf/pipelines/*.conf leaves it unset, so CI always
takes the resolvable branch.

This drives the decision with an injected resolver, so it also checks the
resolver is never ASKED about the placeholder rather than only checking the
return value. It fails at the extraction guard until the routine exists, so the
behavioural proof is the mutation on top of the fix, not this red alone.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 18:47:10 -03:00

82 lines
3.4 KiB
Perl

#!/usr/bin/env perl
use strict;
use warnings;
use File::Spec;
use FindBin;
use Test::More;
# Regression: nodes whose noderes.xcatmaster is unset stopped getting a boot config at all.
#
# When xcatmaster is unset, debian.pm sets $instserver to the literal '!myipfn!'. That is a
# placeholder, not a hostname: pxe.pm:176 and grub2.pm:129 substitute it with
# my_ip_facing($node) -- an address -- when they write the boot config. Resolving it as a name
# returns undef, so the subiquity path reported "Could not resolve the install server" and
# `next`ed past the node, where before it had produced exactly the numeric nfsroot the fix
# wants. anaconda.pm and sles.pm both guard the same placeholder with
# `unless ($instserver eq '!myipfn!')`; the Ubuntu path did not.
#
# noderes.5.rst:125 documents an unset xcatmaster as supported, and the CI confs all set it
# (reg_linux_diskfull_installation_flat chdefs xcatmaster=$$MN), which is why no pipeline
# would catch this.
#
# The decision lives in its own routine so it can be driven with an injected resolver instead
# of a management node.
my $repo_root = File::Spec->rel2abs(
File::Spec->catdir( $FindBin::Bin, '..', '..' )
);
my $plugin = File::Spec->catfile(
$repo_root, 'xCAT-server', 'lib', 'xcat', 'plugins', 'debian.pm'
);
plan skip_all => "debian.pm not found" unless -f $plugin;
my $src = do { local $/; open my $fh, '<', $plugin or die $!; <$fh> };
# Lift the routine into a scratch package: debian.pm itself needs a management node to load.
# BAIL_OUT rather than skip, so a rename fails loudly instead of silently covering nothing.
my ($body) = $src =~ /\n(sub subiquity_nfsroot_server \{.*?\n\})\n/s;
BAIL_OUT('could not extract subiquity_nfsroot_server from debian.pm')
unless defined $body;
{
package T;
eval "$body; 1" or main::BAIL_OUT("could not eval subiquity_nfsroot_server: $@");
}
# A resolver that records what it was asked, so "never asked" is checkable.
my @asked;
my $resolver = sub { push @asked, $_[0]; return $_[0] eq 'mn.cluster' ? '10.0.0.1' : undef };
# The bug: the placeholder must survive to the boot config, not be resolved.
@asked = ();
is( T::subiquity_nfsroot_server( '!myipfn!', $resolver ), '!myipfn!',
'the !myipfn! placeholder is passed through untouched' );
is_deeply( \@asked, [],
'and the resolver is never asked to resolve it' );
# A name that resolves still resolves.
@asked = ();
is( T::subiquity_nfsroot_server( 'mn.cluster', $resolver ), '10.0.0.1',
'a resolvable install server name becomes its address' );
is_deeply( \@asked, ['mn.cluster'], 'by asking the resolver' );
# An address passes through the resolver unchanged, as getipaddr does.
is( T::subiquity_nfsroot_server( '10.0.0.1', sub { $_[0] } ), '10.0.0.1',
'an address is returned as itself' );
# The guard that motivated the original commit must survive: a name that does not resolve is
# still a failure, because klibc's nfsmount cannot resolve it either.
@asked = ();
ok( !defined T::subiquity_nfsroot_server( 'nosuchhost', $resolver ),
'a name that does not resolve is still rejected' );
is_deeply( \@asked, ['nosuchhost'], 'after actually trying to resolve it' );
# Degenerate inputs are rejected rather than passed to the resolver.
ok( !defined T::subiquity_nfsroot_server( undef, $resolver ),
'an undefined install server is rejected' );
ok( !defined T::subiquity_nfsroot_server( '', $resolver ),
'an empty install server is rejected' );
done_testing();