MXS-1300: Send requests to multiple hosts

The host and port options were replace with one hosts option which
combines the two. This allows a single command to be executed on multiple
maxscale instances. Added accompanying code for checking responsiveness of
all maxscale instances and handling synchronization of the check
results. The code uses ECMAScript 2016 native promises to do this.

Added a configurable timeout to all requests. This should allow users to
interact with MaxScale over slow and laggy networks.

Before each executed command, all hosts given as parameters are pinged to
make sure they are alive. This should reduce the possibility of partial
execution of commands due to failed MaxScales.

The results of requests are grouped by the values of the hosts
option. This allows users to compare results of multiple servers with
relative ease.

Fixed reversion of the default credentials from mariadb:admin to
admin:mariadb.
This commit is contained in:
Markus Mäkelä
2017-07-10 23:14:36 +03:00
parent 11c8493167
commit 2670ca36a0
2 changed files with 75 additions and 41 deletions

View File

@ -114,44 +114,52 @@ module.exports = function() {
} }
// Helper for converting endpoints to acutal URLs // Helper for converting endpoints to acutal URLs
this.getUri = function(endpoint, options) { this.getUri = function(host, endpoint) {
var base = 'http://'; var base = 'http://'
var argv = this.program.argv var argv = this.program.argv
if (argv.secure) { if (argv.secure) {
base = 'https://'; base = 'https://'
} }
return base + argv.user + ':' + argv.password + '@' + return base + argv.user + ':' + argv.password + '@' + host + '/v1/' + endpoint
argv.host + ':' + argv.port + '/v1/' + endpoint;
} }
// Helper for executing requests and handling their responses // Helper for executing requests and handling their responses
this.doRequest = function(resource, cb, obj) { this.doRequest = function(resource, cb, obj) {
pingCluster()
.then(function() {
var argv = this.program.argv
argv.hosts.forEach(function(host) {
args = obj || {}
args.uri = getUri(host, resource)
args.json = true
args.timeout = argv.timeout
args = obj || {} request(args, function(err, resp, res) {
args.uri = getUri(resource), if (err) {
args.json = true // Failed to request
console.log(colors.yellow(host) + ':')
request(args, function(err, resp, res) { logError(JSON.stringify(err, null, 4))
if (err) { } else if (resp.statusCode == 200 && cb) {
// Failed to request // Request OK, returns data
logError(JSON.stringify(err, null, 4)) console.log(colors.yellow(host) + ':')
} else if (resp.statusCode == 200 && cb) { cb(res)
// Request OK, returns data } else if (resp.statusCode == 204) {
cb(res) // Request OK, no data
} else if (resp.statusCode == 204) { console.log(colors.yellow(host) + ': ' + colors.green('OK'))
// Request OK, no data } else {
console.log(colors.green('OK')) // Unexpected return code, probably an error
} else { var errstr = resp.statusCode + ' ' + resp.statusMessage
// Unexpected return code, probably an error if (res) {
var errstr = resp.statusCode + ' ' + resp.statusMessage errstr += ' ' + JSON.stringify(res, null, 4)
if (res) { }
errstr += ' ' + JSON.stringify(res, null, 4) console.log(colors.yellow(host) + ':')
} logError(errstr)
logError(errstr) }
} })
}) })
})
} }
this.updateValue = function(resource, key, value) { this.updateValue = function(resource, key, value) {
@ -180,3 +188,27 @@ function getTable(headobj) {
head: headobj head: headobj
}) })
} }
function pingMaxScale(host) {
return new Promise(function(resolve, reject) {
request('http://' + host + '/v1', function(err, resp, res) {
if (err) {
reject(err)
} else if (resp.statusCode != 200) {
reject(resp.statusCode + ' ' + resp.statusMessage)
} else {
resolve()
}
})
})
}
function pingCluster() {
var promises = []
this.program.argv.hosts.forEach(function(i) {
promises.push(pingMaxScale(i))
})
return Promise.all(promises)
}

View File

@ -18,31 +18,26 @@ const maxctrl_version = '1.0.0';
program program
.version(maxctrl_version) .version(maxctrl_version)
.group(['u', 'p', 'h', 'p', 'P', 's'], 'Global Options:') .group(['u', 'p', 'h', 's', 't'], 'Global Options:')
.option('u', { .option('u', {
alias:'user', alias:'user',
global: true, global: true,
default: 'mariadb', default: 'admin',
describe: 'Username to use', describe: 'Username to use',
type: 'string' type: 'string'
}) })
.option('p', { .option('p', {
alias: 'password', alias: 'password',
describe: 'Password for the user', describe: 'Password for the user',
default: 'admin', default: 'mariadb',
type: 'string' type: 'string'
}) })
.option('h', { .option('h', {
alias: 'host', alias: 'hosts',
describe: 'MaxScale hostname', describe: 'List of MaxScale hosts. The hosts must be in ' +
default: 'localhost', '<hostname>:<port> format and each host must be separated by spaces.',
type: 'string' default: 'localhost:8989',
}) type: 'array'
.option('P', {
alias: 'port',
describe: 'MaxScale REST API port',
default: 8989,
type: 'number'
}) })
.option('s', { .option('s', {
alias: 'secure', alias: 'secure',
@ -50,6 +45,13 @@ program
default: 'false', default: 'false',
type: 'boolean' type: 'boolean'
}) })
.option('t', {
alias: 'timeout',
describe: 'Request timeout in milliseconds',
default: '10000',
type: 'number'
})
.command(require('./lib/list.js')) .command(require('./lib/list.js'))
.command(require('./lib/show.js')) .command(require('./lib/show.js'))
.command(require('./lib/set.js')) .command(require('./lib/set.js'))