fix(configs): update unused badge logic [EE-6608] (#11500)

Co-authored-by: testa113 <testa113>
pull/11764/head
Ali 2024-05-03 09:13:33 +12:00 committed by GitHub
parent 9b6779515e
commit 14a365045d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 590 additions and 113 deletions

View File

@ -13,9 +13,10 @@ import {
getApplicationRevisionList,
} from './application.service';
import type { AppKind, Application, ApplicationPatch } from './types';
import { deletePod, getNamespacePods } from './pod.service';
import { deletePod } from './pod.service';
import { getNamespaceHorizontalPodAutoscalers } from './autoscaling.service';
import { applicationIsKind, matchLabelsToLabelSelectorValue } from './utils';
import { getNamespacePods } from './usePods';
const queryKeys = {
applicationsForCluster: (environmentId: EnvironmentId) =>

View File

@ -15,7 +15,7 @@ import { isFulfilled } from '@/portainer/helpers/promise-utils';
import { parseKubernetesAxiosError } from '../axiosError';
import { getPod, getNamespacePods, patchPod } from './pod.service';
import { getPod, patchPod } from './pod.service';
import { filterRevisionsByOwnerUid, getNakedPods } from './utils';
import {
AppKind,
@ -24,6 +24,7 @@ import {
ApplicationPatch,
} from './types';
import { appRevisionAnnotation } from './constants';
import { getNamespacePods } from './usePods';
// This file contains services for Kubernetes apps/v1 resources (Deployments, DaemonSets, StatefulSets)

View File

@ -1,4 +1,4 @@
import { Pod, PodList } from 'kubernetes-types/core/v1';
import { Pod } from 'kubernetes-types/core/v1';
import { EnvironmentId } from '@/react/portainer/environments/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
@ -7,37 +7,6 @@ import { parseKubernetesAxiosError } from '../axiosError';
import { ApplicationPatch } from './types';
export async function getNamespacePods(
environmentId: EnvironmentId,
namespace: string,
labelSelector?: string
) {
try {
const { data } = await axios.get<PodList>(
buildUrl(environmentId, namespace),
{
params: {
labelSelector,
},
}
);
const items = (data.items || []).map(
(pod) =>
<Pod>{
...pod,
kind: 'Pod',
apiVersion: data.apiVersion,
}
);
return items;
} catch (e) {
throw parseKubernetesAxiosError(
e,
`Unable to retrieve pods in namespace '${namespace}'`
);
}
}
export async function getPod<T extends Pod | string = Pod>(
environmentId: EnvironmentId,
namespace: string,

View File

@ -0,0 +1,77 @@
import { CronJob, CronJobList } from 'kubernetes-types/batch/v1';
import { useQuery } from '@tanstack/react-query';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { withError } from '@/react-tools/react-query';
import axios from '@/portainer/services/axios';
import { parseKubernetesAxiosError } from '../axiosError';
const queryKeys = {
cronJobsForCluster: (environmentId: EnvironmentId) => [
'environments',
environmentId,
'kubernetes',
'cronjobs',
],
};
export function useCronJobs(
environmentId: EnvironmentId,
namespaces?: string[]
) {
return useQuery(
queryKeys.cronJobsForCluster(environmentId),
() => getCronJobsForCluster(environmentId, namespaces),
{
...withError('Unable to retrieve CronJobs'),
enabled: !!namespaces?.length,
}
);
}
export async function getCronJobsForCluster(
environmentId: EnvironmentId,
namespaceNames?: string[]
) {
if (!namespaceNames) {
return [];
}
const jobs = await Promise.all(
namespaceNames.map((namespace) =>
getNamespaceCronJobs(environmentId, namespace)
)
);
return jobs.flat();
}
export async function getNamespaceCronJobs(
environmentId: EnvironmentId,
namespace: string,
labelSelector?: string
) {
try {
const { data } = await axios.get<CronJobList>(
`/endpoints/${environmentId}/kubernetes/apis/batch/v1/namespaces/${namespace}/cronjobs`,
{
params: {
labelSelector,
},
}
);
const items = (data.items || []).map(
(cronJob) =>
<CronJob>{
...cronJob,
kind: 'CronJob',
apiVersion: data.apiVersion,
}
);
return items;
} catch (e) {
throw parseKubernetesAxiosError(
e,
`Unable to retrieve CronJobs in namespace '${namespace}'`
);
}
}

View File

@ -0,0 +1,74 @@
import { Job, JobList } from 'kubernetes-types/batch/v1';
import { useQuery } from '@tanstack/react-query';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { withError } from '@/react-tools/react-query';
import axios from '@/portainer/services/axios';
import { parseKubernetesAxiosError } from '../axiosError';
const queryKeys = {
jobsForCluster: (environmentId: EnvironmentId) => [
'environments',
environmentId,
'kubernetes',
'jobs',
],
};
export function useJobs(environmentId: EnvironmentId, namespaces?: string[]) {
return useQuery(
queryKeys.jobsForCluster(environmentId),
() => getJobsForCluster(environmentId, namespaces),
{
...withError('Unable to retrieve Jobs'),
enabled: !!namespaces?.length,
}
);
}
export async function getJobsForCluster(
environmentId: EnvironmentId,
namespaceNames?: string[]
) {
if (!namespaceNames) {
return [];
}
const jobs = await Promise.all(
namespaceNames.map((namespace) =>
getNamespaceJobs(environmentId, namespace)
)
);
return jobs.flat();
}
export async function getNamespaceJobs(
environmentId: EnvironmentId,
namespace: string,
labelSelector?: string
) {
try {
const { data } = await axios.get<JobList>(
`/endpoints/${environmentId}/kubernetes/apis/batch/v1/namespaces/${namespace}/jobs`,
{
params: {
labelSelector,
},
}
);
const items = (data.items || []).map(
(job) =>
<Job>{
...job,
kind: 'Job',
apiVersion: data.apiVersion,
}
);
return items;
} catch (e) {
throw parseKubernetesAxiosError(
e,
`Unable to retrieve Jobs in namespace '${namespace}'`
);
}
}

View File

@ -0,0 +1,74 @@
import { useQuery } from '@tanstack/react-query';
import { Pod, PodList } from 'kubernetes-types/core/v1';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { withError } from '@/react-tools/react-query';
import axios from '@/portainer/services/axios';
import { parseKubernetesAxiosError } from '../axiosError';
const queryKeys = {
podsForCluster: (environmentId: EnvironmentId) => [
'environments',
environmentId,
'kubernetes',
'pods',
],
};
export function usePods(environemtId: EnvironmentId, namespaces?: string[]) {
return useQuery(
queryKeys.podsForCluster(environemtId),
() => getPodsForCluster(environemtId, namespaces),
{
...withError('Unable to retrieve Pods'),
enabled: !!namespaces?.length,
}
);
}
export async function getPodsForCluster(
environmentId: EnvironmentId,
namespaceNames?: string[]
) {
if (!namespaceNames) {
return [];
}
const pods = await Promise.all(
namespaceNames.map((namespace) =>
getNamespacePods(environmentId, namespace)
)
);
return pods.flat();
}
export async function getNamespacePods(
environmentId: EnvironmentId,
namespace: string,
labelSelector?: string
) {
try {
const { data } = await axios.get<PodList>(
`/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/pods`,
{
params: {
labelSelector,
},
}
);
const items = (data.items || []).map(
(pod) =>
<Pod>{
...pod,
kind: 'Pod',
apiVersion: data.apiVersion,
}
);
return items;
} catch (e) {
throw parseKubernetesAxiosError(
e,
`Unable to retrieve Pods in namespace '${namespace}'`
);
}
}

View File

@ -1,23 +1,25 @@
import { useMemo } from 'react';
import { FileCode } from 'lucide-react';
import { ConfigMap } from 'kubernetes-types/core/v1';
import { ConfigMap, Pod } from 'kubernetes-types/core/v1';
import { CronJob, Job } from 'kubernetes-types/batch/v1';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
import { useApplicationsQuery } from '@/react/kubernetes/applications/application.queries';
import { Application } from '@/react/kubernetes/applications/types';
import { pluralize } from '@/portainer/helpers/strings';
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
import { Namespaces } from '@/react/kubernetes/namespaces/types';
import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton';
import { usePods } from '@/react/kubernetes/applications/usePods';
import { useJobs } from '@/react/kubernetes/applications/useJobs';
import { useCronJobs } from '@/react/kubernetes/applications/useCronJobs';
import { Datatable, TableSettingsMenu } from '@@/datatables';
import { AddButton } from '@@/buttons';
import { useTableState } from '@@/datatables/useTableState';
import { DeleteButton } from '@@/buttons/DeleteButton';
import { AddButton } from '@@/buttons/AddButton';
import {
useConfigMapsForCluster,
@ -55,10 +57,11 @@ export function ConfigMapsDatatable() {
autoRefreshRate: tableState.autoRefreshRate * 1000,
}
);
const { data: applications, ...applicationsQuery } = useApplicationsQuery(
environmentId,
namespaceNames
);
const podsQuery = usePods(environmentId, namespaceNames);
const jobsQuery = useJobs(environmentId, namespaceNames);
const cronJobsQuery = useCronJobs(environmentId, namespaceNames);
const isInUseLoading =
podsQuery.isLoading || jobsQuery.isLoading || cronJobsQuery.isLoading;
const filteredConfigMaps = useMemo(
() =>
@ -71,8 +74,10 @@ export function ConfigMapsDatatable() {
);
const configMapRowData = useConfigMapRowData(
filteredConfigMaps,
applications ?? [],
applicationsQuery.isLoading,
podsQuery.data ?? [],
jobsQuery.data ?? [],
cronJobsQuery.data ?? [],
isInUseLoading,
namespaces
);
@ -112,8 +117,10 @@ export function ConfigMapsDatatable() {
// and wraps with useMemo to prevent unnecessary calculations
function useConfigMapRowData(
configMaps: ConfigMap[],
applications: Application[],
applicationsLoading: boolean,
pods: Pod[],
jobs: Job[],
cronJobs: CronJob[],
isInUseLoading: boolean,
namespaces?: Namespaces
): ConfigMapRowData[] {
return useMemo(
@ -122,12 +129,13 @@ function useConfigMapRowData(
...configMap,
inUse:
// if the apps are loading, set inUse to true to hide the 'unused' badge
applicationsLoading || getIsConfigMapInUse(configMap, applications),
isInUseLoading ||
getIsConfigMapInUse(configMap, pods, jobs, cronJobs),
isSystem: namespaces
? namespaces?.[configMap.metadata?.namespace ?? '']?.IsSystem
: false,
})),
[configMaps, applicationsLoading, applications, namespaces]
[configMaps, isInUseLoading, pods, jobs, cronJobs, namespaces]
);
}

View File

@ -0,0 +1,99 @@
import { ConfigMap, Pod } from 'kubernetes-types/core/v1';
import { CronJob, Job } from 'kubernetes-types/batch/v1';
import { getIsConfigMapInUse } from './utils';
describe('getIsConfigMapInUse', () => {
it('should return false when no resources reference the configMap', () => {
const configMap: ConfigMap = {
metadata: { name: 'my-configmap', namespace: 'default' },
};
const pods: Pod[] = [];
const jobs: Job[] = [];
const cronJobs: CronJob[] = [];
expect(getIsConfigMapInUse(configMap, pods, jobs, cronJobs)).toBe(false);
});
it('should return true when a pod references the configMap', () => {
const configMap: ConfigMap = {
metadata: { name: 'my-configmap', namespace: 'default' },
};
const pods: Pod[] = [
{
metadata: { namespace: 'default' },
spec: {
containers: [
{
name: 'container1',
envFrom: [{ configMapRef: { name: 'my-configmap' } }],
},
],
},
},
];
const jobs: Job[] = [];
const cronJobs: CronJob[] = [];
expect(getIsConfigMapInUse(configMap, pods, jobs, cronJobs)).toBe(true);
});
it('should return true when a job references the configMap', () => {
const configMap: ConfigMap = {
metadata: { name: 'my-configmap', namespace: 'default' },
};
const pods: Pod[] = [];
const jobs: Job[] = [
{
metadata: { namespace: 'default' },
spec: {
template: {
spec: {
containers: [
{
name: 'container1',
envFrom: [{ configMapRef: { name: 'my-configmap' } }],
},
],
},
},
},
},
];
const cronJobs: CronJob[] = [];
expect(getIsConfigMapInUse(configMap, pods, jobs, cronJobs)).toBe(true);
});
it('should return true when a cronJob references the configMap', () => {
const configMap: ConfigMap = {
metadata: { name: 'my-configmap', namespace: 'default' },
};
const pods: Pod[] = [];
const jobs: Job[] = [];
const cronJobs: CronJob[] = [
{
metadata: { namespace: 'default' },
spec: {
schedule: '0 0 * * *',
jobTemplate: {
spec: {
template: {
spec: {
containers: [
{
name: 'container1',
envFrom: [{ configMapRef: { name: 'my-configmap' } }],
},
],
},
},
},
},
},
},
];
expect(getIsConfigMapInUse(configMap, pods, jobs, cronJobs)).toBe(true);
});
});

View File

@ -1,33 +1,67 @@
import { ConfigMap, Pod } from 'kubernetes-types/core/v1';
import { ConfigMap, Pod, PodSpec } from 'kubernetes-types/core/v1';
import { CronJob, Job } from 'kubernetes-types/batch/v1';
import { Application } from '@/react/kubernetes/applications/types';
import { applicationIsKind } from '@/react/kubernetes/applications/utils';
// getIsConfigMapInUse returns true if the configmap is referenced by any
// application in the cluster
/**
* getIsConfigMapInUse returns true if the configmap is referenced by any pod, job, or cronjob in the same namespace
*/
export function getIsConfigMapInUse(
configMap: ConfigMap,
applications: Application[]
pods: Pod[],
jobs: Job[],
cronJobs: CronJob[]
) {
return applications.some((app) => {
const appSpec = applicationIsKind<Pod>('Pod', app)
? app?.spec
: app?.spec?.template?.spec;
// get all podspecs from pods, jobs and cronjobs that are in the same namespace
const podsInNamespace = pods
.filter((pod) => pod.metadata?.namespace === configMap.metadata?.namespace)
.map((pod) => pod.spec);
const jobsInNamespace = jobs
.filter((job) => job.metadata?.namespace === configMap.metadata?.namespace)
.map((job) => job.spec?.template.spec);
const cronJobsInNamespace = cronJobs
.filter(
(cronJob) => cronJob.metadata?.namespace === configMap.metadata?.namespace
)
.map((cronJob) => cronJob.spec?.jobTemplate.spec?.template.spec);
const allPodSpecs = [
...podsInNamespace,
...jobsInNamespace,
...cronJobsInNamespace,
];
const hasEnvVarReference = appSpec?.containers.some((container) => {
const valueFromEnv = container.env?.some(
(envVar) =>
envVar.valueFrom?.configMapKeyRef?.name === configMap.metadata?.name
);
const envFromEnv = container.envFrom?.some(
(envVar) => envVar.configMapRef?.name === configMap.metadata?.name
);
return valueFromEnv || envFromEnv;
});
const hasVolumeReference = appSpec?.volumes?.some(
(volume) => volume.configMap?.name === configMap.metadata?.name
);
return hasEnvVarReference || hasVolumeReference;
// check if the configmap is referenced by any pod, job or cronjob in the namespace
const isReferenced = allPodSpecs.some((podSpec) => {
if (!podSpec || !configMap.metadata?.name) {
return false;
}
return doesPodSpecReferenceConfigMap(podSpec, configMap.metadata?.name);
});
return isReferenced;
}
/**
* Checks if a PodSpec references a specific ConfigMap.
* @param podSpec - The PodSpec object to check.
* @param configmapName - The name of the ConfigMap to check for references.
* @returns A boolean indicating whether the PodSpec references the ConfigMap.
*/
function doesPodSpecReferenceConfigMap(
podSpec: PodSpec,
configmapName: string
) {
const hasEnvVarReference = podSpec?.containers.some((container) => {
const valueFromEnv = container.env?.some(
(envVar) => envVar.valueFrom?.configMapKeyRef?.name === configmapName
);
const envFromEnv = container.envFrom?.some(
(envVar) => envVar.configMapRef?.name === configmapName
);
return valueFromEnv || envFromEnv;
});
const hasVolumeReference = podSpec?.volumes?.some(
(volume) => volume.configMap?.name === configmapName
);
return hasEnvVarReference || hasVolumeReference;
}

View File

@ -1,18 +1,20 @@
import { useMemo } from 'react';
import { Lock } from 'lucide-react';
import { Secret } from 'kubernetes-types/core/v1';
import { Pod, Secret } from 'kubernetes-types/core/v1';
import { CronJob, Job } from 'kubernetes-types/batch/v1';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
import { useApplicationsQuery } from '@/react/kubernetes/applications/application.queries';
import { Application } from '@/react/kubernetes/applications/types';
import { pluralize } from '@/portainer/helpers/strings';
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
import { Namespaces } from '@/react/kubernetes/namespaces/types';
import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton';
import { usePods } from '@/react/kubernetes/applications/usePods';
import { useJobs } from '@/react/kubernetes/applications/useJobs';
import { useCronJobs } from '@/react/kubernetes/applications/useCronJobs';
import { Datatable, TableSettingsMenu } from '@@/datatables';
import { AddButton } from '@@/buttons';
@ -55,10 +57,11 @@ export function SecretsDatatable() {
autoRefreshRate: tableState.autoRefreshRate * 1000,
}
);
const { data: applications, ...applicationsQuery } = useApplicationsQuery(
environmentId,
namespaceNames
);
const podsQuery = usePods(environmentId, namespaceNames);
const jobsQuery = useJobs(environmentId, namespaceNames);
const cronJobsQuery = useCronJobs(environmentId, namespaceNames);
const isInUseLoading =
podsQuery.isLoading || jobsQuery.isLoading || cronJobsQuery.isLoading;
const filteredSecrets = useMemo(
() =>
@ -71,8 +74,10 @@ export function SecretsDatatable() {
);
const secretRowData = useSecretRowData(
filteredSecrets,
applications ?? [],
applicationsQuery.isLoading,
podsQuery.data ?? [],
jobsQuery.data ?? [],
cronJobsQuery.data ?? [],
isInUseLoading,
namespaces
);
@ -112,8 +117,10 @@ export function SecretsDatatable() {
// and wraps with useMemo to prevent unnecessary calculations
function useSecretRowData(
secrets: Secret[],
applications: Application[],
applicationsLoading: boolean,
pods: Pod[],
jobs: Job[],
cronJobs: CronJob[],
isInUseLoading: boolean,
namespaces?: Namespaces
): SecretRowData[] {
return useMemo(
@ -122,12 +129,12 @@ function useSecretRowData(
...secret,
inUse:
// if the apps are loading, set inUse to true to hide the 'unused' badge
applicationsLoading || getIsSecretInUse(secret, applications),
isInUseLoading || getIsSecretInUse(secret, pods, jobs, cronJobs),
isSystem: namespaces
? namespaces?.[secret.metadata?.namespace ?? '']?.IsSystem
: false,
})),
[secrets, applicationsLoading, applications, namespaces]
[secrets, isInUseLoading, pods, jobs, cronJobs, namespaces]
);
}

View File

@ -0,0 +1,99 @@
import { CronJob, Job } from 'kubernetes-types/batch/v1';
import { Secret, Pod } from 'kubernetes-types/core/v1';
import { getIsSecretInUse } from './utils';
describe('getIsSecretInUse', () => {
it('should return false when no resources reference the secret', () => {
const secret: Secret = {
metadata: { name: 'my-secret', namespace: 'default' },
};
const pods: Pod[] = [];
const jobs: Job[] = [];
const cronJobs: CronJob[] = [];
expect(getIsSecretInUse(secret, pods, jobs, cronJobs)).toBe(false);
});
it('should return true when a pod references the secret', () => {
const secret: Secret = {
metadata: { name: 'my-secret', namespace: 'default' },
};
const pods: Pod[] = [
{
metadata: { namespace: 'default' },
spec: {
containers: [
{
name: 'container1',
envFrom: [{ secretRef: { name: 'my-secret' } }],
},
],
},
},
];
const jobs: Job[] = [];
const cronJobs: CronJob[] = [];
expect(getIsSecretInUse(secret, pods, jobs, cronJobs)).toBe(true);
});
it('should return true when a job references the secret', () => {
const secret: Secret = {
metadata: { name: 'my-secret', namespace: 'default' },
};
const pods: Pod[] = [];
const jobs: Job[] = [
{
metadata: { namespace: 'default' },
spec: {
template: {
spec: {
containers: [
{
name: 'container1',
envFrom: [{ secretRef: { name: 'my-secret' } }],
},
],
},
},
},
},
];
const cronJobs: CronJob[] = [];
expect(getIsSecretInUse(secret, pods, jobs, cronJobs)).toBe(true);
});
it('should return true when a cronJob references the secret', () => {
const secret: Secret = {
metadata: { name: 'my-secret', namespace: 'default' },
};
const pods: Pod[] = [];
const jobs: Job[] = [];
const cronJobs: CronJob[] = [
{
metadata: { namespace: 'default' },
spec: {
schedule: '0 0 * * *',
jobTemplate: {
spec: {
template: {
spec: {
containers: [
{
name: 'container1',
envFrom: [{ secretRef: { name: 'my-secret' } }],
},
],
},
},
},
},
},
},
];
expect(getIsSecretInUse(secret, pods, jobs, cronJobs)).toBe(true);
});
});

View File

@ -1,30 +1,64 @@
import { Secret, Pod } from 'kubernetes-types/core/v1';
import { Secret, Pod, PodSpec } from 'kubernetes-types/core/v1';
import { CronJob, Job } from 'kubernetes-types/batch/v1';
import { Application } from '@/react/kubernetes/applications/types';
import { applicationIsKind } from '@/react/kubernetes/applications/utils';
/**
* getIsSecretInUse returns true if the secret is referenced by any pod, job, or cronjob in the same namespace
*/
export function getIsSecretInUse(
secret: Secret,
pods: Pod[],
jobs: Job[],
cronJobs: CronJob[]
) {
// get all podspecs from pods, jobs and cronjobs that are in the same namespace
const podsInNamespace = pods
.filter((pod) => pod.metadata?.namespace === secret.metadata?.namespace)
.map((pod) => pod.spec);
const jobsInNamespace = jobs
.filter((job) => job.metadata?.namespace === secret.metadata?.namespace)
.map((job) => job.spec?.template.spec);
const cronJobsInNamespace = cronJobs
.filter(
(cronJob) => cronJob.metadata?.namespace === secret.metadata?.namespace
)
.map((cronJob) => cronJob.spec?.jobTemplate.spec?.template.spec);
const allPodSpecs = [
...podsInNamespace,
...jobsInNamespace,
...cronJobsInNamespace,
];
// getIsSecretInUse returns true if the secret is referenced by any
// application in the cluster
export function getIsSecretInUse(secret: Secret, applications: Application[]) {
return applications.some((app) => {
const appSpec = applicationIsKind<Pod>('Pod', app)
? app?.spec
: app?.spec?.template?.spec;
const hasEnvVarReference = appSpec?.containers.some((container) => {
const valueFromEnv = container.env?.some(
(envVar) =>
envVar.valueFrom?.secretKeyRef?.name === secret.metadata?.name
);
const envFromEnv = container.envFrom?.some(
(envVar) => envVar.secretRef?.name === secret.metadata?.name
);
return valueFromEnv || envFromEnv;
});
const hasVolumeReference = appSpec?.volumes?.some(
(volume) => volume.secret?.secretName === secret.metadata?.name
);
return hasEnvVarReference || hasVolumeReference;
// check if the secret is referenced by any pod, job or cronjob in the namespace
const isReferenced = allPodSpecs.some((podSpec) => {
if (!podSpec || !secret.metadata?.name) {
return false;
}
return doesPodSpecReferenceSecret(podSpec, secret.metadata?.name);
});
return isReferenced;
}
/**
* Checks if a PodSpec references a specific Secret.
* @param podSpec - The PodSpec object to check.
* @param secretName - The name of the Secret to check for references.
* @returns A boolean indicating whether the PodSpec references the Secret.
*/
function doesPodSpecReferenceSecret(podSpec: PodSpec, secretName: string) {
const hasEnvVarReference = podSpec?.containers.some((container) => {
const valueFromEnv = container.env?.some(
(envVar) => envVar.valueFrom?.secretKeyRef?.name === secretName
);
const envFromEnv = container.envFrom?.some(
(envVar) => envVar.secretRef?.name === secretName
);
return valueFromEnv || envFromEnv;
});
const hasVolumeReference = podSpec?.volumes?.some(
(volume) => volume.secret?.secretName === secretName
);
return hasEnvVarReference || hasVolumeReference;
}