ombi reached plex at its public name, typed into its settings screen, so it depended on plex's public route and on nobody moving plex. ombi now requires plex-api, and the run-once step that writes its Servarr connections writes its Plex one too - renamed from `servarr` to `connections`, since it is no longer only that. ombi keeps several Plex servers. The entry this provision names is found by the server's own machineIdentifier (plex answers it at /identity, and ombi stored it when the server was loaded), and only its host, port, TLS, base path and token are written, only when they differ. Another server's entry, the selected libraries, whether Plex is enabled and every other choice are left alone. An ombi with no entry for the server gets one. The token is tried against plex first. Until the operator accepts the server's X-Plex-Token for this pair the mesh delivers a value it minted, which plex refuses (401, or 400 on a network it trusts); refused, nothing is written and the step fails naming the secret accept, so a working token in ombi is never replaced by a dead one. Tests import the compiled step, as keycloak's do: the step imports its sibling with the .js specifier the build needs, which type stripping does not resolve. `npm test` builds first.
65 lines
2.9 KiB
TypeScript
65 lines
2.9 KiB
TypeScript
// ombi's connections 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).
|
|
// It brings ombi's connections to Sonarr, Radarr, Lidarr (servarr/settings.ts) and Plex
|
|
// (plex/settings.ts) in line with what the mesh bound.
|
|
//
|
|
// **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). One app failing does not stop the others being put right.
|
|
//
|
|
// Reads, per provision, `<dir>/<provision>.json` (the binding) and `<dir>/<provision>.secret` (the
|
|
// pair credential), where <dir> is MESH_CONNECTIONS_DIR. Never prints a key or a token.
|
|
|
|
import { join } from "node:path";
|
|
|
|
import { PLEX_PROVISION, reconcilePlex } from "../plex/settings.js";
|
|
import { APPS, ombiReady, readBinding, readIfThere, reconcileApp, type Http, type Outcome } from "../servarr/settings.js";
|
|
|
|
const dir = process.env.MESH_CONNECTIONS_DIR ?? "/run/connections";
|
|
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-connections] 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-connections] ombi did not answer at ${url} within ${waitSeconds}s`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const inputs = async (provision: string) =>
|
|
[await readBinding(join(dir, `${provision}.json`)), await readIfThere(join(dir, `${provision}.secret`))] as const;
|
|
|
|
const outcomes: Outcome[] = [];
|
|
for (const spec of APPS) {
|
|
outcomes.push(await reconcileApp(http, ombi, spec, ...(await inputs(spec.provision))));
|
|
}
|
|
outcomes.push(await reconcilePlex(http, ombi, ...(await inputs(PLEX_PROVISION))));
|
|
|
|
let failed = 0;
|
|
for (const outcome of outcomes) {
|
|
switch (outcome.result) {
|
|
case "unchanged":
|
|
console.log(`[ombi-connections] ${outcome.app}: already as the mesh says; connection tested`);
|
|
break;
|
|
case "written":
|
|
console.log(`[ombi-connections] ${outcome.app}: wrote ${outcome.fields.join(", ")}; connection tested`);
|
|
break;
|
|
case "refused":
|
|
failed++;
|
|
console.error(`[ombi-connections] ${outcome.app}: ${outcome.problem}`);
|
|
break;
|
|
}
|
|
}
|
|
process.exitCode = failed > 0 ? 1 : 0;
|