-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proxy ExpressJS requests to HapiJS. #13844
Proxy ExpressJS requests to HapiJS. #13844
Conversation
8391303
to
213f7ee
Compare
platform/cli/cli.ts
Outdated
root.logger.get('proxy'), | ||
argv.kbnServer | ||
)); | ||
proxy.on('platform-start', async () => await root.start()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just exploring the idea, but maybe it's not that bad if we start root
only when old platform starts Hapi with our proxy
as a listener. This way we reuse port that old platform supplies us via listener
so we don't have to read kbnServer.settings.server.port
...
platform/config/Env.ts
Outdated
interface WithConfig { | ||
config?: string; | ||
kbnServer?: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't really know where to plug this kbnServer
in - env
was the easiest one :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works for now. I might want to move it later, but for the PoC it's 👍
platform/server/http/HttpServer.ts
Outdated
const proxy = this.env.getProxy(); | ||
if (proxy) { | ||
proxy.bind(this.server); | ||
port = proxy.getPort(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The real port is passed by old Kibana in setup_connection -> proxy.listen(...)
so we don't have to think which port to use here, just grab what old platform gave us.
platform/server/http/HttpServer.ts
Outdated
this.app.use([ | ||
|
||
// We will register body parser only for routes defined in the new platform. | ||
/*this.app.use([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we don't have to register body parsers "globally" and we can do that on route basis (maybe even router level). I'll check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++ I like the idea of adding them on routers instead
platform/server/http/Proxy/Proxy.ts
Outdated
|
||
import { Logger } from '../../../logging'; | ||
|
||
export class Proxy extends EventEmitter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really a "proxy" - it's more like a "something-else", so just semi-randomly picked "proxy" :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ProxyToLegacyPlatform
, just to be clearer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
platform/server/http/Proxy/Proxy.ts
Outdated
this.port = port; | ||
|
||
this.emit('platform-start'); | ||
callback && callback(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we should call callback
only when platform is ready, but I should think how to do that. And I haven't noticed any issues with this approach yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we removed the events and rather injected start
and stop
functions when creating the Proxy
, we could call the callback
when they finish instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea ✔️
src/server/http/setup_connection.js
Outdated
@@ -3,6 +3,8 @@ import { map } from 'lodash'; | |||
import secureOptions from './secure_options'; | |||
|
|||
export default function (kbnServer, server, config) { | |||
const proxy = kbnServer && kbnServer.proxy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely don't want to expose proxy
property on the kbnServer
, but I haven't figured out the better way yet.... Do you have any ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've already got tons of things on it, so I think it's okey. We could probably rename this to be clearer. newPlatformProxyListener
or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
UPDATE:
Example:
/cc @kjbekkelund @spalger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great start. I think we should get rid of code that lets the new platform start on its own, and always have it start through old Kibana (aka we always receive kbnServer
etc).
platform/cli/cli.ts
Outdated
@@ -20,7 +21,7 @@ export const parseArgv = (argv: Array<string>) => | |||
.epilogue(args.docs) | |||
.check(args.check(args.options)).argv; | |||
|
|||
const run = (argv: { [key: string]: any }) => { | |||
export const run = (argv: { [key: string]: any }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like we should create a separate runWithLegacyKbnServer
. We probably need to explore not listening for SIGHUP
, SIGINT
etc here, and rather receive that through the old platform somehow. I've got a feeling we might want to receive the config from the old platform too, and have a transformLegacyConfig
or something like that which transforms it into whatever we need/want in the new platform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Or just rewrite this code to be explicit about kbnServer
always being there, and getting rid of node scripts/platform
entirely. Then, when we get to K7 we can add whatever we need to have the new platform start cleanly on its own)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have strong opinion on that, having runWithLegacyKbnServer
sounds reasonable to me. I'd get rid of node scritps/platform
only once we have dev setup ready, I still use ts:start
with scripts/platform
to quickly test changes.
I've got a feeling we might want to receive the config from the old platform too, and have a transformLegacyConfig or something like that which transforms it into whatever we need/want in the new platform.
I'll explore that.
platform/server/http/Proxy/Proxy.ts
Outdated
this.port = port; | ||
|
||
this.emit('platform-start'); | ||
callback && callback(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we removed the events and rather injected start
and stop
functions when creating the Proxy
, we could call the callback
when they finish instead.
platform/server/http/Proxy/Proxy.ts
Outdated
export class Proxy extends EventEmitter { | ||
private server: Server; | ||
private readonly eventHandlers: any; | ||
private port: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't specified in the constructor, so should be:
private port?: number;
(Also: I'm hoping they implement microsoft/TypeScript#8476 soon)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
platform/server/http/Proxy/Proxy.ts
Outdated
listening: forwardEvent.bind(this, 'listening'), | ||
error: forwardEvent.bind(this, 'error'), | ||
clientError: forwardEvent.bind(this, 'clientError'), | ||
connection: forwardEvent.bind(this, 'connection') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these needed? (Might be obvious, just add a comment about it in the code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these needed?
Hapi relies on these events, some are critical, some aren't:
listening
: https://github.com/hapijs/hapi/blob/v14.2.0/lib/connection.js#L120error
: https://github.com/hapijs/hapi/blob/v14.2.0/lib/connection.js#L166clientError
: https://github.com/hapijs/hapi/blob/v14.2.0/lib/connection.js#L92connection
: https://github.com/hapijs/hapi/blob/v14.2.0/lib/connection.js#L142
Might be obvious, just add a comment about it in the code
Sure ✔️
platform/server/http/Proxy/Proxy.ts
Outdated
|
||
getConnections(callback: (error?: Error, count?: number) => void) { | ||
// This method is used by `even-better` (before we start platform). | ||
// It seems that the latest version of parent `good` doesn't use this anymore. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hopefully we'll get rid of it soon: #13800
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that would be nice
platform/server/http/Proxy/Proxy.ts
Outdated
|
||
import { Logger } from '../../../logging'; | ||
|
||
export class Proxy extends EventEmitter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ProxyToLegacyPlatform
, just to be clearer?
@@ -12,8 +14,17 @@ let app: any; | |||
let port: number; | |||
|
|||
beforeEach(() => { | |||
const log: Logger = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not something we need to fix in this PR: There is a logger mock in ../../../logging/__mocks__
, not sure if that works out-of-the-box here. We need some way of sharing this across tests, though, just so we don't have to keep mocks like this up-to-date. Jest has some automatic mocking support, but I haven't looked at it yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree
src/server/http/setup_connection.js
Outdated
@@ -3,6 +3,8 @@ import { map } from 'lodash'; | |||
import secureOptions from './secure_options'; | |||
|
|||
export default function (kbnServer, server, config) { | |||
const proxy = kbnServer && kbnServer.proxy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've already got tons of things on it, so I think it's okey. We could probably rename this to be clearer. newPlatformProxyListener
or something like that?
8bd9bb2
to
7565b4e
Compare
platform/cli/cli.ts
Outdated
@@ -52,4 +53,24 @@ const run = (argv: { [key: string]: any }) => { | |||
} | |||
}; | |||
|
|||
export const runWithLegacyKbnServer = (kbnServer: any) => { | |||
const root = new Root( | |||
getLegacyConfig$(kbnServer), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kjbekkelund I've added some initial support for transforming legacy config to a new format, could you please take a look?
It also required some changes to Duration
and ByteSize
since in the old Kibana we use numbers for these type of values (either number of ms or bytes). I prefer the new platform approach though (strings like 1mb
etc.) and would get rid of number support once we finish migration. Alternatively LegacyPlatformConfig
can transform numbers to format new platform understands like newconfig.timeout: "${oldconfig.timeout}"ms
, but I found it a bit clumsy :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, I'll take a look now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just scanned quickly through the code and noted a couple things. Will try to run it etc now, just wanted to submit the comments so far.
@@ -31,6 +31,38 @@ describe('parsing units', () => { | |||
}); | |||
}); | |||
|
|||
describe('#constructor', () => { | |||
test('throws if number of bytes is negative', () => { | |||
expect(() => new ByteSizeValue(-1024)).toThrowError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see the full error message here, maybe use toThrowErrorMatchingSnapshot
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
|
||
export const getLegacyConfig$ = (kbnServer: any) => { | ||
return Observable.from([ | ||
new Proxy({ [configProperty]: kbnServer.config }, configProxyHandler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proxy isn't supported in IE11, I think(?) Or do we maybe do some Babel magic which enables it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very gooood point! Will check, totally forgot about that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, likely will have to get rid of Proxy
and update ConfigService
to work a bit differently :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But first will check https://www.npmjs.com/package/proxy-polyfill
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No way here, got rid of Proxy
in favor of an additional abstraction (RawConfig
) around config plain object.
platform/cli/cli.ts
Outdated
@@ -52,4 +53,24 @@ const run = (argv: { [key: string]: any }) => { | |||
} | |||
}; | |||
|
|||
export const runWithLegacyKbnServer = (kbnServer: any) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary yet, but before we merge we should create a interface LegacyKbnServer { ... }
with the fields we use in ts files, just so we don't type it as any
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
platform/cli/cli.ts
Outdated
() => root.shutdown() | ||
); | ||
|
||
// TODO: Do something to reload config. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could for example register a callback on kbnServer
in this file, and then call that whenever
Line 173 in c79e223
process.on('SIGHUP', function reloadConfig() { |
Another idea could be to create a BehaviorSubject
in legacy Kibana that we expose as an Observable. We'd next
on whenever the sighup triggers in the above code I linked. So here we'd only need to map
over it and do the transform from legacy config stuff. Then we could remove this process.on('SIGHUP'
entirely from this function.
No matter what we choose I feel it's important that the new platform can do "full config reload" from day 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, both options look the same to me, just that in the second option code will be rxjs-fied (unless I'm missing something). I'll experiment with various options and see what we can get.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Both approaches are fine with me too
private readonly log: Logger, | ||
readonly kbnServer: any, | ||
private readonly startPlatform: () => Promise<any>, | ||
private readonly stopPlatform: () => Promise<any> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Promise<void>
for both of these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
super(); | ||
|
||
const forwardEvent = (name: string, ...args: any[]) => { | ||
this.log.info(`Event is being forwarded: ${name}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info
-> debug
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
} | ||
|
||
proxy(request: IncomingMessage, response: OutgoingMessage) { | ||
this.log.info( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info
-> debug
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
callback && callback(); | ||
} | ||
|
||
async close(callback: Function) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe:
async close(callback: () => {}) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean () => void
right? If so, then ✔️
return this.server && this.server.address(); | ||
} | ||
|
||
async listen(port: number, host: string, callback: Function) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
callback: Function
to
callback: () => void
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
) { | ||
super(); | ||
|
||
const forwardEvent = (name: string, ...args: any[]) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
niiiitpick:
const forwardEvent = (name: string) => (...args: any[]) => {
this.log.info(`Event is being forwarded: ${name}`);
this.emit(name, ...args);
};
then there no need to .bind
below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
platform/server/http/HttpServer.ts
Outdated
cert: readFileSync(config.ssl.certificate!), | ||
ca: | ||
config.ssl.certificateAuthorities && | ||
config.ssl.certificateAuthorities!.map(caFilePath => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need the !
here, I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, ✔️
platform/config/Env.ts
Outdated
@@ -42,6 +45,10 @@ export class Env { | |||
return resolve(this.pluginsDir, pluginName, 'target', 'dist'); | |||
} | |||
|
|||
getProxy(): LegacyPlatformProxifier | undefined { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getNewPlatformProxyListener
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
platform/cli/cli.ts
Outdated
@@ -52,4 +53,24 @@ const run = (argv: { [key: string]: any }) => { | |||
} | |||
}; | |||
|
|||
export const runWithLegacyKbnServer = (kbnServer: any) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, if we remove the process.on
stuff below, I think we should extract this into a separate file (and a separate folder) as there's no cli related stuff left in this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree if we don't have process.on
stuff it will reside in legacy
sub-folder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kjbekkelund regarding SIGINT
and SIGTERM
: it seems old platform doesn't handle these events (it only cleans pid
file on SIGINT
). I'm not sure if we should handle these events in the new platform if it's run within old one and basically acts as an advanced HTTP server, what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should listen for them specifically, but we probably want to listen for something that signals that we should shut down (just to make sure we run all the shutdown stuff correctly for later when we remove the old platform). However, I'm not sure how we indicate that in current platform.
) { | ||
super(); | ||
|
||
const forwardEvent = (name: string) => (name: string, ...args: any[]) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove name: string,
from second function here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yeah, good catch. I'm really surprised that it still works :) Will figure that out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, main 'request'
event is still being forwarded correctly from another method
configPath = LegacyConfigToRawConfigAdapter.flattenConfigPath(configPath); | ||
|
||
// Explicitly disable plugins until they are supported by the legacy config. | ||
if (!this.legacyConfig.has(configPath) && configPath.endsWith('.enabled')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what would be the best solution here. New platform checks enabled
status for every plugin (eg. pid.enabled
), but old platform doesn't know these keys and throws error. So special casing and disabling such plugins was the simplest solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove this (as #14288 is merged)
import { EventEmitter } from 'events'; | ||
import { IncomingMessage, ServerResponse } from 'http'; | ||
|
||
const mockConfigService = jest.fn(() => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of stuff here is just to mock Root
, maybe we just need RootMock
or something, but I didn't think about it too much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about something like this:
diff --git a/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts b/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts
index 57ce148ea4..f25cfad479 100644
--- a/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts
+++ b/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts
@@ -1,34 +1,6 @@
import { EventEmitter } from 'events';
import { IncomingMessage, ServerResponse } from 'http';
-const mockConfigService = jest.fn(() => ({
- atPath: jest.fn()
-}));
-jest.mock('../../config/ConfigService', () => ({
- ConfigService: mockConfigService
-}));
-
-const mockServer = jest.fn(() => ({
- start: jest.fn(),
- stop: jest.fn()
-}));
-jest.mock('../../server', () => ({ Server: mockServer }));
-
-const mockLoggingService = jest.fn(() => ({
- upgrade: jest.fn(),
- stop: jest.fn()
-}));
-jest.mock('../../logging/LoggingService', () => ({
- LoggingService: mockLoggingService
-}));
-
-const mockMutableLoggerFactory = jest.fn(() => ({
- get: jest.fn(() => ({ info: jest.fn(), debug: jest.fn() }))
-}));
-jest.mock('../../logging/LoggerFactory', () => ({
- MutableLoggerFactory: mockMutableLoggerFactory
-}));
-
class mockNetServer extends EventEmitter {
address() {
return { port: 1234, family: 'test-family', address: 'test-address' };
@@ -43,7 +15,6 @@ jest.mock('net', () => ({
}));
import { BehaviorSubject } from 'rxjs';
-import { Root } from '../../root';
import { Env, ObjectToRawConfigAdapter } from '../../config';
import { LegacyPlatformProxifier } from '..';
import { Server } from 'net';
@@ -54,11 +25,17 @@ beforeEach(() => {
const env = new Env('.', {});
const config$ = new BehaviorSubject(new ObjectToRawConfigAdapter({}));
- root = new Root(config$, env, () => {});
+ root = {
+ start: jest.fn(),
+ shutdown: jest.fn(),
+ logger: {
+ get: jest.fn(() => ({
+ info: jest.fn(),
+ debug: jest.fn()
+ }))
+ }
+ } as any;
proxifier = new LegacyPlatformProxifier(root);
-
- jest.spyOn(root, 'start');
- jest.spyOn(root, 'shutdown');
});
test('correctly binds to the server.', () => {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's much better, thanks 👍 any
to the rescue :)
@@ -171,10 +173,17 @@ export default function (program) { | |||
} | |||
|
|||
process.on('SIGHUP', function reloadConfig() { | |||
const settings = getCurrentSettings(); | |||
const settings = transformDeprecations(getCurrentSettings()); | |||
const config = new Config(kbnServer.config.getSchema(), settings); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use existing schema since I need schemas registered by plugins (e.g. elasticsearch
), default schema doesn't contain those.
src/cli/serve/serve.js
Outdated
kbnServer.server.log(['info', 'config'], 'Reloaded logging configuration due to SIGHUP.'); | ||
|
||
// If new platform config subscription is active, let's notify it with the updated config. | ||
if (kbnServer.newPlatformConfig) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are only two legacy kibana server tests that are failing because of this PR. I haven't fixed them in this PR, but plan to do so in the follow-ups, just need more time to figure out the best way to do so.
-
src/ui/__tests__/ui_exports_replace_injected_vars.js
- test createsKibanaServer
with custom plugins path that doesn't includeelasticsearch
and config doesn't include schema for it as well, but it's a requirement for the new platform, fix is easy. -
src/cli/serve/__tests__/reload_logging_config.js
- this test is full of magic and fails just because of absence oftags
property in new platformLogRecord
, fix is also trivial, but I've deferred that to PR that will introducelegacy-json
andlegacy-pattern
layouts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 We don't have to do them in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@kjbekkelund PR is now ready for your review :) |
…g.dest,logging.json` values into new format.
…plugins) for new config on SIGHUP.
7166d81
to
6385ef4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a first pass. It's looking good, and I think we're almost there. I just wanted to send this feedback before taking another round through this to make sure I understand all of it.
platform/config/Env.ts
Outdated
@@ -1,8 +1,11 @@ | |||
import * as process from 'process'; | |||
import { resolve } from 'path'; | |||
|
|||
import { LegacyPlatformProxifier } from '../legacy'; | |||
|
|||
interface WithConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe rename to EnvOptions
, and rename argv
to options
in the Env class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
configPath = LegacyConfigToRawConfigAdapter.flattenConfigPath(configPath); | ||
|
||
// Explicitly disable plugins until they are supported by the legacy config. | ||
if (!this.legacyConfig.has(configPath) && configPath.endsWith('.enabled')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove this (as #14288 is merged)
platform/server/http/HttpServer.ts
Outdated
this.httpServer.close(); | ||
this.log.info('stopping http server'); | ||
|
||
if (!this.server) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: if (this.server == null)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️ (== undefined
since we changed type to Server | undefined
above).
platform/server/http/HttpServer.ts
Outdated
import { Router } from './Router'; | ||
|
||
export class HttpServer { | ||
private readonly app: express.Application; | ||
private readonly httpServer: http.Server; | ||
private server: Server | null = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Any specific reason for null
? Otherwise, maybe use undefined
instead? I'm okey with either, but undefined
is a bit more consistent with the rest of the code base.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, there is no reason, just used null
for some reason :) By the way, should we use server: Server | undefined
or just server?: Server
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️ (chose private server?: Server;
, let me know if you disagree)
platform/server/http/HttpServer.ts
Outdated
this.server = this.initializeServer(config); | ||
|
||
const proxyListener = this.env.getNewPlatformProxyListener(); | ||
if (proxyListener) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: proxyListener !== undefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
platform/legacy/index.ts
Outdated
); | ||
|
||
kbnServer.newPlatformProxyListener = new LegacyPlatformProxifier( | ||
new Root(config$, Env.createDefault({ kbnServer }), () => {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: Maybe make the last param just default to noop
in Root
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️ (I'm trying to avoid using lodash
where possible, so used () => {}
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
import { EventEmitter } from 'events'; | ||
import { IncomingMessage, ServerResponse } from 'http'; | ||
|
||
const mockConfigService = jest.fn(() => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about something like this:
diff --git a/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts b/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts
index 57ce148ea4..f25cfad479 100644
--- a/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts
+++ b/platform/legacy/__tests__/LegacyPlatformProxifier.test.ts
@@ -1,34 +1,6 @@
import { EventEmitter } from 'events';
import { IncomingMessage, ServerResponse } from 'http';
-const mockConfigService = jest.fn(() => ({
- atPath: jest.fn()
-}));
-jest.mock('../../config/ConfigService', () => ({
- ConfigService: mockConfigService
-}));
-
-const mockServer = jest.fn(() => ({
- start: jest.fn(),
- stop: jest.fn()
-}));
-jest.mock('../../server', () => ({ Server: mockServer }));
-
-const mockLoggingService = jest.fn(() => ({
- upgrade: jest.fn(),
- stop: jest.fn()
-}));
-jest.mock('../../logging/LoggingService', () => ({
- LoggingService: mockLoggingService
-}));
-
-const mockMutableLoggerFactory = jest.fn(() => ({
- get: jest.fn(() => ({ info: jest.fn(), debug: jest.fn() }))
-}));
-jest.mock('../../logging/LoggerFactory', () => ({
- MutableLoggerFactory: mockMutableLoggerFactory
-}));
-
class mockNetServer extends EventEmitter {
address() {
return { port: 1234, family: 'test-family', address: 'test-address' };
@@ -43,7 +15,6 @@ jest.mock('net', () => ({
}));
import { BehaviorSubject } from 'rxjs';
-import { Root } from '../../root';
import { Env, ObjectToRawConfigAdapter } from '../../config';
import { LegacyPlatformProxifier } from '..';
import { Server } from 'net';
@@ -54,11 +25,17 @@ beforeEach(() => {
const env = new Env('.', {});
const config$ = new BehaviorSubject(new ObjectToRawConfigAdapter({}));
- root = new Root(config$, env, () => {});
+ root = {
+ start: jest.fn(),
+ shutdown: jest.fn(),
+ logger: {
+ get: jest.fn(() => ({
+ info: jest.fn(),
+ debug: jest.fn()
+ }))
+ }
+ } as any;
proxifier = new LegacyPlatformProxifier(root);
-
- jest.spyOn(root, 'start');
- jest.spyOn(root, 'shutdown');
});
test('correctly binds to the server.', () => {
@@ -0,0 +1,27 @@ | |||
export class LegacyConfigMock { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a note above this with something like:
This is a partial mock of
~/src/server/config/config.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
readonly has: jest.Mock<boolean>; | ||
|
||
constructor(public __rawData: Map<string, any> = new Map()) { | ||
this.get = jest.fn(key => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if better, but could be:
diff --git a/platform/legacy/__mocks__/LegacyConfigMock.ts b/platform/legacy/__mocks__/LegacyConfigMock.ts
index 05d5614c1a..dc91c8e84a 100644
--- a/platform/legacy/__mocks__/LegacyConfigMock.ts
+++ b/platform/legacy/__mocks__/LegacyConfigMock.ts
@@ -1,10 +1,7 @@
export class LegacyConfigMock {
- readonly get: jest.Mock<any>;
- readonly set: jest.Mock<void>;
- readonly has: jest.Mock<boolean>;
+ constructor(public __rawData: Map<string, any> = new Map()) {}
- constructor(public __rawData: Map<string, any> = new Map()) {
- this.get = jest.fn(key => {
+ get = jest.fn(key => {
// Real legacy config throws error if key is not presented in the schema.
if (!this.__rawData.has(key)) {
throw new TypeError(`Unknown schema key: ${key}`);
@@ -13,7 +10,7 @@ export class LegacyConfigMock {
return this.__rawData.get(key);
});
- this.set = jest.fn((key, value) => {
+ set = jest.fn((key, value) => {
// Real legacy config throws error if key is not presented in the schema.
if (!this.__rawData.has(key)) {
throw new TypeError(`Unknown schema key: ${key}`);
@@ -22,6 +19,5 @@ export class LegacyConfigMock {
this.__rawData.set(key, value);
});
- this.has = jest.fn(key => this.__rawData.has(key));
- }
+ has = jest.fn(key => this.__rawData.has(key));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better, thanks :) We should always offload constructor code if there is a way to do so.
✔️
src/cli/serve/serve.js
Outdated
kbnServer.server.log(['info', 'config'], 'Reloaded logging configuration due to SIGHUP.'); | ||
|
||
// If new platform config subscription is active, let's notify it with the updated config. | ||
if (kbnServer.newPlatformConfig) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 We don't have to do them in this PR.
Went through it again, and I just have the mostly nitpicks above, otherwise I'm good with merging this. |
…names, do not expose RxJS Subject to legacy platform).
Pushed commit that handles your comments @kjbekkelund, should be ready for the next round. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we’re good to go! 🎉🎉 LGTM
Thanks! 🎉 Let's see if it sticks. |
Really early-draft of ExpressJS (new platform) and HapiJS (existing platform) integration.
This is supposed to handle #12520