mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
docker healthcheck api thingy
This commit is contained in:
@@ -16,6 +16,20 @@ app.use(morgan('dev'));
|
||||
*/
|
||||
app.use(PUBLIC_MEDIA_PREFIX, createMediaMtxProxy({ logger }));
|
||||
app.use(express.json());
|
||||
/*
|
||||
Docker and the later lifecycle controller need one stable readiness result,
|
||||
but they do not need administrator credentials or application details. Load
|
||||
the health service only when the route is called so the global HTTP module
|
||||
remains safe to initialize before the service graph during bootstrap.
|
||||
*/
|
||||
app.get('/health', async (_req, res) => {
|
||||
const { getContainerHealth } = require('../services/healthService');
|
||||
const health = await getContainerHealth();
|
||||
res.status(health.healthy ? 200 : 503).json({
|
||||
status: health.healthy ? 'healthy' : 'unhealthy',
|
||||
checks: health.checks,
|
||||
});
|
||||
});
|
||||
app.use(express.static(config.staticDir, { index: false }));
|
||||
|
||||
const httpServer = http.createServer(app);
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// Purpose: Defines the health Service module and the helpers/state used by this service unit.
|
||||
// Scope: Keeps runtime behavior unchanged while isolating responsibilities into a clear module boundary.
|
||||
const fsp = require('fs/promises');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { resolveRoverSnapshotDir } = require('../../helpers/dataPaths');
|
||||
const { resolveDataDir, resolveRoverSnapshotDir } = require('../../helpers/dataPaths');
|
||||
const roverManager = require('../roverManager');
|
||||
const { getRoomCameras } = require('../roomCameraService');
|
||||
const { getRoomCameraState } = require('../roomCameraService');
|
||||
@@ -13,6 +14,8 @@ const ROVER_SNAPSHOT_DIR = resolveRoverSnapshotDir();
|
||||
const HEALTH_INTERVAL_MS = 5000;
|
||||
const ROOM_CAMERA_STALE_MS = 5000;
|
||||
const ROVER_SNAPSHOT_STALE_MS = 5000;
|
||||
const MEDIAMTX_HEALTH_URL = 'http://127.0.0.1:9998/metrics';
|
||||
const MEDIAMTX_HEALTH_TIMEOUT_MS = 1500;
|
||||
|
||||
let latest = {
|
||||
updatedAt: Date.now(),
|
||||
@@ -88,6 +91,49 @@ function getHealthSnapshot() {
|
||||
return latest;
|
||||
}
|
||||
|
||||
async function getContainerHealth() {
|
||||
let dataDirectory = false;
|
||||
let mediaMtx = false;
|
||||
|
||||
try {
|
||||
/*
|
||||
The mounted data root is the container's only persistent storage. Check
|
||||
the directory itself instead of creating a probe file on every request;
|
||||
this proves that the running application user can reach the mount without
|
||||
adding health-check writes to backups or administrative file listings.
|
||||
*/
|
||||
await fsp.access(resolveDataDir(), fs.constants.R_OK | fs.constants.W_OK);
|
||||
dataDirectory = true;
|
||||
} catch {
|
||||
dataDirectory = false;
|
||||
}
|
||||
|
||||
try {
|
||||
/*
|
||||
MediaMTX already exposes metrics only on loopback, so it is also the
|
||||
smallest reliable readiness probe. Reading the response closes the body
|
||||
before this request completes and avoids accumulating idle connections
|
||||
across Docker's recurring health checks.
|
||||
*/
|
||||
const response = await fetch(MEDIAMTX_HEALTH_URL, {
|
||||
signal: AbortSignal.timeout(MEDIAMTX_HEALTH_TIMEOUT_MS),
|
||||
});
|
||||
await response.text();
|
||||
mediaMtx = response.ok;
|
||||
} catch {
|
||||
mediaMtx = false;
|
||||
}
|
||||
|
||||
return {
|
||||
healthy: dataDirectory && mediaMtx,
|
||||
checks: {
|
||||
dataDirectory,
|
||||
mediaMtx,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getContainerHealth,
|
||||
getHealthSnapshot,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user