Merge branch '2.1' into 2.2

This commit is contained in:
Johan Wikman
2018-02-02 15:31:18 +02:00
15 changed files with 597 additions and 11 deletions

View File

@ -106,6 +106,7 @@ kill_master
large_insert_hang
load_balancing
load_balancing_galera
local_address
long_sysbench
longblob
lots_of_rows

View File

@ -840,6 +840,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)

View File

@ -28,6 +28,7 @@ static const char* integer_values[] =
"-1",
"20",
"-20",
"NULL",
NULL
};
@ -47,6 +48,7 @@ static const char* decimal_values[] =
"-1.5",
"20.5",
"-20.5",
"NULL",
NULL
};
@ -65,7 +67,7 @@ static const char* string_values[] =
{
"\"Hello world!\"",
"\"The quick brown fox jumps over the lazy dog\"",
// "\"The Unicode should work: äöåǢ\"",
"NULL",
NULL
};
@ -85,26 +87,27 @@ static const char* binary_values[] =
"\"Hello world!\"",
"\"The quick brown fox jumps over the lazy dog\"",
"NULL",
// "\"The Unicode should work: äöåǢ\"",
// "\"These should work for binary types: ⦿☏☃☢😤😂\"",
NULL
};
static const char* datetime_types[] =
{
"DATETIME",
"DATETIME(1)",
"DATETIME(2)",
"DATETIME(3)",
"DATETIME(4)",
"DATETIME(5)",
"DATETIME(6)",
"TIMESTAMP",
// TODO: Fix test setup to use same timezone
// "TIMESTAMP",
NULL
};
static const char* datetime_values[] =
{
"'2018-01-01 11:11:11'",
"NULL",
NULL
};
@ -117,6 +120,26 @@ static const char* date_types[] =
static const char* date_values[] =
{
"'2018-01-01'",
"NULL",
NULL
};
static const char* time_types[] =
{
"TIME",
"TIME(1)",
"TIME(2)",
"TIME(3)",
"TIME(4)",
"TIME(5)",
"TIME(6)",
NULL
};
static const char* time_values[] =
{
"'12:00:00'",
"NULL",
NULL
};
@ -132,6 +155,7 @@ struct
{ binary_types, binary_values },
{ datetime_types, datetime_values },
{ date_types, date_values },
{ time_types, time_values },
{ 0 }
};

View File

@ -39,8 +39,10 @@ public:
bool operator ==(const TestOutput& output) const
{
return m_value == output.getValue() ||
(m_type.find("BLOB") != std::string::npos &&
output.getValue().length() == 0);
(m_type.find("BLOB") != std::string::npos && output.getValue().length() == 0) ||
// A NULL timestamp appears to be inserted as NOW() by default in 10.2, a NULL INT is
// inserted as 0 and a NULL string gets converted into an empty string by the CDC system
(m_value == "NULL" && (output.getValue().empty() || m_type == "TIMESTAMP" || output.getValue() == "0"));
}
bool operator !=(const TestOutput& output) const

View 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

View File

@ -60,6 +60,8 @@ int main(int argc, char *argv[])
Test->maxscales->ssh_node(0, (char *) "yum clean all", true);
Test->maxscales->ssh_node(0, (char *) "yum install rng-tools -y", true);
Test->maxscales->ssh_node(0, (char *) "rngd -r /dev/urandom -o /dev/random", true);
Test->maxscales->ssh_node(0, (char *)
"yum install -y MariaDB-gssapi-server MariaDB-gssapi-client krb5-server krb5-workstation pam_krb5", true);
Test->maxscales->ssh_node_f(0, true, (char *)
"yum install -y MariaDB-gssapi-server MariaDB-gssapi-client krb5-server krb5-workstation pam_krb5", true);

View File

@ -0,0 +1,333 @@
/*
* 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)
{
int exit_code;
string output(test.maxscales->ssh_node_output(0, "ip addr|fgrep inet|fgrep -v ::", false, &exit_code));
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.maxscales->conn_rwsplit[0], 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.maxscales->conn_rwsplit[0], 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.maxscales->conn_rwsplit[0], stmt.c_str());
test.try_query(test.maxscales->conn_rwsplit[0], "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)
{
int exit_code;
string output(test.maxscales->ssh_node_output(0, "nslookup maxscale|fgrep Server:|sed s/Server://", false, &exit_code));
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.maxscales->ssh_node(0, command.c_str(), true);
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.maxscales->IP[0], test.maxscales->rwsplit_port[0], 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.maxscales->connect();
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.maxscales->disconnect();
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.maxscales->connect();
test_connecting(test, zUser1, zPassword1, ip1.c_str(), true);
test_connecting(test, zUser2, zPassword2, ip2.c_str(), false);
test.maxscales->disconnect();
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.maxscales->disconnect();
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;
}

View File

@ -20,7 +20,8 @@ int main(int argc, char** argv)
// Give the monitor some time to detect it
sleep(5);
test.add_result(execute_query_silent(test.maxscales->conn_master[0], "SELECT 1") == 0, "Query should fail");
test.add_result(execute_query_silent(test.maxscales->conn_master[0], "SELECT 1") == 0,
"Query should fail");
// Change the master back to the original one
test.repl->change_master(0, 1);