mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 22:05:23 +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
52
app/react/kubernetes/configs/queries/query-keys.ts
Normal file
52
app/react/kubernetes/configs/queries/query-keys.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { ConfigMapQueryParams, SecretQueryParams } from './types';
|
||||
|
||||
export const configMapQueryKeys = {
|
||||
configMap: (
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
configMap: string
|
||||
) => [
|
||||
'environments',
|
||||
environmentId,
|
||||
'kubernetes',
|
||||
'configmaps',
|
||||
'namespaces',
|
||||
namespace,
|
||||
configMap,
|
||||
],
|
||||
configMaps: (environmentId: EnvironmentId, namespace?: string) => [
|
||||
'environments',
|
||||
environmentId,
|
||||
'kubernetes',
|
||||
'configmaps',
|
||||
'namespaces',
|
||||
namespace,
|
||||
],
|
||||
configMapsForCluster: (
|
||||
environmentId: EnvironmentId,
|
||||
params?: ConfigMapQueryParams
|
||||
) =>
|
||||
params
|
||||
? ['environments', environmentId, 'kubernetes', 'configmaps', params]
|
||||
: ['environments', environmentId, 'kubernetes', 'configmaps'],
|
||||
};
|
||||
|
||||
export const secretQueryKeys = {
|
||||
secrets: (environmentId: EnvironmentId, namespace?: string) => [
|
||||
'environments',
|
||||
environmentId,
|
||||
'kubernetes',
|
||||
'secrets',
|
||||
'namespaces',
|
||||
namespace,
|
||||
],
|
||||
secretsForCluster: (
|
||||
environmentId: EnvironmentId,
|
||||
params?: SecretQueryParams
|
||||
) =>
|
||||
params
|
||||
? ['environments', environmentId, 'kubernetes', 'secrets', params]
|
||||
: ['environments', environmentId, 'kubernetes', 'secrets'],
|
||||
};
|
2
app/react/kubernetes/configs/queries/types.ts
Normal file
2
app/react/kubernetes/configs/queries/types.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export type ConfigMapQueryParams = { isUsed?: boolean };
|
||||
export type SecretQueryParams = { isUsed?: boolean };
|
48
app/react/kubernetes/configs/queries/useConfigMap.ts
Normal file
48
app/react/kubernetes/configs/queries/useConfigMap.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { Configuration } from '../types';
|
||||
|
||||
import { configMapQueryKeys } from './query-keys';
|
||||
import { ConfigMapQueryParams } from './types';
|
||||
|
||||
export function useConfigMap(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
configMap: string,
|
||||
options?: { autoRefreshRate?: number } & ConfigMapQueryParams
|
||||
) {
|
||||
return useQuery(
|
||||
configMapQueryKeys.configMap(environmentId, namespace, configMap),
|
||||
() => getConfigMap(environmentId, namespace, configMap, { withData: true }),
|
||||
{
|
||||
...withGlobalError('Unable to retrieve ConfigMaps for cluster'),
|
||||
refetchInterval() {
|
||||
return options?.autoRefreshRate ?? false;
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// get a configmap
|
||||
async function getConfigMap(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
configMap: string,
|
||||
params?: { withData?: boolean }
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Configuration[]>(
|
||||
`/kubernetes/${environmentId}/namespaces/${namespace}/configmaps/${configMap}`,
|
||||
{ params }
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
// use parseAxiosError instead of parseKubernetesAxiosError
|
||||
// because this is an internal portainer api endpoint, not through the kube proxy
|
||||
throw parseAxiosError(e, 'Unable to retrieve ConfigMaps');
|
||||
}
|
||||
}
|
41
app/react/kubernetes/configs/queries/useConfigMaps.ts
Normal file
41
app/react/kubernetes/configs/queries/useConfigMaps.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import { ConfigMap, ConfigMapList } from 'kubernetes-types/core/v1';
|
||||
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
|
||||
import { parseKubernetesAxiosError } from '../../axiosError';
|
||||
|
||||
import { configMapQueryKeys } from './query-keys';
|
||||
|
||||
// returns a usequery hook for the list of configmaps within a namespace from the kubernetes API
|
||||
export function useConfigMaps(environmentId: EnvironmentId, namespace: string) {
|
||||
return useQuery(
|
||||
configMapQueryKeys.configMaps(environmentId, namespace),
|
||||
() => (namespace ? getConfigMaps(environmentId, namespace) : []),
|
||||
{
|
||||
...withGlobalError(
|
||||
`Unable to get ConfigMaps in namespace '${namespace}'`
|
||||
),
|
||||
enabled: !!namespace,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// get all configmaps for a namespace
|
||||
async function getConfigMaps(environmentId: EnvironmentId, namespace?: string) {
|
||||
try {
|
||||
const { data } = await axios.get<ConfigMapList>(
|
||||
`/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/configmaps`
|
||||
);
|
||||
// when fetching a list, the kind isn't appended to the items, so we need to add it
|
||||
const configmaps: ConfigMap[] = data.items.map((configmap) => ({
|
||||
...configmap,
|
||||
kind: 'ConfigMap',
|
||||
}));
|
||||
return configmaps;
|
||||
} catch (e) {
|
||||
throw parseKubernetesAxiosError(e, 'Unable to retrieve ConfigMaps');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { Configuration } from '../types';
|
||||
|
||||
import { configMapQueryKeys } from './query-keys';
|
||||
import { ConfigMapQueryParams } from './types';
|
||||
|
||||
export function useConfigMapsForCluster<TData = Configuration[]>(
|
||||
environmentId: EnvironmentId,
|
||||
options?: {
|
||||
autoRefreshRate?: number;
|
||||
select?: (data: Configuration[]) => TData;
|
||||
} & ConfigMapQueryParams
|
||||
) {
|
||||
const { autoRefreshRate, select, ...params } = options ?? {};
|
||||
return useQuery(
|
||||
configMapQueryKeys.configMapsForCluster(environmentId, params),
|
||||
() =>
|
||||
getConfigMapsForCluster(environmentId, {
|
||||
...params,
|
||||
isUsed: params?.isUsed,
|
||||
}),
|
||||
{
|
||||
...withGlobalError('Unable to retrieve ConfigMaps for cluster'),
|
||||
refetchInterval() {
|
||||
return options?.autoRefreshRate ?? false;
|
||||
},
|
||||
select,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// get all configmaps for a cluster
|
||||
async function getConfigMapsForCluster(
|
||||
environmentId: EnvironmentId,
|
||||
params?: { withData?: boolean; isUsed?: boolean }
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Configuration[]>(
|
||||
`/kubernetes/${environmentId}/configmaps`,
|
||||
{ params }
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
// use parseAxiosError instead of parseKubernetesAxiosError
|
||||
// because this is an internal portainer api endpoint, not through the kube proxy
|
||||
throw parseAxiosError(e, 'Unable to retrieve ConfigMaps');
|
||||
}
|
||||
}
|
77
app/react/kubernetes/configs/queries/useDeleteConfigMaps.ts
Normal file
77
app/react/kubernetes/configs/queries/useDeleteConfigMaps.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { queryClient, withGlobalError } from '@/react-tools/react-query';
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import {
|
||||
error as notifyError,
|
||||
notifySuccess,
|
||||
} from '@/portainer/services/notifications';
|
||||
import { isFulfilled, isRejected } from '@/portainer/helpers/promise-utils';
|
||||
import { pluralize } from '@/portainer/helpers/strings';
|
||||
|
||||
import { parseKubernetesAxiosError } from '../../axiosError';
|
||||
|
||||
import { configMapQueryKeys } from './query-keys';
|
||||
|
||||
export function useDeleteConfigMaps(environmentId: EnvironmentId) {
|
||||
return useMutation(
|
||||
async (configMaps: { namespace: string; name: string }[]) => {
|
||||
const promises = await Promise.allSettled(
|
||||
configMaps.map(({ namespace, name }) =>
|
||||
deleteConfigMap(environmentId, namespace, name)
|
||||
)
|
||||
);
|
||||
const successfulConfigMaps = promises
|
||||
.filter(isFulfilled)
|
||||
.map((_, index) => configMaps[index].name);
|
||||
const failedConfigMaps = promises
|
||||
.filter(isRejected)
|
||||
.map(({ reason }, index) => ({
|
||||
name: configMaps[index].name,
|
||||
reason,
|
||||
}));
|
||||
return { failedConfigMaps, successfulConfigMaps };
|
||||
},
|
||||
{
|
||||
...withGlobalError('Unable to remove ConfigMaps'),
|
||||
onSuccess: ({ failedConfigMaps, successfulConfigMaps }) => {
|
||||
// Promise.allSettled can also resolve with errors, so check for errors here
|
||||
// show an error message for each configmap that failed to delete
|
||||
failedConfigMaps.forEach(({ name, reason }) => {
|
||||
notifyError(
|
||||
`Failed to remove ConfigMap '${name}'`,
|
||||
new Error(reason.message) as Error
|
||||
);
|
||||
});
|
||||
// show one summary message for all successful deletes
|
||||
if (successfulConfigMaps.length) {
|
||||
notifySuccess(
|
||||
`${pluralize(
|
||||
successfulConfigMaps.length,
|
||||
'ConfigMap'
|
||||
)} successfully removed`,
|
||||
successfulConfigMaps.join(', ')
|
||||
);
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: configMapQueryKeys.configMapsForCluster(environmentId),
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function deleteConfigMap(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
try {
|
||||
await axios.delete(
|
||||
`/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/configmaps/${name}`
|
||||
);
|
||||
} catch (e) {
|
||||
throw parseKubernetesAxiosError(e, 'Unable to remove ConfigMap');
|
||||
}
|
||||
}
|
76
app/react/kubernetes/configs/queries/useDeleteSecrets.ts
Normal file
76
app/react/kubernetes/configs/queries/useDeleteSecrets.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { useMutation } from '@tanstack/react-query';
|
||||
|
||||
import { queryClient, withGlobalError } from '@/react-tools/react-query';
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import {
|
||||
error as notifyError,
|
||||
notifySuccess,
|
||||
} from '@/portainer/services/notifications';
|
||||
import { isFulfilled, isRejected } from '@/portainer/helpers/promise-utils';
|
||||
import { pluralize } from '@/portainer/helpers/strings';
|
||||
|
||||
import { parseKubernetesAxiosError } from '../../axiosError';
|
||||
|
||||
import { secretQueryKeys } from './query-keys';
|
||||
|
||||
export function useDeleteSecrets(environmentId: EnvironmentId) {
|
||||
return useMutation(
|
||||
async (secrets: { namespace: string; name: string }[]) => {
|
||||
const promises = await Promise.allSettled(
|
||||
secrets.map(({ namespace, name }) =>
|
||||
deleteSecret(environmentId, namespace, name)
|
||||
)
|
||||
);
|
||||
const successfulSecrets = promises
|
||||
.filter(isFulfilled)
|
||||
.map((_, index) => secrets[index].name);
|
||||
const failedSecrets = promises
|
||||
.filter(isRejected)
|
||||
.map(({ reason }, index) => ({
|
||||
name: secrets[index].name,
|
||||
reason,
|
||||
}));
|
||||
return { failedSecrets, successfulSecrets };
|
||||
},
|
||||
{
|
||||
...withGlobalError('Unable to remove secrets'),
|
||||
onSuccess: ({ failedSecrets, successfulSecrets }) => {
|
||||
// show an error message for each secret that failed to delete
|
||||
failedSecrets.forEach(({ name, reason }) => {
|
||||
notifyError(
|
||||
`Failed to remove secret '${name}'`,
|
||||
new Error(reason.message) as Error
|
||||
);
|
||||
});
|
||||
// show one summary message for all successful deletes
|
||||
if (successfulSecrets.length) {
|
||||
notifySuccess(
|
||||
`${pluralize(
|
||||
successfulSecrets.length,
|
||||
'Secret'
|
||||
)} successfully removed`,
|
||||
successfulSecrets.join(', ')
|
||||
);
|
||||
}
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: secretQueryKeys.secretsForCluster(environmentId),
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function deleteSecret(
|
||||
environmentId: EnvironmentId,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
try {
|
||||
await axios.delete(
|
||||
`/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/secrets/${name}`
|
||||
);
|
||||
} catch (e) {
|
||||
throw parseKubernetesAxiosError(e, 'Unable to remove secret');
|
||||
}
|
||||
}
|
39
app/react/kubernetes/configs/queries/useSecrets.ts
Normal file
39
app/react/kubernetes/configs/queries/useSecrets.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Secret, SecretList } from 'kubernetes-types/core/v1';
|
||||
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { parseKubernetesAxiosError } from '../../axiosError';
|
||||
|
||||
import { secretQueryKeys } from './query-keys';
|
||||
|
||||
// returns a usequery hook for the list of secrets from the kubernetes API
|
||||
export function useSecrets(environmentId: EnvironmentId, namespace?: string) {
|
||||
return useQuery(
|
||||
secretQueryKeys.secrets(environmentId, namespace),
|
||||
() => (namespace ? getSecrets(environmentId, namespace) : []),
|
||||
{
|
||||
...withGlobalError(`Unable to get secrets in namespace '${namespace}'`),
|
||||
enabled: !!namespace,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// get all secrets for a namespace
|
||||
async function getSecrets(environmentId: EnvironmentId, namespace: string) {
|
||||
try {
|
||||
const { data } = await axios.get<SecretList>(
|
||||
`/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/secrets`
|
||||
);
|
||||
// when fetching a list, the kind isn't appended to the items, so we need to add it
|
||||
const secrets: Secret[] = data.items.map((secret) => ({
|
||||
...secret,
|
||||
kind: 'Secret',
|
||||
}));
|
||||
return secrets;
|
||||
} catch (e) {
|
||||
throw parseKubernetesAxiosError(e, 'Unable to retrieve secrets');
|
||||
}
|
||||
}
|
59
app/react/kubernetes/configs/queries/useSecretsForCluster.ts
Normal file
59
app/react/kubernetes/configs/queries/useSecretsForCluster.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { Configuration } from '../types';
|
||||
|
||||
import { SecretQueryParams } from './types';
|
||||
import { secretQueryKeys } from './query-keys';
|
||||
|
||||
export function useSecretsForCluster<TData = Configuration[]>(
|
||||
environmentId: EnvironmentId,
|
||||
options?: {
|
||||
autoRefreshRate?: number;
|
||||
select?: (data: Configuration[]) => TData;
|
||||
} & SecretQueryParams
|
||||
) {
|
||||
const { autoRefreshRate, select, ...params } = options ?? {};
|
||||
return useQuery(
|
||||
secretQueryKeys.secretsForCluster(environmentId, params),
|
||||
() =>
|
||||
getSecretsForCluster(environmentId, {
|
||||
...params,
|
||||
isUsed: params?.isUsed,
|
||||
}),
|
||||
{
|
||||
...withGlobalError('Unable to retrieve secrets for cluster'),
|
||||
refetchInterval() {
|
||||
return options?.autoRefreshRate ?? false;
|
||||
},
|
||||
select,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function getSecretsForCluster(
|
||||
environmentId: EnvironmentId,
|
||||
params?: { withData?: boolean; isUsed?: boolean }
|
||||
) {
|
||||
const secrets = await getSecrets(environmentId, params);
|
||||
return secrets;
|
||||
}
|
||||
|
||||
// get all secrets for a cluster
|
||||
async function getSecrets(
|
||||
environmentId: EnvironmentId,
|
||||
params?: { withData?: boolean; isUsed?: boolean } | undefined
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<Configuration[]>(
|
||||
`/kubernetes/${environmentId}/secrets`,
|
||||
{ params }
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseAxiosError(e, 'Unable to retrieve secrets');
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue