From fa50733e3cce19035c170656442be7cbba6493d4 Mon Sep 17 00:00:00 2001 From: Alexis SIMON Date: Thu, 12 Jun 2025 15:19:11 +0200 Subject: [PATCH 1/2] Add support for custom client options in DockerComposeEnvironment --- docs/features/compose.md | 11 ++++++++++ .../compose/default-compose-options.ts | 1 + .../clients/compose/types.ts | 2 ++ .../docker-compose-environment.ts | 21 ++++++++++++++++--- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/docs/features/compose.md b/docs/features/compose.md index 669a1899b..85aedbebd 100644 --- a/docs/features/compose.md +++ b/docs/features/compose.md @@ -142,6 +142,17 @@ const environment = await new DockerComposeEnvironment(composeFilePath, composeF .up(); ``` +### With custom client options + +See [docker-compose](https://github.com/PDMLab/docker-compose/) library. + +```javascript +const environment = await new DockerComposeEnvironment(composeFilePath, composeFile) + .withClientOptions({ executable: { standalone: true, executablePath: "/path/to/docker-compose" } }) + .up(); +``` + + ## Downing a Docker compose environment Testcontainers by default will not wait until the environment has downed. It will simply issue the down command and return immediately. This is to save time when running tests. diff --git a/packages/testcontainers/src/container-runtime/clients/compose/default-compose-options.ts b/packages/testcontainers/src/container-runtime/clients/compose/default-compose-options.ts index fafa25cb4..4c3240991 100644 --- a/packages/testcontainers/src/container-runtime/clients/compose/default-compose-options.ts +++ b/packages/testcontainers/src/container-runtime/clients/compose/default-compose-options.ts @@ -29,5 +29,6 @@ export function defaultComposeOptions( COMPOSE_PROJECT_NAME: options.projectName, ...{ ...environment, ...options.environment }, }, + executable: options.executable, }; } diff --git a/packages/testcontainers/src/container-runtime/clients/compose/types.ts b/packages/testcontainers/src/container-runtime/clients/compose/types.ts index 747fe0a43..985807e80 100644 --- a/packages/testcontainers/src/container-runtime/clients/compose/types.ts +++ b/packages/testcontainers/src/container-runtime/clients/compose/types.ts @@ -1,3 +1,4 @@ +import { IDockerComposeExecutableOptions } from "docker-compose"; import { Logger } from "../../../common"; export type ComposeOptions = { @@ -8,6 +9,7 @@ export type ComposeOptions = { composeOptions?: string[]; environment?: NodeJS.ProcessEnv; logger?: Logger; + executable?: IDockerComposeExecutableOptions; }; export type ComposeDownOptions = { diff --git a/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts b/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts index 252998ed2..40273b48b 100644 --- a/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts +++ b/packages/testcontainers/src/docker-compose-environment/docker-compose-environment.ts @@ -1,6 +1,6 @@ import { ContainerInfo } from "dockerode"; import { containerLog, log, RandomUuid, Uuid } from "../common"; -import { getContainerRuntimeClient, parseComposeContainerName } from "../container-runtime"; +import { ComposeOptions, getContainerRuntimeClient, parseComposeContainerName } from "../container-runtime"; import { StartedGenericContainer } from "../generic-container/started-generic-container"; import { getReaper } from "../reaper/reaper"; import { Environment } from "../types"; @@ -26,6 +26,7 @@ export class DockerComposeEnvironment { private defaultWaitStrategy: WaitStrategy = Wait.forListeningPorts(); private waitStrategy: { [containerName: string]: WaitStrategy } = {}; private startupTimeoutMs?: number; + private clientOptions: Partial = {}; constructor(composeFilePath: string, composeFiles: string | string[], uuid: Uuid = new RandomUuid()) { this.composeFilePath = composeFilePath; @@ -84,19 +85,33 @@ export class DockerComposeEnvironment { return this; } + public withClientOptions( + options: Partial> + ): this { + this.clientOptions = { ...this.clientOptions, ...options }; + return this; + } + public async up(services?: Array): Promise { log.info(`Starting DockerCompose environment "${this.projectName}"...`); const client = await getContainerRuntimeClient(); const reaper = await getReaper(client); reaper.addComposeProject(this.projectName); + const { + composeOptions: clientComposeOptions = [], + commandOptions: clientCommandOptions = [], + ...remainingClientOptions + } = this.clientOptions; + const options = { + ...remainingClientOptions, filePath: this.composeFilePath, files: this.composeFiles, projectName: this.projectName, }; - const commandOptions = []; + const commandOptions = [...clientCommandOptions]; if (this.build) { commandOptions.push("--build"); } @@ -104,7 +119,7 @@ export class DockerComposeEnvironment { commandOptions.push("--no-recreate"); } - const composeOptions: string[] = []; + const composeOptions = [...clientComposeOptions]; if (this.environmentFile) { composeOptions.push("--env-file", this.environmentFile); } From 6db412ad2e5ab3cf5095468fb815e5c4d409f88a Mon Sep 17 00:00:00 2001 From: Alexis SIMON Date: Mon, 16 Jun 2025 11:47:01 +0200 Subject: [PATCH 2/2] Create our own ComposeExecutableOptions --- .../container-runtime/clients/compose/types.ts | 15 +++++++++++++-- .../testcontainers/src/container-runtime/index.ts | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/testcontainers/src/container-runtime/clients/compose/types.ts b/packages/testcontainers/src/container-runtime/clients/compose/types.ts index 985807e80..9e50f5fec 100644 --- a/packages/testcontainers/src/container-runtime/clients/compose/types.ts +++ b/packages/testcontainers/src/container-runtime/clients/compose/types.ts @@ -1,4 +1,3 @@ -import { IDockerComposeExecutableOptions } from "docker-compose"; import { Logger } from "../../../common"; export type ComposeOptions = { @@ -9,9 +8,21 @@ export type ComposeOptions = { composeOptions?: string[]; environment?: NodeJS.ProcessEnv; logger?: Logger; - executable?: IDockerComposeExecutableOptions; + executable?: ComposeExecutableOptions; }; +export type ComposeExecutableOptions = + | { + executablePath: string; + options?: string[] | (string | string[])[]; + standalone?: never; + } + | { + executablePath?: string; + options?: never; + standalone: true; + }; + export type ComposeDownOptions = { timeout: number; removeVolumes: boolean; diff --git a/packages/testcontainers/src/container-runtime/index.ts b/packages/testcontainers/src/container-runtime/index.ts index 6717aaec5..d0332c0b5 100644 --- a/packages/testcontainers/src/container-runtime/index.ts +++ b/packages/testcontainers/src/container-runtime/index.ts @@ -1,6 +1,6 @@ export { getAuthConfig } from "./auth/get-auth-config"; export { ContainerRuntimeClient, getContainerRuntimeClient } from "./clients/client"; export { parseComposeContainerName } from "./clients/compose/parse-compose-container-name"; -export { ComposeDownOptions, ComposeOptions } from "./clients/compose/types"; +export { ComposeDownOptions, ComposeExecutableOptions, ComposeOptions } from "./clients/compose/types"; export { HostIp } from "./clients/types"; export { ImageName } from "./image-name";