Skip to content
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

[New platform] Introduce start phase for core services on server #35297

Merged
merged 23 commits into from
Apr 30, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4eef5fb
introduce start phase. setup is bloated with start functionality
mshustov Apr 18, 2019
f116a29
fix amp typings: server is part of start contract now
mshustov Apr 18, 2019
faad244
update mock files
mshustov Apr 18, 2019
a037fe8
root.start(): necessary to run test server
mshustov Apr 18, 2019
25aa5a5
expose setup&start server api to simplify testing
mshustov Apr 18, 2019
7ffd2aa
move tests to the new API
mshustov Apr 18, 2019
fad9a06
test servers also should call root.start()
mshustov Apr 18, 2019
cfddce2
update docs
mshustov Apr 18, 2019
406ae80
update snapshots: this functionality is tested in http server
mshustov Apr 18, 2019
bfd7b0b
split setup/start phases
mshustov Apr 24, 2019
38b7394
Merge branch 'master' into introduce-start-phase
mshustov Apr 24, 2019
f504d00
update docs
mshustov Apr 24, 2019
ea34a0e
expose http server if it not started
mshustov Apr 25, 2019
3d245f5
adopt test to exposed Http server via SetupContract
mshustov Apr 25, 2019
05e0333
udpate docs
mshustov Apr 25, 2019
d46e7a8
cleanup apm changees
mshustov Apr 25, 2019
62a6724
Merge branch 'master' into introduce-start-phase
mshustov Apr 25, 2019
255d3c3
check legacy service setup before start
mshustov Apr 25, 2019
8880af1
check http server setup before start
mshustov Apr 25, 2019
02ccc54
restrict server options mutation; unify Promise interface for setup
mshustov Apr 30, 2019
f8cc8c8
Merge branch 'master' into introduce-start-phase
mshustov Apr 30, 2019
c04fdd2
introduce start pahse for plugins service for parity with client side
mshustov Apr 30, 2019
37de718
Revert "introduce start pahse for plugins service for parity with cli…
mshustov Apr 30, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/public/plugins/plugins_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type PluginsServiceContract = PublicMethodsOf<PluginsService>;
const createMock = () => {
const mocked: jest.Mocked<PluginsServiceContract> = {
setup: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
};

Expand Down
2 changes: 2 additions & 0 deletions src/core/public/plugins/plugins_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export class PluginsService implements CoreService<PluginsServiceSetup> {
return { contracts };
}

public async start() {}
mshustov marked this conversation as resolved.
Show resolved Hide resolved

public async stop() {
// Stop plugins in reverse topological order.
for (const pluginName of this.satupPlugins.reverse()) {
Expand Down
1 change: 1 addition & 0 deletions src/core/server/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export async function bootstrap({

try {
await root.setup();
await root.start();
} catch (err) {
await shutdown(err);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type ElasticsearchServiceContract = PublicMethodsOf<ElasticsearchService>;
const createMock = () => {
const mocked: jest.Mocked<ElasticsearchServiceContract> = {
setup: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
};
mocked.setup.mockResolvedValue(createSetupContractMock());
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/elasticsearch/elasticsearch_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ export class ElasticsearchService implements CoreService<ElasticsearchServiceSet
};
}

public async start() {}

public async stop() {
this.log.debug('Stopping elasticsearch service');

Expand Down
60 changes: 39 additions & 21 deletions src/core/server/http/http_server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ afterEach(async () => {
test('listening after started', async () => {
expect(server.isListening()).toBe(false);

await server.setup(config);
await server.start(config);

expect(server.isListening()).toBe(true);
Expand All @@ -68,8 +69,8 @@ test('200 OK with body', async () => {
return res.ok({ key: 'value' });
});

server.registerRouter(router);

const { registerRouter } = await server.setup(config);
registerRouter(router);
const { server: innerServer } = await server.start(config);

await supertest(innerServer.listener)
Expand All @@ -87,7 +88,8 @@ test('202 Accepted with body', async () => {
return res.accepted({ location: 'somewhere' });
});

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand All @@ -106,7 +108,8 @@ test('204 No content', async () => {
return res.noContent();
});

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand All @@ -127,7 +130,8 @@ test('400 Bad request with error', async () => {
return res.badRequest(err);
});

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -156,7 +160,8 @@ test('valid params', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -185,7 +190,8 @@ test('invalid params', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -217,7 +223,8 @@ test('valid query', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -246,7 +253,8 @@ test('invalid query', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -278,7 +286,8 @@ test('valid body', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -311,7 +320,8 @@ test('invalid body', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -343,7 +353,8 @@ test('handles putting', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -373,7 +384,8 @@ test('handles deleting', async () => {
}
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand All @@ -398,7 +410,8 @@ test('filtered headers', async () => {
return res.noContent();
});

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(config);

Expand Down Expand Up @@ -430,7 +443,8 @@ describe('with `basepath: /bar` and `rewriteBasePath: false`', () => {
res.ok({ key: 'value:/foo' })
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(configWithBasePath);
innerServerListener = innerServer.listener;
Expand Down Expand Up @@ -490,7 +504,8 @@ describe('with `basepath: /bar` and `rewriteBasePath: true`', () => {
res.ok({ key: 'value:/foo' })
);

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

const { server: innerServer } = await server.start(configWithBasePath);
innerServerListener = innerServer.listener;
Expand Down Expand Up @@ -555,25 +570,28 @@ describe('with defined `redirectHttpFromPort`', () => {
const router = new Router('/');
router.get({ path: '/', validate: false }, async (req, res) => res.ok({ key: 'value:/' }));

server.registerRouter(router);
const { registerRouter } = await server.setup(config);
registerRouter(router);

await server.start(configWithSSL);
});
});

test('returns server and connection options on start', async () => {
const { server: innerServer, options } = await server.start({
const configWithPort = {
...config,
port: 12345,
});
};
const { options } = await server.setup(configWithPort);
const { server: innerServer } = await server.start(configWithPort);

expect(innerServer).toBeDefined();
expect(innerServer).toBe((server as any).server);
expect(options).toMatchSnapshot();
});

test('registers auth request interceptor only once', async () => {
const { registerAuth } = await server.start(config);
const { registerAuth } = await server.setup(config);
const doRegister = () =>
registerAuth(() => null as any, {
encryptionKey: 'any_password',
Expand All @@ -584,7 +602,7 @@ test('registers auth request interceptor only once', async () => {
});

test('registers onRequest interceptor several times', async () => {
const { registerOnRequest } = await server.start(config);
const { registerOnRequest } = await server.setup(config);
const doRegister = () => registerOnRequest(() => null as any);

doRegister();
Expand Down
38 changes: 26 additions & 12 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ import {
createCookieSessionStorageFactory,
} from './cookie_session_storage';

export interface HttpServerInfo {
server: Server;
export interface HttpServerSetup {
options: ServerOptions;
registerRouter: (router: Router) => void;
/**
* Define custom authentication and/or authorization mechanism for incoming requests.
* Applied to all resources by default. Only one AuthenticationHandler can be registered.
Expand All @@ -50,6 +50,10 @@ export interface HttpServerInfo {
registerOnRequest: (requestHandler: OnRequestHandler) => void;
}

export interface HttpServerInfo {
server: Server;
}

export class HttpServer {
private server?: Server;
private registeredRouters = new Set<Router>();
Expand All @@ -61,20 +65,36 @@ export class HttpServer {
return this.server !== undefined && this.server.listener.listening;
}

public registerRouter(router: Router) {
private registerRouter(router: Router) {
if (this.isListening()) {
throw new Error('Routers can be registered only when HTTP server is stopped.');
}

this.log.debug(`registering route handler for [${router.path}]`);
this.registeredRouters.add(router);
}

public async start(config: HttpConfig): Promise<HttpServerInfo> {
this.log.debug('starting http server');

public setup(config: HttpConfig): HttpServerSetup {
const serverOptions = getServerOptions(config);
this.server = createServer(serverOptions);

return {
options: serverOptions,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deepFreeze might be a good idea here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aah yeah. I wanted to add freeze here, but found that we have 2 implementations - one for js in x-pack/secuirty, one for ts in core/public/utils. I think it makes sense to reach an agreement how we going to distribute environment agnostic code snippets, case with deepFreeze is perfect example. Do we have such agreement? If not I will create a discussion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think that we do have a guideline on sharing code other than things that live as @kbn/ packages. Could make sense to move this utility there, but it's so small, I'm not sure the boilerplate of a separate package is worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turned out the correct implementation is not so simple microsoft/TypeScript#13923

for now I will copy this functionality, but we need to extract it in a separate package later

registerRouter: this.registerRouter.bind(this),
registerOnRequest: this.registerOnRequest.bind(this),
registerAuth: <T>(
fn: AuthenticationHandler<T>,
cookieOptions: SessionStorageCookieOptions<T>
) => this.registerAuth(fn, cookieOptions, config.basePath),
};
}

public async start(config: HttpConfig): Promise<HttpServerInfo> {
if (this.server === undefined) {
throw new Error('server is not setup up yet');
}
this.log.debug('starting http server');

this.setupBasePathRewrite(this.server, config);

for (const router of this.registeredRouters) {
Expand All @@ -100,12 +120,6 @@ export class HttpServer {
// needed anymore we shouldn't return anything from this method.
return {
server: this.server,
options: serverOptions,
registerOnRequest: this.registerOnRequest.bind(this),
registerAuth: <T>(
fn: AuthenticationHandler<T>,
cookieOptions: SessionStorageCookieOptions<T>
) => this.registerAuth(fn, cookieOptions, config.basePath),
};
}

Expand Down
14 changes: 11 additions & 3 deletions src/core/server/http/http_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,31 @@ import { HttpService } from './http_service';

const createSetupContractMock = () => {
const setupContract = {
// we can mock some hapi server method when we need it
server: {} as Server,
options: {} as ServerOptions,
registerAuth: jest.fn(),
registerOnRequest: jest.fn(),
registerRouter: jest.fn(),
};
return setupContract;
};

const createStartContractMock = () => {
const startContract = {
// we can mock some hapi server method when we need it
server: {} as Server,
};
return startContract;
};

type HttpServiceContract = PublicMethodsOf<HttpService>;
const createHttpServiceMock = () => {
const mocked: jest.Mocked<HttpServiceContract> = {
setup: jest.fn(),
start: jest.fn(),
stop: jest.fn(),
registerRouter: jest.fn(),
};
mocked.setup.mockResolvedValue(createSetupContractMock());
mocked.start.mockResolvedValue(createStartContractMock());
return mocked;
};

Expand Down