ombi keeps its Servarr connections in its own database, so the mesh has no file to write them into. A run-once step reads the three bindings and pair credentials and writes host, port, TLS, base path and key into ombi through ombi's own API - only when they differ, and nothing else ombi keeps. Until the operator accepts an app's key for this pair the mesh delivers a value it minted, which no Servarr app accepts. The step tries the key against the app first and, refused, writes nothing and fails naming the secret accept that fixes it, so the old working key in ombi is never replaced by a dead one. Declared last so its failing gates nothing else of ombi (ADR 0136), and restart-on its six inputs so it runs again when a provider moves (ADR 0099).
60 lines
2.4 KiB
TypeScript
60 lines
2.4 KiB
TypeScript
// ombi's Servarr step — run once by the host after ombi's server starts, and run again whenever a
|
|
// binding or pair credential it reads changes (the container's `restart-on`, novox/hq ADR 0099).
|
|
//
|
|
// **A step, not a loop**, for the reason route-adapter gives: everything it does is a function of
|
|
// files the mesh writes, and the host already knows when they change. It connects to no broker.
|
|
//
|
|
// Exits non-zero when any app could not be put right — a refused credential, an unreachable app, an
|
|
// ombi that cannot reach it — so the node reports the step failed and the host runs it again on the
|
|
// next apply. It is declared last in the manifest, so its failing gates nothing else of ombi's
|
|
// (novox/hq ADR 0136).
|
|
//
|
|
// Reads, per app, `<dir>/<provision>.json` (the binding) and `<dir>/<provision>.secret` (the pair
|
|
// credential), where <dir> is MESH_SERVARR_DIR. Never prints a key.
|
|
|
|
import { join } from "node:path";
|
|
|
|
import { APPS, ombiReady, readBinding, readIfThere, reconcileApp, type Http } from "./settings.js";
|
|
|
|
const dir = process.env.MESH_SERVARR_DIR ?? "/run/servarr";
|
|
const url = process.env.MESH_OMBI_URL ?? "http://127.0.0.1:3579";
|
|
const apiKey = (await readIfThere(process.env.MESH_OMBI_API_KEY_FILE))?.trim() ?? process.env.MESH_OMBI_API_KEY ?? "";
|
|
const waitSeconds = Number(process.env.MESH_OMBI_WAIT_SECONDS ?? "180");
|
|
|
|
const http: Http = { fetch: (u, init) => fetch(u, init) };
|
|
|
|
if (!apiKey) {
|
|
console.error("[ombi-servarr] no ombi API key — ombi's own `api-key` secret has not been accepted");
|
|
process.exit(1);
|
|
}
|
|
const ombi = { url, apiKey };
|
|
|
|
if (!(await ombiReady(http, ombi, waitSeconds * 1000))) {
|
|
console.error(`[ombi-servarr] ombi did not answer at ${url} within ${waitSeconds}s`);
|
|
process.exit(1);
|
|
}
|
|
|
|
let failed = 0;
|
|
for (const spec of APPS) {
|
|
const outcome = await reconcileApp(
|
|
http,
|
|
ombi,
|
|
spec,
|
|
await readBinding(join(dir, `${spec.provision}.json`)),
|
|
await readIfThere(join(dir, `${spec.provision}.secret`)),
|
|
);
|
|
switch (outcome.result) {
|
|
case "unchanged":
|
|
console.log(`[ombi-servarr] ${outcome.app}: already as the mesh says; connection tested`);
|
|
break;
|
|
case "written":
|
|
console.log(`[ombi-servarr] ${outcome.app}: wrote ${outcome.fields.join(", ")}; connection tested`);
|
|
break;
|
|
case "refused":
|
|
failed++;
|
|
console.error(`[ombi-servarr] ${outcome.app}: ${outcome.problem}`);
|
|
break;
|
|
}
|
|
}
|
|
process.exitCode = failed > 0 ? 1 : 0;
|