mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
Change the health gateway to use the status API (#160125)
## Summary Follow-up of https://github.com/elastic/kibana/pull/159768 Related to https://github.com/elastic/kibana/issues/158910 Change the health-gateway behavior to hit the `/api/status` endpoint instead of just the root `/` path. This was made possible by https://github.com/elastic/kibana/pull/159768, as we now always return the correct http code from the status endpoint even for unauthenticated requests.
This commit is contained in:
parent
968c09a695
commit
c10ab82521
15 changed files with 195 additions and 95 deletions
|
@ -6,4 +6,4 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { RootRoute } from './root';
|
export { StatusHandler } from './status';
|
|
@ -8,12 +8,13 @@
|
||||||
|
|
||||||
import { Server } from '@hapi/hapi';
|
import { Server } from '@hapi/hapi';
|
||||||
import { duration } from 'moment';
|
import { duration } from 'moment';
|
||||||
|
import { URL } from 'url';
|
||||||
import fetch, { Response } from 'node-fetch';
|
import fetch, { Response } from 'node-fetch';
|
||||||
import { loggerMock, MockedLogger } from '@kbn/logging-mocks';
|
import { loggerMock, MockedLogger } from '@kbn/logging-mocks';
|
||||||
import type { KibanaConfig } from '../kibana_config';
|
import type { KibanaConfig } from '../kibana_config';
|
||||||
import { RootRoute } from './root';
|
import { StatusHandler } from './status';
|
||||||
|
|
||||||
describe('RootRoute', () => {
|
describe('StatusHandler', () => {
|
||||||
let kibanaConfig: KibanaConfig;
|
let kibanaConfig: KibanaConfig;
|
||||||
let logger: MockedLogger;
|
let logger: MockedLogger;
|
||||||
let server: Server;
|
let server: Server;
|
||||||
|
@ -30,7 +31,11 @@ describe('RootRoute', () => {
|
||||||
logger = loggerMock.create();
|
logger = loggerMock.create();
|
||||||
|
|
||||||
server = new Server();
|
server = new Server();
|
||||||
server.route(new RootRoute(kibanaConfig, logger));
|
server.route({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/',
|
||||||
|
handler: new StatusHandler(kibanaConfig, logger).handler,
|
||||||
|
});
|
||||||
await server.initialize();
|
await server.initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -248,5 +253,49 @@ describe('RootRoute', () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call the host with the correct path', async () => {
|
||||||
|
kibanaConfig.hosts.splice(0, kibanaConfig.hosts.length);
|
||||||
|
kibanaConfig.hosts.push('http://localhost:5601', 'http://localhost:5602');
|
||||||
|
|
||||||
|
(fetch as jest.MockedFunction<typeof fetch>).mockResolvedValue(new Response('', ok));
|
||||||
|
|
||||||
|
await server.inject({
|
||||||
|
method: 'get',
|
||||||
|
url: '/',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(fetch).toHaveBeenCalledWith(
|
||||||
|
new URL('http://localhost:5601/api/status'),
|
||||||
|
expect.any(Object)
|
||||||
|
);
|
||||||
|
expect(fetch).toHaveBeenCalledWith(
|
||||||
|
new URL('http://localhost:5602/api/status'),
|
||||||
|
expect.any(Object)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should append the status path when path already present on the host', async () => {
|
||||||
|
kibanaConfig.hosts.splice(0, kibanaConfig.hosts.length);
|
||||||
|
kibanaConfig.hosts.push('http://localhost:5601/prefix', 'http://localhost:5602/other/path');
|
||||||
|
|
||||||
|
(fetch as jest.MockedFunction<typeof fetch>).mockResolvedValue(new Response('', ok));
|
||||||
|
|
||||||
|
await server.inject({
|
||||||
|
method: 'get',
|
||||||
|
url: '/',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(fetch).toHaveBeenCalledWith(
|
||||||
|
new URL('http://localhost:5601/prefix/api/status'),
|
||||||
|
expect.any(Object)
|
||||||
|
);
|
||||||
|
expect(fetch).toHaveBeenCalledWith(
|
||||||
|
new URL('http://localhost:5602/other/path/api/status'),
|
||||||
|
expect.any(Object)
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -6,17 +6,19 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { capitalize, chain, memoize, pick } from 'lodash';
|
import { capitalize, chain, memoize } from 'lodash';
|
||||||
import { Agent, AgentOptions } from 'https';
|
import { Agent, AgentOptions } from 'https';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import type { Request, ResponseObject, ResponseToolkit, ServerRoute } from '@hapi/hapi';
|
import type { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi';
|
||||||
import nodeFetch, { Response } from 'node-fetch';
|
import nodeFetch, { Response } from 'node-fetch';
|
||||||
import type { Logger } from '@kbn/logging';
|
import type { Logger } from '@kbn/logging';
|
||||||
import type { KibanaConfig } from '../kibana_config';
|
import type { KibanaConfig } from '../kibana_config';
|
||||||
|
|
||||||
type Status = 'healthy' | 'unhealthy' | 'failure' | 'timeout';
|
type Status = 'healthy' | 'unhealthy' | 'failure' | 'timeout';
|
||||||
|
|
||||||
interface RootRouteResponse {
|
const statusApiPath = '/api/status';
|
||||||
|
|
||||||
|
interface StatusRouteResponse {
|
||||||
status: Status;
|
status: Status;
|
||||||
hosts?: HostStatus[];
|
hosts?: HostStatus[];
|
||||||
}
|
}
|
||||||
|
@ -27,9 +29,9 @@ interface HostStatus {
|
||||||
code?: number;
|
code?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RootRoute implements ServerRoute {
|
export class StatusHandler {
|
||||||
private static isHealthy(response: Response) {
|
private static isHealthy(response: Response) {
|
||||||
return RootRoute.isSuccess(response) || RootRoute.isUnauthorized(response);
|
return StatusHandler.isSuccess(response) || StatusHandler.isUnauthorized(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static isUnauthorized({ status, headers }: Response): boolean {
|
private static isUnauthorized({ status, headers }: Response): boolean {
|
||||||
|
@ -47,25 +49,20 @@ export class RootRoute implements ServerRoute {
|
||||||
timeout: 504,
|
timeout: 504,
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly method = 'GET';
|
|
||||||
readonly path = '/';
|
|
||||||
|
|
||||||
constructor(private kibanaConfig: KibanaConfig, private logger: Logger) {
|
constructor(private kibanaConfig: KibanaConfig, private logger: Logger) {
|
||||||
this.handler = this.handler.bind(this);
|
this.handler = this.handler.bind(this);
|
||||||
|
|
||||||
return pick(this, ['method', 'path', 'handler']) as RootRoute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async handler(request: Request, toolkit: ResponseToolkit): Promise<ResponseObject> {
|
async handler(request: Request, toolkit: ResponseToolkit): Promise<ResponseObject> {
|
||||||
const body = await this.poll();
|
const body = await this.poll();
|
||||||
const code = RootRoute.STATUS_CODE[body.status];
|
const code = StatusHandler.STATUS_CODE[body.status];
|
||||||
|
|
||||||
this.logger.debug(`Returning ${code} response with body: ${JSON.stringify(body)}`);
|
this.logger.debug(`Returning ${code} response with body: ${JSON.stringify(body)}`);
|
||||||
|
|
||||||
return toolkit.response(body).type('application/json').code(code);
|
return toolkit.response(body).type('application/json').code(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async poll(): Promise<RootRouteResponse> {
|
private async poll(): Promise<StatusRouteResponse> {
|
||||||
const hosts = await Promise.all(this.kibanaConfig.hosts.map(this.pollHost.bind(this)));
|
const hosts = await Promise.all(this.kibanaConfig.hosts.map(this.pollHost.bind(this)));
|
||||||
const statuses = chain(hosts).map('status').uniq().value();
|
const statuses = chain(hosts).map('status').uniq().value();
|
||||||
const status = statuses.length <= 1 ? statuses[0] ?? 'healthy' : 'unhealthy';
|
const status = statuses.length <= 1 ? statuses[0] ?? 'healthy' : 'unhealthy';
|
||||||
|
@ -81,7 +78,7 @@ export class RootRoute implements ServerRoute {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.fetch(host);
|
const response = await this.fetch(host);
|
||||||
const status = RootRoute.isHealthy(response) ? 'healthy' : 'unhealthy';
|
const status = StatusHandler.isHealthy(response) ? 'healthy' : 'unhealthy';
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`${capitalize(status)} response from '${host}' with code ${response.status}`
|
`${capitalize(status)} response from '${host}' with code ${response.status}`
|
||||||
);
|
);
|
||||||
|
@ -112,8 +109,9 @@ export class RootRoute implements ServerRoute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetch(url: string) {
|
private async fetch(host: string) {
|
||||||
const { protocol } = new URL(url);
|
const url = new URL(host);
|
||||||
|
appendStatusApiPath(url);
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(
|
const timeoutId = setTimeout(
|
||||||
|
@ -123,7 +121,7 @@ export class RootRoute implements ServerRoute {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await nodeFetch(url, {
|
return await nodeFetch(url, {
|
||||||
agent: protocol === 'https:' ? this.getAgent() : undefined,
|
agent: url.protocol === 'https:' ? this.getAgent() : undefined,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
redirect: 'manual',
|
redirect: 'manual',
|
||||||
});
|
});
|
||||||
|
@ -161,3 +159,7 @@ export class RootRoute implements ServerRoute {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const appendStatusApiPath = (url: URL) => {
|
||||||
|
url.pathname = `${url.pathname}/${statusApiPath}`.replace(/\/{2,}/g, '/');
|
||||||
|
};
|
|
@ -50,6 +50,19 @@ describe('KibanaService', () => {
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/',
|
path: '/',
|
||||||
|
handler: expect.any(Function),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('registers /api/status route with the server', async () => {
|
||||||
|
const kibanaService = new KibanaService({ config, logger });
|
||||||
|
await kibanaService.start({ server });
|
||||||
|
expect(server.addRoute).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/api/status',
|
||||||
|
handler: expect.any(Function),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type { IConfigService } from '@kbn/config';
|
||||||
import type { Logger, LoggerFactory } from '@kbn/logging';
|
import type { Logger, LoggerFactory } from '@kbn/logging';
|
||||||
import { ServerStart } from '../server';
|
import { ServerStart } from '../server';
|
||||||
import { KibanaConfig } from './kibana_config';
|
import { KibanaConfig } from './kibana_config';
|
||||||
import { RootRoute } from './routes';
|
import { StatusHandler } from './handlers';
|
||||||
|
|
||||||
interface KibanaServiceStartDependencies {
|
interface KibanaServiceStartDependencies {
|
||||||
server: ServerStart;
|
server: ServerStart;
|
||||||
|
@ -34,7 +34,17 @@ export class KibanaService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async start({ server }: KibanaServiceStartDependencies) {
|
async start({ server }: KibanaServiceStartDependencies) {
|
||||||
server.addRoute(new RootRoute(this.kibanaConfig, this.logger));
|
const statusHandler = new StatusHandler(this.kibanaConfig, this.logger);
|
||||||
|
server.addRoute({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/',
|
||||||
|
handler: statusHandler.handler,
|
||||||
|
});
|
||||||
|
server.addRoute({
|
||||||
|
method: 'GET',
|
||||||
|
path: '/api/status',
|
||||||
|
handler: statusHandler.handler,
|
||||||
|
});
|
||||||
this.logger.info('Server is ready');
|
this.logger.info('Server is ready');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ server:
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
hosts:
|
hosts:
|
||||||
- ${KIBANA_URL}/api/status/ok
|
- ${KIBANA_URL}/health/ok
|
||||||
- ${KIBANA_URL}/api/status/flaky?session=${SESSION}
|
- ${KIBANA_URL}/health/flaky?session=${SESSION}
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
root:
|
root:
|
||||||
|
|
|
@ -4,9 +4,9 @@ server:
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
hosts:
|
hosts:
|
||||||
- ${KIBANA_URL}/api/status/ok
|
- ${KIBANA_URL}/health/ok
|
||||||
- ${KIBANA_URL}/api/status/redirect
|
- ${KIBANA_URL}/health/redirect
|
||||||
- ${KIBANA_URL}/api/status/unauthorized
|
- ${KIBANA_URL}/health/unauthorized
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
root:
|
root:
|
||||||
|
|
|
@ -4,7 +4,7 @@ server:
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
hosts:
|
hosts:
|
||||||
- http://localhost:65537/api/status/ok
|
- http://localhost:65537/health/ok
|
||||||
requestTimeout: 2s
|
requestTimeout: 2s
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
|
|
|
@ -4,8 +4,8 @@ server:
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
hosts:
|
hosts:
|
||||||
- ${KIBANA_URL}/api/status/ok
|
- ${KIBANA_URL}/health/ok
|
||||||
- ${KIBANA_URL}/api/status/not-found
|
- ${KIBANA_URL}/health/not-found
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
root:
|
root:
|
||||||
|
|
|
@ -4,7 +4,7 @@ server:
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
hosts:
|
hosts:
|
||||||
- ${KIBANA_URL}/api/status/slow
|
- ${KIBANA_URL}/health/slow
|
||||||
requestTimeout: 2s
|
requestTimeout: 2s
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
|
|
|
@ -4,7 +4,7 @@ server:
|
||||||
|
|
||||||
kibana:
|
kibana:
|
||||||
hosts:
|
hosts:
|
||||||
- ${KIBANA_URL}/api/status/not-found
|
- ${KIBANA_URL}/health/not-found
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
root:
|
root:
|
||||||
|
|
|
@ -13,23 +13,27 @@ export class HealthGatewayStatusPlugin implements Plugin<void, void> {
|
||||||
public setup(core: CoreSetup) {
|
public setup(core: CoreSetup) {
|
||||||
const router = core.http.createRouter();
|
const router = core.http.createRouter();
|
||||||
|
|
||||||
router.get({ path: '/api/status/ok', validate: {} }, async (context, req, res) => res.ok());
|
router.get({ path: '/health/ok/api/status', 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) =>
|
router.get({ path: '/health/redirect/api/status', validate: {} }, async (context, req, res) =>
|
||||||
|
res.redirected({ headers: { location: '/health/ok/api/status' } })
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
{ path: '/health/unauthorized/api/status', validate: {} },
|
||||||
|
async (context, req, res) =>
|
||||||
res.unauthorized({
|
res.unauthorized({
|
||||||
headers: { 'www-authenticate': 'Basic' },
|
headers: { 'www-authenticate': 'Basic' },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get({ path: '/api/status/not-found', validate: {} }, async (context, req, res) =>
|
router.get({ path: '/health/not-found/api/status', validate: {} }, async (context, req, res) =>
|
||||||
res.notFound()
|
res.notFound()
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get({ path: '/api/status/slow', validate: {} }, async (context, req, res) => {
|
router.get({ path: '/health/slow/api/status', validate: {} }, async (context, req, res) => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
return res.ok();
|
return res.ok();
|
||||||
|
@ -38,7 +42,7 @@ export class HealthGatewayStatusPlugin implements Plugin<void, void> {
|
||||||
const sessions = new Set<string>();
|
const sessions = new Set<string>();
|
||||||
router.get(
|
router.get(
|
||||||
{
|
{
|
||||||
path: '/api/status/flaky',
|
path: '/health/flaky/api/status',
|
||||||
validate: {
|
validate: {
|
||||||
query: schema.object({ session: schema.string() }),
|
query: schema.object({ session: schema.string() }),
|
||||||
},
|
},
|
||||||
|
@ -58,5 +62,6 @@ export class HealthGatewayStatusPlugin implements Plugin<void, void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public start() {}
|
public start() {}
|
||||||
|
|
||||||
public stop() {}
|
public stop() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,9 +66,9 @@ export class HealthGatewayService extends FtrService {
|
||||||
this.port = undefined;
|
this.port = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
poll() {
|
poll(path: string) {
|
||||||
this.assertRunning();
|
this.assertRunning();
|
||||||
|
|
||||||
return supertest(`http://${this.host}:${this.port}`).get('/').send();
|
return supertest(`http://${this.host}:${this.port}`).get(path).send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,58 +6,10 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import expect from '@kbn/expect';
|
|
||||||
import { FtrProviderContext } from './ftr_provider_context';
|
import { FtrProviderContext } from './ftr_provider_context';
|
||||||
|
|
||||||
export default function ({ getService }: FtrProviderContext) {
|
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||||
const healthGateway = getService('healthGateway');
|
|
||||||
|
|
||||||
describe('health gateway', () => {
|
describe('health gateway', () => {
|
||||||
it('returns 200 on healthy hosts', async () => {
|
loadTestFile(require.resolve('./status'));
|
||||||
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();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
69
test/health_gateway/tests/status.ts
Normal file
69
test/health_gateway/tests/status.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* 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('status API', () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
await healthGateway.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
['/', '/api/status'].forEach((path) => {
|
||||||
|
describe(`${path}`, () => {
|
||||||
|
it('returns 200 on healthy hosts', async () => {
|
||||||
|
await healthGateway.start('fixtures/healthy.yaml');
|
||||||
|
|
||||||
|
const { body } = await healthGateway.poll(path).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(path).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(path).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(path).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(path).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(path).expect(200);
|
||||||
|
await healthGateway.poll(path).expect(503);
|
||||||
|
await healthGateway.poll(path).expect(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue