backend-app-api: flip logic of health service

Signed-off-by: Vincenzo Scamporlino <vincenzos@spotify.com>
This commit is contained in:
Vincenzo Scamporlino
2024-06-05 13:27:39 +02:00
parent 918b397763
commit 44f97c173b
7 changed files with 88 additions and 14 deletions
@@ -22,7 +22,13 @@ describe('DefaultHealthService', () => {
const service = new DefaultHealthService({
lifecycle: mockServices.rootLifecycle.mock(),
});
await expect(service.getReadiness()).resolves.toEqual({ status: 503 });
await expect(service.getReadiness()).resolves.toEqual({
status: 503,
payload: {
message: 'Backend has not started yet',
status: 'error',
},
});
});
it('should return 200 if the server has started', async () => {
@@ -33,7 +39,7 @@ describe('DefaultHealthService', () => {
);
const service = new DefaultHealthService({
lifecycle: mockServices.rootLifecycle.mock(),
lifecycle,
});
mockServerStartedFn();
@@ -63,6 +69,10 @@ describe('DefaultHealthService', () => {
mockServerStoppedFn();
await expect(service.getReadiness()).resolves.toEqual({
status: 503,
payload: {
message: 'Backend has not started yet',
status: 'error',
},
});
});
});
@@ -37,6 +37,7 @@ export class DefaultHealthService implements HealthService {
async getLiveness(): Promise<{ status: number; payload?: any }> {
return { status: 200, payload: { status: 'ok' } };
}
async getReadiness(): Promise<{ status: number; payload?: any }> {
if (!this.#isRunning) {
return {
@@ -0,0 +1,42 @@
import { HealthService } from '@backstage/backend-plugin-api';
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Router from 'express-promise-router';
import { Request, Response } from 'express';
export function createHealthRouter(options: { health: HealthService }) {
const router = Router();
router.get(
'.backstage/health/v1/readiness',
async (_request: Request, response: Response) => {
const { status, payload } = await options.health.getReadiness();
response.status(status).json(payload);
},
);
router.get(
'.backstage/health/v1/liveness',
async (_request: Request, response: Response) => {
const { status, payload } = await options.health.getLiveness();
response.status(status).json(payload);
},
);
return router;
}
@@ -29,6 +29,7 @@ import {
readHttpServerOptions,
} from './http';
import { DefaultRootHttpRouter } from './DefaultRootHttpRouter';
import { createHealthRouter } from './createHealthRouter';
/**
* @public
@@ -41,6 +42,7 @@ export interface RootHttpRouterConfigureContext {
config: RootConfigService;
logger: LoggerService;
lifecycle: LifecycleService;
healthRouter: RequestHandler;
applyDefaults: () => void;
}
@@ -75,8 +77,9 @@ export const rootHttpRouterServiceFactory = createServiceFactory(
config: coreServices.rootConfig,
rootLogger: coreServices.rootLogger,
lifecycle: coreServices.rootLifecycle,
health: coreServices.health,
},
async factory({ config, rootLogger, lifecycle }) {
async factory({ config, rootLogger, lifecycle, health }) {
const { indexPath, configure = defaultConfigure } = options ?? {};
const logger = rootLogger.child({ service: 'rootHttpRouter' });
const app = express();
@@ -84,6 +87,8 @@ export const rootHttpRouterServiceFactory = createServiceFactory(
const router = DefaultRootHttpRouter.create({ indexPath });
const middleware = MiddlewareFactory.create({ config, logger });
const routes = router.handler();
const healthRouter = createHealthRouter({ health });
const server = await createHttpServer(
app,
readHttpServerOptions(config.getOptionalConfig('backend')),
@@ -98,11 +103,13 @@ export const rootHttpRouterServiceFactory = createServiceFactory(
config,
logger,
lifecycle,
healthRouter,
applyDefaults() {
app.use(middleware.helmet());
app.use(middleware.cors());
app.use(middleware.compression());
app.use(middleware.logging());
app.use(healthRouter);
app.use(routes);
app.use(middleware.notFound());
app.use(middleware.error());
+12 -1
View File
@@ -335,7 +335,18 @@ export type ExtensionPoint<T> = {
export type ExtensionPointConfig = CreateExtensionPointOptions;
// @public (undocumented)
export interface HealthService {}
export interface HealthService {
// (undocumented)
getLiveness(): Promise<{
status: number;
payload?: any;
}>;
// (undocumented)
getReadiness(): Promise<{
status: number;
payload?: any;
}>;
}
// @public
export interface HttpAuthService {
@@ -17,4 +17,7 @@
/**
* @public
*/
export interface HealthService {}
export interface HealthService {
getLiveness(): Promise<{ status: number; payload?: any }>;
getReadiness(): Promise<{ status: number; payload?: any }>;
}
@@ -20,11 +20,11 @@ import {
HostDiscovery,
discoveryServiceFactory,
} from '@backstage/backend-defaults/discovery';
import { healthServiceFactory } from '@backstage/backend-defaults/health';
import { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';
import { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';
import { loggerServiceFactory } from '@backstage/backend-defaults/logger';
import { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';
import { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';
import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';
import { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';
import { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';
@@ -345,6 +345,14 @@ export namespace mockServices {
}));
}
export namespace health {
export const factory = healthServiceFactory;
export const mock = simpleMock(coreServices.health, () => ({
getLiveness: jest.fn(),
getReadiness: jest.fn(),
}));
}
export namespace httpRouter {
export const factory = httpRouterServiceFactory;
export const mock = simpleMock(coreServices.httpRouter, () => ({
@@ -384,14 +392,6 @@ export namespace mockServices {
}));
}
export namespace rootHealth {
export const factory = rootHealthServiceFactory;
export const mock = simpleMock(coreServices.health, () => ({
getLiveness: jest.fn(),
getReadiness: jest.fn(),
}));
}
export namespace rootLifecycle {
export const factory = rootLifecycleServiceFactory;
export const mock = simpleMock(coreServices.rootLifecycle, () => ({