2026-10-24 14:39:17 +02:00
|
|
|
import { mkdirSync } from "fs";
|
|
|
|
|
import { join } from "path";
|
2024-12-12 15:42:09 +01:00
|
|
|
import { execCommand, sleep } from "./helper";
|
|
|
|
|
|
|
|
|
|
const LOCAL_DEPLOYMENT_PATH = "../../openvidu-local-deployment/community/docker-compose.yaml";
|
2026-10-24 14:39:17 +02:00
|
|
|
const LOGS_PATH = "logs";
|
2024-12-12 15:42:09 +01:00
|
|
|
|
|
|
|
|
export class LocalDeployment {
|
|
|
|
|
static async start() {
|
|
|
|
|
console.log("Starting local deployment...");
|
|
|
|
|
execCommand(`docker compose -f ${LOCAL_DEPLOYMENT_PATH} up -d`);
|
|
|
|
|
let statusCode: string;
|
|
|
|
|
|
|
|
|
|
// Check that container "ready-check" exited with code 0
|
|
|
|
|
do {
|
|
|
|
|
await sleep(1);
|
|
|
|
|
statusCode = execCommand("docker inspect ready-check -f {{.State.Status}}:{{.State.ExitCode}}");
|
|
|
|
|
} while (statusCode !== "exited:0");
|
|
|
|
|
|
|
|
|
|
console.log("Local deployment started");
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-02 20:15:49 +01:00
|
|
|
/** Removes the deployment, saving the server log first unless the caller already saved what it needs. */
|
|
|
|
|
static stop(saveLogs = true) {
|
2024-12-12 15:42:09 +01:00
|
|
|
console.log("Stopping local deployment...");
|
2026-11-02 20:15:49 +01:00
|
|
|
if (saveLogs) {
|
|
|
|
|
LocalDeployment.saveServerLogs();
|
|
|
|
|
}
|
2024-12-12 15:42:09 +01:00
|
|
|
execCommand(`docker compose -f ${LOCAL_DEPLOYMENT_PATH} down -v`);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-02 20:15:49 +01:00
|
|
|
/**
|
|
|
|
|
* Saves the log of the OpenVidu server in logs/, named after the current test, before the deployment is removed;
|
|
|
|
|
* with `since`, only what the server logged from then on.
|
|
|
|
|
*/
|
|
|
|
|
static saveServerLogs(since?: Date) {
|
2026-10-24 14:39:17 +02:00
|
|
|
const name = (expect.getState().currentTestName ?? "deployment").replace(/[^\w-]+/g, "-");
|
2026-11-02 20:15:49 +01:00
|
|
|
const sinceArg = since ? `--since ${since.toISOString()}` : "";
|
2026-10-24 14:39:17 +02:00
|
|
|
mkdirSync(LOGS_PATH, { recursive: true });
|
2026-11-02 20:15:49 +01:00
|
|
|
execCommand(`docker logs ${sinceArg} openvidu > ${join(LOGS_PATH, `${name}.log`)} 2>&1 || true`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** What the OpenVidu server logged since the given time. */
|
|
|
|
|
static serverLogsSince(since: Date): string {
|
|
|
|
|
return execCommand(`docker logs --since ${since.toISOString()} openvidu 2>&1 || true`);
|
2026-10-24 14:39:17 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-12 15:42:09 +01:00
|
|
|
static startContainer(containerName: string) {
|
|
|
|
|
console.log(`Starting container ${containerName}...`);
|
|
|
|
|
execCommand(`docker compose -f ${LOCAL_DEPLOYMENT_PATH} start ${containerName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static stopContainer(containerName: string) {
|
|
|
|
|
console.log(`Stopping container ${containerName}...`);
|
|
|
|
|
execCommand(`docker compose -f ${LOCAL_DEPLOYMENT_PATH} stop ${containerName}`);
|
|
|
|
|
}
|
2026-10-15 20:47:40 +02:00
|
|
|
|
|
|
|
|
/** SIGTERM and, after the timeout, SIGKILL: a short timeout cuts the shutdown of the service short. */
|
|
|
|
|
static stopContainerWithTimeout(containerName: string, timeoutSeconds: number) {
|
|
|
|
|
console.log(`Stopping container ${containerName} with a ${timeoutSeconds} s timeout...`);
|
|
|
|
|
execCommand(`docker stop --timeout=${timeoutSeconds} ${containerName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** SIGKILL, as a crash: the service gets no chance to shut down. */
|
|
|
|
|
static killContainer(containerName: string) {
|
|
|
|
|
console.log(`Killing container ${containerName}...`);
|
|
|
|
|
execCommand(`docker kill ${containerName}`);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-02 20:15:49 +01:00
|
|
|
/** Sends a signal to the main process of a container, as an orchestrator does on a planned stop. */
|
|
|
|
|
static signalContainer(containerName: string, signal: NodeJS.Signals) {
|
|
|
|
|
console.log(`Sending ${signal} to container ${containerName}...`);
|
|
|
|
|
execCommand(`docker kill --signal=${signal} ${containerName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** When the current run of a container started, to tell a restarted container from the previous run. */
|
|
|
|
|
static containerStartedAt(containerName: string): string {
|
|
|
|
|
return execCommand(`docker inspect -f {{.State.StartedAt}} ${containerName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static isContainerRunning(containerName: string): boolean {
|
|
|
|
|
return execCommand(`docker inspect -f {{.State.Running}} ${containerName}`) === "true";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Empties Redis, as a Redis restarted without its persisted data: LiveKit's room listing is gone while the rooms
|
|
|
|
|
* keep running on the server.
|
|
|
|
|
*/
|
|
|
|
|
static flushRedis() {
|
|
|
|
|
console.log("Flushing Redis...");
|
|
|
|
|
execCommand("docker exec redis redis-cli --no-auth-warning -a redispassword FLUSHALL");
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-15 20:47:40 +02:00
|
|
|
static restartContainer(containerName: string) {
|
|
|
|
|
console.log(`Restarting container ${containerName}...`);
|
|
|
|
|
execCommand(`docker restart ${containerName}`);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-02 20:15:49 +01:00
|
|
|
/**
|
|
|
|
|
* Waits until the OpenVidu server logs that its analytics database is initialized after the given time, and
|
|
|
|
|
* returns when that was seen, polling every half second.
|
|
|
|
|
*/
|
|
|
|
|
static async waitForAnalytics(since: Date, timeoutSeconds = 60): Promise<Date> {
|
2026-10-15 20:47:40 +02:00
|
|
|
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
|
|
|
|
|
|
|
|
while (Date.now() < deadline) {
|
2026-11-02 20:15:49 +01:00
|
|
|
if (LocalDeployment.serverLogsSince(since).includes("analytics database initialized")) {
|
|
|
|
|
return new Date();
|
2026-10-15 20:47:40 +02:00
|
|
|
}
|
2026-11-02 20:15:49 +01:00
|
|
|
await sleep(0.5);
|
2026-10-15 20:47:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error("The OpenVidu server did not initialize its analytics database");
|
|
|
|
|
}
|
2024-12-12 15:42:09 +01:00
|
|
|
}
|