Merge pull request #27921 from aramissennyeydd/sennyeya/host-discovery
feat: add an instance metadata experimental service
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/backend-app-api': minor
|
||||
'@backstage/backend-plugin-api': minor
|
||||
---
|
||||
|
||||
**EXPERIMENTAL**: Adds a new `instanceMetadataService` to hold information about a specific backend instance.
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
createBackendFeatureLoader,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { BackendInitializer } from './BackendInitializer';
|
||||
import { instanceMetadataServiceRef } from '@backstage/backend-plugin-api/alpha';
|
||||
|
||||
class MockLogger {
|
||||
debug() {}
|
||||
@@ -724,4 +725,59 @@ describe('BackendInitializer', () => {
|
||||
|
||||
await init.start();
|
||||
});
|
||||
|
||||
it('should properly add plugins + modules to the instance metadata service', async () => {
|
||||
expect.assertions(1);
|
||||
const backend = new BackendInitializer(baseFactories);
|
||||
const plugin = createBackendPlugin({
|
||||
pluginId: 'test',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {},
|
||||
async init() {},
|
||||
});
|
||||
},
|
||||
});
|
||||
const instanceMetadataPlugin = createBackendPlugin({
|
||||
pluginId: 'instance-metadata',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {
|
||||
instanceMetadata: instanceMetadataServiceRef,
|
||||
},
|
||||
async init({ instanceMetadata }) {
|
||||
expect(instanceMetadata.getInstalledFeatures()).toEqual([
|
||||
{
|
||||
pluginId: 'test',
|
||||
type: 'plugin',
|
||||
},
|
||||
{
|
||||
pluginId: 'test',
|
||||
moduleId: 'test',
|
||||
type: 'module',
|
||||
},
|
||||
{
|
||||
pluginId: 'instance-metadata',
|
||||
type: 'plugin',
|
||||
},
|
||||
]);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
const module = createBackendModule({
|
||||
pluginId: 'test',
|
||||
moduleId: 'test',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {},
|
||||
async init() {},
|
||||
});
|
||||
},
|
||||
});
|
||||
backend.add(plugin);
|
||||
backend.add(module);
|
||||
backend.add(instanceMetadataPlugin);
|
||||
await backend.start();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ServiceFactory,
|
||||
LifecycleService,
|
||||
RootLifecycleService,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { ServiceOrExtensionPoint } from './types';
|
||||
// Direct internal import to avoid duplication
|
||||
@@ -34,7 +35,11 @@ import type {
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types';
|
||||
import { ForwardedError, ConflictError } from '@backstage/errors';
|
||||
import { featureDiscoveryServiceRef } from '@backstage/backend-plugin-api/alpha';
|
||||
import {
|
||||
instanceMetadataServiceRef,
|
||||
featureDiscoveryServiceRef,
|
||||
BackendFeatureMeta,
|
||||
} from '@backstage/backend-plugin-api/alpha';
|
||||
import { DependencyGraph } from '../lib/DependencyGraph';
|
||||
import { ServiceRegistry } from './ServiceRegistry';
|
||||
import { createInitializationLogger } from './createInitializationLogger';
|
||||
@@ -96,6 +101,39 @@ const instanceRegistry = new (class InstanceRegistry {
|
||||
};
|
||||
})();
|
||||
|
||||
function createInstanceMetadataServiceFactory(
|
||||
registrations: InternalBackendRegistrations[],
|
||||
) {
|
||||
const installedFeatures = registrations
|
||||
.map(registration => {
|
||||
if (registration.featureType === 'registrations') {
|
||||
return registration
|
||||
.getRegistrations()
|
||||
.map(feature => {
|
||||
if (feature.type === 'plugin') {
|
||||
return { type: 'plugin', pluginId: feature.pluginId };
|
||||
} else if (feature.type === 'module') {
|
||||
return {
|
||||
type: 'module',
|
||||
pluginId: feature.pluginId,
|
||||
moduleId: feature.moduleId,
|
||||
};
|
||||
}
|
||||
// Ignore unknown feature types.
|
||||
return undefined;
|
||||
})
|
||||
.filter(Boolean) as BackendFeatureMeta[];
|
||||
}
|
||||
return [];
|
||||
})
|
||||
.flat();
|
||||
return createServiceFactory({
|
||||
service: instanceMetadataServiceRef,
|
||||
deps: {},
|
||||
factory: async () => ({ getInstalledFeatures: () => installedFeatures }),
|
||||
});
|
||||
}
|
||||
|
||||
export class BackendInitializer {
|
||||
#startPromise?: Promise<void>;
|
||||
#stopPromise?: Promise<void>;
|
||||
@@ -210,6 +248,10 @@ export class BackendInitializer {
|
||||
|
||||
await this.#applyBackendFeatureLoaders(this.#registeredFeatureLoaders);
|
||||
|
||||
this.#serviceRegistry.add(
|
||||
createInstanceMetadataServiceFactory(this.#registrations),
|
||||
);
|
||||
|
||||
// Initialize all root scoped services
|
||||
await this.#serviceRegistry.initializeEagerServicesWithScope('root');
|
||||
|
||||
|
||||
@@ -6,6 +6,18 @@
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type BackendFeatureMeta =
|
||||
| {
|
||||
type: 'plugin';
|
||||
pluginId: string;
|
||||
}
|
||||
| {
|
||||
type: 'module';
|
||||
pluginId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface FeatureDiscoveryService {
|
||||
// (undocumented)
|
||||
@@ -21,5 +33,18 @@ export const featureDiscoveryServiceRef: ServiceRef<
|
||||
'singleton'
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface InstanceMetadataService {
|
||||
// (undocumented)
|
||||
getInstalledFeatures: () => BackendFeatureMeta[];
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export const instanceMetadataServiceRef: ServiceRef<
|
||||
InstanceMetadataService,
|
||||
'plugin',
|
||||
'singleton'
|
||||
>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -34,3 +34,19 @@ export const featureDiscoveryServiceRef =
|
||||
id: 'core.featureDiscovery',
|
||||
scope: 'root',
|
||||
});
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL: Instance metadata service.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const instanceMetadataServiceRef = createServiceRef<
|
||||
import('./services/definitions/InstanceMetadataService').InstanceMetadataService
|
||||
>({
|
||||
id: 'core.instanceMetadata',
|
||||
});
|
||||
|
||||
export type {
|
||||
BackendFeatureMeta,
|
||||
InstanceMetadataService,
|
||||
} from './services/definitions/InstanceMetadataService';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** @alpha */
|
||||
export type BackendFeatureMeta =
|
||||
| {
|
||||
type: 'plugin';
|
||||
pluginId: string;
|
||||
}
|
||||
| {
|
||||
type: 'module';
|
||||
pluginId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
|
||||
/** @alpha */
|
||||
export interface InstanceMetadataService {
|
||||
getInstalledFeatures: () => BackendFeatureMeta[];
|
||||
}
|
||||
@@ -59,5 +59,6 @@ backend.add(searchLoader);
|
||||
backend.add(import('@backstage/plugin-techdocs-backend'));
|
||||
backend.add(import('@backstage/plugin-signals-backend'));
|
||||
backend.add(import('@backstage/plugin-notifications-backend'));
|
||||
backend.add(import('./instanceMetadata'));
|
||||
|
||||
backend.start();
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 { instanceMetadataServiceRef } from '@backstage/backend-plugin-api/alpha';
|
||||
|
||||
// Example usage of the instance metadata service to log the installed features.
|
||||
export default createBackendPlugin({
|
||||
pluginId: 'instance-metadata-logging',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
instanceMetadata: instanceMetadataServiceRef,
|
||||
logger: coreServices.logger,
|
||||
},
|
||||
async init({ instanceMetadata, logger }) {
|
||||
logger.info(
|
||||
`Installed features on this instance: ${instanceMetadata.getInstalledFeatures()}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user