aboutsummaryrefslogtreecommitdiffstats
path: root/packages/benchmarks/src/startContainers.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/benchmarks/src/startContainers.ts')
-rw-r--r--packages/benchmarks/src/startContainers.ts96
1 files changed, 96 insertions, 0 deletions
diff --git a/packages/benchmarks/src/startContainers.ts b/packages/benchmarks/src/startContainers.ts
new file mode 100644
index 00000000..ed4e0250
--- /dev/null
+++ b/packages/benchmarks/src/startContainers.ts
@@ -0,0 +1,96 @@
+import { execSync } from "child_process";
+import net from "net";
+import path from "path";
+import { fileURLToPath } from "url";
+
+import { logInfo, logStep, logSuccess, logWarn } from "./log";
+import { sleep, waitUntil } from "./utils";
+
+async function getRandomPort(): Promise<number> {
+ const server = net.createServer();
+ return new Promise<number>((resolve, reject) => {
+ server.unref();
+ server.on("error", reject);
+ server.listen(0, () => {
+ const port = (server.address() as net.AddressInfo).port;
+ server.close(() => resolve(port));
+ });
+ });
+}
+
+async function waitForHealthy(port: number): Promise<void> {
+ await waitUntil(
+ async () => {
+ const res = await fetch(`http://localhost:${port}/api/health`);
+ return res.status === 200;
+ },
+ "Karakeep stack to become healthy",
+ 60_000,
+ 1_000,
+ );
+}
+
+async function captureDockerLogs(composeDir: string): Promise<void> {
+ const logsDir = path.join(composeDir, "setup", "docker-logs");
+ try {
+ execSync(`mkdir -p "${logsDir}"`, { cwd: composeDir });
+ } catch {
+ // ignore
+ }
+
+ const services = ["web", "meilisearch", "chrome", "nginx", "minio"];
+ for (const service of services) {
+ try {
+ execSync(
+ `/bin/sh -c 'docker compose logs ${service} > "${logsDir}/${service}.log" 2>&1'`,
+ {
+ cwd: composeDir,
+ stdio: "ignore",
+ },
+ );
+ logInfo(`Captured logs for ${service}`);
+ } catch (error) {
+ logWarn(`Failed to capture logs for ${service}: ${error}`);
+ }
+ }
+}
+
+export interface RunningContainers {
+ port: number;
+ stop: () => Promise<void>;
+}
+
+export async function startContainers(): Promise<RunningContainers> {
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
+ const composeDir = path.join(__dirname, "..");
+ const port = await getRandomPort();
+ const skipBuild =
+ process.env.BENCH_NO_BUILD === "1" || process.env.BENCH_SKIP_BUILD === "1";
+ const buildArg = skipBuild ? "" : "--build";
+
+ logStep(`Starting docker compose on port ${port}`);
+ execSync(`docker compose up ${buildArg} -d`, {
+ cwd: composeDir,
+ stdio: "inherit",
+ env: { ...process.env, KARAKEEP_PORT: String(port) },
+ });
+
+ logInfo("Waiting for services to report healthy...");
+ await waitForHealthy(port);
+ await sleep(5_000);
+ logSuccess("Containers are ready");
+
+ process.env.KARAKEEP_PORT = String(port);
+
+ let stopped = false;
+ const stop = async (): Promise<void> => {
+ if (stopped) return;
+ stopped = true;
+ logStep("Collecting docker logs");
+ await captureDockerLogs(composeDir);
+ logStep("Stopping docker compose");
+ execSync("docker compose down", { cwd: composeDir, stdio: "inherit" });
+ };
+
+ return { port, stop };
+}