mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Health Gateway] Add integration tests (#146334)
This commit is contained in:
parent
866101eda6
commit
1bf581af01
25 changed files with 411 additions and 7 deletions
|
@ -96,6 +96,7 @@ enabled:
|
|||
- test/functional/apps/visualize/replaced_vislib_chart_types/config.ts
|
||||
- test/functional/config.ccs.ts
|
||||
- test/functional/config.firefox.js
|
||||
- test/health_gateway/config.ts
|
||||
- test/interactive_setup_api_integration/enrollment_flow.config.ts
|
||||
- test/interactive_setup_api_integration/manual_configuration_flow_without_tls.config.ts
|
||||
- test/interactive_setup_api_integration/manual_configuration_flow.config.ts
|
||||
|
|
|
@ -35,6 +35,7 @@ export class KibanaService {
|
|||
|
||||
async start({ server }: KibanaServiceStartDependencies) {
|
||||
server.addRoute(new RootRoute(this.kibanaConfig, this.logger));
|
||||
this.logger.info('Server is ready');
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
|
|
@ -40,7 +40,6 @@ export class RootRoute implements ServerRoute {
|
|||
return (status >= 200 && status <= 299) || status === 302;
|
||||
}
|
||||
|
||||
private static readonly POLL_ROUTE = '/';
|
||||
private static readonly STATUS_CODE: Record<Status, number> = {
|
||||
healthy: 200,
|
||||
unhealthy: 503,
|
||||
|
@ -78,13 +77,14 @@ export class RootRoute implements ServerRoute {
|
|||
}
|
||||
|
||||
private async pollHost(host: string): Promise<HostStatus> {
|
||||
const url = `${host}${RootRoute.POLL_ROUTE}`;
|
||||
this.logger.debug(`Requesting ${url}`);
|
||||
this.logger.debug(`Requesting '${host}'`);
|
||||
|
||||
try {
|
||||
const response = await this.fetch(url);
|
||||
const response = await this.fetch(host);
|
||||
const status = RootRoute.isHealthy(response) ? 'healthy' : 'unhealthy';
|
||||
this.logger.debug(`${capitalize(status)} response from ${url} with code ${response.status}`);
|
||||
this.logger.debug(
|
||||
`${capitalize(status)} response from '${host}' with code ${response.status}`
|
||||
);
|
||||
|
||||
return {
|
||||
host,
|
||||
|
@ -95,7 +95,7 @@ export class RootRoute implements ServerRoute {
|
|||
this.logger.error(error);
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
this.logger.error(`Request timeout for ${url}`);
|
||||
this.logger.error(`Request timeout for '${host}'`);
|
||||
|
||||
return {
|
||||
host,
|
||||
|
@ -103,7 +103,7 @@ export class RootRoute implements ServerRoute {
|
|||
};
|
||||
}
|
||||
|
||||
this.logger.error(`Failed response from ${url}: ${error.message}`);
|
||||
this.logger.error(`Failed response from '${host}': ${error.message}`);
|
||||
|
||||
return {
|
||||
host,
|
||||
|
|
|
@ -23,6 +23,7 @@ function getPluginSearchPaths({ rootDir, oss, examples, testPlugins }) {
|
|||
...(testPlugins
|
||||
? [
|
||||
resolve(rootDir, 'test/analytics/fixtures/plugins'),
|
||||
resolve(rootDir, 'test/health_gateway/plugins'),
|
||||
resolve(rootDir, 'test/plugin_functional/plugins'),
|
||||
resolve(rootDir, 'test/interpreter_functional/plugins'),
|
||||
resolve(rootDir, 'test/common/fixtures/plugins'),
|
||||
|
|
34
test/health_gateway/config.ts
Normal file
34
test/health_gateway/config.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
import { services } from './services';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js'));
|
||||
|
||||
return {
|
||||
services,
|
||||
rootTags: ['runOutsideOfCiGroups'],
|
||||
esTestCluster: functionalConfig.get('esTestCluster'),
|
||||
servers: functionalConfig.get('servers'),
|
||||
testFiles: [require.resolve('./tests')],
|
||||
junit: {
|
||||
reportName: 'Health Gateway Functional Tests',
|
||||
},
|
||||
kbnTestServer: {
|
||||
...functionalConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
...functionalConfig.get('kbnTestServer.serverArgs'),
|
||||
'--env.name=development',
|
||||
`--plugin-path=${path.resolve(__dirname, 'plugins/status')}`,
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
13
test/health_gateway/fixtures/flaky.yaml
Normal file
13
test/health_gateway/fixtures/flaky.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
server:
|
||||
port: ${PORT}
|
||||
host: ${HOST}
|
||||
|
||||
kibana:
|
||||
hosts:
|
||||
- ${KIBANA_URL}/api/status/ok
|
||||
- ${KIBANA_URL}/api/status/flaky?session=${SESSION}
|
||||
|
||||
logging:
|
||||
root:
|
||||
appenders: ['console']
|
||||
level: 'all'
|
14
test/health_gateway/fixtures/healthy.yaml
Normal file
14
test/health_gateway/fixtures/healthy.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
server:
|
||||
port: ${PORT}
|
||||
host: ${HOST}
|
||||
|
||||
kibana:
|
||||
hosts:
|
||||
- ${KIBANA_URL}/api/status/ok
|
||||
- ${KIBANA_URL}/api/status/redirect
|
||||
- ${KIBANA_URL}/api/status/unauthorized
|
||||
|
||||
logging:
|
||||
root:
|
||||
appenders: ['console']
|
||||
level: 'all'
|
13
test/health_gateway/fixtures/invalid.yaml
Normal file
13
test/health_gateway/fixtures/invalid.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
server:
|
||||
port: ${PORT}
|
||||
host: ${HOST}
|
||||
|
||||
kibana:
|
||||
hosts:
|
||||
- http://localhost:65537/api/status/ok
|
||||
requestTimeout: 2s
|
||||
|
||||
logging:
|
||||
root:
|
||||
appenders: ['console']
|
||||
level: 'all'
|
13
test/health_gateway/fixtures/mixed.yaml
Normal file
13
test/health_gateway/fixtures/mixed.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
server:
|
||||
port: ${PORT}
|
||||
host: ${HOST}
|
||||
|
||||
kibana:
|
||||
hosts:
|
||||
- ${KIBANA_URL}/api/status/ok
|
||||
- ${KIBANA_URL}/api/status/not-found
|
||||
|
||||
logging:
|
||||
root:
|
||||
appenders: ['console']
|
||||
level: 'all'
|
13
test/health_gateway/fixtures/timeout.yaml
Normal file
13
test/health_gateway/fixtures/timeout.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
server:
|
||||
port: ${PORT}
|
||||
host: ${HOST}
|
||||
|
||||
kibana:
|
||||
hosts:
|
||||
- ${KIBANA_URL}/api/status/slow
|
||||
requestTimeout: 2s
|
||||
|
||||
logging:
|
||||
root:
|
||||
appenders: ['console']
|
||||
level: 'all'
|
12
test/health_gateway/fixtures/unhealthy.yaml
Normal file
12
test/health_gateway/fixtures/unhealthy.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
server:
|
||||
port: ${PORT}
|
||||
host: ${HOST}
|
||||
|
||||
kibana:
|
||||
hosts:
|
||||
- ${KIBANA_URL}/api/status/not-found
|
||||
|
||||
logging:
|
||||
root:
|
||||
appenders: ['console']
|
||||
level: 'all'
|
12
test/health_gateway/plugins/status/kibana.json
Normal file
12
test/health_gateway/plugins/status/kibana.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"id": "kbnHealthGatewayStatus",
|
||||
"owner": {
|
||||
"name": "Core",
|
||||
"githubTeam": "kibana-core"
|
||||
},
|
||||
"version": "0.1.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"requiredPlugins": [],
|
||||
"server": true,
|
||||
"requiredBundles": []
|
||||
}
|
10
test/health_gateway/plugins/status/package.json
Normal file
10
test/health_gateway/plugins/status/package.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "kbn_health_gateway_status",
|
||||
"version": "0.1.0",
|
||||
"main": "target/test/health_gateway/plugins/status",
|
||||
"kibana": {
|
||||
"version": "kibana",
|
||||
"templateVersion": "0.1.0"
|
||||
},
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
13
test/health_gateway/plugins/status/server/index.ts
Normal file
13
test/health_gateway/plugins/status/server/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { HealthGatewayStatusPlugin } from './plugin';
|
||||
|
||||
export function plugin() {
|
||||
return new HealthGatewayStatusPlugin();
|
||||
}
|
62
test/health_gateway/plugins/status/server/plugin.ts
Normal file
62
test/health_gateway/plugins/status/server/plugin.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { CoreSetup, KibanaRequest, Plugin } from '@kbn/core/server';
|
||||
|
||||
export class HealthGatewayStatusPlugin implements Plugin<void, void> {
|
||||
public setup(core: CoreSetup) {
|
||||
const router = core.http.createRouter();
|
||||
|
||||
router.get({ path: '/api/status/ok', validate: {} }, async (context, req, res) => res.ok());
|
||||
|
||||
router.get({ path: '/api/status/redirect', validate: {} }, async (context, req, res) =>
|
||||
res.redirected({ headers: { location: '/api/status/ok' } })
|
||||
);
|
||||
|
||||
router.get({ path: '/api/status/unauthorized', validate: {} }, async (context, req, res) =>
|
||||
res.unauthorized({
|
||||
headers: { 'www-authenticate': 'Basic' },
|
||||
})
|
||||
);
|
||||
|
||||
router.get({ path: '/api/status/not-found', validate: {} }, async (context, req, res) =>
|
||||
res.notFound()
|
||||
);
|
||||
|
||||
router.get({ path: '/api/status/slow', validate: {} }, async (context, req, res) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
|
||||
return res.ok();
|
||||
});
|
||||
|
||||
const sessions = new Set<string>();
|
||||
router.get(
|
||||
{
|
||||
path: '/api/status/flaky',
|
||||
validate: {
|
||||
query: schema.object({ session: schema.string() }),
|
||||
},
|
||||
},
|
||||
async (context, req: KibanaRequest<void, { session: string }>, res) => {
|
||||
if (sessions.has(req.query.session)) {
|
||||
sessions.delete(req.query.session);
|
||||
|
||||
return res.custom({ statusCode: 500, body: 'Flaky' });
|
||||
}
|
||||
|
||||
sessions.add(req.query.session);
|
||||
|
||||
return res.ok();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public start() {}
|
||||
public stop() {}
|
||||
}
|
13
test/health_gateway/plugins/status/tsconfig.json
Normal file
13
test/health_gateway/plugins/status/tsconfig.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./target/types"
|
||||
},
|
||||
"include": [
|
||||
"server/**/*.ts",
|
||||
],
|
||||
"exclude": [],
|
||||
"kbn_references": [
|
||||
{ "path": "../../../../src/core/tsconfig.json" }
|
||||
]
|
||||
}
|
74
test/health_gateway/services/health_gateway.ts
Normal file
74
test/health_gateway/services/health_gateway.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { format } from 'url';
|
||||
import getPort from 'get-port';
|
||||
import supertest from 'supertest';
|
||||
import { ProcRunner } from '@kbn/dev-proc-runner';
|
||||
import { REPO_ROOT } from '@kbn/utils';
|
||||
import { FtrService } from '../../functional/ftr_provider_context';
|
||||
|
||||
interface HealthGatewayOptions {
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
|
||||
export class HealthGatewayService extends FtrService {
|
||||
private runner = new ProcRunner(this.ctx.getService('log'));
|
||||
private kibanaUrl = format(this.ctx.getService('config').get('servers.kibana'));
|
||||
private host = 'localhost';
|
||||
private port?: number;
|
||||
|
||||
private assertRunning() {
|
||||
if (!this.port) {
|
||||
throw new Error('Health gateway is not running');
|
||||
}
|
||||
}
|
||||
|
||||
async start(config: string, { env = {} }: HealthGatewayOptions = {}) {
|
||||
if (this.port) {
|
||||
throw new Error('Health gateway is already running');
|
||||
}
|
||||
|
||||
this.port = await getPort({ port: getPort.makeRange(1024, 65536) });
|
||||
|
||||
await this.runner.run(`health-gateway-${this.port}`, {
|
||||
cmd: 'yarn',
|
||||
args: [
|
||||
'kbn',
|
||||
'run-in-packages',
|
||||
'--filter=@kbn/health-gateway-server',
|
||||
'start',
|
||||
'--config',
|
||||
resolve(__dirname, '..', config),
|
||||
],
|
||||
cwd: REPO_ROOT,
|
||||
env: {
|
||||
...env,
|
||||
KIBANA_URL: this.kibanaUrl,
|
||||
HOST: this.host,
|
||||
PORT: `${this.port}`,
|
||||
CI: '', // Override in the CI environment to capture the logs.
|
||||
},
|
||||
wait: /Server is ready/,
|
||||
});
|
||||
}
|
||||
|
||||
async stop() {
|
||||
this.assertRunning();
|
||||
|
||||
await this.runner?.stop(`health-gateway-${this.port}`);
|
||||
this.port = undefined;
|
||||
}
|
||||
|
||||
poll() {
|
||||
this.assertRunning();
|
||||
|
||||
return supertest(`http://${this.host}:${this.port}`).get('/').send();
|
||||
}
|
||||
}
|
16
test/health_gateway/services/index.ts
Normal file
16
test/health_gateway/services/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { services as commonServices } from '../../common/services';
|
||||
|
||||
import { HealthGatewayService } from './health_gateway';
|
||||
|
||||
export const services = {
|
||||
...commonServices,
|
||||
healthGateway: HealthGatewayService,
|
||||
};
|
12
test/health_gateway/tests/ftr_provider_context.d.ts
vendored
Normal file
12
test/health_gateway/tests/ftr_provider_context.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { GenericFtrProviderContext } from '@kbn/test';
|
||||
import type { services } from '../services';
|
||||
|
||||
export type FtrProviderContext = GenericFtrProviderContext<typeof services, {}>;
|
63
test/health_gateway/tests/index.ts
Normal file
63
test/health_gateway/tests/index.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from './ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const healthGateway = getService('healthGateway');
|
||||
|
||||
describe('health gateway', () => {
|
||||
it('returns 200 on healthy hosts', async () => {
|
||||
await healthGateway.start('fixtures/healthy.yaml');
|
||||
|
||||
const { body } = await healthGateway.poll().expect(200);
|
||||
expect(body).to.have.property('status', 'healthy');
|
||||
});
|
||||
|
||||
it('returns 503 on unhealthy host', async () => {
|
||||
await healthGateway.start('fixtures/unhealthy.yaml');
|
||||
|
||||
const { body } = await healthGateway.poll().expect(503);
|
||||
expect(body).to.have.property('status', 'unhealthy');
|
||||
});
|
||||
|
||||
it('returns 503 on mixed responses', async () => {
|
||||
await healthGateway.start('fixtures/mixed.yaml');
|
||||
|
||||
const { body } = await healthGateway.poll().expect(503);
|
||||
expect(body).to.have.property('status', 'unhealthy');
|
||||
});
|
||||
|
||||
it('returns 504 on timeout', async () => {
|
||||
await healthGateway.start('fixtures/timeout.yaml');
|
||||
|
||||
const { body } = await healthGateway.poll().expect(504);
|
||||
expect(body).to.have.property('status', 'timeout');
|
||||
});
|
||||
|
||||
it('returns 502 on exception', async () => {
|
||||
await healthGateway.start('fixtures/invalid.yaml');
|
||||
|
||||
const { body } = await healthGateway.poll().expect(502);
|
||||
expect(body).to.have.property('status', 'failure');
|
||||
});
|
||||
|
||||
it('returns different status codes on state changes', async () => {
|
||||
await healthGateway.start('fixtures/flaky.yaml', { env: { SESSION: `${Math.random()}` } });
|
||||
|
||||
await healthGateway.poll().expect(200);
|
||||
await healthGateway.poll().expect(503);
|
||||
await healthGateway.poll().expect(200);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await healthGateway.stop();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -5,6 +5,7 @@ source src/dev/ci_setup/setup_env.sh
|
|||
echo " -> building kibana platform plugins"
|
||||
node scripts/build_kibana_platform_plugins \
|
||||
--scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
|
||||
--scan-dir "$KIBANA_DIR/test/health_gateway/plugins" \
|
||||
--scan-dir "$KIBANA_DIR/test/interpreter_functional/plugins" \
|
||||
--scan-dir "$KIBANA_DIR/test/common/fixtures/plugins" \
|
||||
--scan-dir "$KIBANA_DIR/examples" \
|
||||
|
|
|
@ -13,6 +13,7 @@ if [[ -z "$CODE_COVERAGE" ]]; then
|
|||
if [[ ! "$TASK_QUEUE_PROCESS_ID" && "$CI_GROUP" == "1" ]]; then
|
||||
source test/scripts/jenkins_build_kbn_sample_panel_action.sh
|
||||
./test/scripts/test/plugin_functional.sh
|
||||
./test/scripts/test/health_gateway.sh
|
||||
./test/scripts/test/interpreter_functional.sh
|
||||
fi
|
||||
else
|
||||
|
|
|
@ -11,4 +11,5 @@ cd -;
|
|||
pwd
|
||||
|
||||
./test/scripts/test/plugin_functional.sh
|
||||
./test/scripts/test/health_gateway.sh
|
||||
./test/scripts/test/interpreter_functional.sh
|
||||
|
|
9
test/scripts/test/health_gateway.sh
Executable file
9
test/scripts/test/health_gateway.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source test/scripts/jenkins_test_setup_oss.sh
|
||||
|
||||
node scripts/functional_tests \
|
||||
--config test/health_gateway/config.ts \
|
||||
--bail \
|
||||
--debug \
|
||||
--kibana-install-dir $KIBANA_INSTALL_DIR
|
|
@ -962,6 +962,8 @@
|
|||
"@kbn/newsfeed-fixtures-plugin/*": ["test/common/fixtures/plugins/newsfeed/*"],
|
||||
"@kbn/open-telemetry-instrumented-plugin": ["test/common/fixtures/plugins/otel_metrics"],
|
||||
"@kbn/open-telemetry-instrumented-plugin/*": ["test/common/fixtures/plugins/otel_metrics/*"],
|
||||
"@kbn/kbn-health-gateway-status-plugin": ["test/health_gateway/plugins/status"],
|
||||
"@kbn/kbn-health-gateway-status-plugin/*": ["test/health_gateway/plugins/status/*"],
|
||||
"@kbn/kbn-tp-run-pipeline-plugin": ["test/interpreter_functional/plugins/kbn_tp_run_pipeline"],
|
||||
"@kbn/kbn-tp-run-pipeline-plugin/*": ["test/interpreter_functional/plugins/kbn_tp_run_pipeline/*"],
|
||||
"@kbn/app-link-test-plugin": ["test/plugin_functional/plugins/app_link_test"],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue