From 85c59c0350f87526566e4a071411e8c3d955b29c Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 11 Jun 2024 17:41:48 +0100 Subject: [PATCH] Changing tactic to relying on stable container names to prevent duplication. --- .../src/integrations/tests/utils/index.ts | 49 ++++++++++++------- .../src/integrations/tests/utils/mssql.ts | 3 ++ .../src/integrations/tests/utils/mysql.ts | 3 ++ .../src/integrations/tests/utils/postgres.ts | 3 ++ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index d9a2960848..afbf4310a8 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -9,6 +9,7 @@ import { testContainerUtils } from "@budibase/backend-core/tests" import lockfile from "proper-lockfile" import path from "path" import fs from "fs" +import _ from "lodash" export type DatasourceProvider = () => Promise @@ -68,28 +69,38 @@ export async function rawQuery(ds: Datasource, sql: string): Promise { } export async function startContainer(container: GenericContainer) { - container = container.withReuse().withLabels({ "com.budibase": "true" }) - - // If two tests try to spin up the same container at the same time, there's a - // possibility that two containers of the same type will be started. To avoid - // this, we use a filesystem lock to ensure that only one container of a given - // type is started at a time. const imageName = (container as any).imageName.string as string - const lockPath = path.resolve( - __dirname, - `${imageName.replaceAll("/", "-")}.lock` - ) + const key = imageName.replaceAll("/", "-").replaceAll(":", "-") - const unlock = await lockfile.lock(lockPath, { - retries: 10, - realpath: false, - }) + container = container + .withReuse() + .withLabels({ "com.budibase": "true" }) + .withName(key) - let startedContainer: StartedTestContainer - try { - startedContainer = await container.start() - } finally { - await unlock() + let startedContainer: StartedTestContainer | undefined = undefined + let lastError = undefined + for (let i = 0; i < 10; i++) { + try { + // container.start() is not an idempotent operation, calling `start` + // modifies the internal state of a GenericContainer instance such that + // the hash it uses to determine reuse changes. We need to clone the + // container before calling start to ensure that we're using the same + // reuse hash every time. + const containerCopy = _.cloneDeep(container) + startedContainer = await containerCopy.start() + lastError = undefined + break + } catch (e: any) { + lastError = e + await new Promise(resolve => setTimeout(resolve, 1000)) + } + } + + if (!startedContainer) { + if (lastError) { + throw lastError + } + throw new Error(`failed to start container: ${imageName}`) } const info = testContainerUtils.getContainerById(startedContainer.getId()) diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index 647f461272..57c5fe8049 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -29,6 +29,9 @@ export async function getDatasource(): Promise { } const port = (await ports).find(x => x.container === 1433)?.host + if (!port) { + throw new Error("SQL Server port not found") + } const datasource: Datasource = { type: "datasource_plus", diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index a78833e1de..560d6bb2d4 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -38,6 +38,9 @@ export async function getDatasource(): Promise { } const port = (await ports).find(x => x.container === 3306)?.host + if (!port) { + throw new Error("MySQL port not found") + } const datasource: Datasource = { type: "datasource_plus", diff --git a/packages/server/src/integrations/tests/utils/postgres.ts b/packages/server/src/integrations/tests/utils/postgres.ts index 4191b107e9..8c0cd886e8 100644 --- a/packages/server/src/integrations/tests/utils/postgres.ts +++ b/packages/server/src/integrations/tests/utils/postgres.ts @@ -21,6 +21,9 @@ export async function getDatasource(): Promise { } const port = (await ports).find(x => x.container === 5432)?.host + if (!port) { + throw new Error("Postgres port not found") + } const datasource: Datasource = { type: "datasource_plus",