From ced7c370d562e69fb3544c7702e0909039456d04 Mon Sep 17 00:00:00 2001 From: Evangelos Skopelitis Date: Fri, 21 Mar 2025 12:56:08 -0400 Subject: [PATCH] frontend: pod: Add quick status for containers This change adds a new icon displaying the status of each container in the pods list. Hovering over the icon gives important info about the container at a glance: name, status, reason, exit code, and start/finish times. Fixes: #2689 Signed-off-by: Evangelos Skopelitis --- frontend/src/components/pod/Details.tsx | 2 +- frontend/src/components/pod/List.tsx | 90 +++++++-- .../PodDetails.Error.stories.storyshot | 20 +- .../PodDetails.Initializing.stories.storyshot | 20 +- ...odDetails.LivenessFailed.stories.storyshot | 20 +- .../PodDetails.PullBackOff.stories.storyshot | 20 +- .../PodDetails.Running.stories.storyshot | 20 +- .../PodDetails.Successful.stories.storyshot | 20 +- .../PodList.Items.stories.storyshot | 184 ++++++++++++------ 9 files changed, 272 insertions(+), 124 deletions(-) diff --git a/frontend/src/components/pod/Details.tsx b/frontend/src/components/pod/Details.tsx index bb9ba404fbf..c5db3c5b8c8 100644 --- a/frontend/src/components/pod/Details.tsx +++ b/frontend/src/components/pod/Details.tsx @@ -472,7 +472,7 @@ export default function PodDetails(props: PodDetailsProps) { item && [ { name: t('State'), - value: makePodStatusLabel(item), + value: makePodStatusLabel(item, false), }, { name: t('Node'), diff --git a/frontend/src/components/pod/List.tsx b/frontend/src/components/pod/List.tsx index 4a3a5e93a4f..5abfe1b0c9b 100644 --- a/frontend/src/components/pod/List.tsx +++ b/frontend/src/components/pod/List.tsx @@ -3,6 +3,7 @@ import { Box } from '@mui/material'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { ApiError } from '../../lib/k8s/apiProxy'; +import { KubeContainerStatus } from '../../lib/k8s/cluster'; import Pod from '../../lib/k8s/pod'; import { METRIC_REFETCH_INTERVAL_MS, PodMetrics } from '../../lib/k8s/PodMetrics'; import { parseCpu, parseRam, unparseCpu, unparseRam } from '../../lib/units'; @@ -13,7 +14,7 @@ import { LightTooltip, Link, SimpleTableProps } from '../common'; import { StatusLabel, StatusLabelProps } from '../common/Label'; import ResourceListView from '../common/Resource/ResourceListView'; -export function makePodStatusLabel(pod: Pod) { +export function makePodStatusLabel(pod: Pod, showContainerStatus: boolean = true) { const phase = pod.status.phase; let status: StatusLabelProps['status'] = ''; @@ -30,17 +31,34 @@ export function makePodStatusLabel(pod: Pod) { } } + const containerStatuses = pod.status?.containerStatuses || []; + const containerIndicators = containerStatuses.map((cs, index) => { + const { color, tooltip } = getContainerDisplayStatus(cs); + return ( + + + + ); + }); + return ( - - - - {reason} - {(status === 'warning' || status === 'error') && ( - - )} - - - + + + + + {reason} + {(status === 'warning' || status === 'error') && ( + + )} + + + + {showContainerStatus && containerIndicators.length > 0 && ( + + {containerIndicators} + + )} + ); } @@ -60,6 +78,56 @@ function getReadinessGatesStatus(pods: Pod) { return readinessGatesMap; } +function getContainerDisplayStatus(container: KubeContainerStatus) { + const state = container.state || {}; + let color = 'grey'; + let label = ''; + const tooltipLines: string[] = [`Name: ${container.name}`]; + + if (state.waiting) { + color = 'orange'; + label = 'Waiting'; + if (state.waiting.reason) { + tooltipLines.push(`Reason: ${state.waiting.reason}`); + } + } else if (state.terminated) { + color = 'green'; + label = 'Terminated'; + if (state.terminated.reason) { + tooltipLines.push(`Reason: ${state.terminated.reason}`); + } + if (state.terminated.exitCode !== undefined) { + tooltipLines.push(`Exit Code: ${state.terminated.exitCode}`); + } + if (state.terminated.startedAt) { + tooltipLines.push(`Started: ${new Date(state.terminated.startedAt).toLocaleString()}`); + } + if (state.terminated.finishedAt) { + tooltipLines.push(`Finished: ${new Date(state.terminated.finishedAt).toLocaleString()}`); + } + if (container.restartCount > 0) { + tooltipLines.push(`Restarts: ${container.restartCount}`); + } + } else if (state.running) { + color = 'green'; + label = 'Running'; + if (state.running.startedAt) { + tooltipLines.push(`Started: ${new Date(state.running.startedAt).toLocaleString()}`); + } + if (container.restartCount > 0) { + tooltipLines.push(`Restarts: ${container.restartCount}`); + } + } + + tooltipLines.splice(1, 0, `Status: ${label}`); + + return { + color, + label, + tooltip: {tooltipLines.join('\n')}, + }; +} + export interface PodListProps { pods: Pod[] | null; metrics: PodMetrics[] | null; diff --git a/frontend/src/components/pod/__snapshots__/PodDetails.Error.stories.storyshot b/frontend/src/components/pod/__snapshots__/PodDetails.Error.stories.storyshot index 2a7df5c86c9..d315e7f86a0 100644 --- a/frontend/src/components/pod/__snapshots__/PodDetails.Error.stories.storyshot +++ b/frontend/src/components/pod/__snapshots__/PodDetails.Error.stories.storyshot @@ -219,16 +219,20 @@ class="MuiGrid-root MuiGrid-item MuiGrid-grid-xs-12 MuiGrid-grid-sm-8 css-deb4a-MuiGrid-root" >
- - Error - + + Error + +
- - Init:0/2 - + + Init:0/2 + +
- - CrashLoopBackOff - + + CrashLoopBackOff + +
- - ImagePullBackOff - + + ImagePullBackOff + +
- - Running - + + Running + +
- - Completed - + + Completed + +
- - ImagePullBackOff - + + ImagePullBackOff + +
+
- - Completed - + + Completed + +
+
- - Init:0/2 - + + Init:0/2 + +
+
- - CrashLoopBackOff - + + CrashLoopBackOff + +
+
- - Error - + + Error + +
+
- - Running - + + Running + +
+
- - Running - + + Running + +
+
- - Running - + + Running + +
+