test: Fix RESTAPI tests for containerized CI and improved debuggability

Update RESTAPI tests to use 'proxysql' hostname instead of 'localhost'
for compatibility with containerized CI environments. Add comprehensive
diagnostic logging including test headers, connection progress, and
better error reporting for easier debugging of test failures.
pull/5435/head
Rene Cannao 2 months ago
parent e086a236b6
commit eaecc26d22

@ -1418,7 +1418,7 @@ const char t_restapi_insert[] {
"VALUES (1,%ld,'%s','%s','%s','comm')",
};
const string base_address { "http://localhost:6070/sync/" };
const string base_address { "http://proxysql:6070/sync/" };
int configure_endpoints(
MYSQL* admin,
@ -1464,7 +1464,8 @@ int configure_endpoints(
int endpoint_timeout = wait_post_enpoint_ready(full_endpoint, "{}", 1000, 100);
if (endpoint_timeout) {
diag("Timeout while trying to reach first valid enpoint");
diag("Timeout while trying to reach first valid endpoint: %s", full_endpoint.c_str());
diag("This usually means RESTAPI enabled but script failed to execute. Check TAP_WORKDIR.");
return EXIT_FAILURE;
}

@ -31,7 +31,7 @@ using std::vector;
using hrc = std::chrono::high_resolution_clock;
using nlohmann::json;
const string base_address { "http://localhost:6070/sync/" };
const string base_address { "http://proxysql:6070/sync/" };
const vector<honest_req_t> honest_requests {
{ { "valid_output_script", "%s.py", "POST", 1000 }, { "{}" } },
@ -126,11 +126,23 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
diag("=== Regression Test #3223: RESTAPI Script Execution & Return Codes ===");
diag("This test ensures that ProxySQL RESTAPI correctly handles script execution");
diag("and returns the expected HTTP status codes and script exit codes.");
diag("The test strategy is:");
diag("1. Register multiple valid and faulty scripts in RESTAPI routes.");
diag("2. Issue POST/GET requests to these endpoints.");
diag("3. Verify HTTP return codes (200 for success, 400/424 for failures).");
diag("4. Verify internal script error codes (exit codes, timeouts, signals).");
diag("5. Check behavior with large outputs and partial flushes.");
diag("=========================================================================");
plan(count_exp_tests(honest_requests, invalid_requests));
MYSQL* admin = mysql_init(NULL);
// Initialize connections
diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username);
if (!admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
return EXIT_FAILURE;
@ -142,12 +154,14 @@ int main(int argc, char** argv) {
}
// Enable 'RESTAPI'
diag("Enabling RESTAPI on port 6070");
MYSQL_QUERY(admin, "SET admin-restapi_enabled='true'");
MYSQL_QUERY(admin, "SET admin-restapi_port=6070");
MYSQL_QUERY(admin, "LOAD ADMIN VARIABLES TO RUNTIME");
// Clean current 'restapi_routes' if any
diag("Configuring RESTAPI routes...");
MYSQL_QUERY(admin, "DELETE FROM restapi_routes");
// Configure restapi_routes to be used
@ -161,18 +175,20 @@ int main(int argc, char** argv) {
honest_requests.begin(), honest_requests.end(), std::back_inserter(v_epts_info), ext_v_epts_info
);
diag("Initializing endpoints for valid requests...");
int ept_conf_res = configure_endpoints(admin, script_base_path, v_epts_info, dummy_ept, true);
if (ept_conf_res) {
diag("Endpoint configuration failed. Skipping endpoint testing...");
return EXIT_FAILURE;
}
diag("Testing valid RESTAPI requests...");
{
for (const auto& req : honest_requests) {
for (const string& params : req.params) {
const string ept { join_path(base_address, req.ept_info.name) };
diag(
"Checking valid '%s' request - ept: '%s', params: '%s'",
" Checking valid '%s' request - ept: '%s', params: '%s'",
req.ept_info.method.c_str(), ept.c_str(), params.c_str()
);
std::chrono::nanoseconds duration;
@ -294,12 +310,14 @@ int main(int argc, char** argv) {
invalid_requests.begin(), invalid_requests.end(), std::back_inserter(i_epts_info), ext_i_epts_info
);
diag("Initializing endpoints for invalid requests...");
ept_conf_res = configure_endpoints(admin, script_base_path, i_epts_info, dummy_ept, true);
if (ept_conf_res) {
diag("Endpoint configuration failed. Skipping endpoint testing...");
return EXIT_FAILURE;
}
diag("Testing invalid RESTAPI requests...");
for (const auto& req : invalid_requests) {
for (const ept_pl_t& ept_pl : req.ept_pls) {
std::chrono::nanoseconds duration;
@ -308,7 +326,7 @@ int main(int argc, char** argv) {
const string ept { join_path(base_address, req.ept_info.name) };
diag(
"Checking valid '%s' request - ept: '%s', params: '%s'",
" Checking invalid '%s' request - ept: '%s', params: '%s'",
req.ept_info.method.c_str(), ept.c_str(), ept_pl.params.c_str()
);

@ -38,6 +38,17 @@ int main(int argc, char** argv) {
diag("Failed to get the required environmental variables.");
return -1;
}
diag("=== Regression Test #3591: RESTAPI with High Number of FDs ===");
diag("This test verifies that the ProxySQL metrics endpoint remains functional");
diag("even when a large number of client connections (2047) are opened.");
diag("The test strategy is:");
diag("1. Elevate process FD limits.");
diag("2. Enable RESTAPI on port 6070.");
diag("3. Open 2047 concurrent MySQL connections to ProxySQL.");
diag("4. Verify that the /metrics endpoint is still reachable.");
diag("==============================================================");
struct rlimit limits { 0, 0 };
getrlimit(RLIMIT_NOFILE, &limits);
diag("Old process limits: { %ld, %ld }", limits.rlim_cur, limits.rlim_max);
@ -48,6 +59,7 @@ int main(int argc, char** argv) {
MYSQL* admin = mysql_init(NULL);
// Initialize connections
diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username);
if (!admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
return -1;
@ -59,12 +71,14 @@ int main(int argc, char** argv) {
}
// Enable 'RESTAPI'
diag("Enabling RESTAPI on port 6070");
MYSQL_QUERY(admin, "SET admin-restapi_enabled='true'");
MYSQL_QUERY(admin, "SET admin-restapi_port=6070");
MYSQL_QUERY(admin, "LOAD ADMIN VARIABLES TO RUNTIME");
std::vector<MYSQL*> mysql_connections {};
diag("Establishing %d connections to ProxySQL...", NUM_CONNECTIONS);
for (int i = 0; i < NUM_CONNECTIONS; i++) {
MYSQL* proxy = mysql_init(NULL);
if (
@ -79,11 +93,15 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
mysql_connections.push_back(proxy);
if ((i + 1) % 500 == 0) diag(" Established %d/%d connections...", i + 1, NUM_CONNECTIONS);
}
diag("Connections established.");
int endpoint_timeout = wait_get_enpoint_ready("http://localhost:6070/metrics/", 1000, 100);
diag("Verifying RESTAPI /metrics endpoint via proxysql:6070...");
int endpoint_timeout = wait_get_enpoint_ready("http://proxysql:6070/metrics/", 1000, 100);
ok(endpoint_timeout == 0, "The endpoint should be available instead of timing out.");
diag("Closing connections and cleaning up...");
for (int i = 0; i < NUM_CONNECTIONS; i++) {
mysql_close(mysql_connections[i]);
}

@ -35,7 +35,7 @@ using std::string;
using std::vector;
const int NUM_CONNECTIONS = 1300;
const string base_address { "http://localhost:6070/sync/" };
const string base_address { "http://proxysql:6070/sync/" };
const vector<honest_req_t> honest_requests {
{ { "valid_output_script", "%s.py", "POST", 1000 }, { "{}" } },
@ -56,6 +56,17 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
diag("=== Regression Test #4001: RESTAPI with High FD Usage ===");
diag("This test verifies that the RESTAPI remains operational when ProxySQL");
diag("is handling a large number of concurrent file descriptors (> FD_SETSIZE).");
diag("The test strategy is:");
diag("1. Elevate process FD limits.");
diag("2. Establish 1300 concurrent MySQL connections to ProxySQL.");
diag("3. While these connections are held, perform continuous RESTAPI requests.");
diag("4. Simultaneously create and destroy additional MySQL connections.");
diag("5. Verify that RESTAPI requests and connection operations remain stable.");
diag("==========================================================");
diag("Setting new process limits beyond 'FD_SETSIZE'");
struct rlimit limits { 0, 0 };
getrlimit(RLIMIT_NOFILE, &limits);
@ -67,6 +78,7 @@ int main(int argc, char** argv) {
MYSQL* admin = mysql_init(NULL);
// Initialize connections
diag("Connecting to ProxySQL Admin at %s:%d as %s", cl.host, cl.admin_port, cl.admin_username);
if (!admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
return EXIT_FAILURE;
@ -77,7 +89,7 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
string script_base_path { string { cl.workdir } + "reg_test_3223_scripts" };
const char* d_env = getenv("REGULAR_INFRA_DATADIR"); string script_base_path = (d_env ? string(d_env) + "/reg_test_3223_scripts" : string(cl.workdir) + "reg_test_3223_scripts");
const ept_info_t dummy_ept { "dummy_ept_script", "%s.py", "POST", 1000 };
vector<ept_info_t> v_epts_info {};
@ -86,6 +98,7 @@ int main(int argc, char** argv) {
honest_requests.begin(), honest_requests.end(), std::back_inserter(v_epts_info), ext_v_epts_info
);
diag("Configuring RESTAPI endpoints...");
int ept_conf_res = configure_endpoints(admin, script_base_path, v_epts_info, dummy_ept, true);
if (ept_conf_res) {
diag("Endpoint configuration failed. Skipping endpoint testing...");
@ -94,6 +107,7 @@ int main(int argc, char** argv) {
std::vector<MYSQL*> mysql_connections {};
diag("Establishing %d baseline connections to ProxySQL...", NUM_CONNECTIONS);
for (int i = 0; i < NUM_CONNECTIONS; i++) {
MYSQL* proxy = mysql_init(NULL);
if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
@ -101,7 +115,9 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
mysql_connections.push_back(proxy);
if ((i + 1) % 200 == 0) diag(" Established %d/%d connections...", i + 1, NUM_CONNECTIONS);
}
diag("Baseline connections established.");
typedef std::chrono::high_resolution_clock hrc;
const uint64_t test_duration = 10000;
@ -111,6 +127,7 @@ int main(int argc, char** argv) {
uint64_t conn_count = 0;
uint64_t mysql_fails = 0;
diag("Starting simultaneous RESTAPI requests and connection churn (Duration: %ldms)...", test_duration);
{
std::thread create_conns([&]() -> int {
std::chrono::nanoseconds duration;
@ -120,7 +137,7 @@ int main(int argc, char** argv) {
start = hrc::now();
while (true) {
if (mysql_fails > (conn_count * FAILURE_RATE) / 100) {
if (conn_count > 0 && mysql_fails > (conn_count * FAILURE_RATE) / 100) {
diag("Too many mysql failures in connection creation, considering test as failed...");
conn_creation_res = EXIT_FAILURE;
return EXIT_FAILURE;
@ -129,17 +146,17 @@ int main(int argc, char** argv) {
MYSQL* proxy = mysql_init(NULL);
if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy));
mysql_fails += 1;
}
int rc = mysql_query(proxy, "DO 1");
if (rc) {
diag("mysql_errno: '%d', mysql_error: '%s'", mysql_errno(proxy), mysql_error(proxy));
diag("Churn connection failed: %s", mysql_error(proxy));
mysql_fails += 1;
} else {
int rc = mysql_query(proxy, "DO 1");
if (rc) {
diag("Churn query failed: mysql_errno: '%d', mysql_error: '%s'", mysql_errno(proxy), mysql_error(proxy));
mysql_fails += 1;
}
mysql_close(proxy);
}
mysql_close(proxy);
end = hrc::now();
duration = end - start;
@ -160,6 +177,7 @@ int main(int argc, char** argv) {
start = hrc::now();
int api_request_count = 0;
while (true) {
int rc = 0;
@ -191,10 +209,13 @@ int main(int argc, char** argv) {
}
if (curl_err == 7) {
diag("Operation over endpoint failed with 'CURLE_COULDNT_CONNECT'. Aborting...");
diag("Operation over endpoint %s failed with 'CURLE_COULDNT_CONNECT'. Aborting...", ept.c_str());
rc = 1;
break;
}
api_request_count++;
}
if (rc) break;
}
end = hrc::now();
@ -205,7 +226,9 @@ int main(int argc, char** argv) {
}
}
diag("Joining churn thread and cleaning up...");
create_conns.join();
diag("Churn thread finished. Total API requests: %d, Total churn conns: %ld, Failures: %ld", api_request_count, conn_count, mysql_fails);
ok(
conn_creation_res == EXIT_SUCCESS,

Loading…
Cancel
Save