From ac0e1acf7847f46fdb41b6f9df48337bbc680664 Mon Sep 17 00:00:00 2001 From: Florian Bauer <45170246+derbauer97@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:15:39 +0100 Subject: [PATCH] Kubernetes: add option to fetch additional objectTypes which are not present in DEFAULT_OBJECTS (#28150) * Fixed lack of secrets Signed-off-by: Florian Fl Bauer * add tests Signed-off-by: Florian Fl Bauer * remove log Signed-off-by: Florian Fl Bauer --------- Signed-off-by: Florian Fl Bauer --- .changeset/gentle-actors-sleep.md | 7 ++ docs/features/kubernetes/configuration.md | 6 +- .../src/service/KubernetesBuilder.ts | 3 +- .../service/KubernetesFanOutHandler.test.ts | 86 ++++++++++++++++++- .../src/service/KubernetesFanOutHandler.ts | 10 +++ plugins/kubernetes-common/report.api.md | 12 ++- plugins/kubernetes-common/src/types.ts | 10 ++- plugins/kubernetes-node/report.api.md | 3 +- plugins/kubernetes-node/src/types/types.ts | 3 +- 9 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 .changeset/gentle-actors-sleep.md diff --git a/.changeset/gentle-actors-sleep.md b/.changeset/gentle-actors-sleep.md new file mode 100644 index 0000000000..8ecea3e367 --- /dev/null +++ b/.changeset/gentle-actors-sleep.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-kubernetes-common': patch +'@backstage/plugin-kubernetes-node': patch +--- + +Fixed the lack of `secrets` to fetch from the kubernetes api by adding option to specify additional Objects which are not part of Default Objects diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index effa099182..1a921103b1 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -592,9 +592,8 @@ Overrides for the Kubernetes object types fetched from the cluster. The default - `statefulsets` - `daemonsets` -You may use this config to override the default object types if you only want a subset of -the default ones. However, it's currently not supported to fetch object types other -than the ones specified in the default types. +You may use this config to override the default object types if you only want specific ones. +However, the only additional object type to fetch at the moment is `secrets`. Example: @@ -608,6 +607,7 @@ kubernetes: - pods - services - statefulsets + - secrets ``` ### Role Based Access Control diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 62577c4fb0..6a4e9cc296 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -67,6 +67,7 @@ import { ServiceLocatorMethod, } from '../types/types'; import { + ALL_OBJECTS, DEFAULT_OBJECTS, KubernetesFanOutHandler, } from './KubernetesFanOutHandler'; @@ -521,7 +522,7 @@ export class KubernetesBuilder { let objectTypesToFetch; if (objectTypesToFetchStrings) { - objectTypesToFetch = DEFAULT_OBJECTS.filter(obj => + objectTypesToFetch = ALL_OBJECTS.filter(obj => objectTypesToFetchStrings.includes(obj.objectType), ); } diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts index ac6a59a7c4..3ca6c98281 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts @@ -20,9 +20,13 @@ import { ObjectFetchParams, KubernetesServiceLocator, ServiceLocatorRequestContext, + ObjectToFetch, } from '../types/types'; import { KubernetesCredential } from '../auth/types'; -import { KubernetesFanOutHandler } from './KubernetesFanOutHandler'; +import { + KubernetesFanOutHandler, + DEFAULT_OBJECTS, +} from './KubernetesFanOutHandler'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; @@ -190,7 +194,10 @@ describe('KubernetesFanOutHandler', () => { ], }; - const getKubernetesFanOutHandler = (customResources: CustomResource[]) => { + const getKubernetesFanOutHandler = ( + customResources: CustomResource[], + objectTypesToFetch?: ObjectToFetch[], + ) => { return new KubernetesFanOutHandler({ logger: mockServices.logger.mock(), fetcher: { @@ -201,6 +208,7 @@ describe('KubernetesFanOutHandler', () => { getClustersByEntity, }, customResources: customResources, + objectTypesToFetch: objectTypesToFetch || DEFAULT_OBJECTS, authStrategy: { getCredential: jest .fn< @@ -451,6 +459,80 @@ describe('KubernetesFanOutHandler', () => { ); }); + it('fetch objects should be called with secrets', async () => { + getClustersByEntity.mockImplementation(() => + Promise.resolve({ + clusters: [cluster2], + }), + ); + + sut = getKubernetesFanOutHandler( + [], + [ + { + group: '', + apiVersion: 'v1', + plural: 'secrets', + objectType: 'secrets', + }, + ], + ); + + await sut.getKubernetesObjectsByEntity( + { + entity, + auth: {}, + }, + { credentials: mockCredentials }, + ); + + expect(fetchObjectsForService).toHaveBeenCalledTimes(1); + expect( + Array.from(fetchObjectsForService.mock.calls[0][0].objectTypesToFetch), + ).toEqual( + expect.arrayContaining([ + { + group: '', + apiVersion: 'v1', + plural: 'secrets', + objectType: 'secrets', + }, + ]), + ); + }); + + it('fetch objects should not be called with secrets', async () => { + getClustersByEntity.mockImplementation(() => + Promise.resolve({ + clusters: [cluster2], + }), + ); + + sut = getKubernetesFanOutHandler([]); + + await sut.getKubernetesObjectsByEntity( + { + entity, + auth: {}, + }, + { credentials: mockCredentials }, + ); + + expect(fetchObjectsForService).toHaveBeenCalledTimes(1); + expect( + Array.from(fetchObjectsForService.mock.calls[0][0].objectTypesToFetch), + ).toEqual( + expect.not.arrayContaining([ + { + group: '', + apiVersion: 'v1', + plural: 'secrets', + objectType: 'secrets', + }, + ]), + ); + }); + it('retrieve objects for two cluster using customResources per cluster', async () => { getClustersByEntity.mockImplementation(() => Promise.resolve({ diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index 31c0c91807..74be477578 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -139,6 +139,16 @@ export const DEFAULT_OBJECTS: ObjectToFetch[] = [ }, ]; +export const ALL_OBJECTS: ObjectToFetch[] = [ + { + group: '', + apiVersion: 'v1', + plural: 'secrets', + objectType: 'secrets', + }, + ...DEFAULT_OBJECTS, +]; + export interface KubernetesFanOutHandlerOptions extends KubernetesObjectsProviderOptions { authStrategy: AuthenticationStrategy; diff --git a/plugins/kubernetes-common/report.api.md b/plugins/kubernetes-common/report.api.md index 81e9ca28e7..8cdac7ed45 100644 --- a/plugins/kubernetes-common/report.api.md +++ b/plugins/kubernetes-common/report.api.md @@ -20,6 +20,7 @@ import { V1LimitRange } from '@kubernetes/client-node'; import { V1Pod } from '@kubernetes/client-node'; import { V1ReplicaSet } from '@kubernetes/client-node'; import { V1ResourceQuota } from '@kubernetes/client-node'; +import { V1Secret } from '@kubernetes/client-node'; import { V1Service } from '@kubernetes/client-node'; import { V1StatefulSet } from '@kubernetes/client-node'; import { V2HorizontalPodAutoscaler } from '@kubernetes/client-node'; @@ -264,7 +265,8 @@ export type FetchResponse = | CustomResourceFetchResponse | StatefulSetsFetchResponse | DaemonSetsFetchResponse - | PodStatusFetchResponse; + | PodStatusFetchResponse + | SecretsFetchResponse; // @public (undocumented) export interface GroupedResponses extends DeploymentResources { @@ -432,6 +434,14 @@ export interface ResourceRef { namespace: string; } +// @public (undocumented) +export interface SecretsFetchResponse { + // (undocumented) + resources: Array; + // (undocumented) + type: 'secrets'; +} + // @public (undocumented) export interface ServiceFetchResponse { // (undocumented) diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index a63c157948..82b0caf3a9 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -30,6 +30,7 @@ import { V1ResourceQuota, V1Service, V1StatefulSet, + V1Secret, } from '@kubernetes/client-node'; import { Entity } from '@backstage/catalog-model'; @@ -139,7 +140,8 @@ export type FetchResponse = | CustomResourceFetchResponse | StatefulSetsFetchResponse | DaemonSetsFetchResponse - | PodStatusFetchResponse; + | PodStatusFetchResponse + | SecretsFetchResponse; /** @public */ export interface PodFetchResponse { @@ -231,6 +233,12 @@ export interface PodStatusFetchResponse { resources: Array; } +/** @public */ +export interface SecretsFetchResponse { + type: 'secrets'; + resources: Array; +} + /** @public */ export type KubernetesFetchError = StatusError | RawFetchError; diff --git a/plugins/kubernetes-node/report.api.md b/plugins/kubernetes-node/report.api.md index f5b210b424..0daad3c21b 100644 --- a/plugins/kubernetes-node/report.api.md +++ b/plugins/kubernetes-node/report.api.md @@ -191,7 +191,8 @@ export type KubernetesObjectTypes = | 'ingresses' | 'customresources' | 'statefulsets' - | 'daemonsets'; + | 'daemonsets' + | 'secrets'; // @public export interface KubernetesServiceLocator { diff --git a/plugins/kubernetes-node/src/types/types.ts b/plugins/kubernetes-node/src/types/types.ts index dd23907e25..4b4fabfceb 100644 --- a/plugins/kubernetes-node/src/types/types.ts +++ b/plugins/kubernetes-node/src/types/types.ts @@ -186,7 +186,8 @@ export type KubernetesObjectTypes = | 'ingresses' | 'customresources' | 'statefulsets' - | 'daemonsets'; + | 'daemonsets' + | 'secrets'; // If updating this list, also make sure to update // `objectTypes` and `apiVersionOverrides` in config.d.ts on @backstage/plugin-kubernetes-backend!