Add test for early backend startup errors
The new test tests the libpq fallback behavior on an early error, which was fixed in the previous commit. This adds an IS_INJECTION_POINT_ATTACHED() macro, to allow writing injected test code alongside the normal source code. In principle, the new test could've been implemented by an extra test module with a callback that sets the FrontendProtocol global variable, but I think it's more clear to have the test code right where the injection point is, because it has pretty intimate knowledge of the surrounding context it runs in. Reviewed-by: Michael Paquier Discussion: https://www.postgresql.org/message-id/CAOYmi%2Bnwvu21mJ4DYKUa98HdfM_KZJi7B1MhyXtnsyOO-PB6Ww%40mail.gmail.com
This commit is contained in:
parent
b9e5249c29
commit
20e0e7da9b
@ -3672,6 +3672,31 @@ custom_injection_callback(const char *name, const void *private_data)
|
|||||||
logic.
|
logic.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
An alternative way to define the action to take when an injection point
|
||||||
|
is reached is to add the testing code alongside the normal source
|
||||||
|
code. This can be useful if the action e.g. depends on local variables
|
||||||
|
that are not accessible to loaded modules. The
|
||||||
|
<function>IS_INJECTION_POINT_ATTACHED</function> macro can then be used
|
||||||
|
to check if an injection point is attached, for example:
|
||||||
|
<programlisting>
|
||||||
|
#ifdef USE_INJECTION_POINTS
|
||||||
|
if (IS_INJECTION_POINT_ATTACHED("before-foobar"))
|
||||||
|
{
|
||||||
|
/* change a local variable if injection point is attached */
|
||||||
|
local_var = 123;
|
||||||
|
|
||||||
|
/* also execute the callback */
|
||||||
|
INJECTION_POINT_CACHED("before-foobar");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
</programlisting>
|
||||||
|
Note that the callback attached to the injection point will not be
|
||||||
|
executed by the <function>IS_INJECTION_POINT_ATTACHED</function>
|
||||||
|
macro. If you want to execute the callback, you must also call
|
||||||
|
<function>INJECTION_POINT_CACHED</function> like in the above example.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Optionally, it is possible to detach an injection point by calling:
|
Optionally, it is possible to detach an injection point by calling:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "tcop/backend_startup.h"
|
#include "tcop/backend_startup.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/injection_point.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/ps_status.h"
|
#include "utils/ps_status.h"
|
||||||
#include "utils/timeout.h"
|
#include "utils/timeout.h"
|
||||||
@ -213,6 +214,21 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
|
|||||||
remote_host)));
|
remote_host)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For testing client error handling */
|
||||||
|
#ifdef USE_INJECTION_POINTS
|
||||||
|
INJECTION_POINT("backend-initialize");
|
||||||
|
if (IS_INJECTION_POINT_ATTACHED("backend-initialize-v2-error"))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This simulates an early error from a pre-v14 server, which used the
|
||||||
|
* version 2 protocol for any errors that occurred before processing
|
||||||
|
* the startup packet.
|
||||||
|
*/
|
||||||
|
FrontendProtocol = PG_PROTOCOL(2, 0);
|
||||||
|
elog(FATAL, "protocol version 2 error triggered");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we did a reverse lookup to name, we might as well save the results
|
* If we did a reverse lookup to name, we might as well save the results
|
||||||
* rather than possibly repeating the lookup during authentication.
|
* rather than possibly repeating the lookup during authentication.
|
||||||
|
@ -570,3 +570,17 @@ InjectionPointCached(const char *name)
|
|||||||
elog(ERROR, "Injection points are not supported by this build");
|
elog(ERROR, "Injection points are not supported by this build");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test if an injection point is defined.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
IsInjectionPointAttached(const char *name)
|
||||||
|
{
|
||||||
|
#ifdef USE_INJECTION_POINTS
|
||||||
|
return InjectionPointCacheRefresh(name) != NULL;
|
||||||
|
#else
|
||||||
|
elog(ERROR, "Injection points are not supported by this build");
|
||||||
|
return false; /* silence compiler */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
#define INJECTION_POINT_LOAD(name) InjectionPointLoad(name)
|
#define INJECTION_POINT_LOAD(name) InjectionPointLoad(name)
|
||||||
#define INJECTION_POINT(name) InjectionPointRun(name)
|
#define INJECTION_POINT(name) InjectionPointRun(name)
|
||||||
#define INJECTION_POINT_CACHED(name) InjectionPointCached(name)
|
#define INJECTION_POINT_CACHED(name) InjectionPointCached(name)
|
||||||
|
#define IS_INJECTION_POINT_ATTACHED(name) IsInjectionPointAttached(name)
|
||||||
#else
|
#else
|
||||||
#define INJECTION_POINT_LOAD(name) ((void) name)
|
#define INJECTION_POINT_LOAD(name) ((void) name)
|
||||||
#define INJECTION_POINT(name) ((void) name)
|
#define INJECTION_POINT(name) ((void) name)
|
||||||
#define INJECTION_POINT_CACHED(name) ((void) name)
|
#define INJECTION_POINT_CACHED(name) ((void) name)
|
||||||
|
#define IS_INJECTION_POINT_ATTACHED(name) (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -41,6 +43,7 @@ extern void InjectionPointAttach(const char *name,
|
|||||||
extern void InjectionPointLoad(const char *name);
|
extern void InjectionPointLoad(const char *name);
|
||||||
extern void InjectionPointRun(const char *name);
|
extern void InjectionPointRun(const char *name);
|
||||||
extern void InjectionPointCached(const char *name);
|
extern void InjectionPointCached(const char *name);
|
||||||
|
extern bool IsInjectionPointAttached(const char *name);
|
||||||
extern bool InjectionPointDetach(const char *name);
|
extern bool InjectionPointDetach(const char *name);
|
||||||
|
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
|
@ -9,11 +9,13 @@
|
|||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
EXTRA_INSTALL=src/test/modules/injection_points
|
||||||
|
|
||||||
subdir = src/interfaces/libpq
|
subdir = src/interfaces/libpq
|
||||||
top_builddir = ../../..
|
top_builddir = ../../..
|
||||||
include $(top_builddir)/src/Makefile.global
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
export with_ssl with_gssapi with_krb_srvnam
|
export with_ssl with_gssapi with_krb_srvnam enable_injection_points
|
||||||
|
|
||||||
PGFILEDESC = "PostgreSQL Access Library"
|
PGFILEDESC = "PostgreSQL Access Library"
|
||||||
|
|
||||||
|
@ -121,6 +121,7 @@ tests += {
|
|||||||
't/005_negotiate_encryption.pl',
|
't/005_negotiate_encryption.pl',
|
||||||
],
|
],
|
||||||
'env': {
|
'env': {
|
||||||
|
'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
|
||||||
'with_ssl': ssl_library,
|
'with_ssl': ssl_library,
|
||||||
'with_gssapi': gssapi.found() ? 'yes' : 'no',
|
'with_gssapi': gssapi.found() ? 'yes' : 'no',
|
||||||
'with_krb_srvnam': 'postgres',
|
'with_krb_srvnam': 'postgres',
|
||||||
|
@ -90,6 +90,8 @@ my $kerberos_enabled =
|
|||||||
$ENV{PG_TEST_EXTRA} && $ENV{PG_TEST_EXTRA} =~ /\bkerberos\b/;
|
$ENV{PG_TEST_EXTRA} && $ENV{PG_TEST_EXTRA} =~ /\bkerberos\b/;
|
||||||
my $ssl_supported = $ENV{with_ssl} eq 'openssl';
|
my $ssl_supported = $ENV{with_ssl} eq 'openssl';
|
||||||
|
|
||||||
|
my $injection_points_supported = $ENV{enable_injection_points} eq 'yes';
|
||||||
|
|
||||||
###
|
###
|
||||||
### Prepare test server for GSSAPI and SSL authentication, with a few
|
### Prepare test server for GSSAPI and SSL authentication, with a few
|
||||||
### different test users and helper functions. We don't actually
|
### different test users and helper functions. We don't actually
|
||||||
@ -155,6 +157,10 @@ $node->safe_psql('postgres', 'CREATE USER ssluser;');
|
|||||||
$node->safe_psql('postgres', 'CREATE USER nossluser;');
|
$node->safe_psql('postgres', 'CREATE USER nossluser;');
|
||||||
$node->safe_psql('postgres', 'CREATE USER gssuser;');
|
$node->safe_psql('postgres', 'CREATE USER gssuser;');
|
||||||
$node->safe_psql('postgres', 'CREATE USER nogssuser;');
|
$node->safe_psql('postgres', 'CREATE USER nogssuser;');
|
||||||
|
if ($injection_points_supported != 0)
|
||||||
|
{
|
||||||
|
$node->safe_psql('postgres', 'CREATE EXTENSION injection_points;');
|
||||||
|
}
|
||||||
|
|
||||||
my $unixdir = $node->safe_psql('postgres', 'SHOW unix_socket_directories;');
|
my $unixdir = $node->safe_psql('postgres', 'SHOW unix_socket_directories;');
|
||||||
chomp($unixdir);
|
chomp($unixdir);
|
||||||
@ -312,6 +318,29 @@ nossluser . disable postgres connect, authok
|
|||||||
['disable'], \@all_sslmodes, \@all_sslnegotiations,
|
['disable'], \@all_sslmodes, \@all_sslnegotiations,
|
||||||
parse_table($test_table));
|
parse_table($test_table));
|
||||||
|
|
||||||
|
if ($injection_points_supported != 0)
|
||||||
|
{
|
||||||
|
$node->safe_psql(
|
||||||
|
'postgres',
|
||||||
|
"SELECT injection_points_attach('backend-initialize', 'error');",
|
||||||
|
connstr => "user=localuser host=$unixdir");
|
||||||
|
connect_test(
|
||||||
|
$node,
|
||||||
|
"user=testuser sslmode=prefer",
|
||||||
|
'connect, backenderror -> fail');
|
||||||
|
$node->restart;
|
||||||
|
|
||||||
|
$node->safe_psql(
|
||||||
|
'postgres',
|
||||||
|
"SELECT injection_points_attach('backend-initialize-v2-error', 'error');",
|
||||||
|
connstr => "user=localuser host=$unixdir");
|
||||||
|
connect_test(
|
||||||
|
$node,
|
||||||
|
"user=testuser sslmode=prefer",
|
||||||
|
'connect, v2error -> fail');
|
||||||
|
$node->restart;
|
||||||
|
}
|
||||||
|
|
||||||
# Disable SSL again
|
# Disable SSL again
|
||||||
$node->adjust_conf('postgresql.conf', 'ssl', 'off');
|
$node->adjust_conf('postgresql.conf', 'ssl', 'off');
|
||||||
$node->reload;
|
$node->reload;
|
||||||
@ -393,6 +422,29 @@ nogssuser disable disable postgres connect, authok
|
|||||||
test_matrix($node, [ 'testuser', 'gssuser', 'nogssuser' ],
|
test_matrix($node, [ 'testuser', 'gssuser', 'nogssuser' ],
|
||||||
\@all_gssencmodes, $sslmodes, $sslnegotiations,
|
\@all_gssencmodes, $sslmodes, $sslnegotiations,
|
||||||
parse_table($test_table));
|
parse_table($test_table));
|
||||||
|
|
||||||
|
if ($injection_points_supported != 0)
|
||||||
|
{
|
||||||
|
$node->safe_psql(
|
||||||
|
'postgres',
|
||||||
|
"SELECT injection_points_attach('backend-initialize', 'error');",
|
||||||
|
connstr => "user=localuser host=$unixdir");
|
||||||
|
connect_test(
|
||||||
|
$node,
|
||||||
|
"user=testuser gssencmode=prefer sslmode=disable",
|
||||||
|
'connect, backenderror, reconnect, backenderror -> fail');
|
||||||
|
$node->restart;
|
||||||
|
|
||||||
|
$node->safe_psql(
|
||||||
|
'postgres',
|
||||||
|
"SELECT injection_points_attach('backend-initialize-v2-error', 'error');",
|
||||||
|
connstr => "user=localuser host=$unixdir");
|
||||||
|
connect_test(
|
||||||
|
$node,
|
||||||
|
"user=testuser gssencmode=prefer sslmode=disable",
|
||||||
|
'connect, v2error -> fail');
|
||||||
|
$node->restart;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -738,6 +790,10 @@ sub parse_log_events
|
|||||||
push @events, "gssreject" if $line =~ /GSSENCRequest rejected/;
|
push @events, "gssreject" if $line =~ /GSSENCRequest rejected/;
|
||||||
push @events, "authfail" if $line =~ /no pg_hba.conf entry/;
|
push @events, "authfail" if $line =~ /no pg_hba.conf entry/;
|
||||||
push @events, "authok" if $line =~ /connection authenticated/;
|
push @events, "authok" if $line =~ /connection authenticated/;
|
||||||
|
push @events, "backenderror"
|
||||||
|
if $line =~ /error triggered for injection point backend-/;
|
||||||
|
push @events, "v2error"
|
||||||
|
if $line =~ /protocol version 2 error triggered/;
|
||||||
}
|
}
|
||||||
|
|
||||||
# No events at all is represented by "-"
|
# No events at all is represented by "-"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user