Add fleet control: service configs, replica floors, monitor, pause/restart
Some checks are pending
offline / test (push) Waiting to run
Some checks are pending
offline / test (push) Waiting to run
Central fleet.json sets min/max copies. Tree-node keepFloor respawns until three healthy unpaused replicas remain. CLI and loopback UI pause, resume, stop, and restart instances that fail health checks.
This commit is contained in:
parent
f3dc0e6eee
commit
dd99ce1c64
33 changed files with 1368 additions and 0 deletions
85
packages/verae-fleet/src/server.js
Normal file
85
packages/verae-fleet/src/server.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Operator HTTP: list, status, pause/resume/stop/start/restart.
|
||||
* Binds 127.0.0.1 only.
|
||||
* @module server
|
||||
*/
|
||||
|
||||
import http from 'node:http';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { listServices } from './load.js';
|
||||
|
||||
const PUBLIC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'public');
|
||||
|
||||
function json(res, code, obj) {
|
||||
res.writeHead(code, { 'content-type': 'application/json', 'cache-control': 'no-store' });
|
||||
res.end(JSON.stringify(obj, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('./supervisor.js').Supervisor} sup
|
||||
* @param {import('./monitor.js').Monitor} [mon]
|
||||
*/
|
||||
export function startControlServer(sup, mon) {
|
||||
const bind = sup.loaded.control?.bind || '127.0.0.1';
|
||||
const port = Number(sup.loaded.control?.port || 3850);
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
const url = new URL(req.url, `http://${bind}:${port}`);
|
||||
try {
|
||||
if (req.method === 'GET' && url.pathname === '/health') {
|
||||
return json(res, 200, { ok: true, role: 'fleet-control' });
|
||||
}
|
||||
if (req.method === 'GET' && url.pathname === '/api/services') {
|
||||
return json(res, 200, { services: listServices(sup.loaded), fleetPath: sup.loaded.fleetPath });
|
||||
}
|
||||
if (req.method === 'GET' && url.pathname === '/api/status') {
|
||||
return json(res, 200, { ...sup.status(), monitor: mon?.last || null });
|
||||
}
|
||||
if (req.method === 'POST' && url.pathname === '/api/reconcile') {
|
||||
const actions = await sup.reconcile();
|
||||
return json(res, 200, { actions, status: sup.status() });
|
||||
}
|
||||
const svc = url.pathname.match(/^\/api\/services\/([^/]+)\/(start|stop|pause|resume)$/);
|
||||
if (svc && req.method === 'POST') {
|
||||
const [, id, op] = svc;
|
||||
const fn = {
|
||||
start: () => sup.startService(id),
|
||||
stop: () => sup.stopService(id),
|
||||
pause: () => sup.pauseService(id),
|
||||
resume: () => sup.resumeService(id),
|
||||
}[op];
|
||||
const result = await fn();
|
||||
return json(res, 200, { op, id, result, status: sup.status().services[id] });
|
||||
}
|
||||
const inst = url.pathname.match(/^\/api\/instances\/([^/]+)\/(pause|resume|restart|stop|unhealthy)$/);
|
||||
if (inst && req.method === 'POST') {
|
||||
const [, id, op] = inst;
|
||||
const fn = {
|
||||
pause: () => sup.pauseInstance(id),
|
||||
resume: () => sup.resumeInstance(id),
|
||||
restart: () => sup.restartInstance(id),
|
||||
stop: () => sup.stopInstance(id, { replace: true }),
|
||||
unhealthy: () => sup.markUnhealthy(id),
|
||||
}[op];
|
||||
const result = await fn();
|
||||
return json(res, 200, { op, id, result: result?.id || result, status: sup.status() });
|
||||
}
|
||||
if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/index.html')) {
|
||||
const dest = path.join(PUBLIC, 'index.html');
|
||||
res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
||||
fs.createReadStream(dest).pipe(res);
|
||||
return;
|
||||
}
|
||||
json(res, 404, { error: 'not found' });
|
||||
} catch (err) {
|
||||
json(res, 400, { error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(port, bind, () => {
|
||||
process.stdout.write(`fleet control http://${bind}:${port}/\n`);
|
||||
});
|
||||
return server;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue