111 lines
2.6 KiB
TypeScript
111 lines
2.6 KiB
TypeScript
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<void> {
|
|
if (this.ready) {
|
|
return;
|
|
}
|
|
return new Promise<void>((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);
|