diff --git a/.changeset/unlucky-laws-doubt.md b/.changeset/unlucky-laws-doubt.md new file mode 100644 index 0000000000..45cb5237be --- /dev/null +++ b/.changeset/unlucky-laws-doubt.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-kubernetes': patch +--- + +Refactor kubernetes fetcher to reduce boilerplate code diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 3d4c275731..3d52c4ff43 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -46,19 +46,15 @@ export function createRouter(options: RouterOptions): Promise; // Warning: (ae-missing-release-tag) "CustomResource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface CustomResource { +export interface CustomResource extends ObjectToFetch { // (undocumented) - apiVersion: string; - // (undocumented) - group: string; - // (undocumented) - plural: string; + objectType: 'customresources'; } // Warning: (ae-missing-release-tag) "DEFAULT_OBJECTS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const DEFAULT_OBJECTS: KubernetesObjectTypes[]; +export const DEFAULT_OBJECTS: ObjectToFetch[]; // Warning: (ae-missing-release-tag) "FetchResponseWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -139,11 +135,25 @@ export interface ObjectFetchParams { // (undocumented) labelSelector: string; // (undocumented) - objectTypesToFetch: Set; + objectTypesToFetch: Set; // (undocumented) serviceId: string; } +// 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) +export interface ObjectToFetch { + // (undocumented) + apiVersion: string; + // (undocumented) + group: string; + // (undocumented) + objectType: KubernetesObjectTypes; + // (undocumented) + plural: string; +} + // Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index fd403f49d1..d520c66d7a 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -19,8 +19,8 @@ import { ClusterDetails, CustomResource, KubernetesFetcher, - KubernetesObjectTypes, KubernetesServiceLocator, + ObjectToFetch, } from '../types/types'; import { ClusterObjects, @@ -29,14 +29,49 @@ import { import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types'; import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator'; -export const DEFAULT_OBJECTS: KubernetesObjectTypes[] = [ - 'pods', - 'services', - 'configmaps', - 'deployments', - 'replicasets', - 'horizontalpodautoscalers', - 'ingresses', +export const DEFAULT_OBJECTS: ObjectToFetch[] = [ + { + group: '', + apiVersion: 'v1', + plural: 'pods', + objectType: 'pods', + }, + { + group: '', + apiVersion: 'v1', + plural: 'services', + objectType: 'services', + }, + { + group: '', + apiVersion: 'v1', + plural: 'configmaps', + objectType: 'configmaps', + }, + { + group: 'apps', + apiVersion: 'v1', + plural: 'deployments', + objectType: 'deployments', + }, + { + group: 'apps', + apiVersion: 'v1', + plural: 'replicasets', + objectType: 'replicasets', + }, + { + group: 'autoscaling', + apiVersion: 'v1', + plural: 'horizontalpodautoscalers', + objectType: 'horizontalpodautoscalers', + }, + { + group: 'networking.k8s.io', + apiVersion: 'v1', + plural: 'ingresses', + objectType: 'ingresses', + }, ]; export interface KubernetesFanOutHandlerOptions { @@ -44,7 +79,7 @@ export interface KubernetesFanOutHandlerOptions { fetcher: KubernetesFetcher; serviceLocator: KubernetesServiceLocator; customResources: CustomResource[]; - objectTypesToFetch?: KubernetesObjectTypes[]; + objectTypesToFetch?: ObjectToFetch[]; } export class KubernetesFanOutHandler { @@ -52,7 +87,7 @@ export class KubernetesFanOutHandler { private readonly fetcher: KubernetesFetcher; private readonly serviceLocator: KubernetesServiceLocator; private readonly customResources: CustomResource[]; - private readonly objectTypesToFetch: KubernetesObjectTypes[]; + private readonly objectTypesToFetch: Set; constructor({ logger, @@ -65,7 +100,7 @@ export class KubernetesFanOutHandler { this.fetcher = fetcher; this.serviceLocator = serviceLocator; this.customResources = customResources; - this.objectTypesToFetch = objectTypesToFetch; + this.objectTypesToFetch = new Set(objectTypesToFetch); } async getKubernetesObjectsByEntity(requestBody: KubernetesRequestBody) { @@ -109,7 +144,7 @@ export class KubernetesFanOutHandler { .fetchObjectsForService({ serviceId: entityName, clusterDetails: clusterDetailsItem, - objectTypesToFetch: new Set(this.objectTypesToFetch), + objectTypesToFetch: this.objectTypesToFetch, labelSelector, customResources: this.customResources, }) diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts index 557e994a2f..e86901ef92 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts @@ -16,6 +16,22 @@ import { getVoidLogger } from '@backstage/backend-common'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; +import { ObjectToFetch } from '../types/types'; + +const OBJECTS_TO_FETCH = new Set([ + { + group: '', + apiVersion: 'v1', + plural: 'pods', + objectType: 'pods', + }, + { + group: '', + apiVersion: 'v1', + plural: 'services', + objectType: 'services', + }, +]); describe('KubernetesFetcher', () => { let clientMock: any; @@ -25,15 +41,11 @@ describe('KubernetesFetcher', () => { beforeEach(() => { jest.resetAllMocks(); clientMock = { - listPodForAllNamespaces: jest.fn(), - listServiceForAllNamespaces: jest.fn(), + listClusterCustomObject: jest.fn(), + addInterceptor: jest.fn(), }; kubernetesClientProvider = { - getCoreClientByClusterDetails: jest.fn(() => clientMock), - getAppsClientByClusterDetails: jest.fn(() => clientMock), - getAutoscalingClientByClusterDetails: jest.fn(() => clientMock), - getNetworkingBeta1Client: jest.fn(() => clientMock), getCustomObjectsClient: jest.fn(() => clientMock), }; @@ -44,7 +56,7 @@ describe('KubernetesFetcher', () => { }); const testErrorResponse = async (errorResponse: any, expectedResult: any) => { - clientMock.listPodForAllNamespaces.mockResolvedValueOnce({ + clientMock.listClusterCustomObject.mockResolvedValueOnce({ body: { items: [ { @@ -56,7 +68,7 @@ describe('KubernetesFetcher', () => { }, }); - clientMock.listServiceForAllNamespaces.mockRejectedValue(errorResponse); + clientMock.listClusterCustomObject.mockRejectedValue(errorResponse); const result = await sut.fetchObjectsForService({ serviceId: 'some-service', @@ -66,7 +78,7 @@ describe('KubernetesFetcher', () => { serviceAccountToken: 'token', authProvider: 'serviceAccount', }, - objectTypesToFetch: new Set(['pods', 'services']), + objectTypesToFetch: OBJECTS_TO_FETCH, labelSelector: '', customResources: [], }); @@ -87,19 +99,35 @@ describe('KubernetesFetcher', () => { ], }); - expect(clientMock.listPodForAllNamespaces.mock.calls.length).toBe(1); - expect(clientMock.listServiceForAllNamespaces.mock.calls.length).toBe(1); + expect(clientMock.listClusterCustomObject.mock.calls.length).toBe(2); + + expect(clientMock.listClusterCustomObject.mock.calls[0]).toEqual([ + '', + 'v1', + 'pods', + '', + '', + '', + 'backstage.io/kubernetes-id=some-service', + ]); + + expect(clientMock.listClusterCustomObject.mock.calls[1]).toEqual([ + '', + 'v1', + 'services', + '', + '', + '', + 'backstage.io/kubernetes-id=some-service', + ]); expect( - kubernetesClientProvider.getAppsClientByClusterDetails.mock.calls.length, - ).toBe(2); - expect( - kubernetesClientProvider.getCoreClientByClusterDetails.mock.calls.length, + kubernetesClientProvider.getCustomObjectsClient.mock.calls.length, ).toBe(2); }; it('should return pods, services', async () => { - clientMock.listPodForAllNamespaces.mockResolvedValueOnce({ + clientMock.listClusterCustomObject.mockResolvedValueOnce({ body: { items: [ { @@ -111,7 +139,7 @@ describe('KubernetesFetcher', () => { }, }); - clientMock.listServiceForAllNamespaces.mockResolvedValueOnce({ + clientMock.listClusterCustomObject.mockResolvedValueOnce({ body: { items: [ { @@ -131,7 +159,7 @@ describe('KubernetesFetcher', () => { serviceAccountToken: 'token', authProvider: 'serviceAccount', }, - objectTypesToFetch: new Set(['pods', 'services']), + objectTypesToFetch: OBJECTS_TO_FETCH, labelSelector: '', customResources: [], }); @@ -162,41 +190,160 @@ describe('KubernetesFetcher', () => { ], }); - expect(clientMock.listPodForAllNamespaces.mock.calls.length).toBe(1); - expect(clientMock.listServiceForAllNamespaces.mock.calls.length).toBe(1); + expect(clientMock.listClusterCustomObject.mock.calls.length).toBe(2); + + expect(clientMock.listClusterCustomObject.mock.calls[0]).toEqual([ + '', + 'v1', + 'pods', + '', + '', + '', + 'backstage.io/kubernetes-id=some-service', + ]); + + expect(clientMock.listClusterCustomObject.mock.calls[1]).toEqual([ + '', + 'v1', + 'services', + '', + '', + '', + 'backstage.io/kubernetes-id=some-service', + ]); expect( - kubernetesClientProvider.getAppsClientByClusterDetails.mock.calls.length, - ).toBe(2); - expect( - kubernetesClientProvider.getCoreClientByClusterDetails.mock.calls.length, + kubernetesClientProvider.getCustomObjectsClient.mock.calls.length, ).toBe(2); }); - it('should throw error on unknown type', () => { - expect(() => - sut.fetchObjectsForService({ - serviceId: 'some-service', - clusterDetails: { - name: 'cluster1', - url: 'http://localhost:9999', - serviceAccountToken: 'token', - authProvider: 'serviceAccount', + it('should return pods, services and customobjects', async () => { + clientMock.listClusterCustomObject.mockResolvedValueOnce({ + body: { + items: [ + { + metadata: { + name: 'pod-name', + }, + }, + ], + }, + }); + + clientMock.listClusterCustomObject.mockResolvedValueOnce({ + body: { + items: [ + { + metadata: { + name: 'service-name', + }, + }, + ], + }, + }); + + clientMock.listClusterCustomObject.mockResolvedValueOnce({ + body: { + items: [ + { + metadata: { + name: 'something-else', + }, + }, + ], + }, + }); + + const result = await sut.fetchObjectsForService({ + serviceId: 'some-service', + clusterDetails: { + name: 'cluster1', + url: 'http://localhost:9999', + serviceAccountToken: 'token', + authProvider: 'serviceAccount', + }, + objectTypesToFetch: OBJECTS_TO_FETCH, + labelSelector: '', + customResources: [ + { + objectType: 'customresources', + group: 'some-group', + apiVersion: 'v2', + plural: 'things', }, - objectTypesToFetch: new Set(['foo']), - labelSelector: '', - customResources: [], - }), - ).toThrow('unrecognised type=foo'); + ], + }); - expect(clientMock.listPodForAllNamespaces.mock.calls.length).toBe(0); - expect(clientMock.listServiceForAllNamespaces.mock.calls.length).toBe(0); + expect(result).toStrictEqual({ + errors: [], + responses: [ + { + type: 'pods', + resources: [ + { + metadata: { + name: 'pod-name', + }, + }, + ], + }, + { + type: 'services', + resources: [ + { + metadata: { + name: 'service-name', + }, + }, + ], + }, + { + type: 'customresources', + resources: [ + { + metadata: { + name: 'something-else', + }, + }, + ], + }, + ], + }); + + expect(clientMock.listClusterCustomObject.mock.calls.length).toBe(3); + + expect(clientMock.listClusterCustomObject.mock.calls[0]).toEqual([ + '', + 'v1', + 'pods', + '', + '', + '', + 'backstage.io/kubernetes-id=some-service', + ]); + + expect(clientMock.listClusterCustomObject.mock.calls[1]).toEqual([ + '', + 'v1', + 'services', + '', + '', + '', + 'backstage.io/kubernetes-id=some-service', + ]); + + expect(clientMock.listClusterCustomObject.mock.calls[2]).toEqual([ + 'some-group', + 'v2', + 'things', + '', + '', + '', + 'backstage.io/kubernetes-id=some-service', + ]); expect( - kubernetesClientProvider.getAppsClientByClusterDetails.mock.calls.length, - ).toBe(0); - expect( - kubernetesClientProvider.getCoreClientByClusterDetails.mock.calls.length, - ).toBe(0); + kubernetesClientProvider.getCustomObjectsClient.mock.calls.length, + ).toBe(3); }); // they're in testErrorResponse // eslint-disable-next-line jest/expect-expect @@ -283,7 +430,7 @@ describe('KubernetesFetcher', () => { ); }); it('should always add a labelSelector query', async () => { - clientMock.listPodForAllNamespaces.mockResolvedValueOnce({ + clientMock.listClusterCustomObject.mockResolvedValueOnce({ body: { items: [ { @@ -295,7 +442,7 @@ describe('KubernetesFetcher', () => { }, }); - clientMock.listServiceForAllNamespaces.mockResolvedValueOnce({ + clientMock.listClusterCustomObject.mockResolvedValueOnce({ body: { items: [ { @@ -315,12 +462,12 @@ describe('KubernetesFetcher', () => { serviceAccountToken: 'token', authProvider: 'serviceAccount', }, - objectTypesToFetch: new Set(['pods', 'services']), + objectTypesToFetch: OBJECTS_TO_FETCH, labelSelector: '', customResources: [], }); - const mockCall = clientMock.listPodForAllNamespaces.mock.calls[0]; + const mockCall = clientMock.listClusterCustomObject.mock.calls[0]; const actualSelector = mockCall[mockCall.length - 1]; const expectedSelector = 'backstage.io/kubernetes-id=some-service'; expect(actualSelector).toBe(expectedSelector); diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts index e17b47e17a..a08f339a20 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts @@ -18,16 +18,8 @@ import { AppsV1Api, AutoscalingV1Api, CoreV1Api, - ExtensionsV1beta1Ingress, NetworkingV1beta1Api, - V1ConfigMap, - V1Deployment, - V1HorizontalPodAutoscaler, - V1Pod, - V1ReplicaSet, } from '@kubernetes/client-node'; -import { V1Service } from '@kubernetes/client-node/dist/gen/model/v1Service'; -import http from 'http'; import lodash, { Dictionary } from 'lodash'; import { Logger } from 'winston'; import { @@ -36,7 +28,7 @@ import { KubernetesFetcher, KubernetesObjectTypes, ObjectFetchParams, - CustomResource, + ObjectToFetch, } from '../types/types'; import { FetchResponse, @@ -88,17 +80,6 @@ const statusCodeToErrorType = (statusCode: number): KubernetesErrorTypes => { } }; -const captureKubernetesErrorsRethrowOthers = (e: any): KubernetesFetchError => { - if (e.response && e.response.statusCode) { - return { - errorType: statusCodeToErrorType(e.response.statusCode), - statusCode: e.response.statusCode, - resourcePath: e.response.request.uri.pathname, - }; - } - throw e; -}; - export class KubernetesClientBasedFetcher implements KubernetesFetcher { private readonly kubernetesClientProvider: KubernetesClientProvider; private readonly logger: Logger; @@ -114,202 +95,60 @@ export class KubernetesClientBasedFetcher implements KubernetesFetcher { fetchObjectsForService( params: ObjectFetchParams, ): Promise { - const fetchResults = Array.from(params.objectTypesToFetch).map(type => { - return this.fetchByObjectType( - params.clusterDetails, - type, - params.labelSelector || - `backstage.io/kubernetes-id=${params.serviceId}`, - ).catch(captureKubernetesErrorsRethrowOthers); - }); + const fetchResults = Array.from(params.objectTypesToFetch) + .concat(params.customResources) + .map(toFetch => { + return this.fetchResource( + params.clusterDetails, + toFetch, + params.labelSelector || + `backstage.io/kubernetes-id=${params.serviceId}`, + toFetch.objectType, + ).catch(this.captureKubernetesErrorsRethrowOthers.bind(this)); + }); - const customObjectsFetchResults = params.customResources.map(cr => { - return this.fetchCustomResource( - params.clusterDetails, - cr, - params.labelSelector || - `backstage.io/kubernetes-id=${params.serviceId}`, - ).catch(captureKubernetesErrorsRethrowOthers); - }); - - return Promise.all(fetchResults.concat(customObjectsFetchResults)).then( - fetchResultsToResponseWrapper, - ); + return Promise.all(fetchResults).then(fetchResultsToResponseWrapper); } - // TODO could probably do with a tidy up - private fetchByObjectType( - clusterDetails: ClusterDetails, - type: KubernetesObjectTypes, - labelSelector: string, - ): Promise { - switch (type) { - case 'pods': - return this.fetchPodsForService(clusterDetails, labelSelector).then( - r => ({ - type: type, - resources: r, - }), - ); - case 'configmaps': - return this.fetchConfigMapsForService( - clusterDetails, - labelSelector, - ).then(r => ({ type: type, resources: r })); - case 'deployments': - return this.fetchDeploymentsForService( - clusterDetails, - labelSelector, - ).then(r => ({ type: type, resources: r })); - case 'replicasets': - return this.fetchReplicaSetsForService( - clusterDetails, - labelSelector, - ).then(r => ({ type: type, resources: r })); - case 'services': - return this.fetchServicesForService(clusterDetails, labelSelector).then( - r => ({ type: type, resources: r }), - ); - case 'horizontalpodautoscalers': - return this.fetchHorizontalPodAutoscalersForService( - clusterDetails, - labelSelector, - ).then(r => ({ type: type, resources: r })); - case 'ingresses': - return this.fetchIngressesForService( - clusterDetails, - labelSelector, - ).then(r => ({ type: type, resources: r })); - default: - // unrecognised type - throw new Error(`unrecognised type=${type}`); + private captureKubernetesErrorsRethrowOthers(e: any): KubernetesFetchError { + if (e.response && e.response.statusCode) { + this.logger.info( + `statusCode=${e.response.statusCode} for resource ${e.response.request.uri.pathname}`, + ); + return { + errorType: statusCodeToErrorType(e.response.statusCode), + statusCode: e.response.statusCode, + resourcePath: e.response.request.uri.pathname, + }; } + throw e; } - private fetchCustomResource( + private fetchResource( clusterDetails: ClusterDetails, - customResource: CustomResource, + resource: ObjectToFetch, labelSelector: string, + objectType: KubernetesObjectTypes, ): Promise { const customObjects = this.kubernetesClientProvider.getCustomObjectsClient(clusterDetails); + customObjects.addInterceptor((requestOptions: any) => { + requestOptions.uri = requestOptions.uri.replace('/apis//v1/', '/api/v1/'); + }); + return customObjects .listClusterCustomObject( - customResource.group, - customResource.apiVersion, - customResource.plural, + resource.group, + resource.apiVersion, + resource.plural, '', '', '', labelSelector, ) .then(r => { - return { type: 'customresources', resources: (r.body as any).items }; + return { type: objectType, resources: (r.body as any).items }; }); } - - private singleClusterFetch( - clusterDetails: ClusterDetails, - fn: ( - client: Clients, - ) => Promise<{ body: { items: Array }; response: http.IncomingMessage }>, - ): Promise> { - const core = - this.kubernetesClientProvider.getCoreClientByClusterDetails( - clusterDetails, - ); - const apps = - this.kubernetesClientProvider.getAppsClientByClusterDetails( - clusterDetails, - ); - const autoscaling = - this.kubernetesClientProvider.getAutoscalingClientByClusterDetails( - clusterDetails, - ); - const networkingBeta1 = - this.kubernetesClientProvider.getNetworkingBeta1Client(clusterDetails); - - this.logger.debug(`calling cluster=${clusterDetails.name}`); - return fn({ core, apps, autoscaling, networkingBeta1 }).then(({ body }) => { - return body.items; - }); - } - - private fetchServicesForService( - clusterDetails: ClusterDetails, - labelSelector: string, - ): Promise> { - return this.singleClusterFetch(clusterDetails, ({ core }) => - core.listServiceForAllNamespaces(false, '', '', labelSelector), - ); - } - - private fetchPodsForService( - clusterDetails: ClusterDetails, - labelSelector: string, - ): Promise> { - return this.singleClusterFetch(clusterDetails, ({ core }) => - core.listPodForAllNamespaces(false, '', '', labelSelector), - ); - } - - private fetchConfigMapsForService( - clusterDetails: ClusterDetails, - labelSelector: string, - ): Promise> { - return this.singleClusterFetch(clusterDetails, ({ core }) => - core.listConfigMapForAllNamespaces(false, '', '', labelSelector), - ); - } - - private fetchDeploymentsForService( - clusterDetails: ClusterDetails, - labelSelector: string, - ): Promise> { - return this.singleClusterFetch(clusterDetails, ({ apps }) => - apps.listDeploymentForAllNamespaces(false, '', '', labelSelector), - ); - } - - private fetchReplicaSetsForService( - clusterDetails: ClusterDetails, - labelSelector: string, - ): Promise> { - return this.singleClusterFetch(clusterDetails, ({ apps }) => - apps.listReplicaSetForAllNamespaces(false, '', '', labelSelector), - ); - } - - private fetchHorizontalPodAutoscalersForService( - clusterDetails: ClusterDetails, - labelSelector: string, - ): Promise> { - return this.singleClusterFetch( - clusterDetails, - ({ autoscaling }) => - autoscaling.listHorizontalPodAutoscalerForAllNamespaces( - false, - '', - '', - labelSelector, - ), - ); - } - - private fetchIngressesForService( - clusterDetails: ClusterDetails, - labelSelector: string, - ): Promise> { - return this.singleClusterFetch( - clusterDetails, - ({ networkingBeta1 }) => - networkingBeta1.listIngressForAllNamespaces( - false, - '', - '', - labelSelector, - ), - ); - } } diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index d8f7c787d9..3fc32dac8b 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -30,7 +30,10 @@ import { } from '../types/types'; import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { KubernetesClientProvider } from './KubernetesClientProvider'; -import { KubernetesFanOutHandler } from './KubernetesFanOutHandler'; +import { + KubernetesFanOutHandler, + DEFAULT_OBJECTS, +} from './KubernetesFanOutHandler'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; export interface RouterOptions { @@ -109,6 +112,7 @@ export async function createRouter( group: c.getString('group'), apiVersion: c.getString('apiVersion'), plural: c.getString('plural'), + objectType: 'customresources', } as CustomResource), ); @@ -134,10 +138,18 @@ export async function createRouter( ); const serviceLocator = getServiceLocator(options.config, clusterDetails); - const objectTypesToFetch = options.config.getOptionalStringArray( + 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, diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index eec1243b19..cff87f265a 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -19,12 +19,6 @@ import type { KubernetesFetchError, } from '@backstage/plugin-kubernetes-common'; -export interface CustomResource { - group: string; - apiVersion: string; - plural: string; -} - export interface ObjectFetchParams { serviceId: string; clusterDetails: @@ -32,7 +26,7 @@ export interface ObjectFetchParams { | GKEClusterDetails | ServiceAccountClusterDetails | ClusterDetails; - objectTypesToFetch: Set; + objectTypesToFetch: Set; labelSelector: string; customResources: CustomResource[]; } @@ -52,6 +46,17 @@ export interface FetchResponseWrapper { // TODO fairly sure there's a easier way to do this +export interface ObjectToFetch { + objectType: KubernetesObjectTypes; + group: string; + apiVersion: string; + plural: string; +} + +export interface CustomResource extends ObjectToFetch { + objectType: 'customresources'; +} + export type KubernetesObjectTypes = | 'pods' | 'services'