Skip to content

Commit bd7b6ab

Browse files
authored
Merge pull request #4556 from mastermaxx03/fix/init-container-clean
frontend: Default to init container if main container not yet running
2 parents 29aaf2a + a4c14ad commit bd7b6ab

File tree

3 files changed

+61
-12
lines changed

3 files changed

+61
-12
lines changed

frontend/src/components/common/Terminal.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { Terminal as XTerminal } from '@xterm/xterm';
2727
import _ from 'lodash';
2828
import React, { useState } from 'react';
2929
import { useTranslation } from 'react-i18next';
30+
import { getDefaultContainer } from '../../helpers/podContainer';
3031
import Pod from '../../lib/k8s/pod';
3132
import { Dialog } from './Dialog';
3233

@@ -60,7 +61,7 @@ type execReturn = ReturnType<Pod['exec']>;
6061
export default function Terminal(props: TerminalProps) {
6162
const { item, onClose, isAttach, noDialog, ...other } = props;
6263
const [terminalContainerRef, setTerminalContainerRef] = React.useState<HTMLElement | null>(null);
63-
const [container, setContainer] = useState<string | null>(getDefaultContainer());
64+
const [container, setContainer] = useState<string | null>(() => getDefaultContainer(item));
6465
const execOrAttachRef = React.useRef<execReturn | null>(null);
6566
const fitAddonRef = React.useRef<FitAddon | null>(null);
6667
const xtermRef = React.useRef<XTerminalConnected | null>(null);
@@ -70,10 +71,6 @@ export default function Terminal(props: TerminalProps) {
7071
});
7172
const { t } = useTranslation(['translation', 'glossary']);
7273

73-
function getDefaultContainer() {
74-
return item.spec.containers.length > 0 ? item.spec.containers[0].name : '';
75-
}
76-
7774
// @todo: Give the real exec type when we have it.
7875
function setupTerminal(containerRef: HTMLElement, xterm: XTerminal, fitAddon: FitAddon) {
7976
if (!containerRef) {
@@ -338,7 +335,7 @@ export default function Terminal(props: TerminalProps) {
338335
React.useEffect(
339336
() => {
340337
if (props.open && container === null) {
341-
setContainer(getDefaultContainer());
338+
setContainer(getDefaultContainer(item));
342339
}
343340
},
344341
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -430,7 +427,7 @@ export default function Terminal(props: TerminalProps) {
430427
<Select
431428
labelId="container-name-chooser-label"
432429
id="container-name-chooser"
433-
value={container !== null ? container : getDefaultContainer()}
430+
value={container !== null ? container : getDefaultContainer(item)}
434431
onChange={handleContainerChange}
435432
>
436433
{item?.spec?.containers && (

frontend/src/components/pod/Details.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import _ from 'lodash';
2727
import React from 'react';
2828
import { useTranslation } from 'react-i18next';
2929
import { useLocation, useParams } from 'react-router-dom';
30+
import { getDefaultContainer } from '../../helpers/podContainer';
3031
import { KubeContainerStatus } from '../../lib/k8s/cluster';
3132
import Pod from '../../lib/k8s/pod';
3233
import { DefaultHeaderAction } from '../../redux/actionButtonsSlice';
@@ -50,6 +51,7 @@ import { useLocalStorageState } from '../globalSearch/useLocalStorageState';
5051
import { colorizePrettifiedLog } from './jsonHandling';
5152
import { makePodStatusLabel } from './List';
5253
import { PodDebugAction } from './PodDebugAction';
54+
5355
const PaddedFormControlLabel = styled(FormControlLabel)(({ theme }) => ({
5456
margin: 0,
5557
paddingTop: theme.spacing(2),
@@ -62,7 +64,7 @@ interface PodLogViewerProps extends Omit<LogViewerProps, 'logs'> {
6264

6365
export function PodLogViewer(props: PodLogViewerProps) {
6466
const { item, onClose, open, ...other } = props;
65-
const [container, setContainer] = React.useState(getDefaultContainer());
67+
const [container, setContainer] = React.useState(() => getDefaultContainer(item));
6668
const [showPrevious, setShowPrevious] = React.useState<boolean>(false);
6769
const [showTimestamps, setShowTimestamps] = useLocalStorageState<boolean>(
6870
'headlamp.logs.showTimestamps',
@@ -82,10 +84,6 @@ export function PodLogViewer(props: PodLogViewerProps) {
8284
const xtermRef = React.useRef<XTerminal | null>(null);
8385
const { t } = useTranslation();
8486

85-
function getDefaultContainer() {
86-
return item.spec.containers.length > 0 ? item.spec.containers[0].name : '';
87-
}
88-
8987
const options = { leading: true, trailing: true, maxWait: 1000 };
9088

9189
function setLogsDebounced({
@@ -136,6 +134,12 @@ export function PodLogViewer(props: PodLogViewerProps) {
136134
}
137135

138136
const debouncedSetState = _.debounce(setLogsDebounced, 500, options);
137+
React.useEffect(() => {
138+
const next = getDefaultContainer(item);
139+
if (next && !container) {
140+
setContainer(next);
141+
}
142+
}, [item?.status]);
139143

140144
React.useEffect(
141145
() => {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2025 The Kubernetes Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import Pod from '../lib/k8s/pod';
18+
19+
/**
20+
* Gets the default container name for a pod based on its current execution state.
21+
*
22+
* It prioritizes running main containers, then running init containers,
23+
* falling back to the first container in the spec if none are running.
24+
*
25+
* @param item - The pod object to check.
26+
* @returns The name of the default container or an empty string if none exist.
27+
*/
28+
29+
export function getDefaultContainer(item: Pod): string {
30+
if (!item) {
31+
return '';
32+
}
33+
34+
// Prefer a running main container
35+
const runningMain = item.status?.containerStatuses?.find(s => s.state?.running);
36+
if (runningMain) {
37+
return runningMain.name;
38+
}
39+
40+
// Otherwise use running init container
41+
const runningInit = item.status?.initContainerStatuses?.find(s => s.state?.running);
42+
if (runningInit) {
43+
return runningInit.name;
44+
}
45+
46+
// Fallback to first main container
47+
return item.spec?.containers?.[0]?.name ?? '';
48+
}

0 commit comments

Comments
 (0)