Skip to content

Commit

Permalink
Improve the way legacy and new platforms interact.
Browse files Browse the repository at this point in the history
  • Loading branch information
azasypkin committed Jul 28, 2018
1 parent ef8222f commit e3f5cb8
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 139 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
"h2o2-latest": "npm:h2o2@8.1.2",
"handlebars": "4.0.5",
"hapi": "14.2.0",
"hapi-latest": "npm:hapi@17.5.0",
"hapi-latest": "npm:hapi@17.5.2",
"hjson": "3.1.0",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.1",
Expand Down Expand Up @@ -244,7 +244,8 @@
"@types/fetch-mock": "^5.12.2",
"@types/getopts": "^2.0.0",
"@types/glob": "^5.0.35",
"@types/hapi-latest": "npm:@types/hapi@17.0.12",
"@types/hapi": "^13.0.38",
"@types/hapi-latest": "npm:@types/hapi@17.0.15",
"@types/has-ansi": "^3.0.0",
"@types/jest": "^22.2.3",
"@types/joi": "^10.4.4",
Expand Down
20 changes: 1 addition & 19 deletions src/cli/cluster/cluster_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,19 @@
* under the License.
*/

import { Server } from 'hapi';
import { debounce, invoke, bindAll, once, uniq } from 'lodash';
import { resolve } from 'path';

import Log from '../log';
import Worker from './worker';
import { Config } from '../../server/config/config';
import { transformDeprecations } from '../../server/config/transform_deprecations';
import { setupLogging } from '../../server/logging';

process.env.kbnWorkerType = 'managr';

export default class ClusterManager {
static create(opts, settings = {}, basePathProxy) {
const config = Config.withDefaultSchema(transformDeprecations(settings));

// New platform forwards all logs to the legacy platform so we need HapiJS server
// here just for logging purposes and nothing else.
const server = new Server();
setupLogging(server, config);

return {
kbnServer: {
async ready() {},
server,
close() {},
listen() {}
},

clusterManager: new ClusterManager(opts, config, basePathProxy)
};
return new ClusterManager(opts, Config.withDefaultSchema(transformDeprecations(settings)), basePathProxy);
}

constructor(opts, config, basePathProxy) {
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/config/__tests__/config_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

/* tslint:disable max-classes-per-file */
import { BehaviorSubject, first, k$, toPromise } from '../../../lib/kbn_observable';
import { AnyType, schema, TypeOf } from '../schema';
import { schema, Type, TypeOf } from '../schema';

import { ConfigService, ObjectToRawConfigAdapter } from '..';
import { logger } from '../../logging/__mocks__';
Expand Down Expand Up @@ -268,7 +268,7 @@ test('treats config as enabled if config path is not present in config', async (
expect(unusedPaths).toEqual([]);
});

function createClassWithSchema(s: AnyType) {
function createClassWithSchema(s: Type<any>) {
return class ExampleClassWithSchema {
public static schema = s;

Expand Down
8 changes: 4 additions & 4 deletions src/core/server/config/config_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { Logger, LoggerFactory } from '../logging';
import { ConfigWithSchema } from './config_with_schema';
import { Env } from './env';
import { RawConfig } from './raw_config';
import { AnyType } from './schema';
import { Type } from './schema';

export type ConfigPath = string | string[];

Expand Down Expand Up @@ -61,7 +61,7 @@ export class ConfigService {
* @param ConfigClass A class (not an instance of a class) that contains a
* static `schema` that we validate the config at the given `path` against.
*/
public atPath<Schema extends AnyType, Config>(
public atPath<Schema extends Type<any>, Config>(
path: ConfigPath,
ConfigClass: ConfigWithSchema<Schema, Config>
) {
Expand All @@ -76,7 +76,7 @@ export class ConfigService {
*
* @see atPath
*/
public optionalAtPath<Schema extends AnyType, Config>(
public optionalAtPath<Schema extends Type<any>, Config>(
path: ConfigPath,
ConfigClass: ConfigWithSchema<Schema, Config>
) {
Expand Down Expand Up @@ -120,7 +120,7 @@ export class ConfigService {
return config.getFlattenedPaths().filter(path => !isPathHandled(path, handledPaths));
}

private createConfig<Schema extends AnyType, Config>(
private createConfig<Schema extends Type<any>, Config>(
path: ConfigPath,
rawConfig: {},
ConfigClass: ConfigWithSchema<Schema, Config>
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/config/config_with_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

// TODO inline all of these
import { Env } from './env';
import { AnyType, TypeOf } from './schema';
import { Type, TypeOf } from './schema';

/**
* Interface that defines the static side of a config class.
Expand All @@ -31,7 +31,7 @@ import { AnyType, TypeOf } from './schema';
* in TypeScript, but it can be used to ensure we have a config class that
* matches whenever it's used.
*/
export interface ConfigWithSchema<S extends AnyType, Config> {
export interface ConfigWithSchema<S extends Type<any>, Config> {
/**
* Any config class must define a schema that validates the config, based on
* the injected `schema` helper.
Expand Down
9 changes: 7 additions & 2 deletions src/core/server/config/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ import {
UnionType,
} from './types';

export { AnyType, ObjectType, TypeOf };
export { ObjectType, TypeOf, Type };
export { ByteSizeValue } from './byte_size_value';

function any(options?: TypeOptions<any>) {
return new AnyType(options);
}

function boolean(options?: TypeOptions<boolean>): Type<boolean> {
return new BooleanType(options);
}
Expand Down Expand Up @@ -135,7 +139,7 @@ function oneOf<A, B, C>(
): Type<A | B | C>;
function oneOf<A, B>(types: [Type<A>, Type<B>], options?: TypeOptions<A | B>): Type<A | B>;
function oneOf<A>(types: [Type<A>], options?: TypeOptions<A>): Type<A>;
function oneOf<RTS extends AnyType[]>(types: RTS, options?: TypeOptions<any>): Type<any> {
function oneOf<RTS extends Array<Type<any>>>(types: RTS, options?: TypeOptions<any>): Type<any> {
return new UnionType(types, options);
}

Expand All @@ -158,6 +162,7 @@ function conditional<A extends ConditionalTypeValue, B, C>(
}

export const schema = {
any,
arrayOf,
boolean,
byteSize,
Expand Down
16 changes: 14 additions & 2 deletions src/core/server/config/schema/types/any_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@
* under the License.
*/

import { Type } from './type';
import typeDetect from 'type-detect';
import { internals } from '../internals';
import { Type, TypeOptions } from './type';

export type AnyType = Type<any>;
export class AnyType extends Type<any> {
constructor(options?: TypeOptions<any>) {
super(internals.any(), options);
}

protected handleError(type: string, { value }: Record<string, any>) {
if (type === 'any.required') {
return `expected value of type [any] but got [${typeDetect(value)}]`;
}
}
}
5 changes: 2 additions & 3 deletions src/core/server/config/schema/types/object_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@

import typeDetect from 'type-detect';
import { AnySchema, internals } from '../internals';
import { AnyType } from './any_type';
import { Type, TypeOptions } from './type';

export type Props = Record<string, AnyType>;
export type Props = Record<string, Type<any>>;

export type TypeOf<RT extends AnyType> = RT['type'];
export type TypeOf<RT extends Type<any>> = RT['type'];

// Because of https://github.com/Microsoft/TypeScript/issues/14041
// this might not have perfect _rendering_ output, but it will be typed.
Expand Down
3 changes: 1 addition & 2 deletions src/core/server/config/schema/types/union_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@
import typeDetect from 'type-detect';
import { SchemaTypeError, SchemaTypesError } from '../errors';
import { internals } from '../internals';
import { AnyType } from './any_type';
import { Type, TypeOptions } from './type';

export class UnionType<RTS extends AnyType[], T> extends Type<T> {
export class UnionType<RTS extends Array<Type<any>>, T> extends Type<T> {
constructor(types: RTS, options?: TypeOptions<T>) {
const schema = internals.alternatives(types.map(type => type.getSchema()));

Expand Down
20 changes: 19 additions & 1 deletion src/core/server/legacy_compat/legacy_platform_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ interface LegacyLoggingConfig {
quiet?: boolean;
dest?: string;
json?: boolean;
events?: Record<string, string>;
}

/**
Expand All @@ -50,7 +51,24 @@ export class LegacyConfigToRawConfigAdapter implements RawConfig {

private static transformLogging(configValue: LegacyLoggingConfig = {}) {
const loggingConfig = {
appenders: { default: { kind: 'legacy-appender' } },
appenders: {
default: {
kind: 'legacy-appender',
// We set `ops.interval` to max allowed number and `ops` filter to value
// that doesn't exist to avoid logging of ops information by log-only
// server instance we use in the core to emulate "legacy" Kibana log layout.
legacy: {
logging: {
...configValue,
events: {
...configValue.events,
ops: '__no-ops__',
},
},
ops: { interval: 2147483647 },
},
},
},
root: { level: 'info' },
};

Expand Down
63 changes: 22 additions & 41 deletions src/core/server/legacy_compat/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { ConfigService, Env, RawConfig } from '../config';
import { DevConfig } from '../dev';
import { BasePathProxyServer, HttpConfig } from '../http';
import { Logger, LoggerFactory } from '../logging';
import { LegacyLogRecord } from './logging/appenders/legacy_appender';

interface LegacyKbnServer {
applyLoggingConfiguration: (settings: Readonly<Record<string, any>>) => void;
Expand All @@ -50,7 +49,6 @@ interface LegacyServiceOptions {

export class LegacyService implements CoreService {
private readonly log: Logger;
private readonly logBuffer: LegacyLogRecord[] = [];
private kbnServer?: LegacyKbnServer;
private kbnServerSubscription?: Subscription;

Expand All @@ -62,7 +60,6 @@ export class LegacyService implements CoreService {
) {
this.log = logger.get('legacy.service');

this.setupLogListener();
this.setupConnectionListener();
}

Expand All @@ -86,8 +83,6 @@ export class LegacyService implements CoreService {
next: async kbnServerPromise => {
this.kbnServer = await kbnServerPromise;

this.flushLogBuffer();

await this.kbnServer.listen();
},
error: err => this.log.error(err),
Expand Down Expand Up @@ -124,26 +119,6 @@ export class LegacyService implements CoreService {
});
}

private setupLogListener() {
this.env.legacy.on('kbn:log', async (record: LegacyLogRecord) => {
// We can't log messages until "legacy" Kibana prepares its logging system.
if (this.kbnServer === undefined) {
this.logBuffer.push(record);
return;
}

this.kbnServer.server.log(record.tags, record.data, record.timestamp);
});
}

private flushLogBuffer() {
for (const logRecord of this.logBuffer) {
this.env.legacy.emit('kbn:log', logRecord);
}

this.logBuffer.length = 0;
}

private async createClusterManager(rawConfig: RawConfig) {
const [devConfig, httpConfig] = await k$(
$combineLatest<DevConfig, HttpConfig>(
Expand All @@ -153,30 +128,25 @@ export class LegacyService implements CoreService {
)(first(), toPromise());

const cliArgs = this.env.getCliArgs();

const { kbnServer } = require(CLUSTER_MANAGER_PATH).create(
require(CLUSTER_MANAGER_PATH).create(
cliArgs,
rawConfig.getRaw(),
cliArgs.basePath
? new BasePathProxyServer(this.logger.get('server'), httpConfig, devConfig)
: undefined
);

return kbnServer as LegacyKbnServer;
return {
close() {
// noop
},
listen() {
// noop
},
} as LegacyKbnServer;
}

private async createKbnServer(rawConfig: RawConfig) {
const KbnServer = require('../../../server/kbn_server');
const kbnServer: LegacyKbnServer = new KbnServer(rawConfig.getRaw(), this.env.legacy);

// The kbnWorkerType check is necessary to prevent the repl
// from being started multiple times in different processes.
// We only want one REPL.
const { repl } = this.env.getCliArgs();
if (repl && process.env.kbnWorkerType === 'server') {
require(REPL_PATH).startRepl(this.kbnServer);
}

const httpConfig = await k$(this.configService.atPath('server', HttpConfig))(
first(),
toPromise()
Expand All @@ -186,8 +156,19 @@ export class LegacyService implements CoreService {
// and connection options won't be passed to the "legacy" Kibana that will block
// its initialization, so we should make "legacy" Kibana aware of that case and
// let it continue initialization without waiting for connection options.
if (!httpConfig.autoListen) {
this.env.legacy.emit('kbn:connection', { autoListen: false });
const connection = !httpConfig.autoListen
? { autoListen: false }
: await new Promise(resolve => this.env.legacy.once('kbn:connection', resolve));

const KbnServer = require('../../../server/kbn_server');
const kbnServer: LegacyKbnServer = new KbnServer(rawConfig.getRaw(), { connection });

// The kbnWorkerType check is necessary to prevent the repl
// from being started multiple times in different processes.
// We only want one REPL.
const { repl } = this.env.getCliArgs();
if (repl && process.env.kbnWorkerType === 'server') {
require(REPL_PATH).startRepl(this.kbnServer);
}

await kbnServer.ready();
Expand Down
Loading

0 comments on commit e3f5cb8

Please sign in to comment.