Skip to content

Commit 21b17fa

Browse files
committed
feat(backend): add service factories by default
This change explicitely adds the default service factories to the backend statically to prevent dynamic plugins from being able to override them by default. It's possible to override each statically added service factory via an environment variable derived from the service factory ID. So for example to add a "core.rootHttpService" service factory configuration from a dynamic plugin, set ENABLE_CORE_ROOTHTTPSERVICE_OVERRIDE to "true". This change also adds a logger to the backend main. Finally, a unit test has been added that checks the installed backend-defaults value for the defaultServiceFactories list against what this change adds to catch future regressions. Signed-off-by: Stan Lewis <[email protected]>
1 parent 59a5fee commit 21b17fa

File tree

6 files changed

+146
-1
lines changed

6 files changed

+146
-1
lines changed

packages/backend/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@backstage/plugin-catalog-backend-module-openapi": "0.2.3",
4141
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.1",
4242
"@backstage/plugin-events-backend": "0.3.15",
43+
"@backstage/plugin-events-node": "^0.4.4",
4344
"@backstage/plugin-proxy-backend": "0.5.7",
4445
"@backstage/plugin-scaffolder-backend": "1.26.2",
4546
"@backstage/plugin-search-backend": "1.6.1",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ServiceFactory } from '@backstage/backend-plugin-api';
2+
3+
import { defaultServiceFactories } from './defaultServiceFactories';
4+
5+
// explicitly check this against the module inside the installed package
6+
const {
7+
defaultServiceFactories: upstreamDefaultServiceFactories,
8+
// eslint-disable-next-line
9+
} = require('../../../node_modules/@backstage/backend-defaults/dist/CreateBackend.cjs.js');
10+
11+
function findDifference(a1: string[], a2: string[]) {
12+
const set = new Set(a2);
13+
return a1.filter(i => !set.has(i));
14+
}
15+
16+
function findSymmetricDifference(a1: string[], a2: string[]) {
17+
return [...new Set([...findDifference(a1, a2), ...findDifference(a2, a1)])];
18+
}
19+
20+
/**
21+
* Validate that the installed backend-defaults package contains the expected
22+
* list of default service factories. A failure in this test indicates that
23+
* either the export was removed, the list was moved, or the list in
24+
* "defaultServiceFactories" should be updated.
25+
*/
26+
describe('Default service factory list comparison', () => {
27+
it('Should produce an expected difference of service factories as compared to the upstream implementation', () => {
28+
const upstreamServiceFactoryIds = upstreamDefaultServiceFactories.map(
29+
(serviceFactory: ServiceFactory) => serviceFactory.service.id,
30+
);
31+
const serviceFactoryIds = defaultServiceFactories.map(
32+
(serviceFactory: ServiceFactory) => serviceFactory.service.id,
33+
);
34+
expect(
35+
findSymmetricDifference(upstreamServiceFactoryIds, serviceFactoryIds),
36+
).toEqual(['core.rootLogger']);
37+
});
38+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { authServiceFactory } from '@backstage/backend-defaults/auth';
2+
import { cacheServiceFactory } from '@backstage/backend-defaults/cache';
3+
import { databaseServiceFactory } from '@backstage/backend-defaults/database';
4+
import { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';
5+
import { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';
6+
import { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';
7+
import { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';
8+
import { loggerServiceFactory } from '@backstage/backend-defaults/logger';
9+
import { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';
10+
import { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';
11+
import { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';
12+
import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';
13+
import { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';
14+
import { WinstonLogger } from '@backstage/backend-defaults/rootLogger';
15+
import { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';
16+
import { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';
17+
import { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';
18+
import { eventsServiceFactory } from '@backstage/plugin-events-node';
19+
20+
/**
21+
* Service factories that are added to the backend statically by default. This
22+
* should be kept up to date with the upstream package code, which is currently
23+
* not exported.
24+
*/
25+
export const defaultServiceFactories = [
26+
authServiceFactory,
27+
cacheServiceFactory,
28+
rootConfigServiceFactory,
29+
databaseServiceFactory,
30+
discoveryServiceFactory,
31+
httpAuthServiceFactory,
32+
httpRouterServiceFactory,
33+
lifecycleServiceFactory,
34+
loggerServiceFactory,
35+
permissionsServiceFactory,
36+
rootHealthServiceFactory,
37+
rootHttpRouterServiceFactory,
38+
rootLifecycleServiceFactory,
39+
// rootLoggerServiceFactory,
40+
schedulerServiceFactory,
41+
userInfoServiceFactory,
42+
urlReaderServiceFactory,
43+
eventsServiceFactory,
44+
];
45+
46+
export const getDefaultServiceFactories = ({
47+
logger,
48+
}: {
49+
logger: WinstonLogger;
50+
}) => {
51+
return defaultServiceFactories.filter(serviceFactory => {
52+
const envName = `ENABLE_${serviceFactory.service.id.toLocaleUpperCase().replace('.', '_')}_OVERRIDE`;
53+
if ((process.env[envName] || '').toLocaleLowerCase() !== 'true') {
54+
logger.debug(
55+
`Adding service factory "${serviceFactory.service.id}", to override set "${envName}" to "true"`,
56+
);
57+
return true;
58+
}
59+
logger.warn(
60+
`Allowing override for service factory "${serviceFactory.service.id}"`,
61+
);
62+
return false;
63+
});
64+
};

packages/backend/src/index.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,31 @@ import { PackageRoles } from '@backstage/cli-node';
55
import * as path from 'path';
66

77
import { configureCorporateProxyAgent } from './corporate-proxy';
8+
import { getDefaultServiceFactories } from './defaultServiceFactories';
89
import { CommonJSModuleLoader } from './loader';
9-
import { transports } from './logger';
10+
import { createStaticLogger, transports } from './logger';
1011
import {
1112
healthCheckPlugin,
1213
pluginIDProviderService,
1314
rbacDynamicPluginsProvider,
1415
} from './modules';
1516

17+
// Create a logger to cover logging static initialization tasks
18+
const staticLogger = createStaticLogger({ service: 'developer-hub-init' });
19+
staticLogger.info('Starting Developer Hub backend');
20+
1621
// RHIDP-2217: adds support for corporate proxy
1722
configureCorporateProxyAgent();
1823

1924
const backend = createBackend();
2025

26+
const defaultServiceFactories = getDefaultServiceFactories({
27+
logger: staticLogger,
28+
});
29+
defaultServiceFactories.forEach(serviceFactory => {
30+
backend.add(serviceFactory);
31+
});
32+
2133
backend.add(
2234
dynamicPluginsFeatureLoader({
2335
schemaLocator(pluginPackage) {

packages/backend/src/logger/customLogger.ts

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { WinstonLogger } from '@backstage/backend-defaults/rootLogger';
12
import type { Config } from '@backstage/config';
23

34
import * as winston from 'winston';
@@ -73,3 +74,18 @@ export const transports = {
7374
];
7475
},
7576
};
77+
78+
export const createStaticLogger = ({ service }: { service: string }) => {
79+
const logger = WinstonLogger.create({
80+
meta: {
81+
service,
82+
},
83+
level: process.env.LOG_LEVEL || 'info',
84+
format:
85+
process.env.NODE_ENV === 'production'
86+
? defaultFormat
87+
: WinstonLogger.colorFormat(),
88+
transports: transports.log,
89+
});
90+
return logger;
91+
};

yarn.lock

+14
Original file line numberDiff line numberDiff line change
@@ -6745,6 +6745,19 @@ __metadata:
67456745
languageName: node
67466746
linkType: hard
67476747

6748+
"@backstage/plugin-events-node@npm:^0.4.4":
6749+
version: 0.4.4
6750+
resolution: "@backstage/plugin-events-node@npm:0.4.4"
6751+
dependencies:
6752+
"@backstage/backend-plugin-api": ^1.0.1
6753+
"@backstage/errors": ^1.2.4
6754+
"@backstage/types": ^1.1.1
6755+
cross-fetch: ^4.0.0
6756+
uri-template: ^2.0.0
6757+
checksum: e1d665686ae3d2462c40b773f9e005d092a18cd866624799a3174100f3b496d76e6a8abfde8888114ec346fbcb081edbc45e1b9be1e8f875e13ad62f81121a68
6758+
languageName: node
6759+
linkType: hard
6760+
67486761
"@backstage/plugin-home-react@npm:0.1.18, @backstage/plugin-home-react@npm:^0.1.15, @backstage/plugin-home-react@npm:^0.1.16, @backstage/plugin-home-react@npm:^0.1.18":
67496762
version: 0.1.18
67506763
resolution: "@backstage/plugin-home-react@npm:0.1.18"
@@ -22367,6 +22380,7 @@ __metadata:
2236722380
"@backstage/plugin-catalog-backend-module-openapi": 0.2.3
2236822381
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": 0.2.1
2236922382
"@backstage/plugin-events-backend": 0.3.15
22383+
"@backstage/plugin-events-node": ^0.4.4
2237022384
"@backstage/plugin-proxy-backend": 0.5.7
2237122385
"@backstage/plugin-scaffolder-backend": 1.26.2
2237222386
"@backstage/plugin-search-backend": 1.6.1

0 commit comments

Comments
 (0)