mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Revamp core environment class to support upcoming core<-->legacy bootstrap inversion. (#21885)
This commit is contained in:
parent
35abc069e8
commit
60ae2f55ba
16 changed files with 478 additions and 472 deletions
|
@ -21,33 +21,14 @@
|
|||
|
||||
import { EnvOptions } from '../../env';
|
||||
|
||||
interface MockEnvOptions {
|
||||
config?: string;
|
||||
kbnServer?: any;
|
||||
mode?: EnvOptions['mode']['name'];
|
||||
packageInfo?: Partial<EnvOptions['packageInfo']>;
|
||||
}
|
||||
|
||||
export function getEnvOptions({
|
||||
config,
|
||||
kbnServer,
|
||||
mode = 'development',
|
||||
packageInfo = {},
|
||||
}: MockEnvOptions = {}): EnvOptions {
|
||||
export function getEnvOptions(options: Partial<EnvOptions> = {}): EnvOptions {
|
||||
return {
|
||||
config,
|
||||
kbnServer,
|
||||
mode: {
|
||||
dev: mode === 'development',
|
||||
name: mode,
|
||||
prod: mode === 'production',
|
||||
},
|
||||
packageInfo: {
|
||||
branch: 'some-branch',
|
||||
buildNum: 1,
|
||||
buildSha: 'some-sha-256',
|
||||
version: 'some-version',
|
||||
...packageInfo,
|
||||
configs: options.configs || [],
|
||||
cliArgs: {
|
||||
dev: true,
|
||||
...(options.cliArgs || {}),
|
||||
},
|
||||
isDevClusterMaster:
|
||||
options.isDevClusterMaster !== undefined ? options.isDevClusterMaster : false,
|
||||
};
|
||||
}
|
||||
|
|
149
src/core/server/config/__tests__/__snapshots__/env.test.ts.snap
Normal file
149
src/core/server/config/__tests__/__snapshots__/env.test.ts.snap
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`correctly creates default environment in dev mode.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/test/cwd/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": true,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/test/cwd/config",
|
||||
"configs": Array [
|
||||
"/test/cwd/config/kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/test/cwd/core_plugins",
|
||||
"homeDir": "/test/cwd",
|
||||
"isDevClusterMaster": true,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/test/cwd/log",
|
||||
"mode": Object {
|
||||
"dev": true,
|
||||
"name": "development",
|
||||
"prod": false,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "some-branch",
|
||||
"buildNum": 9007199254740991,
|
||||
"buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"version": "some-version",
|
||||
},
|
||||
"staticFilesDir": "/test/cwd/ui",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`correctly creates default environment in prod distributable mode.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/test/cwd/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": false,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/test/cwd/config",
|
||||
"configs": Array [
|
||||
"/some/other/path/some-kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/test/cwd/core_plugins",
|
||||
"homeDir": "/test/cwd",
|
||||
"isDevClusterMaster": false,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/test/cwd/log",
|
||||
"mode": Object {
|
||||
"dev": false,
|
||||
"name": "production",
|
||||
"prod": true,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "feature-v1",
|
||||
"buildNum": 100,
|
||||
"buildSha": "feature-v1-build-sha",
|
||||
"version": "v1",
|
||||
},
|
||||
"staticFilesDir": "/test/cwd/ui",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`correctly creates default environment in prod non-distributable mode.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/test/cwd/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": false,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/test/cwd/config",
|
||||
"configs": Array [
|
||||
"/some/other/path/some-kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/test/cwd/core_plugins",
|
||||
"homeDir": "/test/cwd",
|
||||
"isDevClusterMaster": false,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/test/cwd/log",
|
||||
"mode": Object {
|
||||
"dev": false,
|
||||
"name": "production",
|
||||
"prod": true,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "feature-v1",
|
||||
"buildNum": 9007199254740991,
|
||||
"buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"version": "v1",
|
||||
},
|
||||
"staticFilesDir": "/test/cwd/ui",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`correctly creates environment with constructor.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/some/home/dir/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": false,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/some/home/dir/config",
|
||||
"configs": Array [
|
||||
"/some/other/path/some-kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/some/home/dir/core_plugins",
|
||||
"homeDir": "/some/home/dir",
|
||||
"isDevClusterMaster": false,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/some/home/dir/log",
|
||||
"mode": Object {
|
||||
"dev": false,
|
||||
"name": "production",
|
||||
"prod": true,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "feature-v1",
|
||||
"buildNum": 100,
|
||||
"buildSha": "feature-v1-build-sha",
|
||||
"version": "v1",
|
||||
},
|
||||
"staticFilesDir": "/some/home/dir/ui",
|
||||
}
|
||||
`;
|
|
@ -18,8 +18,13 @@
|
|||
*/
|
||||
|
||||
/* tslint:disable max-classes-per-file */
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../../utils/package_json', () => ({ pkg: mockPackage }));
|
||||
|
||||
import { schema, Type, TypeOf } from '../schema';
|
||||
|
||||
import { ConfigService, ObjectToRawConfigAdapter } from '..';
|
||||
|
@ -161,21 +166,19 @@ test('tracks unhandled paths', async () => {
|
|||
});
|
||||
|
||||
test('correctly passes context', async () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: true,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const env = new Env('/kibana', getEnvOptions());
|
||||
|
||||
const config$ = new BehaviorSubject(new ObjectToRawConfigAdapter({ foo: {} }));
|
||||
|
||||
const env = new Env(
|
||||
'/kibana',
|
||||
getEnvOptions({
|
||||
mode: 'development',
|
||||
packageInfo: {
|
||||
branch: 'feature-v1',
|
||||
buildNum: 100,
|
||||
buildSha: 'feature-v1-build-sha',
|
||||
version: 'v1',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const configService = new ConfigService(config$, env, logger);
|
||||
const configs = configService.atPath(
|
||||
'foo',
|
||||
|
|
|
@ -29,76 +29,82 @@ jest.mock('path', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../../utils/package_json', () => ({ pkg: mockPackage }));
|
||||
|
||||
import { Env } from '../env';
|
||||
import { getEnvOptions } from './__mocks__/env';
|
||||
|
||||
test('correctly creates default environment with empty options.', () => {
|
||||
const envOptions = getEnvOptions();
|
||||
const defaultEnv = Env.createDefault(envOptions);
|
||||
test('correctly creates default environment in dev mode.', () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'some-branch',
|
||||
version: 'some-version',
|
||||
};
|
||||
|
||||
expect(defaultEnv.homeDir).toEqual('/test/cwd');
|
||||
expect(defaultEnv.configDir).toEqual('/test/cwd/config');
|
||||
expect(defaultEnv.corePluginsDir).toEqual('/test/cwd/core_plugins');
|
||||
expect(defaultEnv.binDir).toEqual('/test/cwd/bin');
|
||||
expect(defaultEnv.logDir).toEqual('/test/cwd/log');
|
||||
expect(defaultEnv.staticFilesDir).toEqual('/test/cwd/ui');
|
||||
const defaultEnv = Env.createDefault({
|
||||
cliArgs: { dev: true, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/test/cwd/config/kibana.yml'],
|
||||
isDevClusterMaster: true,
|
||||
});
|
||||
|
||||
expect(defaultEnv.getConfigFile()).toEqual('/test/cwd/config/kibana.yml');
|
||||
expect(defaultEnv.getLegacyKbnServer()).toBeUndefined();
|
||||
expect(defaultEnv.getMode()).toEqual(envOptions.mode);
|
||||
expect(defaultEnv.getPackageInfo()).toEqual(envOptions.packageInfo);
|
||||
expect(defaultEnv).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
||||
test('correctly creates default environment with options overrides.', () => {
|
||||
const mockEnvOptions = getEnvOptions({
|
||||
config: '/some/other/path/some-kibana.yml',
|
||||
kbnServer: {},
|
||||
mode: 'production',
|
||||
packageInfo: {
|
||||
branch: 'feature-v1',
|
||||
buildNum: 100,
|
||||
buildSha: 'feature-v1-build-sha',
|
||||
version: 'v1',
|
||||
test('correctly creates default environment in prod distributable mode.', () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: true,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultEnv = Env.createDefault({
|
||||
cliArgs: { dev: false, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/some/other/path/some-kibana.yml'],
|
||||
isDevClusterMaster: false,
|
||||
});
|
||||
const defaultEnv = Env.createDefault(mockEnvOptions);
|
||||
|
||||
expect(defaultEnv.homeDir).toEqual('/test/cwd');
|
||||
expect(defaultEnv.configDir).toEqual('/test/cwd/config');
|
||||
expect(defaultEnv.corePluginsDir).toEqual('/test/cwd/core_plugins');
|
||||
expect(defaultEnv.binDir).toEqual('/test/cwd/bin');
|
||||
expect(defaultEnv.logDir).toEqual('/test/cwd/log');
|
||||
expect(defaultEnv.staticFilesDir).toEqual('/test/cwd/ui');
|
||||
expect(defaultEnv).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
||||
expect(defaultEnv.getConfigFile()).toEqual(mockEnvOptions.config);
|
||||
expect(defaultEnv.getLegacyKbnServer()).toBe(mockEnvOptions.kbnServer);
|
||||
expect(defaultEnv.getMode()).toEqual(mockEnvOptions.mode);
|
||||
expect(defaultEnv.getPackageInfo()).toEqual(mockEnvOptions.packageInfo);
|
||||
test('correctly creates default environment in prod non-distributable mode.', () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: false,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultEnv = Env.createDefault({
|
||||
cliArgs: { dev: false, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/some/other/path/some-kibana.yml'],
|
||||
isDevClusterMaster: false,
|
||||
});
|
||||
|
||||
expect(defaultEnv).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
||||
test('correctly creates environment with constructor.', () => {
|
||||
const mockEnvOptions = getEnvOptions({
|
||||
config: '/some/other/path/some-kibana.yml',
|
||||
mode: 'production',
|
||||
packageInfo: {
|
||||
branch: 'feature-v1',
|
||||
buildNum: 100,
|
||||
buildSha: 'feature-v1-build-sha',
|
||||
version: 'v1',
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: true,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const env = new Env('/some/home/dir', {
|
||||
cliArgs: { dev: false, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/some/other/path/some-kibana.yml'],
|
||||
isDevClusterMaster: false,
|
||||
});
|
||||
|
||||
const defaultEnv = new Env('/some/home/dir', mockEnvOptions);
|
||||
|
||||
expect(defaultEnv.homeDir).toEqual('/some/home/dir');
|
||||
expect(defaultEnv.configDir).toEqual('/some/home/dir/config');
|
||||
expect(defaultEnv.corePluginsDir).toEqual('/some/home/dir/core_plugins');
|
||||
expect(defaultEnv.binDir).toEqual('/some/home/dir/bin');
|
||||
expect(defaultEnv.logDir).toEqual('/some/home/dir/log');
|
||||
expect(defaultEnv.staticFilesDir).toEqual('/some/home/dir/ui');
|
||||
|
||||
expect(defaultEnv.getConfigFile()).toEqual(mockEnvOptions.config);
|
||||
expect(defaultEnv.getLegacyKbnServer()).toBeUndefined();
|
||||
expect(defaultEnv.getMode()).toEqual(mockEnvOptions.mode);
|
||||
expect(defaultEnv.getPackageInfo()).toEqual(mockEnvOptions.packageInfo);
|
||||
expect(env).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
|
|
@ -138,13 +138,12 @@ export class ConfigService {
|
|||
);
|
||||
}
|
||||
|
||||
const environmentMode = this.env.getMode();
|
||||
const config = ConfigClass.schema.validate(
|
||||
rawConfig,
|
||||
{
|
||||
dev: environmentMode.dev,
|
||||
prod: environmentMode.prod,
|
||||
...this.env.getPackageInfo(),
|
||||
dev: this.env.mode.dev,
|
||||
prod: this.env.mode.prod,
|
||||
...this.env.packageInfo,
|
||||
},
|
||||
namespace
|
||||
);
|
||||
|
|
|
@ -17,10 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { resolve } from 'path';
|
||||
import process from 'process';
|
||||
|
||||
import { LegacyKbnServer } from '../legacy_compat';
|
||||
import { pkg } from '../../../utils/package_json';
|
||||
|
||||
interface PackageInfo {
|
||||
version: string;
|
||||
|
@ -36,11 +37,9 @@ interface EnvironmentMode {
|
|||
}
|
||||
|
||||
export interface EnvOptions {
|
||||
config?: string;
|
||||
kbnServer?: any;
|
||||
packageInfo: PackageInfo;
|
||||
mode: EnvironmentMode;
|
||||
[key: string]: any;
|
||||
configs: string[];
|
||||
cliArgs: Record<string, any>;
|
||||
isDevClusterMaster: boolean;
|
||||
}
|
||||
|
||||
export class Env {
|
||||
|
@ -57,44 +56,64 @@ export class Env {
|
|||
public readonly logDir: string;
|
||||
public readonly staticFilesDir: string;
|
||||
|
||||
/**
|
||||
* Information about Kibana package (version, build number etc.).
|
||||
*/
|
||||
public readonly packageInfo: Readonly<PackageInfo>;
|
||||
|
||||
/**
|
||||
* Mode Kibana currently run in (development or production).
|
||||
*/
|
||||
public readonly mode: Readonly<EnvironmentMode>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(readonly homeDir: string, private readonly options: EnvOptions) {
|
||||
public readonly legacy: EventEmitter;
|
||||
|
||||
/**
|
||||
* Arguments provided through command line.
|
||||
*/
|
||||
public readonly cliArgs: Readonly<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Paths to the configuration files.
|
||||
*/
|
||||
public readonly configs: ReadonlyArray<string>;
|
||||
|
||||
/**
|
||||
* Indicates that this Kibana instance is run as development Node Cluster master.
|
||||
*/
|
||||
public readonly isDevClusterMaster: boolean;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(readonly homeDir: string, options: EnvOptions) {
|
||||
this.configDir = resolve(this.homeDir, 'config');
|
||||
this.corePluginsDir = resolve(this.homeDir, 'core_plugins');
|
||||
this.binDir = resolve(this.homeDir, 'bin');
|
||||
this.logDir = resolve(this.homeDir, 'log');
|
||||
this.staticFilesDir = resolve(this.homeDir, 'ui');
|
||||
}
|
||||
|
||||
public getConfigFile() {
|
||||
const defaultConfigFile = this.getDefaultConfigFile();
|
||||
return this.options.config === undefined ? defaultConfigFile : this.options.config;
|
||||
}
|
||||
this.cliArgs = Object.freeze(options.cliArgs);
|
||||
this.configs = Object.freeze(options.configs);
|
||||
this.isDevClusterMaster = options.isDevClusterMaster;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public getLegacyKbnServer(): LegacyKbnServer | undefined {
|
||||
return this.options.kbnServer;
|
||||
}
|
||||
this.mode = Object.freeze<EnvironmentMode>({
|
||||
dev: this.cliArgs.dev,
|
||||
name: this.cliArgs.dev ? 'development' : 'production',
|
||||
prod: !this.cliArgs.dev,
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets information about Kibana package (version, build number etc.).
|
||||
*/
|
||||
public getPackageInfo() {
|
||||
return this.options.packageInfo;
|
||||
}
|
||||
const isKibanaDistributable = pkg.build && pkg.build.distributable === true;
|
||||
this.packageInfo = Object.freeze({
|
||||
branch: pkg.branch,
|
||||
buildNum: isKibanaDistributable ? pkg.build.number : Number.MAX_SAFE_INTEGER,
|
||||
buildSha: isKibanaDistributable ? pkg.build.sha : 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
version: pkg.version,
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets mode Kibana currently run in (development or production).
|
||||
*/
|
||||
public getMode() {
|
||||
return this.options.mode;
|
||||
}
|
||||
|
||||
private getDefaultConfigFile() {
|
||||
return resolve(this.configDir, 'kibana.yml');
|
||||
this.legacy = new EventEmitter();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`broadcasts server and connection options to the legacy "channel" 1`] = `
|
||||
Object {
|
||||
"host": "127.0.0.1",
|
||||
"port": 12345,
|
||||
"routes": Object {
|
||||
"cors": undefined,
|
||||
"payload": Object {
|
||||
"maxBytes": 1024,
|
||||
},
|
||||
"validate": Object {
|
||||
"options": Object {
|
||||
"abortEarly": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"state": Object {
|
||||
"strictHeader": false,
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -24,7 +24,6 @@ jest.mock('fs', () => ({
|
|||
}));
|
||||
|
||||
import Chance from 'chance';
|
||||
import http from 'http';
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { Env } from '../../config';
|
||||
|
@ -36,6 +35,7 @@ import { Router } from '../router';
|
|||
|
||||
const chance = new Chance();
|
||||
|
||||
let env: Env;
|
||||
let server: HttpServer;
|
||||
let config: HttpConfig;
|
||||
|
||||
|
@ -51,7 +51,8 @@ beforeEach(() => {
|
|||
ssl: {},
|
||||
} as HttpConfig;
|
||||
|
||||
server = new HttpServer(logger.get(), new Env('/kibana', getEnvOptions()));
|
||||
env = new Env('/kibana', getEnvOptions());
|
||||
server = new HttpServer(logger.get(), env);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -563,99 +564,21 @@ describe('with defined `redirectHttpFromPort`', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when run within legacy platform', () => {
|
||||
let newPlatformProxyListenerMock: any;
|
||||
beforeEach(() => {
|
||||
newPlatformProxyListenerMock = {
|
||||
bind: jest.fn(),
|
||||
proxy: jest.fn(),
|
||||
};
|
||||
test('broadcasts server and connection options to the legacy "channel"', async () => {
|
||||
const onConnectionListener = jest.fn();
|
||||
env.legacy.on('connection', onConnectionListener);
|
||||
|
||||
const kbnServerMock = {
|
||||
newPlatformProxyListener: newPlatformProxyListenerMock,
|
||||
};
|
||||
expect(onConnectionListener).not.toHaveBeenCalled();
|
||||
|
||||
server = new HttpServer(
|
||||
logger.get(),
|
||||
new Env('/kibana', getEnvOptions({ kbnServer: kbnServerMock }))
|
||||
);
|
||||
|
||||
const router = new Router('/new');
|
||||
router.get({ path: '/', validate: false }, async (req, res) => {
|
||||
return res.ok({ key: 'new-platform' });
|
||||
});
|
||||
|
||||
server.registerRouter(router);
|
||||
|
||||
newPlatformProxyListenerMock.proxy.mockImplementation(
|
||||
(req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ key: `legacy-platform:${req.url}` }));
|
||||
}
|
||||
);
|
||||
await server.start({
|
||||
...config,
|
||||
port: 12345,
|
||||
});
|
||||
|
||||
test('binds proxy listener to server.', async () => {
|
||||
expect(newPlatformProxyListenerMock.bind).not.toHaveBeenCalled();
|
||||
expect(onConnectionListener).toHaveBeenCalledTimes(1);
|
||||
|
||||
await server.start(config);
|
||||
|
||||
expect(newPlatformProxyListenerMock.bind).toHaveBeenCalledTimes(1);
|
||||
expect(newPlatformProxyListenerMock.bind).toHaveBeenCalledWith(
|
||||
expect.any((http as any).Server)
|
||||
);
|
||||
expect(newPlatformProxyListenerMock.bind.mock.calls[0][0]).toBe(getServerListener(server));
|
||||
});
|
||||
|
||||
test('forwards request to legacy platform if new one cannot handle it', async () => {
|
||||
await server.start(config);
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/legacy')
|
||||
.expect(200)
|
||||
.then(res => {
|
||||
expect(res.body).toEqual({ key: 'legacy-platform:/legacy' });
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledTimes(1);
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledWith(
|
||||
expect.any((http as any).IncomingMessage),
|
||||
expect.any((http as any).ServerResponse)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('forwards request to legacy platform and rewrites base path if needed', async () => {
|
||||
await server.start({
|
||||
...config,
|
||||
basePath: '/bar',
|
||||
rewriteBasePath: true,
|
||||
});
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/legacy')
|
||||
.expect(404);
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/bar/legacy')
|
||||
.expect(200)
|
||||
.then(res => {
|
||||
expect(res.body).toEqual({ key: 'legacy-platform:/legacy' });
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledTimes(1);
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledWith(
|
||||
expect.any((http as any).IncomingMessage),
|
||||
expect.any((http as any).ServerResponse)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('do not forward request to legacy platform if new one can handle it', async () => {
|
||||
await server.start(config);
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/new/')
|
||||
.expect(200)
|
||||
.then(res => {
|
||||
expect(res.body).toEqual({ key: 'new-platform' });
|
||||
expect(newPlatformProxyListenerMock.proxy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
const [[{ options, server: rawServer }]] = onConnectionListener.mock.calls;
|
||||
expect(rawServer).toBeDefined();
|
||||
expect(rawServer).toBe((server as any).server);
|
||||
expect(options).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -45,7 +45,10 @@ export class HttpServer {
|
|||
}
|
||||
|
||||
public async start(config: HttpConfig) {
|
||||
this.server = createServer(getServerOptions(config));
|
||||
this.log.debug('starting http server');
|
||||
|
||||
const serverOptions = getServerOptions(config);
|
||||
this.server = createServer(serverOptions);
|
||||
|
||||
this.setupBasePathRewrite(this.server, config);
|
||||
|
||||
|
@ -59,32 +62,13 @@ export class HttpServer {
|
|||
}
|
||||
}
|
||||
|
||||
const legacyKbnServer = this.env.getLegacyKbnServer();
|
||||
if (legacyKbnServer !== undefined) {
|
||||
legacyKbnServer.newPlatformProxyListener.bind(this.server.listener);
|
||||
|
||||
// We register Kibana proxy middleware right before we start server to allow
|
||||
// all new platform plugins register their routes, so that `legacyKbnServer`
|
||||
// handles only requests that aren't handled by the new platform.
|
||||
this.server.route({
|
||||
handler: ({ raw: { req, res } }, responseToolkit) => {
|
||||
legacyKbnServer.newPlatformProxyListener.proxy(req, res);
|
||||
return responseToolkit.abandon;
|
||||
},
|
||||
method: '*',
|
||||
options: {
|
||||
payload: {
|
||||
output: 'stream',
|
||||
parse: false,
|
||||
timeout: false,
|
||||
// Having such a large value here will allow legacy routes to override
|
||||
// maximum allowed payload size set in the core http server if needed.
|
||||
maxBytes: Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
},
|
||||
path: '/{p*}',
|
||||
});
|
||||
}
|
||||
// Notify legacy compatibility layer about HTTP(S) connection providing server
|
||||
// instance with connection options so that we can properly bridge core and
|
||||
// the "legacy" Kibana internally.
|
||||
this.env.legacy.emit('connection', {
|
||||
options: serverOptions,
|
||||
server: this.server,
|
||||
});
|
||||
|
||||
await this.server.start();
|
||||
|
||||
|
@ -96,12 +80,13 @@ export class HttpServer {
|
|||
}
|
||||
|
||||
public async stop() {
|
||||
this.log.info('stopping http server');
|
||||
|
||||
if (this.server !== undefined) {
|
||||
await this.server.stop();
|
||||
this.server = undefined;
|
||||
if (this.server === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log.debug('stopping http server');
|
||||
await this.server.stop();
|
||||
this.server = undefined;
|
||||
}
|
||||
|
||||
private setupBasePathRewrite(server: Server, config: HttpConfig) {
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`correctly unbinds from the previous server. 1`] = `"Unhandled \\"error\\" event. (Error: Some error)"`;
|
||||
exports[`correctly binds to the server.: proxy route options 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"handler": [Function],
|
||||
"method": "*",
|
||||
"options": Object {
|
||||
"payload": Object {
|
||||
"maxBytes": 9007199254740991,
|
||||
"output": "stream",
|
||||
"parse": false,
|
||||
"timeout": false,
|
||||
},
|
||||
},
|
||||
"path": "/{p*}",
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { LegacyKbnServer } from '..';
|
||||
|
||||
test('correctly returns `newPlatformProxyListener`.', () => {
|
||||
const rawKbnServer = {
|
||||
newPlatform: {
|
||||
proxyListener: {},
|
||||
},
|
||||
};
|
||||
|
||||
const legacyKbnServer = new LegacyKbnServer(rawKbnServer);
|
||||
expect(legacyKbnServer.newPlatformProxyListener).toBe(rawKbnServer.newPlatform.proxyListener);
|
||||
});
|
|
@ -17,131 +17,74 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
|
||||
class MockNetServer extends EventEmitter {
|
||||
public address() {
|
||||
return { port: 1234, family: 'test-family', address: 'test-address' };
|
||||
}
|
||||
|
||||
public getConnections(callback: (error: Error | null, count: number) => void) {
|
||||
callback(null, 100500);
|
||||
}
|
||||
}
|
||||
|
||||
function mockNetServer() {
|
||||
return new MockNetServer();
|
||||
}
|
||||
|
||||
jest.mock('net', () => ({
|
||||
createServer: jest.fn(() => mockNetServer()),
|
||||
}));
|
||||
|
||||
import { createServer } from 'net';
|
||||
import { Server as HapiServer } from 'hapi-latest';
|
||||
import { Server } from 'net';
|
||||
import { LegacyPlatformProxifier } from '..';
|
||||
import { Env } from '../../config';
|
||||
import { getEnvOptions } from '../../config/__tests__/__mocks__/env';
|
||||
import { logger } from '../../logging/__mocks__';
|
||||
|
||||
let server: jest.Mocked<Server>;
|
||||
let mockHapiServer: jest.Mocked<HapiServer>;
|
||||
let root: any;
|
||||
let proxifier: LegacyPlatformProxifier;
|
||||
beforeEach(() => {
|
||||
server = {
|
||||
addListener: jest.fn(),
|
||||
address: jest
|
||||
.fn()
|
||||
.mockReturnValue({ port: 1234, family: 'test-family', address: 'test-address' }),
|
||||
getConnections: jest.fn(),
|
||||
} as any;
|
||||
|
||||
mockHapiServer = { listener: server, route: jest.fn() } as any;
|
||||
|
||||
root = {
|
||||
logger: {
|
||||
get: jest.fn(() => ({
|
||||
debug: jest.fn(),
|
||||
info: jest.fn(),
|
||||
})),
|
||||
},
|
||||
logger,
|
||||
shutdown: jest.fn(),
|
||||
start: jest.fn(),
|
||||
} as any;
|
||||
|
||||
proxifier = new LegacyPlatformProxifier(root);
|
||||
const env = new Env('/kibana', getEnvOptions());
|
||||
proxifier = new LegacyPlatformProxifier(root, env);
|
||||
env.legacy.emit('connection', {
|
||||
server: mockHapiServer,
|
||||
options: { someOption: 'foo', someAnotherOption: 'bar' },
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly binds to the server.', () => {
|
||||
const server = createServer();
|
||||
jest.spyOn(server, 'addListener');
|
||||
proxifier.bind(server);
|
||||
|
||||
expect(server.addListener).toHaveBeenCalledTimes(4);
|
||||
for (const eventName of ['listening', 'error', 'clientError', 'connection']) {
|
||||
expect(mockHapiServer.route.mock.calls).toMatchSnapshot('proxy route options');
|
||||
expect(server.addListener).toHaveBeenCalledTimes(6);
|
||||
for (const eventName of ['clientError', 'close', 'connection', 'error', 'listening', 'upgrade']) {
|
||||
expect(server.addListener).toHaveBeenCalledWith(eventName, expect.any(Function));
|
||||
}
|
||||
});
|
||||
|
||||
test('correctly binds to the server and redirects its events.', () => {
|
||||
const server = createServer();
|
||||
proxifier.bind(server);
|
||||
test('correctly redirects server events.', () => {
|
||||
for (const eventName of ['clientError', 'close', 'connection', 'error', 'listening', 'upgrade']) {
|
||||
expect(server.addListener).toHaveBeenCalledWith(eventName, expect.any(Function));
|
||||
|
||||
const eventsAndListeners = new Map(
|
||||
['listening', 'error', 'clientError', 'connection'].map(eventName => {
|
||||
const listener = jest.fn();
|
||||
proxifier.addListener(eventName, listener);
|
||||
|
||||
return [eventName, listener] as [string, () => void];
|
||||
})
|
||||
);
|
||||
|
||||
for (const [eventName, listener] of eventsAndListeners) {
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
const listener = jest.fn();
|
||||
proxifier.addListener(eventName, listener);
|
||||
|
||||
// Emit several events, to make sure that server is not being listened with `once`.
|
||||
server.emit(eventName, 1, 2, 3, 4);
|
||||
server.emit(eventName, 5, 6, 7, 8);
|
||||
const [, serverListener] = server.addListener.mock.calls.find(
|
||||
([serverEventName]) => serverEventName === eventName
|
||||
)!;
|
||||
|
||||
serverListener(1, 2, 3, 4);
|
||||
serverListener(5, 6, 7, 8);
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(2);
|
||||
expect(listener).toHaveBeenCalledWith(1, 2, 3, 4);
|
||||
expect(listener).toHaveBeenCalledWith(5, 6, 7, 8);
|
||||
}
|
||||
});
|
||||
|
||||
test('correctly unbinds from the previous server.', () => {
|
||||
const previousServer = createServer();
|
||||
proxifier.bind(previousServer);
|
||||
|
||||
const currentServer = createServer();
|
||||
proxifier.bind(currentServer);
|
||||
|
||||
const eventsAndListeners = new Map(
|
||||
['listening', 'error', 'clientError', 'connection'].map(eventName => {
|
||||
const listener = jest.fn();
|
||||
proxifier.addListener(eventName, listener);
|
||||
|
||||
return [eventName, listener] as [string, () => void];
|
||||
})
|
||||
);
|
||||
|
||||
// Any events from the previous server should not be forwarded.
|
||||
for (const [eventName, listener] of eventsAndListeners) {
|
||||
// `error` event is a special case in node, if `error` is emitted, but
|
||||
// there is no listener for it error will be thrown.
|
||||
if (eventName === 'error') {
|
||||
expect(() =>
|
||||
previousServer.emit(eventName, new Error('Some error'))
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
} else {
|
||||
previousServer.emit(eventName, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
// Only events from the last server should be forwarded.
|
||||
for (const [eventName, listener] of eventsAndListeners) {
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
|
||||
currentServer.emit(eventName, 1, 2, 3, 4);
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith(1, 2, 3, 4);
|
||||
proxifier.removeListener(eventName, listener);
|
||||
}
|
||||
});
|
||||
|
||||
test('returns `address` from the underlying server.', () => {
|
||||
expect(proxifier.address()).toBeUndefined();
|
||||
|
||||
proxifier.bind(createServer());
|
||||
|
||||
expect(proxifier.address()).toEqual({
|
||||
address: 'test-address',
|
||||
family: 'test-family',
|
||||
|
@ -168,33 +111,35 @@ test('`close` shuts down the `root`.', async () => {
|
|||
});
|
||||
|
||||
test('returns connection count from the underlying server.', () => {
|
||||
server.getConnections.mockImplementation(callback => callback(null, 0));
|
||||
const onGetConnectionsComplete = jest.fn();
|
||||
|
||||
proxifier.getConnections(onGetConnectionsComplete);
|
||||
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledTimes(1);
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledWith(null, 0);
|
||||
onGetConnectionsComplete.mockReset();
|
||||
|
||||
proxifier.bind(createServer());
|
||||
server.getConnections.mockImplementation(callback => callback(null, 100500));
|
||||
proxifier.getConnections(onGetConnectionsComplete);
|
||||
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledTimes(1);
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledWith(null, 100500);
|
||||
});
|
||||
|
||||
test('correctly proxies request and response objects.', () => {
|
||||
test('proxy route abandons request processing and forwards it to the legacy Kibana', async () => {
|
||||
const mockResponseToolkit = { response: jest.fn(), abandon: Symbol('abandon') };
|
||||
const mockRequest = { raw: { req: { a: 1 }, res: { b: 2 } } };
|
||||
|
||||
const onRequest = jest.fn();
|
||||
proxifier.addListener('request', onRequest);
|
||||
|
||||
const request = {} as IncomingMessage;
|
||||
const response = {} as ServerResponse;
|
||||
proxifier.proxy(request, response);
|
||||
const [[{ handler }]] = mockHapiServer.route.mock.calls;
|
||||
const response = await handler(mockRequest, mockResponseToolkit);
|
||||
|
||||
expect(response).toBe(mockResponseToolkit.abandon);
|
||||
expect(mockResponseToolkit.response).not.toHaveBeenCalled();
|
||||
|
||||
// Make sure request hasn't been passed to the legacy platform.
|
||||
expect(onRequest).toHaveBeenCalledTimes(1);
|
||||
expect(onRequest).toHaveBeenCalledWith(request, response);
|
||||
|
||||
// Check that exactly same objects were passed as event arguments.
|
||||
expect(onRequest.mock.calls[0][0]).toBe(request);
|
||||
expect(onRequest.mock.calls[0][1]).toBe(response);
|
||||
expect(onRequest).toHaveBeenCalledWith(mockRequest.raw.req, mockRequest.raw.res);
|
||||
});
|
||||
|
|
|
@ -19,24 +19,18 @@
|
|||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
/** @internal */
|
||||
export { LegacyPlatformProxifier } from './legacy_platform_proxifier';
|
||||
/** @internal */
|
||||
export { LegacyConfigToRawConfigAdapter, LegacyConfig } from './legacy_platform_config';
|
||||
/** @internal */
|
||||
export { LegacyKbnServer } from './legacy_kbn_server';
|
||||
|
||||
import {
|
||||
LegacyConfig,
|
||||
LegacyConfigToRawConfigAdapter,
|
||||
LegacyKbnServer,
|
||||
LegacyPlatformProxifier,
|
||||
} from '.';
|
||||
import { LegacyConfig, LegacyConfigToRawConfigAdapter, LegacyPlatformProxifier } from '.';
|
||||
import { Env } from '../config';
|
||||
import { Root } from '../root';
|
||||
import { BasePathProxyRoot } from '../root/base_path_proxy_root';
|
||||
|
||||
function initEnvironment(rawKbnServer: any) {
|
||||
function initEnvironment(rawKbnServer: any, isDevClusterMaster = false) {
|
||||
const config: LegacyConfig = rawKbnServer.config;
|
||||
|
||||
const legacyConfig$ = new BehaviorSubject(config);
|
||||
|
@ -45,12 +39,12 @@ function initEnvironment(rawKbnServer: any) {
|
|||
);
|
||||
|
||||
const env = Env.createDefault({
|
||||
kbnServer: new LegacyKbnServer(rawKbnServer),
|
||||
// The defaults for the following parameters are retrieved by the legacy
|
||||
// platform from the command line or from `package.json` and stored in the
|
||||
// config, so we can borrow these parameters and avoid double parsing.
|
||||
mode: config.get('env'),
|
||||
packageInfo: config.get('pkg'),
|
||||
// The core doesn't work with configs yet, everything is provided by the
|
||||
// "legacy" Kibana, so we can have empty array here.
|
||||
configs: [],
|
||||
// `dev` is the only CLI argument we currently use.
|
||||
cliArgs: { dev: config.get('env.dev') },
|
||||
isDevClusterMaster,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -71,12 +65,12 @@ export const injectIntoKbnServer = (rawKbnServer: any) => {
|
|||
|
||||
rawKbnServer.newPlatform = {
|
||||
// Custom HTTP Listener that will be used within legacy platform by HapiJS server.
|
||||
proxyListener: new LegacyPlatformProxifier(new Root(config$, env)),
|
||||
proxyListener: new LegacyPlatformProxifier(new Root(config$, env), env),
|
||||
updateConfig,
|
||||
};
|
||||
};
|
||||
|
||||
export const createBasePathProxy = (rawKbnServer: any) => {
|
||||
const { env, config$ } = initEnvironment(rawKbnServer);
|
||||
const { env, config$ } = initEnvironment(rawKbnServer, true /*isDevClusterMaster*/);
|
||||
return new BasePathProxyRoot(config$, env);
|
||||
};
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a wrapper around legacy `kbnServer` instance that exposes only
|
||||
* a subset of `kbnServer` APIs used by the new platform.
|
||||
* @internal
|
||||
*/
|
||||
export class LegacyKbnServer {
|
||||
constructor(private readonly rawKbnServer: any) {}
|
||||
|
||||
/**
|
||||
* Custom HTTP Listener used by HapiJS server in the legacy platform.
|
||||
*/
|
||||
get newPlatformProxyListener() {
|
||||
return this.rawKbnServer.newPlatform.proxyListener;
|
||||
}
|
||||
}
|
|
@ -18,16 +18,29 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import { Server } from 'net';
|
||||
|
||||
import { Server as HapiServer, ServerOptions as HapiServerOptions } from 'hapi-latest';
|
||||
import { Env } from '../config';
|
||||
import { Logger } from '../logging';
|
||||
import { Root } from '../root';
|
||||
|
||||
interface ConnectionInfo {
|
||||
server: HapiServer;
|
||||
options: HapiServerOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of the server events to be forwarded to the legacy platform.
|
||||
*/
|
||||
const ServerEventsToForward = ['listening', 'error', 'clientError', 'connection'];
|
||||
const ServerEventsToForward = [
|
||||
'clientError',
|
||||
'close',
|
||||
'connection',
|
||||
'error',
|
||||
'listening',
|
||||
'upgrade',
|
||||
];
|
||||
|
||||
/**
|
||||
* Represents "proxy" between legacy and current platform.
|
||||
|
@ -38,7 +51,7 @@ export class LegacyPlatformProxifier extends EventEmitter {
|
|||
private readonly log: Logger;
|
||||
private server?: Server;
|
||||
|
||||
constructor(private readonly root: Root) {
|
||||
constructor(private readonly root: Root, private readonly env: Env) {
|
||||
super();
|
||||
|
||||
this.log = root.logger.get('legacy-platform-proxifier');
|
||||
|
@ -56,6 +69,14 @@ export class LegacyPlatformProxifier extends EventEmitter {
|
|||
] as [string, (...args: any[]) => void];
|
||||
})
|
||||
);
|
||||
|
||||
// Once core HTTP service is ready it broadcasts the internal server it relies on
|
||||
// and server options that were used to create that server so that we can properly
|
||||
// bridge with the "legacy" Kibana. If server isn't run (e.g. if process is managed
|
||||
// by ClusterManager or optimizer) then this event will never fire.
|
||||
this.env.legacy.once('connection', (connectionInfo: ConnectionInfo) =>
|
||||
this.onConnection(connectionInfo)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,31 +137,36 @@ export class LegacyPlatformProxifier extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds Http/Https server to the LegacyPlatformProxifier.
|
||||
* @param server Server to bind to.
|
||||
*/
|
||||
public bind(server: Server) {
|
||||
const oldServer = this.server;
|
||||
this.server = server;
|
||||
private onConnection({ server }: ConnectionInfo) {
|
||||
this.server = server.listener;
|
||||
|
||||
for (const [eventName, eventHandler] of this.eventHandlers) {
|
||||
if (oldServer !== undefined) {
|
||||
oldServer.removeListener(eventName, eventHandler);
|
||||
}
|
||||
|
||||
this.server.addListener(eventName, eventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards request and response objects to the legacy platform.
|
||||
* This method is used whenever new platform doesn't know how to handle the request.
|
||||
* @param request Native Node request object instance.
|
||||
* @param response Native Node response object instance.
|
||||
*/
|
||||
public proxy(request: IncomingMessage, response: ServerResponse) {
|
||||
this.log.debug(`Request will be handled by proxy ${request.method}:${request.url}.`);
|
||||
this.emit('request', request, response);
|
||||
// We register Kibana proxy middleware right before we start server to allow
|
||||
// all new platform plugins register their routes, so that `legacyProxy`
|
||||
// handles only requests that aren't handled by the new platform.
|
||||
server.route({
|
||||
path: '/{p*}',
|
||||
method: '*',
|
||||
options: {
|
||||
payload: {
|
||||
output: 'stream',
|
||||
parse: false,
|
||||
timeout: false,
|
||||
// Having such a large value here will allow legacy routes to override
|
||||
// maximum allowed payload size set in the core http server if needed.
|
||||
maxBytes: Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
},
|
||||
handler: async ({ raw: { req, res } }, responseToolkit) => {
|
||||
this.log.trace(`Request will be handled by proxy ${req.method}:${req.url}.`);
|
||||
// Forward request and response objects to the legacy platform. This method
|
||||
// is used whenever new platform doesn't know how to handle the request.
|
||||
this.emit('request', req, res);
|
||||
return responseToolkit.abandon;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,5 +22,6 @@ import { dirname } from 'path';
|
|||
export const pkg = {
|
||||
__filename: require.resolve('../../package.json'),
|
||||
__dirname: dirname(require.resolve('../../package.json')),
|
||||
...require('../../package.json')
|
||||
// tslint:disable no-var-requires
|
||||
...require('../../package.json'),
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue