From 016f8abe6838dac333625e1c409d5bd64b4183b6 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 9 Apr 2026 23:45:10 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20accurate=20instance=20status=20=E2=80=94?= =?UTF-8?q?=20STARTING=20until=20pod=20is=20actually=20running?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instance status now reflects actual container state: - startOne() sets STARTING (not RUNNING) after container creation - syncStatus() promotes STARTING→RUNNING when pod is ready - syncStatus() demotes RUNNING→STARTING if pod restarts (CrashLoop) - External servers still get RUNNING immediately (no container) Previously, CrashLooping pods showed as RUNNING in mcpctl get instances. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/mcpd/src/services/instance.service.ts | 10 +++++++++- src/mcpd/tests/mcp-server-flow.test.ts | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/mcpd/src/services/instance.service.ts b/src/mcpd/src/services/instance.service.ts index 7129f67..6e72bcc 100644 --- a/src/mcpd/src/services/instance.service.ts +++ b/src/mcpd/src/services/instance.service.ts @@ -49,6 +49,7 @@ export class InstanceService { if ((inst.status === 'RUNNING' || inst.status === 'STARTING') && inst.containerId) { try { const info = await this.orchestrator.inspectContainer(inst.containerId); + if (info.state === 'stopped' || info.state === 'error') { // Container died — get last logs for error context let errorMsg = `Container ${info.state}`; @@ -60,6 +61,12 @@ export class InstanceService { await this.instanceRepo.updateStatus(inst.id, 'ERROR', { metadata: { error: errorMsg }, }); + } else if (info.state === 'starting' && inst.status === 'RUNNING') { + // Pod went back to starting (e.g. CrashLoopBackOff restart) + await this.instanceRepo.updateStatus(inst.id, 'STARTING', {}); + } else if (info.state === 'running' && inst.status === 'STARTING') { + // Pod became ready — promote to RUNNING + await this.instanceRepo.updateStatus(inst.id, 'RUNNING', {}); } } catch { // Container gone entirely @@ -305,7 +312,8 @@ export class InstanceService { updateFields.port = containerInfo.port; } - instance = await this.instanceRepo.updateStatus(instance.id, 'RUNNING', updateFields); + // Set STARTING — syncStatus will promote to RUNNING once the container is actually ready + instance = await this.instanceRepo.updateStatus(instance.id, 'STARTING', updateFields); } catch (err) { instance = await this.instanceRepo.updateStatus(instance.id, 'ERROR', { metadata: { error: err instanceof Error ? err.message : String(err) }, diff --git a/src/mcpd/tests/mcp-server-flow.test.ts b/src/mcpd/tests/mcp-server-flow.test.ts index 5cbc831..ce3c348 100644 --- a/src/mcpd/tests/mcp-server-flow.test.ts +++ b/src/mcpd/tests/mcp-server-flow.test.ts @@ -484,7 +484,7 @@ describe('MCP server full flow', () => { expect(instancesRes.statusCode).toBe(200); const instances = instancesRes.json>(); expect(instances).toHaveLength(1); - expect(instances[0]!.status).toBe('RUNNING'); + expect(instances[0]!.status).toBe('STARTING'); expect(instances[0]!.containerId).toBeTruthy(); // 3. Verify orchestrator was called with correct spec @@ -564,7 +564,7 @@ describe('MCP server full flow', () => { expect(listRes.statusCode).toBe(200); const instances = listRes.json>(); expect(instances).toHaveLength(1); - expect(instances[0]!.status).toBe('RUNNING'); + expect(instances[0]!.status).toBe('STARTING'); const instanceId = instances[0]!.id; // Delete instance → triggers reconcile → new instance auto-created