mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-21 23:28:19 +00:00
Merge pull request #7899 from VersatusHPC/fix/xcat-ws-conf-module-path
fix(xCAT-server): xcat-ws.conf names a SUSE module path and never matches its package
This commit is contained in:
@@ -498,6 +498,34 @@ sub run_bats_tests{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#--------------------------------------------------------
|
||||
# Fuction name: run_source_integration_tests
|
||||
# Description: Run the integration tests that read a file the Debian
|
||||
# packages do not install. They run against the pre-build copy
|
||||
# of the source tree, after install_xcat() put Apache on the
|
||||
# runner. A skip fails the phase: the runner has every
|
||||
# prerequisite.
|
||||
# Attributes:
|
||||
# Return code: 0 all tests passed, 1 otherwise
|
||||
#--------------------------------------------------------
|
||||
sub run_source_integration_tests{
|
||||
my $cmd = "cd $unitsrc && prove -v xCAT-test/integration/wsapi_apache_access.t";
|
||||
print "[run_source_integration_tests] running $cmd\n";
|
||||
my @output = runcmd("$cmd");
|
||||
print Dumper \@output;
|
||||
if($::RUNCMD_RC or grep { /skipped/i } @output){
|
||||
print RED "[run_source_integration_tests] $cmd ....[Failed]\n";
|
||||
$check_result_str .= "> **SOURCE INTEGRATION TESTS Failed** : Please click ``Details`` label in ``Merge pull request`` box for detailed information\n";
|
||||
print $check_result_str;
|
||||
return 1;
|
||||
}
|
||||
|
||||
print "[run_source_integration_tests] $cmd ....[Pass]\n";
|
||||
$check_result_str .= "> **SOURCE INTEGRATION TESTS Successful**\n";
|
||||
print $check_result_str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#--------------------------------------------------------
|
||||
# Fuction name: run_blackboxtest_unit_tests
|
||||
# Description: Run the blackboxtest offline checks: its Python unit tests and
|
||||
@@ -1017,6 +1045,14 @@ if($rst){
|
||||
}
|
||||
mark_time("run_bats_tests");
|
||||
|
||||
print GREEN "\n------Running xCAT-test source integration tests ------\n";
|
||||
$rst = run_source_integration_tests();
|
||||
if($rst){
|
||||
print RED "Run of xCAT-test source integration tests failed\n";
|
||||
exit $rst;
|
||||
}
|
||||
mark_time("run_source_integration_tests");
|
||||
|
||||
#Check the syntax of changing code
|
||||
print GREEN "\n------ Checking the syntax of changed code------\n";
|
||||
$rst = check_syntax();
|
||||
|
||||
@@ -418,32 +418,26 @@ rm -f $RPM_BUILD_ROOT/%{prefix}/ws/zvmxcatws.cgi
|
||||
|
||||
%if %fsm
|
||||
%else
|
||||
echo "ScriptAlias /xcatrhevh %{prefix}/ws/xcatrhevh.cgi" > $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22
|
||||
echo "ScriptAlias /xcatrhevh %{prefix}/ws/xcatrhevh.cgi" > $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24
|
||||
echo "ScriptAlias /xcatrhevh %{prefix}/ws/xcatrhevh.cgi" > $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf
|
||||
%if %notpcm
|
||||
%if %nots390x
|
||||
echo "ScriptAlias /xcatws %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22
|
||||
echo "ScriptAlias /xcatws %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24
|
||||
echo "ScriptAlias /xcatws %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf
|
||||
%else
|
||||
# Add in old version 1 REST-API and version 2 REST-API to z/VM, default to version 1
|
||||
echo "ScriptAlias /xcatwsv2 %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22
|
||||
echo "ScriptAlias /xcatwsv2 %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24
|
||||
echo "ScriptAlias /xcatws %{prefix}/ws/zvmxcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22
|
||||
echo "ScriptAlias /xcatws %{prefix}/ws/zvmxcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24
|
||||
echo "ScriptAlias /xcatwsv2 %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf
|
||||
echo "ScriptAlias /xcatws %{prefix}/ws/zvmxcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf
|
||||
%endif
|
||||
%endif
|
||||
|
||||
cat $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache22 >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22
|
||||
cat $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache24 >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24
|
||||
#install lower version(<2.4) apache/httpd conf files by default
|
||||
cp $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 $RPM_BUILD_ROOT/etc/apache2/conf.d/xcat-ws.conf
|
||||
cp $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 $RPM_BUILD_ROOT/etc/httpd/conf.d/xcat-ws.conf
|
||||
cat $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf
|
||||
# One fragment serves Apache 2.2 and 2.4, so the payload is also the final file.
|
||||
cp $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf $RPM_BUILD_ROOT/etc/apache2/conf.d/xcat-ws.conf
|
||||
cp $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf $RPM_BUILD_ROOT/etc/httpd/conf.d/xcat-ws.conf
|
||||
%endif
|
||||
|
||||
|
||||
|
||||
rm -f $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache22
|
||||
rm -f $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache24
|
||||
rm -f $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
@@ -607,23 +601,9 @@ if [ "$1" -gt "1" ]; then #only on upgrade for AIX...
|
||||
fi
|
||||
%endif
|
||||
|
||||
#Apply the correct httpd/apache configuration file according to the httpd/apache version
|
||||
if [ -n "$(httpd -v 2>&1 |grep -e '^Server version\s*:.*/2\.4')" ]
|
||||
then
|
||||
rm -rf /etc/httpd/conf.d/xcat-ws.conf
|
||||
cp /etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 /etc/httpd/conf.d/xcat-ws.conf
|
||||
fi
|
||||
|
||||
if [ -n "$(apachectl -v 2>&1 |grep -e '^Server version\s*:.*/2\.4')" ]
|
||||
then
|
||||
rm -rf /etc/apache2/conf.d/xcat-ws.conf
|
||||
cp /etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 /etc/apache2/conf.d/xcat-ws.conf
|
||||
fi
|
||||
|
||||
if [ -n "$(apache2ctl -v 2>&1 |grep -e '^Server version\s*:.*/2\.4')" ]
|
||||
then
|
||||
rm -rf /etc/apache2/conf.d/xcat-ws.conf
|
||||
cp /etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 /etc/apache2/conf.d/xcat-ws.conf
|
||||
# SUSE does not load mod_rewrite by default. A no-op where a2enmod is absent.
|
||||
if command -v a2enmod >/dev/null 2>&1; then
|
||||
a2enmod rewrite >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# The Apache configuration for the xCAT REST API, for every RPM family.
|
||||
#
|
||||
# Do not add a LoadModule: it names one distribution's module directory and is
|
||||
# then absent on the others, which stops Apache from starting.
|
||||
|
||||
# Redirect http to https. Guarded because mod_rewrite can be absent.
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{SERVER_PORT} 80
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L]
|
||||
RewriteRule ^/?xcatwsv2/(.*) https://%{SERVER_NAME}/xcatwsv2/$1 [R,L]
|
||||
</IfModule>
|
||||
# Without the redirect, deny the REST aliases.
|
||||
<IfModule !mod_rewrite.c>
|
||||
RedirectMatch 403 ^/xcatws(v2)?(/|$)
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch "^(xcatws.cgi|zvmxcatws.cgi)$">
|
||||
# Both spellings, so one file serves Apache 2.2 and 2.4.
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all granted
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</IfModule>
|
||||
</FilesMatch>
|
||||
@@ -1,14 +0,0 @@
|
||||
LoadModule rewrite_module /usr/lib64/apache2-prefork/mod_rewrite.so
|
||||
|
||||
# Redirect all http request to https
|
||||
RewriteEngine On
|
||||
RewriteCond %{SERVER_PORT} 80
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L]
|
||||
RewriteRule ^/?xcatwsv2/(.*) https://%{SERVER_NAME}/xcatwsv2/$1 [R,L]
|
||||
|
||||
<FilesMatch "^(xcatws.cgi|zvmxcatws.cgi)$">
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</FilesMatch>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
LoadModule rewrite_module /usr/lib64/apache2-prefork/mod_rewrite.so
|
||||
RewriteEngine On
|
||||
RewriteCond %{SERVER_PORT} 80
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L]
|
||||
RewriteRule ^/?xcatwsv2/(.*) https://%{SERVER_NAME}/xcatwsv2/$1 [R,L]
|
||||
|
||||
<FilesMatch "^(xcatws.cgi|zvmxcatws.cgi)$">
|
||||
Require all granted
|
||||
</FilesMatch>
|
||||
@@ -4,7 +4,7 @@ os:Linux
|
||||
label:mn_only,ci_test,integration
|
||||
cmd:prove -I/opt/xcat/lib/perl -I/opt/xcat/lib/perl/xCAT -r /opt/xcat/share/xcat/tools/autotest/integration
|
||||
check:rc==0
|
||||
check:output=~Files=9,
|
||||
check:output=~Files=10,
|
||||
end
|
||||
|
||||
start:xcatd_dispatch_redaction
|
||||
|
||||
@@ -27,8 +27,8 @@ against it.
|
||||
Note the `-I` flags: unlike the unit tests these run from the installed location, so
|
||||
they pick up xCAT modules from `/opt/xcat/lib/perl` rather than from a source tree.
|
||||
|
||||
The case checks `rc==0` and `output=~Files=9,`. The file count checks that `prove`
|
||||
found all nine files, which catches a packaging regression or a test renamed away from
|
||||
The case checks `rc==0` and `output=~Files=10,`. The file count checks that `prove`
|
||||
found all ten files, which catches a packaging regression or a test renamed away from
|
||||
`.t` without updating the count. It does not prove that environment-gated cases ran;
|
||||
inspect the TAP output for skips. **Add to that number when you add a test.** A missing
|
||||
directory is already caught by `rc==0` -- `prove -r` on a path that does not exist exits
|
||||
@@ -55,6 +55,7 @@ A test belongs in `integration/` when it needs something the checkout cannot pro
|
||||
| `html_form_runtime_dependency.t` | the installed `HTML::Form` module |
|
||||
| `openeuler_package_postscripts.t` | Linux root, private mount namespaces and DNF; tests complete package postscripts with command doubles and an isolated repository directory |
|
||||
| `openeuler_postscript_repositories.t` | Linux root, private mount namespaces, DNF and RPM signing tools; tests actual signed repository transactions in isolated install roots |
|
||||
| `wsapi_apache_access.t` | an Apache binary and its modules; reads `xcat-ws.conf` from the source tree, so `github_action_xcat_test.pl` runs it from the checkout and the installed copy skips |
|
||||
| `xcatd_dispatch_redaction.sh` | a live `xcatd`, syslog delivery to `/var/log/xcat/cluster.log`, and root |
|
||||
|
||||
Run `xcattest -t xcatd_dispatch_redaction` to check password redaction in the
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
#!/usr/bin/env perl
|
||||
# Starts a private Apache that includes the xcat-ws.conf fragment from the
|
||||
# source tree, once without mod_rewrite and once with it, and checks what the
|
||||
# REST aliases and a provisioning path answer.
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use File::Slurper qw(read_text);
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use IO::Socket::INET;
|
||||
use POSIX qw(WNOHANG _exit setsid);
|
||||
use Test::More;
|
||||
use Time::HiRes qw(sleep);
|
||||
|
||||
my $fragment = $ENV{XCAT_WS_CONF}
|
||||
|| File::Spec->rel2abs("$FindBin::Bin/../../xCAT-server/xCAT-wsapi/xcat-ws.conf");
|
||||
plan skip_all => "the xcat-ws.conf fragment is not at $fragment"
|
||||
unless -r $fragment;
|
||||
|
||||
my $httpd = find_httpd();
|
||||
plan skip_all => 'no Apache httpd binary is installed' unless $httpd;
|
||||
|
||||
my $builtin = join( "\n", `$httpd -l 2>/dev/null` );
|
||||
my $moddir = find_module_dir();
|
||||
plan skip_all => 'no Apache module directory with mod_alias.so is present'
|
||||
unless $moddir;
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 find_httpd
|
||||
|
||||
Descriptions: Finds the Apache binary of the distribution.
|
||||
Arguments: none
|
||||
Returns: the path, or undef when no binary is installed.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub find_httpd {
|
||||
foreach my $path (qw(/usr/sbin/httpd /usr/sbin/apache2 /usr/sbin/httpd-prefork)) {
|
||||
return $path if -x $path;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 find_module_dir
|
||||
|
||||
Descriptions: Finds the directory that holds the Apache shared modules.
|
||||
Arguments: none
|
||||
Returns: the path, or undef when no candidate holds mod_alias.so.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub find_module_dir {
|
||||
foreach my $dir (
|
||||
qw(/usr/lib64/httpd/modules /usr/lib/httpd/modules /usr/lib/apache2/modules
|
||||
/usr/lib64/apache2-prefork /usr/lib64/apache2)
|
||||
)
|
||||
{
|
||||
return $dir if -e "$dir/mod_alias.so";
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 load_module_lines
|
||||
|
||||
Descriptions: Builds the LoadModule lines for the modules that are not
|
||||
compiled into the binary.
|
||||
Arguments: the module names without the mod_ prefix
|
||||
Returns: the configuration text.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub load_module_lines {
|
||||
my (@names) = @_;
|
||||
my $text = '';
|
||||
foreach my $name (@names) {
|
||||
next if $builtin =~ /\bmod_\Q$name\E\.c\b/;
|
||||
die "mod_$name.so is not in $moddir" unless -e "$moddir/mod_$name.so";
|
||||
$text .= "LoadModule ${name}_module $moddir/mod_$name.so\n";
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 free_port
|
||||
|
||||
Descriptions: Asks the kernel for a free TCP port on the loopback address.
|
||||
Arguments: none
|
||||
Returns: the port number.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub free_port {
|
||||
my $sock = IO::Socket::INET->new(
|
||||
LocalAddr => '127.0.0.1',
|
||||
LocalPort => 0,
|
||||
Listen => 1,
|
||||
Proto => 'tcp',
|
||||
) or die "Unable to find a free port: $!";
|
||||
my $port = $sock->sockport();
|
||||
close($sock);
|
||||
return $port;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 write_config
|
||||
|
||||
Descriptions: Writes an Apache configuration that serves a stub REST CGI
|
||||
through the xCAT aliases and a provisioning file under
|
||||
/install, then includes the fragment.
|
||||
Arguments: the work directory, the port, true to load mod_rewrite
|
||||
Returns: the path of the configuration file.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub write_config {
|
||||
my ( $dir, $port, $with_rewrite ) = @_;
|
||||
|
||||
my @modules = qw(mpm_prefork unixd authz_core alias cgi);
|
||||
push @modules, 'rewrite' if $with_rewrite;
|
||||
@modules = grep { $_ ne 'mpm_prefork' } @modules
|
||||
if $builtin =~ /\b(prefork|worker|event)\.c\b/;
|
||||
|
||||
my $user = '';
|
||||
if ( $> == 0 ) {
|
||||
my $group = getgrgid( ( getpwnam('nobody') )[3] );
|
||||
$user = "User nobody\nGroup $group\n";
|
||||
}
|
||||
|
||||
# UseCanonicalName gives the fragment SERVER_PORT 80 on a private port.
|
||||
my $conf = load_module_lines(@modules) . <<"EOF";
|
||||
ServerRoot $dir
|
||||
ServerName localhost:80
|
||||
UseCanonicalName On
|
||||
Listen 127.0.0.1:$port
|
||||
PidFile $dir/httpd.pid
|
||||
DefaultRuntimeDir $dir
|
||||
ErrorLog $dir/error_log
|
||||
LogLevel warn
|
||||
$user
|
||||
DocumentRoot $dir/htdocs
|
||||
<Directory />
|
||||
AllowOverride None
|
||||
Require all denied
|
||||
</Directory>
|
||||
<Directory $dir/htdocs>
|
||||
Require all granted
|
||||
</Directory>
|
||||
Alias /install $dir/install
|
||||
<Directory $dir/install>
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /xcatws $dir/ws/xcatws.cgi
|
||||
ScriptAlias /xcatwsv2 $dir/ws/xcatws.cgi
|
||||
Include $fragment
|
||||
EOF
|
||||
|
||||
my $path = "$dir/httpd.conf";
|
||||
open( my $fh, '>', $path ) or die "Unable to write $path: $!";
|
||||
print $fh $conf;
|
||||
close($fh) or die "Unable to close $path: $!";
|
||||
return $path;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 make_tree
|
||||
|
||||
Descriptions: Creates the document tree, the provisioning file and the
|
||||
stub REST CGI under a new directory.
|
||||
Arguments: none
|
||||
Returns: the directory.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub make_tree {
|
||||
my $dir = tempdir( CLEANUP => 1 );
|
||||
|
||||
# Apache drops to nobody when the test runs as root.
|
||||
chmod 0755, $dir or die "Unable to chmod $dir: $!";
|
||||
foreach my $sub (qw(htdocs install ws)) {
|
||||
mkdir "$dir/$sub" or die "Unable to create $dir/$sub: $!";
|
||||
}
|
||||
|
||||
open( my $probe, '>', "$dir/install/probe.txt" ) or die "probe.txt: $!";
|
||||
print $probe "install-probe\n";
|
||||
close($probe);
|
||||
|
||||
my $cgi = "$dir/ws/xcatws.cgi";
|
||||
open( my $fh, '>', $cgi ) or die "Unable to write $cgi: $!";
|
||||
print $fh "#!/bin/sh\nprintf 'Content-Type: text/plain\\n\\nxcatws-stub\\n'\n";
|
||||
close($fh);
|
||||
chmod 0755, $cgi or die "Unable to chmod $cgi: $!";
|
||||
return $dir;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 start_httpd
|
||||
|
||||
Descriptions: Starts Apache in the foreground and waits for its port.
|
||||
Arguments: the configuration file, the port, the work directory
|
||||
Returns: the process id.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub start_httpd {
|
||||
my ( $conf, $port, $dir ) = @_;
|
||||
|
||||
my $pid = fork();
|
||||
die "Unable to fork: $!" unless defined $pid;
|
||||
if ( $pid == 0 ) {
|
||||
|
||||
# prefork signals its whole process group when it stops.
|
||||
setsid();
|
||||
open( STDIN, '<', '/dev/null' );
|
||||
open( STDOUT, '>', "$dir/stdout" );
|
||||
open( STDERR, '>&', \*STDOUT );
|
||||
{ exec( $httpd, '-f', $conf, '-DFOREGROUND' ) };
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
foreach ( 1 .. 100 ) {
|
||||
if ( waitpid( $pid, WNOHANG ) == $pid ) {
|
||||
diag( read_log("$dir/stdout") . read_log("$dir/error_log") );
|
||||
die "Apache exited with status $?";
|
||||
}
|
||||
my $sock = IO::Socket::INET->new( PeerAddr => "127.0.0.1:$port" );
|
||||
return $pid if $sock;
|
||||
sleep 0.1;
|
||||
}
|
||||
stop_httpd($pid);
|
||||
die "Apache did not listen on port $port";
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 stop_httpd
|
||||
|
||||
Descriptions: Stops the Apache parent process and reaps it.
|
||||
Arguments: the process id
|
||||
Returns: nothing.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub stop_httpd {
|
||||
my ($pid) = @_;
|
||||
kill 'TERM', $pid;
|
||||
waitpid( $pid, 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 read_log
|
||||
|
||||
Descriptions: Reads an Apache log for a diagnostic message.
|
||||
Arguments: the path
|
||||
Returns: the contents, or an empty string when the file is absent.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub read_log {
|
||||
my ($path) = @_;
|
||||
return -e $path ? read_text($path) : '';
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 get
|
||||
|
||||
Descriptions: Sends one GET request to the private Apache.
|
||||
Arguments: the port, the request path
|
||||
Returns: the status code, the Location header and the body.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub get {
|
||||
my ( $port, $path ) = @_;
|
||||
my $sock = IO::Socket::INET->new( PeerAddr => "127.0.0.1:$port", Timeout => 10 )
|
||||
or die "Unable to connect to port $port: $!";
|
||||
print $sock "GET $path HTTP/1.0\r\nHost: localhost\r\n\r\n";
|
||||
local $/;
|
||||
my $response = <$sock>;
|
||||
close($sock);
|
||||
|
||||
my ($status) = $response =~ m{^HTTP/\S+\s+(\d+)};
|
||||
my ($location) = $response =~ m{^Location:\s*(\S+)}mi;
|
||||
my ( undef, $body ) = split( /\r\n\r\n/, $response, 2 );
|
||||
return ( $status // 0, $location // '', $body // '' );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 with_httpd
|
||||
|
||||
Descriptions: Runs a block of checks against a private Apache.
|
||||
Arguments: true to load mod_rewrite, the block, which gets the port
|
||||
Returns: nothing.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub with_httpd {
|
||||
my ( $with_rewrite, $code ) = @_;
|
||||
my $dir = make_tree();
|
||||
my $port = free_port();
|
||||
my $conf = write_config( $dir, $port, $with_rewrite );
|
||||
|
||||
my $syntax = `$httpd -t -f $conf 2>&1`;
|
||||
is( $?, 0, ( $with_rewrite ? 'with' : 'without' ) . ' mod_rewrite the configuration parses' )
|
||||
or diag($syntax);
|
||||
|
||||
my $pid = start_httpd( $conf, $port, $dir );
|
||||
my $ok = eval { $code->($port); 1 };
|
||||
my $error = $@;
|
||||
stop_httpd($pid);
|
||||
die $error unless $ok;
|
||||
return;
|
||||
}
|
||||
|
||||
my @rest_paths = ( '/xcatws/version', '/xcatwsv2/version', '/xcatws', '/xcatwsv2' );
|
||||
|
||||
subtest 'without mod_rewrite' => sub {
|
||||
with_httpd(
|
||||
0,
|
||||
sub {
|
||||
my ($port) = @_;
|
||||
foreach my $path (@rest_paths) {
|
||||
my ( $status, undef, $body ) = get( $port, $path );
|
||||
is( $status, 403, "$path answers 403" );
|
||||
unlike( $body, qr/xcatws-stub/, "$path does not run the REST CGI" );
|
||||
}
|
||||
my ( $status, undef, $body ) = get( $port, '/install/probe.txt' );
|
||||
is( $status, 200, '/install is served' );
|
||||
like( $body, qr/install-probe/, '/install returns the file' );
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
subtest 'with mod_rewrite' => sub {
|
||||
with_httpd(
|
||||
1,
|
||||
sub {
|
||||
my ($port) = @_;
|
||||
foreach my $alias (qw(xcatws xcatwsv2)) {
|
||||
my ( $status, $location ) = get( $port, "/$alias/version" );
|
||||
is( $status, 302, "/$alias/version answers 302" );
|
||||
is( $location, "https://localhost/$alias/version",
|
||||
"/$alias/version redirects to https" );
|
||||
}
|
||||
my ( $status, undef, $body ) = get( $port, '/install/probe.txt' );
|
||||
is( $status, 200, '/install is served' );
|
||||
like( $body, qr/install-probe/, '/install returns the file' );
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env perl
|
||||
# The fragment and the spec do not execute and their text IS the contract, so this
|
||||
# test parses them rather than running them.
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use Test::More;
|
||||
|
||||
use XCAT::Test::File qw(repo_path slurp_repo_file);
|
||||
|
||||
my $wsapi_dir = 'xCAT-server/xCAT-wsapi';
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 parse_apache_fragment
|
||||
|
||||
Descriptions: Parses an Apache configuration fragment.
|
||||
Arguments: the fragment contents
|
||||
Returns: a list of hash references, one per directive, each with the
|
||||
directive name, its arguments and the enclosing sections from the
|
||||
outside in. A section is recorded as its name plus its argument,
|
||||
for example "IfModule mod_rewrite.c".
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub parse_apache_fragment {
|
||||
my ($contents) = @_;
|
||||
my (@directives, @open_sections);
|
||||
|
||||
foreach my $line ( split( /\n/, $contents ) ) {
|
||||
my $text = $line;
|
||||
$text =~ s/^\s+//;
|
||||
$text =~ s/\s+$//;
|
||||
next unless length $text;
|
||||
next if $text =~ /^#/;
|
||||
|
||||
if ( $text =~ m{^</(\w+)\s*>$} ) {
|
||||
my $closed = pop @open_sections;
|
||||
die "Unbalanced </$1> in an Apache fragment" unless defined $closed;
|
||||
next;
|
||||
}
|
||||
if ( $text =~ m{^<(\w+)\s*(.*?)\s*>$} ) {
|
||||
my ( $name, $argument ) = ( $1, $2 );
|
||||
$argument =~ s/^"(.*)"$/$1/;
|
||||
push @open_sections,
|
||||
length($argument) ? "$name $argument" : $name;
|
||||
next;
|
||||
}
|
||||
|
||||
my ( $directive, @arguments ) = split( /\s+/, $text );
|
||||
push @directives,
|
||||
{
|
||||
directive => $directive,
|
||||
arguments => [@arguments],
|
||||
sections => [@open_sections],
|
||||
};
|
||||
}
|
||||
|
||||
die 'An Apache fragment left a section open' if @open_sections;
|
||||
return @directives;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
=head3 parse_spec_sections
|
||||
|
||||
Descriptions: Splits an RPM spec into its sections.
|
||||
Arguments: the spec contents
|
||||
Returns: a hash reference keyed on the section name without the leading
|
||||
percent sign, each value the lines of that section.
|
||||
=cut
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
sub parse_spec_sections {
|
||||
my ($contents) = @_;
|
||||
my %section;
|
||||
my $current = 'preamble';
|
||||
|
||||
foreach my $line ( split( /\n/, $contents ) ) {
|
||||
if ( $line =~ /^%(build|install|clean|files|pre|post|preun|postun|posttrans|changelog|description|package)\b/ ) {
|
||||
$current = $1;
|
||||
$section{$current} ||= [];
|
||||
next;
|
||||
}
|
||||
push @{ $section{$current} ||= [] }, $line;
|
||||
}
|
||||
|
||||
return \%section;
|
||||
}
|
||||
|
||||
sub guarded_by {
|
||||
my ( $directive, $section ) = @_;
|
||||
return scalar grep { $_ eq $section } @{ $directive->{sections} };
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# The RPM families only. debian/rules owns xcat-ws.conf.ubuntu.
|
||||
#-----------------------------------------------------------------------------
|
||||
my @fragments = sort grep { !m{\.ubuntu$} }
|
||||
map { my $p = $_; $p =~ s{^.*/(xCAT-server/)}{$1}; $p }
|
||||
glob( repo_path("$wsapi_dir/xcat-ws.conf*") );
|
||||
|
||||
die "No Apache fragment found under $wsapi_dir; the test can measure nothing\n"
|
||||
unless @fragments;
|
||||
|
||||
foreach my $fragment (@fragments) {
|
||||
my @directives = parse_apache_fragment( slurp_repo_file($fragment) );
|
||||
|
||||
# A LoadModule naming one distribution's MPM directory is absent on the others.
|
||||
my @load_module = grep { lc( $_->{directive} ) eq 'loadmodule' } @directives;
|
||||
is( scalar @load_module, 0,
|
||||
"$fragment declares no LoadModule, so the server owns module loading" )
|
||||
or diag( 'LoadModule found: '
|
||||
. join( ', ', map { join( ' ', @{ $_->{arguments} } ) } @load_module ) );
|
||||
|
||||
my @absolute = grep {
|
||||
my $d = $_;
|
||||
grep { m{^/} } @{ $d->{arguments} }
|
||||
} @directives;
|
||||
is( scalar @absolute, 0,
|
||||
"$fragment names no absolute filesystem path, so no distribution layout is assumed" )
|
||||
or diag( 'absolute path in: '
|
||||
. join( ', ', map { $_->{directive} } @absolute ) );
|
||||
|
||||
# One file serves both generations, each directive behind its own guard.
|
||||
my @rewrite = grep { lc( $_->{directive} ) =~ /^rewrite/ } @directives;
|
||||
ok( scalar @rewrite, "$fragment still redirects http to https" );
|
||||
foreach my $directive (@rewrite) {
|
||||
ok( guarded_by( $directive, 'IfModule mod_rewrite.c' ),
|
||||
"$fragment guards $directive->{directive} with <IfModule mod_rewrite.c>" );
|
||||
}
|
||||
|
||||
my @modern = grep { lc( $_->{directive} ) eq 'require' } @directives;
|
||||
my @legacy = grep { lc( $_->{directive} ) =~ /^(order|allow|deny)$/ } @directives;
|
||||
ok( scalar @modern, "$fragment grants access in the Apache 2.4 spelling" );
|
||||
ok( scalar @legacy, "$fragment grants access in the Apache 2.2 spelling" );
|
||||
foreach my $directive (@modern) {
|
||||
ok( guarded_by( $directive, 'IfModule mod_authz_core.c' ),
|
||||
"$fragment guards $directive->{directive} with <IfModule mod_authz_core.c>" );
|
||||
}
|
||||
foreach my $directive (@legacy) {
|
||||
ok( guarded_by( $directive, 'IfModule !mod_authz_core.c' ),
|
||||
"$fragment guards $directive->{directive} with <IfModule !mod_authz_core.c>" );
|
||||
}
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# %post must not replace the payload, or rpm -V reports drift for the life of the install.
|
||||
#-----------------------------------------------------------------------------
|
||||
my $spec = parse_spec_sections( slurp_repo_file('xCAT-server/xCAT-server.spec') );
|
||||
|
||||
my @live_paths =
|
||||
( '/etc/httpd/conf.d/xcat-ws.conf', '/etc/apache2/conf.d/xcat-ws.conf' );
|
||||
|
||||
foreach my $path (@live_paths) {
|
||||
my @post = grep { index( $_, $path ) >= 0 } @{ $spec->{post} };
|
||||
is( scalar @post, 0,
|
||||
"%post never names $path, so it cannot replace the installed payload" );
|
||||
diag("%post line: $_") foreach @post;
|
||||
|
||||
my @install = grep { /^\s*cp\s.*\Q$path\E\s*$/ } @{ $spec->{install} };
|
||||
ok( scalar @install, "%install installs $path as package payload" );
|
||||
|
||||
ok( scalar( grep { /^\Q$path\E\s*$/ } @{ $spec->{files} } ),
|
||||
"%files owns $path, so rpm verifies it" );
|
||||
}
|
||||
|
||||
# The LoadModule removed from the fragment is replaced by the distribution's own
|
||||
# module management. On SUSE apache2 that is a2enmod, as xCAT.spec already does
|
||||
# for mod_headers; on EL conf.modules.d loads mod_rewrite already.
|
||||
ok( scalar( grep { /\ba2enmod\s+rewrite\b/ } @{ $spec->{post} } ),
|
||||
'%post enables mod_rewrite where a2enmod manages modules' );
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user