Merge branch '2.2' into develop

This commit is contained in:
Markus Mäkelä
2018-03-06 17:02:03 +02:00
13 changed files with 256 additions and 145 deletions

View File

@ -9,14 +9,17 @@
echo TRAVIS_BUILD_DIR: ${TRAVIS_BUILD_DIR} echo TRAVIS_BUILD_DIR: ${TRAVIS_BUILD_DIR}
# Configure the build environment
./BUILD/install_build_deps.sh
mkdir build mkdir build
cd build cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=Y -DBUILD_AVRO=N cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=Y
make make
make test make test || exit 1
sudo make install
sudo make install
sudo ./postinst sudo ./postinst
maxscale --version maxscale --version

View File

@ -15,7 +15,7 @@ then
sudo apt-get install -y --force-yes dpkg-dev git wget \ sudo apt-get install -y --force-yes dpkg-dev git wget \
build-essential libssl-dev ncurses-dev bison flex \ build-essential libssl-dev ncurses-dev bison flex \
perl libtool libpcre3-dev tcl tcl-dev uuid \ perl libtool libpcre3-dev tcl tcl-dev uuid \
uuid-dev libsqlite3-dev liblzma-dev libpam0g-dev uuid-dev libsqlite3-dev liblzma-dev libpam0g-dev pkg-config
## separatelibgnutls installation process for Ubuntu Trusty ## separatelibgnutls installation process for Ubuntu Trusty
cat /etc/*release | grep -E "Trusty|wheezy" cat /etc/*release | grep -E "Trusty|wheezy"
if [ $? == 0 ] if [ $? == 0 ]
@ -76,18 +76,18 @@ else
fi fi
# cmake # cmake
wget http://max-tst-01.mariadb.com/ci-repository/cmake-3.7.1-Linux-x86_64.tar.gz --no-check-certificate wget -q http://max-tst-01.mariadb.com/ci-repository/cmake-3.7.1-Linux-x86_64.tar.gz --no-check-certificate
if [ $? != 0 ] ; then if [ $? != 0 ] ; then
echo "CMake can not be downloaded from Maxscale build server, trying from cmake.org" echo "CMake can not be downloaded from Maxscale build server, trying from cmake.org"
wget https://cmake.org/files/v3.7/cmake-3.7.1-Linux-x86_64.tar.gz --no-check-certificate wget -q https://cmake.org/files/v3.7/cmake-3.7.1-Linux-x86_64.tar.gz --no-check-certificate
fi fi
sudo tar xzvf cmake-3.7.1-Linux-x86_64.tar.gz -C /usr/ --strip-components=1 sudo tar xzf cmake-3.7.1-Linux-x86_64.tar.gz -C /usr/ --strip-components=1
cmake_version=`cmake --version | grep "cmake version" | awk '{ print $3 }'` cmake_version=`cmake --version | grep "cmake version" | awk '{ print $3 }'`
if [ "$cmake_version" \< "3.7.1" ] ; then if [ "$cmake_version" \< "3.7.1" ] ; then
echo "cmake does not work! Trying to build from source" echo "cmake does not work! Trying to build from source"
wget https://cmake.org/files/v3.7/cmake-3.7.1.tar.gz --no-check-certificate wget -q https://cmake.org/files/v3.7/cmake-3.7.1.tar.gz --no-check-certificate
tar xzvf cmake-3.7.1.tar.gz tar xzf cmake-3.7.1.tar.gz
cd cmake-3.7.1 cd cmake-3.7.1
./bootstrap ./bootstrap
@ -116,7 +116,7 @@ cd ../../
# TCL # TCL
mkdir tcl mkdir tcl
cd tcl cd tcl
wget --no-check-certificate http://prdownloads.sourceforge.net/tcl/tcl8.6.5-src.tar.gz wget -q --no-check-certificate http://prdownloads.sourceforge.net/tcl/tcl8.6.5-src.tar.gz
if [ $? != 0 ] if [ $? != 0 ]
then then
@ -124,7 +124,7 @@ then
exit 1 exit 1
fi fi
tar xzvf tcl8.6.5-src.tar.gz tar xzf tcl8.6.5-src.tar.gz
cd tcl8.6.5/unix cd tcl8.6.5/unix
./configure ./configure
sudo make install sudo make install
@ -149,7 +149,7 @@ sudo make install
cd ../../ cd ../../
# Avro C API # Avro C API
wget -r -l1 -nH --cut-dirs=2 --no-parent -A.tar.gz --no-directories http://mirror.netinch.com/pub/apache/avro/stable/c wget -q -r -l1 -nH --cut-dirs=2 --no-parent -A.tar.gz --no-directories http://mirror.netinch.com/pub/apache/avro/stable/c
if [ $? != 0 ] if [ $? != 0 ]
then then
echo "Error getting avro-c" echo "Error getting avro-c"

View File

@ -28,6 +28,7 @@
the master. There is also limited capability for rejoining nodes. the master. There is also limited capability for rejoining nodes.
For more details, please refer to: For more details, please refer to:
* [MariaDB MaxScale 2.2.3 Release Notes](Release-Notes/MaxScale-2.2.3-Release-Notes.md)
* [MariaDB MaxScale 2.2.2 Release Notes](Release-Notes/MaxScale-2.2.2-Release-Notes.md) * [MariaDB MaxScale 2.2.2 Release Notes](Release-Notes/MaxScale-2.2.2-Release-Notes.md)
* [MariaDB MaxScale 2.2.1 Release Notes](Release-Notes/MaxScale-2.2.1-Release-Notes.md) * [MariaDB MaxScale 2.2.1 Release Notes](Release-Notes/MaxScale-2.2.1-Release-Notes.md)
* [MariaDB MaxScale 2.2.0 Release Notes](Release-Notes/MaxScale-2.2.0-Release-Notes.md) * [MariaDB MaxScale 2.2.0 Release Notes](Release-Notes/MaxScale-2.2.0-Release-Notes.md)

View File

@ -17,6 +17,18 @@ being monitored by the mariadbmon monitor, the current GTID position will be
displayed in the newly added column. If no GTID is available, an empty value is displayed in the newly added column. If no GTID is available, an empty value is
returned. returned.
### MaxAdmin input from scripts
The failure to set terminal attributes for MaxScale is no longer considered an
error as scripts most often do not have an actual terminal that control the
process. This means that passwords and other commands can be passed to MaxAdmin
without a controlling terminal.
### MaxCtrl password input
MaxCtrl can now query the password from the user. This allows passwords to be
given without giving them as process arguments.
## Dropped Features ## Dropped Features
## New Features ## New Features
@ -25,6 +37,13 @@ returned.
[Here is a list of bugs fixed in MaxScale 2.2.3.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.2.3) [Here is a list of bugs fixed in MaxScale 2.2.3.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.2.3)
* [MXS-1698](https://jira.mariadb.org/browse/MXS-1698) error:140940F5:SSL routines:ssl3_read_bytes:unexpected record
* [MXS-1697](https://jira.mariadb.org/browse/MXS-1697) MaxScale 2.2.2 missing avrorouter library
* [MXS-1693](https://jira.mariadb.org/browse/MXS-1693) In Maxscale 2.2.2 getting users with native password from mysql backends does not work
* [MXS-1688](https://jira.mariadb.org/browse/MXS-1688) Some date functions are not parsed properly with schemarouter
* [MXS-1684](https://jira.mariadb.org/browse/MXS-1684) Empty space on a line in rule file confuses dbfwfilter which refuses to start
* [MXS-1683](https://jira.mariadb.org/browse/MXS-1683) Commands that take passwords should allow input from stdin and not just from controlling terminals
## Known Issues and Limitations ## Known Issues and Limitations
There are some limitations and known issues within this version of MaxScale. There are some limitations and known issues within this version of MaxScale.

View File

@ -43,6 +43,9 @@
#ifdef HISTORY #ifdef HISTORY
#include <histedit.h> #include <histedit.h>
#define USE_HIST 1
#else
#define USE_HIST 0
#endif #endif
#define MAX_PASSWORD_LEN 80 #define MAX_PASSWORD_LEN 80
@ -62,6 +65,7 @@ static void read_inifile(char **socket,
char **hostname, char **port, char **user, char **passwd, char **hostname, char **port, char **user, char **passwd,
int *editor); int *editor);
static bool getPassword(char *password, size_t length); static bool getPassword(char *password, size_t length);
static void rtrim(char *str);
#ifdef HISTORY #ifdef HISTORY
@ -93,6 +97,128 @@ static struct option long_options[] =
#define MAXADMIN_DEFAULT_USER "admin" #define MAXADMIN_DEFAULT_USER "admin"
#define MAXADMIN_BUFFER_SIZE 2048 #define MAXADMIN_BUFFER_SIZE 2048
static bool term_error = false;
bool process_command(int so, char* buf)
{
bool rval = true;
if (isquit(buf))
{
rval = false;
}
else if (!strncasecmp(buf, "source", 6))
{
char *ptr;
/* Find the filename */
ptr = &buf[strlen("source")];
while (*ptr && isspace(*ptr))
{
ptr++;
}
DoSource(so, ptr);
}
else if (*buf)
{
if (!sendCommand(so, buf))
{
rval = false;
}
}
return rval;
}
void cmd_with_history(int so, char** argv, bool use_emacs)
{
#ifdef HISTORY
char *buf;
EditLine *el = NULL;
Tokenizer *tok;
History *hist;
HistEvent ev;
hist = history_init(); /* Init the builtin history */
/* Remember 100 events */
history(hist, &ev, H_SETSIZE, 100);
tok = tok_init(NULL); /* Initialize the tokenizer */
/* Initialize editline */
el = el_init(*argv, stdin, stdout, stderr);
if (use_emacs)
{
el_set(el, EL_EDITOR, "emacs"); /** Editor is emacs */
}
else
{
el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */
}
el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
el_set(el, EL_PROMPT, prompt); /* Set the prompt function */
/* Tell editline to use this history interface */
el_set(el, EL_HIST, history, hist);
/*
* Bind j, k in vi command mode to previous and next line, instead
* of previous and next history.
*/
el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL);
el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL);
/*
* Source the user's defaults file.
*/
el_source(el, NULL);
int num = 0;
while ((buf = (char *) el_gets(el, &num)))
{
rtrim(buf);
history(hist, &ev, H_ENTER, buf);
if (!strcasecmp(buf, "history"))
{
for (int rv = history(hist, &ev, H_LAST); rv != -1;
rv = history(hist, &ev, H_PREV))
{
fprintf(stdout, "%4d %s\n", ev.num, ev.str);
}
}
else if (!process_command(so, buf))
{
break;
}
}
el_end(el);
tok_end(tok);
history_end(hist);
#endif
}
void cmd_no_history(int so)
{
char buf[MAXADMIN_BUFFER_SIZE];
while (printf("MaxScale> ") && fgets(buf, 1024, stdin) != NULL)
{
rtrim(buf);
if (!strcasecmp(buf, "history"))
{
fprintf(stderr, "History not supported in this version.\n");
}
else if (!process_command(so, buf))
{
break;
}
}
}
/** /**
* The main for the maxadmin client * The main for the maxadmin client
* *
@ -102,15 +228,6 @@ static struct option long_options[] =
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
#ifdef HISTORY
char *buf;
EditLine *el = NULL;
Tokenizer *tok;
History *hist;
HistEvent ev;
#else
char buf[MAXADMIN_BUFFER_SIZE];
#endif
char *hostname = NULL; char *hostname = NULL;
char *port = NULL; char *port = NULL;
char *user = NULL; char *user = NULL;
@ -293,105 +410,16 @@ main(int argc, char **argv)
} }
(void) setlocale(LC_CTYPE, ""); (void) setlocale(LC_CTYPE, "");
#ifdef HISTORY
hist = history_init(); /* Init the builtin history */
/* Remember 100 events */
history(hist, &ev, H_SETSIZE, 100);
tok = tok_init(NULL); /* Initialize the tokenizer */ if (!term_error && USE_HIST)
/* Initialize editline */
el = el_init(*argv, stdin, stdout, stderr);
if (use_emacs)
{ {
el_set(el, EL_EDITOR, "emacs"); /** Editor is emacs */ cmd_with_history(so, argv, use_emacs);
} }
else else
{ {
el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */ cmd_no_history(so);
}
el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
el_set(el, EL_PROMPT, prompt); /* Set the prompt function */
/* Tell editline to use this history interface */
el_set(el, EL_HIST, history, hist);
/*
* Bind j, k in vi command mode to previous and next line, instead
* of previous and next history.
*/
el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL);
el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL);
/*
* Source the user's defaults file.
*/
el_source(el, NULL);
int num;
while ((buf = (char *) el_gets(el, &num)) != NULL && num != 0)
{
#else
while (printf("MaxScale> ") && fgets(buf, 1024, stdin) != NULL)
{
int num = strlen(buf);
#endif
/* Strip trailing \n\r */
for (int i = num - 1; buf[i] == '\r' || buf[i] == '\n'; i--)
{
buf[i] = 0;
}
#ifdef HISTORY
history(hist, &ev, H_ENTER, buf);
#endif
if (isquit(buf))
{
break;
}
else if (!strcasecmp(buf, "history"))
{
#ifdef HISTORY
int rv;
for (rv = history(hist, &ev, H_LAST); rv != -1;
rv = history(hist, &ev, H_PREV))
{
fprintf(stdout, "%4d %s\n",
ev.num, ev.str);
}
#else
fprintf(stderr, "History not supported in this version.\n");
#endif
}
else if (!strncasecmp(buf, "source", 6))
{
char *ptr;
/* Find the filename */
ptr = &buf[strlen("source")];
while (*ptr && isspace(*ptr))
{
ptr++;
}
DoSource(so, ptr);
}
else if (*buf)
{
if (!sendCommand(so, buf))
{
return 0;
}
}
} }
#ifdef HISTORY
el_end(el);
tok_end(tok);
history_end(hist);
#endif
close(so); close(so);
return 0; return 0;
} }
@ -937,8 +965,7 @@ read_inifile(char **socket,
*/ */
bool getPassword(char *passwd, size_t len) bool getPassword(char *passwd, size_t len)
{ {
bool gotten = false; bool err = false;
struct termios tty_attr; struct termios tty_attr;
tcflag_t c_lflag; tcflag_t c_lflag;
@ -948,36 +975,55 @@ bool getPassword(char *passwd, size_t len)
tty_attr.c_lflag &= ~ICANON; tty_attr.c_lflag &= ~ICANON;
tty_attr.c_lflag &= ~ECHO; tty_attr.c_lflag &= ~ECHO;
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) == 0) if (tcsetattr(STDIN_FILENO, 0, &tty_attr) != 0)
{ {
printf("Password: "); err = true;
if (fgets(passwd, len, stdin) == NULL) }
{ }
printf("Failed to read password\n"); else
} {
err = true;
}
tty_attr.c_lflag = c_lflag; if (err)
{
fprintf(stderr,
"Warning: Could not configure terminal. Terminal echo is still enabled. This\n"
"means that the password will be visible on the controlling terminal when\n"
"it is written!\n");
}
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) == 0) printf("Password: ");
{ if (fgets(passwd, len, stdin) == NULL)
int i = strlen(passwd); {
printf("Failed to read password\n");
}
if (i > 1) if (!err)
{ {
passwd[i - 1] = '\0'; tty_attr.c_lflag = c_lflag;
}
printf("\n"); if (tcsetattr(STDIN_FILENO, 0, &tty_attr) != 0)
{
gotten = true; err = true;
}
} }
} }
if (!gotten) int i = strlen(passwd);
if (i > 0)
{ {
fprintf(stderr, "Could not configure terminal.\n"); passwd[i - 1] = '\0';
} }
return gotten; printf("\n");
// Store failure globally so that interactive parts are skipped
if (err)
{
term_error = true;
}
return *passwd;
} }

View File

@ -17,6 +17,7 @@ var Table = require('cli-table');
var consoleLib = require('console') var consoleLib = require('console')
var os = require('os') var os = require('os')
var fs = require('fs') var fs = require('fs')
var readlineSync = require('readline-sync')
module.exports = function() { module.exports = function() {
@ -27,6 +28,13 @@ module.exports = function() {
// servers. // servers.
this.maxctrl = function(argv, cb) { this.maxctrl = function(argv, cb) {
// No password given, ask it from the command line
if (argv.p == '') {
argv.p = readlineSync.question('Enter password: ', {
hideEchoBack: true
})
}
// Split the hostnames, separated by commas // Split the hostnames, separated by commas
argv.hosts = argv.hosts.split(',') argv.hosts = argv.hosts.split(',')
@ -209,7 +217,7 @@ module.exports = function() {
base = 'https://' base = 'https://'
} }
return base + argv.user + ':' + argv.password + '@' + host + '/v1/' + endpoint return base + argv.u + ':' + argv.p + '@' + host + '/v1/' + endpoint
} }
this.OK = function() { this.OK = function() {

View File

@ -31,7 +31,7 @@ program
}) })
.option('p', { .option('p', {
alias: 'password', alias: 'password',
describe: 'Password for the user', describe: 'Password for the user. To input the password manually, give -p as the last argument or use --password=\'\'',
default: 'mariadb', default: 'mariadb',
type: 'string' type: 'string'
}) })
@ -104,7 +104,7 @@ program
.demandCommand(1, 'At least one command is required') .demandCommand(1, 'At least one command is required')
.command('*', 'the default command', {}, function(argv) { .command('*', 'the default command', {}, function(argv) {
maxctrl(argv, function() { maxctrl(argv, function() {
return error('Unknown command. See output of `help` for a list of commands.') return error('Unknown command ' + JSON.stringify(argv._) + '. See output of `help` for a list of commands.')
}) })
}) })

View File

@ -19,6 +19,7 @@
"cli-table": "^0.3.1", "cli-table": "^0.3.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"lodash-getpath": "^0.2.4", "lodash-getpath": "^0.2.4",
"readline-sync": "^1.4.9",
"request": "^2.81.0", "request": "^2.81.0",
"request-promise-native": "^1.0.3", "request-promise-native": "^1.0.3",
"yargs": "^8.0.2" "yargs": "^8.0.2"

View File

@ -6,6 +6,14 @@
#include "testconnections.h" #include "testconnections.h"
void list_servers(TestConnections& test)
{
int rc;
char *output = test.maxscales->ssh_node_output_f(0, true, &rc, "maxadmin list servers");
test.tprintf("%s", output);
free(output);
}
void do_test(TestConnections& test, int master, int slave) void do_test(TestConnections& test, int master, int slave)
{ {
test.maxscales->connect_maxscale(0); test.maxscales->connect_maxscale(0);
@ -15,24 +23,32 @@ void do_test(TestConnections& test, int master, int slave)
test.tprintf("Stop a slave node and perform an insert"); test.tprintf("Stop a slave node and perform an insert");
test.galera->block_node(slave); test.galera->block_node(slave);
sleep(5); sleep(10);
list_servers(test);
test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)"); test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)");
test.tprintf("Start the slave node and perform another insert"); test.tprintf("Start the slave node and perform another insert");
test.galera->unblock_node(slave); test.galera->unblock_node(slave);
sleep(5); sleep(10);
list_servers(test);
test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)"); test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)");
test.maxscales->close_maxscale_connections(0); test.maxscales->close_maxscale_connections(0);
test.tprintf("Stop the master node and perform an insert"); test.tprintf("Stop the master node and perform an insert");
test.galera->block_node(master); test.galera->block_node(master);
sleep(5); sleep(10);
list_servers(test);
test.maxscales->connect_maxscale(0); test.maxscales->connect_maxscale(0);
test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)"); test.try_query(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)");
test.tprintf("Start the master node and perform another insert (expecting failure)"); test.tprintf("Start the master node and perform another insert (expecting failure)");
test.galera->unblock_node(master); test.galera->unblock_node(master);
sleep(5); sleep(10);
list_servers(test);
test.add_result(execute_query_silent(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)") == 0, test.add_result(execute_query_silent(test.maxscales->conn_rwsplit[0], "INSERT INTO test.t1 VALUES (1)") == 0,
"Query should fail"); "Query should fail");
test.maxscales->close_maxscale_connections(0); test.maxscales->close_maxscale_connections(0);

View File

@ -176,6 +176,11 @@ bool MessageQueue::post(const Message& message) const
{ {
ssize_t n = write(m_write_fd, &message, sizeof(message)); ssize_t n = write(m_write_fd, &message, sizeof(message));
rv = (n == sizeof(message)); rv = (n == sizeof(message));
if (n == -1)
{
MXS_ERROR("Failed to write message: %d, %s", errno, mxs_strerror(errno));
}
} }
else else
{ {

View File

@ -624,11 +624,21 @@ int service_launch_all()
SERVICE *ptr; SERVICE *ptr;
int n = 0, i; int n = 0, i;
bool error = false; bool error = false;
int num_svc = 0;
for (ptr = allServices; ptr; ptr = ptr->next)
{
num_svc++;
}
MXS_NOTICE("Starting a total of %d services...", num_svc);
int curr_svc = 1;
ptr = allServices; ptr = allServices;
while (ptr && !ptr->svc_do_shutdown) while (ptr && !ptr->svc_do_shutdown)
{ {
n += (i = serviceInitialize(ptr)); n += (i = serviceInitialize(ptr));
MXS_NOTICE("Service '%s' started (%d/%d)", ptr->name, curr_svc++, num_svc);
if (i == 0) if (i == 0)
{ {

View File

@ -1376,7 +1376,7 @@ mxs_auth_state_t gw_send_backend_auth(DCB *dcb)
with_ssl, ssl_established); with_ssl, ssl_established);
ss_dassert(buffer); ss_dassert(buffer);
if (with_ssl) if (with_ssl && !ssl_established)
{ {
if (dcb_write(dcb, buffer) && dcb_connect_SSL(dcb) >= 0) if (dcb_write(dcb, buffer) && dcb_connect_SSL(dcb) >= 0)
{ {

View File

@ -1253,10 +1253,12 @@ static void clientReply(MXS_ROUTER *instance,
rses->expected_responses--; rses->expected_responses--;
ss_dassert(rses->expected_responses >= 0); ss_dassert(rses->expected_responses >= 0);
ss_dassert(backend->get_reply_state() == REPLY_STATE_DONE); ss_dassert(backend->get_reply_state() == REPLY_STATE_DONE);
MXS_INFO("Reply complete, last reply from %s", backend->name());
} }
else else
{ {
MXS_DEBUG("Reply not yet complete, waiting for %d replies", rses->expected_responses); MXS_INFO("Reply not yet complete. Waiting for %d replies, got one from %s",
rses->expected_responses, backend->name());
} }
/** /**