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:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
'@backstage/plugin-kubernetes-node': patch
|
||||
---
|
||||
|
||||
Added support for providing a factory to the extension points
|
||||
@@ -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':
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user