@ -16,6 +16,7 @@
# include <vector>
# include <cstdlib>
# include "libpq-fe.h"
# include "mysql.h"
# include "command_line.h"
# include "tap.h"
# include "utils.h"
@ -135,6 +136,28 @@ static long getMonitorValue(PGconn* admin, const char* varname) {
return v ;
}
static long getConnectInterval ( PGconn * admin ) {
PGresult * res = PQexec ( admin ,
" SELECT Variable_Value FROM global_variables WHERE Variable_Name='pgsql-monitor_connect_interval'; "
) ;
if ( PQresultStatus ( res ) ! = PGRES_TUPLES_OK | | PQntuples ( res ) = = 0 ) {
PQclear ( res ) ;
return 60000 ;
}
long v = atol ( PQgetvalue ( res , 0 , 0 ) ) ;
PQclear ( res ) ;
return v ;
}
static bool setConnectInterval ( PGconn * admin , int value ) {
std : : stringstream q ;
q < < " SET pgsql-monitor_connect_interval= " < < value < < " ; " ;
bool ok1 = exec_ok ( admin , q . str ( ) . c_str ( ) ) ;
bool ok2 = exec_ok ( admin , " LOAD PGSQL VARIABLES TO RUNTIME " ) ;
usleep ( 10000 ) ;
return ok1 & & ok2 ;
}
// ============================================================================
// Part 1: Admin CRUD Operations
// ============================================================================
@ -432,7 +455,8 @@ static void test_monitor_ssl_with_per_server_params(PGconn* admin) {
long initial_ssl = getMonitorValue ( admin , " PgSQL_Monitor_ssl_connections_OK " ) ;
diag ( " Initial PgSQL_Monitor_ssl_connections_OK: %ld " , initial_ssl ) ;
usleep ( 3000000 ) ; // 3 seconds for monitor cycles
// Wait for at least 2 monitor cycles (interval was set to 2000ms in main)
usleep ( 5000000 ) ; // 5 seconds
long after_ssl = getMonitorValue ( admin , " PgSQL_Monitor_ssl_connections_OK " ) ;
diag ( " After PgSQL_Monitor_ssl_connections_OK: %ld " , after_ssl ) ;
@ -478,7 +502,7 @@ static void test_monitor_uses_per_server_row(PGconn* admin) {
long ok_before = getMonitorValue ( admin , " PgSQL_Monitor_ssl_connections_OK " ) ;
diag ( " With TLSv1 per-server pin, ssl OK before wait: %ld " , ok_before ) ;
usleep ( 3000000) ; // 3 seconds — multiple monitor cycles
usleep ( 5000000) ; // 5 seconds — multiple monitor cycles
long ok_after = getMonitorValue ( admin , " PgSQL_Monitor_ssl_connections_OK " ) ;
diag ( " With TLSv1 per-server pin, ssl OK after wait: %ld (delta=%ld) " ,
@ -495,7 +519,7 @@ static void test_monitor_uses_per_server_row(PGconn* admin) {
exec_ok ( admin , " LOAD PGSQL SERVERS TO RUNTIME " ) ;
long recover_before = getMonitorValue ( admin , " PgSQL_Monitor_ssl_connections_OK " ) ;
usleep ( 3 000000) ;
usleep ( 5 000000) ;
long recover_after = getMonitorValue ( admin , " PgSQL_Monitor_ssl_connections_OK " ) ;
diag ( " After cleanup, ssl OK recovered from %ld to %ld " ,
recover_before , recover_after ) ;
@ -505,12 +529,86 @@ static void test_monitor_uses_per_server_row(PGconn* admin) {
" removing the per-server row " ) ;
}
// ============================================================================
// Part 3: Cluster Query Support
// ============================================================================
/**
* @ brief Test that the PROXY_SELECT cluster query for pgsql_servers_ssl_params
* returns correct data via the MySQL admin port .
*
* Cluster sync uses MySQL - protocol admin connections . The PROXY_SELECT query
* is intercepted by Admin_Handler and returns data from
* get_current_pgsql_table ( ) or dump_table_pgsql ( ) .
*/
static void test_cluster_query_ssl_params ( PGconn * pgsql_admin ) {
cleanup_ssl_params ( pgsql_admin ) ;
// Insert test data via PgSQL admin and load to runtime
exec_ok ( pgsql_admin ,
" INSERT INTO pgsql_servers_ssl_params "
" (hostname, port, username, ssl_ca, ssl_cert, ssl_key, ssl_protocol_version_range, comment) "
" VALUES ('cluster-host', 5432, 'clusteruser', '/certs/ca.crt', '/certs/cert.crt', "
" '/certs/key.pem', 'TLSv1.2-TLSv1.3', 'cluster test') " ) ;
exec_ok ( pgsql_admin , " LOAD PGSQL SERVERS TO RUNTIME " ) ;
// Connect to MySQL admin port and execute the cluster query
MYSQL * mysql_admin = mysql_init ( NULL ) ;
if ( ! mysql_real_connect ( mysql_admin , cl . admin_host , cl . admin_username ,
cl . admin_password , NULL , cl . admin_port , NULL , 0 ) ) {
ok ( 0 , " Cluster query: MySQL admin connection failed: %s " , mysql_error ( mysql_admin ) ) ;
ok ( 0 , " Cluster query: skipping result check " ) ;
ok ( 0 , " Cluster query: skipping field check " ) ;
mysql_close ( mysql_admin ) ;
cleanup_ssl_params ( pgsql_admin ) ;
return ;
}
ok ( 1 , " Cluster query: MySQL admin connection established " ) ;
// Execute the PROXY_SELECT cluster query
const char * cluster_query =
" PROXY_SELECT hostname, port, username, ssl_ca, ssl_cert, ssl_key, "
" ssl_crl, ssl_crlpath, ssl_protocol_version_range, comment "
" FROM runtime_pgsql_servers_ssl_params ORDER BY hostname, port, username " ;
int rc = mysql_query ( mysql_admin , cluster_query ) ;
if ( rc ! = 0 ) {
ok ( 0 , " Cluster query: PROXY_SELECT failed: %s " , mysql_error ( mysql_admin ) ) ;
ok ( 0 , " Cluster query: skipping field check " ) ;
mysql_close ( mysql_admin ) ;
cleanup_ssl_params ( pgsql_admin ) ;
return ;
}
MYSQL_RES * result = mysql_store_result ( mysql_admin ) ;
ok ( result ! = NULL & & mysql_num_rows ( result ) = = 1 ,
" Cluster query: PROXY_SELECT returns 1 row " ) ;
if ( result & & mysql_num_rows ( result ) = = 1 ) {
MYSQL_ROW row = mysql_fetch_row ( result ) ;
bool hostname_ok = ( row [ 0 ] & & strcmp ( row [ 0 ] , " cluster-host " ) = = 0 ) ;
bool ssl_ca_ok = ( row [ 3 ] & & strcmp ( row [ 3 ] , " /certs/ca.crt " ) = = 0 ) ;
bool tls_range_ok = ( row [ 8 ] & & strcmp ( row [ 8 ] , " TLSv1.2-TLSv1.3 " ) = = 0 ) ;
ok ( hostname_ok & & ssl_ca_ok & & tls_range_ok ,
" Cluster query: returned row has correct hostname, ssl_ca, and ssl_protocol_version_range " ) ;
} else {
ok ( 0 , " Cluster query: skipping field check (no rows) " ) ;
}
if ( result ) mysql_free_result ( result ) ;
mysql_close ( mysql_admin ) ;
cleanup_ssl_params ( pgsql_admin ) ;
}
// ============================================================================
// main
// ============================================================================
int main ( int argc , char * * argv ) {
plan ( 34 ) ;
plan ( 3 7 ) ;
if ( cl . getEnv ( ) ) {
BAIL_OUT ( " Failed to get environment variables " ) ;
@ -546,8 +644,43 @@ int main(int argc, char** argv) {
test_tls_version_pin_causes_failure ( a ) ;
test_per_server_overrides_global ( a ) ;
test_remove_per_server_fallback_to_global ( a ) ;
// Configure monitor: use 'postgres' user which is accepted by the
// backend, set a tight connect_interval so cycles run within the test
// wait window. Restore original values afterwards.
long original_connect_interval = getConnectInterval ( a ) ;
std : : string original_monitor_username = exec_scalar ( a ,
" SELECT Variable_Value FROM global_variables WHERE Variable_Name='pgsql-monitor_username' " ) ;
std : : string original_monitor_password = exec_scalar ( a ,
" SELECT Variable_Value FROM global_variables WHERE Variable_Name='pgsql-monitor_password' " ) ;
diag ( " Original monitor: user=%s interval=%ld ms " ,
original_monitor_username . c_str ( ) , original_connect_interval ) ;
exec_ok ( a , " SET pgsql-monitor_username='postgres' " ) ;
exec_ok ( a , " SET pgsql-monitor_password='postgres' " ) ;
setConnectInterval ( a , 2000 ) ;
exec_ok ( a , " UPDATE pgsql_servers SET use_ssl=1 " ) ;
exec_ok ( a , " LOAD PGSQL SERVERS TO RUNTIME " ) ;
usleep ( 3000000 ) ; // let the monitor pick up the new settings
test_monitor_ssl_with_per_server_params ( a ) ;
test_monitor_uses_per_server_row ( a ) ;
// Restore original monitor settings
{
std : : stringstream q ;
q < < " SET pgsql-monitor_username=' " < < original_monitor_username < < " ' " ;
exec_ok ( a , q . str ( ) . c_str ( ) ) ;
q . str ( " " ) ;
q < < " SET pgsql-monitor_password=' " < < original_monitor_password < < " ' " ;
exec_ok ( a , q . str ( ) . c_str ( ) ) ;
}
setConnectInterval ( a , ( int ) original_connect_interval ) ;
// Part 3: Cluster query support
diag ( " ---- Part 3: Cluster Query Support ---- " ) ;
test_cluster_query_ssl_params ( a ) ;
// Cleanup
remove_bogus_cert_file ( ) ;
cleanup_ssl_params ( a ) ;