1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-23 15:29:42 +02:00
portainer/app/react/kubernetes/applications/DetailsView/ApplicationContainersDatatable/computeContainerStatus.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

361 lines
11 KiB
TypeScript
Raw Normal View History

import { ContainerStatus } from 'kubernetes-types/core/v1';
import { ContainerRowData } from './types';
/**
* Compute the status of a container, with translated messages.
*
* The cases are hardcoded, because there is not a single source that enumerates
* all the possible states.
* @param containerName - The name of the container
* @param containerStatuses - The statuses of the container
* @param initContainerStatuses - The statuses of the init container
* @returns The status of the container
*/
export function computeContainerStatus(
containerName: string,
containerStatuses?: ContainerStatus[],
initContainerStatuses?: ContainerStatus[]
): ContainerRowData['status'] {
// Choose the correct status array based on container type
const statuses = [
...(containerStatuses || []),
...(initContainerStatuses || []),
];
const status = statuses?.find((status) => status.name === containerName);
if (!status) {
return {
status: 'Unknown',
type: 'muted',
message: 'Container status information is not available',
};
}
const hasLogs = hasContainerEverStarted(status);
const { state, restartCount = 0 } = status;
// Handle waiting state with more specific reasons
if (state?.waiting) {
const { reason, message } = state.waiting;
if (reason) {
// Return specific waiting reasons that match kubectl output
switch (reason) {
case 'ImagePullBackOff':
case 'ErrImagePull':
case 'ImageInspectError':
case 'ErrImageNeverPull':
return {
status: reason,
type: 'danger',
message:
message ||
'Failed to pull container image. Check image name, registry access, and network connectivity.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'ContainerCreating':
return {
status: reason,
type: 'info',
message:
message ||
'Container is being created. This may take a few moments.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'info')
? restartCount
: undefined,
};
case 'PodInitializing':
return {
status: `Waiting (${reason})`,
type: 'info',
message:
message ||
'Waiting for init containers to complete. Wait a few moments or check the logs of any init containers that failed to complete.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'info')
? restartCount
: undefined,
};
case 'CreateContainerConfigError':
case 'CreateContainerError':
return {
status: reason,
type: 'danger',
message:
message ||
'Failed to create container. Check resource limits, security contexts, and volume mounts.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'InvalidImageName':
return {
status: reason,
type: 'danger',
message:
message ||
'The specified container image name is invalid or malformed.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'CrashLoopBackOff':
return {
status: reason,
type: 'danger',
message: `Container keeps crashing after startup. Check application logs and startup configuration. ${
message ? `Details: '${message}'` : ''
}`,
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'RunContainerError':
return {
status: reason,
type: 'danger',
message:
message ||
'Failed to start container process. Check command, arguments, and environment variables.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'KillContainerError':
return {
status: reason,
type: 'danger',
message:
message ||
'Failed to stop container gracefully. Container may be unresponsive.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'VerifyNonRootError':
return {
status: reason,
type: 'danger',
message:
message ||
'Container is trying to run as root but security policy requires non-root execution.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'ConfigError':
return {
status: reason,
type: 'danger',
message:
message ||
'Container configuration is invalid. Check resource requirements and security settings.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
default:
return {
status: reason,
type: 'muted',
message: message || `Container is waiting: ${reason}`,
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'muted')
? restartCount
: undefined,
};
}
}
return {
status: 'Waiting',
type: 'muted',
message: message || 'Container is waiting to be scheduled or started.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'muted')
? restartCount
: undefined,
};
}
// Handle terminated state
if (state?.terminated) {
const { exitCode = 0, reason, message } = state.terminated;
if (reason) {
switch (reason) {
case 'Error':
return {
status: 'Error',
type: 'danger',
message: `Container exited with code ${exitCode}${
restartCount > 0 ? ` (restarted ${restartCount} times)` : ''
}. ${message || 'Check application logs for error details.'}`,
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'Completed':
return {
status: 'Completed',
type: 'success',
message,
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'success')
? restartCount
: undefined,
};
case 'OOMKilled':
return {
status: 'OOMKilled',
type: 'danger',
message:
message ||
'Container was killed due to out-of-memory. Consider increasing memory limits.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'DeadlineExceeded':
return {
status: 'DeadlineExceeded',
type: 'danger',
message:
message ||
'Container was terminated because it exceeded the active deadline.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
case 'Evicted':
return {
status: 'Evicted',
type: 'warn',
message:
message ||
'Container was evicted due to resource pressure on the node.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'warn')
? restartCount
: undefined,
};
case 'NodeLost':
return {
status: 'NodeLost',
type: 'danger',
message:
message || 'Container was lost when the node became unreachable.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
default:
return {
status: reason,
type: 'muted',
message: `Container terminated: ${reason}${
restartCount > 0 ? ` (restarted ${restartCount} times)` : ''
}. ${message || ''}`,
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'muted')
? restartCount
: undefined,
};
}
}
if (exitCode === 0) {
return {
status: 'Completed',
type: 'success',
message,
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'success')
? restartCount
: undefined,
};
}
return {
status: 'Error',
type: 'danger',
message: `Container exited with code ${exitCode}${
restartCount > 0 ? ` (restarted ${restartCount} times)` : ''
}. ${message || 'Check application logs for error details.'}`,
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'danger')
? restartCount
: undefined,
};
}
// Handle running state
if (state?.running) {
// Check if container is ready
if (status.ready === false) {
return {
status: 'Running (not ready)',
type: 'warn',
message:
'Container is running but not ready. Check readiness probe configuration.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'warn')
? restartCount
: undefined,
};
}
return {
status: 'Running',
type: 'success',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'success')
? restartCount
: undefined,
};
}
// Fallback
return {
status: 'Unknown',
type: 'muted',
message:
'Container state cannot be determined. Status information may be incomplete.',
hasLogs,
restartCount: shouldShowRestartCount(restartCount, 'muted')
? restartCount
: undefined,
};
}
// Helper function to determine if restart count should be shown
function shouldShowRestartCount(
restartCount: number,
type: ContainerRowData['status']['type']
) {
return restartCount >= 1 && (type === 'danger' || type === 'warn');
}
function hasContainerEverStarted(status: ContainerStatus): boolean {
return (
!!status.state?.running?.startedAt ||
!!status.state?.terminated?.startedAt ||
!!status.lastState?.running?.startedAt ||
!!status.lastState?.terminated?.startedAt ||
!!status.containerID
);
}