fix: make Ops gateway resilient to Synology reboot
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
||||
const builder = join(scriptDir, "build-ops-agents-reboot-resilience-artifact.mjs");
|
||||
const basename = "nodedc-ops-agents-reboot-resilience-20260809-001";
|
||||
|
||||
test("builds a deterministic, minimal Ops Agents application overlay", async () => {
|
||||
const firstRoot = await mkdtemp(join(tmpdir(), "ops-agents-artifact-first-"));
|
||||
const secondRoot = await mkdtemp(join(tmpdir(), "ops-agents-artifact-second-"));
|
||||
|
||||
try {
|
||||
const first = runBuilder(firstRoot);
|
||||
const second = runBuilder(secondRoot);
|
||||
const firstArtifact = join(firstRoot, `${basename}.tgz`);
|
||||
const secondArtifact = join(secondRoot, `${basename}.tgz`);
|
||||
|
||||
assert.equal(first.artifactSha256, second.artifactSha256);
|
||||
assert.equal(first.artifactSha256, sha256(await readFile(firstArtifact)));
|
||||
assert.deepEqual(first.files, [
|
||||
"docker-compose.synology.yml",
|
||||
"src/db/wait-for-database.test.ts",
|
||||
"src/db/wait-for-database.ts",
|
||||
"src/scripts/migrate.ts",
|
||||
]);
|
||||
assert.equal(first.runnerChanged, false);
|
||||
assert.equal(first.databaseVolumeSelected, false);
|
||||
|
||||
const members = new Set(runTar(["-tzf", firstArtifact]).trim().split("\n"));
|
||||
assert.deepEqual(members, new Set([
|
||||
"manifest.env",
|
||||
"files.txt",
|
||||
"payload/",
|
||||
"payload/docker-compose.synology.yml",
|
||||
"payload/src/",
|
||||
"payload/src/db/",
|
||||
"payload/src/db/wait-for-database.test.ts",
|
||||
"payload/src/db/wait-for-database.ts",
|
||||
"payload/src/scripts/",
|
||||
"payload/src/scripts/migrate.ts",
|
||||
]));
|
||||
assert.equal([...members].some((member) => member.includes("._")), false);
|
||||
|
||||
assert.equal(
|
||||
runTar(["-xOzf", firstArtifact, "manifest.env"]),
|
||||
"id=ops-agents-reboot-resilience-20260809-001\ncomponent=ops-agents\ntype=app-overlay\n",
|
||||
);
|
||||
assert.equal(
|
||||
runTar(["-xOzf", firstArtifact, "files.txt"]),
|
||||
`${first.files.join("\n")}\n`,
|
||||
);
|
||||
|
||||
const predecessors = await readFile(join(firstRoot, `${basename}.predecessor.sha256`), "utf8");
|
||||
assert.match(predecessors, /^e78d37368c3d255f257086773ba908e44aae2e475661b80ee17f98c3d55d178f .*docker-compose\.synology\.yml$/m);
|
||||
assert.match(predecessors, /^8396a42deded31f1fe2381f772838a33b05bfd389075ebd34a5f546593f08769 .*src\/scripts\/migrate\.ts$/m);
|
||||
|
||||
const newPaths = await readFile(join(firstRoot, `${basename}.new-paths`), "utf8");
|
||||
assert.match(newPaths, /src\/db\/wait-for-database\.ts/);
|
||||
assert.match(newPaths, /src\/db\/wait-for-database\.test\.ts/);
|
||||
} finally {
|
||||
await rm(firstRoot, { recursive: true, force: true });
|
||||
await rm(secondRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
function runBuilder(outputRoot) {
|
||||
const result = spawnSync(process.execPath, [builder], {
|
||||
encoding: "utf8",
|
||||
env: { ...process.env, NODEDC_DEPLOY_ARTIFACT_DIR: outputRoot },
|
||||
});
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
return JSON.parse(result.stdout);
|
||||
}
|
||||
|
||||
function runTar(args) {
|
||||
const result = spawnSync("tar", args, { encoding: "utf8" });
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
function sha256(bytes) {
|
||||
return createHash("sha256").update(bytes).digest("hex");
|
||||
}
|
||||
Reference in New Issue
Block a user