1
0
Fork 0
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:
Ali 2023-03-08 11:22:08 +13:00 committed by GitHub
parent 5f0af62521
commit 89194405ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 569 additions and 210 deletions

View 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,
}
);
}

View 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;
}