From 7206a94871e612c81cac962d61087abb012f339c Mon Sep 17 00:00:00 2001 From: William Valentin Date: Sun, 22 Feb 2026 21:02:17 -0800 Subject: [PATCH] fix(observability): avoid false stopped Flynn status outside systemd --- src/gateway/handlers/observability.test.ts | 63 ++++++++++++++++++++++ src/gateway/handlers/observability.ts | 22 +++++--- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/gateway/handlers/observability.test.ts b/src/gateway/handlers/observability.test.ts index c73f649..b46aae3 100644 --- a/src/gateway/handlers/observability.test.ts +++ b/src/gateway/handlers/observability.test.ts @@ -253,4 +253,67 @@ describe('ObservabilityCollector', () => { expect(typeof logs.lines[0]?.ts).toBe('number'); expect(logs.lines[0]?.text).toContain('queue depth high'); }); + + it('falls back to process-running Flynn status when not launched under systemd', async () => { + const originalInvocationId = process.env.INVOCATION_ID; + const originalJournalStream = process.env.JOURNAL_STREAM; + const originalSystemdExecPid = process.env.SYSTEMD_EXEC_PID; + delete process.env.INVOCATION_ID; + delete process.env.JOURNAL_STREAM; + delete process.env.SYSTEMD_EXEC_PID; + + const runner = async (command: string, args: string[]) => { + const key = `${command} ${args.join(' ')}`; + if (key === 'systemctl show flynn.service --property=LoadState,ActiveState,SubState,Description,ExecMainPID,Result --no-pager') { + return { + stdout: 'LoadState=loaded\nActiveState=inactive\nSubState=dead\nDescription=Flynn daemon\nExecMainPID=0\nResult=success\n', + stderr: '', + }; + } + if (key === 'systemctl --user show ollama.service --property=LoadState,ActiveState,SubState,UnitFileState,Description,ExecMainPID,Result --no-pager') { + return { stdout: 'LoadState=not-found\nActiveState=inactive\nSubState=dead\nDescription=Ollama\nExecMainPID=0\nResult=not-found\n', stderr: '' }; + } + if (key === 'systemctl --user show llama-server.service --property=LoadState,ActiveState,SubState,UnitFileState,Description,ExecMainPID,Result --no-pager') { + return { stdout: 'LoadState=not-found\nActiveState=inactive\nSubState=dead\nDescription=llama.cpp\nExecMainPID=0\nResult=not-found\n', stderr: '' }; + } + if (key === 'docker compose -f docker-compose.yml config --profiles') { + return { stdout: '', stderr: '' }; + } + if (key === 'docker compose -f docker-compose.yml config --services') { + return { stdout: 'flynn\n', stderr: '' }; + } + if (key === 'docker compose -f docker-compose.yml ps --all --format json') { + return { stdout: '[]', stderr: '' }; + } + throw new Error(`Unexpected command: ${key}`); + }; + + try { + const collector = new ObservabilityCollector({ + config: createConfig(), + runner, + }); + await collector.forceSample(); + const sources = await collector.listSources(); + const flynn = sources.find((entry) => entry.id === 'systemd:flynn'); + expect(flynn?.status).toBe('running'); + expect(flynn?.logCapable).toBe(false); + } finally { + if (originalInvocationId === undefined) { + delete process.env.INVOCATION_ID; + } else { + process.env.INVOCATION_ID = originalInvocationId; + } + if (originalJournalStream === undefined) { + delete process.env.JOURNAL_STREAM; + } else { + process.env.JOURNAL_STREAM = originalJournalStream; + } + if (originalSystemdExecPid === undefined) { + delete process.env.SYSTEMD_EXEC_PID; + } else { + process.env.SYSTEMD_EXEC_PID = originalSystemdExecPid; + } + } + }); }); diff --git a/src/gateway/handlers/observability.ts b/src/gateway/handlers/observability.ts index 6696ff3..6b99a08 100644 --- a/src/gateway/handlers/observability.ts +++ b/src/gateway/handlers/observability.ts @@ -130,6 +130,10 @@ interface SampleRecord { restartCount: number; } +function isGatewayRunningUnderSystemd(): boolean { + return Boolean(process.env.INVOCATION_ID || process.env.JOURNAL_STREAM || process.env.SYSTEMD_EXEC_PID); +} + function defaultRunner( command: string, args: string[], @@ -665,24 +669,28 @@ export class ObservabilityCollector { const snapshots: SourceSnapshot[] = []; const flynnMapped = mapSystemdStatus(flynnStatus.activeState, flynnStatus.error); + const fallbackToProcessRuntime = !isGatewayRunningUnderSystemd() && flynnMapped.status !== 'running'; + const flynnStatusValue = fallbackToProcessRuntime ? 'running' : flynnMapped.status; + const flynnStateCode = fallbackToProcessRuntime ? STATE_RUNNING : flynnMapped.stateCode; + const flynnHealthCode = fallbackToProcessRuntime ? HEALTH_HEALTHY : flynnMapped.healthCode; snapshots.push({ source: { id: 'systemd:flynn', name: 'Flynn daemon', kind: 'systemd_system', runtime: 'systemd_system', - status: flynnMapped.status, + status: flynnStatusValue, graphCapable: true, - logCapable: true, + logCapable: !fallbackToProcessRuntime, metadata: { unit: this.flynnSystemdUnit, - state: flynnStatus.activeState, - statusText: flynnStatus.statusText, + state: fallbackToProcessRuntime ? 'running' : flynnStatus.activeState, + statusText: fallbackToProcessRuntime ? 'running (gateway process)' : flynnStatus.statusText, }, }, - stateCode: flynnMapped.stateCode, - healthCode: flynnMapped.healthCode, - hasError: Boolean(flynnStatus.error), + stateCode: flynnStateCode, + healthCode: flynnHealthCode, + hasError: fallbackToProcessRuntime ? false : Boolean(flynnStatus.error), fingerprint: flynnStatus.pid ? `pid:${flynnStatus.pid}` : null, });