Skip to content

Commit 55a174d

Browse files
authored
fix: prevent UI crash through safe error rendering and optional chaining (#4626)
1 parent 5c71173 commit 55a174d

File tree

6 files changed

+35
-32
lines changed

6 files changed

+35
-32
lines changed

src/components/Nodes/MachineInfo/MachineInfo.jsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { EMPTY_TEXT_PLACEHOLDER } from 'shared/constants';
55
import './MachineInfo.scss';
66

77
export function MachineInfo({ nodeInfo, capacity, addresses, spec, gpus }) {
8-
const formattedMemory =
9-
Math.round((parseInt(capacity.memory) / 1024 / 1024) * 10) / 10;
8+
const formattedMemory = capacity?.memory
9+
? Math.round((parseInt(capacity.memory) / 1024 / 1024) * 10) / 10
10+
: 0;
1011
const { t } = useTranslation();
1112

1213
return (
@@ -19,22 +20,22 @@ export function MachineInfo({ nodeInfo, capacity, addresses, spec, gpus }) {
1920
<DynamicPageComponent.Column
2021
title={t('node-details.machine-info.operating-system')}
2122
>
22-
{`${nodeInfo.operatingSystem} (${nodeInfo.osImage})`}
23+
{`${nodeInfo?.operatingSystem} (${nodeInfo?.osImage})`}
2324
</DynamicPageComponent.Column>
2425
<DynamicPageComponent.Column
2526
title={t('node-details.machine-info.provider')}
2627
>
27-
{spec.providerID}
28+
{spec?.providerID || EMPTY_TEXT_PLACEHOLDER}
2829
</DynamicPageComponent.Column>
2930
<DynamicPageComponent.Column
3031
title={t('node-details.machine-info.architecture')}
3132
>
32-
{nodeInfo.architecture}
33+
{nodeInfo?.architecture || EMPTY_TEXT_PLACEHOLDER}
3334
</DynamicPageComponent.Column>
3435
<DynamicPageComponent.Column
3536
title={t('node-details.machine-info.cpus')}
3637
>
37-
{capacity.cpu}
38+
{capacity?.cpu || EMPTY_TEXT_PLACEHOLDER}
3839
</DynamicPageComponent.Column>
3940
{gpus > 0 && (
4041
<DynamicPageComponent.Column
@@ -51,26 +52,26 @@ export function MachineInfo({ nodeInfo, capacity, addresses, spec, gpus }) {
5152
<DynamicPageComponent.Column
5253
title={t('node-details.machine-info.pods-capacity')}
5354
>
54-
{capacity.pods}
55+
{capacity?.pods || EMPTY_TEXT_PLACEHOLDER}
5556
</DynamicPageComponent.Column>
5657
<DynamicPageComponent.Column title={t('node-details.pod-cidr')}>
57-
{spec.podCIDRs.join(',')}
58+
{spec?.podCIDRs?.join(',') || EMPTY_TEXT_PLACEHOLDER}
5859
</DynamicPageComponent.Column>
5960
<DynamicPageComponent.Column
6061
title={t('node-details.machine-info.kubelet-version')}
6162
>
62-
{nodeInfo.kubeletVersion}
63+
{nodeInfo?.kubeletVersion || EMPTY_TEXT_PLACEHOLDER}
6364
</DynamicPageComponent.Column>
6465
<DynamicPageComponent.Column
6566
title={t('node-details.machine-info.internal-ip')}
6667
>
67-
{addresses.find((a) => a.type === 'InternalIP')?.address ||
68+
{addresses?.find((a) => a.type === 'InternalIP')?.address ||
6869
EMPTY_TEXT_PLACEHOLDER}
6970
</DynamicPageComponent.Column>
7071
<DynamicPageComponent.Column
7172
title={t('node-details.machine-info.hostname')}
7273
>
73-
{addresses.find((a) => a.type === 'Hostname').address ||
74+
{addresses?.find((a) => a.type === 'Hostname')?.address ||
7475
EMPTY_TEXT_PLACEHOLDER}
7576
</DynamicPageComponent.Column>
7677
</>

src/components/Nodes/NodeDetails/NodeDetails.jsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default function NodeDetails({ nodeName }) {
4545
}, [nodeName]);
4646

4747
if (loading) return <Spinner />;
48-
if (error) return <Text>{error}</Text>;
48+
if (error) return <Text>{error?.message || String(error)}</Text>;
4949

5050
const gpus = node ? getAvailableNvidiaGPUs([node]) : 0;
5151

@@ -57,7 +57,7 @@ export default function NodeDetails({ nodeName }) {
5757
<Spinner key="node-resources" />
5858
) : (
5959
<div className="flexwrap" key="node-resources">
60-
<NodeResources metrics={data.metrics} resources={resources} />
60+
<NodeResources metrics={data?.metrics} resources={resources} />
6161
</div>
6262
),
6363
() => (
@@ -72,20 +72,21 @@ export default function NodeDetails({ nodeName }) {
7272
const customColumns = [
7373
{
7474
header: t('node-details.region'),
75-
value: (node) => node.metadata?.labels?.['topology.kubernetes.io/region'],
75+
value: (node) =>
76+
node?.metadata?.labels?.['topology.kubernetes.io/region'],
7677
},
7778
{
7879
header: t('node-details.zone'),
79-
value: (node) => node.metadata?.labels?.['topology.kubernetes.io/zone'],
80+
value: (node) => node?.metadata?.labels?.['topology.kubernetes.io/zone'],
8081
},
8182
{
8283
header: t('node-details.pool'),
83-
value: (node) => node.metadata?.labels?.['worker.gardener.cloud/pool'],
84+
value: (node) => node?.metadata?.labels?.['worker.gardener.cloud/pool'],
8485
},
8586
{
8687
header: t('node-details.machine-type'),
8788
value: (node) =>
88-
node.metadata?.labels?.['node.kubernetes.io/instance-type'],
89+
node?.metadata?.labels?.['node.kubernetes.io/instance-type'],
8990
},
9091
];
9192

@@ -107,9 +108,9 @@ export default function NodeDetails({ nodeName }) {
107108
resource={node}
108109
customStatus={
109110
<MachineInfo
110-
nodeInfo={node?.status.nodeInfo}
111-
capacity={node?.status.capacity}
112-
addresses={node?.status.addresses}
111+
nodeInfo={node?.status?.nodeInfo}
112+
capacity={node?.status?.capacity}
113+
addresses={node?.status?.addresses}
113114
gpus={gpus}
114115
spec={node?.spec}
115116
/>

src/components/Nodes/NodeResources/NodeResources.jsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,13 @@ export function NodeResources({ metrics, resources }) {
6464
<UI5RadialChart
6565
color="var(--sapChart_OrderedColor_6)"
6666
value={
67-
bytesToHumanReadable(resources.requests?.memory, { unit: 'Gi' }).value
67+
bytesToHumanReadable(resources?.requests?.memory, { unit: 'Gi' })
68+
.value
6869
}
6970
max={bytesToHumanReadable(memory.capacity, { unit: 'Gi' }).value}
7071
titleText={t('cluster-overview.statistics.memory-requests')}
7172
additionalInfo={`${
72-
bytesToHumanReadable(resources.requests?.memory, { unit: 'Gi' })
73+
bytesToHumanReadable(resources?.requests?.memory, { unit: 'Gi' })
7374
.string
7475
} / ${bytesToHumanReadable(memory.capacity, { unit: 'Gi' }).string}`}
7576
accessibleName={t('cluster-overview.statistics.memory-requests')}
@@ -101,12 +102,12 @@ export function NodeResources({ metrics, resources }) {
101102
<UI5RadialChart
102103
color="var(--sapChart_OrderedColor_6)"
103104
value={
104-
bytesToHumanReadable(resources.limits.memory, { unit: 'Gi' }).value
105+
bytesToHumanReadable(resources?.limits?.memory, { unit: 'Gi' }).value
105106
}
106107
max={bytesToHumanReadable(memory.capacity, { unit: 'Gi' }).value}
107108
titleText={t('cluster-overview.statistics.memory-limits')}
108109
additionalInfo={`${
109-
bytesToHumanReadable(resources.limits.memory, { unit: 'Gi' }).string
110+
bytesToHumanReadable(resources?.limits?.memory, { unit: 'Gi' }).string
110111
} / ${bytesToHumanReadable(memory.capacity, { unit: 'Gi' }).string}`}
111112
accessibleName={t('cluster-overview.statistics.memory-limits')}
112113
/>

src/components/Nodes/nodeQueries.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ const getPercentageFromUsage = (value, total) => {
1010
};
1111

1212
export const createUsageMetrics = (node, metricsForNode) => {
13-
const cpuUsage = getCpus(metricsForNode?.usage.cpu);
14-
const memoryUsage = getBytes(metricsForNode?.usage.memory);
15-
const cpuCapacity = getCpus(node.status.allocatable?.cpu || '0');
16-
const memoryCapacity = getBytes(node.status.allocatable?.memory);
13+
const cpuUsage = getCpus(metricsForNode?.usage?.cpu);
14+
const memoryUsage = getBytes(metricsForNode?.usage?.memory);
15+
const cpuCapacity = getCpus(node?.status?.allocatable?.cpu);
16+
const memoryCapacity = getBytes(node?.status?.allocatable?.memory);
1717

1818
const cpuPercentage = getPercentageFromUsage(cpuUsage, cpuCapacity);
1919
const memoryPercentage = getPercentageFromUsage(memoryUsage, memoryCapacity);
@@ -53,8 +53,8 @@ export function useNodesQuery(skip = false) {
5353
const data = useMemo(() => {
5454
if (nodes) {
5555
const getNodeMetrics = (node) => {
56-
const metricsForNode = nodeMetrics.items.find(
57-
(metrics) => node.metadata.name === metrics.metadata.name,
56+
const metricsForNode = nodeMetrics?.items?.find(
57+
(metrics) => node?.metadata?.name === metrics?.metadata?.name,
5858
);
5959
return createUsageMetrics(node, metricsForNode);
6060
};

src/shared/components/K8sResourceSelect.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function K8sResourceSelect({
5959
const getValidationState = () => {
6060
if (error) {
6161
return {
62-
state: 'Error',
62+
state: 'Negative',
6363
text: t('common.messages.cannot-load', {
6464
value: pluralResourceType,
6565
error: error.message,

src/shared/hooks/BackendAPI/useGet.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ function handleSingleDataReceived(
402402
) {
403403
if (
404404
!oldData || // current data is empty and we received some. There's no doubdt we should update.
405-
newData.metadata.resourceVersion !== oldData.metadata?.resourceVersion ||
405+
newData?.metadata?.resourceVersion !== oldData?.metadata?.resourceVersion ||
406406
(compareEntireResource &&
407407
JSON.stringify(newData) !== JSON.stringify(oldData))
408408
) {

0 commit comments

Comments
 (0)