diff --git a/packages/backend-defaults/src/alpha/entrypoints/rootSystemMetadata/lib/DefaultRootSystemMetadataService.ts b/packages/backend-defaults/src/alpha/entrypoints/rootSystemMetadata/lib/DefaultRootSystemMetadataService.ts index 69bd676fe7..edbffdfb0e 100644 --- a/packages/backend-defaults/src/alpha/entrypoints/rootSystemMetadata/lib/DefaultRootSystemMetadataService.ts +++ b/packages/backend-defaults/src/alpha/entrypoints/rootSystemMetadata/lib/DefaultRootSystemMetadataService.ts @@ -19,11 +19,17 @@ import { RootConfigService, RootInstanceMetadataService, } from '@backstage/backend-plugin-api'; -import { HostDiscovery } from '../../../../entrypoints/discovery'; import { RootSystemMetadataService, RootSystemMetadataServicePluginInfo, } from '@backstage/backend-plugin-api/alpha'; +import { getEndpoints } from '../../../../entrypoints/discovery/parsing'; +import { Config } from '@backstage/config'; + +function getPlugins(config: Config): string[] { + const endpoints = getEndpoints(config); + return Array.from(new Set(endpoints.flatMap(endpoint => endpoint.plugins))); +} /** * @alpha @@ -31,20 +37,17 @@ import { export class DefaultRootSystemMetadataService implements RootSystemMetadataService { - #hostDiscovery: HostDiscovery; + #plugins: string[]; #instanceMetadata: RootInstanceMetadataService; constructor(options: { logger: LoggerService; config: RootConfigService; instanceMetadata: RootInstanceMetadataService; }) { - this.#hostDiscovery = HostDiscovery.fromConfig(options.config, { - logger: options.logger, - }); - options.config.subscribe?.(() => { - this.#hostDiscovery = HostDiscovery.fromConfig(options.config, { - logger: options.logger, - }); + const { config } = options; + this.#plugins = getPlugins(config); + config.subscribe?.(() => { + this.#plugins = getPlugins(config); }); this.#instanceMetadata = options.instanceMetadata; } @@ -60,11 +63,7 @@ export class DefaultRootSystemMetadataService public async getInstalledPlugins(): Promise< RootSystemMetadataServicePluginInfo[] > { - const resolutions = await this.#hostDiscovery.listResolutions(); - const plugins = []; - for (const pluginId of resolutions.keys()) { - plugins.push({ pluginId }); - } + const plugins = this.#plugins.map(pluginId => ({ pluginId })); for (const plugin of await this.#instanceMetadata.getInstalledPlugins()) { plugins.push({ pluginId: plugin.pluginId }); diff --git a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts index 34b521b310..18ed72a085 100644 --- a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts +++ b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts @@ -23,6 +23,7 @@ import { import { readHttpServerOptions } from '../rootHttpRouter/http/config'; import { SrvResolvers } from './SrvResolvers'; import { trimEnd } from 'lodash'; +import { getEndpoints } from './parsing'; type Resolver = (pluginId: string) => Promise; @@ -150,11 +151,6 @@ export class HostDiscovery implements DiscoveryService { throw new Error('Not initialized'); }; - #resolutions: Map< - string, - Set<{ hash: string; target: { internal?: string; external?: string } }> - > = new Map(); - static fromConfig(config: RootConfigService, options?: HostDiscoveryOptions) { const discovery = new HostDiscovery(new SrvResolvers()); @@ -198,23 +194,12 @@ export class HostDiscovery implements DiscoveryService { return await resolver(pluginId); } - async listResolutions() { - const _targets: Map = - new Map(); - for (const [pluginId, targets] of this.#resolutions.entries()) { - const currentTargets = [...targets.values()].map(({ target }) => ({ - ...target, - })); - if (_targets.has(pluginId)) { - _targets.set(pluginId, [..._targets.get(pluginId)!, ...currentTargets]); - } else { - _targets.set(pluginId, currentTargets); - } - } - return _targets; + #updateResolvers(config: Config, defaultEndpoints?: HostDiscoveryEndpoint[]) { + this.#updateFallbackResolvers(config); + this.#updatePluginResolvers(config, defaultEndpoints); } - #getInstanceAddress(config: Config) { + #updateFallbackResolvers(config: Config) { const backendBaseUrl = trimEnd(config.getString('backend.baseUrl'), '/'); const { @@ -236,26 +221,12 @@ export class HostDiscovery implements DiscoveryService { host = `[${host}]`; } - return { - internal: `${protocol}://${host}:${listenPort}`, - external: backendBaseUrl, - }; - } - - #updateResolvers(config: Config, defaultEndpoints?: HostDiscoveryEndpoint[]) { - this.#updateFallbackResolvers(config); - this.#updatePluginResolvers(config, defaultEndpoints); - } - - #updateFallbackResolvers(config: Config) { - const { internal, external } = this.#getInstanceAddress(config); - this.#internalFallbackResolver = this.#makeResolver( - `${internal}/api/{{pluginId}}`, + `${protocol}://${host}:${listenPort}/api/{{pluginId}}`, false, ); this.#externalFallbackResolver = this.#makeResolver( - `${external}/api/{{pluginId}}`, + `${backendBaseUrl}/api/{{pluginId}}`, false, ); } @@ -268,25 +239,7 @@ export class HostDiscovery implements DiscoveryService { const endpoints = defaultEndpoints?.slice() ?? []; // Allow config to override the default endpoints - const endpointConfigs = config.getOptionalConfigArray( - 'discovery.endpoints', - ); - for (const endpointConfig of endpointConfigs ?? []) { - if (typeof endpointConfig.get('target') === 'string') { - endpoints.push({ - target: endpointConfig.getString('target'), - plugins: endpointConfig.getStringArray('plugins'), - }); - } else { - endpoints.push({ - target: { - internal: endpointConfig.getOptionalString('target.internal'), - external: endpointConfig.getOptionalString('target.external'), - }, - plugins: endpointConfig.getStringArray('plugins'), - }); - } - } + endpoints.push(...getEndpoints(config)); // Build up a new set of resolvers const internalResolvers: Map = new Map(); @@ -294,7 +247,6 @@ export class HostDiscovery implements DiscoveryService { for (const { target, plugins } of endpoints) { let internalResolver: Resolver | undefined; let externalResolver: Resolver | undefined; - this.#addResolution(target, plugins); if (typeof target === 'string') { internalResolver = externalResolver = this.#makeResolver(target, false); @@ -324,26 +276,6 @@ export class HostDiscovery implements DiscoveryService { this.#externalResolvers = externalResolvers; } - #addResolution( - target: string | { internal?: string; external?: string }, - plugins: string[], - ) { - for (const pluginId of plugins) { - if (!this.#resolutions.has(pluginId)) { - this.#resolutions.set(pluginId, new Set()); - } - const standardizedTarget = - typeof target === 'string' - ? { external: target, internal: target } - : target; - const matchingResolution = this.#resolutions.get(pluginId)!; - const hash = JSON.stringify(standardizedTarget); - if (![...matchingResolution.values()].some(e => e.hash === hash)) { - matchingResolution.add({ target: standardizedTarget, hash }); - } - } - } - #makeResolver(urlPattern: string, allowSrv: boolean): Resolver { const withPluginId = (pluginId: string, url: string) => { return url.replace( diff --git a/packages/backend-defaults/src/entrypoints/discovery/parsing.ts b/packages/backend-defaults/src/entrypoints/discovery/parsing.ts new file mode 100644 index 0000000000..3a02fc4f51 --- /dev/null +++ b/packages/backend-defaults/src/entrypoints/discovery/parsing.ts @@ -0,0 +1,41 @@ +/* + * 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 { Config } from '@backstage/config'; +import type { HostDiscoveryEndpoint } from './HostDiscovery'; + +export function getEndpoints(config: Config): HostDiscoveryEndpoint[] { + const endpoints: HostDiscoveryEndpoint[] = []; + // Allow config to override the default endpoints + const endpointConfigs = config.getOptionalConfigArray('discovery.endpoints'); + for (const endpointConfig of endpointConfigs ?? []) { + if (typeof endpointConfig.get('target') === 'string') { + endpoints.push({ + target: endpointConfig.getString('target'), + plugins: endpointConfig.getStringArray('plugins'), + }); + } else { + endpoints.push({ + target: { + internal: endpointConfig.getOptionalString('target.internal'), + external: endpointConfig.getOptionalString('target.external'), + }, + plugins: endpointConfig.getStringArray('plugins'), + }); + } + } + return endpoints; +}