mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-31 12:48:10 +00:00
feat(sidebar): surface partial status for multi-container stacks (#1426)
Bulk stack-status aggregation collapsed a stack to "running" as soon as any container was up, so a multi-container stack with crashed containers showed a green UP pill and the degradation was invisible from the sidebar. Add a crash-aware "partial" state: a stack is partial when at least one container is running and at least one has genuinely failed (exited with a non-zero code, dead, or crash-looping). Cleanly finished one-shot containers (exit 0) and clean restart-policy cycling do not count, so an app with a completed init job stays UP. The exit code is read from the container Status string, so no extra inspect calls are needed. Render partial as an amber PT pill with a hover tooltip showing the running/total count, fold it into the Down filter (needs-attention), and treat it as running for context-menu lifecycle actions so operators keep stop/restart/update. The dashboard stack-health table, cross-node search rows, and the command palette all pick up the new state through the shared status surfaces.
This commit is contained in:
@@ -4,6 +4,7 @@ import { Sparkline } from '@/components/ui/sparkline';
|
||||
import { ChevronLeft, ChevronRight, Layers } from 'lucide-react';
|
||||
import type { StackStatusEntry, MetricPoint, StackCpuSeries } from './types';
|
||||
import { aggregateCurrentUsage } from './aggregateCurrentUsage';
|
||||
import { classifyRow, type RowState } from './classifyRow';
|
||||
|
||||
interface StackHealthTableProps {
|
||||
stackStatuses: Record<string, StackStatusEntry>;
|
||||
@@ -14,8 +15,6 @@ interface StackHealthTableProps {
|
||||
}
|
||||
|
||||
const PAGE_SIZE = 8;
|
||||
const WARN = 80;
|
||||
const CRIT = 90;
|
||||
// Shared by the header and data rows so their columns stay aligned. The
|
||||
// `max-md:min-w` keeps both at the same width below md, where the card scrolls
|
||||
// horizontally; desktop is unaffected by the `max-md:` prefix.
|
||||
@@ -37,15 +36,6 @@ function formatUptime(seconds: number): string {
|
||||
return `${Math.max(1, Math.floor(seconds))}s`;
|
||||
}
|
||||
|
||||
type RowState = 'healthy' | 'warn' | 'error';
|
||||
|
||||
function classifyRow(status: StackStatusEntry['status'], peakCpu: number): RowState {
|
||||
if (status === 'exited') return 'error';
|
||||
if (peakCpu >= CRIT) return 'error';
|
||||
if (peakCpu >= WARN) return 'warn';
|
||||
return 'healthy';
|
||||
}
|
||||
|
||||
const stateDot: Record<RowState, string> = {
|
||||
healthy: 'bg-success',
|
||||
warn: 'bg-warning',
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { classifyRow } from '../classifyRow';
|
||||
|
||||
describe('classifyRow', () => {
|
||||
it('marks a partially-crashed stack as warn (degraded), not healthy', () => {
|
||||
expect(classifyRow('partial', 0)).toBe('warn');
|
||||
});
|
||||
|
||||
it('marks an exited stack as error', () => {
|
||||
expect(classifyRow('exited', 0)).toBe('error');
|
||||
});
|
||||
|
||||
it('marks a running stack with low CPU as healthy', () => {
|
||||
expect(classifyRow('running', 0)).toBe('healthy');
|
||||
});
|
||||
|
||||
it('escalates a partial stack with critical CPU to error', () => {
|
||||
expect(classifyRow('partial', 95)).toBe('error');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { StackStatusEntry } from './types';
|
||||
|
||||
export type RowState = 'healthy' | 'warn' | 'error';
|
||||
|
||||
const WARN = 80;
|
||||
const CRIT = 90;
|
||||
|
||||
export function classifyRow(status: StackStatusEntry['status'], peakCpu: number): RowState {
|
||||
if (status === 'exited') return 'error';
|
||||
if (peakCpu >= CRIT) return 'error';
|
||||
// A partially-crashed stack is degraded, not down: surface it as a warning
|
||||
// (the same amber as the sidebar PT pill) unless CPU is already critical.
|
||||
if (status === 'partial' || peakCpu >= WARN) return 'warn';
|
||||
return 'healthy';
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export interface NotificationItem {
|
||||
}
|
||||
|
||||
export interface StackStatusEntry {
|
||||
status: 'running' | 'exited' | 'unknown';
|
||||
status: 'running' | 'exited' | 'unknown' | 'partial';
|
||||
mainPort?: number;
|
||||
/** Unix seconds of the oldest running container (approximates stack uptime). */
|
||||
runningSince?: number;
|
||||
|
||||
Reference in New Issue
Block a user