simplify implementation

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
aramissennyeydd
2025-11-12 15:14:50 -05:00
parent d924a5cd4f
commit 5ea6957a6d
3 changed files with 62 additions and 90 deletions
@@ -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 });
@@ -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<string>;
@@ -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<string, { internal?: string; external?: string }[]> =
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<string, Resolver> = 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(
@@ -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;
}