MXS-1300: Un-modularize the core
Making the core a module proved to bring more problems than it solved. For the sake of simplicity in installation and code coverage reporting, the core is now completely contained in the `lib/` directory. Also added preliminary coverage reporting with nyc.
This commit is contained in:
266
maxctrl/lib/common.js
Normal file
266
maxctrl/lib/common.js
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2016 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: 2020-01-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.
|
||||
*/
|
||||
|
||||
var request = require('request-promise-native');
|
||||
var colors = require('colors/safe');
|
||||
var Table = require('cli-table');
|
||||
var consoleLib = require('console')
|
||||
var fs = require('fs')
|
||||
|
||||
module.exports = function() {
|
||||
|
||||
this._ = require('lodash-getpath')
|
||||
|
||||
this.logger = console
|
||||
|
||||
// The main entry point into the library. This function is used to do
|
||||
// cluster health checks and to propagate the commands to multiple
|
||||
// servers.
|
||||
this.maxctrl = function(argv, cb) {
|
||||
|
||||
if (argv.quiet) {
|
||||
this.logger = new consoleLib.Console(fs.createWriteStream('/dev/null'),
|
||||
fs.createWriteStream('/dev/null'))
|
||||
}
|
||||
|
||||
this.argv = argv
|
||||
|
||||
return pingCluster(argv.hosts)
|
||||
.then(function() {
|
||||
var promises = []
|
||||
|
||||
argv.hosts.forEach(function(i) {
|
||||
promises.push(cb(i))
|
||||
})
|
||||
|
||||
return Promise.all(promises)
|
||||
}, function(err) {
|
||||
// One of the HTTP request pings to the cluster failed, log the error
|
||||
logError(JSON.stringify(err.error, null, 4))
|
||||
})
|
||||
}
|
||||
|
||||
// Request a resource collection and format it as a table
|
||||
this.getCollection = function (host, resource, fields) {
|
||||
return doRequest(host, resource, function(res) {
|
||||
|
||||
var header = []
|
||||
|
||||
fields.forEach(function(i) {
|
||||
header.push(Object.keys(i))
|
||||
})
|
||||
|
||||
var table = getTable(header)
|
||||
|
||||
res.data.forEach(function(i) {
|
||||
row = []
|
||||
|
||||
fields.forEach(function(p) {
|
||||
var v = _.getPath(i, p[Object.keys(p)[0]], '')
|
||||
|
||||
if (Array.isArray(v)) {
|
||||
v = v.join(', ')
|
||||
}
|
||||
row.push(v)
|
||||
})
|
||||
|
||||
table.push(row)
|
||||
})
|
||||
|
||||
logger.log(table.toString())
|
||||
})
|
||||
}
|
||||
|
||||
// Request a part of a resource as a collection
|
||||
this.getSubCollection = function (host, resource, subres, fields) {
|
||||
|
||||
return doRequest(host, resource, function(res) {
|
||||
var header = []
|
||||
|
||||
fields.forEach(function(i) {
|
||||
header.push(Object.keys(i))
|
||||
})
|
||||
|
||||
var table = getTable(header)
|
||||
|
||||
_.getPath(res.data, subres, []).forEach(function(i) {
|
||||
row = []
|
||||
|
||||
fields.forEach(function(p) {
|
||||
var v = _.getPath(i, p[Object.keys(p)[0]], '')
|
||||
|
||||
if (Array.isArray(v) && typeof(v[0]) != 'object') {
|
||||
v = v.join(', ')
|
||||
} else if (typeof(v) == 'object') {
|
||||
v = JSON.stringify(v, null, 4)
|
||||
}
|
||||
row.push(v)
|
||||
})
|
||||
|
||||
table.push(row)
|
||||
})
|
||||
|
||||
logger.log(table.toString())
|
||||
})
|
||||
}
|
||||
|
||||
// Request a single resource and format it as a key-value list
|
||||
this.getResource = function (host, resource, fields) {
|
||||
|
||||
return doRequest(host, resource, function(res) {
|
||||
var table = getList()
|
||||
|
||||
fields.forEach(function(i) {
|
||||
var k = Object.keys(i)[0]
|
||||
var path = i[k]
|
||||
var v = _.getPath(res.data, path, '')
|
||||
|
||||
if (Array.isArray(v) && typeof(v[0]) != 'object') {
|
||||
v = v.join(', ')
|
||||
} else if (typeof(v) == 'object') {
|
||||
v = JSON.stringify(v, null, 4)
|
||||
}
|
||||
|
||||
var o = {}
|
||||
o[k] = v
|
||||
table.push(o)
|
||||
})
|
||||
|
||||
logger.log(table.toString())
|
||||
})
|
||||
}
|
||||
|
||||
this.updateValue = function(host, resource, key, value) {
|
||||
var body = {}
|
||||
_.set(body, key, value)
|
||||
return doRequest(host, resource, null, { method: 'PATCH', body: body })
|
||||
}
|
||||
|
||||
// Helper for converting endpoints to acutal URLs
|
||||
this.getUri = function(host, secure, endpoint) {
|
||||
var base = 'http://'
|
||||
|
||||
if (secure) {
|
||||
base = 'https://'
|
||||
}
|
||||
|
||||
return base + argv.user + ':' + argv.password + '@' + host + '/v1/' + endpoint
|
||||
}
|
||||
|
||||
// Helper for executing requests and handling their responses, returns a
|
||||
// promise that is fulfilled when all requests successfully complete. The
|
||||
// promise is rejected if any of the requests fails.
|
||||
this.doAsyncRequest = function(host, resource, cb, obj) {
|
||||
args = obj || {}
|
||||
args.uri = getUri(host, this.argv.secure, resource)
|
||||
args.json = true
|
||||
args.timeout = this.argv.timeout
|
||||
|
||||
return request(args)
|
||||
.then(function(res) {
|
||||
if (res && cb) {
|
||||
// Request OK, returns data
|
||||
return cb(res)
|
||||
} else {
|
||||
// Request OK, no data or data is ignored
|
||||
logger.log(colors.green('OK'))
|
||||
return Promise.resolve()
|
||||
}
|
||||
}, function(err) {
|
||||
if (err.response && err.response.body) {
|
||||
logError(JSON.stringify(err.response.body, null, 4))
|
||||
} else if (err.statusCode) {
|
||||
logError('Server responded with ' + err.statusCode)
|
||||
} else if (err.error) {
|
||||
logError(JSON.stringify(err.error, null, 4))
|
||||
} else {
|
||||
logError('Undefined error: ' + JSON.stringify(err, null, 4))
|
||||
}
|
||||
return Promise.reject()
|
||||
})
|
||||
}
|
||||
|
||||
this.doRequest = function(host, resource, cb, obj) {
|
||||
return doAsyncRequest(host, resource, cb, obj)
|
||||
.then(this.argv.resolve, this.argv.reject)
|
||||
}
|
||||
|
||||
this.logError = function(err) {
|
||||
this.logger.error(colors.red('Error:'), err)
|
||||
}
|
||||
|
||||
this.error = function(err) {
|
||||
logger.log(colors.red('Error:'), err)
|
||||
this.argv.reject()
|
||||
}
|
||||
}
|
||||
|
||||
var tsvopts = {
|
||||
chars: {
|
||||
'top': '' , 'top-mid': '' , 'top-left': '' , 'top-right': ''
|
||||
, 'bottom': '' , 'bottom-mid': '' , 'bottom-left': '' , 'bottom-right': ''
|
||||
, 'left': '' , 'left-mid': '' , 'mid': '' , 'mid-mid': ''
|
||||
, 'right': '' , 'right-mid': '' , 'middle': ' '
|
||||
},
|
||||
style: {
|
||||
'padding-left': 0,
|
||||
'padding-right': 0,
|
||||
compact: true
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getList() {
|
||||
var opts = {
|
||||
style: { head: ['cyan'] }
|
||||
}
|
||||
|
||||
if (this.argv.tsv)
|
||||
{
|
||||
opts = _.assign(opts, tsvopts)
|
||||
}
|
||||
|
||||
return new Table(opts)
|
||||
}
|
||||
|
||||
// Creates a table-like array for output. The parameter is an array of header names
|
||||
function getTable(headobj) {
|
||||
|
||||
for (i = 0; i < headobj.length; i++) {
|
||||
headobj[i] = colors.cyan(headobj[i])
|
||||
}
|
||||
|
||||
var opts
|
||||
|
||||
if (this.argv.tsv)
|
||||
{
|
||||
opts = _.assign(opts, tsvopts)
|
||||
} else {
|
||||
opts = {
|
||||
head: headobj
|
||||
}
|
||||
}
|
||||
|
||||
return new Table(opts)
|
||||
}
|
||||
|
||||
function pingCluster(hosts) {
|
||||
var promises = []
|
||||
|
||||
hosts.forEach(function(i) {
|
||||
promises.push(request('http://' + i + '/v1'))
|
||||
})
|
||||
|
||||
return Promise.all(promises)
|
||||
}
|
Reference in New Issue
Block a user