mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
fix(configs): update unused badge logic [EE-6608] (#11500)
Co-authored-by: testa113 <testa113>
This commit is contained in:
parent
9b6779515e
commit
14a365045d
12 changed files with 590 additions and 113 deletions
|
@ -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]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue