From 061f15d4dc08fd361311233fb96022da2fa32c3f Mon Sep 17 00:00:00 2001 From: Weyert de Boer <7049+weyert@users.noreply.github.com> Date: Sun, 28 Jul 2024 13:31:08 +0100 Subject: [PATCH 1/7] feat: add `withPlatform`-function to TestContainer Adds the ability to the pass `--platform`-argument for the docker container refs #773 --- .../src/generic-container/generic-container-builder.ts | 7 +++++++ .../src/generic-container/generic-container.test.ts | 9 +++++++++ .../src/generic-container/generic-container.ts | 5 +++++ packages/testcontainers/src/test-container.ts | 1 + 4 files changed, 22 insertions(+) diff --git a/packages/testcontainers/src/generic-container/generic-container-builder.ts b/packages/testcontainers/src/generic-container/generic-container-builder.ts index 74d254b2..25ee2c32 100644 --- a/packages/testcontainers/src/generic-container/generic-container-builder.ts +++ b/packages/testcontainers/src/generic-container/generic-container-builder.ts @@ -18,6 +18,7 @@ export class GenericContainerBuilder { private pullPolicy: ImagePullPolicy = PullPolicy.defaultPolicy(); private cache = true; private target?: string; + private platform?: string; constructor( private readonly context: string, @@ -40,6 +41,11 @@ export class GenericContainerBuilder { return this; } + public withPlatform(platform: string): this { + this.platform = platform; + return this; + } + public withTarget(target: string): this { this.target = target; return this; @@ -72,6 +78,7 @@ export class GenericContainerBuilder { registryconfig: registryConfig, labels, target: this.target, + platform: this.platform, }; if (this.pullPolicy.shouldPull()) { diff --git a/packages/testcontainers/src/generic-container/generic-container.test.ts b/packages/testcontainers/src/generic-container/generic-container.test.ts index fa02099d..47a2481c 100644 --- a/packages/testcontainers/src/generic-container/generic-container.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container.test.ts @@ -123,6 +123,15 @@ describe("GenericContainer", () => { expect(output).toEqual(expect.stringContaining("/tmp")); }); + it("should set platform", async () => { + const container = await new GenericContainer("amd64/alpine") + .withPlatform("linux/amd64") + .withExposedPorts(8080) + .start(); + + await checkContainerIsHealthy(container); + }); + it("should set entrypoint", async () => { const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") .withEntrypoint(["node"]) diff --git a/packages/testcontainers/src/generic-container/generic-container.ts b/packages/testcontainers/src/generic-container/generic-container.ts index ef50853c..08b4ab59 100644 --- a/packages/testcontainers/src/generic-container/generic-container.ts +++ b/packages/testcontainers/src/generic-container/generic-container.ts @@ -278,6 +278,11 @@ export class GenericContainer implements TestContainer { return this; } + public withPlatform(platform: string): this { + this.createOpts.platform = platform; + return this; + } + public withTmpFs(tmpFs: TmpFs): this { this.hostConfig.Tmpfs = { ...this.hostConfig.Tmpfs, ...tmpFs }; return this; diff --git a/packages/testcontainers/src/test-container.ts b/packages/testcontainers/src/test-container.ts index bfcf9d12..b2410024 100644 --- a/packages/testcontainers/src/test-container.ts +++ b/packages/testcontainers/src/test-container.ts @@ -36,6 +36,7 @@ export interface TestContainer { withExtraHosts(extraHosts: ExtraHost[]): this; withDefaultLogDriver(): this; withPrivilegedMode(): this; + withPlatform(platform: string): this; withUser(user: string): this; withPullPolicy(pullPolicy: ImagePullPolicy): this; withReuse(): this; From 3f0665f4220a63f3eff2f9bbcf9897fab2dc3c7a Mon Sep 17 00:00:00 2001 From: Weyert de Boer <7049+weyert@users.noreply.github.com> Date: Sat, 10 Aug 2024 23:41:45 +0100 Subject: [PATCH 2/7] ci: temporary try to run all tests even when some other fail --- .github/workflows/test-template.yml | 23 ++++++++++---------- .github/workflows/test.yml | 33 ++++++++++++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/test-template.yml b/.github/workflows/test-template.yml index 7f8e3c6f..93098b4a 100644 --- a/.github/workflows/test-template.yml +++ b/.github/workflows/test-template.yml @@ -21,6 +21,7 @@ jobs: test: name: "Runner (${{ inputs.runner }}) / Node (${{ inputs.node-version }}) / Runtime (${{ inputs.container-runtime }}) / Workspace (${{ inputs.workspace }})" runs-on: ${{ inputs.runner }} + continue-on-error: true steps: - name: Docker rootless setup 1/2 if: ${{ inputs.container-runtime == 'docker-rootless' }} @@ -70,13 +71,13 @@ jobs: run: | brew install docker docker-compose brew install --cask rancher - + TIMEOUT_SECS_CLI_TOOLS=60 TIMEOUT_SECS_USER_SOCKET=300 - + echo "Open Rancher Desktop app" open "/Applications/Rancher Desktop.app" - + echo "Wait max of ${TIMEOUT_SECS_CLI_TOOLS}s for Rancher Desktop to create CLI tools" for i in $(seq 1 ${TIMEOUT_SECS_CLI_TOOLS}); do if [ -e "$HOME/.rd/bin/rdctl" ]; then @@ -85,12 +86,12 @@ jobs: fi sleep 1 done - + if [ ! -e "$HOME/.rd/bin/rdctl" ]; then echo "Rancher Desktop CLI tools not found" exit 1 fi - + echo "Rancher Desktop initialised successfully, now configure the container runtime" $HOME/.rd/bin/rdctl set \ --container-engine.name=moby \ @@ -101,13 +102,13 @@ jobs: --virtual-machine.number-cpus=3 \ --virtual-machine.memory-in-gb=14 \ || true - + echo "Restart Rancher Desktop" $HOME/.rd/bin/rdctl shutdown $HOME/.rd/bin/rdctl start - + echo "Wait max of ${TIMEOUT_SECS_USER_SOCKET}s for Rancher socket" - + for i in $(seq 1 ${TIMEOUT_SECS_USER_SOCKET}); do if [ -e "$HOME/.rd/docker.sock" ]; then echo "Rancher Desktop socket created after ${i}s" @@ -115,14 +116,14 @@ jobs: fi sleep 1 done - + if [ ! -e "$HOME/.rd/docker.sock" ]; then echo "Rancher Desktop socket not found" exit 1 fi - + echo "{}" > $HOME/.docker/config.json - + echo "DOCKER_HOST=unix://${HOME}/.rd/docker.sock" >> $GITHUB_ENV echo "TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock" >> $GITHUB_ENV echo "NODE_OPTIONS=--dns-result-order=ipv4first" >> $GITHUB_ENV diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8fa79d29..8e781716 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,17 +2,17 @@ name: Test on: push: - branches: [ main ] + branches: [main] paths-ignore: - - 'mkdocs.yml' - - 'docs/**' - - 'README.md' + - "mkdocs.yml" + - "docs/**" + - "README.md" pull_request: - branches: [ main ] + branches: [main] paths-ignore: - - 'mkdocs.yml' - - 'docs/**' - - 'README.md' + - "mkdocs.yml" + - "docs/**" + - "README.md" concurrency: group: "${{ github.workflow }}-${{ github.head_ref || github.sha }}" @@ -24,8 +24,8 @@ jobs: runs-on: ${{ matrix.runner }} strategy: matrix: - runner: [ ubuntu-22.04 ] - node-version: [ 18.x, 20.x ] + runner: [ubuntu-22.04] + node-version: [20.x] steps: - name: Code checkout uses: actions/checkout@v3 @@ -49,13 +49,11 @@ jobs: needs: smoke-test strategy: matrix: - node-version: [ 18.x, 20.x ] - container-runtime: [ docker, podman ] + node-version: [20.x] + container-runtime: [docker] include: - container-runtime: docker runner: ubuntu-22.04 - - container-runtime: podman - runner: ubuntu-22.04 uses: ./.github/workflows/test-template.yml with: runner: ${{ matrix.runner }} @@ -75,11 +73,12 @@ jobs: run: echo "modules=$(ls packages/modules | jq -cnR '[inputs | select(length>0) | select(. != "couchbase")]')" >> $GITHUB_OUTPUT test-modules: name: Module (${{ matrix.module }}) - needs: [ test-testcontainers, list-modules ] + needs: [test-testcontainers, list-modules] strategy: + fail-fast: false matrix: - node-version: [ 18.x, 20.x ] - container-runtime: [ docker, podman ] + node-version: [18.x, 20.x] + container-runtime: [docker, podman] include: - container-runtime: docker runner: ubuntu-22.04 From a410bf0e05e69ad0b25bbb2d08bb5578b913d6c3 Mon Sep 17 00:00:00 2001 From: Weyert de Boer <7049+weyert@users.noreply.github.com> Date: Sun, 11 Aug 2024 13:34:04 +0100 Subject: [PATCH 3/7] fix: attempt to make `withPlatform` or `--platform` passing work Updated the `image-client` so it also allows passing the `platform` configuration option to `dockerode` --- .../container-runtime/clients/image/docker-image-client.ts | 7 +++++-- .../src/container-runtime/clients/image/image-client.ts | 2 +- .../src/generic-container/generic-container.test.ts | 7 +++++-- .../src/generic-container/generic-container.ts | 5 ++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/testcontainers/src/container-runtime/clients/image/docker-image-client.ts b/packages/testcontainers/src/container-runtime/clients/image/docker-image-client.ts index 0c57fce3..47f2fb79 100644 --- a/packages/testcontainers/src/container-runtime/clients/image/docker-image-client.ts +++ b/packages/testcontainers/src/container-runtime/clients/image/docker-image-client.ts @@ -87,7 +87,7 @@ export class DockerImageClient implements ImageClient { }); } - async pull(imageName: ImageName, opts?: { force: boolean }): Promise { + async pull(imageName: ImageName, opts?: { force: boolean; platform: string | undefined }): Promise { try { if (!opts?.force && (await this.exists(imageName))) { log.debug(`Image "${imageName.string}" already exists`); @@ -96,7 +96,10 @@ export class DockerImageClient implements ImageClient { log.debug(`Pulling image "${imageName.string}"...`); const authconfig = await getAuthConfig(imageName.registry ?? this.indexServerAddress); - const stream = await this.dockerode.pull(imageName.string, { authconfig }); + const stream = await this.dockerode.pull(imageName.string, { + authconfig, + platform: opts?.platform, + }); await new Promise((resolve) => { byline(stream).on("data", (line) => { if (pullLog.enabled()) { diff --git a/packages/testcontainers/src/container-runtime/clients/image/image-client.ts b/packages/testcontainers/src/container-runtime/clients/image/image-client.ts index 15a1eed9..a4b92f2d 100644 --- a/packages/testcontainers/src/container-runtime/clients/image/image-client.ts +++ b/packages/testcontainers/src/container-runtime/clients/image/image-client.ts @@ -3,6 +3,6 @@ import { ImageName } from "../../image-name"; export interface ImageClient { build(context: string, opts: ImageBuildOptions): Promise; - pull(imageName: ImageName, opts?: { force: boolean }): Promise; + pull(imageName: ImageName, opts?: { force: boolean; platform: string | undefined }): Promise; exists(imageName: ImageName): Promise; } diff --git a/packages/testcontainers/src/generic-container/generic-container.test.ts b/packages/testcontainers/src/generic-container/generic-container.test.ts index 47a2481c..ce63c7a9 100644 --- a/packages/testcontainers/src/generic-container/generic-container.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container.test.ts @@ -124,12 +124,15 @@ describe("GenericContainer", () => { }); it("should set platform", async () => { - const container = await new GenericContainer("amd64/alpine") + const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withWorkingDir("/tmp") + .withCommand(["node", "../index.js"]) .withPlatform("linux/amd64") .withExposedPorts(8080) .start(); - await checkContainerIsHealthy(container); + const { output } = await container.exec(["arch"]); + expect(output).toEqual(expect.stringContaining("x86_64")); }); it("should set entrypoint", async () => { diff --git a/packages/testcontainers/src/generic-container/generic-container.ts b/packages/testcontainers/src/generic-container/generic-container.ts index 08b4ab59..373f31ce 100644 --- a/packages/testcontainers/src/generic-container/generic-container.ts +++ b/packages/testcontainers/src/generic-container/generic-container.ts @@ -79,7 +79,10 @@ export class GenericContainer implements TestContainer { public async start(): Promise { const client = await getContainerRuntimeClient(); - await client.image.pull(this.imageName, { force: this.pullPolicy.shouldPull() }); + await client.image.pull(this.imageName, { + force: this.pullPolicy.shouldPull(), + platform: this.createOpts.platform, + }); if (this.beforeContainerCreated) { await this.beforeContainerCreated(); From 84cad998607be7ecf236c1b7ee7d177387fee83b Mon Sep 17 00:00:00 2001 From: Weyert de Boer <7049+weyert@users.noreply.github.com> Date: Sun, 11 Aug 2024 13:43:40 +0100 Subject: [PATCH 4/7] test: update the test to ensure different testcontainer image is pulled --- .../src/generic-container/generic-container.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/testcontainers/src/generic-container/generic-container.test.ts b/packages/testcontainers/src/generic-container/generic-container.test.ts index ce63c7a9..706070d5 100644 --- a/packages/testcontainers/src/generic-container/generic-container.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container.test.ts @@ -124,7 +124,8 @@ describe("GenericContainer", () => { }); it("should set platform", async () => { - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + // This test uses an older version of cristianrgreco/testcontainer as all the other tests are using the latest version and the code doesn't pull the image again when the platform differs at this time + const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.13") .withWorkingDir("/tmp") .withCommand(["node", "../index.js"]) .withPlatform("linux/amd64") From 12a4209447caad9d1dd7b18530756c8c647964c4 Mon Sep 17 00:00:00 2001 From: Weyert de Boer <7049+weyert@users.noreply.github.com> Date: Sun, 11 Aug 2024 14:53:54 +0100 Subject: [PATCH 5/7] test: use alwaysPull policy instead of different version number --- .../src/generic-container/generic-container.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/testcontainers/src/generic-container/generic-container.test.ts b/packages/testcontainers/src/generic-container/generic-container.test.ts index 706070d5..6484c0ef 100644 --- a/packages/testcontainers/src/generic-container/generic-container.test.ts +++ b/packages/testcontainers/src/generic-container/generic-container.test.ts @@ -124,9 +124,8 @@ describe("GenericContainer", () => { }); it("should set platform", async () => { - // This test uses an older version of cristianrgreco/testcontainer as all the other tests are using the latest version and the code doesn't pull the image again when the platform differs at this time - const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.13") - .withWorkingDir("/tmp") + const container = await new GenericContainer("cristianrgreco/testcontainer:1.1.14") + .withPullPolicy(PullPolicy.alwaysPull()) .withCommand(["node", "../index.js"]) .withPlatform("linux/amd64") .withExposedPorts(8080) From 151fe79ddc5cfe95908e7a1a649e6cdead6bb7b5 Mon Sep 17 00:00:00 2001 From: Weyert de Boer <7049+weyert@users.noreply.github.com> Date: Sun, 11 Aug 2024 14:57:01 +0100 Subject: [PATCH 6/7] Revert "ci: temporary try to run all tests even when some other fail" This reverts commit 3f0665f4220a63f3eff2f9bbcf9897fab2dc3c7a. --- .github/workflows/test-template.yml | 23 ++++++++++---------- .github/workflows/test.yml | 33 +++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/test-template.yml b/.github/workflows/test-template.yml index 93098b4a..7f8e3c6f 100644 --- a/.github/workflows/test-template.yml +++ b/.github/workflows/test-template.yml @@ -21,7 +21,6 @@ jobs: test: name: "Runner (${{ inputs.runner }}) / Node (${{ inputs.node-version }}) / Runtime (${{ inputs.container-runtime }}) / Workspace (${{ inputs.workspace }})" runs-on: ${{ inputs.runner }} - continue-on-error: true steps: - name: Docker rootless setup 1/2 if: ${{ inputs.container-runtime == 'docker-rootless' }} @@ -71,13 +70,13 @@ jobs: run: | brew install docker docker-compose brew install --cask rancher - + TIMEOUT_SECS_CLI_TOOLS=60 TIMEOUT_SECS_USER_SOCKET=300 - + echo "Open Rancher Desktop app" open "/Applications/Rancher Desktop.app" - + echo "Wait max of ${TIMEOUT_SECS_CLI_TOOLS}s for Rancher Desktop to create CLI tools" for i in $(seq 1 ${TIMEOUT_SECS_CLI_TOOLS}); do if [ -e "$HOME/.rd/bin/rdctl" ]; then @@ -86,12 +85,12 @@ jobs: fi sleep 1 done - + if [ ! -e "$HOME/.rd/bin/rdctl" ]; then echo "Rancher Desktop CLI tools not found" exit 1 fi - + echo "Rancher Desktop initialised successfully, now configure the container runtime" $HOME/.rd/bin/rdctl set \ --container-engine.name=moby \ @@ -102,13 +101,13 @@ jobs: --virtual-machine.number-cpus=3 \ --virtual-machine.memory-in-gb=14 \ || true - + echo "Restart Rancher Desktop" $HOME/.rd/bin/rdctl shutdown $HOME/.rd/bin/rdctl start - + echo "Wait max of ${TIMEOUT_SECS_USER_SOCKET}s for Rancher socket" - + for i in $(seq 1 ${TIMEOUT_SECS_USER_SOCKET}); do if [ -e "$HOME/.rd/docker.sock" ]; then echo "Rancher Desktop socket created after ${i}s" @@ -116,14 +115,14 @@ jobs: fi sleep 1 done - + if [ ! -e "$HOME/.rd/docker.sock" ]; then echo "Rancher Desktop socket not found" exit 1 fi - + echo "{}" > $HOME/.docker/config.json - + echo "DOCKER_HOST=unix://${HOME}/.rd/docker.sock" >> $GITHUB_ENV echo "TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock" >> $GITHUB_ENV echo "NODE_OPTIONS=--dns-result-order=ipv4first" >> $GITHUB_ENV diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8e781716..8fa79d29 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,17 +2,17 @@ name: Test on: push: - branches: [main] + branches: [ main ] paths-ignore: - - "mkdocs.yml" - - "docs/**" - - "README.md" + - 'mkdocs.yml' + - 'docs/**' + - 'README.md' pull_request: - branches: [main] + branches: [ main ] paths-ignore: - - "mkdocs.yml" - - "docs/**" - - "README.md" + - 'mkdocs.yml' + - 'docs/**' + - 'README.md' concurrency: group: "${{ github.workflow }}-${{ github.head_ref || github.sha }}" @@ -24,8 +24,8 @@ jobs: runs-on: ${{ matrix.runner }} strategy: matrix: - runner: [ubuntu-22.04] - node-version: [20.x] + runner: [ ubuntu-22.04 ] + node-version: [ 18.x, 20.x ] steps: - name: Code checkout uses: actions/checkout@v3 @@ -49,11 +49,13 @@ jobs: needs: smoke-test strategy: matrix: - node-version: [20.x] - container-runtime: [docker] + node-version: [ 18.x, 20.x ] + container-runtime: [ docker, podman ] include: - container-runtime: docker runner: ubuntu-22.04 + - container-runtime: podman + runner: ubuntu-22.04 uses: ./.github/workflows/test-template.yml with: runner: ${{ matrix.runner }} @@ -73,12 +75,11 @@ jobs: run: echo "modules=$(ls packages/modules | jq -cnR '[inputs | select(length>0) | select(. != "couchbase")]')" >> $GITHUB_OUTPUT test-modules: name: Module (${{ matrix.module }}) - needs: [test-testcontainers, list-modules] + needs: [ test-testcontainers, list-modules ] strategy: - fail-fast: false matrix: - node-version: [18.x, 20.x] - container-runtime: [docker, podman] + node-version: [ 18.x, 20.x ] + container-runtime: [ docker, podman ] include: - container-runtime: docker runner: ubuntu-22.04 From 033ca2bfd46b9a1af822a406a81312a61b06b108 Mon Sep 17 00:00:00 2001 From: Weyert de Boer <7049+weyert@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:41:45 +0000 Subject: [PATCH 7/7] docs: added callout to `withPlatform` --- docs/features/containers.md | 66 +++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/docs/features/containers.md b/docs/features/containers.md index 8f664b6d..43bbbb0c 100644 --- a/docs/features/containers.md +++ b/docs/features/containers.md @@ -68,6 +68,14 @@ const container = await new GenericContainer("alpine") .start(); ``` +### With a platform + +```javascript +const container = await new GenericContainer("alpine") + .withPlatform("linux/arm64") // similar to `--platform linux/arm64` + .start(); +``` + ### With bind mounts **Not recommended.** @@ -76,9 +84,9 @@ Bind mounts are not portable. They do not work with Docker in Docker or in cases ```javascript const container = await new GenericContainer("alpine") - .withBindMounts([{ - source: "/local/file.txt", - target:"/remote/file.txt" + .withBindMounts([{ + source: "/local/file.txt", + target:"/remote/file.txt" }, { source: "/local/dir", target:"/remote/dir", @@ -97,7 +105,7 @@ const container = await new GenericContainer("alpine") ### With a name -**Not recommended.** +**Not recommended.** If a container with the same name already exists, Docker will raise a conflict. If you are specifying a name to enable container to container communication, look into creating a network and using [network aliases](../networking#network-aliases). @@ -113,15 +121,15 @@ Copy files/directories or content to a container before it starts: ```javascript const container = await new GenericContainer("alpine") - .withCopyFilesToContainer([{ - source: "/local/file.txt", + .withCopyFilesToContainer([{ + source: "/local/file.txt", target: "/remote/file1.txt" }]) .withCopyDirectoriesToContainer([{ source: "/localdir", target: "/some/nested/remotedir" }]) - .withCopyContentToContainer([{ + .withCopyContentToContainer([{ content: "hello world", target: "/remote/file2.txt" }]) @@ -133,15 +141,15 @@ Or after it starts: ```javascript const container = await new GenericContainer("alpine").start(); -container.copyFilesToContainer([{ - source: "/local/file.txt", +container.copyFilesToContainer([{ + source: "/local/file.txt", target: "/remote/file1.txt" }]) container.copyDirectoriesToContainer([{ source: "/localdir", target: "/some/nested/remotedir" }]) -container.copyContentToContainer([{ +container.copyContentToContainer([{ content: "hello world", target: "/remote/file2.txt" }]) @@ -151,8 +159,8 @@ An optional `mode` can be specified in octal for setting file permissions: ```javascript const container = await new GenericContainer("alpine") - .withCopyFilesToContainer([{ - source: "/local/file.txt", + .withCopyFilesToContainer([{ + source: "/local/file.txt", target: "/remote/file1.txt", mode: parseInt("0644", 8) }]) @@ -161,7 +169,7 @@ const container = await new GenericContainer("alpine") target: "/some/nested/remotedir", mode: parseInt("0644", 8) }]) - .withCopyContentToContainer([{ + .withCopyContentToContainer([{ content: "hello world", target: "/remote/file2.txt", mode: parseInt("0644", 8) @@ -258,10 +266,10 @@ const container = await new GenericContainer("alpine") ```javascript const container = await new GenericContainer("aline") - .withUlimits({ - memlock: { - hard: -1, - soft: -1 + .withUlimits({ + memlock: { + hard: -1, + soft: -1 } }) .start(); @@ -339,7 +347,7 @@ await container.restart(); ## Reusing a container -Enabling container re-use means that Testcontainers will not start a new container if a Testcontainers managed container with the same configuration is already running. +Enabling container re-use means that Testcontainers will not start a new container if a Testcontainers managed container with the same configuration is already running. This is useful for example if you want to share a container across tests without global set up. @@ -403,29 +411,29 @@ const startedCustomContainer: StartedTestContainer = await customContainer.start Define your own lifecycle callbacks for better control over your custom containers: ```typescript -import { - GenericContainer, - AbstractStartedContainer, - StartedTestContainer, - InspectResult +import { + GenericContainer, + AbstractStartedContainer, + StartedTestContainer, + InspectResult } from "testcontainers"; class CustomContainer extends GenericContainer { protected override async beforeContainerCreated(): Promise { // ... } - + protected override async containerCreated(containerId: string): Promise { // ... } - + protected override async containerStarting( inspectResult: InspectResult, reused: boolean ): Promise { // ... } - + protected override async containerStarted( container: StartedTestContainer, inspectResult: InspectResult, @@ -443,7 +451,7 @@ class CustomStartedContainer extends AbstractStartedContainer { protected override async containerStopping(): Promise { // ... } - + protected override async containerStopped(): Promise { // ... } @@ -495,7 +503,7 @@ const container = await new GenericContainer("alpine") ## Running commands -To run a command inside an already started container use the `exec` method. The command will be run in the container's +To run a command inside an already started container use the `exec` method. The command will be run in the container's working directory, returning the command output and exit code: ```javascript @@ -555,7 +563,7 @@ const container = await new GenericContainer("alpine") .start(); ``` -You can specify a point in time as a UNIX timestamp from which you want the logs to start: +You can specify a point in time as a UNIX timestamp from which you want the logs to start: ```javascript const msInSec = 1000;