Skip to content

Commit

Permalink
Log Kibana URL only when server is really ready (main http server, ba…
Browse files Browse the repository at this point in the history
…se path proxy server and http->https redirection server).
  • Loading branch information
azasypkin committed Aug 6, 2018
1 parent 47cd0b9 commit 8c12c59
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 109 deletions.
19 changes: 11 additions & 8 deletions src/core/server/http/base_path_proxy_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export class BasePathProxyServer {
}

public async start(options: Readonly<BasePathProxyServerOptions>) {
this.log.debug('starting basepath proxy server');

const serverOptions = getServerOptions(this.httpConfig);
this.server = createServer(serverOptions);

Expand All @@ -79,21 +81,22 @@ export class BasePathProxyServer {

this.setupRoutes(options);

await this.server.start();

this.log.info(
`starting basepath proxy server at ${this.server.info.uri}${this.httpConfig.basePath}`
`basepath proxy server running at ${this.server.info.uri}${this.httpConfig.basePath}`
);

await this.server.start();
}

public async stop() {
this.log.info('stopping basepath proxy server');

if (this.server !== undefined) {
await this.server.stop();
this.server = undefined;
if (this.server === undefined) {
return;
}

this.log.debug('stopping basepath proxy server');
await this.server.stop();
this.server = undefined;

if (this.httpsAgent !== undefined) {
this.httpsAgent.destroy();
this.httpsAgent = undefined;
Expand Down
12 changes: 7 additions & 5 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export class HttpServer {
}

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

const serverOptions = getServerOptions(config);
this.server = createServer(serverOptions);

Expand All @@ -70,10 +72,10 @@ export class HttpServer {

await this.server.start();

this.log.info(
`Server running at ${this.server.info.uri}${config.rewriteBasePath ? config.basePath : ''}`,
// The "legacy" Kibana will output log records with `listening` tag even if `quiet` logging mode is enabled.
{ tags: ['listening'] }
this.log.debug(
`http server running at ${this.server.info.uri}${
config.rewriteBasePath ? config.basePath : ''
}`
);
}

Expand All @@ -82,7 +84,7 @@ export class HttpServer {
return;
}

this.log.info('stopping http server');
this.log.debug('stopping http server');
await this.server.stop();
this.server = undefined;
}
Expand Down
9 changes: 4 additions & 5 deletions src/core/server/http/https_redirect_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,15 @@ export class HttpsRedirectServer {
constructor(private readonly log: Logger) {}

public async start(config: HttpConfig) {
this.log.debug('starting http --> https redirect server');

if (!config.ssl.enabled || config.ssl.redirectHttpFromPort === undefined) {
throw new Error(
'Redirect server cannot be started when [ssl.enabled] is set to `false`' +
' or [ssl.redirectHttpFromPort] is not specified.'
);
}

this.log.info(
`starting HTTP --> HTTPS redirect server [${config.host}:${config.ssl.redirectHttpFromPort}]`
);

// Redirect server is configured in the same way as any other HTTP server
// within the platform with the only exception that it should always be a
// plain HTTP server, so we just ignore `tls` part of options.
Expand All @@ -65,6 +63,7 @@ export class HttpsRedirectServer {

try {
await this.server.start();
this.log.debug(`http --> https redirect server running at ${this.server.info.uri}`);
} catch (err) {
if (err.code === 'EADDRINUSE') {
throw new Error(
Expand All @@ -83,7 +82,7 @@ export class HttpsRedirectServer {
return;
}

this.log.info('stopping HTTP --> HTTPS redirect server');
this.log.debug('stopping http --> https redirect server');
await this.server.stop();
this.server = undefined;
}
Expand Down
153 changes: 63 additions & 90 deletions src/core/server/legacy_compat/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,7 @@

import { Server, ServerOptions } from 'hapi-latest';
import { CLUSTER_MANAGER_PATH, REPL_PATH } from '../../cli/installation_features';
import {
$combineLatest,
BehaviorSubject,
filter,
first,
k$,
map,
Subscription,
toPromise,
} from '../../lib/kbn_observable';
import { $combineLatest, first, k$, map, Subscription, toPromise } from '../../lib/kbn_observable';
import { CoreService } from '../../types/core_service';
import { ConfigService, Env, RawConfig } from '../config';
import { DevConfig } from '../dev';
Expand All @@ -40,27 +31,22 @@ interface LegacyKbnServer {
applyLoggingConfiguration: (settings: Readonly<Record<string, any>>) => void;
listen: () => Promise<void>;
close: () => Promise<void>;
ready: () => Promise<void>;
server: {
/**
* Forwards log request to the legacy platform.
* @param tags A string or array of strings used to briefly identify the event.
* @param [data] Optional string or object to log with the event.
* @param [timestamp] Timestamp value associated with the log record.
*/
log: (tags: string | string[], data?: string | Error, timestamp?: Date) => void;
};
}

interface LegacyServiceOptions {
isDevClusterMaster: boolean;
}

interface HttpConnection {
server: Server;
options: ServerOptions;
}

export class LegacyService implements CoreService {
private readonly log: Logger;
private kbnServer?: LegacyKbnServer;
private rawConfigSubscription?: Subscription;
private readonly connection$: BehaviorSubject<ServerOptions | null> = new BehaviorSubject(null);
private connection?: HttpConnection;

constructor(
private readonly env: Env,
Expand All @@ -74,7 +60,7 @@ export class LegacyService implements CoreService {
}

public async start() {
this.log.debug('Starting legacy service');
this.log.debug('starting legacy service');

this.rawConfigSubscription = this.configService.getConfig$().subscribe({
next: rawConfig => {
Expand All @@ -101,7 +87,7 @@ export class LegacyService implements CoreService {
}

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

if (this.rawConfigSubscription !== undefined) {
this.rawConfigSubscription.unsubscribe();
Expand All @@ -115,51 +101,46 @@ export class LegacyService implements CoreService {
}

private setupConnectionListener() {
this.env.legacy.once(
'connection',
({ server, options }: { server: Server; options: ServerOptions }) => {
const legacyProxifier = new LegacyPlatformProxifier(
this.logger.get('legacy', 'proxy'),
server.listener
);

// We register Kibana proxy middleware right before we start server to allow
// all new platform plugins register their routes, so that `legacyProxifier`
// handles only requests that aren't handled by the new platform.
server.route({
path: '/{p*}',
method: '*',
options: { payload: { output: 'stream', parse: false, timeout: false } },
handler: async ({ raw: { req, res } }, responseToolkit) => {
if (this.kbnServer === undefined) {
this.log.debug(`Kibana server is not ready yet ${req.method}:${req.url}.`);

// If legacy server is not ready yet (e.g. it's still in optimization phase),
// we should let client know that and ask to retry after 30 seconds.
return responseToolkit
.response('Kibana server is not ready yet')
.code(503)
.header('Retry-After', '30');
}

this.log.debug(`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.
legacyProxifier.emit('request', req, res);

return responseToolkit.abandon;
},
});

this.connection$.next({
...options,
port: undefined,
autoListen: false,
listener: legacyProxifier as any,
});
}
);
this.env.legacy.once('connection', ({ server, options }: HttpConnection) => {
const legacyProxifier = new LegacyPlatformProxifier(
this.logger.get('legacy', 'proxy'),
server.listener
);

// We register Kibana proxy middleware right before we start server to allow
// all new platform plugins register their routes, so that `legacyProxifier`
// handles only requests that aren't handled by the new platform.
server.route({
path: '/{p*}',
method: '*',
options: { payload: { output: 'stream', parse: false, timeout: false } },
handler: async ({ raw: { req, res } }, responseToolkit) => {
if (this.kbnServer === undefined) {
this.log.debug(`Kibana server is not ready yet ${req.method}:${req.url}.`);

// If legacy server is not ready yet (e.g. it's still in optimization phase),
// we should let client know that and ask to retry after 30 seconds.
return responseToolkit
.response('Kibana server is not ready yet')
.code(503)
.header('Retry-After', '30');
}

this.log.debug(`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.
legacyProxifier.emit('request', req, res);

return responseToolkit.abandon;
},
});

this.connection = {
server,
options: { ...options, listener: legacyProxifier as any },
};
});
}

private async createClusterManager(rawConfig: RawConfig) {
Expand All @@ -178,14 +159,7 @@ export class LegacyService implements CoreService {
: undefined
);

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

private async createKbnServer(rawConfig: RawConfig) {
Expand All @@ -194,16 +168,13 @@ export class LegacyService implements CoreService {
toPromise()
);

// If `server.autoListen` is set to `false`, then HTTP service won't be started
// 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.
const connection = !httpConfig.autoListen
? { autoListen: false }
: await k$(this.connection$)(filter(conn => conn !== null), first(), toPromise());

const KbnServer = require('../../../server/kbn_server');
const kbnServer: LegacyKbnServer = new KbnServer(rawConfig.getRaw(), { connection });
const kbnServer: LegacyKbnServer = new KbnServer(rawConfig.getRaw(), {
// If we haven't received connection from the core that means that server wasn't
// supposed to start automatically (e.g. when `--server.autoListen=false`) and
// we should make sure that "legacy" Kibana is aware of that.
connection: this.connection === undefined ? { autoListen: false } : this.connection.options,
});

// The kbnWorkerType check is necessary to prevent the repl
// from being started multiple times in different processes.
Expand All @@ -212,11 +183,13 @@ export class LegacyService implements CoreService {
require(REPL_PATH).startRepl(this.kbnServer);
}

try {
await kbnServer.listen();
} catch (err) {
await kbnServer.close();
throw err;
if (httpConfig.autoListen) {
try {
await kbnServer.listen();
} catch (err) {
await kbnServer.close();
throw err;
}
}

return kbnServer;
Expand Down
8 changes: 7 additions & 1 deletion src/server/kbn_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,13 @@ export default class KbnServer {
process.send(['WORKER_LISTENING']);
}

return this.server;
const { server, config } = this;
server.log(['listening', 'info'], `Server running at ${server.info.uri}${
config.get('server.rewriteBasePath')
? config.get('server.basePath')
: ''
}`);
return server;
}

async close() {
Expand Down

0 comments on commit 8c12c59

Please sign in to comment.