MXS-1635 Test program for local_address
Tests that local_address is taken into account. However, at the time of writing the maxscale VM does not have two usable IP addresses, so we only test that explicitly specifying an IP-address does not break things. Locally it has been confirmed that this indeed works the way it is supposed to.
This commit is contained in:
parent
7ae931ce9c
commit
facb8d60f7
1
maxscale-system-test/.gitignore
vendored
1
maxscale-system-test/.gitignore
vendored
@ -97,6 +97,7 @@ kill_master
|
||||
large_insert_hang
|
||||
load_balancing
|
||||
load_balancing_galera
|
||||
local_address
|
||||
long_sysbench
|
||||
longblob
|
||||
lots_of_rows
|
||||
|
@ -761,6 +761,9 @@ add_test_executable_notest(create_rds.cpp create_rds replication LABELS EXTERN_B
|
||||
# start sysbench ageints RWSplit for infinite execution
|
||||
add_test_executable_notest(long_sysbench.cpp long_sysbench replication LABELS readwritesplit REPL_BACKEND)
|
||||
|
||||
# test effect of local_address in configuration file
|
||||
add_test_executable(local_address.cpp local_address local_address LABELS REPL_BACKEND)
|
||||
|
||||
configure_file(templates.h.in templates.h @ONLY)
|
||||
|
||||
include(CTest)
|
||||
|
90
maxscale-system-test/cnf/maxscale.cnf.template.local_address
Normal file
90
maxscale-system-test/cnf/maxscale.cnf.template.local_address
Normal file
@ -0,0 +1,90 @@
|
||||
[maxscale]
|
||||
threads=###threads###
|
||||
###local_address###
|
||||
|
||||
[MySQL Monitor]
|
||||
type=monitor
|
||||
module=mysqlmon
|
||||
###repl51###
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
monitor_interval=1000
|
||||
detect_stale_master=false
|
||||
|
||||
[RW Split Router]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
router_options=slave_selection_criteria=LEAST_GLOBAL_CONNECTIONS
|
||||
max_slave_connections=1
|
||||
|
||||
[Read Connection Router Slave]
|
||||
type=service
|
||||
router=readconnroute
|
||||
router_options=slave
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
|
||||
[Read Connection Router Master]
|
||||
type=service
|
||||
router=readconnroute
|
||||
router_options=master
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
|
||||
[RW Split Listener]
|
||||
type=listener
|
||||
service=RW Split Router
|
||||
protocol=MySQLClient
|
||||
port=4006
|
||||
|
||||
[Read Connection Listener Slave]
|
||||
type=listener
|
||||
service=Read Connection Router Slave
|
||||
protocol=MySQLClient
|
||||
port=4009
|
||||
|
||||
[Read Connection Listener Master]
|
||||
type=listener
|
||||
service=Read Connection Router Master
|
||||
protocol=MySQLClient
|
||||
port=4008
|
||||
|
||||
[CLI]
|
||||
type=service
|
||||
router=cli
|
||||
|
||||
[CLI Listener]
|
||||
type=listener
|
||||
service=CLI
|
||||
protocol=maxscaled
|
||||
socket=default
|
||||
|
||||
[server1]
|
||||
type=server
|
||||
address=###node_server_IP_1###
|
||||
port=###node_server_port_1###
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server2]
|
||||
type=server
|
||||
address=###node_server_IP_2###
|
||||
port=###node_server_port_2###
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server3]
|
||||
type=server
|
||||
address=###node_server_IP_3###
|
||||
port=###node_server_port_3###
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server4]
|
||||
type=server
|
||||
address=###node_server_IP_4###
|
||||
port=###node_server_port_4###
|
||||
protocol=MySQLBackend
|
331
maxscale-system-test/local_address.cpp
Normal file
331
maxscale-system-test/local_address.cpp
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (c) 2018 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2019-07-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include "testconnections.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<class T>
|
||||
void to_collection(string s, const string& delimiter, T* pT)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
while ((pos = s.find(delimiter)) != std::string::npos)
|
||||
{
|
||||
pT->push_back(s.substr(0, pos));
|
||||
s.erase(0, pos + delimiter.length());
|
||||
}
|
||||
|
||||
if (s.length() != 0)
|
||||
{
|
||||
pT->push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
string& ltrim(std::string& s)
|
||||
{
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
|
||||
std::not1(std::ptr_fun<int, int>(std::isspace))));
|
||||
return s;
|
||||
}
|
||||
|
||||
string& rtrim(std::string& s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(),
|
||||
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
string& trim(std::string& s)
|
||||
{
|
||||
return ltrim(rtrim(s));
|
||||
}
|
||||
|
||||
string extract_ip(string s)
|
||||
{
|
||||
// 's' looks something like: " inet 127.0.0.1/...";
|
||||
s = s.substr(9); // => "127.0.0.1/...";
|
||||
s = s.substr(0, s.find_first_of('/')); // => "127.0.0.1"
|
||||
return s;
|
||||
}
|
||||
|
||||
void get_maxscale_ips(TestConnections& test, vector<string>* pIps)
|
||||
{
|
||||
string output(test.ssh_maxscale_output(false, "ip addr|fgrep inet|fgrep -v ::"));
|
||||
|
||||
to_collection(output, "\n", pIps);
|
||||
transform(pIps->begin(), pIps->end(), pIps->begin(), extract_ip);
|
||||
|
||||
pIps->erase(find(pIps->begin(), pIps->end(), "127.0.0.1"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void drop_user(TestConnections& test, const string& user, const string& host)
|
||||
{
|
||||
string stmt("DROP USER IF EXISTS ");
|
||||
|
||||
stmt += "'";
|
||||
stmt += user;
|
||||
stmt += "'@'";
|
||||
stmt += host;
|
||||
stmt += "'";
|
||||
test.try_query(test.conn_rwsplit, stmt.c_str());
|
||||
}
|
||||
|
||||
void create_user(TestConnections& test, const string& user, const string& password, const string& host)
|
||||
{
|
||||
string stmt("CREATE USER ");
|
||||
|
||||
stmt += "'";
|
||||
stmt += user;
|
||||
stmt += "'@'";
|
||||
stmt += host;
|
||||
stmt += "'";
|
||||
stmt += " IDENTIFIED BY ";
|
||||
stmt += "'";
|
||||
stmt += password;
|
||||
stmt += "'";
|
||||
test.try_query(test.conn_rwsplit, stmt.c_str());
|
||||
}
|
||||
|
||||
void grant_access(TestConnections& test, const string& user, const string& host)
|
||||
{
|
||||
string stmt("GRANT SELECT, INSERT, UPDATE ON *.* TO ");
|
||||
|
||||
stmt += "'";
|
||||
stmt += user;
|
||||
stmt += "'@'";
|
||||
stmt += host;
|
||||
stmt += "'";
|
||||
test.try_query(test.conn_rwsplit, stmt.c_str());
|
||||
|
||||
test.try_query(test.conn_rwsplit, "FLUSH PRIVILEGES");
|
||||
}
|
||||
|
||||
void create_user_and_grants(TestConnections& test,
|
||||
const string& user, const string& password, const string& host)
|
||||
{
|
||||
test.tprintf("Creating user: %s@%s", user.c_str(), host.c_str());
|
||||
|
||||
drop_user(test, user, host);
|
||||
create_user(test, user, password, host);
|
||||
grant_access(test, user, host);
|
||||
}
|
||||
|
||||
bool select_user(MYSQL* pMysql, string* pUser)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
if (mysql_query(pMysql, "SELECT USER()") == 0)
|
||||
{
|
||||
MYSQL_RES* pRes = mysql_store_result(pMysql);
|
||||
|
||||
if (mysql_num_rows(pRes) == 1)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pRes);
|
||||
*pUser = row[0];
|
||||
rv = true;
|
||||
}
|
||||
|
||||
mysql_free_result(pRes);
|
||||
|
||||
while (mysql_next_result(pMysql) == 0)
|
||||
{
|
||||
MYSQL_RES* pRes = mysql_store_result(pMysql);
|
||||
mysql_free_result(pRes);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool can_connect_to_maxscale(const char* zHost, int port, const char* zUser, const char* zPassword)
|
||||
{
|
||||
bool could_connect = false;
|
||||
|
||||
MYSQL* pMysql = mysql_init(NULL);
|
||||
|
||||
if (pMysql)
|
||||
{
|
||||
unsigned int timeout = 5;
|
||||
mysql_options(pMysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
|
||||
mysql_options(pMysql, MYSQL_OPT_READ_TIMEOUT, &timeout);
|
||||
mysql_options(pMysql, MYSQL_OPT_WRITE_TIMEOUT, &timeout);
|
||||
|
||||
if (mysql_real_connect(pMysql, zHost, zUser, zPassword, NULL, port, NULL, 0))
|
||||
{
|
||||
string user;
|
||||
if (select_user(pMysql, &user))
|
||||
{
|
||||
could_connect = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Could not 'SELECT USER()' as '" << zUser << "': " << mysql_error(pMysql) << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Could not connect as '" << zUser << "': " << mysql_error(pMysql) << endl;
|
||||
}
|
||||
|
||||
mysql_close(pMysql);
|
||||
}
|
||||
|
||||
return could_connect;
|
||||
}
|
||||
|
||||
string get_local_ip(TestConnections& test)
|
||||
{
|
||||
string output(test.ssh_maxscale_output(false, "nslookup maxscale|fgrep Server:|sed s/Server://"));
|
||||
return trim(output);
|
||||
}
|
||||
|
||||
void start_maxscale_with_local_address(TestConnections& test,
|
||||
const string& replace,
|
||||
const string& with)
|
||||
{
|
||||
string command("sed -i s/");
|
||||
command += replace;
|
||||
command += "/";
|
||||
command += with;
|
||||
command += "/ ";
|
||||
command += "/etc/maxscale.cnf";
|
||||
|
||||
test.ssh_maxscale_output(true, command.c_str());
|
||||
|
||||
test.start_maxscale();
|
||||
}
|
||||
|
||||
void test_connecting(TestConnections& test,
|
||||
const char* zUser, const char* zPassword, const char* zHost,
|
||||
bool should_be_able_to)
|
||||
{
|
||||
bool could_connect = can_connect_to_maxscale(test.maxscale_IP, test.rwsplit_port, zUser, zPassword);
|
||||
|
||||
if (!could_connect && should_be_able_to)
|
||||
{
|
||||
test.assert(false, "%s@%s should have been able to connect, but wasn't.", zUser, zHost);
|
||||
}
|
||||
else if (could_connect && !should_be_able_to)
|
||||
{
|
||||
test.assert(false, "%s@%s should NOT have been able to connect, but was.", zUser, zHost);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (could_connect)
|
||||
{
|
||||
test.tprintf("%s@%s could connect, as expected.", zUser, zHost);
|
||||
}
|
||||
else
|
||||
{
|
||||
test.tprintf("%s@%s could NOT connect, as expected.", zUser, zHost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run_test(TestConnections& test, const string& ip1, const string& ip2)
|
||||
{
|
||||
test.connect_maxscale();
|
||||
|
||||
string local_ip = get_local_ip(test);
|
||||
|
||||
const char* zUser1 = "alice";
|
||||
const char* zUser2 = "bob";
|
||||
const char* zPassword1 = "alicepwd";
|
||||
const char* zPassword2 = "bobpwd";
|
||||
|
||||
create_user_and_grants(test, zUser1, zPassword1, ip1);
|
||||
create_user_and_grants(test, zUser1, zPassword1, local_ip);
|
||||
create_user_and_grants(test, zUser2, zPassword2, ip2);
|
||||
create_user_and_grants(test, zUser2, zPassword2, local_ip);
|
||||
|
||||
test.tprintf("\n");
|
||||
test.tprintf("Testing default; alice should be able to access, bob not.");
|
||||
|
||||
test_connecting(test, zUser1, zPassword1, ip1.c_str(), true);
|
||||
test_connecting(test, zUser2, zPassword2, ip2.c_str(), false);
|
||||
|
||||
test.close_maxscale_connections();
|
||||
test.stop_maxscale();
|
||||
|
||||
test.tprintf("\n");
|
||||
test.tprintf("Testing with local_address=%s; alice should be able to access, bob not.",
|
||||
ip1.c_str());
|
||||
|
||||
string local_address_ip1 = "local_address=" + ip1;
|
||||
start_maxscale_with_local_address(test, "###local_address###", local_address_ip1);
|
||||
test.connect_maxscale();
|
||||
|
||||
test_connecting(test, zUser1, zPassword1, ip1.c_str(), true);
|
||||
test_connecting(test, zUser2, zPassword2, ip2.c_str(), false);
|
||||
|
||||
test.close_maxscale_connections();
|
||||
test.stop_maxscale();
|
||||
|
||||
test.tprintf("\n");
|
||||
test.tprintf("WARNING: Other IP-address not tested, as usable IP-address not available.");
|
||||
|
||||
#ifdef USABLE_SECOND_IP_ADDRESS_ON_MAXSCALE_NODE_IS_AVAILABLE
|
||||
test.tprintf("\n");
|
||||
test.tprintf("\nTesting with local_address=%s, bob should be able to access, alice not.",
|
||||
ip2.c_str());
|
||||
|
||||
string local_address_ip2 = "local_address=" + ip2;
|
||||
start_maxscale_with_local_address(test, local_address_ip1, local_address_ip2);
|
||||
test.connect_maxscale();
|
||||
|
||||
test_connecting(test, zUser1, zPassword1, ip1.c_str(), false);
|
||||
test_connecting(test, zUser2, zPassword2, ip2.c_str(), true);
|
||||
|
||||
test.close_maxscale_connections();
|
||||
test.stop_maxscale();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
TestConnections test(argc, argv);
|
||||
|
||||
vector<string> ips;
|
||||
get_maxscale_ips(test, &ips);
|
||||
|
||||
if (ips.size() >= 2)
|
||||
{
|
||||
run_test(test, ips[0], ips[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
test.assert(false, "MaxScale node does not have at least two IP-addresses.");
|
||||
}
|
||||
|
||||
return test.global_result;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user