Merge pull request #30915 from backstage/blam/refactor-buiklder

`kubernetes-backend`: Added the ability to get access to default implementations in extension points
This commit is contained in:
Ben Lambert
2025-08-19 10:15:31 +02:00
committed by GitHub
16 changed files with 805 additions and 700 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-kubernetes-backend': patch
'@backstage/plugin-kubernetes-node': patch
---
Added support for providing a factory to the extension points
+18 -3
View File
@@ -87,6 +87,7 @@ import {
ClusterDetails,
KubernetesClustersSupplier,
kubernetesClusterSupplierExtensionPoint,
kubernetesServiceLocatorExtensionPoint,
} from '@backstage/plugin-kubernetes-node';
export class CustomClustersSupplier implements KubernetesClustersSupplier {
@@ -119,12 +120,26 @@ export const kubernetesModuleCustomClusterDiscovery = createBackendModule({
register(env) {
env.registerInit({
deps: {
kubernetes: kubernetesClusterSupplierExtensionPoint,
clusterSupplier: kubernetesClusterSupplierExtensionPoint,
serviceLocator: kubernetesServiceLocatorExtensionPoint,
},
async init({ kubernetes }) {
kubernetes.addClusterSupplier(
async init({ clusterSupplier, serviceLocator }) {
// simple replace of the internal dependency
clusterSupplier.addClusterSupplier(
CustomClustersSupplier.create(Duration.fromObject({ minutes: 60 })),
);
// there's also the ability to get access to some of the default implementations of the extension points where
// neccessary:
serviceLocator.addServiceLocator(
async ({ getDefault, clusterSupplier }) => {
// get access to the default service locator:
const defaultImplementation = await getDefault();
// build your own with the clusterSupplier dependency:
return new MyNewServiceLocator({ clusterSupplier });
},
);
},
});
},
@@ -51,6 +51,7 @@ export class DispatchStrategy implements AuthenticationStrategy {
): Promise<KubernetesCredential> {
const authProvider =
clusterDetails.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];
if (this.strategyMap[authProvider]) {
return this.strategyMap[authProvider].getCredential(clusterDetails, auth);
}
@@ -0,0 +1,46 @@
/*
* 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,
RootConfigService,
} from '@backstage/backend-plugin-api';
import { AuthenticationStrategy } from '@backstage/plugin-kubernetes-node';
import { AksStrategy } from './AksStrategy';
import { AwsIamStrategy } from './AwsIamStrategy';
import { AzureIdentityStrategy } from './AzureIdentityStrategy';
import { GoogleStrategy } from './GoogleStrategy';
import { GoogleServiceAccountStrategy } from './GoogleServiceAccountStrategy';
import { AnonymousStrategy } from './AnonymousStrategy';
import { OidcStrategy } from './OidcStrategy';
import { ServiceAccountStrategy } from './ServiceAccountStrategy';
export const buildDefaultAuthStrategyMap = ({
logger,
config,
}: {
logger: LoggerService;
config: RootConfigService;
}): Map<string, AuthenticationStrategy> =>
new Map([
['aks', new AksStrategy()],
['aws', new AwsIamStrategy({ config })],
['azure', new AzureIdentityStrategy(logger)],
['google', new GoogleStrategy()],
['googleServiceAccount', new GoogleServiceAccountStrategy({ config })],
['localKubectlProxy', new AnonymousStrategy()],
['oidc', new OidcStrategy()],
['serviceAccount', new ServiceAccountStrategy()],
]);
@@ -22,7 +22,7 @@ import {
ClusterDetails,
KubernetesClustersSupplier,
} from '@backstage/plugin-kubernetes-node';
import { CATALOG_FILTER_EXISTS, CatalogApi } from '@backstage/catalog-client';
import { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';
import {
ANNOTATION_KUBERNETES_API_SERVER,
ANNOTATION_KUBERNETES_API_SERVER_CA,
@@ -34,22 +34,23 @@ import {
ANNOTATION_KUBERNETES_DASHBOARD_PARAMETERS,
} from '@backstage/plugin-kubernetes-common';
import { JsonObject } from '@backstage/types';
import { CatalogService } from '@backstage/plugin-catalog-node';
function isObject(obj: unknown): obj is JsonObject {
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
}
export class CatalogClusterLocator implements KubernetesClustersSupplier {
private catalogClient: CatalogApi;
private catalogService: CatalogService;
private auth: AuthService;
constructor(catalogClient: CatalogApi, auth: AuthService) {
this.catalogClient = catalogClient;
constructor(catalogService: CatalogService, auth: AuthService) {
this.catalogService = catalogService;
this.auth = auth;
}
static fromConfig(
catalogApi: CatalogApi,
catalogApi: CatalogService,
auth: AuthService,
): CatalogClusterLocator {
return new CatalogClusterLocator(catalogApi, auth);
@@ -70,20 +71,14 @@ export class CatalogClusterLocator implements KubernetesClustersSupplier {
[authProviderKey]: CATALOG_FILTER_EXISTS,
};
const clusters = await this.catalogClient.getEntities(
const clusters = await this.catalogService.getEntities(
{
filter: [filter],
},
options?.credentials
? {
token: (
await this.auth.getPluginRequestToken({
onBehalfOf: options.credentials,
targetPluginId: 'catalog',
})
).token,
}
: undefined,
{
credentials:
options?.credentials ?? (await this.auth.getNoneCredentials()),
},
);
return clusters.items.map(entity => {
const annotations = entity.metadata.annotations!;
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { Duration } from 'luxon';
import { ConfigClusterLocator } from './ConfigClusterLocator';
@@ -31,6 +30,7 @@ import {
ClusterDetails,
KubernetesClustersSupplier,
} from '@backstage/plugin-kubernetes-node';
import { CatalogService } from '@backstage/plugin-catalog-node';
class CombinedClustersSupplier implements KubernetesClustersSupplier {
constructor(
@@ -72,7 +72,7 @@ class CombinedClustersSupplier implements KubernetesClustersSupplier {
export const getCombinedClusterSupplier = (
rootConfig: Config,
catalogClient: CatalogApi,
catalogService: CatalogService,
authStrategy: AuthenticationStrategy,
logger: LoggerService,
refreshInterval: Duration | undefined = undefined,
@@ -84,7 +84,7 @@ export const getCombinedClusterSupplier = (
const type = clusterLocatorMethod.getString('type');
switch (type) {
case 'catalog':
return CatalogClusterLocator.fromConfig(catalogClient, auth);
return CatalogClusterLocator.fromConfig(catalogService, auth);
case 'localKubectlProxy':
return new LocalKubectlProxyClusterLocator();
case 'config':
+86 -49
View File
@@ -18,7 +18,7 @@ import {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
import { catalogServiceRef } from '@backstage/plugin-catalog-node';
import {
type AuthenticationStrategy,
@@ -27,112 +27,133 @@ import {
type KubernetesClustersSupplier,
kubernetesClusterSupplierExtensionPoint,
type KubernetesClusterSupplierExtensionPoint,
KubernetesClusterSupplierFactory,
type KubernetesFetcher,
kubernetesFetcherExtensionPoint,
type KubernetesFetcherExtensionPoint,
KubernetesFetcherFactory,
type KubernetesObjectsProvider,
kubernetesObjectsProviderExtensionPoint,
type KubernetesObjectsProviderExtensionPoint,
KubernetesObjectsProviderFactory,
type KubernetesServiceLocator,
kubernetesServiceLocatorExtensionPoint,
type KubernetesServiceLocatorExtensionPoint,
KubernetesServiceLocatorFactory,
} from '@backstage/plugin-kubernetes-node';
import { KubernetesBuilder } from './service/KubernetesBuilder';
import { KubernetesRouter } from './service/KubernetesRouter';
import { KubernetesInitializer } from './service/KubernetesInitializer';
class ObjectsProvider implements KubernetesObjectsProviderExtensionPoint {
private objectsProvider: KubernetesObjectsProvider | undefined;
private objectsProvider: KubernetesObjectsProviderFactory | undefined;
getObjectsProvider() {
return this.objectsProvider;
}
addObjectsProvider(provider: KubernetesObjectsProvider) {
addObjectsProvider(
provider: KubernetesObjectsProvider | KubernetesObjectsProviderFactory,
) {
if (this.objectsProvider) {
throw new Error(
'Multiple Kubernetes objects provider is not supported at this time',
);
}
this.objectsProvider = provider;
if (typeof provider !== 'function') {
this.objectsProvider = async () => provider;
} else {
this.objectsProvider = provider;
}
}
}
class ClusterSuplier implements KubernetesClusterSupplierExtensionPoint {
private clusterSupplier: KubernetesClustersSupplier | undefined;
private clusterSupplier: KubernetesClusterSupplierFactory | undefined;
getClusterSupplier() {
return this.clusterSupplier;
}
addClusterSupplier(clusterSupplier: KubernetesClustersSupplier) {
addClusterSupplier(
clusterSupplier:
| KubernetesClustersSupplier
| KubernetesClusterSupplierFactory,
) {
if (this.clusterSupplier) {
throw new Error(
'Multiple Kubernetes Cluster Suppliers is not supported at this time',
);
}
this.clusterSupplier = clusterSupplier;
if (typeof clusterSupplier !== 'function') {
this.clusterSupplier = async () => clusterSupplier;
} else {
this.clusterSupplier = clusterSupplier;
}
}
}
class Fetcher implements KubernetesFetcherExtensionPoint {
private fetcher: KubernetesFetcher | undefined;
private fetcher: KubernetesFetcherFactory | undefined;
getFetcher() {
return this.fetcher;
}
addFetcher(fetcher: KubernetesFetcher) {
addFetcher(fetcher: KubernetesFetcher | KubernetesFetcherFactory) {
if (this.fetcher) {
throw new Error(
'Multiple Kubernetes Fetchers is not supported at this time',
);
}
this.fetcher = fetcher;
if (typeof fetcher !== 'function') {
this.fetcher = async () => fetcher;
} else {
this.fetcher = fetcher;
}
}
}
class ServiceLocator implements KubernetesServiceLocatorExtensionPoint {
private serviceLocator: KubernetesServiceLocator | undefined;
private serviceLocator: KubernetesServiceLocatorFactory | undefined;
getServiceLocator() {
return this.serviceLocator;
}
addServiceLocator(serviceLocator: KubernetesServiceLocator) {
addServiceLocator(
serviceLocator: KubernetesServiceLocator | KubernetesServiceLocatorFactory,
) {
if (this.serviceLocator) {
throw new Error(
'Multiple Kubernetes Service Locators is not supported at this time',
);
}
this.serviceLocator = serviceLocator;
if (typeof serviceLocator !== 'function') {
this.serviceLocator = async () => serviceLocator;
} else {
this.serviceLocator = serviceLocator;
}
}
}
class AuthStrategy implements KubernetesAuthStrategyExtensionPoint {
private authStrategies: Array<{
key: string;
strategy: AuthenticationStrategy;
}>;
constructor() {
this.authStrategies = new Array<{
key: string;
strategy: AuthenticationStrategy;
}>();
}
static addAuthStrategiesFromArray(
authStrategies: Array<{ key: string; strategy: AuthenticationStrategy }>,
builder: KubernetesBuilder,
) {
authStrategies.forEach(st => builder.addAuthStrategy(st.key, st.strategy));
}
private authStrategies: Map<string, AuthenticationStrategy> | undefined;
getAuthenticationStrategies() {
return this.authStrategies;
}
addAuthStrategy(key: string, authStrategy: AuthenticationStrategy) {
this.authStrategies.push({ key, strategy: authStrategy });
if (!this.authStrategies) {
this.authStrategies = new Map<string, AuthenticationStrategy>();
}
if (key.includes('-')) {
throw new Error('Strategy name can not include dashes');
}
this.authStrategies.set(key, authStrategy);
}
}
@@ -176,7 +197,7 @@ export const kubernetesPlugin = createBackendPlugin({
logger: coreServices.logger,
config: coreServices.rootConfig,
discovery: coreServices.discovery,
catalogApi: catalogServiceRef,
catalog: catalogServiceRef,
permissions: coreServices.permissions,
auth: coreServices.auth,
httpAuth: coreServices.httpAuth,
@@ -186,33 +207,49 @@ export const kubernetesPlugin = createBackendPlugin({
logger,
config,
discovery,
catalogApi,
catalog,
permissions,
auth,
httpAuth,
}) {
// TODO: this could do with a cleanup and push some of this initalization somewhere else
if (config.has('kubernetes')) {
// TODO: expose all of the customization & extension points of the builder here
const builder: KubernetesBuilder = KubernetesBuilder.createBuilder({
const initializer = KubernetesInitializer.create({
logger,
config,
catalogApi,
catalog,
auth,
fetcher: extPointFetcher.getFetcher(),
clusterSupplier: extPointClusterSuplier.getClusterSupplier(),
serviceLocator: extPointServiceLocator.getServiceLocator(),
objectsProvider: extPointObjectsProvider.getObjectsProvider(),
authStrategyMap: extPointAuthStrategy.getAuthenticationStrategies(),
});
const {
fetcher,
authStrategyMap,
clusterSupplier,
serviceLocator,
objectsProvider,
} = await initializer.init();
const router = KubernetesRouter.create({
logger,
config,
catalog,
permissions,
discovery,
auth,
httpAuth,
})
.setObjectsProvider(extPointObjectsProvider.getObjectsProvider())
.setClusterSupplier(extPointClusterSuplier.getClusterSupplier())
.setFetcher(extPointFetcher.getFetcher())
.setServiceLocator(extPointServiceLocator.getServiceLocator());
authStrategyMap: Object.fromEntries(authStrategyMap.entries()),
fetcher,
clusterSupplier,
serviceLocator,
objectsProvider,
});
AuthStrategy.addAuthStrategiesFromArray(
extPointAuthStrategy.getAuthenticationStrategies(),
builder,
);
const { router } = await builder.build();
http.use(router);
http.use(await router.getRouter());
} else {
logger.warn(
'Failed to initialize kubernetes backend: valid kubernetes config is missing',
@@ -18,20 +18,19 @@ import {
parseEntityRef,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { CatalogApi } from '@backstage/catalog-client';
import { InputError } from '@backstage/errors';
import express, { Request } from 'express';
import { KubernetesObjectsProvider } from '@backstage/plugin-kubernetes-node';
import { AuthService, HttpAuthService } from '@backstage/backend-plugin-api';
import { HttpAuthService } from '@backstage/backend-plugin-api';
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
import { requirePermission } from '../auth/requirePermission';
import { kubernetesResourcesReadPermission } from '@backstage/plugin-kubernetes-common';
import { CatalogService } from '@backstage/plugin-catalog-node';
export const addResourceRoutesToRouter = (
router: express.Router,
catalogApi: CatalogApi,
catalog: CatalogService,
objectsProvider: KubernetesObjectsProvider,
auth: AuthService,
httpAuth: HttpAuthService,
permissionApi: PermissionEvaluator,
) => {
@@ -50,12 +49,9 @@ export const addResourceRoutesToRouter = (
throw new InputError(`Invalid entity ref, ${error}`);
}
const { token } = await auth.getPluginRequestToken({
onBehalfOf: await httpAuth.credentials(req),
targetPluginId: 'catalog',
const entity = await catalog.getEntityByRef(entityRef, {
credentials: await httpAuth.credentials(req),
});
const entity = await catalogApi.getEntityByRef(entityRef, { token });
if (!entity) {
throw new InputError(
`Entity ref missing, ${stringifyEntityRef(entityRef)}`,
@@ -0,0 +1,48 @@
/*
* 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 { RootConfigService } from '@backstage/backend-plugin-api';
import { ServiceLocatorMethod } from '../types';
import { KubernetesClustersSupplier } from '@backstage/plugin-kubernetes-node';
import { MultiTenantServiceLocator } from './MultiTenantServiceLocator';
import { SingleTenantServiceLocator } from './SingleTenantServiceLocator';
import { CatalogRelationServiceLocator } from './CatalogRelationServiceLocator';
export const buildDefaultServiceLocator = ({
config,
clusterSupplier,
}: {
config: RootConfigService;
clusterSupplier: KubernetesClustersSupplier;
}) => {
const method = config.getString(
'kubernetes.serviceLocatorMethod.type',
) as ServiceLocatorMethod;
switch (method) {
case 'multiTenant':
return new MultiTenantServiceLocator(clusterSupplier);
case 'singleTenant':
return new SingleTenantServiceLocator(clusterSupplier);
case 'catalogRelation':
return new CatalogRelationServiceLocator(clusterSupplier);
case 'http':
throw new Error('not implemented');
default:
throw new Error(
`Unsupported kubernetes.serviceLocatorMethod "${method}"`,
);
}
};
@@ -1,564 +0,0 @@
/*
* Copyright 2020 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 { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import {
ANNOTATION_KUBERNETES_AUTH_PROVIDER,
ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER,
kubernetesClustersReadPermission,
kubernetesPermissions,
kubernetesResourcesReadPermission,
} from '@backstage/plugin-kubernetes-common';
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';
import express from 'express';
import Router from 'express-promise-router';
import { Duration } from 'luxon';
import {
AksStrategy,
AnonymousStrategy,
AwsIamStrategy,
AzureIdentityStrategy,
DispatchStrategy,
GoogleServiceAccountStrategy,
GoogleStrategy,
OidcStrategy,
ServiceAccountStrategy,
} from '../auth';
import { getCombinedClusterSupplier } from '../cluster-locator';
import {
AuthService,
BackstageCredentials,
DiscoveryService,
HttpAuthService,
LoggerService,
} from '@backstage/backend-plugin-api';
import {
AuthenticationStrategy,
AuthMetadata,
CustomResource,
KubernetesClustersSupplier,
KubernetesFetcher,
KubernetesObjectsProvider,
KubernetesObjectTypes,
KubernetesServiceLocator,
} from '@backstage/plugin-kubernetes-node';
import { addResourceRoutesToRouter } from '../routes/resourcesRoutes';
import { CatalogRelationServiceLocator } from '../service-locator/CatalogRelationServiceLocator';
import { MultiTenantServiceLocator } from '../service-locator/MultiTenantServiceLocator';
import { SingleTenantServiceLocator } from '../service-locator/SingleTenantServiceLocator';
import {
KubernetesObjectsProviderOptions,
ObjectsByEntityRequest,
ServiceLocatorMethod,
} from '../types/types';
import {
ALL_OBJECTS,
DEFAULT_OBJECTS,
KubernetesFanOutHandler,
} from './KubernetesFanOutHandler';
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
import { KubernetesProxy } from './KubernetesProxy';
import { requirePermission } from '../auth/requirePermission';
/**
* @deprecated Please migrate to the new backend system as this will be removed in the future.
* @public
*/
export interface KubernetesEnvironment {
logger: LoggerService;
config: Config;
catalogApi: CatalogApi;
discovery: DiscoveryService;
permissions: PermissionEvaluator;
auth: AuthService;
httpAuth: HttpAuthService;
}
/**
* The return type of the `KubernetesBuilder.build` method
* @deprecated Please migrate to the new backend system as this will be removed in the future.
* @public
*/
export type KubernetesBuilderReturn = Promise<{
router: express.Router;
clusterSupplier: KubernetesClustersSupplier;
customResources: CustomResource[];
fetcher: KubernetesFetcher;
proxy: KubernetesProxy;
objectsProvider: KubernetesObjectsProvider;
serviceLocator: KubernetesServiceLocator;
authStrategyMap: { [key: string]: AuthenticationStrategy };
}>;
export class KubernetesBuilder {
private clusterSupplier?: KubernetesClustersSupplier;
private defaultClusterRefreshInterval: Duration = Duration.fromObject({
minutes: 60,
});
private objectsProvider?: KubernetesObjectsProvider;
private fetcher?: KubernetesFetcher;
private serviceLocator?: KubernetesServiceLocator;
private proxy?: KubernetesProxy;
private authStrategyMap?: { [key: string]: AuthenticationStrategy };
static createBuilder(env: KubernetesEnvironment) {
return new KubernetesBuilder(env);
}
constructor(protected readonly env: KubernetesEnvironment) {}
public async build(): KubernetesBuilderReturn {
const logger = this.env.logger;
const config = this.env.config;
const permissions = this.env.permissions;
logger.info('Initializing Kubernetes backend');
if (!config.has('kubernetes')) {
if (process.env.NODE_ENV !== 'development') {
throw new Error('Kubernetes configuration is missing');
}
logger.warn(
'Failed to initialize kubernetes backend: kubernetes config is missing',
);
return {
router: Router(),
} as unknown as KubernetesBuilderReturn;
}
const customResources = this.buildCustomResources();
const fetcher = this.getFetcher();
const clusterSupplier = this.getClusterSupplier();
const authStrategyMap = this.getAuthStrategyMap();
const proxy = this.getProxy(
logger,
clusterSupplier,
this.env.discovery,
this.env.httpAuth,
);
const serviceLocator = this.getServiceLocator();
const objectsProvider = this.getObjectsProvider({
logger,
fetcher,
config,
serviceLocator,
customResources,
objectTypesToFetch: this.getObjectTypesToFetch(),
});
const router = this.buildRouter(
objectsProvider,
clusterSupplier,
this.env.catalogApi,
proxy,
permissions,
this.env.auth,
this.env.httpAuth,
);
return {
clusterSupplier,
customResources,
fetcher,
proxy,
objectsProvider,
router,
serviceLocator,
authStrategyMap,
};
}
public setClusterSupplier(clusterSupplier?: KubernetesClustersSupplier) {
this.clusterSupplier = clusterSupplier;
return this;
}
public setDefaultClusterRefreshInterval(refreshInterval: Duration) {
this.defaultClusterRefreshInterval = refreshInterval;
return this;
}
public setObjectsProvider(objectsProvider?: KubernetesObjectsProvider) {
this.objectsProvider = objectsProvider;
return this;
}
public setFetcher(fetcher?: KubernetesFetcher) {
this.fetcher = fetcher;
return this;
}
public setServiceLocator(serviceLocator?: KubernetesServiceLocator) {
this.serviceLocator = serviceLocator;
return this;
}
public setProxy(proxy?: KubernetesProxy) {
this.proxy = proxy;
return this;
}
public setAuthStrategyMap(authStrategyMap: {
[key: string]: AuthenticationStrategy;
}) {
this.authStrategyMap = authStrategyMap;
}
public addAuthStrategy(key: string, strategy: AuthenticationStrategy) {
if (key.includes('-')) {
throw new Error('Strategy name can not include dashes');
}
this.getAuthStrategyMap()[key] = strategy;
return this;
}
protected buildCustomResources() {
const customResources: CustomResource[] = (
this.env.config.getOptionalConfigArray('kubernetes.customResources') ?? []
).map(
c =>
({
group: c.getString('group'),
apiVersion: c.getString('apiVersion'),
plural: c.getString('plural'),
objectType: 'customresources',
} as CustomResource),
);
this.env.logger.info(
`action=LoadingCustomResources numOfCustomResources=${customResources.length}`,
);
return customResources;
}
protected buildClusterSupplier(
refreshInterval: Duration,
): KubernetesClustersSupplier {
const config = this.env.config;
this.clusterSupplier = getCombinedClusterSupplier(
config,
this.env.catalogApi,
new DispatchStrategy({ authStrategyMap: this.getAuthStrategyMap() }),
this.env.logger,
refreshInterval,
this.env.auth,
);
return this.clusterSupplier;
}
protected buildObjectsProvider(
options: KubernetesObjectsProviderOptions,
): KubernetesObjectsProvider {
const authStrategyMap = this.getAuthStrategyMap();
this.objectsProvider = new KubernetesFanOutHandler({
...options,
authStrategy: new DispatchStrategy({
authStrategyMap,
}),
});
return this.objectsProvider;
}
protected buildFetcher(): KubernetesFetcher {
this.fetcher = new KubernetesClientBasedFetcher({
logger: this.env.logger,
});
return this.fetcher;
}
protected buildServiceLocator(
method: ServiceLocatorMethod,
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
switch (method) {
case 'multiTenant':
this.serviceLocator =
this.buildMultiTenantServiceLocator(clusterSupplier);
break;
case 'singleTenant':
this.serviceLocator =
this.buildSingleTenantServiceLocator(clusterSupplier);
break;
case 'catalogRelation':
this.serviceLocator =
this.buildCatalogRelationServiceLocator(clusterSupplier);
break;
case 'http':
this.serviceLocator = this.buildHttpServiceLocator(clusterSupplier);
break;
default:
throw new Error(
`Unsupported kubernetes.serviceLocatorMethod "${method}"`,
);
}
return this.serviceLocator;
}
protected buildMultiTenantServiceLocator(
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
return new MultiTenantServiceLocator(clusterSupplier);
}
protected buildSingleTenantServiceLocator(
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
return new SingleTenantServiceLocator(clusterSupplier);
}
protected buildCatalogRelationServiceLocator(
clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
return new CatalogRelationServiceLocator(clusterSupplier);
}
protected buildHttpServiceLocator(
_clusterSupplier: KubernetesClustersSupplier,
): KubernetesServiceLocator {
throw new Error('not implemented');
}
protected buildProxy(
logger: LoggerService,
clusterSupplier: KubernetesClustersSupplier,
discovery: DiscoveryService,
httpAuth: HttpAuthService,
): KubernetesProxy {
const authStrategyMap = this.getAuthStrategyMap();
const authStrategy = new DispatchStrategy({
authStrategyMap,
});
this.proxy = new KubernetesProxy({
logger,
clusterSupplier,
authStrategy,
discovery,
httpAuth,
});
return this.proxy;
}
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterSupplier: KubernetesClustersSupplier,
catalogApi: CatalogApi,
proxy: KubernetesProxy,
permissionApi: PermissionEvaluator,
authService: AuthService,
httpAuth: HttpAuthService,
): express.Router {
const logger = this.env.logger;
const router = Router();
router.use('/proxy', proxy.createRequestHandler({ permissionApi }));
router.use(express.json());
router.use(
createPermissionIntegrationRouter({
permissions: kubernetesPermissions,
}),
);
// @deprecated
router.post('/services/:serviceId', async (req, res) => {
await requirePermission(
permissionApi,
kubernetesResourcesReadPermission,
httpAuth,
req,
);
const serviceId = req.params.serviceId;
const requestBody: ObjectsByEntityRequest = req.body;
try {
const response = await objectsProvider.getKubernetesObjectsByEntity(
{
entity: requestBody.entity,
auth: requestBody.auth || {},
},
{ credentials: await httpAuth.credentials(req) },
);
res.json(response);
} catch (e) {
logger.error(
`action=retrieveObjectsByServiceId service=${serviceId}, error=${e}`,
);
res.status(500).json({ error: e.message });
}
});
router.get('/clusters', async (req, res) => {
await requirePermission(
permissionApi,
kubernetesClustersReadPermission,
httpAuth,
req,
);
const credentials = await httpAuth.credentials(req);
const clusterDetails = await this.fetchClusterDetails(clusterSupplier, {
credentials,
});
res.json({
items: clusterDetails.map(cd => {
const oidcTokenProvider =
cd.authMetadata[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];
const authProvider =
cd.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];
const strategy = this.getAuthStrategyMap()[authProvider];
let auth: AuthMetadata = {};
if (strategy) {
auth = strategy.presentAuthMetadata(cd.authMetadata);
}
return {
name: cd.name,
title: cd.title,
dashboardUrl: cd.dashboardUrl,
authProvider,
...(oidcTokenProvider && { oidcTokenProvider }),
...(auth && Object.keys(auth).length !== 0 && { auth }),
};
}),
});
});
addResourceRoutesToRouter(
router,
catalogApi,
objectsProvider,
authService,
httpAuth,
permissionApi,
);
return router;
}
protected buildAuthStrategyMap() {
this.authStrategyMap = {
aks: new AksStrategy(),
aws: new AwsIamStrategy({ config: this.env.config }),
azure: new AzureIdentityStrategy(this.env.logger),
google: new GoogleStrategy(),
googleServiceAccount: new GoogleServiceAccountStrategy({
config: this.env.config,
}),
localKubectlProxy: new AnonymousStrategy(),
oidc: new OidcStrategy(),
serviceAccount: new ServiceAccountStrategy(),
};
return this.authStrategyMap;
}
protected async fetchClusterDetails(
clusterSupplier: KubernetesClustersSupplier,
options: { credentials: BackstageCredentials },
) {
const clusterDetails = await clusterSupplier.getClusters(options);
this.env.logger.debug(
`action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`,
);
return clusterDetails;
}
protected getServiceLocatorMethod() {
return this.env.config.getString(
'kubernetes.serviceLocatorMethod.type',
) as ServiceLocatorMethod;
}
protected getFetcher(): KubernetesFetcher {
return this.fetcher ?? this.buildFetcher();
}
protected getClusterSupplier() {
return (
this.clusterSupplier ??
this.buildClusterSupplier(this.defaultClusterRefreshInterval)
);
}
protected getServiceLocator(): KubernetesServiceLocator {
return (
this.serviceLocator ??
this.buildServiceLocator(
this.getServiceLocatorMethod(),
this.getClusterSupplier(),
)
);
}
protected getObjectsProvider(options: KubernetesObjectsProviderOptions) {
return this.objectsProvider ?? this.buildObjectsProvider(options);
}
protected getObjectTypesToFetch() {
const objectTypesToFetchStrings = this.env.config.getOptionalStringArray(
'kubernetes.objectTypes',
) as KubernetesObjectTypes[];
const apiVersionOverrides = this.env.config.getOptionalConfig(
'kubernetes.apiVersionOverrides',
);
let objectTypesToFetch;
if (objectTypesToFetchStrings) {
objectTypesToFetch = ALL_OBJECTS.filter(obj =>
objectTypesToFetchStrings.includes(obj.objectType),
);
}
if (apiVersionOverrides) {
objectTypesToFetch = objectTypesToFetch ?? DEFAULT_OBJECTS;
for (const obj of objectTypesToFetch) {
if (apiVersionOverrides.has(obj.objectType)) {
obj.apiVersion = apiVersionOverrides.getString(obj.objectType);
}
}
}
return objectTypesToFetch;
}
protected getProxy(
logger: LoggerService,
clusterSupplier: KubernetesClustersSupplier,
discovery: DiscoveryService,
httpAuth: HttpAuthService,
) {
return (
this.proxy ??
this.buildProxy(logger, clusterSupplier, discovery, httpAuth)
);
}
protected getAuthStrategyMap() {
return this.authStrategyMap ?? this.buildAuthStrategyMap();
}
}
@@ -0,0 +1,242 @@
/*
* 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 {
AuthenticationStrategy,
CustomResource,
KubernetesClustersSupplier,
KubernetesClusterSupplierFactory,
KubernetesFetcher,
KubernetesFetcherFactory,
KubernetesObjectsProviderFactory,
KubernetesObjectTypes,
KubernetesServiceLocator,
KubernetesServiceLocatorFactory,
ObjectToFetch,
} from '@backstage/plugin-kubernetes-node';
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
import {
AuthService,
LoggerService,
RootConfigService,
} from '@backstage/backend-plugin-api';
import { buildDefaultAuthStrategyMap } from '../auth/buildDefaultAuthStrategyMap';
import { Duration } from 'luxon';
import { getCombinedClusterSupplier } from '../cluster-locator';
import { DispatchStrategy } from '../auth/DispatchStrategy';
import { CatalogService } from '@backstage/plugin-catalog-node';
import { buildDefaultServiceLocator } from '../service-locator/buildDefaultServiceLocator';
import {
ALL_OBJECTS,
DEFAULT_OBJECTS,
KubernetesFanOutHandler,
} from './KubernetesFanOutHandler';
export class KubernetesInitializer {
constructor(
private readonly opts: {
fetcher?: KubernetesFetcherFactory;
authStrategyMap?: Map<string, AuthenticationStrategy>;
clusterSupplier?: KubernetesClusterSupplierFactory;
logger: LoggerService;
config: RootConfigService;
catalog: CatalogService;
auth: AuthService;
serviceLocator?: KubernetesServiceLocatorFactory;
objectsProvider?: KubernetesObjectsProviderFactory;
},
) {}
static create(opts: {
fetcher?: KubernetesFetcherFactory;
clusterSupplier?: KubernetesClusterSupplierFactory;
serviceLocator?: KubernetesServiceLocatorFactory;
objectsProvider?: KubernetesObjectsProviderFactory;
authStrategyMap?: Map<string, AuthenticationStrategy>;
logger: LoggerService;
config: RootConfigService;
catalog: CatalogService;
auth: AuthService;
}) {
return new KubernetesInitializer(opts);
}
private async defaultFetcher() {
return new KubernetesClientBasedFetcher({
logger: this.opts.logger,
});
}
private async defaultAuthStrategy() {
return buildDefaultAuthStrategyMap({
logger: this.opts.logger,
config: this.opts.config,
});
}
private async defaultClusterSupplier(opts: {
authStrategyMap: Map<string, AuthenticationStrategy>;
}) {
const refreshInterval = Duration.fromObject({
minutes: 60,
});
return getCombinedClusterSupplier(
this.opts.config,
this.opts.catalog,
new DispatchStrategy({
authStrategyMap: Object.fromEntries(opts.authStrategyMap.entries()),
}),
this.opts.logger,
refreshInterval,
this.opts.auth,
);
}
private async defaultServiceLocator(opts: {
clusterSupplier: KubernetesClustersSupplier;
}) {
return buildDefaultServiceLocator({
config: this.opts.config,
clusterSupplier: opts.clusterSupplier,
});
}
private async defaultObjectsProvider(opts: {
clusterSupplier: KubernetesClustersSupplier;
authStrategyMap: Map<string, AuthenticationStrategy>;
fetcher: KubernetesFetcher;
serviceLocator: KubernetesServiceLocator;
customResources: CustomResource[];
objectTypesToFetch: ObjectToFetch[];
}) {
return new KubernetesFanOutHandler({
logger: this.opts.logger,
config: this.opts.config,
fetcher: opts.fetcher,
serviceLocator: opts.serviceLocator,
customResources: opts.customResources,
objectTypesToFetch: opts.objectTypesToFetch,
authStrategy: new DispatchStrategy({
authStrategyMap: Object.fromEntries(opts.authStrategyMap.entries()),
}),
});
}
private async defaultObjectsProviderOptions() {
const customResources: CustomResource[] = (
this.opts.config.getOptionalConfigArray('kubernetes.customResources') ??
[]
).map(
c =>
({
group: c.getString('group'),
apiVersion: c.getString('apiVersion'),
plural: c.getString('plural'),
objectType: 'customresources',
} as CustomResource),
);
const objectTypesToFetchStrings = this.opts.config.getOptionalStringArray(
'kubernetes.objectTypes',
) as KubernetesObjectTypes[];
const apiVersionOverrides = this.opts.config.getOptionalConfig(
'kubernetes.apiVersionOverrides',
);
let objectTypesToFetch: ObjectToFetch[] | undefined = undefined;
if (objectTypesToFetchStrings) {
objectTypesToFetch = ALL_OBJECTS.filter(obj =>
objectTypesToFetchStrings.includes(obj.objectType),
);
}
if (apiVersionOverrides) {
objectTypesToFetch ??= DEFAULT_OBJECTS;
for (const obj of objectTypesToFetch) {
if (apiVersionOverrides.has(obj.objectType)) {
obj.apiVersion = apiVersionOverrides.getString(obj.objectType);
}
}
}
return {
customResources,
objectTypesToFetch: objectTypesToFetch ?? [],
};
}
async init() {
const fetcher =
(await this.opts.fetcher?.({ getDefault: this.defaultFetcher })) ??
(await this.defaultFetcher());
const authStrategyMap =
this.opts.authStrategyMap ?? (await this.defaultAuthStrategy());
const clusterSupplier =
(await this.opts.clusterSupplier?.({
getDefault: () => this.defaultClusterSupplier({ authStrategyMap }),
})) ?? (await this.defaultClusterSupplier({ authStrategyMap }));
const serviceLocator =
(await this.opts.serviceLocator?.({
getDefault: () => this.defaultServiceLocator({ clusterSupplier }),
clusterSupplier,
})) ?? (await this.defaultServiceLocator({ clusterSupplier }));
const { customResources, objectTypesToFetch } =
await this.defaultObjectsProviderOptions();
const objectsProvider =
(await this.opts.objectsProvider?.({
getDefault: () =>
this.defaultObjectsProvider({
clusterSupplier,
authStrategyMap,
fetcher,
serviceLocator,
customResources,
objectTypesToFetch,
}),
clusterSupplier,
serviceLocator,
customResources,
objectTypesToFetch,
authStrategy: new DispatchStrategy({
authStrategyMap: Object.fromEntries(authStrategyMap.entries()),
}),
})) ??
(await this.defaultObjectsProvider({
clusterSupplier,
authStrategyMap,
fetcher,
serviceLocator,
customResources,
objectTypesToFetch,
}));
return {
fetcher,
authStrategyMap,
clusterSupplier,
serviceLocator,
objectsProvider,
};
}
}
@@ -0,0 +1,241 @@
/*
* Copyright 2020 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 {
ANNOTATION_KUBERNETES_AUTH_PROVIDER,
ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER,
kubernetesClustersReadPermission,
kubernetesPermissions,
kubernetesResourcesReadPermission,
} from '@backstage/plugin-kubernetes-common';
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';
import express from 'express';
import Router from 'express-promise-router';
import { DispatchStrategy } from '../auth';
import {
AuthService,
BackstageCredentials,
DiscoveryService,
HttpAuthService,
LoggerService,
} from '@backstage/backend-plugin-api';
import {
AuthenticationStrategy,
AuthMetadata,
KubernetesClustersSupplier,
KubernetesFetcher,
KubernetesObjectsProvider,
KubernetesServiceLocator,
} from '@backstage/plugin-kubernetes-node';
import { addResourceRoutesToRouter } from '../routes/resourcesRoutes';
import { ObjectsByEntityRequest } from '../types/types';
import { KubernetesProxy } from './KubernetesProxy';
import { requirePermission } from '../auth/requirePermission';
import { CatalogService } from '@backstage/plugin-catalog-node';
export interface KubernetesEnvironment {
logger: LoggerService;
config: Config;
catalog: CatalogService;
discovery: DiscoveryService;
permissions: PermissionEvaluator;
auth: AuthService;
httpAuth: HttpAuthService;
authStrategyMap: { [key: string]: AuthenticationStrategy };
fetcher: KubernetesFetcher;
clusterSupplier: KubernetesClustersSupplier;
serviceLocator: KubernetesServiceLocator;
objectsProvider: KubernetesObjectsProvider;
}
export class KubernetesRouter {
static create(env: KubernetesEnvironment) {
return new KubernetesRouter(env);
}
constructor(protected readonly env: KubernetesEnvironment) {}
public async getRouter() {
const {
logger,
config,
permissions,
authStrategyMap,
clusterSupplier,
objectsProvider,
catalog,
discovery,
httpAuth,
} = this.env;
logger.info('Initializing Kubernetes backend');
if (!config.has('kubernetes')) {
if (process.env.NODE_ENV !== 'development') {
throw new Error('Kubernetes configuration is missing');
}
logger.warn(
'Failed to initialize kubernetes backend: kubernetes config is missing',
);
return Router();
}
const proxy = this.buildProxy(
logger,
clusterSupplier,
discovery,
httpAuth,
authStrategyMap,
);
return this.buildRouter(
objectsProvider,
clusterSupplier,
catalog,
proxy,
permissions,
httpAuth,
authStrategyMap,
);
}
private buildProxy(
logger: LoggerService,
clusterSupplier: KubernetesClustersSupplier,
discovery: DiscoveryService,
httpAuth: HttpAuthService,
authStrategyMap: { [key: string]: AuthenticationStrategy },
): KubernetesProxy {
const authStrategy = new DispatchStrategy({
authStrategyMap,
});
return new KubernetesProxy({
logger,
clusterSupplier,
authStrategy,
discovery,
httpAuth,
});
}
private buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterSupplier: KubernetesClustersSupplier,
catalog: CatalogService,
proxy: KubernetesProxy,
permissionApi: PermissionEvaluator,
httpAuth: HttpAuthService,
authStrategyMap: { [key: string]: AuthenticationStrategy },
): express.Router {
const logger = this.env.logger;
const router = Router();
router.use('/proxy', proxy.createRequestHandler({ permissionApi }));
router.use(express.json());
router.use(
createPermissionIntegrationRouter({
permissions: kubernetesPermissions,
}),
);
// @deprecated
router.post('/services/:serviceId', async (req, res) => {
await requirePermission(
permissionApi,
kubernetesResourcesReadPermission,
httpAuth,
req,
);
const serviceId = req.params.serviceId;
const requestBody: ObjectsByEntityRequest = req.body;
try {
const response = await objectsProvider.getKubernetesObjectsByEntity(
{
entity: requestBody.entity,
auth: requestBody.auth || {},
},
{ credentials: await httpAuth.credentials(req) },
);
res.json(response);
} catch (e) {
logger.error(
`action=retrieveObjectsByServiceId service=${serviceId}, error=${e}`,
);
res.status(500).json({ error: e.message });
}
});
router.get('/clusters', async (req, res) => {
await requirePermission(
permissionApi,
kubernetesClustersReadPermission,
httpAuth,
req,
);
const credentials = await httpAuth.credentials(req);
const clusterDetails = await this.fetchClusterDetails(clusterSupplier, {
credentials,
});
res.json({
items: clusterDetails.map(cd => {
const oidcTokenProvider =
cd.authMetadata[ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER];
const authProvider =
cd.authMetadata[ANNOTATION_KUBERNETES_AUTH_PROVIDER];
const strategy = authStrategyMap[authProvider];
let auth: AuthMetadata = {};
if (strategy) {
auth = strategy.presentAuthMetadata(cd.authMetadata);
}
return {
name: cd.name,
title: cd.title,
dashboardUrl: cd.dashboardUrl,
authProvider,
...(oidcTokenProvider && { oidcTokenProvider }),
...(auth && Object.keys(auth).length !== 0 && { auth }),
};
}),
});
});
addResourceRoutesToRouter(
router,
catalog,
objectsProvider,
httpAuth,
permissionApi,
);
return router;
}
private async fetchClusterDetails(
clusterSupplier: KubernetesClustersSupplier,
options: { credentials: BackstageCredentials },
) {
const clusterDetails = await clusterSupplier.getClusters(options);
this.env.logger.debug(
`action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`,
);
return clusterDetails;
}
}
@@ -1,48 +0,0 @@
/*
* Copyright 2020 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 { Logger } from 'winston';
import { KubernetesClustersSupplier } from '@backstage/plugin-kubernetes-node';
import express from 'express';
import { KubernetesBuilder } from './KubernetesBuilder';
import { CatalogApi } from '@backstage/catalog-client';
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
import {
DiscoveryService,
HttpAuthService,
RootConfigService,
AuthService,
} from '@backstage/backend-plugin-api';
interface RouterOptions {
logger: Logger;
config: RootConfigService;
catalogApi: CatalogApi;
clusterSupplier?: KubernetesClustersSupplier;
discovery: DiscoveryService;
permissions: PermissionEvaluator;
auth: AuthService;
httpAuth: HttpAuthService;
}
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { router } = await KubernetesBuilder.createBuilder(options)
.setClusterSupplier(options.clusterSupplier)
.build();
return router;
}
+42 -4
View File
@@ -5,6 +5,7 @@
```ts
import { AuthenticationStrategy as AuthenticationStrategy_2 } from '@backstage/plugin-kubernetes-node';
import { BackstageCredentials } from '@backstage/backend-plugin-api';
import { CustomResource as CustomResource_2 } from '@backstage/plugin-kubernetes-node';
import { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common';
import { Entity } from '@backstage/catalog-model';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
@@ -18,6 +19,7 @@ import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common';
import { KubernetesServiceLocator as KubernetesServiceLocator_2 } from '@backstage/plugin-kubernetes-node';
import { LoggerService } from '@backstage/backend-plugin-api';
import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common';
import { ObjectToFetch as ObjectToFetch_2 } from '@backstage/plugin-kubernetes-node';
// @public (undocumented)
export interface AuthenticationStrategy {
@@ -95,12 +97,21 @@ export interface KubernetesClustersSupplier {
// @public
export interface KubernetesClusterSupplierExtensionPoint {
// (undocumented)
addClusterSupplier(clusterSupplier: KubernetesClustersSupplier_2): void;
addClusterSupplier(
clusterSupplier:
| KubernetesClustersSupplier_2
| KubernetesClusterSupplierFactory,
): void;
}
// @public
export const kubernetesClusterSupplierExtensionPoint: ExtensionPoint<KubernetesClusterSupplierExtensionPoint>;
// @public
export type KubernetesClusterSupplierFactory = (opts: {
getDefault: () => Promise<KubernetesClustersSupplier_2>;
}) => Promise<KubernetesClustersSupplier_2>;
// @public
export type KubernetesCredential =
| {
@@ -134,12 +145,17 @@ export interface KubernetesFetcher {
// @public
export interface KubernetesFetcherExtensionPoint {
// (undocumented)
addFetcher(fetcher: KubernetesFetcher_2): void;
addFetcher(fetcher: KubernetesFetcher_2 | KubernetesFetcherFactory): void;
}
// @public
export const kubernetesFetcherExtensionPoint: ExtensionPoint<KubernetesFetcherExtensionPoint>;
// @public
export type KubernetesFetcherFactory = (opts: {
getDefault: () => Promise<KubernetesFetcher_2>;
}) => Promise<KubernetesFetcher_2>;
// @public (undocumented)
export interface KubernetesObjectsByEntity {
// (undocumented)
@@ -169,12 +185,24 @@ export interface KubernetesObjectsProvider {
// @public
export interface KubernetesObjectsProviderExtensionPoint {
// (undocumented)
addObjectsProvider(provider: KubernetesObjectsProvider_2): void;
addObjectsProvider(
provider: KubernetesObjectsProvider_2 | KubernetesObjectsProviderFactory,
): void;
}
// @public
export const kubernetesObjectsProviderExtensionPoint: ExtensionPoint<KubernetesObjectsProviderExtensionPoint>;
// @public
export type KubernetesObjectsProviderFactory = (opts: {
getDefault: () => Promise<KubernetesObjectsProvider_2>;
clusterSupplier: KubernetesClustersSupplier_2;
serviceLocator: KubernetesServiceLocator_2;
customResources: CustomResource_2[];
objectTypesToFetch?: ObjectToFetch_2[];
authStrategy: AuthenticationStrategy_2;
}) => Promise<KubernetesObjectsProvider_2>;
// @public (undocumented)
export type KubernetesObjectTypes =
| 'pods'
@@ -207,12 +235,22 @@ export interface KubernetesServiceLocator {
// @public
export interface KubernetesServiceLocatorExtensionPoint {
// (undocumented)
addServiceLocator(serviceLocator: KubernetesServiceLocator_2): void;
addServiceLocator(
serviceLocator:
| KubernetesServiceLocator_2
| KubernetesServiceLocatorFactory,
): void;
}
// @public
export const kubernetesServiceLocatorExtensionPoint: ExtensionPoint<KubernetesServiceLocatorExtensionPoint>;
// @public
export type KubernetesServiceLocatorFactory = (opts: {
getDefault: () => Promise<KubernetesServiceLocator_2>;
clusterSupplier: KubernetesClustersSupplier_2;
}) => Promise<KubernetesServiceLocator_2>;
// @public (undocumented)
export interface ObjectFetchParams {
// (undocumented)
+56 -4
View File
@@ -16,19 +16,37 @@
import { createExtensionPoint } from '@backstage/backend-plugin-api';
import {
AuthenticationStrategy,
CustomResource,
ObjectToFetch,
KubernetesClustersSupplier,
KubernetesFetcher,
KubernetesObjectsProvider,
KubernetesServiceLocator,
} from '@backstage/plugin-kubernetes-node';
/**
* A factory function for creating a KubernetesObjectsProvider.
*
* @public
*/
export type KubernetesObjectsProviderFactory = (opts: {
getDefault: () => Promise<KubernetesObjectsProvider>;
clusterSupplier: KubernetesClustersSupplier;
serviceLocator: KubernetesServiceLocator;
customResources: CustomResource[];
objectTypesToFetch?: ObjectToFetch[];
authStrategy: AuthenticationStrategy;
}) => Promise<KubernetesObjectsProvider>;
/**
* The interface for {@link kubernetesObjectsProviderExtensionPoint}.
*
* @public
*/
export interface KubernetesObjectsProviderExtensionPoint {
addObjectsProvider(provider: KubernetesObjectsProvider): void;
addObjectsProvider(
provider: KubernetesObjectsProvider | KubernetesObjectsProviderFactory,
): void;
}
/**
@@ -41,13 +59,26 @@ export const kubernetesObjectsProviderExtensionPoint =
id: 'kubernetes.objects-provider',
});
/**
* A factory function for creating a KubernetesClustersSupplier.
*
* @public
*/
export type KubernetesClusterSupplierFactory = (opts: {
getDefault: () => Promise<KubernetesClustersSupplier>;
}) => Promise<KubernetesClustersSupplier>;
/**
* The interface for {@link kubernetesClusterSupplierExtensionPoint}.
*
* @public
*/
export interface KubernetesClusterSupplierExtensionPoint {
addClusterSupplier(clusterSupplier: KubernetesClustersSupplier): void;
addClusterSupplier(
clusterSupplier:
| KubernetesClustersSupplier
| KubernetesClusterSupplierFactory,
): void;
}
/**
@@ -79,13 +110,22 @@ export const kubernetesAuthStrategyExtensionPoint =
id: 'kubernetes.auth-strategy',
});
/**
* A factory function for creating a KubernetesFetcher.
*
* @public
*/
export type KubernetesFetcherFactory = (opts: {
getDefault: () => Promise<KubernetesFetcher>;
}) => Promise<KubernetesFetcher>;
/**
* The interface for {@link kubernetesFetcherExtensionPoint}.
*
* @public
*/
export interface KubernetesFetcherExtensionPoint {
addFetcher(fetcher: KubernetesFetcher): void;
addFetcher(fetcher: KubernetesFetcher | KubernetesFetcherFactory): void;
}
/**
@@ -98,13 +138,25 @@ export const kubernetesFetcherExtensionPoint =
id: 'kubernetes.fetcher',
});
/**
* A factory function for creating a KubernetesServiceLocator.
*
* @public
*/
export type KubernetesServiceLocatorFactory = (opts: {
getDefault: () => Promise<KubernetesServiceLocator>;
clusterSupplier: KubernetesClustersSupplier;
}) => Promise<KubernetesServiceLocator>;
/**
* The interface for {@link kubernetesServiceLocatorExtensionPoint}.
*
* @public
*/
export interface KubernetesServiceLocatorExtensionPoint {
addServiceLocator(serviceLocator: KubernetesServiceLocator): void;
addServiceLocator(
serviceLocator: KubernetesServiceLocator | KubernetesServiceLocatorFactory,
): void;
}
/**