[5.6] Implement esTestCluster test util (#13099) (#13408)

* [partial backport] extract kbnServer test utils from #12554

* Implement esTestCluster test util (#13099)

* [es/tests] remove unused module

* [testUtil/es] add utility for starting es nodes in tests

* [ftr/tests] use esTestCluster util to start es

* [es/tests/routes] use esTestCluster to start own es

* [testUtils/kbnServer] disable uiSettings unless plugins are enabled

* [testUtils/esTestCluster] use standard test es port by default

* [server/http/tests] add esTestCluster to setup

* [test/config] unify es test config into a single module

* [testUtils/esTestCluster] directory is no longer configurable

* [testUtils/esTestCluster] throw when es.start() is called again without es.stop()

* [testUtils/esTestCluster] is* checks should not mutate state

(cherry picked from commit 6748b22d03)

* [testUtils/esTestCluster] use more standard api style (#13197)

(cherry picked from commit d36080bca8)

* [scanner] use new esTestConfig service
This commit is contained in:
Spencer 2017-08-09 00:36:59 -07:00 committed by GitHub
parent b4be678efd
commit 4cc37a38d7
25 changed files with 265 additions and 209 deletions

View file

@ -264,7 +264,6 @@
"ncp": "2.0.0",
"nock": "8.0.0",
"node-sass": "3.8.0",
"portscanner": "1.0.0",
"proxyquire": "1.7.10",
"sass-loader": "4.0.0",
"simple-git": "1.37.0",

View file

@ -2,10 +2,9 @@ import _ from 'lodash';
import Promise from 'bluebird';
import sinon from 'sinon';
import expect from 'expect.js';
import url from 'url';
import SetupError from '../setup_error';
import { esTestServerUrlParts } from '../../../../../test/es_test_server_url_parts';
import { esTestConfig } from '../../../../test_utils/es';
import { ensureEsVersion } from '../ensure_es_version';
describe('plugins/elasticsearch', () => {
@ -27,7 +26,7 @@ describe('plugins/elasticsearch', () => {
status: {
red: sinon.stub()
},
url: url.format(esTestServerUrlParts)
url: esTestConfig.getUrl()
}
}
};

View file

@ -1,13 +0,0 @@
import Promise from 'bluebird';
import portscanner from 'portscanner';
module.exports = function findPort(start, end, host) {
host = host || 'localhost';
return new Promise(function (resolve, reject) {
portscanner.findAPortNotInUse(start, end, host, function (err, port) {
if (err) return reject(err);
resolve(port);
});
});
};

View file

@ -1,20 +1,18 @@
import Promise from 'bluebird';
import sinon from 'sinon';
import expect from 'expect.js';
import url from 'url';
const NoConnections = require('elasticsearch').errors.NoConnections;
import mappings from './fixtures/mappings';
import healthCheck from '../health_check';
import kibanaVersion from '../kibana_version';
import { esTestServerUrlParts } from '../../../../../test/es_test_server_url_parts';
import { esTestConfig } from '../../../../test_utils/es';
import * as determineEnabledScriptingLangsNS from '../determine_enabled_scripting_langs';
import { determineEnabledScriptingLangs } from '../determine_enabled_scripting_langs';
import * as ensureTypesExistNS from '../ensure_types_exist';
const esPort = esTestServerUrlParts.port;
const esUrl = url.format(esTestServerUrlParts);
const esPort = esTestConfig.getPort();
const esUrl = esTestConfig.getUrl();
describe('plugins/elasticsearch', () => {
describe('lib/health_check', function () {

View file

@ -1,32 +1,27 @@
import { format } from 'util';
import * as kbnTestServer from '../../../../test_utils/kbn_server';
import { fromRoot } from '../../../../utils';
import { createEsTestCluster } from '../../../../test_utils/es';
describe('plugins/elasticsearch', function () {
describe('routes', function () {
let kbnServer;
const es = createEsTestCluster({
name: 'core_plugins/es/routes',
});
before(async function () {
// Sometimes waiting for server takes longer than 10s.
// NOTE: This can't be a fat-arrow function because `this` needs to refer to the execution
// context, not to the parent context.
this.timeout(60000);
kbnServer = kbnTestServer.createServer({
plugins: {
scanDirs: [
fromRoot('src/core_plugins')
]
},
});
this.timeout(es.getStartTimeout());
await es.start();
kbnServer = kbnTestServer.createServerWithCorePlugins();
await kbnServer.ready();
await kbnServer.server.plugins.elasticsearch.waitUntilReady();
});
after(function () {
return kbnServer.close();
after(async function () {
await kbnServer.close();
await es.stop();
});
function testRoute(options, statusCode = 200) {

View file

@ -1,7 +1,6 @@
import expect from 'expect.js';
import sinon from 'sinon';
import * as kbnTestServer from '../../../../../test_utils/kbn_server.js';
import { fromRoot } from '../../../../../utils';
import manageUuid from '../manage_uuid';
describe('core_plugins/kibana/server/lib', function () {
@ -13,13 +12,7 @@ describe('core_plugins/kibana/server/lib', function () {
before(async function () {
this.timeout(60000); // sometimes waiting for server takes longer than 10
kbnServer = kbnTestServer.createServer({
plugins: {
scanDirs: [
fromRoot('src/core_plugins')
]
}
});
kbnServer = kbnTestServer.createServerWithCorePlugins();
await kbnServer.ready();
});

View file

@ -1,4 +1,6 @@
module.exports = function ({ env, bundle }) {
import { esTestConfig } from '../../test_utils/es';
export default function ({ env, bundle }) {
const pluginSlug = env.pluginInfo.sort()
.map(p => ' * - ' + p)
@ -26,22 +28,22 @@ window.__KBN__ = {
vars: {
kbnIndex: '.kibana',
esShardTimeout: 1500,
esApiVersion: '5.x',
esApiVersion: ${JSON.stringify(esTestConfig.getBranch())},
esRequestTimeout: '300000',
tilemapsConfig: {
deprecated: {
isOverridden: false,
config: {
options: {
config: {
options: {
}
}
}
}
}
},
regionmapsConfig: {
layers: []
},
mapConfig: {
manifestServiceUrl: 'https://geo.elastic.co/v1/manifest'
manifestServiceUrl: 'https://geo.elastic.co/v1/manifest'
}
},
uiSettings: {
@ -55,4 +57,4 @@ ${requires}
require('ui/test_harness').bootstrap(/* go! */);
`;
};
}

View file

@ -4,18 +4,17 @@ import { format as formatUrl } from 'url';
import { readConfigFile } from '../../lib';
import { createToolingLog, createReduceStream } from '../../../utils';
import { startupEs, startupKibana } from '../lib';
import { createEsTestCluster } from '../../../test_utils/es';
import { startupKibana } from '../lib';
const SCRIPT = resolve(__dirname, '../../../../scripts/functional_test_runner.js');
const CONFIG = resolve(__dirname, '../fixtures/with_es_archiver/config.js');
describe('single test that uses esArchiver', function () {
this.timeout(3 * 60 * 1000);
describe('single test that uses esArchiver', () => {
let log;
const cleanupWork = [];
before(async () => {
before(async function () {
log = createToolingLog('debug');
log.pipe(process.stdout);
log.indent(6);
@ -24,11 +23,17 @@ describe('single test that uses esArchiver', function () {
log.info('starting elasticsearch');
log.indent(2);
const es = await startupEs({
log,
port: config.get('servers.elasticsearch.port'),
fresh: false
const es = createEsTestCluster({
log: msg => log.debug(msg),
name: 'ftr/withEsArchiver',
port: config.get('servers.elasticsearch.port')
});
cleanupWork.unshift(() => es.stop());
this.timeout(es.getStartTimeout());
await es.start();
log.indent(-2);
log.info('starting kibana');
@ -39,8 +44,7 @@ describe('single test that uses esArchiver', function () {
});
log.indent(-2);
cleanupWork.push(() => kibana.close());
cleanupWork.push(() => es.shutdown());
cleanupWork.unshift(() => kibana.close());
});
it('test', async () => {

View file

@ -1,43 +0,0 @@
import { resolve } from 'path';
import { once, merge } from 'lodash';
import libesvm from 'libesvm';
const VERSION = 'master';
const DIRECTORY = resolve(__dirname, '../../../../esvm/functional_test_runner_tests');
const createCluster = (options = {}) => {
return libesvm.createCluster(merge({
directory: DIRECTORY,
branch: VERSION,
}, options));
};
const install = once(async (fresh) => {
await createCluster({ fresh }).install();
});
export async function startupEs(opts) {
const {
port,
log,
fresh = true
} = opts;
await install({ fresh });
const cluster = createCluster({
config: {
http: {
port
}
}
});
cluster.on('log', (event) => {
const method = event.level.toLowerCase() === 'info' ? 'verbose' : 'debug';
log[method](`${event.level}: ${event.type} - ${event.message}`);
});
await cluster.start();
return cluster;
}

View file

@ -1,2 +1 @@
export { startupEs } from './es';
export { startupKibana } from './kibana';

View file

@ -1,20 +1,12 @@
import { resolve } from 'path';
import { createServer } from '../../../test_utils/kbn_server';
import { createServerWithCorePlugins } from '../../../test_utils/kbn_server';
export async function startupKibana({ port, esUrl }) {
const server = createServer({
const server = createServerWithCorePlugins({
server: {
port,
autoListen: true,
},
plugins: {
scanDirs: [
resolve(__dirname, '../../../core_plugins')
],
},
elasticsearch: {
url: esUrl
}

View file

@ -1,24 +1,24 @@
import expect from 'expect.js';
import * as kbnTestServer from '../../../test_utils/kbn_server';
import { fromRoot } from '../../../utils';
describe('routes', function () {
this.slow(10000);
this.timeout(60000);
import { createEsTestCluster } from '../../../test_utils/es';
describe('routes', () => {
let kbnServer;
beforeEach(function () {
kbnServer = kbnTestServer.createServer({
plugins: {
scanDirs: [
fromRoot('src/core_plugins')
]
}
});
return kbnServer.ready();
const es = createEsTestCluster({
name: 'server/http',
});
afterEach(function () {
return kbnServer.close();
before(async function () {
this.timeout(es.getStartTimeout());
await es.start();
kbnServer = kbnTestServer.createServerWithCorePlugins();
await kbnServer.ready();
await kbnServer.server.plugins.elasticsearch.waitUntilReady();
});
after(async () => {
await kbnServer.close();
await es.stop();
});
describe('cookie validation', function () {

View file

@ -0,0 +1,99 @@
import { resolve } from 'path';
import libesvm from 'libesvm';
import { esTestConfig } from './es_test_config';
const ESVM_DIR = resolve(__dirname, '../../../esvm/test_utils/es_test_cluster');
const BRANCHES_DOWNLOADED = [];
function isDownloadNeeded(branch) {
if (process.env.ESVM_NO_FRESH || process.argv.includes('--esvm-no-fresh')) {
return false;
}
if (BRANCHES_DOWNLOADED.includes(branch)) {
return false;
}
return true;
}
export function createEsTestCluster(options = {}) {
const {
name,
log = console.log,
port = esTestConfig.getPort(),
branch = esTestConfig.getBranch(),
} = options;
if (!name) {
throw new Error('createEsTestCluster() requires { name }');
}
// assigned in use.start(), reassigned in use.stop()
let cluster;
return new class EsTestCluster {
getStartTimeout() {
return esTestConfig.getLibesvmStartTimeout();
}
async start() {
const download = isDownloadNeeded(branch);
if (cluster) {
throw new Error(`
EsTestCluster[${name}] is already started, call and await es.stop()
before calling es.start() again.
`);
}
cluster = libesvm.createCluster({
fresh: download,
purge: !download,
directory: ESVM_DIR,
branch,
config: {
http: {
port,
},
cluster: {
name,
},
discovery: {
zen: {
ping: {
unicast: {
hosts: [ `localhost:${port}` ]
}
}
}
}
}
});
cluster.on('log', (event) => {
log(`EsTestCluster[${name}]: ${event.type} - ${event.message}`);
});
await cluster.install();
if (download) {
// track the branches that have successfully downloaded
// after cluster.install() resolves
BRANCHES_DOWNLOADED.push(branch);
}
await cluster.start();
}
async stop() {
if (cluster) {
const c = cluster;
cluster = null;
await c.shutdown();
}
}
};
}

View file

@ -0,0 +1,44 @@
import { format as formatUrl } from 'url';
import { resolve } from 'path';
import { admin } from '../../../test/shield';
const SECOND = 1000;
const MINUTE = 60 * SECOND;
export const esTestConfig = new class EsTestConfig {
getLibesvmStartTimeout() {
return process.env.TEST_ES_STARTUP_TIMEOUT || (5 * MINUTE);
}
getDirectoryForEsvm(uniqueSubDir) {
if (!uniqueSubDir) {
throw new Error('getDirectoryForEsvm() requires uniqueSubDir');
}
return resolve(__dirname, '../esvm', uniqueSubDir);
}
getBranch() {
return '5.x';
}
getPort() {
return this.getUrlParts().port;
}
getUrl() {
return formatUrl(this.getUrlParts());
}
getUrlParts() {
return {
protocol: process.env.TEST_ES_PROTOCOL || 'http',
hostname: process.env.TEST_ES_HOSTNAME || 'localhost',
port: parseInt(process.env.TEST_ES_PORT, 10) || 9220,
auth: admin.username + ':' + admin.password,
username: admin.username,
password: admin.password,
};
}
};

View file

@ -0,0 +1,2 @@
export { esTestConfig } from './es_test_config';
export { createEsTestCluster } from './es_test_cluster';

View file

@ -1,11 +1,11 @@
import url from 'url';
import { resolve } from 'path';
import { defaultsDeep, set } from 'lodash';
import { header as basicAuthHeader } from './base_auth';
import { kibanaUser, kibanaServer } from '../../test/shield';
import { esTestServerUrlParts } from '../../test/es_test_server_url_parts';
import { esTestConfig } from '../test_utils/es';
import KbnServer from '../../src/server/kbn_server';
const SERVER_DEFAULTS = {
const DEFAULTS_SETTINGS = {
server: {
autoListen: false,
xsrf: {
@ -16,25 +16,50 @@ const SERVER_DEFAULTS = {
quiet: true
},
plugins: {},
uiSettings: {
enabled: false
},
optimize: {
enabled: false
},
};
const DEFAULT_SETTINGS_WITH_CORE_PLUGINS = {
plugins: {
scanDirs: [
resolve(__dirname, '../core_plugins'),
],
},
elasticsearch: {
url: url.format(esTestServerUrlParts),
url: esTestConfig.getUrl(),
username: kibanaServer.username,
password: kibanaServer.password
}
},
uiSettings: {
enabled: true
},
};
/**
* Creates an instance of KbnServer with default configuration
* tailored for unit tests
*
* @param {object} params Any config overrides for this instance
* @param {Object} [settings={}] Any config overrides for this instance
* @return {KbnServer}
*/
export function createServer(params = {}) {
params = defaultsDeep({}, params, SERVER_DEFAULTS);
return new KbnServer(params);
export function createServer(settings = {}) {
return new KbnServer(defaultsDeep({}, settings, DEFAULTS_SETTINGS));
}
/**
* Creates an instance of KbnServer, including all of the core plugins,
* with default configuration tailored for unit tests
*
* @param {Object} [settings={}]
* @return {KbnServer}
*/
export function createServerWithCorePlugins(settings = {}) {
return new KbnServer(defaultsDeep({}, settings, DEFAULT_SETTINGS_WITH_CORE_PLUGINS, DEFAULTS_SETTINGS));
}
/**

View file

@ -0,0 +1 @@
export { esTestConfig } from '../es/es_test_config';

View file

@ -4,9 +4,8 @@ import Bluebird from 'bluebird';
import 'elasticsearch-browser';
import ngMock from 'ng_mock';
import sinon from 'sinon';
import url from 'url';
import { esTestServerUrlParts } from '../../../../../test/es_test_server_url_parts';
import { esTestConfig } from 'test_utils/es_test_config';
describe('Scanner', function () {
let es;
@ -14,7 +13,7 @@ describe('Scanner', function () {
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (esFactory) {
es = esFactory({
host: url.format(esTestServerUrlParts),
host: esTestConfig.getUrl(),
defer: function () {
return Bluebird.defer();
}

View file

@ -1,14 +1,12 @@
import { esTestServerUrlParts } from '../../test/es_test_server_url_parts';
import { esTestConfig } from '../../src/test_utils/es';
module.exports = function (grunt) {
const resolve = require('path').resolve;
const directory = resolve(__dirname, '../../esvm');
const dataDir = resolve(directory, 'data_dir');
const pkgBranch = grunt.config.get('pkg.branch');
const branch = esTestConfig.getBranch();
const dataDir = esTestConfig.getDirectoryForEsvm('data_dir');
return {
options: {
branch: pkgBranch,
branch,
fresh: !grunt.option('esvm-no-fresh'),
config: {
http: {
@ -23,7 +21,7 @@ module.exports = function (grunt) {
dev: {
options: {
directory: resolve(directory, 'dev'),
directory: esTestConfig.getDirectoryForEsvm('dev'),
config: {
path: {
data: dataDir
@ -37,7 +35,7 @@ module.exports = function (grunt) {
tribe: {
options: {
directory: resolve(directory, 'tribe'),
directory: esTestConfig.getDirectoryForEsvm('tribe'),
config: {
path: {
data: dataDir
@ -88,37 +86,13 @@ module.exports = function (grunt) {
},
},
test: {
options: {
directory: resolve(directory, 'test'),
purge: true,
config: {
http: {
port: esTestServerUrlParts.port
},
cluster: {
name: 'esvm-test'
},
discovery: {
zen: {
ping: {
unicast: {
hosts: [ `localhost:${esTestServerUrlParts.port}` ]
}
}
}
}
}
}
},
ui: {
options: {
directory: resolve(directory, 'test'),
directory: esTestConfig.getDirectoryForEsvm('test'),
purge: true,
config: {
http: {
port: esTestServerUrlParts.port
port: esTestConfig.getPort()
},
cluster: {
name: 'esvm-ui'
@ -127,7 +101,7 @@ module.exports = function (grunt) {
zen: {
ping: {
unicast: {
hosts: [ `localhost:${esTestServerUrlParts.port}` ]
hosts: [ `localhost:${esTestConfig.getPort()}` ]
}
}
}

View file

@ -1,5 +1,4 @@
import { format } from 'url';
import { esTestServerUrlParts } from '../../test/es_test_server_url_parts';
import { esTestConfig } from '../../src/test_utils/es';
import { kibanaTestServerUrlParts } from '../../test/kibana_test_server_url_parts';
module.exports = function (grunt) {
@ -55,7 +54,7 @@ module.exports = function (grunt) {
args: [
...stdDevArgs,
'--optimize.enabled=false',
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
'--server.port=' + kibanaTestServerUrlParts.port,
'--server.xsrf.disableProtection=true',
...kbnServerFlags,
@ -76,7 +75,7 @@ module.exports = function (grunt) {
'--no-base-path',
'--no-ssl',
'--optimize.enabled=false',
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
'--server.port=' + kibanaTestServerUrlParts.port,
'--server.xsrf.disableProtection=true',
...kbnServerFlags,
@ -94,7 +93,7 @@ module.exports = function (grunt) {
args: [
...stdDevArgs,
'--server.port=' + kibanaTestServerUrlParts.port,
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
...kbnServerFlags,
]
},
@ -110,7 +109,7 @@ module.exports = function (grunt) {
args: [
...stdDevArgs,
'--server.port=' + kibanaTestServerUrlParts.port,
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
...kbnServerFlags,
]
},
@ -126,7 +125,7 @@ module.exports = function (grunt) {
args: [
...stdDevArgs,
'--server.port=' + kibanaTestServerUrlParts.port,
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
'--dev',
'--no-base-path',
'--no-ssl',

View file

@ -14,9 +14,7 @@ module.exports = function (grunt) {
grunt.registerTask('test:server', [
'checkPlugins',
'esvm:test',
'simplemocha:all',
'esvm_shutdown:test',
]);
grunt.registerTask('test:browser', [

View file

@ -4,14 +4,14 @@ import {
EsArchiverProvider,
} from './services';
import { esTestServerUrlParts } from '../es_test_server_url_parts';
import { esTestConfig } from '../../src/test_utils/es';
import { kibanaTestServerUrlParts } from '../kibana_test_server_url_parts';
export default function () {
return {
servers: {
kibana: kibanaTestServerUrlParts,
elasticsearch: esTestServerUrlParts,
elasticsearch: esTestConfig.getUrlParts(),
},
services: {
kibanaServer: KibanaServerProvider,

View file

@ -1,10 +0,0 @@
const shield = require('./shield');
export const esTestServerUrlParts = {
protocol: process.env.TEST_ES_PROTOCOL || 'http',
hostname: process.env.TEST_ES_HOSTNAME || 'localhost',
port: parseInt(process.env.TEST_ES_PORT, 10) || 9220,
auth: shield.admin.username + ':' + shield.admin.password,
username: shield.admin.username,
password: shield.admin.password,
};

View file

@ -1,10 +1,10 @@
const shield = require('./shield');
import { kibanaUser } from './shield';
export const kibanaTestServerUrlParts = {
protocol: process.env.TEST_KIBANA_PROTOCOL || 'http',
hostname: process.env.TEST_KIBANA_HOSTNAME || 'localhost',
port: parseInt(process.env.TEST_KIBANA_PORT, 10) || 5620,
auth: shield.kibanaUser.username + ':' + shield.kibanaUser.password,
username: shield.kibanaUser.username,
password: shield.kibanaUser.password,
auth: kibanaUser.username + ':' + kibanaUser.password,
username: kibanaUser.username,
password: kibanaUser.password,
};

View file

@ -1,16 +1,16 @@
const env = process.env;
exports.kibanaUser = {
export const kibanaUser = {
username: env.TEST_KIBANA_USER || 'elastic',
password: env.TEST_KIBANA_PASS || 'changeme'
};
exports.kibanaServer = {
export const kibanaServer = {
username: env.TEST_KIBANA_SERVER_USER || 'kibana',
password: env.TEST_KIBANA_SERVER_PASS || 'changeme'
};
exports.admin = {
export const admin = {
username: env.TEST_ES_USER || 'elastic',
password: env.TEST_ES_PASS || 'changeme'
};