mirror of https://github.com/vector-im/riot-web
Improvements around docker in Playwright (#12261)
* Extract Postgres Docker to its own class Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Don't specify docker `--rm` in CI as it makes debugging harder Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve docker commands and introspection Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove `HOST_DOCKER_INTERNAL` magic in favour of `host.containers.internal` Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Always pipe Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Re-add pipe flag to silence pg_isready and podman checks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>pull/28217/head
parent
dd5b7417be
commit
a9add4504f
|
@ -25,7 +25,7 @@ test.describe("Email Registration", async () => {
|
||||||
use({
|
use({
|
||||||
template: "email",
|
template: "email",
|
||||||
variables: {
|
variables: {
|
||||||
SMTP_HOST: "{{HOST_DOCKER_INTERNAL}}", // This will get replaced in synapseStart
|
SMTP_HOST: "host.containers.internal",
|
||||||
SMTP_PORT: mailhog.instance.smtpPort,
|
SMTP_PORT: mailhog.instance.smtpPort,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -19,6 +19,37 @@ import * as crypto from "crypto";
|
||||||
import * as childProcess from "child_process";
|
import * as childProcess from "child_process";
|
||||||
import * as fse from "fs-extra";
|
import * as fse from "fs-extra";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cmd - command to execute
|
||||||
|
* @param args - arguments to pass to executed command
|
||||||
|
* @param suppressOutput - whether to suppress the stdout and stderr resulting from this command.
|
||||||
|
* @return Promise which resolves to an object containing the string value of what was
|
||||||
|
* written to stdout and stderr by the executed command.
|
||||||
|
*/
|
||||||
|
const exec = (cmd: string, args: string[], suppressOutput = false): Promise<{ stdout: string; stderr: string }> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!suppressOutput) {
|
||||||
|
const log = ["Running command:", cmd, ...args, "\n"].join(" ");
|
||||||
|
// When in CI mode we combine reports from multiple runners into a single HTML report
|
||||||
|
// which has separate files for stdout and stderr, so we print the executed command to both
|
||||||
|
process.stdout.write(log);
|
||||||
|
if (process.env.CI) process.stderr.write(log);
|
||||||
|
}
|
||||||
|
const { stdout, stderr } = childProcess.execFile(cmd, args, { encoding: "utf8" }, (err, stdout, stderr) => {
|
||||||
|
if (err) reject(err);
|
||||||
|
resolve({ stdout, stderr });
|
||||||
|
if (!suppressOutput) {
|
||||||
|
process.stdout.write("\n");
|
||||||
|
if (process.env.CI) process.stderr.write("\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!suppressOutput) {
|
||||||
|
stdout.pipe(process.stdout);
|
||||||
|
stderr.pipe(process.stderr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export class Docker {
|
export class Docker {
|
||||||
public id: string;
|
public id: string;
|
||||||
|
|
||||||
|
@ -26,9 +57,10 @@ export class Docker {
|
||||||
const userInfo = os.userInfo();
|
const userInfo = os.userInfo();
|
||||||
const params = opts.params ?? [];
|
const params = opts.params ?? [];
|
||||||
|
|
||||||
if (params?.includes("-v") && userInfo.uid >= 0) {
|
const isPodman = await Docker.isPodman();
|
||||||
|
if (params.includes("-v") && userInfo.uid >= 0) {
|
||||||
// Run the docker container as our uid:gid to prevent problems with permissions.
|
// Run the docker container as our uid:gid to prevent problems with permissions.
|
||||||
if (await Docker.isPodman()) {
|
if (isPodman) {
|
||||||
// Note: this setup is for podman rootless containers.
|
// Note: this setup is for podman rootless containers.
|
||||||
|
|
||||||
// In podman, run as root in the container, which maps to the current
|
// In podman, run as root in the container, which maps to the current
|
||||||
|
@ -45,75 +77,57 @@ export class Docker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make host.containers.internal work to allow the container to talk to other services via host ports.
|
||||||
|
if (isPodman) {
|
||||||
|
params.push("--network");
|
||||||
|
params.push("slirp4netns:allow_host_loopback=true");
|
||||||
|
} else {
|
||||||
|
// Docker for Desktop includes a host-gateway mapping on host.docker.internal but to simplify the config
|
||||||
|
// we use the Podman variant host.containers.internal in all environments.
|
||||||
|
params.push("--add-host");
|
||||||
|
params.push("host.containers.internal:host-gateway");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provided we are not running in CI, add a `--rm` parameter.
|
||||||
|
// There is no need to remove containers in CI (since they are automatically removed anyway), and
|
||||||
|
// `--rm` means that if a container crashes this means its logs are wiped out.
|
||||||
|
if (!process.env.CI) params.unshift("--rm");
|
||||||
|
|
||||||
const args = [
|
const args = [
|
||||||
"run",
|
"run",
|
||||||
"--name",
|
"--name",
|
||||||
`${opts.containerName}-${crypto.randomBytes(4).toString("hex")}`,
|
`${opts.containerName}-${crypto.randomBytes(4).toString("hex")}`,
|
||||||
"-d",
|
"-d",
|
||||||
"--rm",
|
|
||||||
...params,
|
...params,
|
||||||
opts.image,
|
opts.image,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (opts.cmd) args.push(...opts.cmd);
|
if (opts.cmd) args.push(...opts.cmd);
|
||||||
|
|
||||||
this.id = await new Promise<string>((resolve, reject) => {
|
const { stdout } = await exec("docker", args);
|
||||||
childProcess.execFile("docker", args, (err, stdout) => {
|
this.id = stdout.trim();
|
||||||
if (err) reject(err);
|
|
||||||
resolve(stdout.trim());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): Promise<void> {
|
async stop(): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
try {
|
||||||
childProcess.execFile("docker", ["stop", this.id], (err) => {
|
await exec("docker", ["stop", this.id]);
|
||||||
if (err) reject(err);
|
} catch (err) {
|
||||||
resolve();
|
console.error(`Failed to stop docker container`, this.id, err);
|
||||||
});
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(params: string[]): Promise<void> {
|
/**
|
||||||
return new Promise<void>((resolve, reject) => {
|
* @param params - list of parameters to pass to `docker exec`
|
||||||
childProcess.execFile(
|
* @param suppressOutput - whether to suppress the stdout and stderr resulting from this command.
|
||||||
"docker",
|
*/
|
||||||
["exec", this.id, ...params],
|
async exec(params: string[], suppressOutput = true): Promise<void> {
|
||||||
{ encoding: "utf8" },
|
await exec("docker", ["exec", this.id, ...params], suppressOutput);
|
||||||
(err, stdout, stderr) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(stdout);
|
|
||||||
console.log(stderr);
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rm(): Promise<void> {
|
async getContainerIp(): Promise<string> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
const { stdout } = await exec("docker", ["inspect", "-f", "{{ .NetworkSettings.IPAddress }}", this.id]);
|
||||||
childProcess.execFile("docker", ["rm", this.id], (err) => {
|
return stdout.trim();
|
||||||
if (err) reject(err);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getContainerIp(): Promise<string> {
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
childProcess.execFile(
|
|
||||||
"docker",
|
|
||||||
["inspect", "-f", "{{ .NetworkSettings.IPAddress }}", this.id],
|
|
||||||
(err, stdout) => {
|
|
||||||
if (err) reject(err);
|
|
||||||
else resolve(stdout.trim());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async persistLogsToFile(args: { stdoutFile?: string; stderrFile?: string }): Promise<void> {
|
async persistLogsToFile(args: { stdoutFile?: string; stderrFile?: string }): Promise<void> {
|
||||||
|
@ -134,20 +148,8 @@ export class Docker {
|
||||||
* Detects whether the docker command is actually podman.
|
* Detects whether the docker command is actually podman.
|
||||||
* To do this, it looks for "podman" in the output of "docker --help".
|
* To do this, it looks for "podman" in the output of "docker --help".
|
||||||
*/
|
*/
|
||||||
static isPodman(): Promise<boolean> {
|
static async isPodman(): Promise<boolean> {
|
||||||
return new Promise<boolean>((resolve, reject) => {
|
const { stdout } = await exec("docker", ["--help"], true);
|
||||||
childProcess.execFile("docker", ["--help"], (err, stdout) => {
|
return stdout.toLowerCase().includes("podman");
|
||||||
if (err) reject(err);
|
|
||||||
else resolve(stdout.toLowerCase().includes("podman"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supply the right hostname to use to talk to the host machine. On Docker this
|
|
||||||
* is "host.docker.internal" and on Podman this is "host.containers.internal".
|
|
||||||
*/
|
|
||||||
static async hostnameOfHost(): Promise<"host.containers.internal" | "host.docker.internal"> {
|
|
||||||
return (await Docker.isPodman()) ? "host.containers.internal" : "host.docker.internal";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,6 @@ export class Dendrite extends Synapse implements Homeserver, HomeserverInstance
|
||||||
const dendriteId = await this.docker.run({
|
const dendriteId = await this.docker.run({
|
||||||
image: this.image,
|
image: this.image,
|
||||||
params: [
|
params: [
|
||||||
"--rm",
|
|
||||||
"-v",
|
"-v",
|
||||||
`${denCfg.configDir}:` + dockerConfigDir,
|
`${denCfg.configDir}:` + dockerConfigDir,
|
||||||
"-p",
|
"-p",
|
||||||
|
@ -140,7 +139,7 @@ async function cfgDirFromTemplate(
|
||||||
const docker = new Docker();
|
const docker = new Docker();
|
||||||
await docker.run({
|
await docker.run({
|
||||||
image: dendriteImage,
|
image: dendriteImage,
|
||||||
params: ["--rm", "--entrypoint=", "-v", `${tempDir}:/mnt`],
|
params: ["--entrypoint=", "-v", `${tempDir}:/mnt`],
|
||||||
containerName: `react-sdk-playwright-dendrite-keygen`,
|
containerName: `react-sdk-playwright-dendrite-keygen`,
|
||||||
cmd: ["/usr/bin/generate-keys", "-private-key", "/mnt/matrix_key.pem"],
|
cmd: ["/usr/bin/generate-keys", "-private-key", "/mnt/matrix_key.pem"],
|
||||||
});
|
});
|
||||||
|
|
|
@ -57,20 +57,9 @@ async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<Homes
|
||||||
if (opts.oAuthServerPort) {
|
if (opts.oAuthServerPort) {
|
||||||
hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort.toString());
|
hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort.toString());
|
||||||
}
|
}
|
||||||
hsYaml = hsYaml.replace(/{{HOST_DOCKER_INTERNAL}}/g, await Docker.hostnameOfHost());
|
|
||||||
if (opts.variables) {
|
if (opts.variables) {
|
||||||
let fetchedHostContainer: Awaited<ReturnType<typeof Docker.hostnameOfHost>> | null = null;
|
|
||||||
for (const key in opts.variables) {
|
for (const key in opts.variables) {
|
||||||
let value = String(opts.variables[key]);
|
hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), String(opts.variables[key]));
|
||||||
|
|
||||||
if (value === "{{HOST_DOCKER_INTERNAL}}") {
|
|
||||||
if (!fetchedHostContainer) {
|
|
||||||
fetchedHostContainer = await Docker.hostnameOfHost();
|
|
||||||
}
|
|
||||||
value = fetchedHostContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,26 +95,13 @@ export class Synapse implements Homeserver, HomeserverInstance {
|
||||||
* Start a synapse instance: the template must be the name of
|
* Start a synapse instance: the template must be the name of
|
||||||
* one of the templates in the playwright/plugins/synapsedocker/templates
|
* one of the templates in the playwright/plugins/synapsedocker/templates
|
||||||
* directory.
|
* directory.
|
||||||
*
|
|
||||||
* Any value in `opts.variables` that is set to `{{HOST_DOCKER_INTERNAL}}'
|
|
||||||
* will be replaced with 'host.docker.internal' (if we are on Docker) or
|
|
||||||
* 'host.containers.internal' if we are on Podman.
|
|
||||||
*/
|
*/
|
||||||
public async start(opts: StartHomeserverOpts): Promise<HomeserverInstance> {
|
public async start(opts: StartHomeserverOpts): Promise<HomeserverInstance> {
|
||||||
if (this.config) await this.stop();
|
if (this.config) await this.stop();
|
||||||
|
|
||||||
const synCfg = await cfgDirFromTemplate(opts);
|
const synCfg = await cfgDirFromTemplate(opts);
|
||||||
console.log(`Starting synapse with config dir ${synCfg.configDir}...`);
|
console.log(`Starting synapse with config dir ${synCfg.configDir}...`);
|
||||||
const dockerSynapseParams = ["--rm", "-v", `${synCfg.configDir}:/data`, "-p", `${synCfg.port}:8008/tcp`];
|
const dockerSynapseParams = ["-v", `${synCfg.configDir}:/data`, "-p", `${synCfg.port}:8008/tcp`];
|
||||||
if (await Docker.isPodman()) {
|
|
||||||
// Make host.containers.internal work to allow Synapse to talk to the test OIDC server.
|
|
||||||
dockerSynapseParams.push("--network");
|
|
||||||
dockerSynapseParams.push("slirp4netns:allow_host_loopback=true");
|
|
||||||
} else {
|
|
||||||
// Make host.docker.internal work to allow Synapse to talk to the test OIDC server.
|
|
||||||
dockerSynapseParams.push("--add-host");
|
|
||||||
dockerSynapseParams.push("host.docker.internal:host-gateway");
|
|
||||||
}
|
|
||||||
const synapseId = await this.docker.run({
|
const synapseId = await this.docker.run({
|
||||||
image: "matrixdotorg/synapse:develop",
|
image: "matrixdotorg/synapse:develop",
|
||||||
containerName: `react-sdk-playwright-synapse`,
|
containerName: `react-sdk-playwright-synapse`,
|
||||||
|
|
|
@ -81,10 +81,8 @@ oidc_providers:
|
||||||
issuer: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth"
|
issuer: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth"
|
||||||
authorization_endpoint: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth/auth.html"
|
authorization_endpoint: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth/auth.html"
|
||||||
# the token endpoint receives requests from synapse, rather than the webapp, so needs to escape the docker container.
|
# the token endpoint receives requests from synapse, rather than the webapp, so needs to escape the docker container.
|
||||||
# Hence, HOST_DOCKER_INTERNAL rather than localhost. This is set to
|
token_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/token"
|
||||||
# host.docker.internal on Docker and host.containers.internal on Podman.
|
userinfo_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/userinfo"
|
||||||
token_endpoint: "http://{{HOST_DOCKER_INTERNAL}}:{{OAUTH_SERVER_PORT}}/oauth/token"
|
|
||||||
userinfo_endpoint: "http://{{HOST_DOCKER_INTERNAL}}:{{OAUTH_SERVER_PORT}}/oauth/userinfo"
|
|
||||||
client_id: "synapse"
|
client_id: "synapse"
|
||||||
discover: false
|
discover: false
|
||||||
scopes: ["profile"]
|
scopes: ["profile"]
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class MailHogServer {
|
||||||
const containerId = await this.docker.run({
|
const containerId = await this.docker.run({
|
||||||
image: "mailhog/mailhog:latest",
|
image: "mailhog/mailhog:latest",
|
||||||
containerName: `react-sdk-playwright-mailhog`,
|
containerName: `react-sdk-playwright-mailhog`,
|
||||||
params: ["--rm", "-p", `${smtpPort}:1025/tcp`, "-p", `${httpPort}:8025/tcp`],
|
params: ["-p", `${smtpPort}:1025/tcp`, "-p", `${httpPort}:8025/tcp`],
|
||||||
});
|
});
|
||||||
console.log(`Started mailhog on ports smtp=${smtpPort} http=${httpPort}.`);
|
console.log(`Started mailhog on ports smtp=${smtpPort} http=${httpPort}.`);
|
||||||
const host = await this.docker.getContainerIp();
|
const host = await this.docker.getContainerIp();
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Docker } from "../docker";
|
||||||
|
|
||||||
|
export const PG_PASSWORD = "p4S5w0rD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to manage a postgres database in docker
|
||||||
|
*/
|
||||||
|
export class PostgresDocker extends Docker {
|
||||||
|
/**
|
||||||
|
* @param key an opaque string to use when naming the docker containers instantiated by this class
|
||||||
|
*/
|
||||||
|
public constructor(private key: string) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async waitForPostgresReady(): Promise<void> {
|
||||||
|
const waitTimeMillis = 30000;
|
||||||
|
const startTime = new Date().getTime();
|
||||||
|
let lastErr: Error | null = null;
|
||||||
|
while (new Date().getTime() - startTime < waitTimeMillis) {
|
||||||
|
try {
|
||||||
|
await this.exec(["pg_isready", "-U", "postgres"], true);
|
||||||
|
lastErr = null;
|
||||||
|
break;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("pg_isready: failed");
|
||||||
|
lastErr = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastErr) {
|
||||||
|
console.log("rethrowing");
|
||||||
|
throw lastErr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start(): Promise<{
|
||||||
|
ipAddress: string;
|
||||||
|
containerId: string;
|
||||||
|
}> {
|
||||||
|
console.log(new Date(), "starting postgres container");
|
||||||
|
const containerId = await this.run({
|
||||||
|
image: "postgres",
|
||||||
|
containerName: `react-sdk-playwright-postgres-${this.key}`,
|
||||||
|
params: ["--tmpfs=/pgtmpfs", "-e", "PGDATA=/pgtmpfs", "-e", `POSTGRES_PASSWORD=${PG_PASSWORD}`],
|
||||||
|
// Optimise for testing - https://www.postgresql.org/docs/current/non-durability.html
|
||||||
|
cmd: ["-c", `fsync=off`, "-c", `synchronous_commit=off`, "-c", `full_page_writes=off`],
|
||||||
|
});
|
||||||
|
|
||||||
|
const ipAddress = await this.getContainerIp();
|
||||||
|
console.log(new Date(), "postgres container up");
|
||||||
|
|
||||||
|
await this.waitForPostgresReady();
|
||||||
|
console.log(new Date(), "postgres container ready");
|
||||||
|
return { ipAddress, containerId };
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,10 +16,10 @@ limitations under the License.
|
||||||
|
|
||||||
import { getFreePort } from "../utils/port";
|
import { getFreePort } from "../utils/port";
|
||||||
import { Docker } from "../docker";
|
import { Docker } from "../docker";
|
||||||
|
import { PG_PASSWORD, PostgresDocker } from "../postgres";
|
||||||
|
|
||||||
// Docker tag to use for `ghcr.io/matrix-org/sliding-sync` image.
|
// Docker tag to use for `ghcr.io/matrix-org/sliding-sync` image.
|
||||||
const SLIDING_SYNC_PROXY_TAG = "v0.99.3";
|
const SLIDING_SYNC_PROXY_TAG = "v0.99.3";
|
||||||
const PG_PASSWORD = "p4S5w0rD";
|
|
||||||
|
|
||||||
export interface ProxyInstance {
|
export interface ProxyInstance {
|
||||||
containerId: string;
|
containerId: string;
|
||||||
|
@ -28,45 +28,16 @@ export interface ProxyInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SlidingSyncProxy {
|
export class SlidingSyncProxy {
|
||||||
private readonly postgresDocker = new Docker();
|
|
||||||
private readonly proxyDocker = new Docker();
|
private readonly proxyDocker = new Docker();
|
||||||
|
private readonly postgresDocker = new PostgresDocker("sliding-sync");
|
||||||
private instance: ProxyInstance;
|
private instance: ProxyInstance;
|
||||||
|
|
||||||
constructor(private synapseIp: string) {}
|
constructor(private synapseIp: string) {}
|
||||||
|
|
||||||
private async waitForPostgresReady(): Promise<void> {
|
|
||||||
const waitTimeMillis = 30000;
|
|
||||||
const startTime = new Date().getTime();
|
|
||||||
let lastErr: Error | null = null;
|
|
||||||
while (new Date().getTime() - startTime < waitTimeMillis) {
|
|
||||||
try {
|
|
||||||
await this.postgresDocker.exec(["pg_isready", "-U", "postgres"]);
|
|
||||||
lastErr = null;
|
|
||||||
break;
|
|
||||||
} catch (err) {
|
|
||||||
console.log("pg_isready: failed");
|
|
||||||
lastErr = err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lastErr) {
|
|
||||||
console.log("rethrowing");
|
|
||||||
throw lastErr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async start(): Promise<ProxyInstance> {
|
async start(): Promise<ProxyInstance> {
|
||||||
console.log(new Date(), "Starting sliding sync proxy...");
|
console.log(new Date(), "Starting sliding sync proxy...");
|
||||||
|
|
||||||
const postgresId = await this.postgresDocker.run({
|
const { ipAddress: postgresIp, containerId: postgresId } = await this.postgresDocker.start();
|
||||||
image: "postgres",
|
|
||||||
containerName: "react-sdk-playwright-sliding-sync-postgres",
|
|
||||||
params: ["--rm", "-e", `POSTGRES_PASSWORD=${PG_PASSWORD}`],
|
|
||||||
});
|
|
||||||
|
|
||||||
const postgresIp = await this.postgresDocker.getContainerIp();
|
|
||||||
console.log(new Date(), "postgres container up");
|
|
||||||
|
|
||||||
await this.waitForPostgresReady();
|
|
||||||
|
|
||||||
const port = await getFreePort();
|
const port = await getFreePort();
|
||||||
console.log(new Date(), "starting proxy container...", SLIDING_SYNC_PROXY_TAG);
|
console.log(new Date(), "starting proxy container...", SLIDING_SYNC_PROXY_TAG);
|
||||||
|
@ -74,7 +45,6 @@ export class SlidingSyncProxy {
|
||||||
image: "ghcr.io/matrix-org/sliding-sync:" + SLIDING_SYNC_PROXY_TAG,
|
image: "ghcr.io/matrix-org/sliding-sync:" + SLIDING_SYNC_PROXY_TAG,
|
||||||
containerName: "react-sdk-playwright-sliding-sync-proxy",
|
containerName: "react-sdk-playwright-sliding-sync-proxy",
|
||||||
params: [
|
params: [
|
||||||
"--rm",
|
|
||||||
"-p",
|
"-p",
|
||||||
`${port}:8008/tcp`,
|
`${port}:8008/tcp`,
|
||||||
"-e",
|
"-e",
|
||||||
|
|
Loading…
Reference in New Issue