Enable customization of services used by the kubernetes backend plugin

Signed-off-by: Morgan Martinet <morgan@mmm-experts.com>
This commit is contained in:
Morgan Martinet
2021-10-01 00:37:18 -04:00
parent 56bd537256
commit a982e166c5
10 changed files with 618 additions and 170 deletions
+25
View File
@@ -0,0 +1,25 @@
---
'@backstage/plugin-kubernetes-backend': patch
---
Enable customization of services used by the kubernetes backend plugin
The createRouter function has been deprecated in favor of a KubernetesBuilder object.
Here's how you should upgrade your projects when configuring the Kubernetes backend plugin.
in your `packages/backend/src/plugins/kubernetes.ts` file for instance:
```typescript
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
config,
}: PluginEnvironment) {
const { router } = await KubernetesBuilder.createBuilder({
logger,
config,
}).build();
return router;
}
```
+6 -2
View File
@@ -59,14 +59,18 @@ add the following:
```typescript
// In packages/backend/src/plugins/kubernetes.ts
import { createRouter } from '@backstage/plugin-kubernetes-backend';
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
config,
}: PluginEnvironment) {
return await createRouter({ logger, config });
const { router } = await KubernetesBuilder.createBuilder({
logger,
config,
}).build();
return router;
}
```
+6 -2
View File
@@ -14,12 +14,16 @@
* limitations under the License.
*/
import { createRouter } from '@backstage/plugin-kubernetes-backend';
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
config,
}: PluginEnvironment) {
return await createRouter({ logger, config });
const { router } = await KubernetesBuilder.createBuilder({
logger,
config,
}).build();
return router;
}
+136 -13
View File
@@ -3,13 +3,13 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { ClusterObjects } from '@backstage/plugin-kubernetes-common';
import { Config } from '@backstage/config';
import express from 'express';
import type { FetchResponse } from '@backstage/plugin-kubernetes-common';
import type { KubernetesFetchError } from '@backstage/plugin-kubernetes-common';
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
import type { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
import { Logger as Logger_2 } from 'winston';
import type { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common';
// Warning: (ae-missing-release-tag) "AWSClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -40,7 +40,7 @@ export interface ClusterDetails {
// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public @deprecated
export function createRouter(options: RouterOptions): Promise<express.Router>;
// Warning: (ae-missing-release-tag) "CustomResource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -71,6 +71,71 @@ export interface FetchResponseWrapper {
// @public (undocumented)
export interface GKEClusterDetails extends ClusterDetails {}
// Warning: (ae-missing-release-tag) "KubernetesBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export class KubernetesBuilder {
constructor(env: KubernetesEnvironment);
// (undocumented)
build(): Promise<{
clusterDetails: ClusterDetails[];
clusterSupplier: KubernetesClustersSupplier;
customResources: CustomResource[];
fetcher: KubernetesFetcher;
objectsProvider: KubernetesObjectsProvider;
router: express.Router;
serviceLocator: KubernetesServiceLocator;
}>;
// (undocumented)
protected buildClusterSupplier(): KubernetesClustersSupplier;
// (undocumented)
protected buildCustomResources(): CustomResource[];
// (undocumented)
protected buildFetcher(): KubernetesFetcher;
// (undocumented)
protected buildHttpServiceLocator(
_clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
// (undocumented)
protected buildMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
// (undocumented)
protected buildObjectsProvider(
options: KubernetesObjectsProviderOptions,
): KubernetesObjectsProvider;
// (undocumented)
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterDetails: ClusterDetails[],
): express.Router;
// (undocumented)
protected buildServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
// (undocumented)
static createBuilder(env: KubernetesEnvironment): KubernetesBuilder;
// (undocumented)
protected readonly env: KubernetesEnvironment;
// (undocumented)
protected fetchClusterDetails(
clusterSupplier: KubernetesClustersSupplier,
): Promise<ClusterDetails[]>;
// (undocumented)
protected getObjectTypesToFetch(): ObjectToFetch[] | undefined;
// (undocumented)
protected getServiceLocatorMethod(): ServiceLocatorMethod;
// (undocumented)
setClusterSupplier(clusterSupplier?: KubernetesClustersSupplier): this;
// (undocumented)
setFetcher(fetcher?: KubernetesFetcher): this;
// (undocumented)
setObjectsProvider(objectsProvider?: KubernetesObjectsProvider): this;
// (undocumented)
setServiceLocator(serviceLocator?: KubernetesServiceLocator): this;
}
// Warning: (ae-missing-release-tag) "KubernetesClustersSupplier" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -79,6 +144,43 @@ export interface KubernetesClustersSupplier {
getClusters(): Promise<ClusterDetails[]>;
}
// Warning: (ae-missing-release-tag) "KubernetesEnvironment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface KubernetesEnvironment {
// (undocumented)
config: Config;
// (undocumented)
logger: Logger_2;
}
// Warning: (ae-missing-release-tag) "KubernetesFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface KubernetesFactory {
// (undocumented)
createHttpServiceLocator(
_clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
// (undocumented)
createKubernetesClustersSupplier(config: Config): KubernetesClustersSupplier;
// (undocumented)
createKubernetesFetcher(logger: Logger_2): KubernetesFetcher;
// (undocumented)
createKubernetesObjectsProvider(
options: KubernetesObjectsProviderOptions,
): KubernetesObjectsProvider;
// (undocumented)
createMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
// (undocumented)
createServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
}
// Warning: (ae-missing-release-tag) "KubernetesFetcher" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -89,6 +191,32 @@ export interface KubernetesFetcher {
): Promise<FetchResponseWrapper>;
}
// Warning: (ae-missing-release-tag) "KubernetesObjectsProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface KubernetesObjectsProvider {
// (undocumented)
getKubernetesObjectsByEntity(
request: ObjectsByEntityRequest,
): Promise<ObjectsByEntityResponse>;
}
// Warning: (ae-missing-release-tag) "KubernetesObjectsProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface KubernetesObjectsProviderOptions {
// (undocumented)
customResources: CustomResource[];
// (undocumented)
fetcher: KubernetesFetcher;
// (undocumented)
logger: Logger_2;
// (undocumented)
objectTypesToFetch?: ObjectToFetch[];
// (undocumented)
serviceLocator: KubernetesServiceLocator;
}
// Warning: (ae-missing-release-tag) "KubernetesObjectTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -110,16 +238,6 @@ export interface KubernetesServiceLocator {
getClustersByServiceId(serviceId: string): Promise<ClusterDetails[]>;
}
// Warning: (ae-forgotten-export) The symbol "KubernetesFanOutHandler" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "makeRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const makeRouter: (
logger: Logger_2,
kubernetesFanOutHandler: KubernetesFanOutHandler,
clusterDetails: ClusterDetails[],
) => express.Router;
// Warning: (ae-missing-release-tag) "ObjectFetchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -140,6 +258,11 @@ export interface ObjectFetchParams {
serviceId: string;
}
// Warning: (ae-missing-release-tag) "ObjectsByEntityRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ObjectsByEntityRequest = KubernetesRequestBody;
// Warning: (ae-missing-release-tag) "ObjectToFetch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
+1
View File
@@ -21,6 +21,7 @@
*/
export * from './service/router';
export * from './service/KubernetesBuilder';
export * from './types/types';
export { DEFAULT_OBJECTS } from './service/KubernetesFanOutHandler';
@@ -14,22 +14,32 @@
* limitations under the License.
*/
import { getVoidLogger } from '@backstage/backend-common';
import { Config } from '@backstage/config';
import { getVoidLogger, loadBackendConfig } from '@backstage/backend-common';
import express from 'express';
import request from 'supertest';
import { makeRouter } from './router';
import { KubernetesFanOutHandler } from './KubernetesFanOutHandler';
import {
ClusterDetails,
FetchResponseWrapper,
KubernetesClustersSupplier,
KubernetesFetcher,
KubernetesServiceLocator,
ObjectFetchParams,
} from '../types/types';
import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common';
import { KubernetesBuilder } from './KubernetesBuilder';
describe('router', () => {
describe('KubernetesBuilder', () => {
let app: express.Express;
let kubernetesFanOutHandler: jest.Mocked<KubernetesFanOutHandler>;
let config: Config;
beforeAll(async () => {
kubernetesFanOutHandler = {
getKubernetesObjectsByEntity: jest.fn(),
} as any;
const logger = getVoidLogger();
config = await loadBackendConfig({ logger, argv: [] });
const router = makeRouter(getVoidLogger(), kubernetesFanOutHandler, [
const clusters: ClusterDetails[] = [
{
name: 'some-cluster',
authProvider: 'serviceAccount',
@@ -41,7 +51,22 @@ describe('router', () => {
url: 'https://localhost:1235',
authProvider: 'google',
},
]);
];
const clusterSupplier: KubernetesClustersSupplier = {
async getClusters() {
return clusters;
},
};
kubernetesFanOutHandler = {
getKubernetesObjectsByEntity: jest.fn(),
} as any;
const { router } = await KubernetesBuilder.createBuilder({ config, logger })
.setObjectsProvider(kubernetesFanOutHandler)
.setClusterSupplier(clusterSupplier)
.build();
app = express().use(router);
});
@@ -130,5 +155,86 @@ describe('router', () => {
expect(response.status).toEqual(500);
expect(response.body).toEqual({ error: 'some internal error' });
});
it('custom service locator', async () => {
const logger = getVoidLogger();
const someCluster: ClusterDetails = {
name: 'some-cluster',
authProvider: 'serviceAccount',
url: 'https://localhost:1234',
serviceAccountToken: 'someToken',
};
const clusters: ClusterDetails[] = [
someCluster,
{
name: 'some-other-cluster',
url: 'https://localhost:1235',
authProvider: 'google',
},
];
const clusterSupplier: KubernetesClustersSupplier = {
async getClusters() {
return clusters;
},
};
const pod = {
metadata: {
name: 'pod1',
},
};
const result: ObjectsByEntityResponse = {
items: [
{
cluster: {
name: someCluster.name,
},
errors: [],
resources: [
{
type: 'pods',
resources: [pod],
},
],
},
],
};
const serviceLocator: KubernetesServiceLocator = {
getClustersByServiceId(_serviceId: string): Promise<ClusterDetails[]> {
return Promise.resolve([someCluster]);
},
};
const fetcher: KubernetesFetcher = {
fetchObjectsForService(
_params: ObjectFetchParams,
): Promise<FetchResponseWrapper> {
return Promise.resolve({
errors: [],
responses: [
{
type: 'pods',
resources: [pod],
},
],
});
},
};
const { router } = await KubernetesBuilder.createBuilder({
logger,
config,
})
.setClusterSupplier(clusterSupplier)
.setServiceLocator(serviceLocator)
.setFetcher(fetcher)
.build();
app = express().use(router);
const response = await request(app).post('/services/test-service');
expect(response.body).toEqual(result);
expect(response.status).toEqual(200);
});
});
});
@@ -0,0 +1,254 @@
/*
* 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 express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { getCombinedClusterDetails } from '../cluster-locator';
import { MultiTenantServiceLocator } from '../service-locator/MultiTenantServiceLocator';
import {
ClusterDetails,
KubernetesObjectTypes,
ServiceLocatorMethod,
CustomResource,
KubernetesObjectsProvider,
ObjectsByEntityRequest,
KubernetesClustersSupplier,
KubernetesFetcher,
KubernetesServiceLocator,
KubernetesObjectsProviderOptions,
} from '../types/types';
import { KubernetesClientProvider } from './KubernetesClientProvider';
import {
DEFAULT_OBJECTS,
KubernetesFanOutHandler,
} from './KubernetesFanOutHandler';
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
export interface KubernetesEnvironment {
logger: Logger;
config: Config;
}
export class KubernetesBuilder {
private clusterSupplier?: KubernetesClustersSupplier;
private objectsProvider?: KubernetesObjectsProvider;
private fetcher?: KubernetesFetcher;
private serviceLocator?: KubernetesServiceLocator;
static createBuilder(env: KubernetesEnvironment) {
return new KubernetesBuilder(env);
}
constructor(protected readonly env: KubernetesEnvironment) {}
public async build() {
const logger = this.env.logger;
logger.info('Initializing Kubernetes backend');
const customResources = this.buildCustomResources();
const fetcher = this.fetcher ?? this.buildFetcher();
const clusterSupplier = this.clusterSupplier ?? this.buildClusterSupplier();
const clusterDetails = await this.fetchClusterDetails(clusterSupplier);
const serviceLocator =
this.serviceLocator ??
this.buildServiceLocator(this.getServiceLocatorMethod(), clusterDetails);
const objectsProvider =
this.objectsProvider ??
this.buildObjectsProvider({
logger,
fetcher,
serviceLocator,
customResources,
objectTypesToFetch: this.getObjectTypesToFetch(),
});
const router = this.buildRouter(objectsProvider, clusterDetails);
return {
clusterDetails,
clusterSupplier,
customResources,
fetcher,
objectsProvider,
router,
serviceLocator,
};
}
public setClusterSupplier(clusterSupplier?: KubernetesClustersSupplier) {
this.clusterSupplier = clusterSupplier;
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;
}
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'),
} as CustomResource),
);
this.env.logger.info(
`action=LoadingCustomResources numOfCustomResources=${customResources.length}`,
);
return customResources;
}
protected buildClusterSupplier(): KubernetesClustersSupplier {
const config = this.env.config;
return {
getClusters() {
return getCombinedClusterDetails(config);
},
};
}
protected buildObjectsProvider(
options: KubernetesObjectsProviderOptions,
): KubernetesObjectsProvider {
return new KubernetesFanOutHandler(options);
}
protected buildFetcher(): KubernetesFetcher {
return new KubernetesClientBasedFetcher({
kubernetesClientProvider: new KubernetesClientProvider(),
logger: this.env.logger,
});
}
protected buildServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator {
switch (method) {
case 'multiTenant':
return this.buildMultiTenantServiceLocator(clusterDetails);
case 'http':
return this.buildHttpServiceLocator(clusterDetails);
default:
throw new Error(
`Unsupported kubernetes.clusterLocatorMethod "${method}"`,
);
}
}
protected buildMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator {
return new MultiTenantServiceLocator(clusterDetails);
}
protected buildHttpServiceLocator(
_clusterDetails: ClusterDetails[],
): KubernetesServiceLocator {
throw new Error('not implemented');
}
protected buildRouter(
objectsProvider: KubernetesObjectsProvider,
clusterDetails: ClusterDetails[],
): express.Router {
const logger = this.env.logger;
const router = Router();
router.use(express.json());
router.post('/services/:serviceId', async (req, res) => {
const serviceId = req.params.serviceId;
const requestBody: ObjectsByEntityRequest = req.body;
try {
const response = await objectsProvider.getKubernetesObjectsByEntity(
requestBody,
);
res.json(response);
} catch (e) {
logger.error(
`action=retrieveObjectsByServiceId service=${serviceId}, error=${e}`,
);
res.status(500).json({ error: e.message });
}
});
router.get('/clusters', async (_, res) => {
res.json({
items: clusterDetails.map(cd => ({
name: cd.name,
dashboardUrl: cd.dashboardUrl,
authProvider: cd.authProvider,
})),
});
});
return router;
}
protected async fetchClusterDetails(
clusterSupplier: KubernetesClustersSupplier,
) {
const clusterDetails = await clusterSupplier.getClusters();
this.env.logger.info(
`action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`,
);
return clusterDetails;
}
protected getServiceLocatorMethod() {
return this.env.config.getString(
'kubernetes.serviceLocatorMethod.type',
) as ServiceLocatorMethod;
}
protected getObjectTypesToFetch() {
const objectTypesToFetchStrings = this.env.config.getOptionalStringArray(
'kubernetes.objectTypes',
) as KubernetesObjectTypes[];
let objectTypesToFetch;
if (objectTypesToFetchStrings) {
objectTypesToFetch = DEFAULT_OBJECTS.filter(obj =>
objectTypesToFetchStrings.includes(obj.objectType),
);
}
return objectTypesToFetch;
}
}
@@ -19,15 +19,17 @@ import {
ClusterDetails,
CustomResource,
KubernetesFetcher,
KubernetesObjectsProviderOptions,
KubernetesServiceLocator,
ObjectsByEntityRequest,
ObjectToFetch,
} from '../types/types';
import {
ClusterObjects,
KubernetesRequestBody,
} from '@backstage/plugin-kubernetes-common';
import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types';
import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator';
import {
ClusterObjects,
ObjectsByEntityResponse,
} from '@backstage/plugin-kubernetes-common';
export const DEFAULT_OBJECTS: ObjectToFetch[] = [
{
@@ -74,13 +76,10 @@ export const DEFAULT_OBJECTS: ObjectToFetch[] = [
},
];
export interface KubernetesFanOutHandlerOptions {
logger: Logger;
fetcher: KubernetesFetcher;
serviceLocator: KubernetesServiceLocator;
customResources: CustomResource[];
objectTypesToFetch?: ObjectToFetch[];
}
export interface KubernetesFanOutHandlerOptions
extends KubernetesObjectsProviderOptions {}
export interface KubernetesRequestBody extends ObjectsByEntityRequest {}
export class KubernetesFanOutHandler {
private readonly logger: Logger;
@@ -103,7 +102,9 @@ export class KubernetesFanOutHandler {
this.objectTypesToFetch = new Set(objectTypesToFetch);
}
async getKubernetesObjectsByEntity(requestBody: KubernetesRequestBody) {
async getKubernetesObjectsByEntity(
requestBody: KubernetesRequestBody,
): Promise<ObjectsByEntityResponse> {
const entityName =
requestBody.entity?.metadata?.annotations?.[
'backstage.io/kubernetes-id'
+20 -133
View File
@@ -15,26 +15,10 @@
*/
import { Config } from '@backstage/config';
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { getCombinedClusterDetails } from '../cluster-locator';
import { MultiTenantServiceLocator } from '../service-locator/MultiTenantServiceLocator';
import {
ClusterDetails,
KubernetesClustersSupplier,
KubernetesObjectTypes,
KubernetesServiceLocator,
ServiceLocatorMethod,
CustomResource,
} from '../types/types';
import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common';
import { KubernetesClientProvider } from './KubernetesClientProvider';
import {
KubernetesFanOutHandler,
DEFAULT_OBJECTS,
} from './KubernetesFanOutHandler';
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
import { KubernetesClustersSupplier } from '../types/types';
import express from 'express';
import { KubernetesBuilder } from './KubernetesBuilder';
export interface RouterOptions {
logger: Logger;
@@ -42,121 +26,24 @@ export interface RouterOptions {
clusterSupplier?: KubernetesClustersSupplier;
}
const getServiceLocator = (
config: Config,
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator => {
const serviceLocatorMethod = config.getString(
'kubernetes.serviceLocatorMethod.type',
) as ServiceLocatorMethod;
switch (serviceLocatorMethod) {
case 'multiTenant':
return new MultiTenantServiceLocator(clusterDetails);
case 'http':
throw new Error('not implemented');
default:
throw new Error(
`Unsupported kubernetes.clusterLocatorMethod "${serviceLocatorMethod}"`,
);
}
};
export const makeRouter = (
logger: Logger,
kubernetesFanOutHandler: KubernetesFanOutHandler,
clusterDetails: ClusterDetails[],
): express.Router => {
const router = Router();
router.use(express.json());
router.post('/services/:serviceId', async (req, res) => {
const serviceId = req.params.serviceId;
const requestBody: KubernetesRequestBody = req.body;
try {
const response =
await kubernetesFanOutHandler.getKubernetesObjectsByEntity(requestBody);
res.json(response);
} catch (e) {
logger.error(
`action=retrieveObjectsByServiceId service=${serviceId}, error=${e}`,
);
res.status(500).json({ error: e.message });
}
});
router.get('/clusters', async (_, res) => {
res.json({
items: clusterDetails.map(cd => ({
name: cd.name,
dashboardUrl: cd.dashboardUrl,
authProvider: cd.authProvider,
})),
});
});
return router;
};
/**
* creates and configure a new router for handling the kubernetes backend APIs
* @param options - specifies the options required by this plugin
* @returns a new router
* @deprecated Please use the new KubernetesBuilder instead like this
* ```
* import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
* const { router } = await KubernetesBuilder.createBuilder({
* logger,
* config,
* }).build();
* ```
*/
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const logger = options.logger;
logger.info('Initializing Kubernetes backend');
const customResources: CustomResource[] = (
options.config.getOptionalConfigArray('kubernetes.customResources') ?? []
).map(
c =>
({
group: c.getString('group'),
apiVersion: c.getString('apiVersion'),
plural: c.getString('plural'),
objectType: 'customresources',
} as CustomResource),
);
logger.info(
`action=LoadingCustomResources numOfCustomResources=${customResources.length}`,
);
const fetcher = new KubernetesClientBasedFetcher({
kubernetesClientProvider: new KubernetesClientProvider(),
logger,
});
let clusterDetails: ClusterDetails[];
if (options.clusterSupplier) {
clusterDetails = await options.clusterSupplier.getClusters();
} else {
clusterDetails = await getCombinedClusterDetails(options.config);
}
logger.info(
`action=loadClusterDetails numOfClustersLoaded=${clusterDetails.length}`,
);
const serviceLocator = getServiceLocator(options.config, clusterDetails);
const objectTypesToFetchStrings = options.config.getOptionalStringArray(
'kubernetes.objectTypes',
) as KubernetesObjectTypes[];
let objectTypesToFetch;
if (objectTypesToFetchStrings) {
objectTypesToFetch = DEFAULT_OBJECTS.filter(obj =>
objectTypesToFetchStrings.includes(obj.objectType),
);
}
const kubernetesFanOutHandler = new KubernetesFanOutHandler({
logger,
fetcher,
serviceLocator,
customResources,
objectTypesToFetch,
});
return makeRouter(logger, kubernetesFanOutHandler, clusterDetails);
const { router } = await KubernetesBuilder.createBuilder(options)
.setClusterSupplier(options.clusterSupplier)
.build();
return router;
}
@@ -14,9 +14,13 @@
* limitations under the License.
*/
import { Logger } from 'winston';
import { Config } from '@backstage/config';
import type {
FetchResponse,
KubernetesFetchError,
KubernetesRequestBody,
ObjectsByEntityResponse,
} from '@backstage/plugin-kubernetes-common';
export interface ObjectFetchParams {
@@ -121,3 +125,42 @@ export interface AWSClusterDetails extends ClusterDetails {
assumeRole?: string;
externalId?: string;
}
export interface KubernetesObjectsProviderOptions {
logger: Logger;
fetcher: KubernetesFetcher;
serviceLocator: KubernetesServiceLocator;
customResources: CustomResource[];
objectTypesToFetch?: ObjectToFetch[];
}
export type ObjectsByEntityRequest = KubernetesRequestBody;
export interface KubernetesObjectsProvider {
getKubernetesObjectsByEntity(
request: ObjectsByEntityRequest,
): Promise<ObjectsByEntityResponse>;
}
export interface KubernetesFactory {
createKubernetesClustersSupplier(config: Config): KubernetesClustersSupplier;
createKubernetesObjectsProvider(
options: KubernetesObjectsProviderOptions,
): KubernetesObjectsProvider;
createKubernetesFetcher(logger: Logger): KubernetesFetcher;
createServiceLocator(
method: ServiceLocatorMethod,
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
createMultiTenantServiceLocator(
clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
createHttpServiceLocator(
_clusterDetails: ClusterDetails[],
): KubernetesServiceLocator;
}