From 3244a66bfa47dac22b65e8d10f207a988081744b Mon Sep 17 00:00:00 2001 From: guilhane Date: Thu, 1 Aug 2024 12:35:38 +0200 Subject: [PATCH] Added /crs page to display a list of all instantiated custom resources. Fixes: #1962 Signed-off-by: guilhane --- .../src/components/Sidebar/prepareRoutes.ts | 6 + .../crd/CustomResourceInstancesList.tsx | 142 ++++++++++++++++++ frontend/src/lib/router.tsx | 8 + 3 files changed, 156 insertions(+) create mode 100644 frontend/src/components/crd/CustomResourceInstancesList.tsx diff --git a/frontend/src/components/Sidebar/prepareRoutes.ts b/frontend/src/components/Sidebar/prepareRoutes.ts index 53804a152a7..fe3f0b50f51 100644 --- a/frontend/src/components/Sidebar/prepareRoutes.ts +++ b/frontend/src/components/Sidebar/prepareRoutes.ts @@ -71,6 +71,12 @@ function prepareRoutes( { name: 'crds', label: t('glossary|Custom Resources'), + subList: [ + { + name: 'crs', + label: t('glossary|CR Instances'), + }, + ], }, ], }, diff --git a/frontend/src/components/crd/CustomResourceInstancesList.tsx b/frontend/src/components/crd/CustomResourceInstancesList.tsx new file mode 100644 index 00000000000..80d31cadd31 --- /dev/null +++ b/frontend/src/components/crd/CustomResourceInstancesList.tsx @@ -0,0 +1,142 @@ +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import CRD, { KubeCRD } from '../../lib/k8s/crd'; +import { Link, Loader, SectionBox } from '../common/'; +import Empty from '../common/EmptyContent'; +import { ResourceListView } from '../common/Resource'; + +interface State { + crList: KubeCRD[]; + loading: boolean; + crDictionary: Map; +} + +export function CrInstanceList() { + const { t } = useTranslation(['glossary', 'translation']); + const [crds, crdsError] = CRD.useList(); + const [state, setState] = useState({ + crList: [], + loading: true, + crDictionary: new Map(), + }); + + useEffect(() => { + const fetchCRs = async () => { + const allCrs: KubeCRD[] = []; + const newCrDictionary = new Map(); + + for (const crd of crds) { + const crClass = crd.makeCRClass(); + const [crItems, crError] = await new Promise<[KubeCRD[] | null, any | null]>(resolve => { + crClass.apiList( + (items: KubeCRD[]) => resolve([items, null]), + (err: any) => resolve([null, err]) + )(); + }); + + if (crError) { + console.error('Error fetching CRs:', crError); + continue; + } + + if (crItems && crItems.length > 0) { + allCrs.push(...crItems); + for (const item of crItems) { + newCrDictionary.set(item.metadata.name, crd); + } + } + } + + setState({ + crList: allCrs, + loading: false, + crDictionary: newCrDictionary, + }); + }; + + if (crds) { + fetchCRs(); + } + }, [crds]); + + if (crdsError) { + return ( + + {t('translation|Error getting custom resource definitions: {{ errorMessage }}', { + errorMessage: crdsError, + })} + + ); + } + + if (state.loading) { + return ; + } + + if (state.crList.length === 0) { + return {t('translation|No custom resources found.')}; + } + + return ( + + { + return cr.metadata.name; + }, + render: cr => { + return ( + + {cr.metadata.name} {/*crd.metadata.name*/} + + ); + }, + }, + { + label: 'CRD', + getValue: cr => { + return cr.metadata.name; + }, + render: cr => { + return ( + + {cr.kind} {/*crd.metadata.name*/} + + ); + }, + }, + { + label: 'Categories', + getValue: cr => { + const categories = state.crDictionary.get(cr.metadata.name)?.jsonData!.status + .acceptedNames.categories; + return categories !== undefined ? categories.toString().split(',').join(', ') : ''; + }, + }, + 'namespace', + + 'age', + ]} + /> + + ); +} diff --git a/frontend/src/lib/router.tsx b/frontend/src/lib/router.tsx index 279cff2fecb..d73e5c0aa93 100644 --- a/frontend/src/lib/router.tsx +++ b/frontend/src/lib/router.tsx @@ -15,6 +15,7 @@ import { PageGrid } from '../components/common/Resource/Resource'; import ConfigDetails from '../components/configmap/Details'; import ConfigMapList from '../components/configmap/List'; import CustomResourceDetails from '../components/crd/CustomResourceDetails'; +import { CrInstanceList } from '../components/crd/CustomResourceInstancesList'; import CustomResourceList from '../components/crd/CustomResourceList'; import CustomResourceDefinitionDetails from '../components/crd/Details'; import CustomResourceDefinitionList from '../components/crd/List'; @@ -652,6 +653,13 @@ const defaultRoutes: { sidebar: 'crds', component: () => , }, + crs: { + path: '/crs', + exact: true, + name: 'CRInstances', + sidebar: 'crs', + component: () => , + }, notifications: { path: '/notifications', exact: true,