From a78ec3bd8aa8a443969317bcb2b65d9d5d3bb862 Mon Sep 17 00:00:00 2001 From: farodin91 Date: Wed, 4 Dec 2024 20:34:06 +0100 Subject: [PATCH] frontend: fix nullable node status Signed-off-by: farodin91 --- frontend/src/components/cluster/Charts.tsx | 4 +- .../Overview.Events.stories.storyshot | 2 +- frontend/src/components/node/Details.tsx | 20 ++-- frontend/src/components/node/List.stories.tsx | 18 +++ frontend/src/components/node/List.tsx | 9 +- .../List.Nodes.stories.storyshot | 110 +++++++++++++++++- frontend/src/components/node/storyHelper.ts | 19 +++ frontend/src/lib/k8s/node.ts | 14 +-- frontend/src/lib/util.ts | 3 + 9 files changed, 176 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/cluster/Charts.tsx b/frontend/src/components/cluster/Charts.tsx index e455ff41f2..c10f5c6a76 100644 --- a/frontend/src/components/cluster/Charts.tsx +++ b/frontend/src/components/cluster/Charts.tsx @@ -19,7 +19,7 @@ export function MemoryCircularChart(props: ResourceCircularChartProps) { } function memoryAvailableGetter(item: Node | Pod) { - return parseRam(item.status!.capacity.memory) / TO_GB; + return parseRam(item.status?.capacity?.memory) / TO_GB; } function getLegend(used: number, available: number) { @@ -55,7 +55,7 @@ export function CpuCircularChart(props: ResourceCircularChartProps) { } function cpuAvailableGetter(item: Node | Pod) { - return parseCpu(item.status!.capacity.cpu) / TO_ONE_CPU; + return parseCpu(item.status?.capacity?.cpu) / TO_ONE_CPU; } function getLegend(used: number, available: number) { diff --git a/frontend/src/components/cluster/__snapshots__/Overview.Events.stories.storyshot b/frontend/src/components/cluster/__snapshots__/Overview.Events.stories.storyshot index 19208247dd..f412efa940 100644 --- a/frontend/src/components/cluster/__snapshots__/Overview.Events.stories.storyshot +++ b/frontend/src/components/cluster/__snapshots__/Overview.Events.stories.storyshot @@ -371,7 +371,7 @@

- 0 / 1 Ready + 0 / 2 Ready

{ - return { - name: type, - value: address, - }; - }); + return ( + item.status.addresses?.map(({ type, address }) => { + return { + name: type, + value: address, + }; + }) || [] + ); } function handleNodeScheduleState(node: Node, cordon: boolean) { @@ -283,7 +285,7 @@ function ChartsSection(props: ChartsSectionProps) { return '…'; } - const readyInfo = node.status.conditions.find(({ type }) => type === 'Ready'); + const readyInfo = node.status.conditions?.find(({ type }) => type === 'Ready'); if (readyInfo) { return timeAgo(readyInfo.lastTransitionTime as string); } @@ -352,7 +354,7 @@ function SystemInfoSection(props: SystemInfoSectionProps) { ); } - if (!node) { + if (!node || !node.status.nodeInfo) { return null; } @@ -412,7 +414,7 @@ interface NodeReadyLabelProps { export function NodeReadyLabel(props: NodeReadyLabelProps) { const { node } = props; - const isReady = !!node.status.conditions.find( + const isReady = !!node.status.conditions?.find( condition => condition.type === 'Ready' && condition.status === 'True' ); const { t } = useTranslation(); diff --git a/frontend/src/components/node/List.stories.tsx b/frontend/src/components/node/List.stories.tsx index 5eecb224c0..087273585f 100644 --- a/frontend/src/components/node/List.stories.tsx +++ b/frontend/src/components/node/List.stories.tsx @@ -1,7 +1,9 @@ import Container from '@mui/material/Container'; import { Meta, StoryFn } from '@storybook/react'; +import { http, HttpResponse } from 'msw'; import { TestContext } from '../../test'; import List from './List'; +import { NODE_DUMMY_DATA } from './storyHelper'; export default { title: 'node/List', @@ -16,6 +18,22 @@ export default { ); }, ], + parameters: { + msw: { + handlers: { + story: [ + http.get('http://localhost:4466/api/v1/nodes', () => + HttpResponse.json({ + kind: 'NodeList', + apiVersion: 'v1', + metadata: {}, + items: NODE_DUMMY_DATA, + }) + ), + ], + }, + }, + }, } as Meta; const Template: StoryFn = () => { diff --git a/frontend/src/components/node/List.tsx b/frontend/src/components/node/List.tsx index 8a0be733f7..c85e98113b 100644 --- a/frontend/src/components/node/List.tsx +++ b/frontend/src/components/node/List.tsx @@ -60,7 +60,7 @@ export default function NodeList() { label: t('translation|Ready'), gridTemplate: 'minmax(150px, .3fr)', getValue: node => { - const isReady = !!node.status.conditions.find( + const isReady = !!node.status.conditions?.find( condition => condition.type === 'Ready' && condition.status === 'True' ); return isReady ? t('translation|Yes') : t('translation|No'); @@ -99,14 +99,17 @@ export default function NodeList() { id: 'version', label: t('translation|Version'), gridTemplate: 'minmax(150px, .5fr)', - getValue: node => node.status.nodeInfo.kubeletVersion, + getValue: node => node.status.nodeInfo?.kubeletVersion, }, { id: 'software', label: t('translation|Software'), gridTemplate: 'minmax(200px, 1.5fr)', - getValue: node => node.status.nodeInfo.operatingSystem, + getValue: node => node.status.nodeInfo?.operatingSystem, render: node => { + if (node.status.nodeInfo === undefined) { + return <>; + } let osIcon = 'mdi:desktop-classic'; if (node.status.nodeInfo.operatingSystem === 'linux') { osIcon = 'mdi:linux'; diff --git a/frontend/src/components/node/__snapshots__/List.Nodes.stories.storyshot b/frontend/src/components/node/__snapshots__/List.Nodes.stories.storyshot index 5d2e4f1406..b405cf9c9e 100644 --- a/frontend/src/components/node/__snapshots__/List.Nodes.stories.storyshot +++ b/frontend/src/components/node/__snapshots__/List.Nodes.stories.storyshot @@ -1096,6 +1096,114 @@ + + + + node-2 + + + +
+ + +
+ + + + No + + + +
+ + + + + + +

+ 3mo +

+ + + + + - 1-1 of 1 + 1-2 of 2
& { + conditions?: (Omit & { lastHeartbeatTime: string; })[]; - nodeInfo: { + nodeInfo?: { architecture: string; bootID: string; containerRuntimeVersion: string; @@ -85,11 +85,11 @@ class Node extends KubeObject { } getExternalIP(): string { - return this.status.addresses.find(address => address.type === 'ExternalIP')?.address || ''; + return this.status.addresses?.find(address => address.type === 'ExternalIP')?.address || ''; } getInternalIP(): string { - return this.status.addresses.find(address => address.type === 'InternalIP')?.address || ''; + return this.status.addresses?.find(address => address.type === 'InternalIP')?.address || ''; } } diff --git a/frontend/src/lib/util.ts b/frontend/src/lib/util.ts index ee504b343b..28f32faaab 100644 --- a/frontend/src/lib/util.ts +++ b/frontend/src/lib/util.ts @@ -134,6 +134,9 @@ export function getResourceMetrics( metrics: KubeMetrics[], resourceType: 'cpu' | 'memory' ) { + if (item.status.capacity === undefined) { + return [0, 0]; + } const resourceParsers: any = { cpu: parseCpu, memory: parseRam,