mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
refactor(k8s): namespace core logic (#12142)
Co-authored-by: testA113 <aliharriss1995@gmail.com> Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io> Co-authored-by: James Carppe <85850129+jamescarppe@users.noreply.github.com> Co-authored-by: Ali <83188384+testA113@users.noreply.github.com>
This commit is contained in:
parent
da010f3d08
commit
ea228c3d6d
276 changed files with 9241 additions and 3361 deletions
20
app/react/kubernetes/namespaces/queries/queryKeys.ts
Normal file
20
app/react/kubernetes/namespaces/queries/queryKeys.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { compact } from 'lodash';
|
||||
|
||||
export const queryKeys = {
|
||||
list: (environmentId: number, options?: { withResourceQuota?: boolean }) =>
|
||||
compact([
|
||||
'environments',
|
||||
environmentId,
|
||||
'kubernetes',
|
||||
'namespaces',
|
||||
options?.withResourceQuota,
|
||||
]),
|
||||
namespace: (environmentId: number, namespace: string) =>
|
||||
[
|
||||
'environments',
|
||||
environmentId,
|
||||
'kubernetes',
|
||||
'namespaces',
|
||||
namespace,
|
||||
] as const,
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { withGlobalError, withInvalidate } from '@/react-tools/react-query';
|
||||
|
||||
import { queryKeys } from './queryKeys';
|
||||
|
||||
type DeleteNamespaceError = {
|
||||
namespaceName: string;
|
||||
error: string;
|
||||
};
|
||||
|
||||
// when successful, the response will contain a list of deleted namespaces and a list of errors
|
||||
type DeleteNamespacesResponse = {
|
||||
deleted: string[];
|
||||
errors: DeleteNamespaceError[];
|
||||
} | null;
|
||||
|
||||
// useDeleteNamespaces is a react query mutation that removes a list of namespaces,
|
||||
export function useDeleteNamespaces(environmentId: number) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
({ namespaceNames }: { namespaceNames: string[] }) =>
|
||||
deleteNamespaces(environmentId, namespaceNames),
|
||||
{
|
||||
...withInvalidate(queryClient, [queryKeys.list(environmentId)]),
|
||||
...withGlobalError('Unable to delete namespaces'),
|
||||
// onSuccess handled by the caller
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function deleteNamespaces(
|
||||
environmentId: number,
|
||||
namespaceNames: string[]
|
||||
) {
|
||||
try {
|
||||
return await axios.delete<DeleteNamespacesResponse>(
|
||||
`kubernetes/${environmentId}/namespaces`,
|
||||
{
|
||||
data: namespaceNames,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e, 'Unable to delete namespace');
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { PortainerNamespace } from '../types';
|
||||
|
||||
import { useNamespaceQuery } from './useNamespaceQuery';
|
||||
|
||||
export function useIsSystemNamespace(namespace: string) {
|
||||
|
@ -10,3 +12,12 @@ export function useIsSystemNamespace(namespace: string) {
|
|||
|
||||
return !!query.data;
|
||||
}
|
||||
|
||||
export function isSystemNamespace(
|
||||
namespaceName: string,
|
||||
namespaces?: PortainerNamespace[]
|
||||
) {
|
||||
return namespaces?.some(
|
||||
(namespace) => namespace.Name === namespaceName && namespace.IsSystem
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,19 +4,21 @@ import axios, { parseAxiosError } from '@/portainer/services/axios';
|
|||
import { notifyError } from '@/portainer/services/notifications';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { DefaultOrSystemNamespace } from '../types';
|
||||
import { PortainerNamespace } from '../types';
|
||||
|
||||
export function useNamespaceQuery<T = DefaultOrSystemNamespace>(
|
||||
import { queryKeys } from './queryKeys';
|
||||
|
||||
export function useNamespaceQuery<T = PortainerNamespace>(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
{
|
||||
select,
|
||||
}: {
|
||||
select?(namespace: DefaultOrSystemNamespace): T;
|
||||
select?(namespace: PortainerNamespace): T;
|
||||
} = {}
|
||||
) {
|
||||
return useQuery(
|
||||
['environments', environmentId, 'kubernetes', 'namespaces', namespace],
|
||||
queryKeys.namespace(environmentId, namespace),
|
||||
() => getNamespace(environmentId, namespace),
|
||||
{
|
||||
onError: (err) => {
|
||||
|
@ -33,11 +35,11 @@ export async function getNamespace(
|
|||
namespace: string
|
||||
) {
|
||||
try {
|
||||
const { data: ns } = await axios.get<DefaultOrSystemNamespace>(
|
||||
const { data: ns } = await axios.get<PortainerNamespace>(
|
||||
`kubernetes/${environmentId}/namespaces/${namespace}`
|
||||
);
|
||||
return ns;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to retrieve namespace');
|
||||
throw parseAxiosError(e, 'Unable to retrieve namespace');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,24 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { withError } from '@/react-tools/react-query';
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import { Namespaces } from '../types';
|
||||
import { getSelfSubjectAccessReview } from '../getSelfSubjectAccessReview';
|
||||
import { PortainerNamespace } from '../types';
|
||||
|
||||
import { queryKeys } from './queryKeys';
|
||||
|
||||
export function useNamespacesQuery(
|
||||
environmentId: EnvironmentId,
|
||||
options?: { autoRefreshRate?: number }
|
||||
options?: { autoRefreshRate?: number; withResourceQuota?: boolean }
|
||||
) {
|
||||
return useQuery(
|
||||
['environments', environmentId, 'kubernetes', 'namespaces'],
|
||||
async () => {
|
||||
const namespaces = await getNamespaces(environmentId);
|
||||
const namespaceNames = Object.keys(namespaces);
|
||||
// use selfsubjectaccess reviews to avoid forbidden requests
|
||||
const allNamespaceAccessReviews = await Promise.all(
|
||||
namespaceNames.map((namespaceName) =>
|
||||
getSelfSubjectAccessReview(environmentId, namespaceName)
|
||||
)
|
||||
);
|
||||
const allowedNamespacesNames = allNamespaceAccessReviews
|
||||
.filter((accessReview) => accessReview.status.allowed)
|
||||
.map((accessReview) => accessReview.spec.resourceAttributes.namespace);
|
||||
const allowedNamespaces = namespaceNames.reduce((acc, namespaceName) => {
|
||||
if (allowedNamespacesNames.includes(namespaceName)) {
|
||||
acc[namespaceName] = namespaces[namespaceName];
|
||||
}
|
||||
return acc;
|
||||
}, {} as Namespaces);
|
||||
return allowedNamespaces;
|
||||
},
|
||||
queryKeys.list(environmentId, {
|
||||
withResourceQuota: !!options?.withResourceQuota,
|
||||
}),
|
||||
async () => getNamespaces(environmentId, options?.withResourceQuota),
|
||||
{
|
||||
...withError('Unable to get namespaces.'),
|
||||
...withGlobalError('Unable to get namespaces.'),
|
||||
refetchInterval() {
|
||||
return options?.autoRefreshRate ?? false;
|
||||
},
|
||||
|
@ -43,13 +27,18 @@ export function useNamespacesQuery(
|
|||
}
|
||||
|
||||
// getNamespaces is used to retrieve namespaces using the Portainer backend with caching
|
||||
async function getNamespaces(environmentId: EnvironmentId) {
|
||||
export async function getNamespaces(
|
||||
environmentId: EnvironmentId,
|
||||
withResourceQuota?: boolean
|
||||
) {
|
||||
const params = withResourceQuota ? { withResourceQuota } : {};
|
||||
try {
|
||||
const { data: namespaces } = await axios.get<Namespaces>(
|
||||
`kubernetes/${environmentId}/namespaces`
|
||||
const { data: namespaces } = await axios.get<PortainerNamespace[]>(
|
||||
`kubernetes/${environmentId}/namespaces`,
|
||||
{ params }
|
||||
);
|
||||
return namespaces;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e as Error, 'Unable to retrieve namespaces');
|
||||
throw parseAxiosError(e, 'Unable to retrieve namespaces');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue