Skip to content

Commit

Permalink
squash! Added /crs page to display a list of all instantiated custom …
Browse files Browse the repository at this point in the history
…resources. Fixes: #1962 Signed-off-by: guilhane <guilhane.bourgoin@orange.com>

NOTE: Add prefix to the base commit title.

Signed-off-by: Joaquim Rocha <joaquim.rocha@microsoft.com>
  • Loading branch information
joaquimrocha committed Oct 2, 2024
1 parent 52aac41 commit 21e0883
Showing 1 changed file with 63 additions and 33 deletions.
96 changes: 63 additions & 33 deletions frontend/src/components/crd/CustomResourceInstancesList.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { KubeMetadata, KubeObject } from '../../lib/k8s/cluster';
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<string, KubeCRD>;
interface CRInstancesCache {
[crdName: string]: {
instances: KubeObject[];
crd: KubeCRD;
};
}

export function CrInstanceList() {
const { t } = useTranslation(['glossary', 'translation']);
const [crds, crdsError] = CRD.useList();
const [state, setState] = useState<State>({
crList: [],
loading: true,
crDictionary: new Map<string, KubeCRD>(),
});
const [isLoading, setIsLoading] = useState(true);
const crInstancesCache = useRef<CRInstancesCache>({});
const crToCRDs = useRef<{ [crUID: KubeMetadata['uid']]: KubeCRD }>({});
const [crInstancesList, setCRInstancesList] = useState<KubeObject[]>([]);

useEffect(() => {
const fetchCRs = async () => {
const allCrs: KubeCRD[] = [];
const newCrDictionary = new Map<string, KubeCRD>();
const crInstances = { ...crInstancesCache.current };
crToCRDs.current = {};

const promises = (crds ?? []).map(async crd => {
// If we have already the CR instances for this CRD, skip fetching them.
if (
crInstances[crd.metadata.name]?.crd.metadata.resourceVersion ===
crd.metadata.resourceVersion
) {
return;
}

for (const crd of crds) {
const crClass = crd.makeCRClass();
const [crItems, crError] = await new Promise<[KubeCRD[] | null, any | null]>(resolve => {
crClass.apiList(
Expand All @@ -36,29 +45,52 @@ export function CrInstanceList() {

if (crError) {
console.error('Error fetching CRs:', crError);
continue;
return;
}

if (crItems && crItems.length > 0) {
allCrs.push(...crItems);
for (const item of crItems) {
newCrDictionary.set(item.metadata.name, crd);
}
crInstances[crd.metadata.name] = {
instances: crItems ?? [],
crd,
};
});

// Wait for all promises to resolve
await Promise.all(promises);

// Remove any CRDs not in the list of CRDs
for (const crdName of Object.keys(crInstances)) {
if (!crds?.find(crd => crd.metadata.name === crdName)) {
delete crInstances[crdName];
}
}

setState({
crList: allCrs,
loading: false,
crDictionary: newCrDictionary,
});
const newCRInstanceList: typeof crInstancesList = [];
for (const item of Object.values(crInstances)) {
for (const instance of item.instances) {
newCRInstanceList.push(instance);
crToCRDs.current[instance.metadata.uid] = item.crd;
}
}

setCRInstancesList(newCRInstanceList);
crInstancesCache.current = crInstances;
setIsLoading(false);
};

if (!!crdsError) {
return;
}

if (crds) {
setIsLoading(true);
fetchCRs();
}
}, [crds]);

function getCRDForCR(cr: KubeObject): KubeCRD {
return crToCRDs.current[cr.metadata.uid];
}

if (crdsError) {
return (
<Empty color="error">
Expand All @@ -69,11 +101,11 @@ export function CrInstanceList() {
);
}

if (state.loading) {
if (isLoading) {
return <Loader title={t('translation|Loading custom resource instances')} />;
}

if (state.crList.length === 0) {
if (crInstancesList.length === 0) {
return <Empty>{t('translation|No custom resources found.')}</Empty>;
}

Expand All @@ -84,7 +116,7 @@ export function CrInstanceList() {
headerProps={{
noNamespaceFilter: false,
}}
data={state.crList}
data={crInstancesList}
columns={[
{
label: 'Instance name',
Expand All @@ -97,8 +129,8 @@ export function CrInstanceList() {
routeName="customresource"
params={{
crName: cr.metadata.name,
crd: `${state.crDictionary.get(cr.metadata.name)?.metadata.name}`,
namespace: cr.metadata.namespace || '-',
crd: getCRDForCR(cr).metadata.name,
namespace: cr.metadata.namespace ?? '-',
}}
>
{cr.metadata.name} {/*crd.metadata.name*/}
Expand All @@ -116,7 +148,7 @@ export function CrInstanceList() {
<Link
routeName="crd"
params={{
name: `${state.crDictionary.get(cr.metadata.name)?.metadata.name}`,
name: getCRDForCR(cr).metadata.name,
}}
>
{cr.kind} {/*crd.metadata.name*/}
Expand All @@ -127,13 +159,11 @@ export function CrInstanceList() {
{
label: 'Categories',
getValue: cr => {
const categories = state.crDictionary.get(cr.metadata.name)?.jsonData!.status
.acceptedNames.categories;
const categories = getCRDForCR(cr).jsonData!.status.acceptedNames.categories;
return categories !== undefined ? categories.toString().split(',').join(', ') : '';
},
},
'namespace',

'age',
]}
/>
Expand Down

0 comments on commit 21e0883

Please sign in to comment.