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)
This commit is contained in:
Spencer 2017-07-28 09:47:41 -07:00 committed by spalger
parent b39c28ed17
commit 48bfe7f1a9
21 changed files with 236 additions and 166 deletions

View file

@ -266,7 +266,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,9 +2,8 @@ import _ from 'lodash';
import Promise from 'bluebird';
import sinon from 'sinon';
import expect from 'expect.js';
import url from 'url';
import { esTestServerUrlParts } from '../../../../../test/es_test_server_url_parts';
import { esTestConfig } from '../../../../test_utils/es';
import { ensureEsVersion } from '../ensure_es_version';
describe('plugins/elasticsearch', () => {
@ -22,7 +21,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';
export default 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,19 +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 patchKibanaIndexNS from '../patch_kibana_index';
import * as migrateConfigNS from '../migrate_config';
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,25 +1,27 @@
import { format } from 'util';
import * as kbnTestServer from '../../../../test_utils/kbn_server';
import { esTestCluster } from '../../../../test_utils/es';
describe('plugins/elasticsearch', function () {
describe('routes', function () {
let kbnServer;
const es = esTestCluster.use({
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);
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,3 +1,5 @@
import { esTestConfig } from '../../test_utils/es';
export default function ({ env, bundle }) {
const pluginSlug = env.pluginInfo.sort()
@ -26,22 +28,22 @@ window.__KBN__ = {
vars: {
kbnIndex: '.kibana',
esShardTimeout: 1500,
esApiVersion: 'master',
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: {

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 { esTestCluster } 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 = esTestCluster.use({
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,17 +1,24 @@
import expect from 'expect.js';
import * as kbnTestServer from '../../../test_utils/kbn_server';
import { esTestCluster } from '../../../test_utils/es';
describe('routes', function () {
this.slow(10000);
this.timeout(60000);
describe('routes', () => {
let kbnServer;
beforeEach(function () {
kbnServer = kbnTestServer.createServerWithCorePlugins();
return kbnServer.ready();
const es = esTestCluster.use({
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,103 @@
import { resolve } from 'path';
import { esTestConfig } from './es_test_config';
import libesvm from 'libesvm';
const ESVM_DIR = resolve(__dirname, '../../../esvm/test_utils/es_test_cluster');
export class EsTestCluster {
_branchesDownloaded = [];
use(options = {}) {
const {
name,
log = console.log,
port = esTestConfig.getPort(),
branch = esTestConfig.getBranch(),
} = options;
if (!name) {
throw new Error('esTestCluster.use() requires { name }');
}
// assigned in use.start(), reassigned in use.stop()
let cluster;
return {
getStartTimeout: () => {
return esTestConfig.getLibesvmStartTimeout();
},
start: async () => {
const download = this._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
this._branchesDownloaded.push(branch);
}
await cluster.start();
},
stop: async () => {
if (cluster) {
const c = cluster;
cluster = null;
await c.shutdown();
}
}
};
}
_isDownloadNeeded(branch) {
if (process.env.ESVM_NO_FRESH || process.argv.includes('--esvm-no-fresh')) {
return false;
}
if (this._branchesDownloaded.includes(branch)) {
return false;
}
return true;
}
}
export const esTestCluster = new EsTestCluster();

View file

@ -0,0 +1,45 @@
import { format as formatUrl } from 'url';
import { resolve } from 'path';
import pkg from '../../../package.json';
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 process.env.TEST_ES_BRANCH || pkg.branch;
}
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 { esTestCluster } from './es_test_cluster';
export { esTestConfig } from './es_test_config';

View file

@ -1,9 +1,8 @@
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 DEFAULTS_SETTINGS = {
@ -17,6 +16,9 @@ const DEFAULTS_SETTINGS = {
quiet: true
},
plugins: {},
uiSettings: {
enabled: false
},
optimize: {
enabled: false
},
@ -29,13 +31,15 @@ const DEFAULT_SETTINGS_WITH_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

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: {
@ -19,7 +17,7 @@ module.exports = function (grunt) {
dev: {
options: {
directory: resolve(directory, 'dev'),
directory: esTestConfig.getDirectoryForEsvm('dev'),
config: {
path: {
data: dataDir
@ -33,7 +31,7 @@ module.exports = function (grunt) {
tribe: {
options: {
directory: resolve(directory, 'tribe'),
directory: esTestConfig.getDirectoryForEsvm('tribe'),
config: {
path: {
data: dataDir
@ -84,37 +82,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'
@ -123,7 +97,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,
@ -75,7 +74,7 @@ module.exports = function (grunt) {
'--dev',
'--no-base-path',
'--optimize.enabled=false',
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
'--server.port=' + kibanaTestServerUrlParts.port,
'--server.xsrf.disableProtection=true',
...kbnServerFlags,
@ -93,7 +92,7 @@ module.exports = function (grunt) {
args: [
...stdDevArgs,
'--server.port=' + kibanaTestServerUrlParts.port,
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
...kbnServerFlags,
]
},
@ -109,7 +108,7 @@ module.exports = function (grunt) {
args: [
...stdDevArgs,
'--server.port=' + kibanaTestServerUrlParts.port,
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
...kbnServerFlags,
]
},
@ -125,7 +124,7 @@ module.exports = function (grunt) {
args: [
...stdDevArgs,
'--server.port=' + kibanaTestServerUrlParts.port,
'--elasticsearch.url=' + format(esTestServerUrlParts),
'--elasticsearch.url=' + esTestConfig.getUrl(),
'--dev',
'--no-base-path',
'--optimize.lazyPort=5611',

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

@ -6,14 +6,14 @@ import {
RetryProvider,
} 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'
};