diff --git a/cypress/e2e/register/email.spec.ts b/cypress/e2e/register/email.spec.ts index a93c05c6ee..988cee9ff2 100644 --- a/cypress/e2e/register/email.spec.ts +++ b/cypress/e2e/register/email.spec.ts @@ -29,7 +29,7 @@ describe("Email Registration", () => { cy.startHomeserver({ template: "email", variables: { - SMTP_HOST: "host.docker.internal", + SMTP_HOST: "{{HOST_DOCKER_INTERNAL}}", // This will get replaced in synapseStart SMTP_PORT: _mailhog.instance.smtpPort, }, }).then((_homeserver) => { diff --git a/cypress/plugins/docker/index.ts b/cypress/plugins/docker/index.ts index 66bab0b853..4c2da5f645 100644 --- a/cypress/plugins/docker/index.ts +++ b/cypress/plugins/docker/index.ts @@ -156,6 +156,14 @@ export function isPodman(): Promise { }); } +/** + * 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". + */ +export async function hostContainerName() { + return (await isPodman()) ? "host.containers.internal" : "host.docker.internal"; +} + /** * @type {Cypress.PluginConfig} */ diff --git a/cypress/plugins/synapsedocker/index.ts b/cypress/plugins/synapsedocker/index.ts index a802c03939..7c278610cc 100644 --- a/cypress/plugins/synapsedocker/index.ts +++ b/cypress/plugins/synapsedocker/index.ts @@ -24,7 +24,7 @@ import * as fse from "fs-extra"; import PluginEvents = Cypress.PluginEvents; import PluginConfigOptions = Cypress.PluginConfigOptions; import { getFreePort } from "../utils/port"; -import { dockerExec, dockerLogs, dockerRun, dockerStop } from "../docker"; +import { dockerExec, dockerLogs, dockerRun, dockerStop, hostContainerName, isPodman } from "../docker"; import { HomeserverConfig, HomeserverInstance } from "../utils/homeserver"; import { StartHomeserverOpts } from "../../support/homeserver"; @@ -58,27 +58,41 @@ async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise ${outputHomeserver}`); + let hsYaml = await fse.readFile(templateHomeserver, "utf8"); hsYaml = hsYaml.replace(/{{REGISTRATION_SECRET}}/g, registrationSecret); hsYaml = hsYaml.replace(/{{MACAROON_SECRET_KEY}}/g, macaroonSecret); hsYaml = hsYaml.replace(/{{FORM_SECRET}}/g, formSecret); hsYaml = hsYaml.replace(/{{PUBLIC_BASEURL}}/g, baseUrl); hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort?.toString()); + hsYaml = hsYaml.replace(/{{HOST_DOCKER_INTERNAL}}/g, await hostContainerName()); if (opts.variables) { + let fetchedHostContainer = null; for (const key in opts.variables) { - hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), String(opts.variables[key])); + let value = String(opts.variables[key]); + + if (value === "{{HOST_DOCKER_INTERNAL}}") { + if (!fetchedHostContainer) { + fetchedHostContainer = await hostContainerName(); + } + value = fetchedHostContainer; + } + + hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), value); } } - await fse.writeFile(path.join(tempDir, "homeserver.yaml"), hsYaml); + await fse.writeFile(outputHomeserver, hsYaml); // now generate a signing key (we could use synapse's config generation for // this, or we could just do this...) // NB. This assumes the homeserver.yaml specifies the key in this location const signingKey = randB64Bytes(32); - console.log(`Gen ${path.join(templateDir, "localhost.signing.key")}`); - await fse.writeFile(path.join(tempDir, "localhost.signing.key"), `ed25519 x ${signingKey}`); + const outputSigningKey = path.join(tempDir, "localhost.signing.key"); + console.log(`Gen -> ${outputSigningKey}`); + await fse.writeFile(outputSigningKey, `ed25519 x ${signingKey}`); return { port, @@ -88,27 +102,38 @@ async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise { const synCfg = await cfgDirFromTemplate(opts); console.log(`Starting synapse with config dir ${synCfg.configDir}...`); + const dockerSynapseParams = ["--rm", "-v", `${synCfg.configDir}:/data`, "-p", `${synCfg.port}:8008/tcp`]; + + if (await 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 dockerRun({ image: "matrixdotorg/synapse:develop", containerName: `react-sdk-cypress-synapse`, - params: [ - "--rm", - "-v", - `${synCfg.configDir}:/data`, - "-p", - `${synCfg.port}:8008/tcp`, - // make host.docker.internal work to allow Synapse to talk to the test OIDC server - "--add-host", - "host.docker.internal:host-gateway", - ], + params: dockerSynapseParams, cmd: ["run"], }); diff --git a/cypress/plugins/synapsedocker/templates/default/homeserver.yaml b/cypress/plugins/synapsedocker/templates/default/homeserver.yaml index a866d4b5be..e51ac1918f 100644 --- a/cypress/plugins/synapsedocker/templates/default/homeserver.yaml +++ b/cypress/plugins/synapsedocker/templates/default/homeserver.yaml @@ -81,9 +81,10 @@ 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. - token_endpoint: "http://host.docker.internal:{{OAUTH_SERVER_PORT}}/oauth/token" - userinfo_endpoint: "http://host.docker.internal:{{OAUTH_SERVER_PORT}}/oauth/userinfo" + # 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" client_id: "synapse" discover: false scopes: ["profile"] diff --git a/cypress/support/homeserver.ts b/cypress/support/homeserver.ts index 9f5e46efb0..2e8a309a65 100644 --- a/cypress/support/homeserver.ts +++ b/cypress/support/homeserver.ts @@ -39,15 +39,22 @@ declare global { interface Chainable { /** * Start a homeserver instance with a given config template. + * * @param opts: either the template path (within cypress/plugins/{homeserver}docker/template/), or * an options object + * + * If any of opts.variables has the special value + * '{{HOST_DOCKER_INTERNAL}}', it will be replaced by + * 'host.docker.interal' if we are on Docker, or + * 'host.containers.internal' on Podman. */ startHomeserver(opts: string | StartHomeserverOpts): Chainable; /** * Custom command wrapping task:{homeserver}Stop whilst preventing uncaught exceptions * for if Homeserver stopping races with the app's background sync loop. - * @param homeserver the homeserver instance returned by start{Homeserver} + * + * @param homeserver the homeserver instance returned by {homeserver}Start (e.g. synapseStart). */ stopHomeserver(homeserver: HomeserverInstance): Chainable;