mirror of https://github.com/portainer/portainer
142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
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;
|
|
}
|