backend-defaults: move over health service
Signed-off-by: Vincenzo Scamporlino <vincenzos@spotify.com>
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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';
|
||||
import { RootLifecycleService } from '@backstage/backend-plugin-api';
|
||||
|
||||
export function createHealthRouter(options: {
|
||||
lifecycle: RootLifecycleService;
|
||||
}) {
|
||||
const router = Router();
|
||||
|
||||
let isRunning = false;
|
||||
options.lifecycle.addStartupHook(() => {
|
||||
isRunning = true;
|
||||
});
|
||||
options.lifecycle.addShutdownHook(() => {
|
||||
isRunning = false;
|
||||
});
|
||||
|
||||
router.get('/v1/readiness', async (_request: Request, response: Response) => {
|
||||
if (!isRunning) {
|
||||
throw new Error('Backend has not started yet');
|
||||
}
|
||||
response.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
router.get('/v1/liveness', async (_request: Request, response: Response) => {
|
||||
response.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { createHealthRouter } from './createHealthRouter';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const healthServiceFactory = createServiceFactory({
|
||||
service: coreServices.health,
|
||||
deps: {
|
||||
rootHttpRouter: coreServices.rootHttpRouter,
|
||||
lifecycle: coreServices.rootLifecycle,
|
||||
},
|
||||
async factory({ lifecycle, rootHttpRouter }) {
|
||||
rootHttpRouter.use('.backstage/health', createHealthRouter({ lifecycle }));
|
||||
|
||||
return {};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
## API Report File for "@backstage/backend-defaults"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { HealthService } from '@backstage/backend-plugin-api';
|
||||
import { ServiceFactory } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @public (undocumented)
|
||||
export const healthServiceFactory: () => ServiceFactory<HealthService, 'root'>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -24,6 +24,7 @@
|
||||
"./cache": "./src/entrypoints/cache/index.ts",
|
||||
"./database": "./src/entrypoints/database/index.ts",
|
||||
"./discovery": "./src/entrypoints/discovery/index.ts",
|
||||
"./health": "./src/entrypoints/health/index.ts",
|
||||
"./httpAuth": "./src/entrypoints/httpAuth/index.ts",
|
||||
"./httpRouter": "./src/entrypoints/httpRouter/index.ts",
|
||||
"./lifecycle": "./src/entrypoints/lifecycle/index.ts",
|
||||
@@ -54,6 +55,9 @@
|
||||
"discovery": [
|
||||
"src/entrypoints/discovery/index.ts"
|
||||
],
|
||||
"health": [
|
||||
"src/entrypoints/health/index.ts"
|
||||
],
|
||||
"httpAuth": [
|
||||
"src/entrypoints/httpAuth/index.ts"
|
||||
],
|
||||
|
||||
+27
-26
@@ -1,3 +1,6 @@
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
import { DefaultHealthService } from './healthServiceFactory';
|
||||
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
@@ -13,22 +16,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
import request from 'supertest';
|
||||
import { createHealthRouter } from './createHealthRouter';
|
||||
import express from 'express';
|
||||
|
||||
describe('createHealthRouter', () => {
|
||||
describe('DefaultHealthService', () => {
|
||||
describe('readiness', () => {
|
||||
it(`should return a 500 response if the server hasn't started yet`, async () => {
|
||||
const hc = createHealthRouter({
|
||||
const service = new DefaultHealthService({
|
||||
lifecycle: mockServices.rootLifecycle.mock(),
|
||||
});
|
||||
const app = express().use(hc);
|
||||
|
||||
const response = await request(app).get('/v1/readiness');
|
||||
expect(response.status).toBe(500);
|
||||
await expect(service.getReadiness()).resolves.toEqual({ status: 503 });
|
||||
});
|
||||
|
||||
it('should return 200 if the server has started', async () => {
|
||||
@@ -38,12 +32,16 @@ describe('createHealthRouter', () => {
|
||||
fn => (mockServerStartedFn = fn),
|
||||
);
|
||||
|
||||
const hc = createHealthRouter({ lifecycle });
|
||||
const app = express().use(hc);
|
||||
const service = new DefaultHealthService({
|
||||
lifecycle: mockServices.rootLifecycle.mock(),
|
||||
});
|
||||
|
||||
mockServerStartedFn();
|
||||
const response = await request(app).get('/v1/readiness').expect(200);
|
||||
expect(response.body).toEqual({ status: 'ok' });
|
||||
|
||||
await expect(service.getReadiness()).resolves.toEqual({
|
||||
status: 200,
|
||||
payload: { status: 'ok' },
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return a 500 response if the server has stopped`, async () => {
|
||||
@@ -57,25 +55,28 @@ describe('createHealthRouter', () => {
|
||||
fn => (mockServerStoppedFn = fn),
|
||||
);
|
||||
|
||||
const hc = createHealthRouter({ lifecycle });
|
||||
const app = express().use(hc);
|
||||
const service = new DefaultHealthService({
|
||||
lifecycle: mockServices.rootLifecycle.mock(),
|
||||
});
|
||||
|
||||
mockServerStartedFn();
|
||||
mockServerStoppedFn();
|
||||
const response = await request(app).get('/v1/readiness');
|
||||
expect(response.status).toBe(500);
|
||||
await expect(service.getReadiness()).resolves.toEqual({
|
||||
status: 503,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('liveness', () => {
|
||||
it('should return 200 if the server has started', async () => {
|
||||
const lifecycle = mockServices.rootLifecycle.mock();
|
||||
const service = new DefaultHealthService({
|
||||
lifecycle: mockServices.rootLifecycle.mock(),
|
||||
});
|
||||
|
||||
const hc = createHealthRouter({ lifecycle });
|
||||
const app = express().use(hc);
|
||||
|
||||
const response = await request(app).get('/v1/liveness').expect(200);
|
||||
expect(response.body).toEqual({ status: 'ok' });
|
||||
await expect(service.getLiveness()).resolves.toEqual({
|
||||
status: 200,
|
||||
payload: { status: 'ok' },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
HealthService,
|
||||
RootLifecycleService,
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} 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.
|
||||
*/
|
||||
|
||||
/** @internal */
|
||||
export class DefaultHealthService implements HealthService {
|
||||
#isRunning = false;
|
||||
|
||||
constructor(readonly options: { lifecycle: RootLifecycleService }) {
|
||||
options.lifecycle.addStartupHook(() => {
|
||||
this.#isRunning = true;
|
||||
});
|
||||
options.lifecycle.addShutdownHook(() => {
|
||||
this.#isRunning = false;
|
||||
});
|
||||
}
|
||||
|
||||
async getLiveness(): Promise<{ status: number; payload?: any }> {
|
||||
return { status: 200, payload: { status: 'ok' } };
|
||||
}
|
||||
async getReadiness(): Promise<{ status: number; payload?: any }> {
|
||||
if (!this.#isRunning) {
|
||||
return {
|
||||
status: 503,
|
||||
payload: { message: 'Backend has not started yet', status: 'error' },
|
||||
};
|
||||
}
|
||||
|
||||
return { status: 200, payload: { status: 'ok' } };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const healthServiceFactory = createServiceFactory({
|
||||
service: coreServices.health,
|
||||
deps: {
|
||||
lifecycle: coreServices.rootLifecycle,
|
||||
},
|
||||
async factory({ lifecycle }) {
|
||||
return new DefaultHealthService({ lifecycle });
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user