From 960282d4edb12a57c59bf5c53afacaf3bd20732a Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Sat, 8 Nov 2025 23:16:42 +0000 Subject: [PATCH] Make node status live. --- src/components/ClusterNodesComponent.tsx | 15 ++++++++++++++- src/services/api/types/cluster.ts | 8 ++++++++ src/services/api/types/node.ts | 1 + src/utils/deriveNodeStatus.ts | 21 +++++++++++++-------- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/components/ClusterNodesComponent.tsx b/src/components/ClusterNodesComponent.tsx index 789551e..c7626bf 100644 --- a/src/components/ClusterNodesComponent.tsx +++ b/src/components/ClusterNodesComponent.tsx @@ -8,6 +8,7 @@ import { Cpu, HardDrive, Network, Monitor, CheckCircle, AlertCircle, BookOpen, E import { useInstanceContext } from '../hooks/useInstanceContext'; import { useNodes, useDiscoveryStatus } from '../hooks/useNodes'; import { useCluster } from '../hooks/useCluster'; +import { useClusterStatus } from '../services/api/hooks/useCluster'; import { BootstrapModal } from './cluster/BootstrapModal'; import { NodeStatusBadge } from './nodes/NodeStatusBadge'; import { NodeFormDrawer } from './nodes/NodeFormDrawer'; @@ -49,6 +50,8 @@ export function ClusterNodesComponent() { status: clusterStatus } = useCluster(currentInstance); + const { data: clusterStatusData } = useClusterStatus(currentInstance || ''); + const [discoverSubnet, setDiscoverSubnet] = useState(''); const [addNodeIp, setAddNodeIp] = useState(''); const [discoverError, setDiscoverError] = useState(null); @@ -241,6 +244,9 @@ export function ClusterNodesComponent() { // Derive status from backend state flags for each node const assignedNodes = nodes.map(node => { + // Get runtime status from cluster status + const runtimeStatus = clusterStatusData?.node_statuses?.[node.hostname]; + let status = 'pending'; if (node.maintenance) { status = 'provisioning'; @@ -249,7 +255,14 @@ export function ClusterNodesComponent() { } else if (node.applied) { status = 'ready'; } - return { ...node, status }; + + return { + ...node, + status, + isReachable: runtimeStatus?.ready, + inKubernetes: runtimeStatus?.ready, // Whether in cluster (from backend 'ready' field) + kubernetesReady: runtimeStatus?.kubernetes_ready, // Whether K8s Ready condition is true + }; }); // Check if cluster needs bootstrap diff --git a/src/services/api/types/cluster.ts b/src/services/api/types/cluster.ts index d8fa85b..ab655d8 100644 --- a/src/services/api/types/cluster.ts +++ b/src/services/api/types/cluster.ts @@ -4,6 +4,13 @@ export interface ClusterConfig { version?: string; } +export interface NodeStatus { + hostname: string; + ready: boolean; + kubernetes_ready: boolean; + role: string; +} + export interface ClusterStatus { ready: boolean; nodes: number; @@ -11,6 +18,7 @@ export interface ClusterStatus { workerNodes: number; kubernetesVersion?: string; talosVersion?: string; + node_statuses?: Record; } export interface HealthCheck { diff --git a/src/services/api/types/node.ts b/src/services/api/types/node.ts index 4b13bf8..a8edaab 100644 --- a/src/services/api/types/node.ts +++ b/src/services/api/types/node.ts @@ -17,6 +17,7 @@ export interface Node { // Optional runtime fields for enhanced status isReachable?: boolean; inKubernetes?: boolean; + kubernetesReady?: boolean; lastHealthCheck?: string; // Optional fields (not yet returned by API) hardware?: HardwareInfo; diff --git a/src/utils/deriveNodeStatus.ts b/src/utils/deriveNodeStatus.ts index d2cbeb6..b80f5b7 100644 --- a/src/utils/deriveNodeStatus.ts +++ b/src/utils/deriveNodeStatus.ts @@ -35,24 +35,29 @@ export function deriveNodeStatus(node: Node): NodeStatus { } if (node.applied) { - // Check Kubernetes membership for healthy state - if (node.inKubernetes === true) { + // Check Kubernetes membership and readiness + if (node.inKubernetes === true && node.kubernetesReady === true) { return NodeStatus.HEALTHY; } - // Applied but not yet in Kubernetes (could be provisioning or ready) - if (node.isReachable === true) { + // In Kubernetes but not Ready + if (node.inKubernetes === true && node.kubernetesReady === false) { + return NodeStatus.DEGRADED; + } + + // Applied and reachable but not yet in Kubernetes + if (node.isReachable === true && node.inKubernetes !== true) { return NodeStatus.READY; } - // Applied but status unknown + // Applied but status unknown (no cluster status data yet) if (node.isReachable === undefined && node.inKubernetes === undefined) { return NodeStatus.READY; } - // Applied but having issues - if (node.inKubernetes === false) { - return NodeStatus.DEGRADED; + // Applied but not reachable at all + if (node.isReachable === false) { + return NodeStatus.UNREACHABLE; } }