diff --git a/Dockerfile b/Dockerfile index 5e737881..6b3da65a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update \ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ && apt-get update \ - && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-khmeros fonts-kacst fonts-freefont-ttf libxss1 \ + && apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-khmeros fonts-kacst fonts-freefont-ttf libxss1 nginx \ --no-install-recommends \ && rm -rf /var/lib/apt/lists/* diff --git a/src/action.ts b/src/action.ts index 968dfdb3..d68383fd 100644 --- a/src/action.ts +++ b/src/action.ts @@ -19,6 +19,7 @@ import { getBaseAndHeadCommitShas } from "./utils/get-base-and-head-commit-shas" import { getCodeChangeEvent } from "./utils/get-code-change-event"; import { getInputs } from "./utils/get-inputs"; import { initLogger, setLogLevel, shortSha } from "./utils/logger.utils"; +import { spinUpProxyIfNeeded } from "./utils/proxy"; import { ResultsReporter } from "./utils/results-reporter"; import { waitForDeploymentUrl } from "./utils/wait-for-deployment-url"; @@ -144,6 +145,7 @@ export const runMeticulousTestsAction = async (): Promise => { : appUrl; if (urlToTestAgainst != null) { + spinUpProxyIfNeeded(urlToTestAgainst, logger); await throwIfCannotConnectToOrigin(urlToTestAgainst); } diff --git a/src/utils/__tests__/get-inputs.spec.ts b/src/utils/__tests__/get-inputs.spec.ts index fb6dc77a..ae9c21f2 100644 --- a/src/utils/__tests__/get-inputs.spec.ts +++ b/src/utils/__tests__/get-inputs.spec.ts @@ -78,7 +78,7 @@ describe("getInputs", () => { expect(getInputs()).toEqual({ ...EXPECTED_DEFAULT_VALUES, - appUrl: "https://example.com/", + appUrl: "https://example.com", localhostAliases: "app1.local,app2.local", parallelTasks: 5, testsFile: "tests.json", @@ -125,16 +125,6 @@ describe("getInputs", () => { expect(() => getInputs()).toThrowError(); }); - it("handles rewriting localhost urls to the docker bridge IP", () => { - setupDefaultEnvVars(); - process.env.APP_URL = "https://localhost/app"; - - expect(getInputs()).toEqual({ - ...EXPECTED_DEFAULT_VALUES, - appUrl: "https://172.17.0.1/app", - }); - }); - const setupDefaultEnvVars = () => { process.env.API_TOKEN = "mock-api-token"; process.env.GITHUB_TOKEN = "mock-github-token"; diff --git a/src/utils/add-localhost-aliases.ts b/src/utils/add-localhost-aliases.ts index 8572b87a..5f366e19 100644 --- a/src/utils/add-localhost-aliases.ts +++ b/src/utils/add-localhost-aliases.ts @@ -2,7 +2,7 @@ import { resolve4 } from "dns/promises"; import { appendFile } from "fs/promises"; import { METICULOUS_LOGGER_NAME } from "@alwaysmeticulous/common"; import log from "loglevel"; -import { DOCKER_BRIDGE_NETWORK_GATEWAY } from "./get-inputs"; +import { DOCKER_BRIDGE_NETWORK_GATEWAY } from "./proxy"; const HOSTS_FILE = "/etc/hosts"; diff --git a/src/utils/check-connection.ts b/src/utils/check-connection.ts index 07d258fb..69651c06 100644 --- a/src/utils/check-connection.ts +++ b/src/utils/check-connection.ts @@ -1,5 +1,4 @@ import { Socket } from "net"; -import { DOCKER_BRIDGE_NETWORK_GATEWAY } from "./get-inputs"; export const throwIfCannotConnectToOrigin = async (url: string) => { const { hostname, port, protocol, origin } = new URL(url); @@ -8,18 +7,10 @@ export const throwIfCannotConnectToOrigin = async (url: string) => { port != null && port != "" ? Number(port) : defaultPortForProtocol; const connectionAccepted = await canConnectTo(hostname, portNumber); if (!connectionAccepted) { - const rewrittenHostname = hostname.replace( - DOCKER_BRIDGE_NETWORK_GATEWAY, - "127.0.0.1" - ); - const rewrittenOrigin = origin.replace( - DOCKER_BRIDGE_NETWORK_GATEWAY, - "127.0.0.1" - ); throw new Error( - `Could not connect to '${rewrittenHostname}:${portNumber}'. Please check:\n\n` + - `1. The server running at '${rewrittenOrigin}' has fully started by the time the Meticulous action starts. You may need to add a 'sleep 30' after starting the server to ensure that this is the case.\n` + - `2. The server running at '${rewrittenOrigin}' is using tcp instead of tcp6. You can use 'netstat -tulpen' to see what addresses and ports it is bound to.\n\n` + `Could not connect to '${hostname}:${portNumber}'. Please check:\n\n` + + `1. The server running at '${origin}' has fully started by the time the Meticulous action starts. You may need to add a 'sleep 30' after starting the server to ensure that this is the case.\n` + + `2. The server running at '${origin}' is using tcp instead of tcp6. You can use 'netstat -tulpen' to see what addresses and ports it is bound to.\n\n` ); } }; diff --git a/src/utils/get-inputs.ts b/src/utils/get-inputs.ts index fce9ea6d..937e052c 100644 --- a/src/utils/get-inputs.ts +++ b/src/utils/get-inputs.ts @@ -12,7 +12,7 @@ export const getInputs = () => { required: true, type: "string", }); - const appUrl_ = getInputFromEnv({ + const appUrl = getInputFromEnv({ name: "app-url", required: false, type: "string", @@ -63,7 +63,7 @@ export const getInputs = () => { type: "string-array", }); - if (appUrl_ != null && appUrl_ != "" && useDeploymentUrl === true) { + if (appUrl != null && appUrl != "" && useDeploymentUrl === true) { throw new Error("Cannot use both app-url and use-deployment-url"); } @@ -80,8 +80,6 @@ export const getInputs = () => { ); } - const appUrl = appUrl_ ? handleLocalhostUrl(appUrl_) : appUrl_; - return { apiToken, githubToken, @@ -97,18 +95,3 @@ export const getInputs = () => { allowedEnvironments, }; }; - -export const DOCKER_BRIDGE_NETWORK_GATEWAY = "172.17.0.1"; - -// Swaps "localhost" with the IP address of the Docker host -const handleLocalhostUrl = (appUrl: string): string => { - try { - const url = new URL(appUrl); - if (url.hostname === "localhost" || url.hostname === "127.0.0.1") { - url.hostname = DOCKER_BRIDGE_NETWORK_GATEWAY; - } - return url.toString(); - } catch (error) { - return appUrl; - } -}; diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts new file mode 100644 index 00000000..eef750d5 --- /dev/null +++ b/src/utils/proxy.ts @@ -0,0 +1,41 @@ +import { execSync } from "child_process"; +import { mkdirSync, writeFileSync } from "fs"; +import * as Sentry from "@sentry/node"; +import { Logger } from "loglevel"; + +export const DOCKER_BRIDGE_NETWORK_GATEWAY = "172.17.0.1"; + +// We are running in a docker container and need to proxy to localhost. +// We previously just rewrote the app URL to point to the host IP, but +// this breaks some sites that behave specially on localhost (e.g. +// allowing auth to go ahead with HTTPS). +export const spinUpProxyIfNeeded = (appUrl: string, logger: Logger): void => { + try { + const url = new URL(appUrl); + if (url.hostname === "localhost" || url.hostname === "127.0.0.1") { + const defaultPortForProtocol = url.protocol === "https:" ? 443 : 80; + const port = url.port || defaultPortForProtocol; + const nginxConfig = ` + events {} + http { + server { + listen ${port}; + server_name localhost; + location / { + proxy_pass http://${DOCKER_BRIDGE_NETWORK_GATEWAY}:${port}; + } + } + } + `; + mkdirSync("/etc/nginx", { recursive: true }); + writeFileSync("/etc/nginx/nginx.conf", nginxConfig); + execSync("service nginx restart"); + logger.info( + `Successfully set up a proxy to host machine on port ${port}` + ); + } + } catch (error) { + Sentry.captureException(error); + logger.error("Error while spinning up proxy", error); + } +};