2024-02-21 18:43:47 +08:00
|
|
|
/*
|
2024-09-09 21:57:16 +08:00
|
|
|
Copyright 2024 New Vector Ltd.
|
2024-02-21 18:43:47 +08:00
|
|
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
|
|
|
2024-09-09 21:57:16 +08:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
|
|
Please see LICENSE files in the repository root for full details.
|
2024-02-21 18:43:47 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
import path, { basename } from "node:path";
|
|
|
|
import os from "node:os";
|
|
|
|
import * as fse from "fs-extra";
|
|
|
|
import { BrowserContext, TestInfo } from "@playwright/test";
|
|
|
|
|
|
|
|
import { getFreePort } from "../utils/port";
|
|
|
|
import { Docker } from "../docker";
|
|
|
|
import { PG_PASSWORD, PostgresDocker } from "../postgres";
|
|
|
|
import { HomeserverInstance } from "../homeserver";
|
|
|
|
import { Instance as MailhogInstance } from "../mailhog";
|
|
|
|
|
|
|
|
// Docker tag to use for `ghcr.io/matrix-org/matrix-authentication-service` image.
|
|
|
|
// We use a debug tag so that we have a shell and can run all 3 necessary commands in one run.
|
|
|
|
const TAG = "0.8.0-debug";
|
|
|
|
|
|
|
|
export interface ProxyInstance {
|
|
|
|
containerId: string;
|
|
|
|
postgresId: string;
|
|
|
|
configDir: string;
|
|
|
|
port: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function cfgDirFromTemplate(opts: {
|
|
|
|
postgresHost: string;
|
|
|
|
synapseUrl: string;
|
|
|
|
masPort: string;
|
|
|
|
smtpPort: string;
|
|
|
|
}): Promise<{
|
|
|
|
configDir: string;
|
|
|
|
}> {
|
|
|
|
const configPath = path.join(__dirname, "config.yaml");
|
|
|
|
const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-mas-"));
|
|
|
|
|
|
|
|
const outputHomeserver = path.join(tempDir, "config.yaml");
|
|
|
|
console.log(`Gen ${configPath} -> ${outputHomeserver}`);
|
|
|
|
let config = await fse.readFile(configPath, "utf8");
|
|
|
|
config = config.replace(/{{MAS_PORT}}/g, opts.masPort);
|
|
|
|
config = config.replace(/{{POSTGRES_HOST}}/g, opts.postgresHost);
|
|
|
|
config = config.replace(/{{POSTGRES_PASSWORD}}/g, PG_PASSWORD);
|
|
|
|
config = config.replace(/%{{SMTP_PORT}}/g, opts.smtpPort);
|
|
|
|
config = config.replace(/{{SYNAPSE_URL}}/g, opts.synapseUrl);
|
|
|
|
|
|
|
|
await fse.writeFile(outputHomeserver, config);
|
|
|
|
|
|
|
|
// Allow anyone to read, write and execute in the temp directory
|
|
|
|
// so that the DIND setup that we use to update the playwright screenshots work without any issues.
|
|
|
|
await fse.chmod(tempDir, 0o757);
|
|
|
|
|
|
|
|
return {
|
|
|
|
configDir: tempDir,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export class MatrixAuthenticationService {
|
|
|
|
private readonly masDocker = new Docker();
|
|
|
|
private readonly postgresDocker = new PostgresDocker("mas");
|
|
|
|
private instance: ProxyInstance;
|
|
|
|
public port: number;
|
|
|
|
|
|
|
|
constructor(private context: BrowserContext) {}
|
|
|
|
|
|
|
|
async prepare(): Promise<{ port: number }> {
|
|
|
|
this.port = await getFreePort();
|
|
|
|
return { port: this.port };
|
|
|
|
}
|
|
|
|
|
|
|
|
async start(homeserver: HomeserverInstance, mailhog: MailhogInstance): Promise<ProxyInstance> {
|
|
|
|
console.log(new Date(), "Starting mas...");
|
|
|
|
|
|
|
|
if (!this.port) await this.prepare();
|
|
|
|
const port = this.port;
|
|
|
|
const { containerId: postgresId, ipAddress: postgresIp } = await this.postgresDocker.start();
|
|
|
|
const { configDir } = await cfgDirFromTemplate({
|
|
|
|
masPort: port.toString(),
|
|
|
|
postgresHost: postgresIp,
|
|
|
|
synapseUrl: homeserver.config.dockerUrl,
|
|
|
|
smtpPort: mailhog.smtpPort.toString(),
|
|
|
|
});
|
|
|
|
|
|
|
|
console.log(new Date(), "starting mas container...", TAG);
|
|
|
|
const containerId = await this.masDocker.run({
|
|
|
|
image: "ghcr.io/matrix-org/matrix-authentication-service:" + TAG,
|
|
|
|
containerName: "react-sdk-playwright-mas",
|
|
|
|
params: ["-p", `${port}:8080/tcp`, "-v", `${configDir}:/config`, "--entrypoint", "sh"],
|
|
|
|
cmd: [
|
|
|
|
"-c",
|
|
|
|
"mas-cli database migrate --config /config/config.yaml && " +
|
|
|
|
"mas-cli config sync --config /config/config.yaml && " +
|
|
|
|
"mas-cli server --config /config/config.yaml",
|
|
|
|
],
|
|
|
|
});
|
|
|
|
console.log(new Date(), "started!");
|
|
|
|
|
|
|
|
// Set up redirects
|
|
|
|
const baseUrl = `http://localhost:${port}`;
|
|
|
|
for (const path of [
|
|
|
|
"**/_matrix/client/*/login",
|
|
|
|
"**/_matrix/client/*/login/**",
|
|
|
|
"**/_matrix/client/*/logout",
|
|
|
|
"**/_matrix/client/*/refresh",
|
|
|
|
]) {
|
|
|
|
await this.context.route(path, async (route) => {
|
|
|
|
await route.continue({
|
|
|
|
url: new URL(route.request().url().split("/").slice(3).join("/"), baseUrl).href,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
this.instance = { containerId, postgresId, port, configDir };
|
|
|
|
return this.instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
async stop(testInfo: TestInfo): Promise<void> {
|
|
|
|
if (!this.instance) return; // nothing to stop
|
|
|
|
const id = this.instance.containerId;
|
|
|
|
const logPath = path.join("playwright", "logs", "matrix-authentication-service", id);
|
|
|
|
await fse.ensureDir(logPath);
|
|
|
|
await this.masDocker.persistLogsToFile({
|
|
|
|
stdoutFile: path.join(logPath, "stdout.log"),
|
|
|
|
stderrFile: path.join(logPath, "stderr.log"),
|
|
|
|
});
|
|
|
|
|
|
|
|
await this.masDocker.stop();
|
|
|
|
await this.postgresDocker.stop();
|
|
|
|
|
|
|
|
if (testInfo.status !== "passed") {
|
|
|
|
const logs = [path.join(logPath, "stdout.log"), path.join(logPath, "stderr.log")];
|
|
|
|
for (const path of logs) {
|
|
|
|
await testInfo.attach(`mas-${basename(path)}`, {
|
|
|
|
path,
|
|
|
|
contentType: "text/plain",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
await testInfo.attach("mas-config.yaml", {
|
|
|
|
path: path.join(this.instance.configDir, "config.yaml"),
|
|
|
|
contentType: "text/plain",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
await fse.remove(this.instance.configDir);
|
|
|
|
console.log(new Date(), "Stopped mas.");
|
|
|
|
}
|
|
|
|
}
|