Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Improvements around docker in Playwright #12261

Merged
merged 10 commits into from
Feb 20, 2024
2 changes: 1 addition & 1 deletion playwright/e2e/register/email.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test.describe("Email Registration", async () => {
use({
template: "email",
variables: {
SMTP_HOST: "{{HOST_DOCKER_INTERNAL}}", // This will get replaced in synapseStart
SMTP_HOST: "host.containers.internal",
SMTP_PORT: mailhog.instance.smtpPort,
},
}),
Expand Down
23 changes: 13 additions & 10 deletions playwright/plugins/docker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ export class Docker {
const userInfo = os.userInfo();
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.
if (await Docker.isPodman()) {
if (isPodman) {
// Note: this setup is for podman rootless containers.

// In podman, run as root in the container, which maps to the current
Expand All @@ -74,6 +75,16 @@ export class Docker {
}
}

if (isPodman) {
// Make host.containers.internal work to allow the container to talk to other services via host ports.
params.push("--network");
params.push("slirp4netns:allow_host_loopback=true");
} else {
// Make host.docker.internal work to allow the container to talk to other services via host ports.
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.
Expand Down Expand Up @@ -139,12 +150,4 @@ export class Docker {
const { stdout } = await exec("docker", ["--help"], false);
return 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";
}
}
26 changes: 1 addition & 25 deletions playwright/plugins/homeserver/synapse/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,9 @@ async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<Homes
if (opts.oAuthServerPort) {
hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort.toString());
}
hsYaml = hsYaml.replace(/{{HOST_DOCKER_INTERNAL}}/g, await Docker.hostnameOfHost());
if (opts.variables) {
let fetchedHostContainer: Awaited<ReturnType<typeof Docker.hostnameOfHost>> | null = null;
for (const key in opts.variables) {
let value = 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);
hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), String(opts.variables[key]));
}
}

Expand Down Expand Up @@ -106,26 +95,13 @@ export class Synapse implements Homeserver, HomeserverInstance {
* Start a synapse instance: the template must be the name of
* one of the templates in the playwright/plugins/synapsedocker/templates
* 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> {
if (this.config) await this.stop();

const synCfg = await cfgDirFromTemplate(opts);
console.log(`Starting synapse with config dir ${synCfg.configDir}...`);
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({
image: "matrixdotorg/synapse:develop",
containerName: `react-sdk-playwright-synapse`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,8 @@ oidc_providers:
issuer: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth"
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.
# Hence, HOST_DOCKER_INTERNAL rather than localhost. This is set to
# host.docker.internal on Docker and host.containers.internal on Podman.
token_endpoint: "http://{{HOST_DOCKER_INTERNAL}}:{{OAUTH_SERVER_PORT}}/oauth/token"
userinfo_endpoint: "http://{{HOST_DOCKER_INTERNAL}}:{{OAUTH_SERVER_PORT}}/oauth/userinfo"
token_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/token"
userinfo_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/userinfo"
client_id: "synapse"
discover: false
scopes: ["profile"]
Expand Down
Loading