mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +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
16
app/react/kubernetes/volumes/queries/query-keys.ts
Normal file
16
app/react/kubernetes/volumes/queries/query-keys.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
export const queryKeys = {
|
||||
volumes: (environmentId: EnvironmentId) => [
|
||||
'environments',
|
||||
environmentId,
|
||||
'kubernetes',
|
||||
'volumes',
|
||||
],
|
||||
storages: (environmentId: EnvironmentId) => [
|
||||
'environments',
|
||||
environmentId,
|
||||
'kubernetes',
|
||||
'storages',
|
||||
],
|
||||
};
|
60
app/react/kubernetes/volumes/queries/useDeleteVolumes.ts
Normal file
60
app/react/kubernetes/volumes/queries/useDeleteVolumes.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { getAllSettledItems } from '@/portainer/helpers/promise-utils';
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
||||
import { pluralize } from '@/portainer/helpers/strings';
|
||||
|
||||
import { parseKubernetesAxiosError } from '../../axiosError';
|
||||
import { VolumeViewModel } from '../ListView/types';
|
||||
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
export function useDeleteVolumes(environmentId: EnvironmentId) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (volumes: VolumeViewModel[]) =>
|
||||
deleteVolumes(volumes, environmentId),
|
||||
onSuccess: ({ fulfilledItems, rejectedItems }) => {
|
||||
// one error notification per rejected item
|
||||
rejectedItems.forEach(({ item, reason }) => {
|
||||
notifyError(
|
||||
`Failed to remove volume '${item.PersistentVolumeClaim.Name}'`,
|
||||
new Error(reason)
|
||||
);
|
||||
});
|
||||
|
||||
// one success notification for all fulfilled items
|
||||
if (fulfilledItems.length) {
|
||||
notifySuccess(
|
||||
`${pluralize(fulfilledItems.length, 'Volume')} successfully removed`,
|
||||
fulfilledItems
|
||||
.map((item) => item.PersistentVolumeClaim.Name)
|
||||
.join(', ')
|
||||
);
|
||||
}
|
||||
queryClient.invalidateQueries(queryKeys.storages(environmentId));
|
||||
queryClient.invalidateQueries(queryKeys.volumes(environmentId));
|
||||
},
|
||||
...withGlobalError('Unable to remove volumes'),
|
||||
});
|
||||
}
|
||||
|
||||
function deleteVolumes(
|
||||
volumes: VolumeViewModel[],
|
||||
environmentId: EnvironmentId
|
||||
) {
|
||||
return getAllSettledItems(volumes, deleteVolume);
|
||||
|
||||
async function deleteVolume(volume: VolumeViewModel) {
|
||||
try {
|
||||
await axios.delete(
|
||||
`/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${volume.ResourcePool.Namespace.Name}/persistentvolumeclaims/${volume.PersistentVolumeClaim.Name}`
|
||||
);
|
||||
} catch (error) {
|
||||
throw parseKubernetesAxiosError(error, 'Unable to remove volume');
|
||||
}
|
||||
}
|
||||
}
|
160
app/react/kubernetes/volumes/queries/useVolumesQuery.ts
Normal file
160
app/react/kubernetes/volumes/queries/useVolumesQuery.ts
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { humanize } from '@/portainer/filters/filters';
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios from '@/portainer/services/axios';
|
||||
import { Volume } from '@/kubernetes/models/volume/Volume';
|
||||
|
||||
import { parseKubernetesAxiosError } from '../../axiosError';
|
||||
import { K8sVolumeInfo } from '../types';
|
||||
import { VolumeViewModel, StorageClassViewModel } from '../ListView/types';
|
||||
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
// useQuery to get a list of all volumes in a cluster
|
||||
export function useAllVolumesQuery(
|
||||
environmentId: EnvironmentId,
|
||||
queryOptions?: {
|
||||
refetchInterval?: number;
|
||||
}
|
||||
) {
|
||||
return useQuery(
|
||||
queryKeys.volumes(environmentId),
|
||||
() => getAllVolumes(environmentId, { withApplications: true }),
|
||||
{
|
||||
refetchInterval: queryOptions?.refetchInterval,
|
||||
select: convertToVolumeViewModels,
|
||||
...withGlobalError('Unable to retrieve volumes'),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// useQuery to get a list of all volumes in a cluster
|
||||
export function useAllStoragesQuery(
|
||||
environmentId: EnvironmentId,
|
||||
queryOptions?: {
|
||||
refetchInterval?: number;
|
||||
}
|
||||
) {
|
||||
return useQuery(
|
||||
queryKeys.storages(environmentId),
|
||||
() => getAllVolumes(environmentId),
|
||||
{
|
||||
refetchInterval: queryOptions?.refetchInterval,
|
||||
select: convertToStorageClassViewModels,
|
||||
...withGlobalError('Unable to retrieve volumes'),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// get all volumes from a namespace
|
||||
export async function getAllVolumes(
|
||||
environmentId: EnvironmentId,
|
||||
params?: { withApplications: boolean }
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<K8sVolumeInfo[]>(
|
||||
`/kubernetes/${environmentId}/volumes`,
|
||||
{ params }
|
||||
);
|
||||
return data;
|
||||
} catch (e) {
|
||||
throw parseKubernetesAxiosError(e, 'Unable to retrieve volumes');
|
||||
}
|
||||
}
|
||||
|
||||
function convertToVolumeViewModels(
|
||||
volumes: K8sVolumeInfo[]
|
||||
): VolumeViewModel[] {
|
||||
return volumes.map((volume) => {
|
||||
const owningApplications =
|
||||
volume.persistentVolumeClaim.owningApplications ?? [];
|
||||
return {
|
||||
Applications: owningApplications.map((app) => ({
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
Kind: app.Kind,
|
||||
})),
|
||||
PersistentVolumeClaim: {
|
||||
Namespace: volume.persistentVolumeClaim.namespace,
|
||||
Name: volume.persistentVolumeClaim.name,
|
||||
storageClass: {
|
||||
Name: volume.persistentVolumeClaim.storageClass || '',
|
||||
},
|
||||
Storage: humanize(volume.persistentVolumeClaim.storage),
|
||||
CreationDate: volume.persistentVolumeClaim.creationDate,
|
||||
ApplicationOwner:
|
||||
volume.persistentVolumeClaim.owningApplications?.[0]?.Name,
|
||||
},
|
||||
ResourcePool: {
|
||||
Namespace: {
|
||||
Name: volume.persistentVolumeClaim.namespace,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function convertToStorageClassViewModels(
|
||||
volumes: K8sVolumeInfo[]
|
||||
): StorageClassViewModel[] {
|
||||
const volumesModels = convertToVolumeModel(volumes);
|
||||
|
||||
// Use reduce to create a new Map
|
||||
const storageClassMap = volumesModels.reduce((acc, volume) => {
|
||||
const pvcStorageClass = volume.PersistentVolumeClaim.storageClass;
|
||||
const storageClassName = pvcStorageClass?.Name || 'none';
|
||||
const defaultStorageClass: StorageClassViewModel = {
|
||||
Name: pvcStorageClass?.Name || 'none',
|
||||
Provisioner: pvcStorageClass?.Provisioner ?? '',
|
||||
ReclaimPolicy: pvcStorageClass?.ReclaimPolicy ?? '',
|
||||
AllowVolumeExpansion: pvcStorageClass?.AllowVolumeExpansion || false,
|
||||
size: 0,
|
||||
Volumes: [],
|
||||
};
|
||||
|
||||
const existingStorageClass =
|
||||
acc.get(storageClassName) ?? defaultStorageClass;
|
||||
|
||||
// Create a new StorageClassViewModel with updated values
|
||||
const updatedStorageClass = {
|
||||
...existingStorageClass,
|
||||
size:
|
||||
existingStorageClass.size + (volume.PersistentVolumeClaim.Storage || 0),
|
||||
Volumes: [...existingStorageClass.Volumes, volume],
|
||||
};
|
||||
|
||||
// Return a new Map with the updated StorageClassViewModel
|
||||
return new Map(acc).set(storageClassName, updatedStorageClass);
|
||||
}, new Map<string, StorageClassViewModel>());
|
||||
|
||||
// Convert the Map values to an array
|
||||
return Array.from(storageClassMap.values());
|
||||
}
|
||||
|
||||
function convertToVolumeModel(volumes: K8sVolumeInfo[]): Volume[] {
|
||||
return volumes.map((volume) => ({
|
||||
PersistentVolumeClaim: {
|
||||
Id: volume.persistentVolumeClaim.id,
|
||||
Name: volume.persistentVolumeClaim.name,
|
||||
PreviousName: '',
|
||||
Namespace: volume.persistentVolumeClaim.namespace,
|
||||
storageClass: {
|
||||
Name: volume.persistentVolumeClaim.storageClass || '',
|
||||
Provisioner: volume.storageClass.provisioner,
|
||||
ReclaimPolicy: volume.storageClass.reclaimPolicy ?? '',
|
||||
AllowVolumeExpansion: volume.storageClass.allowVolumeExpansion || false,
|
||||
},
|
||||
Storage: volume.persistentVolumeClaim.storage,
|
||||
CreationDate: volume.persistentVolumeClaim.creationDate,
|
||||
ApplicationOwner:
|
||||
volume.persistentVolumeClaim.owningApplications?.[0]?.Name ?? '',
|
||||
AccessModes: volume.persistentVolumeClaim.accessModes ?? [],
|
||||
ApplicationName: '',
|
||||
MountPath: '',
|
||||
Yaml: '',
|
||||
},
|
||||
Applications: [],
|
||||
}));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue