Files
headlamp-tns-csi-plugin/src/components/DriverPodDetailSection.tsx
T
DevContainer User c1c5e8a37d fix: resolve bugs in benchmark lifecycle, snapshot filtering, and dark mode
- Fix PVC bind loop leak on unmount via cancellation ref
- Fix DeleteOptions body structure for proper foreground propagation
- Filter snapshots to tns-csi driver only (was showing all drivers)
- Fix stale closures in Escape key handlers with useCallback
- Add loading state to cleanup delete button, remove window.confirm/alert
- Use CSS custom properties for protocol chart colors (dark mode support)
- Fix all 35 ESLint warnings (import sort, indent, boolean attrs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:47:33 +00:00

150 lines
4.8 KiB
TypeScript

/**
* DriverPodDetailSection — injected into Headlamp's Pod detail view.
*
* Shown only for tns-csi driver pods (identified by
* app.kubernetes.io/name=tns-csi-driver label). Returns null for all other pods.
* Uses registerDetailsViewSection in index.tsx.
*/
import { NameValueTable, SectionBox } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import React from 'react';
import { formatAge, getPodRestarts, isPodReady, TnsCsiPod } from '../api/k8s';
interface DriverPodDetailSectionProps {
resource: {
kind?: string;
metadata?: {
name?: string;
namespace?: string;
labels?: Record<string, string>;
creationTimestamp?: string;
};
spec?: { nodeName?: string };
status?: {
phase?: string;
conditions?: Array<{ type: string; status: string }>;
containerStatuses?: Array<{
name: string;
ready: boolean;
restartCount: number;
image?: string;
state?: {
running?: { startedAt?: string };
waiting?: { reason?: string };
terminated?: { exitCode?: number; reason?: string };
};
}>;
};
// KubeObject instance: raw JSON lives under jsonData;
// metadata here only exposes what the class getter provides (labels, creationTimestamp).
// The jsonData.metadata has the full shape.
jsonData?: {
metadata?: {
name?: string;
namespace?: string;
labels?: Record<string, string>;
creationTimestamp?: string;
};
spec?: { nodeName?: string };
status?: {
phase?: string;
conditions?: Array<{ type: string; status: string }>;
containerStatuses?: Array<{
name: string;
ready: boolean;
restartCount: number;
image?: string;
state?: {
running?: { startedAt?: string };
waiting?: { reason?: string };
terminated?: { exitCode?: number; reason?: string };
};
}>;
};
};
};
}
export default function DriverPodDetailSection({ resource }: DriverPodDetailSectionProps) {
// Extract from jsonData (KubeObject instance) or fall back to direct props.
// jsonData.metadata has the full shape including name/namespace; resource.metadata
// only exposes fields that the Headlamp class getter provides (labels, creationTimestamp).
const meta = (resource?.jsonData?.metadata ?? resource?.metadata) as
| {
name?: string;
namespace?: string;
labels?: Record<string, string>;
creationTimestamp?: string;
}
| undefined;
const spec = resource?.jsonData?.spec ?? resource?.spec;
const status = resource?.jsonData?.status ?? resource?.status;
const labels = meta?.labels ?? {};
// Guard: only tns-csi driver pods
if (labels['app.kubernetes.io/name'] !== 'tns-csi-driver') {
return null;
}
const component = labels['app.kubernetes.io/component'] ?? 'unknown';
const roleLabel =
component === 'controller' ? 'Controller' : component === 'node' ? 'Node' : component;
// Build a minimal pod shape that isPodReady / getPodRestarts can consume
const podShape: TnsCsiPod = {
metadata: {
name: meta?.name ?? '',
namespace: meta?.namespace,
creationTimestamp: meta?.creationTimestamp,
labels,
},
spec: { nodeName: spec?.nodeName },
status: status as TnsCsiPod['status'],
};
const ready = isPodReady(podShape);
const restarts = getPodRestarts(podShape);
const phase = status?.phase ?? '—';
const nodeName = spec?.nodeName ?? '—';
const age = formatAge(meta?.creationTimestamp);
// Container statuses
const containerStatuses = status?.containerStatuses ?? [];
const containerRows = containerStatuses.map(cs => {
let stateText = 'Unknown';
if (cs.state?.running) {
stateText = `Running since ${
cs.state.running.startedAt ? formatAge(cs.state.running.startedAt) : '?'
} ago`;
} else if (cs.state?.waiting) {
stateText = `Waiting: ${cs.state.waiting.reason ?? 'unknown'}`;
} else if (cs.state?.terminated) {
stateText = `Terminated (exit ${cs.state.terminated.exitCode ?? '?'}): ${
cs.state.terminated.reason ?? ''
}`;
}
return {
name: cs.name,
value: `${cs.ready ? '✓ Ready' : '✗ Not Ready'}${stateText}${
cs.restartCount
} restart(s)`,
};
});
return (
<SectionBox title="TNS-CSI Driver Info">
<NameValueTable
rows={[
{ name: 'Role', value: roleLabel },
{ name: 'Phase', value: phase },
{ name: 'Ready', value: ready ? 'Yes' : 'No' },
{ name: 'Restarts', value: String(restarts) },
{ name: 'Node', value: nodeName },
{ name: 'Age', value: age },
...containerRows,
]}
/>
</SectionBox>
);
}