mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-25 17:24:07 +00:00
Merge pull request #7861 from VersatusHPC/test/r26-deprecated-request-behavior
test(provision): verify deprecated request rejection
This commit is contained in:
@@ -3,82 +3,234 @@ use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use File::Spec;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
use File::Temp qw(tempdir);
|
||||
use Getopt::Long;
|
||||
use Storable qw(dclone);
|
||||
use Test::More;
|
||||
use XCAT::Test::File qw(repo_path);
|
||||
|
||||
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
|
||||
my ( $root, %images, @effects, @locks );
|
||||
|
||||
sub slurp {
|
||||
my ($rel) = @_;
|
||||
my $path = File::Spec->catfile( $repo_root, $rel );
|
||||
return unless -r $path;
|
||||
open( my $fh, '<', $path ) or die "Unable to read $path: $!";
|
||||
my $c = do { local $/; <$fh> };
|
||||
close($fh);
|
||||
return $c;
|
||||
}
|
||||
|
||||
# Report statements that sit after an unconditional return inside the same
|
||||
# block. Those are unreachable, which is how the deprecated provisioning paths
|
||||
# survived in the tree for years after they stopped running.
|
||||
sub unreachable_after_return {
|
||||
my ($source) = @_;
|
||||
my @lines = split( /\n/, $source, -1 );
|
||||
my @found;
|
||||
for my $i ( 0 .. $#lines ) {
|
||||
my ($indent) = $lines[$i] =~ /^(\s*)(?:return\s*;|return\s+\d+\s*;)\s*$/;
|
||||
next unless defined $indent;
|
||||
my $depth = length($indent);
|
||||
for my $j ( $i + 1 .. $#lines ) {
|
||||
my $next = $lines[$j];
|
||||
next if $next =~ /^\s*$/ || $next =~ /^\s*#/;
|
||||
my ($ni) = $next =~ /^(\s*)/;
|
||||
last if length($ni) < $depth;
|
||||
last if $next =~ /^\s*[}\]\)]/;
|
||||
push @found, ( $j + 1 ) . ": $next";
|
||||
last;
|
||||
}
|
||||
BEGIN {
|
||||
package xCAT::Table;
|
||||
sub new {
|
||||
my ( $class, $table ) = @_;
|
||||
die "Unexpected table $table" unless $table =~ /^(?:chain|bootparams|nodetype|noderes|osimage|linuximage)$/;
|
||||
return bless { table => $table }, $class;
|
||||
}
|
||||
return @found;
|
||||
sub getNodesAttribs {
|
||||
my ( $self, $nodes, @attributes ) = @_;
|
||||
@attributes = @{ $attributes[0] } if ref($attributes[0]) eq 'ARRAY';
|
||||
die "Unexpected read from $self->{table}" unless $self->{table} eq 'bootparams';
|
||||
return { map { $_ => [ { map { $_ => undef } @attributes } ] } @$nodes };
|
||||
}
|
||||
sub getNodeAttribs {
|
||||
my ( $self, $node, $attributes ) = @_;
|
||||
die "Unexpected read from $self->{table}" unless $self->{table} eq 'noderes';
|
||||
return { netboot => 'xnba' };
|
||||
}
|
||||
sub getAttribs {
|
||||
my ( $self, $query, @attributes ) = @_;
|
||||
@attributes = @{ $attributes[0] } if ref($attributes[0]) eq 'ARRAY';
|
||||
die "Unexpected read from $self->{table}" unless exists $images{$self->{table}};
|
||||
my $row = $images{$self->{table}}->{$query->{imagename}};
|
||||
return unless $row;
|
||||
my %result;
|
||||
for my $attribute (@attributes) {
|
||||
my $value = $row->{$attribute};
|
||||
$result{$attribute} = $value if defined($value) && $value ne '';
|
||||
}
|
||||
return keys(%result) ? \%result : undef;
|
||||
}
|
||||
sub setNodesAttribs {
|
||||
push @effects, 'database update';
|
||||
die "Unexpected database update";
|
||||
}
|
||||
sub close { return; }
|
||||
$INC{'xCAT/Table.pm'} = __FILE__;
|
||||
|
||||
package xCAT::Utils;
|
||||
sub isMN { return 0; }
|
||||
sub Version { return 'fixture-version'; }
|
||||
sub acquire_lock_imageop {
|
||||
my ( $class, $directory ) = @_;
|
||||
push @locks, $directory;
|
||||
return ( 1, 'fixture image lock busy' );
|
||||
}
|
||||
sub runcmd {
|
||||
push @effects, 'external command';
|
||||
die "Unexpected external command";
|
||||
}
|
||||
$INC{'xCAT/Utils.pm'} = __FILE__;
|
||||
|
||||
package xCAT::TableUtils;
|
||||
sub getInstallDir { return "$root/install"; }
|
||||
sub get_site_attribute {
|
||||
my ( $class, $attribute ) = @_;
|
||||
return (1) if $attribute eq 'nodestatus';
|
||||
return ('UTC') if $attribute eq 'timezone';
|
||||
die "Unexpected site attribute $attribute";
|
||||
}
|
||||
$INC{'xCAT/TableUtils.pm'} = __FILE__;
|
||||
|
||||
package xCAT::MsgUtils;
|
||||
sub trace { return; }
|
||||
$INC{'xCAT/MsgUtils.pm'} = __FILE__;
|
||||
|
||||
package xCAT::Postage;
|
||||
sub create_mypostscript_or_not {
|
||||
push @effects, 'postscript generation';
|
||||
die "Unexpected postscript generation";
|
||||
}
|
||||
$INC{'xCAT/Postage.pm'} = __FILE__;
|
||||
|
||||
$INC{'xCAT/NodeRange.pm'} = __FILE__;
|
||||
$INC{'xCAT/SvrUtils.pm'} = __FILE__;
|
||||
$INC{'xCAT/PasswordUtils.pm'} = __FILE__;
|
||||
$INC{'xCAT_monitoring/monitorctrl.pm'} = __FILE__;
|
||||
}
|
||||
|
||||
my $destiny = slurp('xCAT-server/lib/xcat/plugins/destiny.pm');
|
||||
my $packimage = slurp('xCAT-server/lib/xcat/plugins/packimage.pm');
|
||||
|
||||
plan skip_all => 'destiny.pm or packimage.pm not found'
|
||||
unless defined($destiny) && defined($packimage);
|
||||
|
||||
my @destiny_dead = unreachable_after_return($destiny);
|
||||
is_deeply( \@destiny_dead, [], 'destiny.pm has no statements after an unconditional return' );
|
||||
|
||||
my @packimage_dead = unreachable_after_return($packimage);
|
||||
is_deeply( \@packimage_dead, [], 'packimage.pm has no statements after an unconditional return' );
|
||||
|
||||
# The deprecated states must still be rejected. Removing the dead path below the
|
||||
# rejection must not remove the rejection itself.
|
||||
like(
|
||||
$destiny,
|
||||
qr/have been deprecated, use \\"osimage=/,
|
||||
'the deprecated nodeset states are still rejected'
|
||||
$root = tempdir( CLEANUP => 1 );
|
||||
local $ENV{XCATROOT} = $root;
|
||||
# Inherited POSIX parsing rules must not change the request-order fixtures.
|
||||
local $ENV{POSIXLY_CORRECT};
|
||||
delete $ENV{POSIXLY_CORRECT};
|
||||
Getopt::Long::Configure('default');
|
||||
$INC{"$root/lib/perl/xCAT/Table.pm"} = __FILE__;
|
||||
%images = (
|
||||
osimage => {
|
||||
unconfigured => { provmethod => undef, osvers => 'rhels9' },
|
||||
empty => { provmethod => undef, osvers => '' },
|
||||
'known-image' => { osvers => 'rhels9', osarch => 'x86_64', profile => 'compute', provmethod => 'netboot' },
|
||||
},
|
||||
linuximage => { 'known-image' => { rootimgdir => "$root/image" } },
|
||||
);
|
||||
|
||||
# packimage rejects -o, -p and -a up front, so nothing after that point can ask
|
||||
# for them again. The old no-imagename branch demanded -o, which could never be
|
||||
# supplied, and reported that as the error.
|
||||
unlike(
|
||||
$packimage,
|
||||
qr/Please specify a os version with the -o flag/,
|
||||
'packimage no longer asks for an option it rejects earlier'
|
||||
);
|
||||
like(
|
||||
$packimage,
|
||||
qr/-o, -p and -a options are obsoleted/,
|
||||
'packimage still rejects the deprecated options'
|
||||
);
|
||||
like(
|
||||
$packimage,
|
||||
qr/An image name is required/,
|
||||
'packimage reports the missing image name instead'
|
||||
);
|
||||
sub run_request {
|
||||
my ( $handler, $request ) = @_;
|
||||
my @responses;
|
||||
@effects = ();
|
||||
@locks = ();
|
||||
local @ARGV;
|
||||
no warnings 'once';
|
||||
local %::XCATSITEVALS = ();
|
||||
my $status;
|
||||
my $completed = eval {
|
||||
$status = $handler->(
|
||||
dclone($request),
|
||||
sub { push @responses, dclone($_[0]); },
|
||||
sub {
|
||||
push @effects, 'subrequest';
|
||||
die "Unexpected subrequest";
|
||||
},
|
||||
);
|
||||
1;
|
||||
};
|
||||
my $exception = $@;
|
||||
is( $completed, 1, 'request completes without an exception' ) or diag($exception);
|
||||
is_deeply( \@effects, [], 'no calls reach the write, command or provisioning guards' );
|
||||
return ( $status, \@responses, [@locks] );
|
||||
}
|
||||
|
||||
require(repo_path('xCAT-server/lib/xcat/plugins/destiny.pm'));
|
||||
|
||||
my $deprecated = 'The options "install", "netboot", and "statelite" have been deprecated, use "osimage=<osimage_name>" instead.';
|
||||
my @destiny_cases;
|
||||
for my $state (qw(install netboot statelite)) {
|
||||
push @destiny_cases, map {
|
||||
{ args => $_, response => { error => $deprecated, errorcode => [1], errorabort => [1] } }
|
||||
} (
|
||||
[$state],
|
||||
["$state=rhels9-x86_64-compute"],
|
||||
["$state=rhels9-x86_64-compute:reboot4deploy"],
|
||||
["$state,boot"],
|
||||
[ '--noupdateinitrd', "$state=rhels9-x86_64-compute" ],
|
||||
);
|
||||
}
|
||||
push @destiny_cases,
|
||||
{
|
||||
args => ['osimage=missing-image'],
|
||||
response => { error => 'Cannot find the OS image missing-image in the osimage table.', errorcode => [1], errorabort => [1] },
|
||||
},
|
||||
{
|
||||
args => ['osimage=unconfigured'],
|
||||
response => { error => 'osimage.provmethod for unconfigured must be set.', errorcode => [1], errorabort => [1] },
|
||||
},
|
||||
{
|
||||
args => ['osimage=empty'],
|
||||
response => { error => 'Cannot find the OS image empty in the osimage table.', errorcode => [1], errorabort => [1] },
|
||||
};
|
||||
|
||||
for my $case (@destiny_cases) {
|
||||
subtest 'setdestiny ' . join( ' ', @{ $case->{args} } ) => sub {
|
||||
my ( undef, $responses, $locks ) = run_request(
|
||||
\&xCAT_plugin::destiny::process_request,
|
||||
{ command => ['setdestiny'], node => [qw(node1 node2)], arg => $case->{args}, bootparams => {} },
|
||||
);
|
||||
is_deeply( $responses, [ $case->{response} ], 'the handler returns the expected failure and abort status' );
|
||||
is_deeply( $locks, [], 'no image operation starts' );
|
||||
};
|
||||
}
|
||||
|
||||
# Load packimage after destiny requests because it changes global option parsing.
|
||||
require(repo_path('xCAT-server/lib/xcat/plugins/packimage.pm'));
|
||||
|
||||
my $obsolete = "-o, -p and -a options are obsoleted, please use 'packimage <osimage name>' instead.";
|
||||
my $missing = "An image name is required, use 'packimage <osimage name>'.";
|
||||
my @packimage_cases;
|
||||
for my $option ( [ '-o', '--osver', 'rhels9' ], [ '-p', '--profile', 'compute' ], [ '-a', '--arch', 'x86_64' ] ) {
|
||||
my ( $short, $long, $value ) = @$option;
|
||||
push @packimage_cases, map {
|
||||
{ args => $_, error => $obsolete }
|
||||
} (
|
||||
[ $short, $value ],
|
||||
[ $long, $value ],
|
||||
[ "$long=$value" ],
|
||||
[ $short, $value, 'known-image' ],
|
||||
[ 'known-image', $short, $value ],
|
||||
);
|
||||
}
|
||||
push @packimage_cases,
|
||||
{ args => [ '-o', 'rhels9', '-p', 'compute', '-a', 'x86_64', 'known-image' ], error => $obsolete },
|
||||
map { { args => $_, error => $missing } } (
|
||||
[ '-m', 'cpio' ], [ '--method', 'tar' ], [ '--compress=gzip' ], ['--nosyncfiles'],
|
||||
[ '-m', 'cpio', '-c', 'xz', '--nosyncfiles' ],
|
||||
);
|
||||
push @packimage_cases, map {
|
||||
{ args => $_, error => 'fixture image lock busy', locks => ["$root/image/rootimg"] }
|
||||
} ( ['known-image'], [ '--method', 'cpio', '--compress=gzip', '--nosyncfiles', 'known-image' ] );
|
||||
|
||||
for my $case (@packimage_cases) {
|
||||
subtest 'packimage ' . join( ' ', @{ $case->{args} } ) => sub {
|
||||
my ( $status, $responses, $locks ) = run_request(
|
||||
\&xCAT_plugin::packimage::process_request,
|
||||
{ command => ['packimage'], arg => $case->{args} },
|
||||
);
|
||||
is( $status, 1, 'the handler returns failure' );
|
||||
is_deeply( $responses, [ { error => [ $case->{error} ], errorcode => [1] } ], 'the callback reports the expected error' );
|
||||
is_deeply( $locks, $case->{locks} || [], 'only a named image reaches the image lock' );
|
||||
};
|
||||
}
|
||||
|
||||
for my $case ( [ [] ], [ ['-h'] ], [ ['--help'] ], [ ['-v'], 'fixture-version' ], [ ['--version'], 'fixture-version' ] ) {
|
||||
my ( $args, $version ) = @$case;
|
||||
subtest 'packimage information request ' . join( ' ', @$args ) => sub {
|
||||
my ( $status, $responses, $locks ) = run_request(
|
||||
\&xCAT_plugin::packimage::process_request,
|
||||
{ command => ['packimage'], arg => $args },
|
||||
);
|
||||
is( $status, 0, 'the information request succeeds' );
|
||||
is( scalar(@$responses), 1, 'one response is returned' );
|
||||
is_deeply( [ sort keys %{ $responses->[0] } ], ['info'], 'the response is informational, not an error' );
|
||||
if (defined $version) {
|
||||
is_deeply( $responses->[0]->{info}, [$version], 'the installed version is returned' );
|
||||
} else {
|
||||
like( $responses->[0]->{info}->[0], qr/^Usage:\n\s+packimage\b/, 'usage is returned' );
|
||||
}
|
||||
is_deeply( $locks, [], 'no image operation starts' );
|
||||
};
|
||||
}
|
||||
|
||||
done_testing();
|
||||
|
||||
Reference in New Issue
Block a user