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 <florian.fl.bauer@deutschebahn.com>

* add tests

Signed-off-by: Florian Fl Bauer <florian.fl.bauer@deutschebahn.com>

* remove log

Signed-off-by: Florian Fl Bauer <florian.fl.bauer@deutschebahn.com>

---------

Signed-off-by: Florian Fl Bauer <florian.fl.bauer@deutschebahn.com>
This commit is contained in:
Florian Bauer
2025-01-17 14:15:39 +01:00
committed by GitHub
parent a5cd0a268f
commit ac0e1acf78
9 changed files with 130 additions and 10 deletions
+7
View File
@@ -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
+3 -3
View File
@@ -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
@@ -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),
);
}
@@ -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({
@@ -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;
+11 -1
View File
@@ -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<V1Secret>;
// (undocumented)
type: 'secrets';
}
// @public (undocumented)
export interface ServiceFetchResponse {
// (undocumented)
+9 -1
View File
@@ -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<PodStatus>;
}
/** @public */
export interface SecretsFetchResponse {
type: 'secrets';
resources: Array<V1Secret>;
}
/** @public */
export type KubernetesFetchError = StatusError | RawFetchError;
+2 -1
View File
@@ -191,7 +191,8 @@ export type KubernetesObjectTypes =
| 'ingresses'
| 'customresources'
| 'statefulsets'
| 'daemonsets';
| 'daemonsets'
| 'secrets';
// @public
export interface KubernetesServiceLocator {
+2 -1
View File
@@ -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!