mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
6dc0f60faa
* refactor(process): share paired prlimit ceiling ownership Route firmware, Wakaru, and pcap paired ceilings through LinuxResourceLimits without changing their byte or CPU budgets. Keep EVMole's soft-only limits as a dated architecture-guard exception. Co-authored-by: morluto <morluto@users.noreply.github.com> * test(pcap): admit capture limits without host prlimit The ceiling test only checks the shared argument prefix, so it uses a temporary executable instead of /usr/bin/prlimit. Co-authored-by: morluto <morluto@users.noreply.github.com> * style(pcap): format the capture ceiling test oxfmt rejected the object literal wrapping in PcapNativeBound.test.ts. Co-authored-by: morluto <morluto@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: morluto <morluto@users.noreply.github.com>
209 lines
7.1 KiB
TypeScript
209 lines
7.1 KiB
TypeScript
import { expect, it, onTestFinished, vi } from "vitest";
|
|
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
|
|
import { ArtifactFileCleanupFailure } from "../artifacts/ArtifactFileOperation.js";
|
|
import { ArtifactAnalysisResources } from "./ArtifactAnalysisResources.js";
|
|
import { readStableArtifact } from "../artifacts/readStableArtifact.js";
|
|
import { ok } from "../domain/result.js";
|
|
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
|
import {
|
|
BOUNDED_PRODUCER_LIMITS,
|
|
clampLinuxResourceLimits,
|
|
prlimitArgumentVector,
|
|
prlimitCeilingArguments,
|
|
readLinuxResourceLimits,
|
|
} from "./LinuxResourceLimits.js";
|
|
|
|
const kernel = vi.hoisted(() => ({ path: "", denied: false, opens: 0 }));
|
|
vi.mock("node:fs/promises", async (importOriginal) => {
|
|
const filesystem = await importOriginal<typeof import("node:fs/promises")>();
|
|
return {
|
|
...filesystem,
|
|
open: async (...args: Parameters<typeof filesystem.open>) => {
|
|
if (args[0] !== "/proc/self/limits" || kernel.path === "")
|
|
return filesystem.open(...args);
|
|
const file = await filesystem.open(kernel.path, args[1], args[2]);
|
|
kernel.opens += 1;
|
|
const close = file.close.bind(file);
|
|
// Keep fixture ownership even when an assertion interrupts retry checks.
|
|
onTestFinished(close);
|
|
file.close = async () => {
|
|
if (kernel.denied) throw new Error("Kernel descriptor close denied");
|
|
await close();
|
|
};
|
|
return file;
|
|
},
|
|
};
|
|
});
|
|
const configured = {
|
|
address_space_bytes: 1073741824,
|
|
file_bytes: 67108864,
|
|
cpu_seconds: 30,
|
|
};
|
|
const report = (
|
|
memory: string,
|
|
file = "unlimited unlimited",
|
|
cpu = "unlimited unlimited",
|
|
) =>
|
|
`Limit Soft Limit Hard Limit Units\nMax address space ${memory} bytes\nMax file size ${file} bytes\nMax cpu time ${cpu} seconds\n`;
|
|
it("keeps configured ceilings below unlimited or larger inherited limits", () => {
|
|
expect(
|
|
clampLinuxResourceLimits(configured, report("unlimited unlimited")),
|
|
).toEqual(configured);
|
|
expect(
|
|
clampLinuxResourceLimits(
|
|
configured,
|
|
report("18446744073709551615 18446744073709551615"),
|
|
),
|
|
).toEqual(configured);
|
|
});
|
|
it("preserves stricter soft limits and lower hard ceilings, including zero", () => {
|
|
expect(
|
|
clampLinuxResourceLimits(
|
|
configured,
|
|
report("268435456 2147483648", "0 1048576", "10 20"),
|
|
),
|
|
).toEqual({ address_space_bytes: 268435456, file_bytes: 0, cpu_seconds: 10 });
|
|
expect(
|
|
clampLinuxResourceLimits(configured, report("268435456 536870912")),
|
|
).toMatchObject({ address_space_bytes: 268435456 });
|
|
});
|
|
it("refuses missing, ambiguous or malformed kernel limits before launch", () => {
|
|
for (const value of [
|
|
"",
|
|
report("invalid unlimited"),
|
|
report("unlimited unlimited") + "Max address space 1 2 bytes\n",
|
|
])
|
|
expect(() => clampLinuxResourceLimits(configured, value)).toThrow();
|
|
});
|
|
it("pins the shared producer budget in one place", () => {
|
|
expect(BOUNDED_PRODUCER_LIMITS).toEqual({
|
|
input_bytes: 33554432,
|
|
output_bytes: 4194304,
|
|
command_timeout_ms: 30000,
|
|
address_space_bytes: 1073741824,
|
|
file_bytes: 67108864,
|
|
cpu_seconds: 30,
|
|
});
|
|
});
|
|
it("pairs each effective ceiling with itself before the producer argv", () => {
|
|
const vector = prlimitArgumentVector(
|
|
BOUNDED_PRODUCER_LIMITS,
|
|
"/bin/producer",
|
|
);
|
|
expect(vector).toEqual([
|
|
"--as=1073741824:1073741824",
|
|
"--fsize=67108864:67108864",
|
|
"--cpu=30:30",
|
|
"--core=0:0",
|
|
"--",
|
|
"/bin/producer",
|
|
]);
|
|
expect(prlimitCeilingArguments(BOUNDED_PRODUCER_LIMITS)).toEqual(
|
|
vector.slice(0, -1),
|
|
);
|
|
expect(
|
|
prlimitArgumentVector(
|
|
clampLinuxResourceLimits(BOUNDED_PRODUCER_LIMITS, report("10 20")),
|
|
"/bin/producer",
|
|
),
|
|
).toEqual([
|
|
"--as=10:10",
|
|
"--fsize=67108864:67108864",
|
|
"--cpu=30:30",
|
|
"--core=0:0",
|
|
"--",
|
|
"/bin/producer",
|
|
]);
|
|
});
|
|
|
|
it.each([
|
|
["completed", true],
|
|
["failed", true],
|
|
["completed", false],
|
|
["failed", false],
|
|
] as const)(
|
|
"retains exact cleanup ownership after a %s read (scoped=%s)",
|
|
async (outcome, scoped) => {
|
|
const directory = await createTestTempDirectory("rea-kernel-limits-");
|
|
kernel.path = join(directory, "limits");
|
|
kernel.denied = true;
|
|
const scope = new ArtifactResourceScope();
|
|
onTestFinished(async () => {
|
|
kernel.denied = false;
|
|
await scope.close();
|
|
kernel.path = "";
|
|
});
|
|
await writeFile(
|
|
kernel.path,
|
|
outcome === "completed" ? report("unlimited unlimited") : "invalid",
|
|
);
|
|
const failure: unknown = await readLinuxResourceLimits(
|
|
configured,
|
|
scoped ? scope : undefined,
|
|
).catch((cause: unknown) => cause);
|
|
expect(failure).toBeInstanceOf(ArtifactFileCleanupFailure);
|
|
if (!(failure instanceof ArtifactFileCleanupFailure))
|
|
throw new Error("Expected exact descriptor cleanup ownership");
|
|
const physical = failure.fileCleanupFailure;
|
|
expect(physical.outcome.kind).toBe(outcome);
|
|
if (physical.outcome.kind === "completed")
|
|
expect(physical.outcome.value).toEqual(configured);
|
|
else
|
|
expect(physical.outcome.cause).toMatchObject({
|
|
message: "Cannot establish inherited Linux limit: Max address space",
|
|
});
|
|
expect(physical.owner.handle.fd).toBeGreaterThanOrEqual(0);
|
|
if (scoped)
|
|
await expect(scope.retryCleanup()).rejects.toMatchObject({
|
|
cleanupLeases: [failure.cleanupLease],
|
|
});
|
|
else
|
|
await expect(failure.cleanupLease.release()).rejects.toThrow(
|
|
"Kernel descriptor close denied",
|
|
);
|
|
kernel.denied = false;
|
|
if (scoped) await scope.retryCleanup();
|
|
else await failure.cleanupLease.release();
|
|
expect(failure.cleanupLease.released).toBe(true);
|
|
expect(physical.owner.handle.fd).toBe(-1);
|
|
expect(scope.hasPending).toBe(false);
|
|
expect(failure.fileCleanupFailure).toBe(physical);
|
|
},
|
|
);
|
|
|
|
it("readmits analysis and resolves the original cleanup error after kernel descriptor recovery", async () => {
|
|
const directory = await createTestTempDirectory("rea-kernel-admission-");
|
|
kernel.path = join(directory, "limits");
|
|
kernel.denied = true;
|
|
const resources = new ArtifactAnalysisResources(
|
|
"kernel-test",
|
|
"read-limits",
|
|
readStableArtifact,
|
|
readLinuxResourceLimits,
|
|
{ message: "busy", details: {} },
|
|
);
|
|
onTestFinished(async () => {
|
|
kernel.denied = false;
|
|
await resources.close();
|
|
kernel.path = "";
|
|
});
|
|
await writeFile(kernel.path, report("unlimited unlimited"));
|
|
const read = () =>
|
|
resources.run(async () => ok(await resources.readLimits(configured)));
|
|
const first = await read();
|
|
if (first.ok) throw new Error("Expected denied descriptor cleanup");
|
|
expect(first.error.cleanupIncomplete).toBe(true);
|
|
const opened = kernel.opens;
|
|
const blocked = await read();
|
|
expect(blocked.ok).toBe(false);
|
|
expect(kernel.opens).toBe(opened);
|
|
kernel.denied = false;
|
|
const recovered = await read();
|
|
expect(recovered).toEqual(ok(configured));
|
|
expect(kernel.opens).toBe(opened + 1);
|
|
expect(first.error.cleanupIncomplete).toBe(false);
|
|
if (!blocked.ok) expect(blocked.error.cleanupIncomplete).toBe(false);
|
|
});
|