diff --git a/packages/backend-defaults/src/entrypoints/systemMetadata/lib/DefaultSystemMetadataService.ts b/packages/backend-defaults/src/entrypoints/systemMetadata/lib/DefaultSystemMetadataService.ts index 1de37c3b42..96fa8b1820 100644 --- a/packages/backend-defaults/src/entrypoints/systemMetadata/lib/DefaultSystemMetadataService.ts +++ b/packages/backend-defaults/src/entrypoints/systemMetadata/lib/DefaultSystemMetadataService.ts @@ -27,12 +27,9 @@ import { * @alpha */ export class DefaultSystemMetadataService implements SystemMetadataService { - private readonly logger: LoggerService; - private readonly config: RootConfigService; - constructor(options: { logger: LoggerService; config: RootConfigService }) { - this.logger = options.logger; - this.config = options.config; - } + constructor( + private options: { logger: LoggerService; config: RootConfigService }, + ) {} public static create(pluginEnv: { logger: LoggerService; @@ -41,24 +38,16 @@ export class DefaultSystemMetadataService implements SystemMetadataService { return new DefaultSystemMetadataService(pluginEnv); } - listInstances() { + async listInstances() { const endpoints = - this.config.getOptionalConfigArray('discovery.instances') ?? []; + this.options.config.getOptionalConfigArray('discovery.instances') ?? []; const instances: BackstageInstance[] = []; for (const endpoint of endpoints) { const baseUrl = endpoint.getOptionalString('baseUrl'); if (baseUrl) { - this.logger.info(`Found instance at ${baseUrl}`); instances.push({ url: baseUrl }); - } else { - this.logger.warn( - `Instance ${endpoint.get( - 'target', - )} is missing a 'baseUrl' property. This is required for the system metadata service.`, - ); } } - this.logger.info(`Found ${instances.length} instances.`); - return Promise.resolve(instances); + return instances; } } diff --git a/packages/backend-defaults/src/entrypoints/systemMetadata/lib/createSystemMetadataRouter.ts b/packages/backend-defaults/src/entrypoints/systemMetadata/lib/createSystemMetadataRouter.ts new file mode 100644 index 0000000000..4750ed07db --- /dev/null +++ b/packages/backend-defaults/src/entrypoints/systemMetadata/lib/createSystemMetadataRouter.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2025 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 { LoggerService } from '@backstage/backend-plugin-api'; +import { + BackendFeatureMeta, + SystemMetadataService, +} from '@backstage/backend-plugin-api/alpha'; +import Router from 'express-promise-router'; + +export async function createSystemMetadataRouter(options: { + logger: LoggerService; + systemMetadata: SystemMetadataService; +}) { + const { logger, systemMetadata } = options; + logger.info( + `Instances in this system: ${JSON.stringify( + await systemMetadata.listInstances(), + )}`, + ); + + const router = Router(); + + router.get('/instances', async (_, res) => { + res.json(await systemMetadata.listInstances()); + }); + + router.get('/features/installed', async (_, res) => { + const instances = await systemMetadata.listInstances(); + const featurePromises = await Promise.allSettled( + instances.map(async instance => { + const response = await fetch( + `${instance.url}/.backstage/instanceMetadata/v1/features/installed`, + ); + if (response.ok) { + return { instance, response: await response.json() }; + } + throw new Error( + `Failed to fetch installed features from ${instance.url}`, + ); + }), + ); + const pluginByInstance: Record = {}; + for (const result of featurePromises) { + if (result.status !== 'fulfilled') { + logger.error(`Failed to fetch installed features: ${result.reason}`); + continue; + } + const instance = result.value.instance; + const installedFeatures = result.value.response + .items as BackendFeatureMeta[]; + for (const feature of installedFeatures) { + if (feature.type === 'plugin') { + if (!pluginByInstance[feature.pluginId]) { + pluginByInstance[feature.pluginId] = []; + } + pluginByInstance[feature.pluginId].push(instance.url); + } + } + } + res.json(pluginByInstance); + }); + + return router; +} diff --git a/packages/backend-defaults/src/entrypoints/systemMetadata/systemMetadataServiceFactory.ts b/packages/backend-defaults/src/entrypoints/systemMetadata/systemMetadataServiceFactory.ts index 59ff9f4253..2eaec11caa 100644 --- a/packages/backend-defaults/src/entrypoints/systemMetadata/systemMetadataServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/systemMetadata/systemMetadataServiceFactory.ts @@ -20,6 +20,7 @@ import { } from '@backstage/backend-plugin-api'; import { DefaultSystemMetadataService } from './lib/DefaultSystemMetadataService'; import { systemMetadataServiceRef } from '@backstage/backend-plugin-api/alpha'; +import { createSystemMetadataRouter } from './lib/createSystemMetadataRouter'; /** * Metadata about an entire Backstage system, a collection of Backstage instances. @@ -29,13 +30,19 @@ import { systemMetadataServiceRef } from '@backstage/backend-plugin-api/alpha'; export const systemMetadataServiceFactory = createServiceFactory({ service: systemMetadataServiceRef, deps: { - logger: coreServices.logger, + logger: coreServices.rootLogger, config: coreServices.rootConfig, + httpRouter: coreServices.rootHttpRouter, }, - async factory({ logger, config }) { - return DefaultSystemMetadataService.create({ + async factory({ logger, config, httpRouter }) { + const systemMetadata = DefaultSystemMetadataService.create({ logger, config, }); + + const router = await createSystemMetadataRouter({ systemMetadata, logger }); + + httpRouter.use('/.backstage/systemMetadata/v1', router); + return systemMetadata; }, }); diff --git a/packages/backend-plugin-api/src/alpha/index.ts b/packages/backend-plugin-api/src/alpha/index.ts index 14bb24ecfe..2262c5a3f1 100644 --- a/packages/backend-plugin-api/src/alpha/index.ts +++ b/packages/backend-plugin-api/src/alpha/index.ts @@ -34,6 +34,7 @@ export const systemMetadataServiceRef = createServiceRef< import('./services/definitions/SystemMetadataService').SystemMetadataService >({ id: 'core.systemMetadata', + scope: 'root', }); export type { diff --git a/packages/backend-split/src/experimental/features.http b/packages/backend-split/src/experimental/features.http index 34c8703bcd..bb9287f61c 100644 --- a/packages/backend-split/src/experimental/features.http +++ b/packages/backend-split/src/experimental/features.http @@ -1,6 +1,7 @@ -// Not working yet, the metadata plugin HTTP APIs need to be moved to rootHttpRouter. -# GET http://localhost:7007/.backstage/systemInfo/features/installed +// Test requests for the split backend to test the system metadata service :) +// The 2 below requests should return the same data. +GET http://localhost:7007/.backstage/systemMetadata/v1/features/installed ### -GET http://localhost:7008/.backstage/systemInfo/features/installed \ No newline at end of file +GET http://localhost:7008/.backstage/systemMetadata/v1/features/installed \ No newline at end of file diff --git a/packages/backend-split/src/experimental/instanceMetadata.ts b/packages/backend-split/src/experimental/instanceMetadata.ts deleted file mode 100644 index abdb53dfeb..0000000000 --- a/packages/backend-split/src/experimental/instanceMetadata.ts +++ /dev/null @@ -1,48 +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, - createBackendPlugin, -} from '@backstage/backend-plugin-api'; - -// Example usage of the instance metadata service to log the installed plugins. -export default createBackendPlugin({ - pluginId: 'instance-metadata', - register(env) { - env.registerInit({ - deps: { - instanceMetadata: coreServices.rootInstanceMetadata, - logger: coreServices.logger, - httpRouter: coreServices.rootHttpRouter, - }, - async init({ instanceMetadata, logger, httpRouter }) { - logger.info( - `Installed plugins on this instance: ${plugins - .map(e => e.pluginId) - .join(', ')}`, - ); - - const router = Router(); - - router.get('/features/installed', (_, res) => { - res.json({ items: instanceMetadata.getInstalledFeatures() }); - }); - - httpRouter.use('/.backstage/instanceInfo', router); - }, - }); - }, -}); diff --git a/packages/backend-split/src/experimental/systemMetadata.ts b/packages/backend-split/src/experimental/systemMetadata.ts deleted file mode 100644 index e567903356..0000000000 --- a/packages/backend-split/src/experimental/systemMetadata.ts +++ /dev/null @@ -1,93 +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, - createBackendPlugin, -} from '@backstage/backend-plugin-api'; -import { - BackendFeatureMeta, - systemMetadataServiceRef, -} from '@backstage/backend-plugin-api/alpha'; -import Router from 'express-promise-router'; - -// Example usage of the instance metadata service to log the list of instances and a small HTTP API. -export default createBackendPlugin({ - pluginId: 'system-metadata', - register(env) { - env.registerInit({ - deps: { - systemMetadata: systemMetadataServiceRef, - logger: coreServices.logger, - httpRouter: coreServices.rootHttpRouter, - }, - async init({ systemMetadata, logger, httpRouter }) { - logger.info( - `Instances in this system: ${JSON.stringify( - await systemMetadata.listInstances(), - )}`, - ); - - const router = Router(); - - router.get('/instances', async (_, res) => { - res.json(await systemMetadata.listInstances()); - }); - - router.get('/features/installed', async (_, res) => { - const instances = await systemMetadata.listInstances(); - const featurePromises = await Promise.allSettled( - instances.map(async instance => { - const response = await fetch( - `${instance.url}/.backstage/instanceInfo/features/installed`, - ); - if (response.ok) { - return { instance, response: await response.json() }; - } - throw new Error( - `Failed to fetch installed features from ${instance.url}`, - ); - }), - ); - const pluginByInstance: Record = {}; - for (const result of featurePromises) { - if (result.status !== 'fulfilled') { - logger.error( - `Failed to fetch installed features: ${result.reason}`, - ); - continue; - } - const instance = result.value.instance; - const installedFeatures = result.value.response - .items as BackendFeatureMeta[]; - for (const feature of installedFeatures) { - if (feature.type === 'plugin') { - if (!pluginByInstance[feature.pluginId]) { - pluginByInstance[feature.pluginId] = []; - } - pluginByInstance[feature.pluginId].push( - `${instance.url}/api/${feature.pluginId}`, - ); - } - } - } - res.json(pluginByInstance); - }); - - httpRouter.use('/.backstage/systemInfo', router); - }, - }); - }, -}); diff --git a/packages/backend-split/src/index.ts b/packages/backend-split/src/index.ts index 52b7559867..2238713ffe 100644 --- a/packages/backend-split/src/index.ts +++ b/packages/backend-split/src/index.ts @@ -30,8 +30,6 @@ backend.add( ); backend.add(import('@backstage/plugin-permission-backend')); -backend.add(import('./experimental/instanceMetadata')); -backend.add(import('./experimental/systemMetadata')); backend.add(systemMetadataServiceFactory); backend.start();