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}
# Configure the build environment
./BUILD/install_build_deps.sh
mkdir 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 test
sudo make install
make test || exit 1
sudo make install
sudo ./postinst
maxscale --version

View File

@ -15,7 +15,7 @@ then
sudo apt-get install -y --force-yes dpkg-dev git wget \
build-essential libssl-dev ncurses-dev bison flex \
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
cat /etc/*release | grep -E "Trusty|wheezy"
if [ $? == 0 ]
@ -76,18 +76,18 @@ else
fi
# 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
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
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 }'`
if [ "$cmake_version" \< "3.7.1" ] ; then
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
tar xzvf cmake-3.7.1.tar.gz
wget -q https://cmake.org/files/v3.7/cmake-3.7.1.tar.gz --no-check-certificate
tar xzf cmake-3.7.1.tar.gz
cd cmake-3.7.1
./bootstrap
@ -116,7 +116,7 @@ cd ../../
# TCL
mkdir 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 ]
then
@ -124,7 +124,7 @@ then
exit 1
fi
tar xzvf tcl8.6.5-src.tar.gz
tar xzf tcl8.6.5-src.tar.gz
cd tcl8.6.5/unix
./configure
sudo make install
@ -149,7 +149,7 @@ sudo make install
cd ../../
# 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 ]
then
echo "Error getting avro-c"

View File

@ -28,6 +28,7 @@
the master. There is also limited capability for rejoining nodes.
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.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)

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
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
## 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)
* [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
There are some limitations and known issues within this version of MaxScale.

View File

@ -43,6 +43,9 @@
#ifdef HISTORY
#include <histedit.h>
#define USE_HIST 1
#else
#define USE_HIST 0
#endif
#define MAX_PASSWORD_LEN 80
@ -62,6 +65,7 @@ static void read_inifile(char **socket,
char **hostname, char **port, char **user, char **passwd,
int *editor);
static bool getPassword(char *password, size_t length);
static void rtrim(char *str);
#ifdef HISTORY
@ -93,6 +97,128 @@ static struct option long_options[] =
#define MAXADMIN_DEFAULT_USER "admin"
#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
*
@ -102,15 +228,6 @@ static struct option long_options[] =
int
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 *port = NULL;
char *user = NULL;
@ -293,105 +410,16 @@ main(int argc, char **argv)
}
(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 */
/* Initialize editline */
el = el_init(*argv, stdin, stdout, stderr);
if (use_emacs)
if (!term_error && USE_HIST)
{
el_set(el, EL_EDITOR, "emacs"); /** Editor is emacs */
cmd_with_history(so, argv, use_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;
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;
}
}
cmd_no_history(so);
}
#ifdef HISTORY
el_end(el);
tok_end(tok);
history_end(hist);
#endif
close(so);
return 0;
}
@ -937,8 +965,7 @@ read_inifile(char **socket,
*/
bool getPassword(char *passwd, size_t len)
{
bool gotten = false;
bool err = false;
struct termios tty_attr;
tcflag_t c_lflag;
@ -948,36 +975,55 @@ bool getPassword(char *passwd, size_t len)
tty_attr.c_lflag &= ~ICANON;
tty_attr.c_lflag &= ~ECHO;
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) == 0)
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) != 0)
{
printf("Password: ");
if (fgets(passwd, len, stdin) == NULL)
{
printf("Failed to read password\n");
}
err = true;
}
}
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)
{
int i = strlen(passwd);
printf("Password: ");
if (fgets(passwd, len, stdin) == NULL)
{
printf("Failed to read password\n");
}
if (i > 1)
{
passwd[i - 1] = '\0';
}
if (!err)
{
tty_attr.c_lflag = c_lflag;
printf("\n");
gotten = true;
}
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) != 0)
{
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 os = require('os')
var fs = require('fs')
var readlineSync = require('readline-sync')
module.exports = function() {
@ -27,6 +28,13 @@ module.exports = function() {
// servers.
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
argv.hosts = argv.hosts.split(',')
@ -209,7 +217,7 @@ module.exports = function() {
base = 'https://'
}
return base + argv.user + ':' + argv.password + '@' + host + '/v1/' + endpoint
return base + argv.u + ':' + argv.p + '@' + host + '/v1/' + endpoint
}
this.OK = function() {

View File

@ -31,7 +31,7 @@ program
})
.option('p', {
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',
type: 'string'
})
@ -104,7 +104,7 @@ program
.demandCommand(1, 'At least one command is required')
.command('*', 'the default command', {}, function(argv) {
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",
"lodash": "^4.17.4",
"lodash-getpath": "^0.2.4",
"readline-sync": "^1.4.9",
"request": "^2.81.0",
"request-promise-native": "^1.0.3",
"yargs": "^8.0.2"

View File

@ -6,6 +6,14 @@
#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)
{
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.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.tprintf("Start the slave node and perform another insert");
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.maxscales->close_maxscale_connections(0);
test.tprintf("Stop the master node and perform an insert");
test.galera->block_node(master);
sleep(5);
sleep(10);
list_servers(test);
test.maxscales->connect_maxscale(0);
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.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,
"Query should fail");
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));
rv = (n == sizeof(message));
if (n == -1)
{
MXS_ERROR("Failed to write message: %d, %s", errno, mxs_strerror(errno));
}
}
else
{

View File

@ -624,11 +624,21 @@ int service_launch_all()
SERVICE *ptr;
int n = 0, i;
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;
while (ptr && !ptr->svc_do_shutdown)
{
n += (i = serviceInitialize(ptr));
MXS_NOTICE("Service '%s' started (%d/%d)", ptr->name, curr_svc++, num_svc);
if (i == 0)
{

View File

@ -1376,7 +1376,7 @@ mxs_auth_state_t gw_send_backend_auth(DCB *dcb)
with_ssl, ssl_established);
ss_dassert(buffer);
if (with_ssl)
if (with_ssl && !ssl_established)
{
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--;
ss_dassert(rses->expected_responses >= 0);
ss_dassert(backend->get_reply_state() == REPLY_STATE_DONE);
MXS_INFO("Reply complete, last reply from %s", backend->name());
}
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());
}
/**