MXS-1300: Combine REST API and MaxCtrl tests
The REST API tests are now located under the maxctrl directory. This allows both tests to use the same framework for testing.
This commit is contained in:
1
maxctrl/CMakeLists.txt
Normal file
1
maxctrl/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(test)
|
5
maxctrl/build.sh
Executable file
5
maxctrl/build.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#/bin/bash
|
||||
|
||||
npm install
|
||||
npm install pkg
|
||||
node_modules/pkg/lib-es5/bin.js -t node6-linux-x64 .
|
214
maxctrl/common.js
Normal file
214
maxctrl/common.js
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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');
|
||||
var colors = require('colors/safe');
|
||||
var Table = require('cli-table');
|
||||
|
||||
module.exports = function() {
|
||||
|
||||
this._ = require('lodash-getpath')
|
||||
// Common options for all commands
|
||||
this.program = require('yargs');
|
||||
|
||||
// Request a resource collection and format it as a table
|
||||
this.getCollection = function (resource, fields) {
|
||||
|
||||
doRequest(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)
|
||||
})
|
||||
|
||||
console.log(table.toString())
|
||||
})
|
||||
}
|
||||
|
||||
// Request a part of a resource as a collection
|
||||
this.getSubCollection = function (resource, subres, fields) {
|
||||
|
||||
doRequest(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)
|
||||
})
|
||||
|
||||
console.log(table.toString())
|
||||
})
|
||||
}
|
||||
|
||||
// Request a single resource and format it as a key-value list
|
||||
this.getResource = function (resource, fields) {
|
||||
|
||||
doRequest(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)
|
||||
})
|
||||
|
||||
console.log(table.toString())
|
||||
})
|
||||
}
|
||||
|
||||
// Helper for converting endpoints to acutal URLs
|
||||
this.getUri = function(host, endpoint) {
|
||||
var base = 'http://'
|
||||
var argv = this.program.argv
|
||||
|
||||
if (argv.secure) {
|
||||
base = 'https://'
|
||||
}
|
||||
|
||||
return base + argv.user + ':' + argv.password + '@' + host + '/v1/' + endpoint
|
||||
}
|
||||
|
||||
// Helper for executing requests and handling their responses
|
||||
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
|
||||
|
||||
request(args, function(err, resp, res) {
|
||||
if (err) {
|
||||
// Failed to request
|
||||
console.log(colors.yellow(host) + ':')
|
||||
logError(JSON.stringify(err, null, 4))
|
||||
} else if (resp.statusCode == 200 && cb) {
|
||||
// Request OK, returns data
|
||||
console.log(colors.yellow(host) + ':')
|
||||
cb(res)
|
||||
} else if (resp.statusCode == 204) {
|
||||
// Request OK, no data
|
||||
console.log(colors.yellow(host) + ': ' + colors.green('OK'))
|
||||
} else {
|
||||
// Unexpected return code, probably an error
|
||||
var errstr = resp.statusCode + ' ' + resp.statusMessage
|
||||
if (res) {
|
||||
errstr += ' ' + JSON.stringify(res, null, 4)
|
||||
}
|
||||
console.log(colors.yellow(host) + ':')
|
||||
logError(errstr)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.updateValue = function(resource, key, value) {
|
||||
var body = {}
|
||||
_.set(body, key, value)
|
||||
doRequest(resource, null, { method: 'PATCH', body: body })
|
||||
}
|
||||
|
||||
this.logError = function(err) {
|
||||
console.log(colors.red('Error:'), err)
|
||||
}
|
||||
}
|
||||
|
||||
function getList() {
|
||||
return new Table({ style: { head: ['cyan'] } })
|
||||
}
|
||||
|
||||
// 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])
|
||||
}
|
||||
|
||||
return new Table({
|
||||
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)
|
||||
}
|
76
maxctrl/core.js
Normal file
76
maxctrl/core.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('./common.js')()
|
||||
|
||||
const maxctrl_version = '1.0.0';
|
||||
|
||||
module.exports = function(argv) {
|
||||
program
|
||||
.version(maxctrl_version)
|
||||
.group(['u', 'p', 'h', 's', 't'], 'Global Options:')
|
||||
.option('u', {
|
||||
alias:'user',
|
||||
global: true,
|
||||
default: 'admin',
|
||||
describe: 'Username to use',
|
||||
type: 'string'
|
||||
})
|
||||
.option('p', {
|
||||
alias: 'password',
|
||||
describe: 'Password for the user',
|
||||
default: 'mariadb',
|
||||
type: 'string'
|
||||
})
|
||||
.option('h', {
|
||||
alias: 'hosts',
|
||||
describe: 'List of MaxScale hosts. The hosts must be in ' +
|
||||
'<hostname>:<port> format and each host must be separated by spaces.',
|
||||
default: 'localhost:8989',
|
||||
type: 'array'
|
||||
})
|
||||
.option('s', {
|
||||
alias: 'secure',
|
||||
describe: 'Enable HTTPS requests',
|
||||
default: 'false',
|
||||
type: 'boolean'
|
||||
})
|
||||
.option('t', {
|
||||
alias: 'timeout',
|
||||
describe: 'Request timeout in milliseconds',
|
||||
default: '10000',
|
||||
type: 'number'
|
||||
})
|
||||
|
||||
.command(require('./lib/list.js'))
|
||||
.command(require('./lib/show.js'))
|
||||
.command(require('./lib/set.js'))
|
||||
.command(require('./lib/clear.js'))
|
||||
.command(require('./lib/enable.js'))
|
||||
.command(require('./lib/disable.js'))
|
||||
.command(require('./lib/create.js'))
|
||||
.command(require('./lib/destroy.js'))
|
||||
.command(require('./lib/link.js'))
|
||||
.command(require('./lib/unlink.js'))
|
||||
.command(require('./lib/start.js'))
|
||||
.command(require('./lib/stop.js'))
|
||||
.command(require('./lib/alter.js'))
|
||||
.command(require('./lib/rotate.js'))
|
||||
.command(require('./lib/call.js'))
|
||||
.help()
|
||||
.demandCommand(1, 'At least one command is required')
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help` for a list of commands.')
|
||||
})
|
||||
.parse(argv)
|
||||
.argv
|
||||
}
|
40
maxctrl/lib/alter.js
Normal file
40
maxctrl/lib/alter.js
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'alter <command>'
|
||||
exports.desc = 'Alter objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('server <server> <key> <value>', 'Alter server parameters', {}, function(argv) {
|
||||
updateValue('servers/' + argv.server, 'data.attributes.parameters.' + argv.key, argv.value)
|
||||
})
|
||||
.command('monitor <monitor> <key> <value>', 'Alter monitor parameters', {}, function(argv) {
|
||||
updateValue('monitors/' + argv.monitor, 'data.attributes.parameters.' + argv.key, argv.value)
|
||||
})
|
||||
.command('service <service> <key> <value>', 'Alter service parameters', {}, function(argv) {
|
||||
updateValue('services/' + argv.service, 'data.attributes.parameters.' + argv.key, argv.value)
|
||||
})
|
||||
.command('logging <key> <value>', 'Alter logging parameters', {}, function(argv) {
|
||||
updateValue('maxscale/logs', 'attributes.parameters.' + argv.key, argv.value)
|
||||
})
|
||||
.command('maxscale <key> <value>', 'Alter MaxScale parameters', {}, function(argv) {
|
||||
updateValue('maxscale', 'attributes.parameters.' + argv.key, argv.value)
|
||||
})
|
||||
.usage('Usage: alter <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help alter` for a list of commands.')
|
||||
})
|
||||
}
|
45
maxctrl/lib/call.js
Normal file
45
maxctrl/lib/call.js
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'call <command>'
|
||||
exports.desc = 'Call module commands'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('command <module> <command> [parameters...]', 'Call a module command', {}, function(argv) {
|
||||
|
||||
// First we have to find the correct method to use
|
||||
doRequest('maxscale/modules/' + argv.module + '/', function(resp) {
|
||||
|
||||
// A GET request will return the correct error if the command is not found
|
||||
var verb = 'GET'
|
||||
|
||||
resp.data.attributes.commands.forEach(function(i) {
|
||||
if (i.id == argv.command) {
|
||||
verb = i.attributes.method;
|
||||
}
|
||||
})
|
||||
|
||||
doRequest('maxscale/modules/' + argv.module + '/' + argv.command + '?' + argv.parameters.join('&'),
|
||||
function(resp) {
|
||||
console.log(JSON.stringify(resp, null, 4))
|
||||
}, { method: verb })
|
||||
})
|
||||
})
|
||||
.usage('Usage: call <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help call` for a list of commands.')
|
||||
})
|
||||
}
|
29
maxctrl/lib/clear.js
Normal file
29
maxctrl/lib/clear.js
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'clear <command>'
|
||||
exports.desc = 'Clear object state'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('server <server> <state>', 'Clear server state', {}, function(argv) {
|
||||
var target = 'servers/' + argv.server + '/clear?state=' + argv.state
|
||||
doRequest(target, null, {method: 'PUT'})
|
||||
})
|
||||
.usage('Usage: clear <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help clear` for a list of commands.')
|
||||
})
|
||||
}
|
182
maxctrl/lib/create.js
Normal file
182
maxctrl/lib/create.js
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'create <command>'
|
||||
exports.desc = 'Create objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
// Common options
|
||||
.group(['protocol', 'authenticator', 'authenticator-options'], 'Common create options:')
|
||||
.option('protocol', {
|
||||
describe: 'Protocol module name',
|
||||
type: 'string'
|
||||
})
|
||||
.option('authenticator', {
|
||||
describe: 'Authenticator module name',
|
||||
type: 'string'
|
||||
})
|
||||
.option('authenticator-options', {
|
||||
describe: 'Option string for the authenticator',
|
||||
type: 'string'
|
||||
})
|
||||
|
||||
// Create server
|
||||
.group(['services', 'monitors'], 'Create server options:')
|
||||
.option('services', {
|
||||
describe: 'Link the created server to these services',
|
||||
type: 'array'
|
||||
})
|
||||
.option('monitors', {
|
||||
describe: 'Link the created server to these monitors',
|
||||
type: 'array'
|
||||
})
|
||||
.command('server <name> <host> <port>', 'Create a new server', {}, function(argv) {
|
||||
var server = {
|
||||
'data': {
|
||||
'id': argv.name,
|
||||
'type': 'servers',
|
||||
'attributes': {
|
||||
'parameters': {
|
||||
'address': argv.host,
|
||||
'port': argv.port,
|
||||
'protocol': argv.protocol,
|
||||
'authenticator': argv.authenticator,
|
||||
'authenticator_options': argv.auth_options
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argv.services) {
|
||||
for (i = 0; i < argv.services.length; i++) {
|
||||
_.set(server, 'data.relationships.services.data[' + i + ']', {id: argv.services[i], type: 'services'})
|
||||
}
|
||||
}
|
||||
|
||||
if (argv.monitors) {
|
||||
for (i = 0; i < argv.monitors.length; i++) {
|
||||
_.set(server, 'data.relationships.monitors.data[' + i + ']', {id: argv.monitors[i], type: 'monitors'})
|
||||
}
|
||||
}
|
||||
|
||||
doRequest('servers', null, {method: 'POST', body: server})
|
||||
})
|
||||
|
||||
// Create monitor
|
||||
.group(['servers'], 'Create monitor options:')
|
||||
.option('servers', {
|
||||
describe: 'Link the created monitor to these servers',
|
||||
type: 'array'
|
||||
})
|
||||
.command('monitor <name> <module>', 'Create a new server', {}, function(argv) {
|
||||
|
||||
var monitor = {
|
||||
'data': {
|
||||
'id': argv.name,
|
||||
'attributes': {
|
||||
'module': argv.module
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argv.servers) {
|
||||
for (i = 0; i < argv.servers.length; i++) {
|
||||
_.set(server, 'data.relationships.servers.data[' + i + ']', {id: argv.monitors[i], type: 'servers'})
|
||||
}
|
||||
}
|
||||
|
||||
doRequest('monitors', null, {method: 'POST', body: monitor})
|
||||
})
|
||||
|
||||
// Create listener
|
||||
.group(['interface', 'tls-key', 'tls-cert', 'tls-ca-cert', 'tls-version', 'tls-cert-verify-depth'], 'Create listener options:')
|
||||
.option('interface', {
|
||||
describe: 'Interface to listen on',
|
||||
type: 'string',
|
||||
default: '::'
|
||||
})
|
||||
// Should these have ssl as a prefix even though SSL isn't supported?
|
||||
.option('tls-key', {
|
||||
describe: 'Path to TLS key',
|
||||
type: 'string'
|
||||
})
|
||||
.option('tls-cert', {
|
||||
describe: 'Path to TLS certificate',
|
||||
type: 'string'
|
||||
})
|
||||
.option('tls-ca-cert', {
|
||||
describe: 'Path to TLS CA certificate',
|
||||
type: 'string'
|
||||
})
|
||||
.option('tls-version', {
|
||||
describe: 'TLS version to use',
|
||||
type: 'string'
|
||||
})
|
||||
.option('tls-cert-verify-depth', {
|
||||
describe: 'TLS certificate verification depth',
|
||||
type: 'string'
|
||||
})
|
||||
.command('listener <service> <name> <port>', 'Create a new server', {}, function(argv) {
|
||||
|
||||
var listener = {
|
||||
'data': {
|
||||
'id': argv.name,
|
||||
'type': 'listeners',
|
||||
'attributes': {
|
||||
'parameters': {
|
||||
'port': argv.port,
|
||||
'address': argv.interface,
|
||||
'protocol': argv.protocol,
|
||||
'authenticator': argv.authenticator,
|
||||
'authenticator_options': argv.auth_options,
|
||||
'ssl_key': argv['tls-key'],
|
||||
'ssl_cert': argv['tls-cert'],
|
||||
'ssl_ca_cert': argv['tls-ca-cert'],
|
||||
'ssl_version': argv['tls-version'],
|
||||
'ssl_cert_verify_depth': argv['tls-cert-verify-depth'],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (argv.servers) {
|
||||
for (i = 0; i < argv.servers.length; i++) {
|
||||
_.set(server, 'data.relationships.servers.data[' + i + ']', {id: argv.monitors[i], type: 'servers'})
|
||||
}
|
||||
}
|
||||
|
||||
doRequest('services/' + argv.service + '/listeners', null, {method: 'POST', body: listener})
|
||||
})
|
||||
.command('user <name> <password>', 'Create a new network user', {}, function(argv) {
|
||||
|
||||
var user = {
|
||||
'data': {
|
||||
'id': argv.name,
|
||||
'type': 'inet',
|
||||
'attributes': {
|
||||
'password': argv.password
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doRequest('users/inet', null, {method: 'POST', body: user})
|
||||
})
|
||||
|
||||
.usage('Usage: create <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help create` for a list of commands.')
|
||||
})
|
||||
}
|
37
maxctrl/lib/destroy.js
Normal file
37
maxctrl/lib/destroy.js
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'destroy <command>'
|
||||
exports.desc = 'Destroy objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('server <name>', 'Destroy an unused server', {}, function(argv) {
|
||||
doRequest('servers/' + argv.name, null, {method: 'DELETE'})
|
||||
})
|
||||
.command('monitor <name>', 'Destroy an unused monitor', {}, function(argv) {
|
||||
doRequest('monitors/' + argv.name, null, {method: 'DELETE'})
|
||||
})
|
||||
.command('listener <service> <name>', 'Destroy an unused listener', {}, function(argv) {
|
||||
doRequest('services/' + argv.service + '/listeners/' + argv.name, null, {method: 'DELETE'})
|
||||
})
|
||||
.command('user <name>', 'Remove a network user', {}, function(argv) {
|
||||
doRequest('users/inet/' + argv.name, null, {method: 'DELETE'})
|
||||
})
|
||||
.usage('Usage: destroy <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help destroy` for a list of commands.')
|
||||
})
|
||||
}
|
48
maxctrl/lib/disable.js
Normal file
48
maxctrl/lib/disable.js
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
const log_levels = [
|
||||
'debug',
|
||||
'info',
|
||||
'notice',
|
||||
'warning'
|
||||
]
|
||||
|
||||
exports.command = 'disable <command>'
|
||||
exports.desc = 'Disable functionality'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('log-priority <log>', 'Disable log priority [warning|notice|info|debug]', {}, function(argv) {
|
||||
if (log_levels.indexOf(argv.log) != -1) {
|
||||
updateValue('maxscale/logs', 'data.attributes.parameters.log_' + argv.log, false)
|
||||
} else {
|
||||
logError('Invalid log priority: ' + argv.log);
|
||||
}
|
||||
})
|
||||
.command('maxlog', 'Disable MaxScale logging', {}, function(argv) {
|
||||
updateValue('maxscale/logs', 'data.attributes.parameters.maxlog', false)
|
||||
})
|
||||
.command('syslog', 'Disable syslog logging', {}, function(argv) {
|
||||
updateValue('maxscale/logs', 'data.attributes.parameters.syslog', false)
|
||||
})
|
||||
.command('account <name>', 'Disable a Linux user account from administrative use', {}, function(argv) {
|
||||
doRequest('users/unix/' + argv.name, null, { method: 'DELETE'})
|
||||
})
|
||||
.usage('Usage: disable <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help disable` for a list of commands.')
|
||||
})
|
||||
}
|
54
maxctrl/lib/enable.js
Normal file
54
maxctrl/lib/enable.js
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
const log_levels = [
|
||||
'debug',
|
||||
'info',
|
||||
'notice',
|
||||
'warning'
|
||||
]
|
||||
|
||||
exports.command = 'enable <command>'
|
||||
exports.desc = 'Enable functionality'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('log-priority <log>', 'Enable log priority [warning|notice|info|debug]', {}, function(argv) {
|
||||
if (log_levels.indexOf(argv.log) != -1) {
|
||||
updateValue('maxscale/logs', 'data.attributes.parameters.log_' + argv.log, true)
|
||||
} else {
|
||||
logError('Invalid log priority: ' + argv.log);
|
||||
}
|
||||
})
|
||||
.command('maxlog', 'Enable MaxScale logging', {}, function(argv) {
|
||||
updateValue('maxscale/logs', 'data.attributes.parameters.maxlog', true)
|
||||
})
|
||||
.command('syslog', 'Enable syslog logging', {}, function(argv) {
|
||||
updateValue('maxscale/logs', 'data.attributes.parameters.syslog', true)
|
||||
})
|
||||
.command('account <name>', 'Activate a Linux user account for administrative use', {}, function(argv) {
|
||||
var req_body = {
|
||||
data: {
|
||||
id: argv.name,
|
||||
type: 'unix'
|
||||
}
|
||||
}
|
||||
doRequest('users/unix', null, { method: 'POST', body: req_body})
|
||||
})
|
||||
.usage('Usage: enable <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help enable` for a list of commands.')
|
||||
})
|
||||
}
|
47
maxctrl/lib/link.js
Normal file
47
maxctrl/lib/link.js
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
function addServer(path, targets) {
|
||||
doRequest(path, function(res) {
|
||||
var servers =_.get(res, 'data.relationships.servers.data', [])
|
||||
|
||||
targets.forEach(function(i){
|
||||
servers.push({id: i, type: 'servers'})
|
||||
})
|
||||
|
||||
// Update relationships and remove unnecessary parts
|
||||
_.set(res, 'data.relationships.servers.data', servers)
|
||||
delete res.data.attributes
|
||||
|
||||
doRequest(path, null, {method: 'PATCH', body: res})
|
||||
})
|
||||
}
|
||||
|
||||
exports.command = 'link <command>'
|
||||
exports.desc = 'Link objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('service <name> <server...>', 'Link servers to a service', {}, function(argv) {
|
||||
addServer('services/' + argv.name, argv.server)
|
||||
})
|
||||
.command('monitor <name> <server...>', 'Link servers to a monitor', {}, function(argv) {
|
||||
addServer('monitors/' + argv.name, argv.server)
|
||||
})
|
||||
.usage('Usage: link <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help link` for a list of commands.')
|
||||
})
|
||||
}
|
84
maxctrl/lib/list.js
Normal file
84
maxctrl/lib/list.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'list <command>'
|
||||
exports.desc = 'List objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('servers', 'List servers', {}, function() {
|
||||
getCollection('servers', [
|
||||
{'Server': 'id'},
|
||||
{'Address': 'attributes.parameters.address'},
|
||||
{'Port': 'attributes.parameters.port'},
|
||||
{'Connections': 'attributes.statistics.connections'},
|
||||
{'State': 'attributes.state'}
|
||||
])
|
||||
})
|
||||
.command('services', 'List services', {}, function() {
|
||||
getCollection('services',[
|
||||
{'Service': 'id'},
|
||||
{'Router': 'attributes.router'},
|
||||
{'Connections': 'attributes.connections'},
|
||||
{'Total Connections': 'attributes.total_connections'},
|
||||
{'Servers': 'relationships.servers.data[].id'}
|
||||
])
|
||||
})
|
||||
.command('monitors', 'List monitors', {}, function() {
|
||||
getCollection('monitors', [
|
||||
{'Monitor': 'id'},
|
||||
{'State': 'attributes.state'},
|
||||
{'Servers': 'relationships.servers.data[].id'}
|
||||
])
|
||||
})
|
||||
.command('sessions', 'List sessions', {}, function() {
|
||||
getCollection('sessions',[
|
||||
{'Id': 'id'},
|
||||
{'Service': 'relationships.services.data[].id'},
|
||||
{'User': 'attributes.user'},
|
||||
{'Host': 'attributes.remote'}
|
||||
])
|
||||
})
|
||||
.command('filters', 'List filters', {}, function() {
|
||||
getCollection('filters', [
|
||||
{'Filter': 'id'},
|
||||
{'Service': 'relationships.services.data[].id'},
|
||||
{'Module': 'attributes.module'}
|
||||
])
|
||||
})
|
||||
.command('modules', 'List loaded modules', {}, function() {
|
||||
getCollection('maxscale/modules',[
|
||||
{'Module':'id'},
|
||||
{'Type':'attributes.module_type'},
|
||||
{'Version': 'attributes.version'}
|
||||
])
|
||||
})
|
||||
.command('users', 'List created network users', {}, function() {
|
||||
getCollection('users/inet',[
|
||||
{'Name':'id'}
|
||||
])
|
||||
})
|
||||
.command('commands', 'List module commands', {}, function() {
|
||||
getCollection('maxscale/modules',[
|
||||
{'Module':'id'},
|
||||
{'Commands': 'attributes.commands[].id'}
|
||||
])
|
||||
})
|
||||
.usage('Usage: list <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help list` for a list of commands.')
|
||||
})
|
||||
}
|
28
maxctrl/lib/rotate.js
Normal file
28
maxctrl/lib/rotate.js
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'rotate <command>'
|
||||
exports.desc = 'Rotate log files'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('logs', 'Rotate log files by closing and reopening the files', {}, function(argv) {
|
||||
doRequest('maxscale/logs/flush/', null, {method: 'POST'})
|
||||
})
|
||||
.usage('Usage: rotate <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help rotate` for a list of commands.')
|
||||
})
|
||||
}
|
29
maxctrl/lib/set.js
Normal file
29
maxctrl/lib/set.js
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'set <command>'
|
||||
exports.desc = 'Set object state'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('server <server> <state>', 'Set server state', {}, function(argv) {
|
||||
var target = 'servers/' + argv.server + '/set?state=' + argv.state
|
||||
doRequest(target, null, {method: 'PUT'})
|
||||
})
|
||||
.usage('Usage: set <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help set` for a list of commands.')
|
||||
})
|
||||
}
|
108
maxctrl/lib/show.js
Normal file
108
maxctrl/lib/show.js
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'show <command>'
|
||||
exports.desc = 'Show objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('server <server>', 'Show server', {}, function(argv) {
|
||||
getResource('servers/' + argv.server, [
|
||||
{'Server': 'id'},
|
||||
{'Address': 'attributes.parameters.address'},
|
||||
{'Port': 'attributes.parameters.port'},
|
||||
{'State': 'attributes.state'},
|
||||
{'Services': 'relationships.services.data[].id'},
|
||||
{'Monitors': 'relationships.monitors.data[].id'},
|
||||
{'Master ID': 'attributes.master_id'},
|
||||
{'Node ID': 'attributes.node_id'},
|
||||
{'Slave Server IDs': 'attributes.slaves'},
|
||||
{'Statistics': 'attributes.statistics'},
|
||||
{'Parameters': 'attributes.parameters'}
|
||||
])
|
||||
})
|
||||
.command('service <service>', 'Show service', {}, function(argv) {
|
||||
getResource('services/' + argv.service, [
|
||||
{'Service': 'id'},
|
||||
{'Router': 'attributes.router'},
|
||||
{'State': 'attributes.state'},
|
||||
{'Started At': 'attributes.started'},
|
||||
{'Current Connections': 'attributes.connections'},
|
||||
{'Total Connections': 'attributes.total_connections'},
|
||||
{'Servers': 'relationships.servers.data[].id'},
|
||||
{'Parameters': 'attributes.parameters'},
|
||||
{'Router Diagnostics': 'attributes.router_diagnostics'}
|
||||
])
|
||||
})
|
||||
.command('monitor <monitor>', 'Show monitor', {}, function(argv) {
|
||||
getResource('monitors/' + argv.monitor, [
|
||||
{'Monitor': 'id'},
|
||||
{'State': 'attributes.state'},
|
||||
{'Servers': 'relationships.servers.data[].id'},
|
||||
{'Parameters': 'attributes.parameters'},
|
||||
{'Monitor Diagnostics': 'attributes.monitor_diagnostics'}
|
||||
])
|
||||
})
|
||||
.command('session <session>', 'Show session', {}, function(argv) {
|
||||
getResource('sessions/' + argv.session, [
|
||||
{'Id': 'id'},
|
||||
{'Service': 'relationships.services.data[].id'},
|
||||
{'State': 'attributes.state'},
|
||||
{'User': 'attributes.user'},
|
||||
{'Host': 'attributes.remote'},
|
||||
{'Connected': 'attributes.connected'},
|
||||
{'Idle': 'attributes.idle'}
|
||||
])
|
||||
})
|
||||
.command('filter <filter>', 'Show filter', {}, function(argv) {
|
||||
getResource('filters/' + argv.filter, [
|
||||
{'Filter': 'id'},
|
||||
{'Module': 'attributes.module'},
|
||||
{'Services': 'relationships.services.data[].id'},
|
||||
{'Parameters': 'attributes.parameters'}
|
||||
])
|
||||
})
|
||||
.command('module <module>', 'Show loaded module', {}, function(argv) {
|
||||
getResource('maxscale/modules/' + argv.module, [
|
||||
{'Module': 'id'},
|
||||
{'Type': 'attributes.module_type'},
|
||||
{'Version': 'attributes.version'},
|
||||
{'Maturity': 'attributes.maturity'},
|
||||
{'Description': 'attributes.description'},
|
||||
{'Parameters': 'attributes.parameters'},
|
||||
{'Commands': 'attributes.commands'}
|
||||
])
|
||||
})
|
||||
.command('maxscale', 'Show MaxScale information', {}, function(argv) {
|
||||
getResource('maxscale', [
|
||||
{'Version': 'attributes.version'},
|
||||
{'Commit': 'attributes.commit'},
|
||||
{'Started At': 'attributes.started_at'},
|
||||
{'Uptime': 'attributes.uptime'}
|
||||
])
|
||||
})
|
||||
.command('commands <module>', 'Show module commands of a module', {}, function(argv) {
|
||||
getSubCollection('maxscale/modules/' + argv.module, 'attributes.commands', [
|
||||
{'Command': 'id'},
|
||||
{'Parameters': 'attributes.parameters[].type'},
|
||||
{'Descriptions': 'attributes.parameters[].description'}
|
||||
])
|
||||
})
|
||||
.usage('Usage: show <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help show` for a list of commands.')
|
||||
})
|
||||
}
|
31
maxctrl/lib/start.js
Normal file
31
maxctrl/lib/start.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'start <command>'
|
||||
exports.desc = 'Start objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('service <name>', 'Start a service', {}, function(argv) {
|
||||
doRequest('services/' + argv.name + '/start', null, {method: 'PUT'})
|
||||
})
|
||||
.command('monitor <name>', 'Start a monitor', {}, function(argv) {
|
||||
doRequest('monitors/' + argv.name + '/start', null, {method: 'PUT'})
|
||||
})
|
||||
.usage('Usage: start <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help start` for a list of commands.')
|
||||
})
|
||||
}
|
31
maxctrl/lib/stop.js
Normal file
31
maxctrl/lib/stop.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
exports.command = 'stop <command>'
|
||||
exports.desc = 'Stop objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('service <name>', 'Stop a service', {}, function(argv) {
|
||||
doRequest('services/' + argv.name + '/stop', null, {method: 'PUT'})
|
||||
})
|
||||
.command('monitor <name>', 'Stop a monitor', {}, function(argv) {
|
||||
doRequest('monitors/' + argv.name + '/stop', null, {method: 'PUT'})
|
||||
})
|
||||
.usage('Usage: stop <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help stop` for a list of commands.')
|
||||
})
|
||||
}
|
47
maxctrl/lib/unlink.js
Normal file
47
maxctrl/lib/unlink.js
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
require('../common.js')()
|
||||
|
||||
function removeServer(path, targets) {
|
||||
doRequest(path, function(res) {
|
||||
var servers =_.get(res, 'data.relationships.servers.data', [])
|
||||
|
||||
_.remove(servers, function(i) {
|
||||
return targets.indexOf(i.id) != -1
|
||||
})
|
||||
|
||||
// Update relationships and remove unnecessary parts
|
||||
_.set(res, 'data.relationships.servers.data', servers)
|
||||
delete res.data.attributes
|
||||
|
||||
doRequest(path, null, {method: 'PATCH', body: res})
|
||||
})
|
||||
}
|
||||
|
||||
exports.command = 'unlink <command>'
|
||||
exports.desc = 'Unlink objects'
|
||||
exports.handler = function() {}
|
||||
exports.builder = function(yargs) {
|
||||
yargs
|
||||
.command('service <name> <server...>', 'Unlink servers from a service', {}, function(argv) {
|
||||
removeServer('services/' + argv.name, argv.server)
|
||||
})
|
||||
.command('monitor <name> <server...>', 'Unlink servers from a monitor', {}, function(argv) {
|
||||
removeServer('monitors/' + argv.name, argv.server)
|
||||
})
|
||||
.usage('Usage: unlink <command>')
|
||||
.help()
|
||||
.command('*', 'the default command', {}, () => {
|
||||
console.log('Unknown command. See output of `help unlink` for a list of commands.')
|
||||
})
|
||||
}
|
16
maxctrl/maxctrl.js
Normal file
16
maxctrl/maxctrl.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('./core.js')(process.argv)
|
25
maxctrl/package.json
Normal file
25
maxctrl/package.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "maxctrl",
|
||||
"version": "1.0.0",
|
||||
"description": "MaxScale Administrative Client",
|
||||
"repository": "https://github.com/mariadb-corporation/MaxScale",
|
||||
"main": "maxctrl.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"maxscale"
|
||||
],
|
||||
"bin": {
|
||||
"maxctrl": "./maxctrl.js"
|
||||
},
|
||||
"author": "MariaDB Corporation Ab",
|
||||
"license": "SEE LICENSE IN ../../LICENSE.TXT",
|
||||
"dependencies": {
|
||||
"cli-table": "^0.3.1",
|
||||
"lodash": "^4.17.4",
|
||||
"lodash-getpath": "^0.2.4",
|
||||
"request": "^2.81.0",
|
||||
"yargs": "^8.0.2"
|
||||
}
|
||||
}
|
3
maxctrl/test/CMakeLists.txt
Normal file
3
maxctrl/test/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
add_custom_target(test_rest_api
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_rest_api.sh ${CMAKE_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
30
maxctrl/test/after.sh
Executable file
30
maxctrl/test/after.sh
Executable file
@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# This script is run after each test block. It kills the MaxScale process
|
||||
# and cleans up the directories that contain generated files.
|
||||
#
|
||||
|
||||
test -z "$MAXSCALE_DIR" && exit 1
|
||||
|
||||
maxscaledir=$MAXSCALE_DIR
|
||||
|
||||
for ((i=0;i<10;i++))
|
||||
do
|
||||
pkill maxscale || break
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# If it wasn't dead before, now it is
|
||||
pgrep maxscale && pkill -9 maxscale
|
||||
|
||||
# Remove created users
|
||||
rm $maxscaledir/passwd
|
||||
rm $maxscaledir/maxadmin-users
|
||||
|
||||
rm -r $maxscaledir/lib/maxscale
|
||||
rm -r $maxscaledir/cache/maxscale
|
||||
rm -r $maxscaledir/run/maxscale
|
||||
mkdir -m 0755 -p $maxscaledir/lib/maxscale
|
||||
mkdir -m 0755 -p $maxscaledir/cache/maxscale
|
||||
mkdir -m 0755 -p $maxscaledir/run/maxscale
|
24
maxctrl/test/before.sh
Executable file
24
maxctrl/test/before.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# This script is run before each test block. It starts MaxScale and waits for it
|
||||
# to become responsive.
|
||||
#
|
||||
|
||||
maxscaledir=$MAXSCALE_DIR
|
||||
|
||||
test -z "$MAXSCALE_DIR" && exit 1
|
||||
|
||||
# Start MaxScale
|
||||
$maxscaledir/bin/maxscale -df $maxscaledir/maxscale.cnf >& $maxscaledir/maxscale.output &
|
||||
pid=$!
|
||||
|
||||
# Wait for MaxScale to start
|
||||
for ((i=0;i<60;i++))
|
||||
do
|
||||
$maxscaledir/bin/maxadmin help >& /dev/null && break
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
# Give MaxScale some time to settle
|
||||
sleep 1
|
19
maxctrl/test/package.json
Normal file
19
maxctrl/test/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "rest-api-tests",
|
||||
"version": "1.0.0",
|
||||
"repository": "https://github.com/mariadb-corporation/MaxScale",
|
||||
"description": "MaxScale REST API tests",
|
||||
"scripts": {
|
||||
"test": "mocha --timeout 30000 --slow 10000"
|
||||
},
|
||||
"author": "",
|
||||
"license": "SEE LICENSE IN ../../../../LICENSE.txt",
|
||||
"dependencies": {
|
||||
"ajv": "^5.0.1",
|
||||
"chai": "^3.5.0",
|
||||
"chai-as-promised": "^6.0.0",
|
||||
"mocha": "^3.3.0",
|
||||
"request": "^2.81.0",
|
||||
"request-promise-native": "^1.0.3"
|
||||
}
|
||||
}
|
116
maxctrl/test/test/auth.js
Normal file
116
maxctrl/test/test/auth.js
Normal file
@ -0,0 +1,116 @@
|
||||
require("../utils.js")()
|
||||
|
||||
|
||||
function set_auth(auth, value) {
|
||||
return request.get(auth + host + "/maxscale")
|
||||
.then(function(resp) {
|
||||
var d = JSON.parse(resp)
|
||||
d.data.attributes.parameters.admin_auth = value;
|
||||
return request.patch(auth + host + "/maxscale", { json: d })
|
||||
})
|
||||
.then(function() {
|
||||
return request.get(auth + host + "/maxscale")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var d = JSON.parse(resp)
|
||||
d.data.attributes.parameters.admin_auth.should.equal(value)
|
||||
})
|
||||
}
|
||||
|
||||
describe("Authentication", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
var user1 = {
|
||||
data: {
|
||||
id: "user1",
|
||||
type: "inet",
|
||||
attributes: {
|
||||
password: "pw1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var user2 = {
|
||||
data: {
|
||||
id: "user2",
|
||||
type: "inet",
|
||||
attributes: {
|
||||
password: "pw2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var auth1 = "http://" + user1.data.id + ":" + user1.data.attributes.password + "@"
|
||||
var auth2 = "http://" + user2.data.id + ":" + user2.data.attributes.password + "@"
|
||||
|
||||
it("unauthorized request without authentication", function() {
|
||||
return request.get(base_url + "/maxscale")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("authorized request without authentication", function() {
|
||||
return request.get(auth1 + host + "/maxscale")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("add user", function() {
|
||||
return request.post(base_url + "/users/inet", { json: user1 })
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("request created user", function() {
|
||||
return request.get(base_url + "/users/inet/" + user1.data.id)
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("enable authentication", function() {
|
||||
return set_auth(auth1, true).should.be.fulfilled
|
||||
})
|
||||
|
||||
it("unauthorized request with authentication", function() {
|
||||
return request.get(base_url + "/maxscale").auth()
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
it("authorized request with authentication", function() {
|
||||
return request.get(auth1 + host + "/maxscale")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("replace user", function() {
|
||||
return request.post(auth1 + host + "/users/inet", { json: user2 })
|
||||
.then(function() {
|
||||
return request.get(auth1 + host + "/users/inet/" + user2.data.id)
|
||||
})
|
||||
.then(function() {
|
||||
return request.delete(auth1 + host + "/users/inet/" + user1.data.id)
|
||||
})
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("request with wrong user", function() {
|
||||
return request.get(auth1 + host + "/maxscale")
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
it("request with correct user", function() {
|
||||
return request.get(auth2 + host + "/maxscale")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("disable authentication", function() {
|
||||
return set_auth(auth2, false).should.be.fulfilled
|
||||
})
|
||||
|
||||
it("unauthorized request without authentication ", function() {
|
||||
return request.get(base_url + "/maxscale/logs")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("authorized request without authentication", function() {
|
||||
return request.get(auth2 + host + "/maxscale")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
39
maxctrl/test/test/core.js
Normal file
39
maxctrl/test/test/core.js
Normal file
@ -0,0 +1,39 @@
|
||||
require("../utils.js")()
|
||||
|
||||
|
||||
function set_value(key, value) {
|
||||
return request.get(base_url + "/maxscale")
|
||||
.then(function(resp) {
|
||||
var d = JSON.parse(resp)
|
||||
d.data.attributes.parameters[key] = value;
|
||||
return request.patch(base_url + "/maxscale", { json: d })
|
||||
})
|
||||
.then(function() {
|
||||
return request.get(base_url + "/maxscale")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var d = JSON.parse(resp)
|
||||
d.data.attributes.parameters[key].should.equal(value)
|
||||
})
|
||||
}
|
||||
|
||||
describe("Core Parameters", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("auth_connect_timeout", function() {
|
||||
return set_value("auth_connect_timeout", 10)
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("auth_read_timeout", function() {
|
||||
return set_value("auth_read_timeout", 10)
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("auth_write_timeout", function() {
|
||||
return set_value("auth_write_timeout", 10)
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
21
maxctrl/test/test/errors.js
Normal file
21
maxctrl/test/test/errors.js
Normal file
@ -0,0 +1,21 @@
|
||||
require("../utils.js")()
|
||||
|
||||
|
||||
describe("Errors", function()
|
||||
{
|
||||
before(startMaxScale)
|
||||
|
||||
it("error on invalid PATCH request", function()
|
||||
{
|
||||
return request.patch(base_url + "/servers/server1", { json: {this_is: "a test"}})
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
it("error on invalid POST request", function()
|
||||
{
|
||||
return request.post(base_url + "/servers", { json: {this_is: "a test"}})
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
126
maxctrl/test/test/http.js
Normal file
126
maxctrl/test/test/http.js
Normal file
@ -0,0 +1,126 @@
|
||||
require("../utils.js")()
|
||||
|
||||
|
||||
describe("HTTP Headers", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("ETag changes after modification", function() {
|
||||
return request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
||||
.then(function(resp) {
|
||||
resp.headers.etag.should.be.equal("\"0\"")
|
||||
var srv = JSON.parse(resp.body)
|
||||
delete srv.data.relationships
|
||||
return request.patch(base_url + "/servers/server1", {json: srv})
|
||||
})
|
||||
.then(function() {
|
||||
return request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
||||
})
|
||||
.then(function(resp) {
|
||||
resp.headers.etag.should.be.equal("\"1\"")
|
||||
})
|
||||
});
|
||||
|
||||
it("Last-Modified changes after modification", function(done) {
|
||||
var date;
|
||||
|
||||
request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
||||
.then(function(resp) {
|
||||
|
||||
// Store the current modification time
|
||||
resp.headers["last-modified"].should.not.be.null
|
||||
date = resp.headers["last-modified"]
|
||||
|
||||
// Modify resource after three seconds
|
||||
setTimeout(function() {
|
||||
var srv = JSON.parse(resp.body)
|
||||
|
||||
srv.data.relationships = {
|
||||
services: {
|
||||
data: [
|
||||
{id: "RW-Split-Router", type: "services"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
request.patch(base_url + "/servers/server1", {json: srv})
|
||||
.then(function() {
|
||||
return request.get(base_url + "/servers/server1", {resolveWithFullResponse: true})
|
||||
})
|
||||
.then(function(resp) {
|
||||
resp.headers["last-modified"].should.not.be.null
|
||||
resp.headers["last-modified"].should.not.be.equal(date)
|
||||
done()
|
||||
})
|
||||
.catch(function(e) {
|
||||
done(e)
|
||||
})
|
||||
|
||||
}, 2000)
|
||||
})
|
||||
.catch(function(e) {
|
||||
done(e)
|
||||
})
|
||||
});
|
||||
|
||||
var oldtime = new Date(new Date().getTime() - 1000000).toUTCString();
|
||||
var newtime = new Date(new Date().getTime() + 1000000).toUTCString();
|
||||
|
||||
it("request with older If-Modified-Since value", function() {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
headers: { "If-Modified-Since": oldtime}
|
||||
}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it("request with newer If-Modified-Since value", function() {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
resolveWithFullResponse: true,
|
||||
headers: { "If-Modified-Since": newtime }
|
||||
}).should.be.rejected
|
||||
})
|
||||
|
||||
it("request with older If-Unmodified-Since value", function() {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
headers: { "If-Unmodified-Since": oldtime}
|
||||
}).should.be.rejected
|
||||
})
|
||||
|
||||
it("request with newer If-Unmodified-Since value", function() {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
headers: { "If-Unmodified-Since": newtime}
|
||||
}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it("request with mismatching If-Match value", function() {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
headers: { "If-Match": "\"0\""}
|
||||
}).should.be.rejected
|
||||
})
|
||||
|
||||
it("request with matching If-Match value", function() {
|
||||
return request.get(base_url + "/servers/server1", { resolveWithFullResponse: true })
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
headers: { "If-Match": resp.headers["etag"]}
|
||||
})
|
||||
})
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("request with mismatching If-None-Match value", function() {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
headers: { "If-None-Match": "\"0\""}
|
||||
}).should.be.fulfilled
|
||||
})
|
||||
|
||||
it("request with matching If-None-Match value", function() {
|
||||
return request.get(base_url + "/servers/server1", { resolveWithFullResponse: true })
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/servers/server1", {
|
||||
headers: { "If-None-Match": resp.headers["etag"]}
|
||||
})
|
||||
})
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
42
maxctrl/test/test/logs.js
Normal file
42
maxctrl/test/test/logs.js
Normal file
@ -0,0 +1,42 @@
|
||||
require("../utils.js")()
|
||||
|
||||
describe("Logs", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("change logging options", function() {
|
||||
return request.get(base_url + "/maxscale/logs")
|
||||
.then(function(resp) {
|
||||
var logs = JSON.parse(resp)
|
||||
logs.data.attributes.parameters.maxlog.should.be.true
|
||||
logs.data.attributes.parameters.syslog.should.be.true
|
||||
logs.data.attributes.parameters.highprecision.should.be.false
|
||||
logs.data.attributes.parameters.maxlog = false
|
||||
logs.data.attributes.parameters.syslog = false
|
||||
logs.data.attributes.parameters.highprecision = true
|
||||
logs.data.attributes.parameters.throttling.count = 1
|
||||
logs.data.attributes.parameters.throttling.suppress_ms = 1
|
||||
logs.data.attributes.parameters.throttling.window_ms = 1
|
||||
|
||||
return request.patch(base_url + "/maxscale/logs", {json: logs})
|
||||
})
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/maxscale/logs")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var logs = JSON.parse(resp)
|
||||
logs.data.attributes.parameters.maxlog.should.be.false
|
||||
logs.data.attributes.parameters.syslog.should.be.false
|
||||
logs.data.attributes.parameters.highprecision.should.be.true
|
||||
logs.data.attributes.parameters.throttling.count.should.be.equal(1)
|
||||
logs.data.attributes.parameters.throttling.suppress_ms.should.be.equal(1)
|
||||
logs.data.attributes.parameters.throttling.window_ms.should.be.equal(1)
|
||||
})
|
||||
});
|
||||
|
||||
it("flush logs", function() {
|
||||
return request.post(base_url + "/maxscale/logs/flush")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
162
maxctrl/test/test/monitor.js
Normal file
162
maxctrl/test/test/monitor.js
Normal file
@ -0,0 +1,162 @@
|
||||
require("../utils.js")()
|
||||
|
||||
var monitor = {
|
||||
data: {
|
||||
id: "test-monitor",
|
||||
type: "monitors",
|
||||
attributes: {
|
||||
module: "mysqlmon"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("Monitor", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("create new monitor", function() {
|
||||
return request.post(base_url + "/monitors/", {json: monitor})
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("request monitor", function() {
|
||||
return request.get(base_url + "/monitors/" + monitor.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("alter monitor", function() {
|
||||
monitor.data.attributes.parameters = {
|
||||
monitor_interval: 1000
|
||||
}
|
||||
return request.patch(base_url + "/monitors/" + monitor.data.id, {json:monitor})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("destroy created monitor", function() {
|
||||
return request.delete(base_url + "/monitors/" + monitor.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
})
|
||||
|
||||
describe("Monitor Relationships", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("create new monitor", function() {
|
||||
return request.post(base_url + "/monitors/", {json: monitor})
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("remove relationships from old monitor", function() {
|
||||
|
||||
return request.get(base_url + "/monitors/MySQL-Monitor")
|
||||
.then(function(resp) {
|
||||
var mon = JSON.parse(resp)
|
||||
delete mon.data.relationships.servers
|
||||
return request.patch(base_url + "/monitors/MySQL-Monitor", {json: mon})
|
||||
})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("add relationships to new monitor", function() {
|
||||
|
||||
return request.get(base_url + "/monitors/" + monitor.data.id)
|
||||
.then(function(resp) {
|
||||
var mon = JSON.parse(resp)
|
||||
mon.data.relationships.servers = [
|
||||
{id: "server1", type: "servers"},
|
||||
{id: "server2", type: "servers"},
|
||||
{id: "server3", type: "servers"},
|
||||
{id: "server4", type: "servers"},
|
||||
]
|
||||
return request.patch(base_url + "/monitors/" + monitor.data.id, {json: mon})
|
||||
})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("move relationships back to old monitor", function() {
|
||||
|
||||
return request.get(base_url + "/monitors/" + monitor.data.id)
|
||||
.then(function(resp) {
|
||||
var mon = JSON.parse(resp)
|
||||
delete mon.data.relationships.servers
|
||||
return request.patch(base_url + "/monitors/" + monitor.data.id, {json: mon})
|
||||
})
|
||||
.then(function() {
|
||||
return request.get(base_url + "/monitors/MySQL-Monitor")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var mon = JSON.parse(resp)
|
||||
mon.data.relationships.servers = [
|
||||
{id: "server1", type: "servers"},
|
||||
{id: "server2", type: "servers"},
|
||||
{id: "server3", type: "servers"},
|
||||
{id: "server4", type: "servers"},
|
||||
]
|
||||
return request.patch(base_url + "/monitors/MySQL-Monitor", {json: mon})
|
||||
})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("destroy created monitor", function() {
|
||||
return request.delete(base_url + "/monitors/" + monitor.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
})
|
||||
|
||||
describe("Monitor Actions", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("stop monitor", function() {
|
||||
return request.put(base_url + "/monitors/MySQL-Monitor/stop")
|
||||
.then(function() {
|
||||
return request.get(base_url + "/monitors/MySQL-Monitor")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var mon = JSON.parse(resp)
|
||||
mon.data.attributes.state.should.be.equal("Stopped")
|
||||
})
|
||||
});
|
||||
|
||||
it("start monitor", function() {
|
||||
return request.put(base_url + "/monitors/MySQL-Monitor/start")
|
||||
.then(function() {
|
||||
return request.get(base_url + "/monitors/MySQL-Monitor")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var mon = JSON.parse(resp)
|
||||
mon.data.attributes.state.should.be.equal("Running")
|
||||
})
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
})
|
||||
|
||||
|
||||
describe("Monitor Regressions", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("alter monitor should not remove servers", function() {
|
||||
var b = {
|
||||
data: {
|
||||
attributes: {
|
||||
parameters: {
|
||||
user: "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return request.patch(base_url + "/monitors/MySQL-Monitor", {json: b})
|
||||
.then(function() {
|
||||
return request.get(base_url + "/monitors/MySQL-Monitor")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var mon = JSON.parse(resp)
|
||||
mon.data.relationships.servers.data.should.not.be.empty
|
||||
})
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
})
|
12
maxctrl/test/test/options.js
Normal file
12
maxctrl/test/test/options.js
Normal file
@ -0,0 +1,12 @@
|
||||
require("../utils.js")()
|
||||
|
||||
describe("Request Options", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("pretty=true", function() {
|
||||
return request.get(base_url + "/services/?pretty=true")
|
||||
.should.eventually.satisfy(validate)
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
111
maxctrl/test/test/schema_validation.js
Normal file
111
maxctrl/test/test/schema_validation.js
Normal file
@ -0,0 +1,111 @@
|
||||
// These tests use the server/test/maxscale_test.cnf configuration
|
||||
|
||||
require("../utils.js")()
|
||||
|
||||
describe("Resource Collections", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
var tests = [
|
||||
"/servers",
|
||||
"/sessions",
|
||||
"/services",
|
||||
"/monitors",
|
||||
"/filters",
|
||||
"/maxscale/threads",
|
||||
"/maxscale/modules",
|
||||
"/maxscale/tasks",
|
||||
"/users",
|
||||
"/users/inet",
|
||||
"/users/unix",
|
||||
]
|
||||
|
||||
tests.forEach(function(endpoint) {
|
||||
it(endpoint + ': resource found', function() {
|
||||
return request(base_url + endpoint)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it(endpoint + ': resource schema is valid', function() {
|
||||
return request(base_url + endpoint)
|
||||
.should.eventually.satisfy(validate)
|
||||
});
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
||||
|
||||
describe("Individual Resources", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
var tests = [
|
||||
"/servers/server1",
|
||||
"/servers/server2",
|
||||
"/services/RW-Split-Router",
|
||||
"/services/RW-Split-Router/listeners",
|
||||
"/monitors/MySQL-Monitor",
|
||||
"/filters/Hint",
|
||||
"/sessions/1",
|
||||
"/maxscale/",
|
||||
"/maxscale/threads/0",
|
||||
"/maxscale/logs",
|
||||
"/maxscale/modules/readwritesplit",
|
||||
]
|
||||
|
||||
tests.forEach(function(endpoint) {
|
||||
it(endpoint + ': resource found', function() {
|
||||
return request(base_url + endpoint)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it(endpoint + ': resource schema is valid', function() {
|
||||
return request(base_url + endpoint)
|
||||
.should.eventually.satisfy(validate)
|
||||
});
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
||||
|
||||
describe("Resource Self Links", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
var tests = [
|
||||
"/servers",
|
||||
"/sessions",
|
||||
"/services",
|
||||
"/monitors",
|
||||
"/filters",
|
||||
"/maxscale/threads",
|
||||
"/maxscale/modules",
|
||||
"/maxscale/tasks",
|
||||
"/servers/server1",
|
||||
"/servers/server2",
|
||||
"/services/RW-Split-Router",
|
||||
"/services/RW-Split-Router/listeners",
|
||||
"/monitors/MySQL-Monitor",
|
||||
"/filters/Hint",
|
||||
"/sessions/1",
|
||||
"/maxscale/",
|
||||
"/maxscale/threads/0",
|
||||
"/maxscale/logs",
|
||||
"/maxscale/modules/readwritesplit",
|
||||
]
|
||||
|
||||
tests.forEach(function(endpoint) {
|
||||
it(endpoint + ': correct self link', function() {
|
||||
var obj = null;
|
||||
return request.get(base_url + endpoint)
|
||||
.then(function(resp) {
|
||||
obj = JSON.parse(resp)
|
||||
return request.get(obj.links.self)
|
||||
})
|
||||
.then(function(resp) {
|
||||
var obj_self = JSON.parse(resp)
|
||||
obj_self.links.self.should.be.equal(obj.links.self)
|
||||
})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
130
maxctrl/test/test/server.js
Normal file
130
maxctrl/test/test/server.js
Normal file
@ -0,0 +1,130 @@
|
||||
require("../utils.js")()
|
||||
|
||||
var server = {
|
||||
data: {
|
||||
id: "test-server",
|
||||
type: "servers",
|
||||
attributes: {
|
||||
parameters: {
|
||||
port: 3003,
|
||||
address: "127.0.0.1",
|
||||
protocol: "MySQLBackend"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var rel = {
|
||||
services: {
|
||||
data: [
|
||||
{ id: "RW-Split-Router", type: "services" },
|
||||
{ id: "Read-Connection-Router", type: "services" },
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
describe("Server", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("create new server", function() {
|
||||
return request.post(base_url + "/servers/", {json: server })
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("request server", function() {
|
||||
return request.get(base_url + "/servers/" + server.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("update server", function() {
|
||||
server.data.attributes.parameters.weight = 10
|
||||
return request.patch(base_url + "/servers/" + server.data.id, { json: server})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("destroy server", function() {
|
||||
return request.delete(base_url + "/servers/" + server.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
||||
|
||||
describe("Server Relationships", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
// We need a deep copy of the original server
|
||||
var rel_server = JSON.parse(JSON.stringify(server))
|
||||
rel_server.data.relationships = rel
|
||||
|
||||
it("create new server", function() {
|
||||
return request.post(base_url + "/servers/", {json: rel_server})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("request server", function() {
|
||||
return request.get(base_url + "/servers/" + rel_server.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("remove relationships", function() {
|
||||
delete rel_server.data["relationships"]
|
||||
return request.patch(base_url + "/servers/" + rel_server.data.id, {json: rel_server})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("destroy server", function() {
|
||||
return request.delete(base_url + "/servers/" + rel_server.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
||||
|
||||
describe("Server State", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("create new server", function() {
|
||||
return request.post(base_url + "/servers/", {json: server })
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
it("set server into maintenance", function() {
|
||||
return request.put(base_url + "/servers/" + server.data.id + "/set?state=maintenance")
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/servers/" + server.data.id)
|
||||
})
|
||||
.then(function(resp) {
|
||||
var srv = JSON.parse(resp)
|
||||
srv.data.attributes.state.should.match(/Maintenance/)
|
||||
})
|
||||
});
|
||||
|
||||
it("clear maintenance", function() {
|
||||
return request.put(base_url + "/servers/" + server.data.id + "/clear?state=maintenance")
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/servers/" + server.data.id)
|
||||
})
|
||||
.then(function(resp) {
|
||||
var srv = JSON.parse(resp)
|
||||
srv.data.attributes.state.should.not.match(/Maintenance/)
|
||||
})
|
||||
});
|
||||
|
||||
it("set invalid state value", function() {
|
||||
return request.put(base_url + "/servers/" + server.data.id + "/set?state=somethingstrange")
|
||||
.should.be.rejected
|
||||
});
|
||||
|
||||
it("clear invalid state value", function() {
|
||||
return request.put(base_url + "/servers/" + server.data.id + "/clear?state=somethingstrange")
|
||||
.should.be.rejected
|
||||
});
|
||||
|
||||
it("destroy server", function() {
|
||||
return request.delete(base_url + "/servers/" + server.data.id)
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
89
maxctrl/test/test/service.js
Normal file
89
maxctrl/test/test/service.js
Normal file
@ -0,0 +1,89 @@
|
||||
require("../utils.js")()
|
||||
|
||||
describe("Service", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
it("change service parameter", function() {
|
||||
return request.get(base_url + "/services/RW-Split-Router")
|
||||
.then(function(resp) {
|
||||
var svc = JSON.parse(resp)
|
||||
svc.data.attributes.parameters.enable_root_user = true
|
||||
return request.patch(base_url + "/services/RW-Split-Router", {json: svc})
|
||||
})
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/services/RW-Split-Router")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var svc = JSON.parse(resp)
|
||||
svc.data.attributes.parameters.enable_root_user.should.be.true
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
it("remove service relationship", function() {
|
||||
return request.get(base_url + "/services/RW-Split-Router")
|
||||
.then(function(resp) {
|
||||
var svc = JSON.parse(resp)
|
||||
delete svc.data.relationships
|
||||
return request.patch(base_url + "/services/RW-Split-Router", {json: svc})
|
||||
})
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/services/RW-Split-Router")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var svc = JSON.parse(resp)
|
||||
svc.data.relationships.should.be.empty
|
||||
})
|
||||
});
|
||||
|
||||
it("add service relationship", function() {
|
||||
return request.get(base_url + "/services/RW-Split-Router")
|
||||
.then(function(resp) {
|
||||
var svc = JSON.parse(resp)
|
||||
svc.data.relationships = {
|
||||
servers: {
|
||||
data: [
|
||||
{id: "server1", type: "servers"},
|
||||
{id: "server2", type: "servers"},
|
||||
{id: "server3", type: "servers"},
|
||||
{id: "server4", type: "servers"},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return request.patch(base_url + "/services/RW-Split-Router", {json: svc})
|
||||
})
|
||||
.then(function(resp) {
|
||||
return request.get(base_url + "/services/RW-Split-Router")
|
||||
})
|
||||
.then(function(resp) {
|
||||
var svc = JSON.parse(resp)
|
||||
svc.data.relationships.servers.data[0].id.should.be.equal("server1")
|
||||
})
|
||||
});
|
||||
|
||||
it("create a listener", function() {
|
||||
var listener = {
|
||||
"links": {
|
||||
"self": "http://localhost:8989/v1/services/RW-Split-Router/listeners"
|
||||
},
|
||||
"data": {
|
||||
"attributes": {
|
||||
"parameters": {
|
||||
"port": 4012,
|
||||
"protocol": "MySQLClient",
|
||||
"authenticator": "MySQLAuth",
|
||||
"address": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
"id": "RW-Split-Listener-2",
|
||||
"type": "listeners"
|
||||
}
|
||||
}
|
||||
|
||||
return request.post(base_url + "/services/RW-Split-Router/listeners", {json: listener})
|
||||
.should.be.fulfilled
|
||||
});
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
59
maxctrl/test/test/users.js
Normal file
59
maxctrl/test/test/users.js
Normal file
@ -0,0 +1,59 @@
|
||||
require("../utils.js")()
|
||||
|
||||
|
||||
describe("Users", function() {
|
||||
before(startMaxScale)
|
||||
|
||||
var user = {
|
||||
data: {
|
||||
id: "user1",
|
||||
type: "inet",
|
||||
attributes: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("add new user without password", function() {
|
||||
return request.post(base_url + "/users/inet", { json: user })
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
it("add user", function() {
|
||||
user.data.attributes.password = "pw1"
|
||||
return request.post(base_url + "/users/inet", { json: user })
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("add user again", function() {
|
||||
return request.post(base_url + "/users/inet", { json: user })
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
it("add user again but without password", function() {
|
||||
delete user.data.attributes.password
|
||||
return request.post(base_url + "/users/inet", { json: user })
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
it("get created user", function() {
|
||||
return request.get(base_url + "/users/inet/user1")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("get non-existent user", function() {
|
||||
return request.get(base_url + "/users/inet/user2")
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
it("delete created user", function() {
|
||||
return request.delete(base_url + "/users/inet/user1")
|
||||
.should.be.fulfilled
|
||||
})
|
||||
|
||||
it("delete created user again", function() {
|
||||
return request.delete(base_url + "/users/inet/user1")
|
||||
.should.be.rejected
|
||||
})
|
||||
|
||||
after(stopMaxScale)
|
||||
});
|
61
maxctrl/test/test_rest_api.sh
Executable file
61
maxctrl/test/test_rest_api.sh
Executable file
@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script builds and installs MaxScale, starts a MaxScale instance, runs the
|
||||
# tests use npm and stops MaxScale.
|
||||
#
|
||||
# This is definitely not the most efficient way to test the binaries but it's a
|
||||
# guaranteed method of creating a consistent and "safe" testing environment.
|
||||
#
|
||||
# TODO: Install and start a local MariaDB server for testing purposes
|
||||
|
||||
|
||||
srcdir=$1
|
||||
maxscaledir=$PWD/maxscale_test/
|
||||
testdir=$PWD/local_test/
|
||||
|
||||
mkdir -p $testdir && cd $testdir
|
||||
|
||||
# Currently all tests that use npm are for the REST API
|
||||
cp -t $testdir -r $srcdir/maxctrl/test/*
|
||||
npm install
|
||||
|
||||
mkdir -p $maxscaledir && cd $maxscaledir
|
||||
|
||||
# Configure and install MaxScale
|
||||
cmake $srcdir -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_INSTALL_PREFIX=$maxscaledir \
|
||||
-DBUILD_TESTS=Y \
|
||||
-DMAXSCALE_VARDIR=$maxscaledir \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DWITH_SCRIPTS=N \
|
||||
-DWITH_MAXSCALE_CNF=N \
|
||||
-DBUILD_CDC=Y \
|
||||
-DTARGET_COMPONENT=all \
|
||||
-DDEFAULT_MODULE_CONFIGDIR=$maxscaledir \
|
||||
-DDEFAULT_ADMIN_USER=`whoami`
|
||||
|
||||
make install
|
||||
|
||||
# Create required directories (we could run the postinst script but it's a bit too invasive)
|
||||
mkdir -p $maxscaledir/lib64/maxscale
|
||||
mkdir -p $maxscaledir/bin
|
||||
mkdir -p $maxscaledir/share/maxscale
|
||||
mkdir -p $maxscaledir/share/doc/MaxScale/maxscale
|
||||
mkdir -p $maxscaledir/log/maxscale
|
||||
mkdir -p $maxscaledir/lib/maxscale
|
||||
mkdir -p $maxscaledir/cache/maxscale
|
||||
mkdir -p $maxscaledir/run/maxscale
|
||||
chmod 0755 $maxscaledir/log/maxscale
|
||||
chmod 0755 $maxscaledir/lib/maxscale
|
||||
chmod 0755 $maxscaledir/cache/maxscale
|
||||
chmod 0755 $maxscaledir/run/maxscale
|
||||
|
||||
# This variable is used to start and stop MaxScale before each test
|
||||
export MAXSCALE_DIR=$maxscaledir
|
||||
|
||||
# Run tests
|
||||
cd $testdir
|
||||
npm test
|
||||
rval=$?
|
||||
|
||||
exit $rval
|
439
maxctrl/test/utils.js
Normal file
439
maxctrl/test/utils.js
Normal file
@ -0,0 +1,439 @@
|
||||
var json_api_schema = {
|
||||
"title": "JSON API Schema",
|
||||
"description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/success"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/failure"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/info"
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"success": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/data"
|
||||
},
|
||||
"included": {
|
||||
"description": "To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called \"compound documents\".",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/resource"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
},
|
||||
"links": {
|
||||
"description": "Link members related to the primary data.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/links"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/pagination"
|
||||
}
|
||||
]
|
||||
},
|
||||
"jsonapi": {
|
||||
"$ref": "#/definitions/jsonapi"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"failure": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"errors"
|
||||
],
|
||||
"properties": {
|
||||
"errors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/error"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
},
|
||||
"jsonapi": {
|
||||
"$ref": "#/definitions/jsonapi"
|
||||
},
|
||||
"links": {
|
||||
"$ref": "#/definitions/links"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"info": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
},
|
||||
"links": {
|
||||
"$ref": "#/definitions/links"
|
||||
},
|
||||
"jsonapi": {
|
||||
"$ref": "#/definitions/jsonapi"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"meta": {
|
||||
"description": "Non-standard meta-information that can not be represented as an attribute or relationship.",
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"data": {
|
||||
"description": "The document's \"primary data\" is a representation of the resource or collection of resources targeted by a request.",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/resource"
|
||||
},
|
||||
{
|
||||
"description": "An array of resource objects, an array of resource identifier objects, or an empty array ([]), for requests that target resource collections.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/resource"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
{
|
||||
"description": "null if the request is one that might correspond to a single resource, but doesn't currently.",
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resource": {
|
||||
"description": "\"Resource objects\" appear in a JSON API document to represent resources.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"attributes": {
|
||||
"$ref": "#/definitions/attributes"
|
||||
},
|
||||
"relationships": {
|
||||
"$ref": "#/definitions/relationships"
|
||||
},
|
||||
"links": {
|
||||
"$ref": "#/definitions/links"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"links": {
|
||||
"description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"self": {
|
||||
"description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"related": {
|
||||
"$ref": "#/definitions/link"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"link": {
|
||||
"description": "A link **MUST** be represented as either: a string containing the link's URL or a link object.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "A string containing the link's URL.",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"href"
|
||||
],
|
||||
"properties": {
|
||||
"href": {
|
||||
"description": "A string containing the link's URL.",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"attributes": {
|
||||
"description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^(?!relationships$|links$)\\w[-\\w_]*$": {
|
||||
"description": "Attributes may contain any valid JSON value."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"relationships": {
|
||||
"description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^\\w[-\\w_]*$": {
|
||||
"properties": {
|
||||
"links": {
|
||||
"$ref": "#/definitions/links"
|
||||
},
|
||||
"data": {
|
||||
"description": "Member, whose value represents \"resource linkage\".",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/relationshipToOne"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/relationshipToMany"
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"meta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"links"
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"relationshipToOne": {
|
||||
"description": "References to other resource objects in a to-one (\"relationship\"). Relationships can be specified by including a member in a resource's links object.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/empty"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/linkage"
|
||||
}
|
||||
]
|
||||
},
|
||||
"relationshipToMany": {
|
||||
"description": "An array of objects each containing \"type\" and \"id\" members for to-many relationships.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/linkage"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"empty": {
|
||||
"description": "Describes an empty to-one relationship.",
|
||||
"type": "null"
|
||||
},
|
||||
"linkage": {
|
||||
"description": "The \"type\" and \"id\" to non-empty members.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"pagination": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"first": {
|
||||
"description": "The first page of data",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"last": {
|
||||
"description": "The last page of data",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prev": {
|
||||
"description": "The previous page of data",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"next": {
|
||||
"description": "The next page of data",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsonapi": {
|
||||
"description": "An object describing the server's implementation",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "A unique identifier for this particular occurrence of the problem.",
|
||||
"type": "string"
|
||||
},
|
||||
"links": {
|
||||
"$ref": "#/definitions/links"
|
||||
},
|
||||
"status": {
|
||||
"description": "The HTTP status code applicable to this problem, expressed as a string value.",
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"description": "An application-specific error code, expressed as a string value.",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.",
|
||||
"type": "string"
|
||||
},
|
||||
"detail": {
|
||||
"description": "A human-readable explanation specific to this occurrence of the problem.",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pointer": {
|
||||
"description": "A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].",
|
||||
"type": "string"
|
||||
},
|
||||
"parameter": {
|
||||
"description": "A string indicating which query parameter caused the error.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"$ref": "#/definitions/meta"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validate_json(data) {
|
||||
return validate_func(JSON.parse(data))
|
||||
}
|
||||
|
||||
var child_process = require("child_process")
|
||||
|
||||
module.exports = function() {
|
||||
this.fs = require("fs")
|
||||
this.request = require("request-promise-native")
|
||||
this.chai = require("chai")
|
||||
this.assert = require("assert")
|
||||
this.chaiAsPromised = require("chai-as-promised")
|
||||
chai.use(chaiAsPromised)
|
||||
this.should = chai.should()
|
||||
this.expect = chai.expect
|
||||
this.Ajv = require("ajv")
|
||||
this.ajv = new Ajv({$data: true, allErrors: true, extendRefs: true, verbose: true})
|
||||
this.validate_func = ajv.compile(json_api_schema)
|
||||
this.validate = validate_json
|
||||
this.host = "localhost:8989/v1"
|
||||
this.base_url = "http://" + this.host
|
||||
this.startMaxScale = function(done) {
|
||||
child_process.execFile("./before.sh", function(err, stdout, stderr) {
|
||||
if (process.env.MAXSCALE_DIR == null) {
|
||||
throw new Error("MAXSCALE_DIR is not set");
|
||||
}
|
||||
|
||||
done()
|
||||
})
|
||||
};
|
||||
this.stopMaxScale = function(done) {
|
||||
child_process.execFile("./after.sh", function(err, stdout, stderr) {
|
||||
done()
|
||||
})
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user