mirror of
https://github.com/portainer/portainer.git
synced 2025-07-25 08:19:40 +02:00
fix(kube): improve dashboard load speed [EE-4941] (#8572)
* apply changes from EE * clear query cache when logging out * Text transitions in smoother
This commit is contained in:
parent
5f0af62521
commit
89194405ee
36 changed files with 569 additions and 210 deletions
21
app/react/kubernetes/applications/queries.ts
Normal file
21
app/react/kubernetes/applications/queries.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { useQuery } from 'react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { withError } from '@/react-tools/react-query';
|
||||
|
||||
import { getApplicationsListForCluster } from './service';
|
||||
|
||||
// useQuery to get a list of all applications from an array of namespaces
|
||||
export function useApplicationsForCluster(
|
||||
environemtId: EnvironmentId,
|
||||
namespaces?: string[]
|
||||
) {
|
||||
return useQuery(
|
||||
['environments', environemtId, 'kubernetes', 'applications'],
|
||||
() => namespaces && getApplicationsListForCluster(environemtId, namespaces),
|
||||
{
|
||||
...withError('Unable to retrieve applications'),
|
||||
enabled: !!namespaces,
|
||||
}
|
||||
);
|
||||
}
|
141
app/react/kubernetes/applications/service.ts
Normal file
141
app/react/kubernetes/applications/service.ts
Normal file
|
@ -0,0 +1,141 @@
|
|||
import { Pod, PodList } from 'kubernetes-types/core/v1';
|
||||
import {
|
||||
Deployment,
|
||||
DeploymentList,
|
||||
DaemonSet,
|
||||
DaemonSetList,
|
||||
StatefulSet,
|
||||
StatefulSetList,
|
||||
} from 'kubernetes-types/apps/v1';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
export async function getApplicationsListForCluster(
|
||||
environmentId: EnvironmentId,
|
||||
namespaces: string[]
|
||||
) {
|
||||
try {
|
||||
const applications = await Promise.all(
|
||||
namespaces.map((namespace) =>
|
||||
getApplicationsListForNamespace(environmentId, namespace)
|
||||
)
|
||||
);
|
||||
return applications.flat();
|
||||
} catch (e) {
|
||||
throw parseAxiosError(
|
||||
e as Error,
|
||||
'Unable to retrieve applications for cluster'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// get a list of all Deployments, DaemonSets and StatefulSets in one namespace
|
||||
export async function getApplicationsListForNamespace(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string
|
||||
) {
|
||||
try {
|
||||
const [deployments, daemonSets, statefulSets, pods] = await Promise.all([
|
||||
getDeployments(environmentId, namespace),
|
||||
getDaemonSets(environmentId, namespace),
|
||||
getStatefulSets(environmentId, namespace),
|
||||
getPods(environmentId, namespace),
|
||||
]);
|
||||
// find all pods which are 'naked' (not owned by a deployment, daemonset or statefulset)
|
||||
const nakedPods = getNakedPods(pods, deployments, daemonSets, statefulSets);
|
||||
return [...deployments, ...daemonSets, ...statefulSets, ...nakedPods];
|
||||
} catch (e) {
|
||||
throw parseAxiosError(
|
||||
e as Error,
|
||||
`Unable to retrieve applications in namespace ${namespace}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function getDeployments(environmentId: EnvironmentId, namespace: string) {
|
||||
try {
|
||||
const { data } = await axios.get<DeploymentList>(
|
||||
buildUrl(environmentId, namespace, 'deployments')
|
||||
);
|
||||
return data.items;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to retrieve deployments');
|
||||
}
|
||||
}
|
||||
|
||||
async function getDaemonSets(environmentId: EnvironmentId, namespace: string) {
|
||||
try {
|
||||
const { data } = await axios.get<DaemonSetList>(
|
||||
buildUrl(environmentId, namespace, 'daemonsets')
|
||||
);
|
||||
return data.items;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to retrieve daemonsets');
|
||||
}
|
||||
}
|
||||
|
||||
async function getStatefulSets(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<StatefulSetList>(
|
||||
buildUrl(environmentId, namespace, 'statefulsets')
|
||||
);
|
||||
return data.items;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to retrieve statefulsets');
|
||||
}
|
||||
}
|
||||
|
||||
async function getPods(environmentId: EnvironmentId, namespace: string) {
|
||||
try {
|
||||
const { data } = await axios.get<PodList>(
|
||||
`/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/pods`
|
||||
);
|
||||
return data.items;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to retrieve pods');
|
||||
}
|
||||
}
|
||||
|
||||
function buildUrl(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
appResource: 'deployments' | 'daemonsets' | 'statefulsets'
|
||||
) {
|
||||
return `/endpoints/${environmentId}/kubernetes/apis/apps/v1/namespaces/${namespace}/${appResource}`;
|
||||
}
|
||||
|
||||
function getNakedPods(
|
||||
pods: Pod[],
|
||||
deployments: Deployment[],
|
||||
daemonSets: DaemonSet[],
|
||||
statefulSets: StatefulSet[]
|
||||
) {
|
||||
// naked pods are pods which are not owned by a deployment, daemonset, statefulset or replicaset
|
||||
// https://kubernetes.io/docs/concepts/configuration/overview/#naked-pods-vs-replicasets-deployments-and-jobs
|
||||
const appLabels = [
|
||||
...deployments.map((deployment) => deployment.spec?.selector.matchLabels),
|
||||
...daemonSets.map((daemonSet) => daemonSet.spec?.selector.matchLabels),
|
||||
...statefulSets.map(
|
||||
(statefulSet) => statefulSet.spec?.selector.matchLabels
|
||||
),
|
||||
];
|
||||
|
||||
const nakedPods = pods.filter((pod) => {
|
||||
const podLabels = pod.metadata?.labels;
|
||||
// if the pod has no labels, it is naked
|
||||
if (!podLabels) return true;
|
||||
// if the pod has labels, but no app labels, it is naked
|
||||
return !appLabels.some((appLabel) => {
|
||||
if (!appLabel) return false;
|
||||
return Object.entries(appLabel).every(
|
||||
([key, value]) => podLabels[key] === value
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return nakedPods;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue