import { hasShape, predicates, is } from "@sealcode/ts-predicates"; import { ChildProcessWithoutNullStreams, spawn, spawnSync, } from "child_process"; import { concurrency } from "../config.json"; import { IMAGE_NAME } from "./docker-args"; export class Container { callbacks: Array<() => void> = []; ready = false; id: string; output: ""; bg_process: ChildProcessWithoutNullStreams; constructor() { this.id = spawnSync( "docker", ["run", "-d", "-v", `${process.cwd()}/static:/opt/static`, IMAGE_NAME], { cwd: process.cwd(), } ) .stdout.toString() .replace("\n", ""); this.bg_process = spawn("docker", ["logs", "-f", this.id]); this.bg_process.stdout.on("data", (d: Buffer) => { try { const parsed = JSON.parse(d.toString()) as unknown; if ( is(parsed, predicates.object) && hasShape({ code: predicates.string }, parsed) && parsed.code == "ready" ) { this.ready = true; this.signalReady(); } } catch (e) { // noop } this.output += d.toString(); }); } signalReady(): void { this.callbacks.forEach((callback) => callback()); } onReady(callback: () => void): void { this.ready ? callback() : this.callbacks.push(callback); } async waitReady(): Promise { if (this.ready) { return; } return new Promise((resolve) => { this.onReady(resolve); }); } close(): void { spawn("docker", ["rm", "-f", this.id]); } closeSync(): void { spawnSync("docker", ["rm", "-f", this.id]); console.log("doker rm done", this.id); } } export default new (class ContainerPool { pool: Container[] = []; constructor(public concurrency: number) { this.concurrency = concurrency; for (let i = 1; i <= this.concurrency; i++) { this.generateContainer(); } process.on("SIGINT", () => { console.log("SIGINT"); this.clear(); }); } generateContainer() { this.pool.push(new Container()); } getContainer() { if (!this.pool.length) { throw new Error("pool is empty, try again!"); } const container = this.pool.shift(); // get and remove from pool the oldest container if (!container) { throw new Error("Pool was somehow empty!"); } this.generateContainer(); return container; } clear() { console.log("Removing all containers from the pool"); for (const container of this.pool) { container.closeSync(); } console.log("Removing containers done"); process.exit(0); } })(concurrency);