From 2d57a01245fad259a47872487ce43291bada3b73 Mon Sep 17 00:00:00 2001 From: testA113 Date: Sat, 5 Oct 2024 22:24:43 +1300 Subject: [PATCH] refactor and get unused cluster roles --- .../ClusterRoleBindingsDatatable.tsx | 15 ++-- ...ingsQuery.ts => useClusterRoleBindings.ts} | 2 +- ...ion.ts => useDeleteClusterRoleBindings.ts} | 4 +- .../ClusterRolesDatatable.tsx | 72 ++++++++++++++++--- .../ClusterRolesDatatable/columns/helper.ts | 4 +- .../ClusterRolesDatatable/columns/name.tsx | 2 + .../ClusterRolesDatatable/types.ts | 4 ++ .../RoleBindingsDatatable.tsx | 4 +- ...eAllRoleBindings.ts => useRoleBindings.ts} | 2 +- .../RolesDatatable/RolesDatatable.tsx | 14 ++-- .../queries/{useAllRoles.ts => useRoles.ts} | 2 +- .../ServiceAccountsDatatable.tsx | 6 -- 12 files changed, 87 insertions(+), 44 deletions(-) rename app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/{useGetClusterRoleBindingsQuery.ts => useClusterRoleBindings.ts} (95%) rename app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/{useDeleteClusterRoleBindingsMutation.ts => useDeleteClusterRoleBindings.ts} (91%) rename app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/queries/{useAllRoleBindings.ts => useRoleBindings.ts} (96%) rename app/react/kubernetes/more-resources/RolesView/RolesDatatable/queries/{useAllRoles.ts => useRoles.ts} (97%) diff --git a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/ClusterRoleBindingsDatatable.tsx b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/ClusterRoleBindingsDatatable.tsx index 1398a306b..1254fa0fa 100644 --- a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/ClusterRoleBindingsDatatable.tsx +++ b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/ClusterRoleBindingsDatatable.tsx @@ -20,8 +20,8 @@ import { DefaultDatatableSettings } from '../../../datatables/DefaultDatatableSe import { ClusterRoleBinding } from './types'; import { columns } from './columns'; -import { useGetClusterRoleBindingsQuery } from './queries/useGetClusterRoleBindingsQuery'; -import { useDeleteClusterRoleBindingsMutation } from './queries/useDeleteClusterRoleBindingsMutation'; +import { useClusterRoleBindings } from './queries/useClusterRoleBindings'; +import { useDeleteClusterRoleBindings } from './queries/useDeleteClusterRoleBindings'; const storageKey = 'clusterRoleBindings'; const settingsStore = createStore(storageKey); @@ -29,12 +29,9 @@ const settingsStore = createStore(storageKey); export function ClusterRoleBindingsDatatable() { const environmentId = useEnvironmentId(); const tableState = useTableState(settingsStore, storageKey); - const clusterRoleBindingsQuery = useGetClusterRoleBindingsQuery( - environmentId, - { - autoRefreshRate: tableState.autoRefreshRate * 1000, - } - ); + const clusterRoleBindingsQuery = useClusterRoleBindings(environmentId, { + autoRefreshRate: tableState.autoRefreshRate * 1000, + }); const filteredClusterRoleBindings = useMemo( () => @@ -102,7 +99,7 @@ type TableActionsProps = { function TableActions({ selectedItems }: TableActionsProps) { const environmentId = useEnvironmentId(); const deleteClusterRoleBindingsMutation = - useDeleteClusterRoleBindingsMutation(environmentId); + useDeleteClusterRoleBindings(environmentId); const router = useRouter(); async function handleRemoveClick(roles: SelectedRole[]) { diff --git a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useGetClusterRoleBindingsQuery.ts b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useClusterRoleBindings.ts similarity index 95% rename from app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useGetClusterRoleBindingsQuery.ts rename to app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useClusterRoleBindings.ts index 3e2ce70c0..2c3d3544d 100644 --- a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useGetClusterRoleBindingsQuery.ts +++ b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useClusterRoleBindings.ts @@ -9,7 +9,7 @@ import { ClusterRoleBinding } from '../types'; import { queryKeys } from './query-keys'; -export function useGetClusterRoleBindingsQuery( +export function useClusterRoleBindings( environmentId: EnvironmentId, options?: { autoRefreshRate?: number } ) { diff --git a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useDeleteClusterRoleBindingsMutation.ts b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useDeleteClusterRoleBindings.ts similarity index 91% rename from app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useDeleteClusterRoleBindingsMutation.ts rename to app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useDeleteClusterRoleBindings.ts index 9767f90e3..5724b4fb9 100644 --- a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useDeleteClusterRoleBindingsMutation.ts +++ b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRoleBindingsDatatable/queries/useDeleteClusterRoleBindings.ts @@ -6,9 +6,7 @@ import { EnvironmentId } from '@/react/portainer/environments/types'; import { queryKeys } from './query-keys'; -export function useDeleteClusterRoleBindingsMutation( - environmentId: EnvironmentId -) { +export function useDeleteClusterRoleBindings(environmentId: EnvironmentId) { const queryClient = useQueryClient(); return useMutation(deleteClusterRoleBindings, { ...withInvalidate(queryClient, [queryKeys.list(environmentId)]), diff --git a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/ClusterRolesDatatable.tsx b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/ClusterRolesDatatable.tsx index 310321dd7..c61c79330 100644 --- a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/ClusterRolesDatatable.tsx +++ b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/ClusterRolesDatatable.tsx @@ -15,8 +15,12 @@ import { LoadingButton } from '@@/buttons'; import { useTableState } from '@@/datatables/useTableState'; import { DefaultDatatableSettings } from '../../../datatables/DefaultDatatableSettings'; +import { useClusterRoleBindings } from '../ClusterRoleBindingsDatatable/queries/useClusterRoleBindings'; +import { useRoleBindings } from '../../RolesView/RoleBindingsDatatable/queries/useRoleBindings'; +import { ClusterRoleBinding } from '../ClusterRoleBindingsDatatable/types'; +import { RoleBinding } from '../../RolesView/RoleBindingsDatatable/types'; -import { ClusterRole } from './types'; +import { ClusterRole, ClusterRoleRowData } from './types'; import { columns } from './columns'; import { useClusterRoles } from './queries/useClusterRoles'; import { useDeleteClusterRoles } from './queries/useDeleteClusterRoles'; @@ -27,26 +31,41 @@ const settingsStore = createStore(storageKey); export function ClusterRolesDatatable() { const environmentId = useEnvironmentId(); const tableState = useTableState(settingsStore, storageKey); + const clusterRolesQuery = useClusterRoles(environmentId, { autoRefreshRate: tableState.autoRefreshRate * 1000, }); + const clusterRoleBindingsQuery = useClusterRoleBindings(environmentId); + const roleBindingsQuery = useRoleBindings(environmentId); + + const clusterRolesWithUnusedFlag = useClusterRolesWithUnusedFlag( + clusterRolesQuery.data, + clusterRoleBindingsQuery.data, + roleBindingsQuery.data + ); + + const filteredClusterRoles = useMemo( + () => + clusterRolesWithUnusedFlag.filter( + (cr) => tableState.showSystemResources || !cr.isSystem + ), + [clusterRolesWithUnusedFlag, tableState.showSystemResources] + ); + + const isLoading = + clusterRolesQuery.isLoading || + clusterRoleBindingsQuery.isLoading || + roleBindingsQuery.isLoading; const { authorized: isAuthorizedToAddEdit } = useAuthorizations([ 'K8sClusterRolesW', ]); - const filteredClusterRoles = useMemo( - () => - clusterRolesQuery.data?.filter( - (cr) => tableState.showSystemResources || !cr.isSystem - ), - [clusterRolesQuery.data, tableState.showSystemResources] - ); return ( ); } + +// Updated custom hook +function useClusterRolesWithUnusedFlag( + clusterRoles?: ClusterRole[], + clusterRoleBindings?: ClusterRoleBinding[], + roleBindings?: RoleBinding[] +): ClusterRoleRowData[] { + return useMemo(() => { + if (!clusterRoles || !clusterRoleBindings || !roleBindings) { + return []; + } + + const usedRoleNames = new Set(); + + // Check ClusterRoleBindings + clusterRoleBindings.forEach((binding) => { + if (binding.roleRef.kind === 'ClusterRole') { + usedRoleNames.add(binding.roleRef.name); + } + }); + + // Check RoleBindings + roleBindings.forEach((binding) => { + if (binding.roleRef.kind === 'ClusterRole') { + usedRoleNames.add(binding.roleRef.name); + } + }); + + // Mark cluster roles as unused if they're not in the usedRoleNames set + return clusterRoles.map((clusterRole) => ({ + ...clusterRole, + isUnused: !usedRoleNames.has(clusterRole.name) && !clusterRole.isSystem, + })); + }, [clusterRoles, clusterRoleBindings, roleBindings]); +} diff --git a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/helper.ts b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/helper.ts index 6dbf16107..4b22ca818 100644 --- a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/helper.ts +++ b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/helper.ts @@ -1,5 +1,5 @@ import { createColumnHelper } from '@tanstack/react-table'; -import { ClusterRole } from '../types'; +import { ClusterRoleRowData } from '../types'; -export const columnHelper = createColumnHelper(); +export const columnHelper = createColumnHelper(); diff --git a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/name.tsx b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/name.tsx index 15a879a3a..53addd62e 100644 --- a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/name.tsx +++ b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/columns/name.tsx @@ -1,4 +1,5 @@ import { SystemBadge } from '@@/Badge/SystemBadge'; +import { UnusedBadge } from '@@/Badge/UnusedBadge'; import { columnHelper } from './helper'; @@ -17,6 +18,7 @@ export const name = columnHelper.accessor(
{row.original.name} {row.original.isSystem && } + {row.original.isUnused && }
), } diff --git a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/types.ts b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/types.ts index fcb0795a8..b3de8ca4c 100644 --- a/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/types.ts +++ b/app/react/kubernetes/more-resources/ClusterRolesView/ClusterRolesDatatable/types.ts @@ -5,6 +5,10 @@ export type ClusterRole = { isSystem: boolean; }; +export type ClusterRoleRowData = ClusterRole & { + isUnused: boolean; +}; + export type DeleteRequestPayload = { clusterRoles: string[]; }; diff --git a/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/RoleBindingsDatatable.tsx b/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/RoleBindingsDatatable.tsx index 0e94790dc..6a70c7b68 100644 --- a/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/RoleBindingsDatatable.tsx +++ b/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/RoleBindingsDatatable.tsx @@ -25,7 +25,7 @@ import { import { RoleBinding } from './types'; import { columns } from './columns'; -import { useAllRoleBindings } from './queries/useAllRoleBindings'; +import { useRoleBindings } from './queries/useRoleBindings'; import { useDeleteRoleBindings } from './queries/useDeleteRoleBindings'; const storageKey = 'roleBindings'; @@ -42,7 +42,7 @@ export function RoleBindingsDatatable() { ...filteredColumnsSettings(set), }) ); - const roleBindingsQuery = useAllRoleBindings(environmentId, { + const roleBindingsQuery = useRoleBindings(environmentId, { autoRefreshRate: tableState.autoRefreshRate * 1000, }); const filteredRoleBindings = useMemo( diff --git a/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/queries/useAllRoleBindings.ts b/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/queries/useRoleBindings.ts similarity index 96% rename from app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/queries/useAllRoleBindings.ts rename to app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/queries/useRoleBindings.ts index 5b544e0f8..d2e399955 100644 --- a/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/queries/useAllRoleBindings.ts +++ b/app/react/kubernetes/more-resources/RolesView/RoleBindingsDatatable/queries/useRoleBindings.ts @@ -8,7 +8,7 @@ import { RoleBinding } from '../types'; import { queryKeys } from './query-keys'; -export function useAllRoleBindings( +export function useRoleBindings( environmentId: EnvironmentId, options?: { autoRefreshRate?: number; enabled?: boolean } ) { diff --git a/app/react/kubernetes/more-resources/RolesView/RolesDatatable/RolesDatatable.tsx b/app/react/kubernetes/more-resources/RolesView/RolesDatatable/RolesDatatable.tsx index 2a1ffb6e7..b24eca7f5 100644 --- a/app/react/kubernetes/more-resources/RolesView/RolesDatatable/RolesDatatable.tsx +++ b/app/react/kubernetes/more-resources/RolesView/RolesDatatable/RolesDatatable.tsx @@ -7,7 +7,6 @@ import { Authorized } from '@/react/hooks/useUser'; import { notifyError, notifySuccess } from '@/portainer/services/notifications'; import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription'; import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton'; -import { useUnauthorizedRedirect } from '@/react/hooks/useUnauthorizedRedirect'; import { DefaultDatatableSettings, TableSettings as KubeTableSettings, @@ -22,12 +21,12 @@ import { filteredColumnsSettings, } from '@@/datatables/types'; -import { useAllRoleBindings } from '../RoleBindingsDatatable/queries/useAllRoleBindings'; +import { useRoleBindings } from '../RoleBindingsDatatable/queries/useRoleBindings'; import { RoleBinding } from '../RoleBindingsDatatable/types'; import { columns } from './columns'; import { Role, RoleRowData } from './types'; -import { useAllRoles } from './queries/useAllRoles'; +import { useRoles } from './queries/useRoles'; import { useDeleteRoles } from './queries/useDeleteRoles'; const storageKey = 'roles'; @@ -36,11 +35,6 @@ interface TableSettings FilteredColumnsTableSettings {} export function RolesDatatable() { - useUnauthorizedRedirect( - { authorizations: ['K8sRolesW'] }, - { to: 'kubernetes.dashboard' } - ); - const environmentId = useEnvironmentId(); const tableState = useKubeStore( storageKey, @@ -49,10 +43,10 @@ export function RolesDatatable() { ...filteredColumnsSettings(set), }) ); - const rolesQuery = useAllRoles(environmentId, { + const rolesQuery = useRoles(environmentId, { autoRefreshRate: tableState.autoRefreshRate * 1000, }); - const roleBindingsQuery = useAllRoleBindings(environmentId, { + const roleBindingsQuery = useRoleBindings(environmentId, { autoRefreshRate: tableState.autoRefreshRate * 1000, }); const roleRowData = useRoleRowData(rolesQuery.data, roleBindingsQuery.data); diff --git a/app/react/kubernetes/more-resources/RolesView/RolesDatatable/queries/useAllRoles.ts b/app/react/kubernetes/more-resources/RolesView/RolesDatatable/queries/useRoles.ts similarity index 97% rename from app/react/kubernetes/more-resources/RolesView/RolesDatatable/queries/useAllRoles.ts rename to app/react/kubernetes/more-resources/RolesView/RolesDatatable/queries/useRoles.ts index a4019466b..aa5ec8eae 100644 --- a/app/react/kubernetes/more-resources/RolesView/RolesDatatable/queries/useAllRoles.ts +++ b/app/react/kubernetes/more-resources/RolesView/RolesDatatable/queries/useRoles.ts @@ -11,7 +11,7 @@ const queryKeys = { ['environments', environmentId, 'kubernetes', 'roles'] as const, }; -export function useAllRoles( +export function useRoles( environmentId: EnvironmentId, options?: { autoRefreshRate?: number; enabled?: boolean } ) { diff --git a/app/react/kubernetes/more-resources/ServiceAccountsView/ServiceAccountsDatatable/ServiceAccountsDatatable.tsx b/app/react/kubernetes/more-resources/ServiceAccountsView/ServiceAccountsDatatable/ServiceAccountsDatatable.tsx index f16ff83cb..eef8b94bf 100644 --- a/app/react/kubernetes/more-resources/ServiceAccountsView/ServiceAccountsDatatable/ServiceAccountsDatatable.tsx +++ b/app/react/kubernetes/more-resources/ServiceAccountsView/ServiceAccountsDatatable/ServiceAccountsDatatable.tsx @@ -12,7 +12,6 @@ import { TableSettings as KubeTableSettings, } from '@/react/kubernetes/datatables/DefaultDatatableSettings'; import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton'; -import { useUnauthorizedRedirect } from '@/react/hooks/useUnauthorizedRedirect'; import { isSystemNamespace } from '@/react/kubernetes/namespaces/queries/useIsSystemNamespace'; import { useKubeStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; @@ -35,11 +34,6 @@ interface TableSettings FilteredColumnsTableSettings {} export function ServiceAccountsDatatable() { - useUnauthorizedRedirect( - { authorizations: ['K8sServiceAccountsW'] }, - { to: 'kubernetes.dashboard' } - ); - const environmentId = useEnvironmentId(); const tableState = useKubeStore( storageKey,