Skip to content

Commit aedcdd7

Browse files
committed
[server] Move /live and /ready endpoints to a separate express app and port
Tool: gitpod/catfood.gitpod.cloud
1 parent 0c1af2c commit aedcdd7

File tree

5 files changed

+55
-10
lines changed

5 files changed

+55
-10
lines changed

components/server/src/container-module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ import { AnalyticsController } from "./analytics-controller";
136136
import { InstallationAdminCleanup } from "./jobs/installation-admin-cleanup";
137137
import { AuditLogService } from "./audit/AuditLogService";
138138
import { AuditLogGarbageCollectorJob } from "./jobs/auditlog-gc";
139+
import { ProbesApp } from "./liveness/probes";
139140

140141
export const productionContainerModule = new ContainerModule(
141142
(bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => {
@@ -241,6 +242,8 @@ export const productionContainerModule = new ContainerModule(
241242
bind(IWorkspaceManagerClientCallMetrics).toService(IClientCallMetrics);
242243

243244
bind(WorkspaceDownloadService).toSelf().inSingletonScope();
245+
246+
bind(ProbesApp).toSelf().inSingletonScope();
244247
bind(LivenessController).toSelf().inSingletonScope();
245248
bind(ReadinessController).toSelf().inSingletonScope();
246249

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) 2025 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License.AGPL.txt in the project root for license information.
5+
*/
6+
7+
import express from "express";
8+
import { inject, injectable } from "inversify";
9+
import { LivenessController } from "./liveness-controller";
10+
import { ReadinessController } from "./readiness-controller";
11+
12+
@injectable()
13+
export class ProbesApp {
14+
constructor(
15+
@inject(LivenessController) protected readonly livenessController: LivenessController,
16+
@inject(ReadinessController) protected readonly readinessController: ReadinessController,
17+
) {}
18+
19+
public create(): express.Application {
20+
const probesApp = express();
21+
probesApp.use("/live", this.livenessController.apiRouter);
22+
probesApp.use("/ready", this.readinessController.apiRouter);
23+
return probesApp;
24+
}
25+
}

components/server/src/server.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ import { NewsletterSubscriptionController } from "./user/newsletter-subscription
3636
import { Config } from "./config";
3737
import { DebugApp } from "@gitpod/gitpod-protocol/lib/util/debug-app";
3838
import { WsConnectionHandler } from "./express/ws-connection-handler";
39-
import { LivenessController } from "./liveness/liveness-controller";
40-
import { ReadinessController } from "./liveness/readiness-controller";
4139
import { IamSessionApp } from "./iam/iam-session-app";
4240
import { API } from "./api/server";
4341
import { GithubApp } from "./prebuilds/github-app";
@@ -54,6 +52,11 @@ import {
5452
} from "./workspace/headless-log-service";
5553
import { runWithRequestContext } from "./util/request-context";
5654
import { AnalyticsController } from "./analytics-controller";
55+
import { ProbesApp as ProbesAppProvider } from "./liveness/probes";
56+
57+
const MONITORING_PORT = 9500;
58+
const IAM_SESSION_PORT = 9876;
59+
const PROBES_PORT = 9400;
5760

5861
@injectable()
5962
export class Server {
@@ -66,6 +69,8 @@ export class Server {
6669
protected privateApiServer?: http.Server;
6770

6871
protected readonly eventEmitter = new EventEmitter();
72+
protected probesApp: express.Application;
73+
protected probesServer?: http.Server;
6974
protected app?: express.Application;
7075
protected httpServer?: http.Server;
7176
protected monitoringApp?: express.Application;
@@ -80,8 +85,6 @@ export class Server {
8085
@inject(UserController) private readonly userController: UserController,
8186
@inject(WebsocketConnectionManager) private readonly websocketConnectionHandler: WebsocketConnectionManager,
8287
@inject(WorkspaceDownloadService) private readonly workspaceDownloadService: WorkspaceDownloadService,
83-
@inject(LivenessController) private readonly livenessController: LivenessController,
84-
@inject(ReadinessController) private readonly readinessController: ReadinessController,
8588
@inject(MonitoringEndpointsApp) private readonly monitoringEndpointsApp: MonitoringEndpointsApp,
8689
@inject(CodeSyncService) private readonly codeSyncService: CodeSyncService,
8790
@inject(HeadlessLogController) private readonly headlessLogController: HeadlessLogController,
@@ -102,6 +105,7 @@ export class Server {
102105
@inject(API) private readonly api: API,
103106
@inject(RedisSubscriber) private readonly redisSubscriber: RedisSubscriber,
104107
@inject(AnalyticsController) private readonly analyticsController: AnalyticsController,
108+
@inject(ProbesAppProvider) private readonly probesAppProvider: ProbesAppProvider,
105109
) {}
106110

107111
public async init(app: express.Application) {
@@ -115,6 +119,9 @@ export class Server {
115119
await this.typeOrm.connect();
116120
log.info("connected to DB");
117121

122+
// probes
123+
this.probesApp = this.probesAppProvider.create();
124+
118125
// metrics
119126
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
120127
const startTime = Date.now();
@@ -321,8 +328,6 @@ export class Server {
321328
// Authorization: none
322329
app.use(this.oneTimeSecretServer.apiRouter);
323330
app.use(this.newsletterSubscriptionController.apiRouter);
324-
app.use("/live", this.livenessController.apiRouter);
325-
app.use("/ready", this.readinessController.apiRouter);
326331
app.use("/version", (req: express.Request, res: express.Response, next: express.NextFunction) => {
327332
res.send(this.config.version);
328333
});
@@ -354,22 +359,27 @@ export class Server {
354359
throw new Error("server cannot start, not initialized");
355360
}
356361

362+
const probeServer = this.probesApp.listen(PROBES_PORT, () => {
363+
log.info(`probes server listening on port: ${(<AddressInfo>probeServer.address()).port}`);
364+
});
365+
this.probesServer = probeServer;
366+
357367
const httpServer = this.app.listen(port, () => {
358368
this.eventEmitter.emit(Server.EVENT_ON_START, httpServer);
359369
log.info(`server listening on port: ${(<AddressInfo>httpServer.address()).port}`);
360370
});
361371
this.httpServer = httpServer;
362372

363373
if (this.monitoringApp) {
364-
this.monitoringHttpServer = this.monitoringApp.listen(9500, "localhost", () => {
374+
this.monitoringHttpServer = this.monitoringApp.listen(MONITORING_PORT, "localhost", () => {
365375
log.info(
366376
`monitoring app listening on port: ${(<AddressInfo>this.monitoringHttpServer!.address()).port}`,
367377
);
368378
});
369379
}
370380

371381
if (this.iamSessionApp) {
372-
this.iamSessionAppServer = this.iamSessionApp.listen(9876, () => {
382+
this.iamSessionAppServer = this.iamSessionApp.listen(IAM_SESSION_PORT, () => {
373383
log.info(
374384
`IAM session server listening on port: ${(<AddressInfo>this.iamSessionAppServer!.address()).port}`,
375385
);
@@ -409,6 +419,7 @@ export class Server {
409419
race(this.stopServer(this.httpServer), "stop httpserver"),
410420
race(this.stopServer(this.privateApiServer), "stop private api server"),
411421
race(this.stopServer(this.publicApiServer), "stop public api server"),
422+
race(this.stopServer(this.probesServer), "stop probe server"),
412423
race((async () => this.disposables.dispose())(), "dispose disposables"),
413424
]);
414425

install/installer/pkg/components/server/constants.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const (
2525
DebugNodePortName = "debugnode"
2626
ServicePort = 3000
2727
personalAccessTokenSigningKeyMountPath = "/secrets/personal-access-token-signing-key"
28+
ProbesPort = 9400
29+
ProbesPortName = "probes"
2830

2931
AdminCredentialsSecretName = "admin-credentials"
3032
AdminCredentialsSecretMountPath = "/credentials/admin"

install/installer/pkg/components/server/deployment.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {
363363
Path: "/live",
364364
Port: intstr.IntOrString{
365365
Type: intstr.Int,
366-
IntVal: ContainerPort,
366+
IntVal: ProbesPort,
367367
},
368368
},
369369
},
@@ -377,7 +377,7 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {
377377
Path: "/ready",
378378
Port: intstr.IntOrString{
379379
Type: intstr.Int,
380-
IntVal: ContainerPort,
380+
IntVal: ProbesPort,
381381
},
382382
},
383383
},
@@ -422,6 +422,10 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {
422422
Name: PublicAPIName,
423423
ContainerPort: PublicAPIPort,
424424
},
425+
{
426+
Name: ProbesPortName,
427+
ContainerPort: ProbesPort,
428+
},
425429
},
426430
// todo(sje): do we need to cater for serverContainer.env from values.yaml?
427431
Env: common.CustomizeEnvvar(ctx, Component, env),

0 commit comments

Comments
 (0)