From 85bdc1ff61c2429ebdd30e5ba486f4a05f3597ff Mon Sep 17 00:00:00 2001 From: Joaquim Rocha Date: Thu, 30 Mar 2023 01:34:22 +0100 Subject: [PATCH] frontend: Add support for multiple clusters to the VersionButton --- .../src/components/Sidebar/VersionButton.tsx | 148 ++++++++++++++---- frontend/src/lib/k8s/index.ts | 12 +- 2 files changed, 126 insertions(+), 34 deletions(-) diff --git a/frontend/src/components/Sidebar/VersionButton.tsx b/frontend/src/components/Sidebar/VersionButton.tsx index f691f01cb6..5a812b61a7 100644 --- a/frontend/src/components/Sidebar/VersionButton.tsx +++ b/frontend/src/components/Sidebar/VersionButton.tsx @@ -10,9 +10,11 @@ import { useSnackbar } from 'notistack'; import React from 'react'; import { useTranslation } from 'react-i18next'; import semver from 'semver'; -import { getVersion, useCluster } from '../../lib/k8s'; +import { getVersion, getVersionForCluster, useCluster } from '../../lib/k8s'; import { StringDict } from '../../lib/k8s/cluster'; +import { getClusterGroup } from '../../lib/util'; import { useTypedSelector } from '../../redux/reducers/reducers'; +import { Tabs } from '../common'; import { NameValueTable } from '../common/SimpleTable'; const versionSnackbarHideTimeout = 5000; // ms @@ -35,12 +37,22 @@ export default function VersionButton() { const sidebar = useTypedSelector(state => state.ui.sidebar); const { enqueueSnackbar } = useSnackbar(); const classes = useVersionButtonStyle(); - const [clusterVersion, setClusterVersion] = React.useState(null); + const [clusterVersions, setClusterVersions] = React.useState<{ + [key: string]: StringDict | null; + }>({}); const cluster = useCluster(); const [open, setOpen] = React.useState(false); const { t } = useTranslation('glossary'); + const clusters = React.useMemo(() => { + return getClusterGroup(); + }, [cluster]); - function getVersionRows() { + function getVersionRows(clusterName: string) { + if (!Object.values(clusterVersions).length) { + return []; + } + + const clusterVersion = clusterVersions[clusterName]; if (!clusterVersion) { return []; } @@ -73,42 +85,70 @@ export default function VersionButton() { () => { let stillAlive = true; function fetchVersion() { - getVersion() - .then((results: StringDict) => { + Promise.allSettled(clusters.map(cluster => getVersionForCluster(cluster || ''))) + .then(results => { if (!stillAlive) { return; } + const newVersions: typeof clusterVersions = {}; + for (const result of results) { + const { status } = result; - setClusterVersion(results); - let versionChange = 0; - if (clusterVersion && results && results.gitVersion) { - versionChange = semver.compare(results.gitVersion, clusterVersion.gitVersion); - - let msg = ''; - if (versionChange > 0) { - msg = t('cluster|Cluster version upgraded to {{ gitVersion }}', { - gitVersion: results.gitVersion, - }); - } else if (versionChange < 0) { - msg = t('cluster|Cluster version downgraded to {{ gitVersion }}', { - gitVersion: results.gitVersion, - }); + if (status === 'rejected') { + console.error( + 'Getting the version for a cluster:', + (result as PromiseRejectedResult).reason + ); + continue; } - if (msg) { - enqueueSnackbar(msg, { - key: 'version', - preventDuplicate: true, - autoHideDuration: versionSnackbarHideTimeout, - variant: 'info', - }); + const [cluster, clusterVersion] = ( + result as PromiseFulfilledResult<[string, StringDict]> + ).value; + + newVersions[cluster] = clusterVersion; + let versionChange = 0; + + if (clusterVersion && clusterVersion && clusterVersion.gitVersion) { + versionChange = semver.compare( + clusterVersion.gitVersion, + clusterVersion.gitVersion + ); + + let msg = ''; + if (versionChange > 0) { + msg = t('cluster|Cluster version upgraded to {{ gitVersion }}', { + gitVersion: clusterVersion.gitVersion, + }); + } else if (versionChange < 0) { + msg = t('cluster|Cluster version downgraded to {{ gitVersion }}', { + gitVersion: clusterVersion.gitVersion, + }); + } + + if (msg) { + enqueueSnackbar(msg, { + key: 'version', + preventDuplicate: true, + autoHideDuration: versionSnackbarHideTimeout, + variant: 'info', + }); + } } } + + setClusterVersions(newVersions); }) .catch((error: Error) => console.error('Getting the cluster version:', error)); + + for (const cluster of []) { + getVersion(cluster) + .then() + .catch((error: Error) => console.error('Getting the cluster version:', error)); + } } - if (!clusterVersion) { + if (Object.keys(clusterVersions).length === 0) { fetchVersion(); } @@ -122,34 +162,78 @@ export default function VersionButton() { }; }, // eslint-disable-next-line - [clusterVersion] + [clusterVersions] ); // Use the location to make sure the version is changed, as it depends on the cluster // (defined in the URL ATM). // @todo: Update this if the active cluster management is changed. React.useEffect(() => { - setClusterVersion(null); + setClusterVersions(versions => { + if (!cluster || !versions[cluster]) { + return {}; + } + return versions; + }); }, [cluster]); function handleClose() { setOpen(false); } - return !clusterVersion ? null : ( + const clusterVersionText = React.useMemo(() => { + let versionText: string[] = []; + // We only up to two versions (if they are different). If more + // than 2 different versions exist, then we show the 1st one + ... . + for (const versionInfo of Object.values(clusterVersions)) { + if (versionText.length > 2) { + break; + } + + if (versionText.length === 0 && !!versionInfo?.gitVersion) { + versionText.push(versionInfo.gitVersion); + } else if (versionText[0] === (versionInfo?.gitVersion || '')) { + // If it's the same version, we just check the next cluster's. + continue; + } else if (!!versionInfo?.gitVersion) { + versionText.push(versionInfo.gitVersion); + } + } + + if (versionText.length > 2) { + versionText = [versionText[0], '...']; + } + + return versionText.join('+'); + }, [clusterVersions]); + + return Object.keys(clusterVersions).length === 0 ? null : ( {t('Kubernetes Version')} - + {Object.keys(clusterVersions).length === 1 ? ( + + ) : ( + ({ + label: clusterName, + component: ( + + + + ), + }))} + /> + )}