diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 731826913c8..5952d31d527 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -2,7 +2,7 @@
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "Go",
- "image": "mcr.microsoft.com/devcontainers/go:1.21-bookworm",
+ "image": "mcr.microsoft.com/devcontainers/go:1.22-bookworm",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
diff --git a/.github/workflows/ci-test-go.yml b/.github/workflows/ci-test-go.yml
index 13fb26422cb..c2c0747278d 100644
--- a/.github/workflows/ci-test-go.yml
+++ b/.github/workflows/ci-test-go.yml
@@ -62,7 +62,7 @@ jobs:
run: sudo rm -rf /var/run/docker.sock
- name: Check out code into the Go module directory
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5
diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml
index ba8a27aaf1d..fce4331c947 100644
--- a/.github/workflows/ci-windows.yml
+++ b/.github/workflows/ci-windows.yml
@@ -24,7 +24,7 @@ jobs:
})
- name: Checkout
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.event.client_payload.pull_request.head.repo.full_name }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5ca90727cbc..f326e9c1dae 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,7 +26,7 @@ jobs:
test:
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest, macos-latest]
uses: ./.github/workflows/ci-test-go.yml
with:
@@ -44,7 +44,7 @@ jobs:
name: "Test with reaper off"
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
uses: ./.github/workflows/ci-test-go.yml
with:
go-version: ${{ matrix.go-version }}
@@ -61,7 +61,7 @@ jobs:
name: "Test with Rootless Docker"
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest]
uses: ./.github/workflows/ci-test-go.yml
with:
@@ -76,7 +76,7 @@ jobs:
test-module-generator:
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
uses: ./.github/workflows/ci-test-go.yml
with:
@@ -92,7 +92,7 @@ jobs:
needs: test
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest]
module: [artemis, azurite, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, dolt, elasticsearch, gcloud, grafana-lgtm, inbucket, influxdb, k3s, k6, kafka, localstack, mariadb, milvus, minio, mockserver, mongodb, mssql, mysql, nats, neo4j, ollama, openfga, openldap, opensearch, postgres, pulsar, qdrant, rabbitmq, redis, redpanda, registry, surrealdb, valkey, vault, vearch, weaviate]
uses: ./.github/workflows/ci-test-go.yml
@@ -112,7 +112,7 @@ jobs:
module: [nginx, toxiproxy]
uses: ./.github/workflows/ci-test-go.yml
with:
- go-version: "1.21.x"
+ go-version: "1.22.x"
fail-fast: true
platform: 'ubuntu-latest'
project-directory: examples/${{ matrix.module }}
@@ -129,7 +129,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 89196632575..15f56f2eb5a 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -49,11 +49,11 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
+ uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -67,7 +67,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
+ uses: github/codeql-action/autobuild@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
# âšī¸ Command-line programs to run using the OS shell.
# đ See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -80,6 +80,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
+ uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
with:
category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/docker-moby-latest.yml b/.github/workflows/docker-moby-latest.yml
index acf8704ec83..39cb96df3d4 100644
--- a/.github/workflows/docker-moby-latest.yml
+++ b/.github/workflows/docker-moby-latest.yml
@@ -27,7 +27,7 @@ jobs:
run: sudo rm -rf /var/run/docker.sock
- name: Check out code into the Go module directory
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
- name: Set up Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 73c657d3c49..82b14c7ad40 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -20,7 +20,7 @@ jobs:
steps:
- name: "Checkout code"
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
persist-credentials: false
@@ -51,6 +51,6 @@ jobs:
# required for Code scanning alerts
- name: "Upload SARIF results to code scanning"
- uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
+ uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
with:
sarif_file: results.sarif
diff --git a/.mockery.yaml b/.mockery.yaml
new file mode 100644
index 00000000000..2f96829f214
--- /dev/null
+++ b/.mockery.yaml
@@ -0,0 +1,11 @@
+quiet: True
+disable-version-string: True
+with-expecter: True
+mockname: "mock{{.InterfaceName}}"
+filename: "{{ .InterfaceName | lower }}_mock_test.go"
+outpkg: "{{.PackageName}}_test"
+dir: "{{.InterfaceDir}}"
+packages:
+ github.com/testcontainers/testcontainers-go/wait:
+ interfaces:
+ StrategyTarget:
diff --git a/Pipfile b/Pipfile
index 6d49cae4dee..2648278724f 100644
--- a/Pipfile
+++ b/Pipfile
@@ -8,9 +8,9 @@ verify_ssl = true
[packages]
mkdocs = "==1.5.3"
mkdocs-codeinclude-plugin = "==0.2.1"
-mkdocs-include-markdown-plugin = "==6.2.1"
+mkdocs-include-markdown-plugin = "==6.2.2"
mkdocs-material = "==9.5.18"
-mkdocs-markdownextradata-plugin = "==0.2.5"
+mkdocs-markdownextradata-plugin = "==0.2.6"
[requires]
python_version = "3.8"
diff --git a/Pipfile.lock b/Pipfile.lock
index 3a6f97e05f2..9a2f6d24c8c 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "de89df66a55ec8bbae341b1e70d15bb1a52b1b04489eee82b2195c83ef08a385"
+ "sha256": "0411eac13d1b06b42671b8a654fb269eb0c329d9a3d41f669ccf7b653ef8ad32"
},
"pipfile-spec": 6,
"requires": {
@@ -26,11 +26,11 @@
},
"bracex": {
"hashes": [
- "sha256:a27eaf1df42cf561fed58b7a8f3fdf129d1ea16a81e1fadd1d17989bc6384beb",
- "sha256:efdc71eff95eaff5e0f8cfebe7d01adf2c8637c8c92edaf63ef348c241a82418"
+ "sha256:0725da5045e8d37ea9592ab3614d8b561e22c3c5fde3964699be672e072ab611",
+ "sha256:d2fcf4b606a82ac325471affe1706dd9bbaa3536c91ef86a31f6b766f3dad1d0"
],
"markers": "python_version >= '3.8'",
- "version": "==2.4"
+ "version": "==2.5"
},
"certifi": {
"hashes": [
@@ -170,11 +170,11 @@
},
"importlib-metadata": {
"hashes": [
- "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f",
- "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"
+ "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1",
+ "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"
],
"markers": "python_version < '3.10'",
- "version": "==8.0.0"
+ "version": "==8.4.0"
},
"jinja2": {
"hashes": [
@@ -186,11 +186,11 @@
},
"markdown": {
"hashes": [
- "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f",
- "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"
+ "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2",
+ "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"
],
"markers": "python_version >= '3.8'",
- "version": "==3.6"
+ "version": "==3.7"
},
"markupsafe": {
"hashes": [
@@ -286,20 +286,21 @@
},
"mkdocs-include-markdown-plugin": {
"hashes": [
- "sha256:46fc372886d48eec541d36138d1fe1db42afd08b976ef7c8d8d4ea6ee4d5d1e8",
- "sha256:8dfc3aee9435679b094cbdff023239e91d86cf357c40b0e99c28036449661830"
+ "sha256:d293950f6499d2944291ca7b9bc4a60e652bbfd3e3a42b564f6cceee268694e7",
+ "sha256:f2bd5026650492a581d2fd44be6c22f90391910d76582b96a34c264f2d17875d"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
- "version": "==6.2.1"
+ "version": "==6.2.2"
},
"mkdocs-markdownextradata-plugin": {
"hashes": [
- "sha256:9c562e8fe375647d5692d11dfe369a7bdd50302174d35995fce2aeca58036ec6"
+ "sha256:34dd40870781784c75809596b2d8d879da783815b075336d541de1f150c94242",
+ "sha256:4aed9b43b8bec65b02598387426ca4809099ea5f5aa78bf114f3296fd46686b5"
],
"index": "pypi",
- "markers": "python_version not in '3.0, 3.1, 3.2, 3.3' and python_full_version >= '2.7.9'",
- "version": "==0.2.5"
+ "markers": "python_version >= '3.6'",
+ "version": "==0.2.6"
},
"mkdocs-material": {
"hashes": [
@@ -382,60 +383,62 @@
},
"pyyaml": {
"hashes": [
- "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5",
- "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc",
- "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df",
- "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741",
- "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206",
- "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27",
- "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595",
- "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62",
- "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98",
- "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696",
- "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290",
- "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9",
- "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d",
- "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6",
- "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867",
- "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47",
- "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486",
- "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6",
- "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3",
- "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007",
- "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938",
- "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0",
- "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c",
- "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735",
- "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d",
- "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28",
- "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4",
- "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba",
- "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8",
- "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef",
- "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5",
- "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd",
- "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3",
- "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0",
- "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515",
- "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c",
- "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c",
- "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924",
- "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34",
- "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43",
- "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859",
- "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673",
- "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54",
- "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a",
- "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b",
- "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab",
- "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa",
- "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c",
- "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585",
- "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d",
- "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"
+ "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff",
+ "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48",
+ "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086",
+ "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e",
+ "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133",
+ "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5",
+ "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484",
+ "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee",
+ "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5",
+ "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68",
+ "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a",
+ "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf",
+ "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99",
+ "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8",
+ "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85",
+ "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19",
+ "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc",
+ "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a",
+ "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1",
+ "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317",
+ "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c",
+ "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631",
+ "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d",
+ "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652",
+ "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5",
+ "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e",
+ "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b",
+ "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8",
+ "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476",
+ "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706",
+ "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563",
+ "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237",
+ "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b",
+ "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083",
+ "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180",
+ "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425",
+ "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e",
+ "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f",
+ "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725",
+ "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183",
+ "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab",
+ "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774",
+ "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725",
+ "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
+ "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5",
+ "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d",
+ "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290",
+ "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44",
+ "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed",
+ "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4",
+ "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba",
+ "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12",
+ "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"
],
- "markers": "python_version >= '3.6'",
- "version": "==6.0.1"
+ "markers": "python_version >= '3.8'",
+ "version": "==6.0.2"
},
"pyyaml-env-tag": {
"hashes": [
@@ -558,57 +561,60 @@
},
"watchdog": {
"hashes": [
- "sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7",
- "sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767",
- "sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175",
- "sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459",
- "sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5",
- "sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429",
- "sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6",
- "sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d",
- "sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7",
- "sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28",
- "sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235",
- "sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57",
- "sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a",
- "sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5",
- "sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709",
- "sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee",
- "sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84",
- "sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd",
- "sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba",
- "sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db",
- "sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682",
- "sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35",
- "sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d",
- "sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645",
- "sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253",
- "sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193",
- "sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b",
- "sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44",
- "sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b",
- "sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625",
- "sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e",
- "sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"
+ "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4",
+ "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19",
+ "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a",
+ "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa",
+ "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a",
+ "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a",
+ "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1",
+ "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc",
+ "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9",
+ "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930",
+ "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73",
+ "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b",
+ "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83",
+ "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7",
+ "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef",
+ "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1",
+ "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040",
+ "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b",
+ "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270",
+ "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c",
+ "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d",
+ "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8",
+ "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508",
+ "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b",
+ "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503",
+ "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757",
+ "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b",
+ "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29",
+ "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c",
+ "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22",
+ "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578",
+ "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e",
+ "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee",
+ "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7",
+ "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"
],
"markers": "python_version >= '3.8'",
- "version": "==4.0.1"
+ "version": "==4.0.2"
},
"wcmatch": {
"hashes": [
- "sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478",
- "sha256:a70222b86dea82fb382dd87b73278c10756c138bd6f8f714e2183128887b9eb2"
+ "sha256:567d66b11ad74384954c8af86f607857c3bdf93682349ad32066231abd556c92",
+ "sha256:af25922e2b6dbd1550fa37a4c8de7dd558d6c1bb330c641de9b907b9776cb3c4"
],
"markers": "python_version >= '3.8'",
- "version": "==8.5.2"
+ "version": "==9.0"
},
"zipp": {
"hashes": [
- "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19",
- "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"
+ "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064",
+ "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"
],
"markers": "python_version >= '3.8'",
- "version": "==3.19.2"
+ "version": "==3.20.1"
}
},
"develop": {}
diff --git a/README.md b/README.md
index cf7e0fc2f97..ea21c63871d 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,14 @@
# Testcontainers
-[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=141451032&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=EastUs)
-
-**Builds**
-
[![Main pipeline](https://github.com/testcontainers/testcontainers-go/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/testcontainers/testcontainers-go/actions/workflows/ci.yml)
-
-**Documentation**
-
[![GoDoc Reference](https://pkg.go.dev/badge/github.com/testcontainers/testcontainers-go.svg)](https://pkg.go.dev/github.com/testcontainers/testcontainers-go)
-
-**Social**
-
-[![Slack](https://img.shields.io/badge/Slack-4A154B?logo=slack)](https://testcontainers.slack.com/)
-
-**Code quality**
-
[![Go Report Card](https://goreportcard.com/badge/github.com/testcontainers/testcontainers-go)](https://goreportcard.com/report/github.com/testcontainers/testcontainers-go)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=testcontainers_testcontainers-go&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=testcontainers_testcontainers-go)
+[![License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/testcontainers/testcontainers-go/blob/main/LICENSE)
-**License**
+[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=141451032&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=EastUs)
-[![License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/testcontainers/testcontainers-go/blob/main/LICENSE)
+[![Join our Slack](https://img.shields.io/badge/Slack-4A154B?logo=slack)](https://testcontainers.slack.com/)
_Testcontainers for Go_ is a Go package that makes it simple to create and clean up container-based dependencies for
automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers
diff --git a/commons-test.mk b/commons-test.mk
index a4553c6f9cc..04d0a6e70ca 100644
--- a/commons-test.mk
+++ b/commons-test.mk
@@ -37,6 +37,7 @@ test-%: $(GOBIN)/gotestsum
--packages="./..." \
--junitfile TEST-unit.xml \
-- \
+ -v \
-coverprofile=coverage.out \
-timeout=30m
diff --git a/container.go b/container.go
index 9b0ca2d7a9e..589fb836c44 100644
--- a/container.go
+++ b/container.go
@@ -1,6 +1,7 @@
package testcontainers
import (
+ "archive/tar"
"context"
"errors"
"fmt"
@@ -10,6 +11,7 @@ import (
"strings"
"time"
+ "github.com/cpuguy83/dockercfg"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
@@ -85,7 +87,7 @@ type ImageBuildInfo interface {
// rather than using a pre-built one
type FromDockerfile struct {
Context string // the path to the context of the docker build
- ContextArchive io.Reader // the tar archive file to send to docker that contains the build context
+ ContextArchive io.ReadSeeker // the tar archive file to send to docker that contains the build context
Dockerfile string // the path from the context to the Dockerfile for the image, defaults to "Dockerfile"
Repo string // the repo label for image, defaults to UUID
Tag string // the tag label for image, defaults to UUID
@@ -305,30 +307,90 @@ func (c *ContainerRequest) GetTag() string {
return strings.ToLower(t)
}
-// Deprecated: Testcontainers will detect registry credentials automatically, and it will be removed in the next major release
-// GetAuthConfigs returns the auth configs to be able to pull from an authenticated docker registry
+// Deprecated: Testcontainers will detect registry credentials automatically, and it will be removed in the next major release.
+// GetAuthConfigs returns the auth configs to be able to pull from an authenticated docker registry.
+// Panics if an error occurs.
func (c *ContainerRequest) GetAuthConfigs() map[string]registry.AuthConfig {
- return getAuthConfigsFromDockerfile(c)
+ auth, err := getAuthConfigsFromDockerfile(c)
+ if err != nil {
+ panic(fmt.Sprintf("failed to get auth configs from Dockerfile: %v", err))
+ }
+ return auth
+}
+
+// dockerFileImages returns the images from the request Dockerfile.
+func (c *ContainerRequest) dockerFileImages() ([]string, error) {
+ if c.ContextArchive == nil {
+ // Source is a directory, we can read the Dockerfile directly.
+ images, err := core.ExtractImagesFromDockerfile(filepath.Join(c.Context, c.GetDockerfile()), c.GetBuildArgs())
+ if err != nil {
+ return nil, fmt.Errorf("extract images from Dockerfile: %w", err)
+ }
+
+ return images, nil
+ }
+
+ // Source is an archive, we need to read it to get the Dockerfile.
+ dockerFile := c.GetDockerfile()
+ tr := tar.NewReader(c.FromDockerfile.ContextArchive)
+
+ for {
+ hdr, err := tr.Next()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ return nil, fmt.Errorf("Dockerfile %q not found in context archive", dockerFile)
+ }
+
+ return nil, fmt.Errorf("reading tar archive: %w", err)
+ }
+
+ if hdr.Name != dockerFile {
+ continue
+ }
+
+ images, err := core.ExtractImagesFromReader(tr, c.GetBuildArgs())
+ if err != nil {
+ return nil, fmt.Errorf("extract images from Dockerfile: %w", err)
+ }
+
+ // Reset the archive to the beginning.
+ if _, err := c.ContextArchive.Seek(0, io.SeekStart); err != nil {
+ return nil, fmt.Errorf("seek context archive to start: %w", err)
+ }
+
+ return images, nil
+ }
}
// getAuthConfigsFromDockerfile returns the auth configs to be able to pull from an authenticated docker registry
-func getAuthConfigsFromDockerfile(c *ContainerRequest) map[string]registry.AuthConfig {
- images, err := core.ExtractImagesFromDockerfile(filepath.Join(c.Context, c.GetDockerfile()), c.GetBuildArgs())
+func getAuthConfigsFromDockerfile(c *ContainerRequest) (map[string]registry.AuthConfig, error) {
+ images, err := c.dockerFileImages()
if err != nil {
- return map[string]registry.AuthConfig{}
+ return nil, fmt.Errorf("docker file images: %w", err)
+ }
+
+ // Get the auth configs once for all images as it can be a time-consuming operation.
+ configs, err := getDockerAuthConfigs()
+ if err != nil {
+ return nil, err
}
authConfigs := map[string]registry.AuthConfig{}
for _, image := range images {
- registry, authConfig, err := DockerImageAuth(context.Background(), image)
+ registry, authConfig, err := dockerImageAuth(context.Background(), image, configs)
if err != nil {
+ if !errors.Is(err, dockercfg.ErrCredentialsNotFound) {
+ return nil, fmt.Errorf("docker image auth %q: %w", image, err)
+ }
+
+ // Credentials not found no config to add.
continue
}
authConfigs[registry] = authConfig
}
- return authConfigs
+ return authConfigs, nil
}
func (c *ContainerRequest) ShouldBuildImage() bool {
@@ -361,7 +423,10 @@ func (c *ContainerRequest) BuildOptions() (types.ImageBuildOptions, error) {
buildOptions.Dockerfile = c.GetDockerfile()
// Make sure the auth configs from the Dockerfile are set right after the user-defined build options.
- authsFromDockerfile := getAuthConfigsFromDockerfile(c)
+ authsFromDockerfile, err := getAuthConfigsFromDockerfile(c)
+ if err != nil {
+ return types.ImageBuildOptions{}, fmt.Errorf("auth configs from Dockerfile: %w", err)
+ }
if buildOptions.AuthConfigs == nil {
buildOptions.AuthConfigs = map[string]registry.AuthConfig{}
@@ -378,7 +443,7 @@ func (c *ContainerRequest) BuildOptions() (types.ImageBuildOptions, error) {
for _, is := range c.ImageSubstitutors {
modifiedTag, err := is.Substitute(tag)
if err != nil {
- return buildOptions, fmt.Errorf("failed to substitute image %s with %s: %w", tag, is.Description(), err)
+ return types.ImageBuildOptions{}, fmt.Errorf("failed to substitute image %s with %s: %w", tag, is.Description(), err)
}
if modifiedTag != tag {
@@ -401,8 +466,9 @@ func (c *ContainerRequest) BuildOptions() (types.ImageBuildOptions, error) {
// Do this as late as possible to ensure we don't leak the context on error/panic.
buildContext, err := c.GetContext()
if err != nil {
- return buildOptions, err
+ return types.ImageBuildOptions{}, err
}
+
buildOptions.Context = buildContext
return buildOptions, nil
diff --git a/container_test.go b/container_test.go
index d2a070f2de0..3cb14ac2962 100644
--- a/container_test.go
+++ b/container_test.go
@@ -147,7 +147,7 @@ func Test_BuildImageWithContexts(t *testing.T) {
type TestCase struct {
Name string
ContextPath string
- ContextArchive func() (io.Reader, error)
+ ContextArchive func() (io.ReadSeeker, error)
ExpectedEchoOutput string
Dockerfile string
ExpectedError string
@@ -157,7 +157,7 @@ func Test_BuildImageWithContexts(t *testing.T) {
{
Name: "test build from context archive",
// fromDockerfileWithContextArchive {
- ContextArchive: func() (io.Reader, error) {
+ ContextArchive: func() (io.ReadSeeker, error) {
var buf bytes.Buffer
tarWriter := tar.NewWriter(&buf)
files := []struct {
@@ -202,7 +202,7 @@ func Test_BuildImageWithContexts(t *testing.T) {
},
{
Name: "test build from context archive and be able to use files in it",
- ContextArchive: func() (io.Reader, error) {
+ ContextArchive: func() (io.ReadSeeker, error) {
var buf bytes.Buffer
tarWriter := tar.NewWriter(&buf)
files := []struct {
@@ -255,14 +255,14 @@ func Test_BuildImageWithContexts(t *testing.T) {
ContextPath: "./testdata",
Dockerfile: "echo.Dockerfile",
ExpectedEchoOutput: "this is from the echo test Dockerfile",
- ContextArchive: func() (io.Reader, error) {
+ ContextArchive: func() (io.ReadSeeker, error) {
return nil, nil
},
},
{
Name: "it should error if neither a context nor a context archive are specified",
ContextPath: "",
- ContextArchive: func() (io.Reader, error) {
+ ContextArchive: func() (io.ReadSeeker, error) {
return nil, nil
},
ExpectedError: "create container: you must specify either a build context or an image",
@@ -275,9 +275,8 @@ func Test_BuildImageWithContexts(t *testing.T) {
t.Parallel()
ctx := context.Background()
a, err := testCase.ContextArchive()
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
+
req := testcontainers.ContainerRequest{
FromDockerfile: testcontainers.FromDockerfile{
ContextArchive: a,
diff --git a/docker.go b/docker.go
index 532460a5623..98058e61fee 100644
--- a/docker.go
+++ b/docker.go
@@ -508,12 +508,12 @@ func (c *DockerContainer) Exec(ctx context.Context, cmd []string, options ...tce
response, err := cli.ContainerExecCreate(ctx, c.ID, processOptions.ExecConfig)
if err != nil {
- return 0, nil, err
+ return 0, nil, fmt.Errorf("container exec create: %w", err)
}
hijack, err := cli.ContainerExecAttach(ctx, response.ID, container.ExecAttachOptions{})
if err != nil {
- return 0, nil, err
+ return 0, nil, fmt.Errorf("container exec attach: %w", err)
}
processOptions.Reader = hijack.Reader
@@ -528,7 +528,7 @@ func (c *DockerContainer) Exec(ctx context.Context, cmd []string, options ...tce
for {
execResp, err := cli.ContainerExecInspect(ctx, response.ID)
if err != nil {
- return 0, nil, err
+ return 0, nil, fmt.Errorf("container exec inspect: %w", err)
}
if !execResp.Running {
@@ -1112,7 +1112,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
// forward the host ports to the container ports.
sshdForwardPortsHook, err := exposeHostPorts(ctx, &req, req.HostAccessPorts...)
if err != nil {
- return nil, fmt.Errorf("failed to expose host ports: %w", err)
+ return nil, fmt.Errorf("expose host ports: %w", err)
}
defaultHooks = append(defaultHooks, sshdForwardPortsHook)
@@ -1127,7 +1127,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
resp, err := p.client.ContainerCreate(ctx, dockerInput, hostConfig, networkingConfig, platform, req.Name)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("container create: %w", err)
}
// #248: If there is more than one network specified in the request attach newly created container to them one by one
@@ -1142,7 +1142,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
}
err = p.client.NetworkConnect(ctx, nw.ID, resp.ID, &endpointSetting)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("network connect: %w", err)
}
}
}
@@ -1240,7 +1240,7 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
if !p.config.RyukDisabled {
r, err := spawner.reaper(context.WithValue(ctx, core.DockerHostContextKey, p.host), sessionID, p)
if err != nil {
- return nil, fmt.Errorf("%w: creating reaper failed", err)
+ return nil, fmt.Errorf("reaper: %w", err)
}
termSignal, err := r.Connect()
@@ -1295,12 +1295,12 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
func (p *DockerProvider) attemptToPullImage(ctx context.Context, tag string, pullOpt image.PullOptions) error {
registry, imageAuth, err := DockerImageAuth(ctx, tag)
if err != nil {
- p.Logger.Printf("Failed to get image auth for %s. Setting empty credentials for the image: %s. Error is:%s", registry, tag, err)
+ p.Logger.Printf("Failed to get image auth for %s. Setting empty credentials for the image: %s. Error is: %s", registry, tag, err)
} else {
// see https://github.com/docker/docs/blob/e8e1204f914767128814dca0ea008644709c117f/engine/api/sdk/examples.md?plain=1#L649-L657
encodedJSON, err := json.Marshal(imageAuth)
if err != nil {
- p.Logger.Printf("Failed to marshal image auth. Setting empty credentials for the image: %s. Error is:%s", tag, err)
+ p.Logger.Printf("Failed to marshal image auth. Setting empty credentials for the image: %s. Error is: %s", tag, err)
} else {
pullOpt.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
}
diff --git a/docker_auth.go b/docker_auth.go
index d45393f3252..99e2d2fdba1 100644
--- a/docker_auth.go
+++ b/docker_auth.go
@@ -2,8 +2,13 @@ package testcontainers
import (
"context"
+ "crypto/md5"
"encoding/base64"
+ "encoding/hex"
"encoding/json"
+ "errors"
+ "fmt"
+ "io"
"net/url"
"os"
"sync"
@@ -21,15 +26,21 @@ var defaultRegistryFn = defaultRegistry
// Finally, it will use the credential helpers to extract the information from the docker config file
// for that registry, if it exists.
func DockerImageAuth(ctx context.Context, image string) (string, registry.AuthConfig, error) {
- defaultRegistry := defaultRegistryFn(ctx)
- reg := core.ExtractRegistry(image, defaultRegistry)
-
- cfgs, err := getDockerAuthConfigs()
+ configs, err := getDockerAuthConfigs()
if err != nil {
+ reg := core.ExtractRegistry(image, defaultRegistryFn(ctx))
return reg, registry.AuthConfig{}, err
}
- if cfg, ok := getRegistryAuth(reg, cfgs); ok {
+ return dockerImageAuth(ctx, image, configs)
+}
+
+// dockerImageAuth returns the auth config for the given Docker image.
+func dockerImageAuth(ctx context.Context, image string, configs map[string]registry.AuthConfig) (string, registry.AuthConfig, error) {
+ defaultRegistry := defaultRegistryFn(ctx)
+ reg := core.ExtractRegistry(image, defaultRegistry)
+
+ if cfg, ok := getRegistryAuth(reg, configs); ok {
return reg, cfg, nil
}
@@ -80,24 +91,93 @@ func defaultRegistry(ctx context.Context) string {
return info.IndexServerAddress
}
-// authConfig represents the details of the auth config for a registry.
-type authConfig struct {
+// authConfigResult is a result looking up auth details for key.
+type authConfigResult struct {
key string
cfg registry.AuthConfig
+ err error
+}
+
+// credentialsCache is a cache for registry credentials.
+type credentialsCache struct {
+ entries map[string]credentials
+ mtx sync.RWMutex
+}
+
+// credentials represents the username and password for a registry.
+type credentials struct {
+ username string
+ password string
+}
+
+var creds = &credentialsCache{entries: map[string]credentials{}}
+
+// Get returns the username and password for the given hostname
+// as determined by the details in configPath.
+func (c *credentialsCache) Get(hostname, configKey string) (string, string, error) {
+ key := configKey + ":" + hostname
+ c.mtx.RLock()
+ entry, ok := c.entries[key]
+ c.mtx.RUnlock()
+
+ if ok {
+ return entry.username, entry.password, nil
+ }
+
+ // No entry found, request and cache.
+ user, password, err := dockercfg.GetRegistryCredentials(hostname)
+ if err != nil {
+ return "", "", fmt.Errorf("getting credentials for %s: %w", hostname, err)
+ }
+
+ c.mtx.Lock()
+ c.entries[key] = credentials{username: user, password: password}
+ c.mtx.Unlock()
+
+ return user, password, nil
+}
+
+// configFileKey returns a key to use for caching credentials based on
+// the contents of the currently active config.
+func configFileKey() (string, error) {
+ configPath, err := dockercfg.ConfigPath()
+ if err != nil {
+ return "", err
+ }
+
+ f, err := os.Open(configPath)
+ if err != nil {
+ return "", fmt.Errorf("open config file: %w", err)
+ }
+
+ defer f.Close()
+
+ h := md5.New()
+ if _, err := io.Copy(h, f); err != nil {
+ return "", fmt.Errorf("copying config file: %w", err)
+ }
+
+ return hex.EncodeToString(h.Sum(nil)), nil
}
// getDockerAuthConfigs returns a map with the auth configs from the docker config file
// using the registry as the key
-var getDockerAuthConfigs = sync.OnceValues(func() (map[string]registry.AuthConfig, error) {
+func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) {
cfg, err := getDockerConfig()
if err != nil {
return nil, err
}
- cfgs := map[string]registry.AuthConfig{}
- results := make(chan authConfig, len(cfg.AuthConfigs)+len(cfg.CredentialHelpers))
+ configKey, err := configFileKey()
+ if err != nil {
+ return nil, err
+ }
+
+ size := len(cfg.AuthConfigs) + len(cfg.CredentialHelpers)
+ cfgs := make(map[string]registry.AuthConfig, size)
+ results := make(chan authConfigResult, size)
var wg sync.WaitGroup
- wg.Add(len(cfg.AuthConfigs) + len(cfg.CredentialHelpers))
+ wg.Add(size)
for k, v := range cfg.AuthConfigs {
go func(k string, v dockercfg.AuthConfig) {
defer wg.Done()
@@ -112,16 +192,23 @@ var getDockerAuthConfigs = sync.OnceValues(func() (map[string]registry.AuthConfi
Username: v.Username,
}
- if v.Username == "" && v.Password == "" {
- u, p, _ := dockercfg.GetRegistryCredentials(k)
+ switch {
+ case ac.Username == "" && ac.Password == "":
+ // Look up credentials from the credential store.
+ u, p, err := creds.Get(k, configKey)
+ if err != nil {
+ results <- authConfigResult{err: err}
+ return
+ }
+
ac.Username = u
ac.Password = p
- }
-
- if v.Auth == "" {
+ case ac.Auth == "":
+ // Create auth from the username and password encoding.
ac.Auth = base64.StdEncoding.EncodeToString([]byte(ac.Username + ":" + ac.Password))
}
- results <- authConfig{key: k, cfg: ac}
+
+ results <- authConfigResult{key: k, cfg: ac}
}(k, v)
}
@@ -131,11 +218,19 @@ var getDockerAuthConfigs = sync.OnceValues(func() (map[string]registry.AuthConfi
go func(k string) {
defer wg.Done()
- ac := registry.AuthConfig{}
- u, p, _ := dockercfg.GetRegistryCredentials(k)
- ac.Username = u
- ac.Password = p
- results <- authConfig{key: k, cfg: ac}
+ u, p, err := creds.Get(k, configKey)
+ if err != nil {
+ results <- authConfigResult{err: err}
+ return
+ }
+
+ results <- authConfigResult{
+ key: k,
+ cfg: registry.AuthConfig{
+ Username: u,
+ Password: p,
+ },
+ }
}(k)
}
@@ -144,12 +239,22 @@ var getDockerAuthConfigs = sync.OnceValues(func() (map[string]registry.AuthConfi
close(results)
}()
- for ac := range results {
- cfgs[ac.key] = ac.cfg
+ var errs []error
+ for result := range results {
+ if result.err != nil {
+ errs = append(errs, result.err)
+ continue
+ }
+
+ cfgs[result.key] = result.cfg
+ }
+
+ if len(errs) > 0 {
+ return nil, errors.Join(errs...)
}
return cfgs, nil
-})
+}
// getDockerConfig returns the docker config file. It will internally check, in this particular order:
// 1. the DOCKER_AUTH_CONFIG environment variable, unmarshalling it into a dockercfg.Config
diff --git a/docker_auth_test.go b/docker_auth_test.go
index 1f8df17c093..4e55d2b9bff 100644
--- a/docker_auth_test.go
+++ b/docker_auth_test.go
@@ -2,13 +2,17 @@ package testcontainers
import (
"context"
+ _ "embed"
+ "encoding/base64"
"fmt"
+ "net"
"os"
"path/filepath"
"testing"
"github.com/cpuguy83/dockercfg"
"github.com/docker/docker/api/types/image"
+ "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -73,12 +77,7 @@ func TestGetDockerConfig(t *testing.T) {
})
t.Run("DOCKER_AUTH_CONFIG env var takes precedence", func(t *testing.T) {
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "`+exampleAuth+`": {}
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(t, exampleAuth, "", "")
t.Setenv("DOCKER_CONFIG", testDockerConfigDirPath)
cfg, err := getDockerConfig()
@@ -98,36 +97,23 @@ func TestGetDockerConfig(t *testing.T) {
})
t.Run("retrieve auth with DOCKER_AUTH_CONFIG env var", func(t *testing.T) {
- base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret
-
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "`+exampleAuth+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" }
- },
- "credsStore": "desktop"
- }`)
+ username, password := "gopher", "secret"
+ creds := setAuthConfig(t, exampleAuth, username, password)
registry, cfg, err := DockerImageAuth(context.Background(), exampleAuth+"/my/image:latest")
require.NoError(t, err)
require.NotEmpty(t, cfg)
assert.Equal(t, exampleAuth, registry)
- assert.Equal(t, "gopher", cfg.Username)
- assert.Equal(t, "secret", cfg.Password)
- assert.Equal(t, base64, cfg.Auth)
+ assert.Equal(t, username, cfg.Username)
+ assert.Equal(t, password, cfg.Password)
+ assert.Equal(t, creds, cfg.Auth)
})
t.Run("match registry authentication by host", func(t *testing.T) {
- base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret
imageReg := "example-auth.com"
imagePath := "/my/image:latest"
-
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "`+exampleAuth+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" }
- },
- "credsStore": "desktop"
- }`)
+ base64 := setAuthConfig(t, exampleAuth, "gopher", "secret")
registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath)
require.NoError(t, err)
@@ -140,17 +126,11 @@ func TestGetDockerConfig(t *testing.T) {
})
t.Run("fail to match registry authentication due to invalid host", func(t *testing.T) {
- base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret
imageReg := "example-auth.com"
imagePath := "/my/image:latest"
invalidRegistryURL := "://invalid-host"
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "`+invalidRegistryURL+`": { "username": "gopher", "password": "secret", "auth": "`+base64+`" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(t, invalidRegistryURL, "gopher", "secret")
registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath)
require.ErrorIs(t, err, dockercfg.ErrCredentialsNotFound)
@@ -168,16 +148,10 @@ func TestGetDockerConfig(t *testing.T) {
return ""
}
- base64 := "Z29waGVyOnNlY3JldA==" // gopher:secret
imageReg := ""
imagePath := "image:latest"
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "example-auth.com": { "username": "gopher", "password": "secret", "auth": "`+base64+`" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(t, "example-auth.com", "gopher", "secret")
registry, cfg, err := DockerImageAuth(context.Background(), imageReg+imagePath)
require.ErrorIs(t, err, dockercfg.ErrCredentialsNotFound)
@@ -198,7 +172,7 @@ func TestBuildContainerFromDockerfile(t *testing.T) {
WaitingFor: wait.ForLog("Ready to accept connections"),
}
- redisC, err := prepareRedisImage(ctx, req, t)
+ redisC, err := prepareRedisImage(ctx, req)
require.NoError(t, err)
terminateContainerOnEnd(t, ctx, redisC)
}
@@ -217,22 +191,16 @@ func removeImageFromLocalCache(t *testing.T, img string) {
Force: true,
PruneChildren: true,
})
- if err != nil {
+ if err != nil && !client.IsErrNotFound(err) {
t.Logf("could not remove image %s: %v\n", img, err)
}
}
func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) {
- mappedPort := prepareLocalRegistryWithAuth(t)
+ registryHost := prepareLocalRegistryWithAuth(t)
// using the same credentials as in the Docker Registry
- base64 := "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" // testuser:testpassword
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+mappedPort+`": { "username": "testuser", "password": "testpassword", "auth": "`+base64+`" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(t, registryHost, "testuser", "testpassword")
ctx := context.Background()
@@ -241,30 +209,26 @@ func TestBuildContainerFromDockerfileWithDockerAuthConfig(t *testing.T) {
Context: "./testdata",
Dockerfile: "auth.Dockerfile",
BuildArgs: map[string]*string{
- "REGISTRY_PORT": &mappedPort,
+ "REGISTRY_HOST": ®istryHost,
},
+ Repo: "localhost",
+ PrintBuildLog: true,
},
AlwaysPullImage: true, // make sure the authentication takes place
ExposedPorts: []string{"6379/tcp"},
WaitingFor: wait.ForLog("Ready to accept connections"),
}
- redisC, err := prepareRedisImage(ctx, req, t)
- require.NoError(t, err)
+ redisC, err := prepareRedisImage(ctx, req)
terminateContainerOnEnd(t, ctx, redisC)
+ require.NoError(t, err)
}
func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *testing.T) {
- mappedPort := prepareLocalRegistryWithAuth(t)
+ registryHost := prepareLocalRegistryWithAuth(t)
// using different credentials than in the Docker Registry
- base64 := "Zm9vOmJhcg==" // foo:bar
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+mappedPort+`": { "username": "foo", "password": "bar", "auth": "`+base64+`" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(t, registryHost, "foo", "bar")
ctx := context.Background()
@@ -273,7 +237,7 @@ func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *test
Context: "./testdata",
Dockerfile: "auth.Dockerfile",
BuildArgs: map[string]*string{
- "REGISTRY_PORT": &mappedPort,
+ "REGISTRY_HOST": ®istryHost,
},
},
AlwaysPullImage: true, // make sure the authentication takes place
@@ -281,26 +245,20 @@ func TestBuildContainerFromDockerfileShouldFailWithWrongDockerAuthConfig(t *test
WaitingFor: wait.ForLog("Ready to accept connections"),
}
- redisC, err := prepareRedisImage(ctx, req, t)
- require.Error(t, err)
+ redisC, err := prepareRedisImage(ctx, req)
terminateContainerOnEnd(t, ctx, redisC)
+ require.Error(t, err)
}
func TestCreateContainerFromPrivateRegistry(t *testing.T) {
- mappedPort := prepareLocalRegistryWithAuth(t)
+ registryHost := prepareLocalRegistryWithAuth(t)
// using the same credentials as in the Docker Registry
- base64 := "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" // testuser:testpassword
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+mappedPort+`": { "username": "testuser", "password": "testpassword", "auth": "`+base64+`" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(t, registryHost, "testuser", "testpassword")
ctx := context.Background()
req := ContainerRequest{
- Image: "localhost:" + mappedPort + "/redis:5.0-alpine",
+ Image: registryHost + "/redis:5.0-alpine",
AlwaysPullImage: true, // make sure the authentication takes place
ExposedPorts: []string{"6379/tcp"},
WaitingFor: wait.ForLog("Ready to accept connections"),
@@ -310,8 +268,8 @@ func TestCreateContainerFromPrivateRegistry(t *testing.T) {
ContainerRequest: req,
Started: true,
})
- require.NoError(t, err)
terminateContainerOnEnd(t, ctx, redisContainer)
+ require.NoError(t, err)
}
func prepareLocalRegistryWithAuth(t *testing.T) string {
@@ -354,10 +312,12 @@ func prepareLocalRegistryWithAuth(t *testing.T) string {
mappedPort, err := registryC.MappedPort(ctx, "5000/tcp")
require.NoError(t, err)
+ ip := localAddress(t)
mp := mappedPort.Port()
+ addr := ip + ":" + mp
t.Cleanup(func() {
- removeImageFromLocalCache(t, "localhost:"+mp+"/redis:5.0-alpine")
+ removeImageFromLocalCache(t, addr+"/redis:5.0-alpine")
})
t.Cleanup(func() {
require.NoError(t, registryC.Terminate(context.Background()))
@@ -366,17 +326,92 @@ func prepareLocalRegistryWithAuth(t *testing.T) string {
_, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
- return mp
+ return addr
}
-func prepareRedisImage(ctx context.Context, req ContainerRequest, t *testing.T) (Container, error) {
+func prepareRedisImage(ctx context.Context, req ContainerRequest) (Container, error) {
genContainerReq := GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: req,
Started: true,
}
- redisC, err := GenericContainer(ctx, genContainerReq)
+ return GenericContainer(ctx, genContainerReq)
+}
+
+// setAuthConfig sets the DOCKER_AUTH_CONFIG environment variable with
+// authentication for with the given host, username and password.
+// It returns the base64 encoded credentials.
+func setAuthConfig(t *testing.T, host, username, password string) string {
+ t.Helper()
+
+ var creds string
+ if username != "" || password != "" {
+ creds = base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
+ }
+
+ auth := fmt.Sprintf(`{
+ "auths": {
+ %q: {
+ "username": %q,
+ "password": %q,
+ "auth": %q
+ }
+ },
+ "credsStore": "desktop"
+}`,
+ host,
+ username,
+ password,
+ creds,
+ )
+ t.Setenv("DOCKER_AUTH_CONFIG", auth)
+
+ return creds
+}
+
+// localAddress returns the local address of the machine
+// which can be used to connect to the local registry.
+// This avoids the issues with localhost on WSL.
+func localAddress(t *testing.T) string {
+ if os.Getenv("WSL_DISTRO_NAME") == "" {
+ return "localhost"
+ }
+
+ conn, err := net.Dial("udp", "golang.org:80")
+ require.NoError(t, err)
+ defer conn.Close()
+
+ localAddr := conn.LocalAddr().(*net.UDPAddr)
+
+ return localAddr.IP.String()
+}
+
+//go:embed testdata/.docker/config.json
+var dockerConfig string
+
+func Test_getDockerAuthConfigs(t *testing.T) {
+ t.Run("file", func(t *testing.T) {
+ got, err := getDockerAuthConfigs()
+ require.NoError(t, err)
+ require.NotNil(t, got)
+ })
+
+ t.Run("env", func(t *testing.T) {
+ t.Setenv("DOCKER_AUTH_CONFIG", dockerConfig)
- return redisC, err
+ got, err := getDockerAuthConfigs()
+ require.NoError(t, err)
+
+ // We can only check the keys as the values are not deterministic.
+ expected := map[string]registry.AuthConfig{
+ "https://index.docker.io/v1/": {},
+ "https://example.com": {},
+ "https://my.private.registry": {},
+ }
+ for k := range got {
+ got[k] = registry.AuthConfig{}
+ }
+ require.Equal(t, expected, got)
+ })
}
diff --git a/docker_client.go b/docker_client.go
index c8e8e825b0f..04df7129167 100644
--- a/docker_client.go
+++ b/docker_client.go
@@ -79,8 +79,8 @@ func (c *DockerClient) Info(ctx context.Context) (system.Info, error) {
dockerInfo.OperatingSystem, dockerInfo.MemTotal/1024/1024,
infoLabels,
internal.Version,
- core.ExtractDockerHost(ctx),
- core.ExtractDockerSocket(ctx),
+ core.MustExtractDockerHost(ctx),
+ core.MustExtractDockerSocket(ctx),
core.SessionID(),
core.ProcessID(),
)
diff --git a/docker_test.go b/docker_test.go
index 0da11ef7f9a..402d944dce2 100644
--- a/docker_test.go
+++ b/docker_test.go
@@ -73,7 +73,7 @@ func TestContainerWithHostNetworkOptions(t *testing.T) {
nginxHighPort,
},
Privileged: true,
- WaitingFor: wait.ForListeningPort(nginxHighPort),
+ WaitingFor: wait.ForHTTP("/").WithPort(nginxHighPort),
HostConfigModifier: func(hc *container.HostConfig) {
hc.NetworkMode = "host"
},
@@ -182,7 +182,7 @@ func TestContainerWithHostNetwork(t *testing.T) {
ProviderType: providerType,
ContainerRequest: ContainerRequest{
Image: nginxAlpineImage,
- WaitingFor: wait.ForListeningPort(nginxHighPort),
+ WaitingFor: wait.ForHTTP("/").WithPort(nginxHighPort),
Files: []ContainerFile{
{
HostFilePath: absPath,
@@ -414,6 +414,7 @@ func TestTwoContainersExposingTheSamePort(t *testing.T) {
ExposedPorts: []string{
nginxDefaultPort,
},
+ WaitingFor: wait.ForHTTP("/").WithPort(nginxDefaultPort),
},
Started: true,
})
@@ -428,7 +429,7 @@ func TestTwoContainersExposingTheSamePort(t *testing.T) {
ExposedPorts: []string{
nginxDefaultPort,
},
- WaitingFor: wait.ForListeningPort(nginxDefaultPort),
+ WaitingFor: wait.ForHTTP("/").WithPort(nginxDefaultPort),
},
Started: true,
})
@@ -475,7 +476,7 @@ func TestContainerCreation(t *testing.T) {
ExposedPorts: []string{
nginxDefaultPort,
},
- WaitingFor: wait.ForListeningPort(nginxDefaultPort),
+ WaitingFor: wait.ForHTTP("/").WithPort(nginxDefaultPort),
},
Started: true,
})
@@ -529,7 +530,7 @@ func TestContainerCreationWithName(t *testing.T) {
ExposedPorts: []string{
nginxDefaultPort,
},
- WaitingFor: wait.ForListeningPort(nginxDefaultPort),
+ WaitingFor: wait.ForHTTP("/").WithPort(nginxDefaultPort),
Name: creationName,
Networks: []string{"bridge"},
},
@@ -592,7 +593,7 @@ func TestContainerCreationAndWaitForListeningPortLongEnough(t *testing.T) {
ExposedPorts: []string{
nginxDefaultPort,
},
- WaitingFor: wait.ForListeningPort(nginxDefaultPort), // default startupTimeout is 60s
+ WaitingFor: wait.ForHTTP("/").WithPort(nginxDefaultPort), // default startupTimeout is 60s
},
Started: true,
})
@@ -736,14 +737,10 @@ func TestContainerCreationWaitsForLog(t *testing.T) {
}
func Test_BuildContainerFromDockerfileWithBuildArgs(t *testing.T) {
- t.Log("getting ctx")
ctx := context.Background()
- t.Log("got ctx, creating container request")
-
// fromDockerfileWithBuildArgs {
ba := "build args value"
-
req := ContainerRequest{
FromDockerfile: FromDockerfile{
Context: filepath.Join(".", "testdata"),
@@ -769,23 +766,16 @@ func Test_BuildContainerFromDockerfileWithBuildArgs(t *testing.T) {
terminateContainerOnEnd(t, ctx, c)
ep, err := c.Endpoint(ctx, "http")
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
resp, err := http.Get(ep + "/env")
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
- if err != nil {
- t.Fatal(err)
- }
-
- assert.Equal(t, 200, resp.StatusCode)
- assert.Equal(t, ba, string(body))
+ require.NoError(t, err)
+ require.Equal(t, http.StatusAccepted, resp.StatusCode)
+ require.Equal(t, ba, string(body))
}
func Test_BuildContainerFromDockerfileWithBuildLog(t *testing.T) {
@@ -2290,3 +2280,30 @@ func TestDockerProvider_attemptToPullImage_retries(t *testing.T) {
})
}
}
+
+func TestCustomPrefixTrailingSlashIsProperlyRemovedIfPresent(t *testing.T) {
+ hubPrefixWithTrailingSlash := "public.ecr.aws/"
+ dockerImage := "amazonlinux/amazonlinux:2023"
+
+ ctx := context.Background()
+ req := ContainerRequest{
+ Image: dockerImage,
+ ImageSubstitutors: []ImageSubstitutor{newPrependHubRegistry(hubPrefixWithTrailingSlash)},
+ }
+
+ c, err := GenericContainer(ctx, GenericContainerRequest{
+ ContainerRequest: req,
+ Started: true,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ terminateContainerOnEnd(t, ctx, c)
+ }()
+
+ // enforce the concrete type, as GenericContainer returns an interface,
+ // which will be changed in future implementations of the library
+ dockerContainer := c.(*DockerContainer)
+ assert.Equal(t, fmt.Sprintf("%s%s", hubPrefixWithTrailingSlash, dockerImage), dockerContainer.Image)
+}
diff --git a/docs/features/configuration.md b/docs/features/configuration.md
index aa5a0d0a615..ee5b6a4d696 100644
--- a/docs/features/configuration.md
+++ b/docs/features/configuration.md
@@ -85,7 +85,7 @@ See [Docker environment variables](https://docs.docker.com/engine/reference/comm
3. `${HOME}/.docker/desktop/docker.sock`.
4. `/run/user/${UID}/docker.sock`, where `${UID}` is the user ID of the current user.
-7. The default Docker socket including schema will be returned if none of the above are set.
+7. The library panics if none of the above are set, meaning that the Docker host was not detected.
## Docker socket path detection
@@ -109,4 +109,4 @@ Path to Docker's socket. Used by Ryuk, Docker Compose, and a few other container
6. Else, the default location of the docker socket is used: `/var/run/docker.sock`
-In any case, if the docker socket schema is `tcp://`, the default docker socket path will be returned.
+The library panics if the Docker host cannot be discovered.
diff --git a/docs/features/wait/file.md b/docs/features/wait/file.md
new file mode 100644
index 00000000000..8bdf8ade277
--- /dev/null
+++ b/docs/features/wait/file.md
@@ -0,0 +1,14 @@
+# File Wait Strategy
+
+File Wait Strategy waits for a file to exist in the container, and allows to set the following conditions:
+
+- the file to wait for.
+- a matcher which reads the file content, no-op if nil or not set.
+- the startup timeout to be used in seconds, default is 60 seconds.
+- the poll interval to be used in milliseconds, default is 100 milliseconds.
+
+## Waiting for file to exist and extract the content
+
+
+[Waiting for file to exist and extract the content](../../../wait/file_test.go) inside_block:waitForFileWithMatcher
+
diff --git a/docs/features/wait/introduction.md b/docs/features/wait/introduction.md
index 5af611321c8..87adabc3edf 100644
--- a/docs/features/wait/introduction.md
+++ b/docs/features/wait/introduction.md
@@ -8,6 +8,7 @@ Below you can find a list of the available wait strategies that you can use:
- [Exec](./exec.md)
- [Exit](./exit.md)
+- [File](./file.md)
- [Health](./health.md)
- [HostPort](./host_port.md)
- [HTTP](./http.md)
diff --git a/docs/modules/grafana-lgtm.md b/docs/modules/grafana-lgtm.md
index 56e263aac0b..b76c56bc667 100644
--- a/docs/modules/grafana-lgtm.md
+++ b/docs/modules/grafana-lgtm.md
@@ -1,6 +1,6 @@
# Grafana LGTM
-Not available until the next release of testcontainers-go :material-tag: main
+Since testcontainers-go :material-tag: v0.33.0
## Introduction
@@ -24,7 +24,7 @@ go get github.com/testcontainers/testcontainers-go/modules/grafanalgtm
### Run function
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
!!!info
The `RunContainer(ctx, opts...)` function is deprecated and will be removed in the next major release of _Testcontainers for Go_.
@@ -50,7 +50,7 @@ E.g. `Run(context.Background(), "grafana/otel-lgtm:0.6.0")`.
#### Admin Credentials
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
If you need to set different admin credentials in the Grafana LGTM container, you can set them using the `WithAdminCredentials(user, password)` option.
@@ -65,37 +65,37 @@ The Grafana LGTM container exposes the following methods:
#### Grafana Endpoint
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
The `HttpEndpoint(ctx)` method returns the HTTP endpoint to connect to Grafana, using the default `3000` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
#### Loki Endpoint
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
The `LokiEndpoint(ctx)` method returns the HTTP endpoint to connect to Loki, using the default `3100` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
#### Tempo Endpoint
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
The `TempoEndpoint(ctx)` method returns the HTTP endpoint to connect to Tempo, using the default `3200` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
#### Otel HTTP Endpoint
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
The `OtelHTTPEndpoint(ctx)` method returns the endpoint to connect to Otel using HTTP, using the default `4318` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
#### Otel gRPC Endpoint
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
The `OtelGRPCEndpoint(ctx)` method returns the endpoint to connect to Otel using gRPC, using the default `4317` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
#### Prometheus Endpoint
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
The `PrometheusHttpEndpoint(ctx)` method returns the endpoint to connect to Prometheus, using the default `9090` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
diff --git a/docs/modules/registry.md b/docs/modules/registry.md
index ced274a2b65..94e7f4dc84b 100644
--- a/docs/modules/registry.md
+++ b/docs/modules/registry.md
@@ -39,6 +39,17 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
- `string`, the Docker image to use.
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.
+### Docker Auth Config
+
+The module exposes a way to set the Docker Auth Config for the Registry container, thanks to the `SetDockerAuthConfig` function.
+This is useful when you need to pull images from a private registry. It basically sets the `DOCKER_AUTH_CONFIG` environment variable
+with authentication for the given host, username and password sets. It returns a function to reset the environment back to the previous state,
+which is helpful when you need to reset the environment after a test.
+
+On the same hand, the module also exposes a way to build a Docker Auth Config for the Registry container, thanks to the `DockerAuthConfig` helper function.
+This function returns a map of `AuthConfigs` including base64 encoded Auth field for the provided details.
+It also accepts additional host, username and password triples to add more auth configurations.
+
### Container Options
When starting the Registry container, you can pass options in a variadic way to configure it.
@@ -80,10 +91,15 @@ Otherwise, the Registry will start but you won't be able to read any images from
The Registry container exposes the following methods:
+#### HostAddress
+
+This method returns the returns the host address including port of the Distribution Registry.
+E.g. `localhost:32878`.
+
#### Address
This method returns the HTTP address string to connect to the Distribution Registry, so that you can use to connect to the Registry.
-E.g. `http://localhost:32878/v2/_catalog`.
+E.g. `http://localhost:32878`.
[HTTP Address](../../modules/registry/registry_test.go) inside_block:httpAddress
diff --git a/docs/modules/valkey.md b/docs/modules/valkey.md
index 3210c233cb6..40b124f242a 100644
--- a/docs/modules/valkey.md
+++ b/docs/modules/valkey.md
@@ -1,6 +1,6 @@
# Valkey
-Not available until the next release of testcontainers-go :material-tag: main
+Since testcontainers-go :material-tag: v0.33.0
## Introduction
@@ -24,7 +24,7 @@ go get github.com/testcontainers/testcontainers-go/modules/valkey
### Run function
-- Not available until the next release of testcontainers-go :material-tag: main
+- Since testcontainers-go :material-tag: v0.33.0
!!!info
The `RunContainer(ctx, opts...)` function is deprecated and will be removed in the next major release of _Testcontainers for Go_.
@@ -52,10 +52,14 @@ E.g. `Run(context.Background(), "valkey/valkey:7.2.5")`.
#### Snapshotting
+- Since testcontainers-go :material-tag: v0.33.0
+
By default Valkey saves snapshots of the dataset on disk, in a binary file called dump.rdb. You can configure Valkey to have it save the dataset every `N` seconds if there are at least `M` changes in the dataset. E.g. `WithSnapshotting(10, 1)`.
#### Log Level
+- Since testcontainers-go :material-tag: v0.33.0
+
You can easily set the valkey logging level. E.g. `WithLogLevel(LogLevelDebug)`.
#### Valkey configuration
@@ -68,6 +72,8 @@ The Valkey container exposes the following methods:
#### ConnectionString
+- Since testcontainers-go :material-tag: v0.33.0
+
This method returns the connection string to connect to the Valkey container, using the default `6379` port.
diff --git a/docs/system_requirements/ci/aws_codebuild.md b/docs/system_requirements/ci/aws_codebuild.md
index 1320e6bdba7..a64d3691e09 100644
--- a/docs/system_requirements/ci/aws_codebuild.md
+++ b/docs/system_requirements/ci/aws_codebuild.md
@@ -11,7 +11,7 @@ version: 0.2
phases:
install:
runtime-versions:
- golang: 1.21
+ golang: 1.22
build:
commands:
- go test ./...
diff --git a/docs/system_requirements/ci/concourse_ci.md b/docs/system_requirements/ci/concourse_ci.md
index 2d711d3b731..ede2aac2aeb 100644
--- a/docs/system_requirements/ci/concourse_ci.md
+++ b/docs/system_requirements/ci/concourse_ci.md
@@ -36,7 +36,7 @@ jobs:
start_docker
cd repo
- docker run -it --rm -v "$PWD:$PWD" -w "$PWD" -v /var/run/docker.sock:/var/run/docker.sock golang:1.21 go test ./...
+ docker run -it --rm -v "$PWD:$PWD" -w "$PWD" -v /var/run/docker.sock:/var/run/docker.sock golang:1.22 go test ./...
```
Finally, you can use Concourse's [fly CLI](https://concourse-ci.org/fly.html) to set the pipeline and trigger the job:
diff --git a/docs/system_requirements/ci/dind_patterns.md b/docs/system_requirements/ci/dind_patterns.md
index 853a174d326..267a4baaf59 100644
--- a/docs/system_requirements/ci/dind_patterns.md
+++ b/docs/system_requirements/ci/dind_patterns.md
@@ -24,7 +24,7 @@ $ tree .
âââ platform
âââ integration_test.go
-$ docker run -it --rm -v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/docker.sock golang:1.21 go test ./... -v
+$ docker run -it --rm -v $PWD:$PWD -w $PWD -v /var/run/docker.sock:/var/run/docker.sock golang:1.22 go test ./... -v
```
Where:
@@ -45,7 +45,7 @@ The same can be achieved with Docker Compose:
```yaml
tests:
- image: golang:1.21
+ image: golang:1.22
stop_signal: SIGKILL
stdin_open: true
tty: true
diff --git a/docs/system_requirements/ci/gitlab_ci.md b/docs/system_requirements/ci/gitlab_ci.md
index 6289dc59ae2..679292a010f 100644
--- a/docs/system_requirements/ci/gitlab_ci.md
+++ b/docs/system_requirements/ci/gitlab_ci.md
@@ -57,7 +57,7 @@ variables:
DOCKER_DRIVER: overlay2
test:
- image: golang:1.21
+ image: golang:1.22
stage: test
script: go test ./... -v
```
diff --git a/docs/system_requirements/ci/tekton.md b/docs/system_requirements/ci/tekton.md
index a8280a0802c..28f23f77fb4 100644
--- a/docs/system_requirements/ci/tekton.md
+++ b/docs/system_requirements/ci/tekton.md
@@ -16,7 +16,7 @@ spec:
- name: source
steps:
- name: read
- image: golang:1.21
+ image: golang:1.22
workingDir: $(workspaces.source.path)
script: go test ./... -v
volumeMounts:
diff --git a/docs/system_requirements/ci/travis.md b/docs/system_requirements/ci/travis.md
index 0701d2e3675..bfc81c303d6 100644
--- a/docs/system_requirements/ci/travis.md
+++ b/docs/system_requirements/ci/travis.md
@@ -7,7 +7,7 @@ is the minimal required config.
language: go
go:
- 1.x
-- "1.21"
+- "1.22"
services:
- docker
diff --git a/examples/nginx/go.mod b/examples/nginx/go.mod
index a9aea2bea15..a6f67e793c9 100644
--- a/examples/nginx/go.mod
+++ b/examples/nginx/go.mod
@@ -1,8 +1,8 @@
module github.com/testcontainers/testcontainers-go/examples/nginx
-go 1.21
+go 1.22
-require github.com/testcontainers/testcontainers-go v0.32.0
+require github.com/testcontainers/testcontainers-go v0.33.0
replace github.com/testcontainers/testcontainers-go => ../..
@@ -16,7 +16,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/examples/nginx/go.sum b/examples/nginx/go.sum
index 4de62e95bc7..85338720c8a 100644
--- a/examples/nginx/go.sum
+++ b/examples/nginx/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/examples/toxiproxy/go.mod b/examples/toxiproxy/go.mod
index 1b0f4663da4..04be7036c13 100644
--- a/examples/toxiproxy/go.mod
+++ b/examples/toxiproxy/go.mod
@@ -1,12 +1,12 @@
module github.com/testcontainers/testcontainers-go/examples/toxiproxy
-go 1.21
+go 1.22
require (
github.com/Shopify/toxiproxy/v2 v2.8.0
github.com/go-redis/redis/v8 v8.11.5
github.com/google/uuid v1.6.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -21,7 +21,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/examples/toxiproxy/go.sum b/examples/toxiproxy/go.sum
index d659cb13a3e..c62c0ac532c 100644
--- a/examples/toxiproxy/go.sum
+++ b/examples/toxiproxy/go.sum
@@ -29,8 +29,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/generic.go b/generic.go
index debea75677e..fd13a607dee 100644
--- a/generic.go
+++ b/generic.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
+ "strings"
"sync"
"github.com/testcontainers/testcontainers-go/internal/core"
@@ -57,7 +58,7 @@ func GenericContainer(ctx context.Context, req GenericContainerRequest) (Contain
}
provider, err := req.ProviderType.GetProvider(WithLogger(logging))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("get provider: %w", err)
}
defer provider.Close()
@@ -74,12 +75,20 @@ func GenericContainer(ctx context.Context, req GenericContainerRequest) (Contain
}
if err != nil {
// At this point `c` might not be nil. Give the caller an opportunity to call Destroy on the container.
+ // TODO: Remove this debugging.
+ if strings.Contains(err.Error(), "toomanyrequests") {
+ // Debugging information for rate limiting.
+ cfg, err := getDockerConfig()
+ if err == nil {
+ fmt.Printf("XXX: too many requests: %+v", cfg)
+ }
+ }
return c, fmt.Errorf("create container: %w", err)
}
if req.Started && !c.IsRunning() {
if err := c.Start(ctx); err != nil {
- return c, fmt.Errorf("failed to start container: %w", err)
+ return c, fmt.Errorf("start container: %w", err)
}
}
return c, nil
diff --git a/go.mod b/go.mod
index 2ed17ab739e..8e9b20a12aa 100644
--- a/go.mod
+++ b/go.mod
@@ -1,13 +1,13 @@
module github.com/testcontainers/testcontainers-go
-go 1.21
+go 1.22
require (
dario.cat/mergo v1.0.0
github.com/cenkalti/backoff/v4 v4.2.1
github.com/containerd/platforms v0.2.1
github.com/cpuguy83/dockercfg v0.3.1
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/docker/go-connections v0.5.0
github.com/google/uuid v1.6.0
github.com/magiconair/properties v1.8.7
@@ -47,6 +47,7 @@ require (
github.com/rogpeppe/go-internal v1.8.1 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/stretchr/objx v0.5.2 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
diff --git a/go.sum b/go.sum
index 02f7ebe2396..ef44f5f91c9 100644
--- a/go.sum
+++ b/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -102,6 +102,8 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
diff --git a/image_test.go b/image_test.go
index 17595b65908..795a521b295 100644
--- a/image_test.go
+++ b/image_test.go
@@ -10,7 +10,7 @@ import (
)
func TestImageList(t *testing.T) {
- t.Setenv("DOCKER_HOST", core.ExtractDockerHost(context.Background()))
+ t.Setenv("DOCKER_HOST", core.MustExtractDockerHost(context.Background()))
provider, err := ProviderDocker.GetProvider()
if err != nil {
@@ -54,7 +54,7 @@ func TestImageList(t *testing.T) {
}
func TestSaveImages(t *testing.T) {
- t.Setenv("DOCKER_HOST", core.ExtractDockerHost(context.Background()))
+ t.Setenv("DOCKER_HOST", core.MustExtractDockerHost(context.Background()))
provider, err := ProviderDocker.GetProvider()
if err != nil {
diff --git a/internal/config/config.go b/internal/config/config.go
index 7f0039cecb7..a172fa3a165 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -11,7 +11,7 @@ import (
"github.com/magiconair/properties"
)
-const ReaperDefaultImage = "testcontainers/ryuk:0.8.1"
+const ReaperDefaultImage = "testcontainers/ryuk:0.9.0"
var (
tcConfig Config
@@ -20,18 +20,71 @@ var (
// testcontainersConfig {
-// Config represents the configuration for Testcontainers
+// Config represents the configuration for Testcontainers.
+// User values are read from ~/.testcontainers.properties file which can be overridden
+// using the specified environment variables. For more information, see [Custom Configuration].
+//
+// The Ryuk prefixed fields controls the [Garbage Collector] feature, which ensures that
+// resources are cleaned up after the test execution.
+//
+// [Garbage Collector]: https://golang.testcontainers.org/features/garbage_collector/
+// [Custom Configuration]: https://golang.testcontainers.org/features/configuration/
type Config struct {
- Host string `properties:"docker.host,default="`
- TLSVerify int `properties:"docker.tls.verify,default=0"`
- CertPath string `properties:"docker.cert.path,default="`
- HubImageNamePrefix string `properties:"hub.image.name.prefix,default="`
- RyukDisabled bool `properties:"ryuk.disabled,default=false"`
- RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"`
+ // Host is the address of the Docker daemon.
+ //
+ // Environment variable: DOCKER_HOST
+ Host string `properties:"docker.host,default="`
+
+ // TLSVerify is a flag to enable or disable TLS verification when connecting to a Docker daemon.
+ //
+ // Environment variable: DOCKER_TLS_VERIFY
+ TLSVerify int `properties:"docker.tls.verify,default=0"`
+
+ // CertPath is the path to the directory containing the Docker certificates.
+ // This is used when connecting to a Docker daemon over TLS.
+ //
+ // Environment variable: DOCKER_CERT_PATH
+ CertPath string `properties:"docker.cert.path,default="`
+
+ // HubImageNamePrefix is the prefix used for the images pulled from the Docker Hub.
+ // This is useful when running tests in environments with restricted internet access.
+ //
+ // Environment variable: TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX
+ HubImageNamePrefix string `properties:"hub.image.name.prefix,default="`
+
+ // RyukDisabled is a flag to enable or disable the Garbage Collector.
+ // Setting this to true will prevent testcontainers from automatically cleaning up
+ // resources, which is particularly important in tests which timeout as they
+ // don't run test clean up.
+ //
+ // Environment variable: TESTCONTAINERS_RYUK_DISABLED
+ RyukDisabled bool `properties:"ryuk.disabled,default=false"`
+
+ // RyukPrivileged is a flag to enable or disable the privileged mode for the Garbage Collector container.
+ // Setting this to true will run the Garbage Collector container in privileged mode.
+ //
+ // Environment variable: TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED
+ RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"`
+
+ // RyukReconnectionTimeout is the time to wait before attempting to reconnect to the Garbage Collector container.
+ //
+ // Environment variable: TESTCONTAINERS_RYUK_RECONNECTION_TIMEOUT
RyukReconnectionTimeout time.Duration `properties:"ryuk.reconnection.timeout,default=10s"`
- RyukConnectionTimeout time.Duration `properties:"ryuk.connection.timeout,default=1m"`
- RyukVerbose bool `properties:"ryuk.verbose,default=false"`
- TestcontainersHost string `properties:"tc.host,default="`
+
+ // RyukConnectionTimeout is the time to wait before timing out when connecting to the Garbage Collector container.
+ //
+ // Environment variable: TESTCONTAINERS_RYUK_CONNECTION_TIMEOUT
+ RyukConnectionTimeout time.Duration `properties:"ryuk.connection.timeout,default=1m"`
+
+ // RyukVerbose is a flag to enable or disable verbose logging for the Garbage Collector.
+ //
+ // Environment variable: TESTCONTAINERS_RYUK_VERBOSE
+ RyukVerbose bool `properties:"ryuk.verbose,default=false"`
+
+ // TestcontainersHost is the address of the Testcontainers host.
+ //
+ // Environment variable: TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE
+ TestcontainersHost string `properties:"tc.host,default="`
}
// }
diff --git a/internal/core/client.go b/internal/core/client.go
index 64af509b9fa..04a54bcbc54 100644
--- a/internal/core/client.go
+++ b/internal/core/client.go
@@ -14,7 +14,7 @@ import (
func NewClient(ctx context.Context, ops ...client.Opt) (*client.Client, error) {
tcConfig := config.Read()
- dockerHost := ExtractDockerHost(ctx)
+ dockerHost := MustExtractDockerHost(ctx)
opts := []client.Opt{client.FromEnv, client.WithAPIVersionNegotiation()}
if dockerHost != "" {
diff --git a/internal/core/docker_host.go b/internal/core/docker_host.go
index 0b55ec01c88..3088a3742bf 100644
--- a/internal/core/docker_host.go
+++ b/internal/core/docker_host.go
@@ -56,7 +56,24 @@ func DefaultGatewayIP() (string, error) {
return ip, nil
}
-// ExtractDockerHost Extracts the docker host from the different alternatives, caching the result to avoid unnecessary
+// dockerHostCheck Use a vanilla Docker client to check if the Docker host is reachable.
+// It will avoid recursive calls to this function.
+var dockerHostCheck = func(ctx context.Context, host string) error {
+ cli, err := client.NewClientWithOpts(client.FromEnv, client.WithHost(host), client.WithAPIVersionNegotiation())
+ if err != nil {
+ return fmt.Errorf("new client: %w", err)
+ }
+ defer cli.Close()
+
+ _, err = cli.Info(ctx)
+ if err != nil {
+ return fmt.Errorf("docker info: %w", err)
+ }
+
+ return nil
+}
+
+// MustExtractDockerHost Extracts the docker host from the different alternatives, caching the result to avoid unnecessary
// calculations. Use this function to get the actual Docker host. This function does not consider Windows containers at the moment.
// The possible alternatives are:
//
@@ -66,16 +83,21 @@ func DefaultGatewayIP() (string, error) {
// 4. Docker host from the default docker socket path, without the unix schema.
// 5. Docker host from the "docker.host" property in the ~/.testcontainers.properties file.
// 6. Rootless docker socket path.
-// 7. Else, the default Docker socket including schema will be returned.
-func ExtractDockerHost(ctx context.Context) string {
+// 7. Else, because the Docker host is not set, it panics.
+func MustExtractDockerHost(ctx context.Context) string {
dockerHostOnce.Do(func() {
- dockerHostCache = extractDockerHost(ctx)
+ cache, err := extractDockerHost(ctx)
+ if err != nil {
+ panic(err)
+ }
+
+ dockerHostCache = cache
})
return dockerHostCache
}
-// ExtractDockerSocket Extracts the docker socket from the different alternatives, removing the socket schema and
+// MustExtractDockerSocket Extracts the docker socket from the different alternatives, removing the socket schema and
// caching the result to avoid unnecessary calculations. Use this function to get the docker socket path,
// not the host (e.g. mounting the socket in a container). This function does not consider Windows containers at the moment.
// The possible alternatives are:
@@ -83,12 +105,12 @@ func ExtractDockerHost(ctx context.Context) string {
// 1. Docker host from the "tc.host" property in the ~/.testcontainers.properties file.
// 2. The TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE environment variable.
// 3. Using a Docker client, check if the Info().OperativeSystem is "Docker Desktop" and return the default docker socket path for rootless docker.
-// 4. Else, Get the current Docker Host from the existing strategies: see ExtractDockerHost.
+// 4. Else, Get the current Docker Host from the existing strategies: see MustExtractDockerHost.
// 5. If the socket contains the unix schema, the schema is removed (e.g. unix:///var/run/docker.sock -> /var/run/docker.sock)
// 6. Else, the default location of the docker socket is used (/var/run/docker.sock)
//
-// In any case, if the docker socket schema is "tcp://", the default docker socket path will be returned.
-func ExtractDockerSocket(ctx context.Context) string {
+// It panics if a Docker client cannot be created, or the Docker host cannot be discovered.
+func MustExtractDockerSocket(ctx context.Context) string {
dockerSocketPathOnce.Do(func() {
dockerSocketPathCache = extractDockerSocket(ctx)
})
@@ -98,7 +120,7 @@ func ExtractDockerSocket(ctx context.Context) string {
// extractDockerHost Extracts the docker host from the different alternatives, without caching the result.
// This internal method is handy for testing purposes.
-func extractDockerHost(ctx context.Context) string {
+func extractDockerHost(ctx context.Context) (string, error) {
dockerHostFns := []func(context.Context) (string, error){
testcontainersHostFromProperties,
dockerHostFromEnv,
@@ -108,25 +130,35 @@ func extractDockerHost(ctx context.Context) string {
rootlessDockerSocketPath,
}
- outerErr := ErrSocketNotFound
+ var errs []error
for _, dockerHostFn := range dockerHostFns {
dockerHost, err := dockerHostFn(ctx)
if err != nil {
- outerErr = fmt.Errorf("%w: %w", outerErr, err)
+ if !isHostNotSet(err) {
+ errs = append(errs, err)
+ }
continue
}
- return dockerHost
+ if err = dockerHostCheck(ctx, dockerHost); err != nil {
+ errs = append(errs, fmt.Errorf("check host %q: %w", dockerHost, err))
+ continue
+ }
+
+ return dockerHost, nil
}
- // We are not supporting Windows containers at the moment
- return DockerSocketPathWithSchema
+ if len(errs) > 0 {
+ return "", errors.Join(errs...)
+ }
+
+ return "", ErrSocketNotFound
}
-// extractDockerHost Extracts the docker socket from the different alternatives, without caching the result.
+// extractDockerSocket Extracts the docker socket from the different alternatives, without caching the result.
// It will internally use the default Docker client, calling the internal method extractDockerSocketFromClient with it.
// This internal method is handy for testing purposes.
-// If a Docker client cannot be created, the program will panic.
+// It panics if a Docker client cannot be created, or the Docker host is not discovered.
func extractDockerSocket(ctx context.Context) string {
cli, err := NewClient(ctx)
if err != nil {
@@ -140,6 +172,7 @@ func extractDockerSocket(ctx context.Context) string {
// extractDockerSocketFromClient Extracts the docker socket from the different alternatives, without caching the result,
// and receiving an instance of the Docker API client interface.
// This internal method is handy for testing purposes, passing a mock type simulating the desired behaviour.
+// It panics if the Docker Info call errors, or the Docker host is not discovered.
func extractDockerSocketFromClient(ctx context.Context, cli client.APIClient) string {
// check that the socket is not a tcp or unix socket
checkDockerSocketFn := func(socket string) string {
@@ -160,7 +193,7 @@ func extractDockerSocketFromClient(ctx context.Context, cli client.APIClient) st
return checkDockerSocketFn(tcHost)
}
- testcontainersDockerSocket, err := dockerSocketOverridePath(ctx)
+ testcontainersDockerSocket, err := dockerSocketOverridePath()
if err == nil {
return checkDockerSocketFn(testcontainersDockerSocket)
}
@@ -179,11 +212,33 @@ func extractDockerSocketFromClient(ctx context.Context, cli client.APIClient) st
return DockerSocketPath
}
- dockerHost := extractDockerHost(ctx)
+ dockerHost, err := extractDockerHost(ctx)
+ if err != nil {
+ panic(err) // Docker host is required to get the Docker socket
+ }
return checkDockerSocketFn(dockerHost)
}
+// isHostNotSet returns true if the error is related to the Docker host
+// not being set, false otherwise.
+func isHostNotSet(err error) bool {
+ switch {
+ case errors.Is(err, ErrTestcontainersHostNotSetInProperties),
+ errors.Is(err, ErrDockerHostNotSet),
+ errors.Is(err, ErrDockerSocketNotSetInContext),
+ errors.Is(err, ErrDockerSocketNotSetInProperties),
+ errors.Is(err, ErrSocketNotFoundInPath),
+ errors.Is(err, ErrXDGRuntimeDirNotSet),
+ errors.Is(err, ErrRootlessDockerNotFoundHomeRunDir),
+ errors.Is(err, ErrRootlessDockerNotFoundHomeDesktopDir),
+ errors.Is(err, ErrRootlessDockerNotFoundRunDir):
+ return true
+ default:
+ return false
+ }
+}
+
// dockerHostFromEnv returns the docker host from the DOCKER_HOST environment variable, if it's not empty
func dockerHostFromEnv(ctx context.Context) (string, error) {
if dockerHostPath := os.Getenv("DOCKER_HOST"); dockerHostPath != "" {
@@ -220,7 +275,7 @@ func dockerHostFromProperties(ctx context.Context) (string, error) {
// dockerSocketOverridePath returns the docker socket from the TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE environment variable,
// if it's not empty
-func dockerSocketOverridePath(ctx context.Context) (string, error) {
+func dockerSocketOverridePath() (string, error) {
if dockerHostPath, exists := os.LookupEnv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"); exists {
return dockerHostPath, nil
}
diff --git a/internal/core/docker_host_test.go b/internal/core/docker_host_test.go
index dbdbaa31fe6..eceee573fcb 100644
--- a/internal/core/docker_host_test.go
+++ b/internal/core/docker_host_test.go
@@ -2,6 +2,7 @@ package core
import (
"context"
+ "fmt"
"os"
"path/filepath"
"testing"
@@ -40,6 +41,22 @@ var resetSocketOverrideFn = func() {
os.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", originalDockerSocketOverride)
}
+func testCallbackCheckPassing(_ context.Context, _ string) error {
+ return nil
+}
+
+func testCallbackCheckError(_ context.Context, _ string) error {
+ return fmt.Errorf("could not check the Docker host")
+}
+
+func mockCallbackCheck(t *testing.T, fn func(_ context.Context, _ string) error) {
+ oldCheck := dockerHostCheck
+ dockerHostCheck = fn
+ t.Cleanup(func() {
+ dockerHostCheck = oldCheck
+ })
+}
+
func TestExtractDockerHost(t *testing.T) {
setupDockerHostNotFound(t)
// do not mess with local .testcontainers.properties
@@ -47,17 +64,21 @@ func TestExtractDockerHost(t *testing.T) {
t.Setenv("HOME", tmpDir)
t.Setenv("USERPROFILE", tmpDir) // Windows support
- t.Run("Docker Host as extracted just once", func(t *testing.T) {
+ // apply the passing check to all sub-tests
+ mockCallbackCheck(t, testCallbackCheckPassing)
+
+ t.Run("Docker Host is extracted just once", func(t *testing.T) {
expected := "/path/to/docker.sock"
t.Setenv("DOCKER_HOST", expected)
- host := ExtractDockerHost(context.Background())
+
+ host := MustExtractDockerHost(context.Background())
assert.Equal(t, expected, host)
t.Setenv("DOCKER_HOST", "/path/to/another/docker.sock")
- host = ExtractDockerHost(context.Background())
- assert.Equal(t, expected, host)
+ host = MustExtractDockerHost(context.Background())
+ require.Equal(t, expected, host)
})
t.Run("Testcontainers Host is resolved first", func(t *testing.T) {
@@ -66,16 +87,30 @@ func TestExtractDockerHost(t *testing.T) {
setupTestcontainersProperties(t, content)
- host := extractDockerHost(context.Background())
+ host, err := extractDockerHost(context.Background())
+ require.NoError(t, err)
+ require.Equal(t, testRemoteHost, host)
+ })
+
+ t.Run("Testcontainers Host is resolved first but not reachable", func(t *testing.T) {
+ t.Setenv("DOCKER_HOST", "/path/to/docker.sock")
+ content := "tc.host=" + testRemoteHost
- assert.Equal(t, testRemoteHost, host)
+ setupTestcontainersProperties(t, content)
+
+ // mock the callback check to return an error
+ mockCallbackCheck(t, testCallbackCheckError)
+
+ host, err := extractDockerHost(context.Background())
+ require.Error(t, err)
+ require.Equal(t, "", host)
})
t.Run("Docker Host as environment variable", func(t *testing.T) {
t.Setenv("DOCKER_HOST", "/path/to/docker.sock")
- host := extractDockerHost(context.Background())
-
- assert.Equal(t, "/path/to/docker.sock", host)
+ host, err := extractDockerHost(context.Background())
+ require.NoError(t, err)
+ require.Equal(t, "/path/to/docker.sock", host)
})
t.Run("Malformed Docker Host is passed in context", func(t *testing.T) {
@@ -84,9 +119,9 @@ func TestExtractDockerHost(t *testing.T) {
ctx := context.Background()
- host := extractDockerHost(context.WithValue(ctx, DockerHostContextKey, "path-to-docker-sock"))
-
- assert.Equal(t, DockerSocketPathWithSchema, host)
+ host, err := extractDockerHost(context.WithValue(ctx, DockerHostContextKey, "path-to-docker-sock"))
+ require.Error(t, err)
+ require.Equal(t, "", host)
})
t.Run("Malformed Schema Docker Host is passed in context", func(t *testing.T) {
@@ -94,17 +129,17 @@ func TestExtractDockerHost(t *testing.T) {
setupRootlessNotFound(t)
ctx := context.Background()
- host := extractDockerHost(context.WithValue(ctx, DockerHostContextKey, "http://path to docker sock"))
-
- assert.Equal(t, DockerSocketPathWithSchema, host)
+ host, err := extractDockerHost(context.WithValue(ctx, DockerHostContextKey, "http://path to docker sock"))
+ require.Error(t, err)
+ require.Equal(t, "", host)
})
t.Run("Unix Docker Host is passed in context", func(t *testing.T) {
ctx := context.Background()
- host := extractDockerHost(context.WithValue(ctx, DockerHostContextKey, DockerSocketSchema+"/this/is/a/sample.sock"))
-
- assert.Equal(t, "/this/is/a/sample.sock", host)
+ host, err := extractDockerHost(context.WithValue(ctx, DockerHostContextKey, DockerSocketSchema+"/this/is/a/sample.sock"))
+ require.NoError(t, err)
+ require.Equal(t, "/this/is/a/sample.sock", host)
})
t.Run("Unix Docker Host is passed as docker.host", func(t *testing.T) {
@@ -114,26 +149,26 @@ func TestExtractDockerHost(t *testing.T) {
setupTestcontainersProperties(t, content)
- host := extractDockerHost(context.Background())
-
- assert.Equal(t, DockerSocketSchema+"/this/is/a/sample.sock", host)
+ host, err := extractDockerHost(context.Background())
+ require.NoError(t, err)
+ require.Equal(t, DockerSocketSchema+"/this/is/a/sample.sock", host)
})
t.Run("Default Docker socket", func(t *testing.T) {
setupRootlessNotFound(t)
tmpSocket := setupDockerSocket(t)
- host := extractDockerHost(context.Background())
-
- assert.Equal(t, tmpSocket, host)
+ host, err := extractDockerHost(context.Background())
+ require.NoError(t, err)
+ require.Equal(t, tmpSocket, host)
})
- t.Run("Default Docker Host when empty", func(t *testing.T) {
+ t.Run("Error when empty", func(t *testing.T) {
setupDockerSocketNotFound(t)
setupRootlessNotFound(t)
- host := extractDockerHost(context.Background())
-
- assert.Equal(t, DockerSocketPathWithSchema, host)
+ host, err := extractDockerHost(context.Background())
+ require.Error(t, err)
+ require.Equal(t, "", host)
})
t.Run("Extract Docker socket", func(t *testing.T) {
@@ -147,7 +182,7 @@ func TestExtractDockerHost(t *testing.T) {
socket, err := testcontainersHostFromProperties(context.Background())
require.NoError(t, err)
- assert.Equal(t, testRemoteHost, socket)
+ require.Equal(t, testRemoteHost, socket)
})
t.Run("Testcontainers host is not defined in properties", func(t *testing.T) {
@@ -169,7 +204,7 @@ func TestExtractDockerHost(t *testing.T) {
socket, err := dockerHostFromEnv(context.Background())
require.NoError(t, err)
- assert.Equal(t, tmpSocket, socket)
+ require.Equal(t, tmpSocket, socket)
})
t.Run("DOCKER_HOST is not set", func(t *testing.T) {
@@ -189,9 +224,9 @@ func TestExtractDockerHost(t *testing.T) {
err := createTmpDockerSocket(tmpDir)
require.NoError(t, err)
- socket, err := dockerSocketOverridePath(context.Background())
+ socket, err := dockerSocketOverridePath()
require.NoError(t, err)
- assert.Equal(t, tmpSocket, socket)
+ require.Equal(t, tmpSocket, socket)
})
t.Run("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE is not set", func(t *testing.T) {
@@ -199,7 +234,7 @@ func TestExtractDockerHost(t *testing.T) {
os.Unsetenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE")
- socket, err := dockerSocketOverridePath(context.Background())
+ socket, err := dockerSocketOverridePath()
require.ErrorIs(t, err, ErrDockerSocketOverrideNotSet)
assert.Empty(t, socket)
})
@@ -209,7 +244,7 @@ func TestExtractDockerHost(t *testing.T) {
socket, err := dockerHostFromContext(context.WithValue(ctx, DockerHostContextKey, DockerSocketSchema+"/this/is/a/sample.sock"))
require.NoError(t, err)
- assert.Equal(t, "/this/is/a/sample.sock", socket)
+ require.Equal(t, "/this/is/a/sample.sock", socket)
})
t.Run("Context sets a malformed Docker socket", func(t *testing.T) {
@@ -233,7 +268,7 @@ func TestExtractDockerHost(t *testing.T) {
socket, err := dockerSocketPath(context.Background())
require.NoError(t, err)
- assert.Equal(t, tmpSocket, socket)
+ require.Equal(t, tmpSocket, socket)
})
t.Run("Docker host is defined in properties", func(t *testing.T) {
@@ -244,7 +279,7 @@ func TestExtractDockerHost(t *testing.T) {
socket, err := dockerHostFromProperties(context.Background())
require.NoError(t, err)
- assert.Equal(t, tmpSocket, socket)
+ require.Equal(t, tmpSocket, socket)
})
t.Run("Docker host is not defined in properties", func(t *testing.T) {
@@ -285,13 +320,15 @@ func (m mockCli) Info(ctx context.Context) (system.Info, error) {
func TestExtractDockerSocketFromClient(t *testing.T) {
setupDockerHostNotFound(t)
+ mockCallbackCheck(t, testCallbackCheckPassing)
+
t.Run("Docker socket from Testcontainers host defined in properties", func(t *testing.T) {
content := "tc.host=" + testRemoteHost
setupTestcontainersProperties(t, content)
socket := extractDockerSocketFromClient(context.Background(), mockCli{OS: "foo"})
- assert.Equal(t, DockerSocketPath, socket)
+ require.Equal(t, DockerSocketPath, socket)
})
t.Run("Docker socket from Testcontainers host takes precedence over TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", func(t *testing.T) {
@@ -303,7 +340,7 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
t.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", "/path/to/docker.sock")
socket := extractDockerSocketFromClient(context.Background(), mockCli{OS: "foo"})
- assert.Equal(t, DockerSocketPath, socket)
+ require.Equal(t, DockerSocketPath, socket)
})
t.Run("Docker Socket as Testcontainers environment variable", func(t *testing.T) {
@@ -314,7 +351,7 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
t.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", "/path/to/docker.sock")
host := extractDockerSocketFromClient(context.Background(), mockCli{OS: "foo"})
- assert.Equal(t, "/path/to/docker.sock", host)
+ require.Equal(t, "/path/to/docker.sock", host)
})
t.Run("Docker Socket as Testcontainers environment variable, removes prefixes", func(t *testing.T) {
@@ -324,11 +361,11 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
t.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", DockerSocketSchema+"/path/to/docker.sock")
host := extractDockerSocketFromClient(context.Background(), mockCli{OS: "foo"})
- assert.Equal(t, "/path/to/docker.sock", host)
+ require.Equal(t, "/path/to/docker.sock", host)
t.Setenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", testRemoteHost)
host = extractDockerSocketFromClient(context.Background(), mockCli{OS: "foo"})
- assert.Equal(t, DockerSocketPath, host)
+ require.Equal(t, DockerSocketPath, host)
})
t.Run("Unix Docker Socket is passed as DOCKER_HOST variable (Docker Desktop on non-Windows)", func(t *testing.T) {
@@ -347,7 +384,7 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
socket := extractDockerSocketFromClient(ctx, mockCli{OS: "Docker Desktop"})
- assert.Equal(t, DockerSocketPath, socket)
+ require.Equal(t, DockerSocketPath, socket)
})
t.Run("Unix Docker Socket is passed as DOCKER_HOST variable (Docker Desktop for Windows)", func(t *testing.T) {
@@ -362,7 +399,7 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
socket := extractDockerSocketFromClient(ctx, mockCli{OS: "Docker Desktop"})
- assert.Equal(t, WindowsDockerSocketPath, socket)
+ require.Equal(t, WindowsDockerSocketPath, socket)
})
t.Run("Unix Docker Socket is passed as DOCKER_HOST variable (Not Docker Desktop)", func(t *testing.T) {
@@ -376,7 +413,7 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
socket := extractDockerSocketFromClient(ctx, mockCli{OS: "Ubuntu"})
- assert.Equal(t, "/this/is/a/sample.sock", socket)
+ require.Equal(t, "/this/is/a/sample.sock", socket)
})
t.Run("Unix Docker Socket is passed as DOCKER_HOST variable (Not Docker Desktop), removes prefixes", func(t *testing.T) {
@@ -389,11 +426,11 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
t.Setenv("DOCKER_HOST", DockerSocketSchema+"/this/is/a/sample.sock")
socket := extractDockerSocketFromClient(ctx, mockCli{OS: "Ubuntu"})
- assert.Equal(t, "/this/is/a/sample.sock", socket)
+ require.Equal(t, "/this/is/a/sample.sock", socket)
t.Setenv("DOCKER_HOST", testRemoteHost)
socket = extractDockerSocketFromClient(ctx, mockCli{OS: "Ubuntu"})
- assert.Equal(t, DockerSocketPath, socket)
+ require.Equal(t, DockerSocketPath, socket)
})
t.Run("Unix Docker Socket is passed as docker.host property", func(t *testing.T) {
@@ -409,7 +446,25 @@ func TestExtractDockerSocketFromClient(t *testing.T) {
socket := extractDockerSocketFromClient(ctx, mockCli{OS: "Ubuntu"})
- assert.Equal(t, "/this/is/a/sample.sock", socket)
+ require.Equal(t, "/this/is/a/sample.sock", socket)
+ })
+
+ t.Run("Unix Docker Socket is passed as docker.host property but not reachable", func(t *testing.T) {
+ content := "docker.host=" + DockerSocketSchema + "/this/is/a/sample.sock"
+ setupTestcontainersProperties(t, content)
+
+ t.Cleanup(resetSocketOverrideFn)
+
+ ctx := context.Background()
+ os.Unsetenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE")
+ os.Unsetenv("DOCKER_HOST")
+
+ mockCallbackCheck(t, testCallbackCheckError)
+
+ require.Panics(t, func() {
+ // no need to check for the returned socket, as it must panic
+ _ = extractDockerSocketFromClient(ctx, mockCli{OS: "Ubuntu"})
+ })
})
}
diff --git a/internal/core/docker_rootless.go b/internal/core/docker_rootless.go
index 44782d31b38..b8e0f6e17e8 100644
--- a/internal/core/docker_rootless.go
+++ b/internal/core/docker_rootless.go
@@ -53,18 +53,24 @@ func rootlessDockerSocketPath(_ context.Context) (string, error) {
rootlessSocketPathFromRunDir,
}
- outerErr := ErrRootlessDockerNotFound
+ var errs []error
for _, socketPathFn := range socketPathFns {
s, err := socketPathFn()
if err != nil {
- outerErr = fmt.Errorf("%w: %w", outerErr, err)
+ if !isHostNotSet(err) {
+ errs = append(errs, err)
+ }
continue
}
return DockerSocketSchema + s, nil
}
- return "", outerErr
+ if len(errs) > 0 {
+ return "", errors.Join(errs...)
+ }
+
+ return "", ErrRootlessDockerNotFound
}
func fileExists(f string) bool {
diff --git a/internal/core/docker_rootless_test.go b/internal/core/docker_rootless_test.go
index ef018eda53b..7897f35783d 100644
--- a/internal/core/docker_rootless_test.go
+++ b/internal/core/docker_rootless_test.go
@@ -178,14 +178,8 @@ func TestRootlessDockerSocketPath(t *testing.T) {
setupRootlessNotFound(t)
socketPath, err := rootlessDockerSocketPath(context.Background())
- require.ErrorIs(t, err, ErrRootlessDockerNotFound)
+ require.ErrorIs(t, err, ErrRootlessDockerNotFoundXDGRuntimeDir)
assert.Empty(t, socketPath)
-
- // the wrapped error includes all the locations that were checked
- require.ErrorContains(t, err, ErrRootlessDockerNotFoundXDGRuntimeDir.Error())
- require.ErrorContains(t, err, ErrRootlessDockerNotFoundHomeRunDir.Error())
- require.ErrorContains(t, err, ErrRootlessDockerNotFoundHomeDesktopDir.Error())
- require.ErrorContains(t, err, ErrRootlessDockerNotFoundRunDir.Error())
})
}
diff --git a/internal/core/images.go b/internal/core/images.go
index 6c7213f12c3..2892267e9cf 100644
--- a/internal/core/images.go
+++ b/internal/core/images.go
@@ -2,6 +2,7 @@ package core
import (
"bufio"
+ "io"
"net/url"
"os"
"regexp"
@@ -25,17 +26,22 @@ const (
var rxURL = regexp.MustCompile(URL)
+// ExtractImagesFromDockerfile extracts images from the Dockerfile sourced from dockerfile.
func ExtractImagesFromDockerfile(dockerfile string, buildArgs map[string]*string) ([]string, error) {
- var images []string
-
file, err := os.Open(dockerfile)
if err != nil {
return nil, err
}
defer file.Close()
+ return ExtractImagesFromReader(file, buildArgs)
+}
+
+// ExtractImagesFromReader extracts images from the Dockerfile sourced from r.
+func ExtractImagesFromReader(r io.Reader, buildArgs map[string]*string) ([]string, error) {
+ var images []string
var lines []string
- scanner := bufio.NewScanner(file)
+ scanner := bufio.NewScanner(r)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
diff --git a/internal/version.go b/internal/version.go
index 3dc92975535..0c688d5e3db 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -1,4 +1,4 @@
package internal
// Version is the next development version of the application
-const Version = "0.33.0"
+const Version = "0.34.0"
diff --git a/lifecycle.go b/lifecycle.go
index 2a8ae949aba..40360a4c0b9 100644
--- a/lifecycle.go
+++ b/lifecycle.go
@@ -165,26 +165,25 @@ var defaultCopyFileToContainerHook = func(files []ContainerFile) ContainerLifecy
var defaultLogConsumersHook = func(cfg *LogConsumerConfig) ContainerLifecycleHooks {
return ContainerLifecycleHooks{
PostStarts: []ContainerHook{
- // first post-start hook is to produce logs and start log consumers
+ // Produce logs sending details to the log consumers.
+ // See combineContainerHooks for the order of execution.
func(ctx context.Context, c Container) error {
- dockerContainer := c.(*DockerContainer)
-
- if cfg == nil {
+ if cfg == nil || len(cfg.Consumers) == 0 {
return nil
}
+ dockerContainer := c.(*DockerContainer)
+ dockerContainer.consumers = dockerContainer.consumers[:0]
for _, consumer := range cfg.Consumers {
dockerContainer.followOutput(consumer)
}
- if len(cfg.Consumers) > 0 {
- return dockerContainer.startLogProduction(ctx, cfg.Opts...)
- }
- return nil
+ return dockerContainer.startLogProduction(ctx, cfg.Opts...)
},
},
- PreTerminates: []ContainerHook{
- // first pre-terminate hook is to stop the log production
+ PostStops: []ContainerHook{
+ // Stop the log production.
+ // See combineContainerHooks for the order of execution.
func(ctx context.Context, c Container) error {
if cfg == nil || len(cfg.Consumers) == 0 {
return nil
diff --git a/logconsumer_test.go b/logconsumer_test.go
index dec0d5aeb76..6265f0a578b 100644
--- a/logconsumer_test.go
+++ b/logconsumer_test.go
@@ -276,7 +276,7 @@ func TestContainerLogWithErrClosed(t *testing.T) {
if errors.Is(err, context.DeadlineExceeded) {
break
}
- time.Sleep(100 * time.Microsecond)
+ time.Sleep(10 * time.Millisecond)
t.Log("retrying get endpoint")
}
if err != nil {
@@ -639,23 +639,54 @@ func Test_MultiContainerLogConsumer_CancelledContext(t *testing.T) {
assert.False(t, strings.Contains(actual, logStoppedForOutOfSyncMessage))
}
+// FooLogConsumer is a test log consumer that accepts logs from the
+// "hello-world" Docker image, which prints out the "Hello from Docker!"
+// log message.
type FooLogConsumer struct {
LogChannel chan string
+ t *testing.T
}
+// Accept receives a log message and sends it to the log channel if it
+// contains the "Hello from Docker!" message.
func (c FooLogConsumer) Accept(rawLog Log) {
log := string(rawLog.Content)
- c.LogChannel <- log
+ if strings.Contains(log, "Hello from Docker!") {
+ select {
+ case c.LogChannel <- log:
+ default:
+ }
+ }
+}
+
+// AssertRead waits for a log message to be received.
+func (c FooLogConsumer) AssertRead() {
+ select {
+ case <-c.LogChannel:
+ case <-time.After(5 * time.Second):
+ c.t.Fatal("receive timeout")
+ }
+}
+
+// SlurpOne reads a value from the channel if it is available.
+func (c FooLogConsumer) SlurpOne() {
+ select {
+ case <-c.LogChannel:
+ default:
+ }
}
-func NewFooLogConsumer() *FooLogConsumer {
+func NewFooLogConsumer(t *testing.T) *FooLogConsumer {
+ t.Helper()
+
return &FooLogConsumer{
- LogChannel: make(chan string),
+ t: t,
+ LogChannel: make(chan string, 2),
}
}
func TestRestartContainerWithLogConsumer(t *testing.T) {
- logConsumer := NewFooLogConsumer()
+ logConsumer := NewFooLogConsumer(t)
ctx := context.Background()
container, err := GenericContainer(ctx, GenericContainerRequest{
@@ -668,28 +699,27 @@ func TestRestartContainerWithLogConsumer(t *testing.T) {
},
Started: false,
})
- if err != nil {
- t.Fatalf("Cant create container: %s", err.Error())
- }
+ terminateContainerOnEnd(t, ctx, container)
+ require.NoError(t, err)
+ // Start and confirm that the log consumer receives the log message.
err = container.Start(ctx)
- if err != nil {
- t.Fatalf("Cant start container: %s", err.Error())
- }
+ require.NoError(t, err)
+
+ logConsumer.AssertRead()
- d := 30 * time.Second
+ // Stop the container and clear any pending message.
+ d := 5 * time.Second
err = container.Stop(ctx, &d)
- if err != nil {
- t.Fatalf("Cant stop container: %s", err.Error())
- }
+ require.NoError(t, err)
+
+ logConsumer.SlurpOne()
+
+ // Restart the container and confirm that the log consumer receives new log messages.
err = container.Start(ctx)
- if err != nil {
- t.Fatalf("Cant start container: %s", err.Error())
- }
+ require.NoError(t, err)
- for s := range logConsumer.LogChannel {
- if strings.Contains(s, "Hello from Docker!") {
- break
- }
- }
+ // First message is from the first start.
+ logConsumer.AssertRead()
+ logConsumer.AssertRead()
}
diff --git a/logger.go b/logger.go
index cfc851ad7ed..fca5da5398f 100644
--- a/logger.go
+++ b/logger.go
@@ -8,34 +8,20 @@ import (
"testing"
"github.com/docker/docker/client"
-
- "github.com/testcontainers/testcontainers-go/internal/config"
)
// Logger is the default log instance
var Logger Logging = log.New(os.Stderr, "", log.LstdFlags)
func init() {
- verbose := false
for _, arg := range os.Args {
if strings.EqualFold(arg, "-test.v=true") || strings.EqualFold(arg, "-v") {
- verbose = true
- break
+ return
}
}
- if !verbose {
- Logger = &noopLogger{}
- }
-
- if config.Read().RyukDisabled {
- ryukDisabledMessage := `
-**********************************************************************************************
-Ryuk has been disabled for the current execution. This can cause unexpected behavior in your environment.
-More on this: https://golang.testcontainers.org/features/garbage_collector/
-**********************************************************************************************`
- Logger.Printf(ryukDisabledMessage)
- }
+ // If we are not running in verbose mode, we configure a noop logger by default.
+ Logger = &noopLogger{}
}
// Validate our types implement the required interfaces.
diff --git a/mkdocs.yml b/mkdocs.yml
index 1494555b0cb..d48a9dff17b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -57,6 +57,7 @@ nav:
- Introduction: features/wait/introduction.md
- Exec: features/wait/exec.md
- Exit: features/wait/exit.md
+ - File: features/wait/file.md
- Health: features/wait/health.md
- HostPort: features/wait/host_port.md
- HTTP: features/wait/http.md
@@ -134,4 +135,4 @@ nav:
- Getting help: getting_help.md
edit_uri: edit/main/docs/
extra:
- latest_version: v0.32.0
+ latest_version: v0.33.0
diff --git a/modulegen/_template/ci.yml.tmpl b/modulegen/_template/ci.yml.tmpl
index 616dd2bee67..e4fd047b245 100644
--- a/modulegen/_template/ci.yml.tmpl
+++ b/modulegen/_template/ci.yml.tmpl
@@ -26,7 +26,7 @@ jobs:
test:
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest, macos-latest]
uses: ./.github/workflows/ci-test-go.yml
with:
@@ -44,7 +44,7 @@ jobs:
name: "Test with reaper off"
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
uses: ./.github/workflows/ci-test-go.yml
with:
go-version: {{ "${{ matrix.go-version }}" }}
@@ -61,7 +61,7 @@ jobs:
name: "Test with Rootless Docker"
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest]
uses: ./.github/workflows/ci-test-go.yml
with:
@@ -76,7 +76,7 @@ jobs:
test-module-generator:
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
uses: ./.github/workflows/ci-test-go.yml
with:
@@ -92,7 +92,7 @@ jobs:
needs: test
strategy:
matrix:
- go-version: [1.21.x, 1.x]
+ go-version: [1.22.x, 1.x]
platform: [ubuntu-latest]
module: [{{ .Modules }}]
uses: ./.github/workflows/ci-test-go.yml
@@ -112,7 +112,7 @@ jobs:
module: [{{ .Examples }}]
uses: ./.github/workflows/ci-test-go.yml
with:
- go-version: "1.21.x"
+ go-version: "1.22.x"
fail-fast: true
platform: 'ubuntu-latest'
project-directory: {{ "examples/${{ matrix.module }}" }}
@@ -129,7 +129,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.1
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0
diff --git a/modulegen/go.mod b/modulegen/go.mod
index 613a7c27911..0e66fb16d70 100644
--- a/modulegen/go.mod
+++ b/modulegen/go.mod
@@ -1,6 +1,6 @@
module github.com/testcontainers/testcontainers-go/modulegen
-go 1.21
+go 1.22
require (
github.com/spf13/cobra v1.8.0
diff --git a/modules/artemis/go.mod b/modules/artemis/go.mod
index e1fcc9958e2..adb9dddf08b 100644
--- a/modules/artemis/go.mod
+++ b/modules/artemis/go.mod
@@ -1,12 +1,12 @@
module github.com/testcontainers/testcontainers-go/modules/artemis
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
github.com/go-stomp/stomp/v3 v3.0.5
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -20,7 +20,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/artemis/go.sum b/modules/artemis/go.sum
index 1db6255fae4..69f6d529977 100644
--- a/modules/artemis/go.sum
+++ b/modules/artemis/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/azurite/go.mod b/modules/azurite/go.mod
index e18fad70505..826a457a9ac 100644
--- a/modules/azurite/go.mod
+++ b/modules/azurite/go.mod
@@ -1,6 +1,6 @@
module github.com/testcontainers/testcontainers-go/modules/azurite
-go 1.21
+go 1.22
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
@@ -8,7 +8,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue v1.0.0
github.com/docker/go-connections v0.5.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -22,7 +22,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/azurite/go.sum b/modules/azurite/go.sum
index 64bfb078096..90f85680886 100644
--- a/modules/azurite/go.sum
+++ b/modules/azurite/go.sum
@@ -41,8 +41,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/cassandra/go.mod b/modules/cassandra/go.mod
index 28207451e7d..fce3c2d9196 100644
--- a/modules/cassandra/go.mod
+++ b/modules/cassandra/go.mod
@@ -1,12 +1,12 @@
module github.com/testcontainers/testcontainers-go/modules/cassandra
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
github.com/gocql/gocql v1.6.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -20,7 +20,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/cassandra/go.sum b/modules/cassandra/go.sum
index f939709a994..775e48ecf59 100644
--- a/modules/cassandra/go.sum
+++ b/modules/cassandra/go.sum
@@ -27,8 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/chroma/go.mod b/modules/chroma/go.mod
index 4908b5b85bd..2c03baa164c 100644
--- a/modules/chroma/go.mod
+++ b/modules/chroma/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/chroma
-go 1.21
+go 1.22
require (
github.com/amikos-tech/chroma-go v0.1.2
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -20,7 +20,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/chroma/go.sum b/modules/chroma/go.sum
index 916290f5929..e7d70f0539f 100644
--- a/modules/chroma/go.sum
+++ b/modules/chroma/go.sum
@@ -28,8 +28,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/clickhouse/go.mod b/modules/clickhouse/go.mod
index ea35c63b57d..5542929f727 100644
--- a/modules/clickhouse/go.mod
+++ b/modules/clickhouse/go.mod
@@ -1,13 +1,13 @@
module github.com/testcontainers/testcontainers-go/modules/clickhouse
-go 1.21
+go 1.22
require (
github.com/ClickHouse/clickhouse-go/v2 v2.20.0
github.com/cenkalti/backoff/v4 v4.2.1
github.com/docker/go-connections v0.5.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -22,7 +22,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-faster/city v1.0.1 // indirect
diff --git a/modules/clickhouse/go.sum b/modules/clickhouse/go.sum
index 8923b019654..2f09c4daae3 100644
--- a/modules/clickhouse/go.sum
+++ b/modules/clickhouse/go.sum
@@ -29,8 +29,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/cockroachdb/go.mod b/modules/cockroachdb/go.mod
index 561f247d295..d31c6f54ec1 100644
--- a/modules/cockroachdb/go.mod
+++ b/modules/cockroachdb/go.mod
@@ -1,12 +1,12 @@
module github.com/testcontainers/testcontainers-go/modules/cockroachdb
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
github.com/jackc/pgx/v5 v5.5.4
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -26,7 +26,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/cockroachdb/go.sum b/modules/cockroachdb/go.sum
index 7f47c1bc1c1..c051f94433c 100644
--- a/modules/cockroachdb/go.sum
+++ b/modules/cockroachdb/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/compose/compose.go b/modules/compose/compose.go
index 44da068e9b3..4a5d4efbecd 100644
--- a/modules/compose/compose.go
+++ b/modules/compose/compose.go
@@ -31,6 +31,7 @@ type composeStackOptions struct {
Paths []string
temporaryPaths map[string]bool
Logger testcontainers.Logging
+ Profiles []string
}
type ComposeStackOption interface {
@@ -115,6 +116,11 @@ func WithStackReaders(readers ...io.Reader) ComposeStackOption {
return ComposeStackReaders(readers)
}
+// WithProfiles allows to enable/disable services based on the profiles defined in the compose file.
+func WithProfiles(profiles ...string) ComposeStackOption {
+ return ComposeProfiles(profiles)
+}
+
func NewDockerCompose(filePaths ...string) (*dockerCompose, error) {
return NewDockerComposeWith(WithStackFiles(filePaths...))
}
@@ -124,11 +130,12 @@ func NewDockerComposeWith(opts ...ComposeStackOption) (*dockerCompose, error) {
Identifier: uuid.New().String(),
temporaryPaths: make(map[string]bool),
Logger: testcontainers.Logger,
+ Profiles: nil,
}
for i := range opts {
if err := opts[i].applyToComposeStack(&composeOptions); err != nil {
- return nil, err
+ return nil, fmt.Errorf("apply compose stack option: %w", err)
}
}
@@ -138,11 +145,11 @@ func NewDockerComposeWith(opts ...ComposeStackOption) (*dockerCompose, error) {
dockerCli, err := command.NewDockerCli()
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("new docker client: %w", err)
}
if err = dockerCli.Initialize(flags.NewClientOptions(), command.WithInitializeClient(makeClient)); err != nil {
- return nil, err
+ return nil, fmt.Errorf("initialize docker client: %w", err)
}
composeAPI := &dockerCompose{
@@ -150,6 +157,7 @@ func NewDockerComposeWith(opts ...ComposeStackOption) (*dockerCompose, error) {
configs: composeOptions.Paths,
temporaryConfigs: composeOptions.temporaryPaths,
logger: composeOptions.Logger,
+ projectProfiles: composeOptions.Profiles,
composeService: compose.NewComposeService(dockerCli),
dockerClient: dockerCli.Client(),
waitStrategies: make(map[string]wait.Strategy),
diff --git a/modules/compose/compose_api.go b/modules/compose/compose_api.go
index de1cd2eb866..88f67d8d3ef 100644
--- a/modules/compose/compose_api.go
+++ b/modules/compose/compose_api.go
@@ -121,19 +121,19 @@ func (r ComposeStackReaders) applyToComposeStack(o *composeStackOptions) error {
tmp = filepath.Join(tmp, strconv.FormatInt(time.Now().UnixNano(), 10))
err := os.MkdirAll(tmp, 0o755)
if err != nil {
- return fmt.Errorf("failed to create temporary directory: %w", err)
+ return fmt.Errorf("create temporary directory: %w", err)
}
name := fmt.Sprintf(baseName, i)
bs, err := io.ReadAll(reader)
if err != nil {
- return fmt.Errorf("failed to read from reader: %w", err)
+ return fmt.Errorf("read from reader: %w", err)
}
err = os.WriteFile(filepath.Join(tmp, name), bs, 0o644)
if err != nil {
- return fmt.Errorf("failed to write to temporary file: %w", err)
+ return fmt.Errorf("write to temporary file: %w", err)
}
f[i] = filepath.Join(tmp, name)
@@ -154,6 +154,13 @@ func (f ComposeStackFiles) applyToComposeStack(o *composeStackOptions) error {
return nil
}
+type ComposeProfiles []string
+
+func (p ComposeProfiles) applyToComposeStack(o *composeStackOptions) error {
+ o.Profiles = append(o.Profiles, p...)
+ return nil
+}
+
type StackIdentifier string
func (f StackIdentifier) applyToComposeStack(o *composeStackOptions) error {
@@ -193,8 +200,8 @@ type dockerCompose struct {
// only one strategy can be added to a service, to use multiple use wait.ForAll(...)
waitStrategies map[string]wait.Strategy
- // used to synchronise writes to the containers map
- containersLock sync.RWMutex
+ // Used to synchronise writes to the containers.
+ containersLock sync.Mutex
// cache for containers that are part of the stack
// used in ServiceContainer(...) function to avoid calls to the Docker API
@@ -213,6 +220,9 @@ type dockerCompose struct {
// e.g. environment settings, ...
projectOptions []cli.ProjectOptionsFn
+ // profiles applied to the compose project after compilation.
+ projectProfiles []string
+
// compiled compose project
// can be nil if the stack wasn't started yet
project *types.Project
@@ -307,7 +317,7 @@ func (d *dockerCompose) Up(ctx context.Context, opts ...StackUpOption) (err erro
},
})
if err != nil {
- return err
+ return fmt.Errorf("compose up: %w", err)
}
err = d.lookupNetworks(ctx)
@@ -387,10 +397,6 @@ func (d *dockerCompose) Up(ctx context.Context, opts ...StackUpOption) (err erro
termSignals = append(termSignals, termSignal)
}
- d.containersLock.Lock()
- defer d.containersLock.Unlock()
- d.containers[srv.Name] = dc
-
return nil
})
}
@@ -416,11 +422,6 @@ func (d *dockerCompose) Up(ctx context.Context, opts ...StackUpOption) (err erro
return err
}
- // cache all the containers on compose.up
- d.containersLock.Lock()
- defer d.containersLock.Unlock()
- d.containers[svc] = target
-
return strategy.WaitUntilReady(errGrpCtx, target)
})
}
@@ -480,7 +481,7 @@ func (d *dockerCompose) lookupContainer(ctx context.Context, svcName string) (*t
),
})
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("container list: %w", err)
}
if len(containers) == 0 {
@@ -496,13 +497,15 @@ func (d *dockerCompose) lookupContainer(ctx context.Context, svcName string) (*t
dockerProvider, err := testcontainers.NewDockerProvider(testcontainers.WithLogger(d.logger))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("new docker provider: %w", err)
}
dockerProvider.SetClient(d.dockerClient)
ctr.SetProvider(dockerProvider)
+ d.containersLock.Lock()
+ defer d.containersLock.Unlock()
d.containers[svcName] = ctr
return ctr, nil
@@ -512,16 +515,13 @@ func (d *dockerCompose) lookupContainer(ctx context.Context, svcName string) (*t
//
// Safe for concurrent calls.
func (d *dockerCompose) lookupNetworks(ctx context.Context) error {
- d.containersLock.Lock()
- defer d.containersLock.Unlock()
-
networks, err := d.dockerClient.NetworkList(ctx, dockernetwork.ListOptions{
Filters: filters.NewArgs(
filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, d.name)),
),
})
if err != nil {
- return err
+ return fmt.Errorf("network list: %w", err)
}
for _, n := range networks {
@@ -546,12 +546,19 @@ func (d *dockerCompose) compileProject(ctx context.Context) (*types.Project, err
compiledOptions, err := cli.NewProjectOptions(d.configs, projectOptions...)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("new project options: %w", err)
}
proj, err := compiledOptions.LoadProject(ctx)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("load project: %w", err)
+ }
+
+ if len(d.projectProfiles) > 0 {
+ proj, err = proj.WithProfiles(d.projectProfiles)
+ if err != nil {
+ return nil, fmt.Errorf("with profiles: %w", err)
+ }
}
for i, s := range proj.Services {
@@ -606,7 +613,7 @@ func withEnv(env map[string]string) func(*cli.ProjectOptions) error {
func makeClient(*command.DockerCli) (client.APIClient, error) {
dockerClient, err := testcontainers.NewDockerClientWithOpts(context.Background())
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("new docker client: %w", err)
}
return dockerClient, nil
}
diff --git a/modules/compose/compose_api_test.go b/modules/compose/compose_api_test.go
index 67714543475..7879dabfa96 100644
--- a/modules/compose/compose_api_test.go
+++ b/modules/compose/compose_api_test.go
@@ -101,6 +101,59 @@ func TestDockerComposeAPIWithRunServices(t *testing.T) {
assert.Contains(t, serviceNames, "api-nginx")
}
+func TestDockerComposeAPIWithProfiles(t *testing.T) {
+ path := RenderComposeProfiles(t)
+
+ testcases := map[string]struct {
+ withProfiles []string
+ wantServices []string
+ }{
+ "nil profile": {
+ withProfiles: nil,
+ wantServices: []string{"starts-always"},
+ },
+ "no profiles": {
+ withProfiles: []string{},
+ wantServices: []string{"starts-always"},
+ },
+ "dev profile": {
+ withProfiles: []string{"dev"},
+ wantServices: []string{"starts-always", "only-dev", "dev-or-test"},
+ },
+ "test profile": {
+ withProfiles: []string{"test"},
+ wantServices: []string{"starts-always", "dev-or-test"},
+ },
+ "wildcard profile": {
+ withProfiles: []string{"*"},
+ wantServices: []string{"starts-always", "only-dev", "dev-or-test", "only-prod"},
+ },
+ "undefined profile": {
+ withProfiles: []string{"undefined-profile"},
+ wantServices: []string{"starts-always"},
+ },
+ }
+
+ for name, test := range testcases {
+ t.Run(name, func(t *testing.T) {
+ compose, err := NewDockerComposeWith(WithStackFiles(path), WithProfiles(test.withProfiles...))
+ require.NoError(t, err, "NewDockerCompose()")
+
+ ctx, cancel := context.WithCancel(context.Background())
+ t.Cleanup(cancel)
+
+ for _, service := range test.wantServices {
+ compose = compose.WaitForService(service, wait.NewHTTPStrategy("/").WithPort("80/tcp").WithStartupTimeout(10*time.Second)).(*dockerCompose)
+ }
+ err = compose.Up(ctx, Wait(true))
+ cleanup(t, compose)
+ require.NoError(t, err, "compose.Up()")
+
+ assert.ElementsMatch(t, test.wantServices, compose.Services())
+ })
+ }
+}
+
func TestDockerComposeAPI_TestcontainersLabelsArePresent(t *testing.T) {
path, _ := RenderComposeComplex(t)
compose, err := NewDockerCompose(path)
diff --git a/modules/compose/compose_builder_test.go b/modules/compose/compose_builder_test.go
index fbfe37baa39..4624c1d3c2c 100644
--- a/modules/compose/compose_builder_test.go
+++ b/modules/compose/compose_builder_test.go
@@ -14,6 +14,12 @@ const (
testdataPackage = "testdata"
)
+func RenderComposeProfiles(t *testing.T) string {
+ t.Helper()
+
+ return writeTemplate(t, "docker-compose-profiles.yml")
+}
+
func RenderComposeComplex(t *testing.T) (string, []int) {
t.Helper()
diff --git a/modules/compose/compose_local.go b/modules/compose/compose_local.go
index cb818dbc94f..964547bae7b 100644
--- a/modules/compose/compose_local.go
+++ b/modules/compose/compose_local.go
@@ -136,7 +136,7 @@ func (dc *LocalDockerCompose) containerNameFromServiceName(service, separator st
func (dc *LocalDockerCompose) applyStrategyToRunningContainer() error {
cli, err := testcontainers.NewDockerClientWithOpts(context.Background())
if err != nil {
- return err
+ return fmt.Errorf("new docker client: %w", err)
}
defer cli.Close()
@@ -150,22 +150,22 @@ func (dc *LocalDockerCompose) applyStrategyToRunningContainer() error {
containerListOptions := container.ListOptions{Filters: f, All: true}
containers, err := cli.ContainerList(context.Background(), containerListOptions)
if err != nil {
- return fmt.Errorf("error %w occurred while filtering the service %s: %d by name and published port", err, k.service, k.publishedPort)
+ return fmt.Errorf("container list service %q: %w", k.service, err)
}
if len(containers) == 0 {
- return fmt.Errorf("service with name %s not found in list of running containers", k.service)
+ return fmt.Errorf("service with name %q not found in list of running containers", k.service)
}
// The length should always be a list of 1, since we are matching one service name at a time
if l := len(containers); l > 1 {
- return fmt.Errorf("expecting only one running container for %s but got %d", k.service, l)
+ return fmt.Errorf("expecting only one running container for %q but got %d", k.service, l)
}
container := containers[0]
strategy := dc.WaitStrategyMap[k]
dockerProvider, err := testcontainers.NewDockerProvider(testcontainers.WithLogger(dc.Logger))
if err != nil {
- return fmt.Errorf("unable to create new Docker Provider: %w", err)
+ return fmt.Errorf("new docker provider: %w", err)
}
defer dockerProvider.Close()
@@ -175,7 +175,7 @@ func (dc *LocalDockerCompose) applyStrategyToRunningContainer() error {
err = strategy.WaitUntilReady(context.Background(), dockercontainer)
if err != nil {
- return fmt.Errorf("unable to apply wait strategy %v to service %s due to %w", strategy, k.service, err)
+ return fmt.Errorf("wait until ready %v to service %q due: %w", strategy, k.service, err)
}
}
return nil
@@ -223,7 +223,6 @@ func (dc *LocalDockerCompose) WithExposedService(service string, port int, strat
// depending on the version services names are composed in a different way
func (dc *LocalDockerCompose) determineVersion() error {
execErr := executeCompose(dc, []string{"version", "--short"})
-
if err := execErr.Error; err != nil {
return err
}
@@ -235,7 +234,7 @@ func (dc *LocalDockerCompose) determineVersion() error {
majorVersion, err := strconv.ParseInt(string(components[0]), 10, 8)
if err != nil {
- return err
+ return fmt.Errorf("parsing major version: %w", err)
}
switch majorVersion {
@@ -263,11 +262,11 @@ func (dc *LocalDockerCompose) validate() error {
yamlFile, err := os.ReadFile(abs)
if err != nil {
- return err
+ return fmt.Errorf("read compose file %q: %w", abs, err)
}
err = yaml.Unmarshal(yamlFile, &c)
if err != nil {
- return err
+ return fmt.Errorf("unmarshalling file %q: %w", abs, err)
}
if dc.Services == nil {
@@ -448,7 +447,9 @@ func (w *capturingPassThroughWriter) Bytes() []byte {
// Which checks if a binary is present in PATH
func which(binary string) error {
- _, err := exec.LookPath(binary)
+ if _, err := exec.LookPath(binary); err != nil {
+ return fmt.Errorf("lookup: %w", err)
+ }
- return err
+ return nil
}
diff --git a/modules/compose/go.mod b/modules/compose/go.mod
index 2e076ed60e2..487bdcb7d0e 100644
--- a/modules/compose/go.mod
+++ b/modules/compose/go.mod
@@ -1,6 +1,6 @@
module github.com/testcontainers/testcontainers-go/modules/compose
-go 1.21
+go 1.22
replace github.com/testcontainers/testcontainers-go => ../..
@@ -8,10 +8,10 @@ require (
github.com/compose-spec/compose-go/v2 v2.1.3
github.com/docker/cli v27.0.3+incompatible
github.com/docker/compose/v2 v2.28.1
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
golang.org/x/sync v0.7.0
gopkg.in/yaml.v3 v3.0.1
)
diff --git a/modules/compose/go.sum b/modules/compose/go.sum
index 8eec100162e..c05580192ee 100644
--- a/modules/compose/go.sum
+++ b/modules/compose/go.sum
@@ -135,8 +135,8 @@ github.com/docker/compose/v2 v2.28.1/go.mod h1:wDtGQFHe99sPLCHXeVbCkc+Wsl4Y/2Zxi
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
diff --git a/modules/compose/testdata/docker-compose-profiles.yml b/modules/compose/testdata/docker-compose-profiles.yml
new file mode 100644
index 00000000000..fdb92853e15
--- /dev/null
+++ b/modules/compose/testdata/docker-compose-profiles.yml
@@ -0,0 +1,25 @@
+services:
+ starts-always:
+ image: docker.io/nginx:stable-alpine
+ ports:
+ - ":80"
+ # profiles: none defined, therefore always starts.
+ only-dev:
+ image: docker.io/nginx:stable-alpine
+ ports:
+ - ":80"
+ profiles:
+ - dev
+ dev-or-test:
+ image: docker.io/nginx:stable-alpine
+ ports:
+ - ":80"
+ profiles:
+ - dev
+ - test
+ only-prod:
+ image: docker.io/nginx:stable-alpine
+ ports:
+ - ":80"
+ profiles:
+ - prod
diff --git a/modules/consul/go.mod b/modules/consul/go.mod
index abff142961b..079447aa276 100644
--- a/modules/consul/go.mod
+++ b/modules/consul/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/consul
-go 1.21
+go 1.22
require (
github.com/hashicorp/consul/api v1.27.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -20,7 +20,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/fatih/color v1.14.1 // indirect
diff --git a/modules/consul/go.sum b/modules/consul/go.sum
index e497d639f39..d82d1cb4350 100644
--- a/modules/consul/go.sum
+++ b/modules/consul/go.sum
@@ -42,8 +42,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/couchbase/go.mod b/modules/couchbase/go.mod
index 03a0563970b..96365d44e7f 100644
--- a/modules/couchbase/go.mod
+++ b/modules/couchbase/go.mod
@@ -1,6 +1,6 @@
module github.com/testcontainers/testcontainers-go/modules/couchbase
-go 1.21
+go 1.22
toolchain go1.21.7
@@ -8,7 +8,7 @@ require (
github.com/cenkalti/backoff/v4 v4.2.1
github.com/couchbase/gocb/v2 v2.7.2
github.com/docker/go-connections v0.5.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
github.com/tidwall/gjson v1.17.1
)
@@ -25,7 +25,7 @@ require (
github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20230515165046-68b522a21131 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/couchbase/go.sum b/modules/couchbase/go.sum
index e1ce1bbbce1..737e1e52369 100644
--- a/modules/couchbase/go.sum
+++ b/modules/couchbase/go.sum
@@ -42,8 +42,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/dolt/go.mod b/modules/dolt/go.mod
index d259079a244..804efb84834 100644
--- a/modules/dolt/go.mod
+++ b/modules/dolt/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/dolt
-go 1.21
+go 1.22
require (
github.com/go-sql-driver/mysql v1.7.1
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/dolt/go.sum b/modules/dolt/go.sum
index 5edb490b56c..7784d0b8332 100644
--- a/modules/dolt/go.sum
+++ b/modules/dolt/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/elasticsearch/go.mod b/modules/elasticsearch/go.mod
index 35f0f4af03f..a766e55dd0d 100644
--- a/modules/elasticsearch/go.mod
+++ b/modules/elasticsearch/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/elasticsearch
-go 1.21
+go 1.22
require (
github.com/elastic/go-elasticsearch/v8 v8.12.1
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
golang.org/x/mod v0.16.0
)
@@ -20,7 +20,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/elastic/elastic-transport-go/v8 v8.4.0 // indirect
diff --git a/modules/elasticsearch/go.sum b/modules/elasticsearch/go.sum
index 5c238b73f5b..59647a3793d 100644
--- a/modules/elasticsearch/go.sum
+++ b/modules/elasticsearch/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/gcloud/go.mod b/modules/gcloud/go.mod
index 825dcfeed01..00a723f58c2 100644
--- a/modules/gcloud/go.mod
+++ b/modules/gcloud/go.mod
@@ -1,6 +1,6 @@
module github.com/testcontainers/testcontainers-go/modules/gcloud
-go 1.21
+go 1.22
require (
cloud.google.com/go/bigquery v1.59.1
@@ -10,7 +10,7 @@ require (
cloud.google.com/go/pubsub v1.36.2
cloud.google.com/go/spanner v1.57.0
github.com/docker/go-connections v0.5.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
google.golang.org/api v0.169.0
google.golang.org/grpc v1.64.1
)
@@ -34,7 +34,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/envoyproxy/go-control-plane v0.12.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
diff --git a/modules/gcloud/go.sum b/modules/gcloud/go.sum
index f1d231f03a3..31d286e7d04 100644
--- a/modules/gcloud/go.sum
+++ b/modules/gcloud/go.sum
@@ -64,8 +64,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/grafana-lgtm/go.mod b/modules/grafana-lgtm/go.mod
index 30f96bce2ad..73a94c714b1 100644
--- a/modules/grafana-lgtm/go.mod
+++ b/modules/grafana-lgtm/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/grafanalgtm
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
go.opentelemetry.io/contrib/bridges/otelslog v0.3.0
go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0
go.opentelemetry.io/otel v1.28.0
@@ -32,7 +32,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
diff --git a/modules/grafana-lgtm/go.sum b/modules/grafana-lgtm/go.sum
index 8a6e413e918..9eb14f31cb0 100644
--- a/modules/grafana-lgtm/go.sum
+++ b/modules/grafana-lgtm/go.sum
@@ -27,8 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/inbucket/go.mod b/modules/inbucket/go.mod
index 89851c357e3..18939bc4452 100644
--- a/modules/inbucket/go.mod
+++ b/modules/inbucket/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/inbucket
-go 1.21
+go 1.22
require (
github.com/inbucket/inbucket v2.0.0+incompatible
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -19,7 +19,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/inbucket/go.sum b/modules/inbucket/go.sum
index 8236abef2fe..2e403c06ad6 100644
--- a/modules/inbucket/go.sum
+++ b/modules/inbucket/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/influxdb/go.mod b/modules/influxdb/go.mod
index a8c0be530ac..f11c1de4495 100644
--- a/modules/influxdb/go.mod
+++ b/modules/influxdb/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/influxdb
-go 1.21
+go 1.22
require (
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -19,7 +19,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/influxdb/go.sum b/modules/influxdb/go.sum
index 52e9ccd448f..ad81df94f6d 100644
--- a/modules/influxdb/go.sum
+++ b/modules/influxdb/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/k3s/go.mod b/modules/k3s/go.mod
index f92938778d1..da3a23fd923 100644
--- a/modules/k3s/go.mod
+++ b/modules/k3s/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/k3s
-go 1.21
+go 1.22
require (
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/docker/go-connections v0.5.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.29.2
k8s.io/apimachinery v0.29.2
diff --git a/modules/k3s/go.sum b/modules/k3s/go.sum
index 01ba28c4f08..e08a07dd42b 100644
--- a/modules/k3s/go.sum
+++ b/modules/k3s/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/k6/go.mod b/modules/k6/go.mod
index 1c752f3db2c..58f96d04aab 100644
--- a/modules/k6/go.mod
+++ b/modules/k6/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/k6
-go 1.21
+go 1.22
require (
- github.com/docker/docker v27.1.0+incompatible
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/docker/docker v27.1.1+incompatible
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
diff --git a/modules/k6/go.sum b/modules/k6/go.sum
index 4de62e95bc7..85338720c8a 100644
--- a/modules/k6/go.sum
+++ b/modules/k6/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/kafka/go.mod b/modules/kafka/go.mod
index 6545097462e..1148bd00f8e 100644
--- a/modules/kafka/go.mod
+++ b/modules/kafka/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/kafka
-go 1.21
+go 1.22
require (
github.com/IBM/sarama v1.42.1
github.com/docker/go-connections v0.5.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
golang.org/x/mod v0.16.0
)
@@ -20,7 +20,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/eapache/go-resiliency v1.4.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect
diff --git a/modules/kafka/go.sum b/modules/kafka/go.sum
index 1c5d061e6f9..778434567d4 100644
--- a/modules/kafka/go.sum
+++ b/modules/kafka/go.sum
@@ -25,8 +25,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/kafka/kafka.go b/modules/kafka/kafka.go
index b2b0e831b56..c0c02890d4f 100644
--- a/modules/kafka/kafka.go
+++ b/modules/kafka/kafka.go
@@ -71,31 +71,16 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
LifecycleHooks: []testcontainers.ContainerLifecycleHooks{
{
PostStarts: []testcontainers.ContainerHook{
- // 1. copy the starter script into the container
+ // Use a single hook to copy the starter script and wait for
+ // the Kafka server to be ready. This prevents the wait running
+ // if the starter script fails to copy.
func(ctx context.Context, c testcontainers.Container) error {
- host, err := c.Host(ctx)
- if err != nil {
- return err
+ // 1. copy the starter script into the container
+ if err := copyStarterScript(ctx, c); err != nil {
+ return fmt.Errorf("copy starter script: %w", err)
}
- inspect, err := c.Inspect(ctx)
- if err != nil {
- return err
- }
-
- hostname := inspect.Config.Hostname
-
- port, err := c.MappedPort(ctx, publicPort)
- if err != nil {
- return err
- }
-
- scriptContent := fmt.Sprintf(starterScriptContent, host, port.Int(), hostname)
-
- return c.CopyToContainer(ctx, []byte(scriptContent), starterScript, 0o755)
- },
- // 2. wait for the Kafka server to be ready
- func(ctx context.Context, c testcontainers.Container) error {
+ // 2. wait for the Kafka server to be ready
return wait.ForLog(".*Transitioning from RECOVERY to RUNNING.*").AsRegexp().WaitUntilReady(ctx, c)
},
},
@@ -131,6 +116,40 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
return &KafkaContainer{Container: container, ClusterID: clusterID}, nil
}
+// copyStarterScript copies the starter script into the container.
+func copyStarterScript(ctx context.Context, c testcontainers.Container) error {
+ if err := wait.ForListeningPort(publicPort).
+ SkipInternalCheck().
+ WaitUntilReady(ctx, c); err != nil {
+ return fmt.Errorf("wait for exposed port: %w", err)
+ }
+
+ host, err := c.Host(ctx)
+ if err != nil {
+ return fmt.Errorf("host: %w", err)
+ }
+
+ inspect, err := c.Inspect(ctx)
+ if err != nil {
+ return fmt.Errorf("inspect: %w", err)
+ }
+
+ hostname := inspect.Config.Hostname
+
+ port, err := c.MappedPort(ctx, publicPort)
+ if err != nil {
+ return fmt.Errorf("mapped port: %w", err)
+ }
+
+ scriptContent := fmt.Sprintf(starterScriptContent, host, port.Int(), hostname)
+
+ if err := c.CopyToContainer(ctx, []byte(scriptContent), starterScript, 0o755); err != nil {
+ return fmt.Errorf("copy to container: %w", err)
+ }
+
+ return nil
+}
+
func WithClusterID(clusterID string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) error {
req.Env["CLUSTER_ID"] = clusterID
diff --git a/modules/localstack/go.mod b/modules/localstack/go.mod
index b6324b8740b..0bd92d1a018 100644
--- a/modules/localstack/go.mod
+++ b/modules/localstack/go.mod
@@ -1,6 +1,6 @@
module github.com/testcontainers/testcontainers-go/modules/localstack
-go 1.21
+go 1.22
require (
github.com/aws/aws-sdk-go v1.50.31
@@ -8,10 +8,10 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.27.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.5
github.com/aws/aws-sdk-go-v2/service/s3 v1.51.2
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/docker/go-connections v0.5.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
golang.org/x/mod v0.16.0
)
diff --git a/modules/localstack/go.sum b/modules/localstack/go.sum
index c12b81d9f5a..532bf9e05b1 100644
--- a/modules/localstack/go.sum
+++ b/modules/localstack/go.sum
@@ -62,8 +62,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/localstack/localstack.go b/modules/localstack/localstack.go
index f06054e83b0..961527cd3e4 100644
--- a/modules/localstack/localstack.go
+++ b/modules/localstack/localstack.go
@@ -74,7 +74,7 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
// Run creates an instance of the LocalStack container type
// - overrideReq: a function that can be used to override the default container request, usually used to set the image version, environment variables for localstack, etc.
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*LocalStackContainer, error) {
- dockerHost := testcontainers.ExtractDockerSocket()
+ dockerHost := testcontainers.MustExtractDockerSocket(ctx)
req := testcontainers.ContainerRequest{
Image: img,
diff --git a/modules/mariadb/go.mod b/modules/mariadb/go.mod
index 375e6a8171d..e8039caf585 100644
--- a/modules/mariadb/go.mod
+++ b/modules/mariadb/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/mariadb
-go 1.21
+go 1.22
require (
github.com/go-sql-driver/mysql v1.7.1
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/mariadb/go.sum b/modules/mariadb/go.sum
index 5edb490b56c..7784d0b8332 100644
--- a/modules/mariadb/go.sum
+++ b/modules/mariadb/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/milvus/go.mod b/modules/milvus/go.mod
index e6820f71f27..1b60fe8cf7f 100644
--- a/modules/milvus/go.mod
+++ b/modules/milvus/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/milvus
-go 1.21
+go 1.22
require (
- github.com/milvus-io/milvus-sdk-go/v2 v2.3.6
+ github.com/milvus-io/milvus-sdk-go/v2 v2.4.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -22,7 +22,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -39,7 +39,7 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
- github.com/milvus-io/milvus-proto/go-api/v2 v2.3.5 // indirect
+ github.com/milvus-io/milvus-proto/go-api/v2 v2.4.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
diff --git a/modules/milvus/go.sum b/modules/milvus/go.sum
index a91219667c2..f8cd7dcf33d 100644
--- a/modules/milvus/go.sum
+++ b/modules/milvus/go.sum
@@ -52,8 +52,8 @@ github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6ps
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -195,10 +195,10 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
-github.com/milvus-io/milvus-proto/go-api/v2 v2.3.5 h1:4XDy6ATB2Z0fl4Jn0hS6BT6/8YaE0d+ZUf4uBH+Z0Do=
-github.com/milvus-io/milvus-proto/go-api/v2 v2.3.5/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek=
-github.com/milvus-io/milvus-sdk-go/v2 v2.3.6 h1:JVn9OdaronLGmtpxvamQf523mtn3Z/CRxkSZCMWutV4=
-github.com/milvus-io/milvus-sdk-go/v2 v2.3.6/go.mod h1:bYFSXVxEj6A/T8BfiR+xkofKbAVZpWiDvKr3SzYUWiA=
+github.com/milvus-io/milvus-proto/go-api/v2 v2.4.0 h1:9KsyZR+neMlRvV52C5sIsLB13jthT2AY/+buyiNjcCg=
+github.com/milvus-io/milvus-proto/go-api/v2 v2.4.0/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek=
+github.com/milvus-io/milvus-sdk-go/v2 v2.4.0 h1:llESmiYiaFqRh0CUrZCLH0IWWkk5r8/vz0tkaA0YzQo=
+github.com/milvus-io/milvus-sdk-go/v2 v2.4.0/go.mod h1:8IKyxVV+kd+RADMuMpo8GXnTDq5ZxrSSWpe9nJieboQ=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
diff --git a/modules/minio/go.mod b/modules/minio/go.mod
index 275258590a4..c50c958c213 100644
--- a/modules/minio/go.mod
+++ b/modules/minio/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/minio
-go 1.21
+go 1.22
require (
github.com/minio/minio-go/v7 v7.0.68
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
diff --git a/modules/minio/go.sum b/modules/minio/go.sum
index 31363a9d8a6..55013e42212 100644
--- a/modules/minio/go.sum
+++ b/modules/minio/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/mockserver/go.mod b/modules/mockserver/go.mod
index 694cb582adf..e2ba76d72d6 100644
--- a/modules/mockserver/go.mod
+++ b/modules/mockserver/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/mockserver
-go 1.21
+go 1.22
require (
github.com/BraspagDevelopers/mock-server-client v0.2.2
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/mockserver/go.sum b/modules/mockserver/go.sum
index 3356838f8b2..e752fd08122 100644
--- a/modules/mockserver/go.sum
+++ b/modules/mockserver/go.sum
@@ -25,8 +25,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/mockserver/mockserver.go b/modules/mockserver/mockserver.go
index 7fb5674755d..b53f164e86b 100644
--- a/modules/mockserver/mockserver.go
+++ b/modules/mockserver/mockserver.go
@@ -26,7 +26,7 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
ExposedPorts: []string{"1080/tcp"},
WaitingFor: wait.ForAll(
wait.ForLog("started on port: 1080"),
- wait.ForListeningPort("1080/tcp"),
+ wait.ForListeningPort("1080/tcp").SkipInternalCheck(),
),
Env: map[string]string{},
}
@@ -43,11 +43,15 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
}
container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
+ var c *MockServerContainer
+ if container != nil {
+ c = &MockServerContainer{Container: container}
+ }
if err != nil {
- return nil, err
+ return c, fmt.Errorf("generic container: %w", err)
}
- return &MockServerContainer{Container: container}, nil
+ return c, nil
}
// GetURL returns the URL of the MockServer container
diff --git a/modules/mongodb/go.mod b/modules/mongodb/go.mod
index 8ab7f9cbd85..e7e8c724bcc 100644
--- a/modules/mongodb/go.mod
+++ b/modules/mongodb/go.mod
@@ -1,9 +1,9 @@
module github.com/testcontainers/testcontainers-go/modules/mongodb
-go 1.21
+go 1.22
require (
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
go.mongodb.org/mongo-driver v1.13.1
)
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/mongodb/go.sum b/modules/mongodb/go.sum
index 070b75e61f4..0f2d5d53369 100644
--- a/modules/mongodb/go.sum
+++ b/modules/mongodb/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/mssql/go.mod b/modules/mssql/go.mod
index d2a0fa1c881..83411fb7aaa 100644
--- a/modules/mssql/go.mod
+++ b/modules/mssql/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/mssql
-go 1.21
+go 1.22
require (
github.com/microsoft/go-mssqldb v1.7.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/mssql/go.sum b/modules/mssql/go.sum
index 850c4d56cbf..4160a61ee2d 100644
--- a/modules/mssql/go.sum
+++ b/modules/mssql/go.sum
@@ -35,8 +35,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/mysql/go.mod b/modules/mysql/go.mod
index 75bb50428e0..f3f00dcb8be 100644
--- a/modules/mysql/go.mod
+++ b/modules/mysql/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/mysql
-go 1.21
+go 1.22
require (
github.com/go-sql-driver/mysql v1.7.1
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
@@ -18,7 +18,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/mysql/go.sum b/modules/mysql/go.sum
index 5edb490b56c..7784d0b8332 100644
--- a/modules/mysql/go.sum
+++ b/modules/mysql/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/nats/go.mod b/modules/nats/go.mod
index 1aad992b239..0a4863c41ec 100644
--- a/modules/nats/go.mod
+++ b/modules/nats/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/nats
-go 1.21
+go 1.22
require (
github.com/nats-io/nats.go v1.33.1
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/nats/go.sum b/modules/nats/go.sum
index 153b3574912..11a786d5d4a 100644
--- a/modules/nats/go.sum
+++ b/modules/nats/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/neo4j/go.mod b/modules/neo4j/go.mod
index c40ca91f0ee..13f310eeb50 100644
--- a/modules/neo4j/go.mod
+++ b/modules/neo4j/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/neo4j
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
github.com/neo4j/neo4j-go-driver/v5 v5.18.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -18,7 +18,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/neo4j/go.sum b/modules/neo4j/go.sum
index 65b9f7c873c..2576f66232f 100644
--- a/modules/neo4j/go.sum
+++ b/modules/neo4j/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/ollama/go.mod b/modules/ollama/go.mod
index 0e7193c540d..5e586e858ed 100644
--- a/modules/ollama/go.mod
+++ b/modules/ollama/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/ollama
-go 1.21
+go 1.22
require (
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/google/uuid v1.6.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
github.com/tmc/langchaingo v0.1.5
)
diff --git a/modules/ollama/go.sum b/modules/ollama/go.sum
index 3b2564fd3b9..07f9ef689fe 100644
--- a/modules/ollama/go.sum
+++ b/modules/ollama/go.sum
@@ -25,8 +25,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0=
github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/openfga/go.mod b/modules/openfga/go.mod
index 98be0b4cc38..388b9c040c1 100644
--- a/modules/openfga/go.mod
+++ b/modules/openfga/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/openfga
-go 1.21
+go 1.22
require (
github.com/openfga/go-sdk v0.3.5
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/openfga/go.sum b/modules/openfga/go.sum
index e83875640f3..a2fe09d6b47 100644
--- a/modules/openfga/go.sum
+++ b/modules/openfga/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/openldap/go.mod b/modules/openldap/go.mod
index 5f5daaf9712..2f13ed78cc1 100644
--- a/modules/openldap/go.mod
+++ b/modules/openldap/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/openldap
-go 1.21
+go 1.22
require (
github.com/go-ldap/ldap/v3 v3.4.6
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -18,7 +18,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/openldap/go.sum b/modules/openldap/go.sum
index 799277ab693..57de4980776 100644
--- a/modules/openldap/go.sum
+++ b/modules/openldap/go.sum
@@ -27,8 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/opensearch/go.mod b/modules/opensearch/go.mod
index 310e35be631..50146f5964b 100644
--- a/modules/opensearch/go.mod
+++ b/modules/opensearch/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/opensearch
-go 1.21
+go 1.22
require (
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/docker/go-units v0.5.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
diff --git a/modules/opensearch/go.sum b/modules/opensearch/go.sum
index 4de62e95bc7..85338720c8a 100644
--- a/modules/opensearch/go.sum
+++ b/modules/opensearch/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/postgres/go.mod b/modules/postgres/go.mod
index 56e8ec9a17e..4b579972603 100644
--- a/modules/postgres/go.mod
+++ b/modules/postgres/go.mod
@@ -1,13 +1,13 @@
module github.com/testcontainers/testcontainers-go/modules/postgres
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
github.com/jackc/pgx/v5 v5.5.4
github.com/lib/pq v1.10.9
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
@@ -22,7 +22,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/postgres/go.sum b/modules/postgres/go.sum
index 292324c2232..42933793538 100644
--- a/modules/postgres/go.sum
+++ b/modules/postgres/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/pulsar/go.mod b/modules/pulsar/go.mod
index 67796255bfd..b9e77f4d58c 100644
--- a/modules/pulsar/go.mod
+++ b/modules/pulsar/go.mod
@@ -1,13 +1,13 @@
module github.com/testcontainers/testcontainers-go/modules/pulsar
-go 1.21
+go 1.22
require (
github.com/apache/pulsar-client-go v0.10.0
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/docker/go-connections v0.5.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
replace github.com/testcontainers/testcontainers-go => ../..
diff --git a/modules/pulsar/go.sum b/modules/pulsar/go.sum
index a38890a005e..e8aa08578a1 100644
--- a/modules/pulsar/go.sum
+++ b/modules/pulsar/go.sum
@@ -98,8 +98,8 @@ github.com/dimfeld/httptreemux v5.0.1+incompatible h1:Qj3gVcDNoOthBAqftuD596rm4w
github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/qdrant/go.mod b/modules/qdrant/go.mod
index 187209855ec..85db7229b35 100644
--- a/modules/qdrant/go.mod
+++ b/modules/qdrant/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/qdrant
-go 1.21
+go 1.22
require (
github.com/qdrant/go-client v1.7.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
google.golang.org/grpc v1.64.1
)
@@ -18,7 +18,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/qdrant/go.sum b/modules/qdrant/go.sum
index 2d9e4c62cad..c4425e81821 100644
--- a/modules/qdrant/go.sum
+++ b/modules/qdrant/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/rabbitmq/go.mod b/modules/rabbitmq/go.mod
index ba23d161122..e9d4a267fa2 100644
--- a/modules/rabbitmq/go.mod
+++ b/modules/rabbitmq/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/rabbitmq
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
github.com/rabbitmq/amqp091-go v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -25,7 +25,7 @@ require (
github.com/containerd/log v0.1.0 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/rabbitmq/go.sum b/modules/rabbitmq/go.sum
index 250c71bee6c..f57610f497b 100644
--- a/modules/rabbitmq/go.sum
+++ b/modules/rabbitmq/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/redis/go.mod b/modules/redis/go.mod
index 6f6c5eb625d..e76c925c7ec 100644
--- a/modules/redis/go.mod
+++ b/modules/redis/go.mod
@@ -1,12 +1,12 @@
module github.com/testcontainers/testcontainers-go/modules/redis
-go 1.21
+go 1.22
require (
github.com/go-redis/redis/v8 v8.11.5
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
@@ -25,7 +25,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/redis/go.sum b/modules/redis/go.sum
index 786b36ccd8c..1698de2ecff 100644
--- a/modules/redis/go.sum
+++ b/modules/redis/go.sum
@@ -28,8 +28,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/redpanda/go.mod b/modules/redpanda/go.mod
index 732f26140fa..4ce12872ee7 100644
--- a/modules/redpanda/go.mod
+++ b/modules/redpanda/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/redpanda
-go 1.21
+go 1.22
require (
github.com/docker/go-connections v0.5.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
github.com/twmb/franz-go v1.16.1
github.com/twmb/franz-go/pkg/kadm v1.11.0
golang.org/x/mod v0.16.0
@@ -28,7 +28,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
diff --git a/modules/redpanda/go.sum b/modules/redpanda/go.sum
index e993beb0b62..cf26162896b 100644
--- a/modules/redpanda/go.sum
+++ b/modules/redpanda/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/redpanda/redpanda.go b/modules/redpanda/redpanda.go
index 7f466175364..3ed39320662 100644
--- a/modules/redpanda/redpanda.go
+++ b/modules/redpanda/redpanda.go
@@ -86,6 +86,13 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
"--smp=1",
"--memory=1G",
},
+ WaitingFor: wait.ForAll(
+ // Wait for the ports to be exposed only as the container needs configuration
+ // before it will bind to the ports and be ready to serve requests.
+ wait.ForListeningPort(defaultKafkaAPIPort).SkipInternalCheck(),
+ wait.ForListeningPort(defaultAdminAPIPort).SkipInternalCheck(),
+ wait.ForListeningPort(defaultSchemaRegistryPort).SkipInternalCheck(),
+ ),
},
Started: true,
}
@@ -119,7 +126,7 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
// 4. Register extra kafka listeners if provided, network aliases will be
// set
- if err := registerListeners(ctx, settings, req); err != nil {
+ if err := registerListeners(settings, req); err != nil {
return nil, fmt.Errorf("failed to register listeners: %w", err)
}
@@ -200,11 +207,13 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
return nil, fmt.Errorf("failed to copy redpanda.yaml into container: %w", err)
}
- // 8. Wait until Redpanda is ready to serve requests
+ // 8. Wait until Redpanda is ready to serve requests.
err = wait.ForAll(
wait.ForListeningPort(defaultKafkaAPIPort),
- wait.ForLog("Successfully started Redpanda!").WithPollInterval(100*time.Millisecond)).
- WaitUntilReady(ctx, container)
+ wait.ForListeningPort(defaultAdminAPIPort),
+ wait.ForListeningPort(defaultSchemaRegistryPort),
+ wait.ForLog("Successfully started Redpanda!"),
+ ).WaitUntilReady(ctx, container)
if err != nil {
return nil, fmt.Errorf("failed to wait for Redpanda readiness: %w", err)
}
@@ -299,7 +308,7 @@ func renderBootstrapConfig(settings options) ([]byte, error) {
// registerListeners validates that the provided listeners are valid and set network aliases for the provided addresses.
// The container must be attached to at least one network.
-func registerListeners(ctx context.Context, settings options, req testcontainers.GenericContainerRequest) error {
+func registerListeners(settings options, req testcontainers.GenericContainerRequest) error {
if len(settings.Listeners) == 0 {
return nil
}
diff --git a/modules/registry/examples_test.go b/modules/registry/examples_test.go
index 3b9f55099da..ada7e33b857 100644
--- a/modules/registry/examples_test.go
+++ b/modules/registry/examples_test.go
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"log"
- "os"
"path/filepath"
"github.com/testcontainers/testcontainers-go"
@@ -40,8 +39,9 @@ func ExampleRun() {
func ExampleRun_withAuthentication() {
// htpasswdFile {
+ ctx := context.Background()
registryContainer, err := registry.Run(
- context.Background(),
+ ctx,
"registry:2.8.3",
registry.WithHtpasswdFile(filepath.Join("testdata", "auth", "htpasswd")),
registry.WithData(filepath.Join("testdata", "data")),
@@ -51,33 +51,21 @@ func ExampleRun_withAuthentication() {
log.Fatalf("failed to start container: %s", err)
}
defer func() {
- if err := registryContainer.Terminate(context.Background()); err != nil {
+ if err := registryContainer.Terminate(ctx); err != nil {
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic
}
}()
- registryPort, err := registryContainer.MappedPort(context.Background(), "5000/tcp")
+ registryHost, err := registryContainer.HostAddress(ctx)
if err != nil {
- log.Fatalf("failed to get mapped port: %s", err) // nolint:gocritic
+ log.Fatalf("failed to get host: %s", err) // nolint:gocritic
}
- strPort := registryPort.Port()
-
- previousAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG")
- // make sure the Docker Auth credentials are set
- // using the same as in the Docker Registry
- // testuser:testpassword
- os.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+strPort+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" }
- },
- "credsStore": "desktop"
- }`)
- defer func() {
- // reset the original state after the example.
- os.Unsetenv("DOCKER_AUTH_CONFIG")
- os.Setenv("DOCKER_AUTH_CONFIG", previousAuthConfig)
- }()
+ cleanup, err := registry.SetDockerAuthConfig(registryHost, "testuser", "testpassword")
+ if err != nil {
+ log.Fatalf("failed to set docker auth config: %s", err) // nolint:gocritic
+ }
+ defer cleanup()
// build a custom redis image from the private registry,
// using RegistryName of the container as the registry.
@@ -87,7 +75,7 @@ func ExampleRun_withAuthentication() {
FromDockerfile: testcontainers.FromDockerfile{
Context: filepath.Join("testdata", "redis"),
BuildArgs: map[string]*string{
- "REGISTRY_PORT": &strPort,
+ "REGISTRY_HOST": ®istryHost,
},
PrintBuildLog: true,
},
@@ -118,9 +106,10 @@ func ExampleRun_withAuthentication() {
}
func ExampleRun_pushImage() {
+ ctx := context.Background()
registryContainer, err := registry.Run(
- context.Background(),
- "registry:2.8.3",
+ ctx,
+ registry.DefaultImage,
registry.WithHtpasswdFile(filepath.Join("testdata", "auth", "htpasswd")),
registry.WithData(filepath.Join("testdata", "data")),
)
@@ -128,37 +117,27 @@ func ExampleRun_pushImage() {
log.Fatalf("failed to start container: %s", err)
}
defer func() {
- if err := registryContainer.Terminate(context.Background()); err != nil {
+ if err := registryContainer.Terminate(ctx); err != nil {
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic
}
}()
- registryPort, err := registryContainer.MappedPort(context.Background(), "5000/tcp")
+ registryHost, err := registryContainer.HostAddress(ctx)
if err != nil {
- log.Fatalf("failed to get mapped port: %s", err) // nolint:gocritic
+ log.Fatalf("failed to get host: %s", err) // nolint:gocritic
}
- strPort := registryPort.Port()
-
- previousAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG")
- // make sure the Docker Auth credentials are set
- // using the same as in the Docker Registry
- // testuser:testpassword
// Besides, we are also setting the authentication
// for both the registry and localhost to make sure
// the image is pushed to the private registry.
- os.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+strPort+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" },
- "`+registryContainer.RegistryName+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" }
- },
- "credsStore": "desktop"
- }`)
- defer func() {
- // reset the original state after the example.
- os.Unsetenv("DOCKER_AUTH_CONFIG")
- os.Setenv("DOCKER_AUTH_CONFIG", previousAuthConfig)
- }()
+ cleanup, err := registry.SetDockerAuthConfig(
+ registryHost, "testuser", "testpassword",
+ registryContainer.RegistryName, "testuser", "testpassword",
+ )
+ if err != nil {
+ log.Fatalf("failed to set docker auth config: %s", err) // nolint:gocritic
+ }
+ defer cleanup()
// build a custom redis image from the private registry,
// using RegistryName of the container as the registry.
@@ -174,7 +153,7 @@ func ExampleRun_pushImage() {
FromDockerfile: testcontainers.FromDockerfile{
Context: filepath.Join("testdata", "redis"),
BuildArgs: map[string]*string{
- "REGISTRY_PORT": &strPort,
+ "REGISTRY_HOST": ®istryHost,
},
Repo: repo,
Tag: tag,
diff --git a/modules/registry/go.mod b/modules/registry/go.mod
index 47e546cd22e..36e95dfdf06 100644
--- a/modules/registry/go.mod
+++ b/modules/registry/go.mod
@@ -1,10 +1,12 @@
module github.com/testcontainers/testcontainers-go/modules/registry
-go 1.21
+go 1.22
require (
- github.com/docker/docker v27.1.0+incompatible
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/cpuguy83/dockercfg v0.3.1
+ github.com/docker/docker v27.1.1+incompatible
+ github.com/stretchr/testify v1.9.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -15,7 +17,7 @@ require (
github.com/containerd/containerd v1.7.18 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
- github.com/cpuguy83/dockercfg v0.3.1 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
@@ -26,6 +28,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
+ github.com/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
@@ -37,6 +40,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
@@ -53,6 +57,7 @@ require (
golang.org/x/sys v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/testcontainers/testcontainers-go => ../..
diff --git a/modules/registry/go.sum b/modules/registry/go.sum
index 4de62e95bc7..5583e9b0fc3 100644
--- a/modules/registry/go.sum
+++ b/modules/registry/go.sum
@@ -16,6 +16,7 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -23,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -52,6 +53,10 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
@@ -78,6 +83,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
+github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
@@ -172,6 +179,8 @@ google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvy
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/modules/registry/registry.go b/modules/registry/registry.go
index 22e1aa1532e..b554f8dcc74 100644
--- a/modules/registry/registry.go
+++ b/modules/registry/registry.go
@@ -5,10 +5,13 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
+ "net"
"net/http"
+ "os"
"strings"
"time"
+ "github.com/cpuguy83/dockercfg"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
@@ -16,6 +19,14 @@ import (
"github.com/testcontainers/testcontainers-go/wait"
)
+const (
+ // registryPort is the default port used by the Registry container.
+ registryPort = "5000/tcp"
+
+ // DefaultImage is the default image used by the Registry container.
+ DefaultImage = "registry:2.8.3"
+)
+
// RegistryContainer represents the Registry container type used in the module
type RegistryContainer struct {
testcontainers.Container
@@ -24,17 +35,56 @@ type RegistryContainer struct {
// Address returns the address of the Registry container, using the HTTP protocol
func (c *RegistryContainer) Address(ctx context.Context) (string, error) {
- port, err := c.MappedPort(ctx, "5000")
+ host, err := c.HostAddress(ctx)
if err != nil {
return "", err
}
- ipAddress, err := c.Host(ctx)
+ return "http://" + host, nil
+}
+
+// HostAddress returns the host address including port of the Registry container.
+func (c *RegistryContainer) HostAddress(ctx context.Context) (string, error) {
+ port, err := c.MappedPort(ctx, registryPort)
+ if err != nil {
+ return "", fmt.Errorf("mapped port: %w", err)
+ }
+
+ host, err := c.Container.Host(ctx)
if err != nil {
- return "", err
+ return "", fmt.Errorf("host: %w", err)
+ }
+
+ if host == "localhost" {
+ // This is a workaround for WSL, where localhost is not reachable from Docker.
+ host, err = localAddress(ctx)
+ if err != nil {
+ return "", fmt.Errorf("local ip: %w", err)
+ }
+ }
+
+ return net.JoinHostPort(host, port.Port()), nil
+}
+
+// localAddress returns the local address of the machine
+// which can be used to connect to the local registry.
+// This avoids the issues with localhost on WSL.
+func localAddress(ctx context.Context) (string, error) {
+ if os.Getenv("WSL_DISTRO_NAME") == "" {
+ return "localhost", nil
}
- return fmt.Sprintf("http://%s:%s", ipAddress, port.Port()), nil
+ var d net.Dialer
+ conn, err := d.DialContext(ctx, "udp", "golang.org:80")
+ if err != nil {
+ return "", fmt.Errorf("dial: %w", err)
+ }
+
+ defer conn.Close()
+
+ localAddr := conn.LocalAddr().(*net.UDPAddr)
+
+ return localAddr.IP.String(), nil
}
// getEndpointWithAuth returns the HTTP endpoint of the Registry container, along with the image auth
@@ -165,7 +215,7 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*RegistryContainer, error) {
req := testcontainers.ContainerRequest{
Image: img,
- ExposedPorts: []string{"5000/tcp"},
+ ExposedPorts: []string{registryPort},
Env: map[string]string{
// convenient for testing
"REGISTRY_STORAGE_DELETE_ENABLED": "true",
@@ -203,3 +253,55 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
return c, nil
}
+
+// SetDockerAuthConfig sets the DOCKER_AUTH_CONFIG environment variable with
+// authentication for the given host, username and password sets.
+// It returns a function to reset the environment back to the previous state.
+func SetDockerAuthConfig(host, username, password string, additional ...string) (func(), error) {
+ authConfigs, err := DockerAuthConfig(host, username, password, additional...)
+ if err != nil {
+ return nil, fmt.Errorf("docker auth config: %w", err)
+ }
+
+ auth, err := json.Marshal(dockercfg.Config{AuthConfigs: authConfigs})
+ if err != nil {
+ return nil, fmt.Errorf("marshal auth config: %w", err)
+ }
+
+ previousAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG")
+ os.Setenv("DOCKER_AUTH_CONFIG", string(auth))
+
+ return func() {
+ if previousAuthConfig == "" {
+ os.Unsetenv("DOCKER_AUTH_CONFIG")
+ return
+ }
+ os.Setenv("DOCKER_AUTH_CONFIG", previousAuthConfig)
+ }, nil
+}
+
+// DockerAuthConfig returns a map of AuthConfigs including base64 encoded Auth field
+// for the provided details. It also accepts additional host, username and password
+// triples to add more auth configurations.
+func DockerAuthConfig(host, username, password string, additional ...string) (map[string]dockercfg.AuthConfig, error) {
+ if len(additional)%3 != 0 {
+ return nil, fmt.Errorf("additional must be a multiple of 3")
+ }
+
+ additional = append(additional, host, username, password)
+ authConfigs := make(map[string]dockercfg.AuthConfig, len(additional)/3)
+ for i := 0; i < len(additional); i += 3 {
+ host, username, password := additional[i], additional[i+1], additional[i+2]
+ auth := dockercfg.AuthConfig{
+ Username: username,
+ Password: password,
+ }
+
+ if username != "" || password != "" {
+ auth.Auth = base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
+ }
+ authConfigs[host] = auth
+ }
+
+ return authConfigs, nil
+}
diff --git a/modules/registry/registry_test.go b/modules/registry/registry_test.go
index dd071ef5be5..d40f75125e4 100644
--- a/modules/registry/registry_test.go
+++ b/modules/registry/registry_test.go
@@ -2,147 +2,103 @@ package registry_test
import (
"context"
+ "encoding/json"
"net/http"
"path/filepath"
- "strings"
"testing"
+ "github.com/cpuguy83/dockercfg"
+ "github.com/stretchr/testify/require"
+
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/registry"
"github.com/testcontainers/testcontainers-go/wait"
)
func TestRegistry_unauthenticated(t *testing.T) {
- container, err := registry.Run(context.Background(), "registry:2.8.3")
- if err != nil {
- t.Fatal(err)
- }
+ ctx := context.Background()
+ container, err := registry.Run(ctx, registry.DefaultImage)
+ terminateContainerOnEnd(t, ctx, container)
+ require.NoError(t, err)
- // Clean up the container after the test is complete
- t.Cleanup(func() {
- if err := container.Terminate(context.Background()); err != nil {
- t.Fatalf("failed to terminate container: %s", err)
- }
- })
-
- httpAddress, err := container.Address(context.Background())
- if err != nil {
- t.Fatal(err)
- }
+ httpAddress, err := container.Address(ctx)
+ require.NoError(t, err)
resp, err := http.Get(httpAddress + "/v2/_catalog")
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("expected status code 200, but got %d", resp.StatusCode)
- }
+ require.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestRunContainer_authenticated(t *testing.T) {
+ ctx := context.Background()
registryContainer, err := registry.Run(
- context.Background(),
- "registry:2.8.3",
+ ctx,
+ registry.DefaultImage,
registry.WithHtpasswdFile(filepath.Join("testdata", "auth", "htpasswd")),
registry.WithData(filepath.Join("testdata", "data")),
)
- if err != nil {
- t.Fatalf("failed to start container: %s", err)
- }
- t.Cleanup(func() {
- if err := registryContainer.Terminate(context.Background()); err != nil {
- t.Fatalf("failed to terminate container: %s", err)
- }
- })
+ terminateContainerOnEnd(t, ctx, registryContainer)
+ require.NoError(t, err)
// httpAddress {
- httpAddress, err := registryContainer.Address(context.Background())
+ httpAddress, err := registryContainer.Address(ctx)
// }
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
- registryPort, err := registryContainer.MappedPort(context.Background(), "5000/tcp")
- if err != nil {
- t.Fatalf("failed to get mapped port: %s", err)
- }
- strPort := registryPort.Port()
+ registryHost, err := registryContainer.HostAddress(ctx)
+ require.NoError(t, err)
t.Run("HTTP connection without basic auth fails", func(tt *testing.T) {
httpCli := http.Client{}
- req, err := http.NewRequest("GET", httpAddress+"/v2/_catalog", nil)
- if err != nil {
- tt.Fatal(err)
- }
+ req, err := http.NewRequest(http.MethodGet, httpAddress+"/v2/_catalog", nil)
+ require.NoError(t, err)
resp, err := httpCli.Do(req)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer resp.Body.Close()
- if resp.StatusCode != http.StatusUnauthorized {
- t.Fatalf("expected status code 401, but got %d", resp.StatusCode)
- }
+ require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("HTTP connection with incorrect basic auth fails", func(tt *testing.T) {
httpCli := http.Client{}
- req, err := http.NewRequest("GET", httpAddress+"/v2/_catalog", nil)
- if err != nil {
- tt.Fatal(err)
- }
+ req, err := http.NewRequest(http.MethodGet, httpAddress+"/v2/_catalog", nil)
+ require.NoError(t, err)
req.SetBasicAuth("foo", "bar")
resp, err := httpCli.Do(req)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer resp.Body.Close()
- if resp.StatusCode != http.StatusUnauthorized {
- t.Fatalf("expected status code 401, but got %d", resp.StatusCode)
- }
+ require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("HTTP connection with basic auth succeeds", func(tt *testing.T) {
httpCli := http.Client{}
- req, err := http.NewRequest("GET", httpAddress+"/v2/_catalog", nil)
- if err != nil {
- tt.Fatal(err)
- }
+ req, err := http.NewRequest(http.MethodGet, httpAddress+"/v2/_catalog", nil)
+ require.NoError(t, err)
req.SetBasicAuth("testuser", "testpassword")
resp, err := httpCli.Do(req)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("expected status code 200, but got %d", resp.StatusCode)
- }
+ require.Equal(t, http.StatusOK, resp.StatusCode)
})
t.Run("build images with wrong credentials fails", func(tt *testing.T) {
- // Zm9vOmJhcg== is base64 for foo:bar
- tt.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+strPort+`": { "username": "foo", "password": "bar", "auth": "Zm9vOmJhcg==" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(tt, registryHost, "foo", "bar")
redisC, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
FromDockerfile: testcontainers.FromDockerfile{
Context: filepath.Join("testdata", "redis"),
BuildArgs: map[string]*string{
- "REGISTRY_PORT": &strPort,
+ "REGISTRY_HOST": ®istryHost,
},
},
AlwaysPullImage: true, // make sure the authentication takes place
@@ -151,31 +107,13 @@ func TestRunContainer_authenticated(t *testing.T) {
},
Started: true,
})
- if err == nil {
- tt.Fatalf("expected to fail to start container, but it did not")
- }
- if redisC != nil {
- tt.Fatal("redis container should not be running")
- tt.Cleanup(func() {
- if err := redisC.Terminate(context.Background()); err != nil {
- tt.Fatalf("failed to terminate container: %s", err)
- }
- })
- }
-
- if !strings.Contains(err.Error(), "unauthorized: authentication required") {
- tt.Fatalf("expected error to be 'unauthorized: authentication required' but got '%s'", err.Error())
- }
+ terminateContainerOnEnd(tt, ctx, redisC)
+ require.Error(tt, err)
+ require.Contains(tt, err.Error(), "unauthorized: authentication required")
})
t.Run("build image with valid credentials", func(tt *testing.T) {
- // dGVzdHVzZXI6dGVzdHBhc3N3b3Jk is base64 for testuser:testpassword
- tt.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+strPort+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(tt, registryHost, "testuser", "testpassword")
// build a custom redis image from the private registry,
// using RegistryName of the container as the registry.
@@ -187,7 +125,7 @@ func TestRunContainer_authenticated(t *testing.T) {
FromDockerfile: testcontainers.FromDockerfile{
Context: filepath.Join("testdata", "redis"),
BuildArgs: map[string]*string{
- "REGISTRY_PORT": &strPort,
+ "REGISTRY_HOST": ®istryHost,
},
},
AlwaysPullImage: true, // make sure the authentication takes place
@@ -196,97 +134,58 @@ func TestRunContainer_authenticated(t *testing.T) {
},
Started: true,
})
- if err != nil {
- tt.Fatalf("failed to start container: %s", err)
- }
-
- tt.Cleanup(func() {
- if err := redisC.Terminate(context.Background()); err != nil {
- tt.Fatalf("failed to terminate container: %s", err)
- }
- })
+ terminateContainerOnEnd(tt, ctx, redisC)
+ require.NoError(tt, err)
state, err := redisC.State(context.Background())
- if err != nil {
- tt.Fatalf("failed to get redis container state: %s", err) // nolint:gocritic
- }
-
- if !state.Running {
- tt.Fatalf("expected redis container to be running, but it is not")
- }
+ require.NoError(tt, err)
+ require.True(tt, state.Running, "expected redis container to be running, but it is not")
})
}
func TestRunContainer_authenticated_withCredentials(t *testing.T) {
+ ctx := context.Background()
// htpasswdString {
registryContainer, err := registry.Run(
- context.Background(),
- "registry:2.8.3",
+ ctx,
+ registry.DefaultImage,
registry.WithHtpasswd("testuser:$2y$05$tTymaYlWwJOqie.bcSUUN.I.kxmo1m5TLzYQ4/ejJ46UMXGtq78EO"),
)
// }
- if err != nil {
- t.Fatalf("failed to start container: %s", err)
- }
- t.Cleanup(func() {
- if err := registryContainer.Terminate(context.Background()); err != nil {
- t.Fatalf("failed to terminate container: %s", err)
- }
- })
+ terminateContainerOnEnd(t, ctx, registryContainer)
+ require.NoError(t, err)
- httpAddress, err := registryContainer.Address(context.Background())
- if err != nil {
- t.Fatal(err)
- }
+ httpAddress, err := registryContainer.Address(ctx)
+ require.NoError(t, err)
httpCli := http.Client{}
- req, err := http.NewRequest("GET", httpAddress+"/v2/_catalog", nil)
- if err != nil {
- t.Fatal(err)
- }
+ req, err := http.NewRequest(http.MethodGet, httpAddress+"/v2/_catalog", nil)
+ require.NoError(t, err)
req.SetBasicAuth("testuser", "testpassword")
resp, err := httpCli.Do(req)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("expected status code 200, but got %d", resp.StatusCode)
- }
+ require.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestRunContainer_wrongData(t *testing.T) {
+ ctx := context.Background()
registryContainer, err := registry.Run(
- context.Background(),
- "registry:2.8.3",
+ ctx,
+ registry.DefaultImage,
registry.WithHtpasswdFile(filepath.Join("testdata", "auth", "htpasswd")),
registry.WithData(filepath.Join("testdata", "wrongdata")),
)
- if err != nil {
- t.Fatalf("failed to start container: %s", err)
- }
- t.Cleanup(func() {
- if err := registryContainer.Terminate(context.Background()); err != nil {
- t.Fatalf("failed to terminate container: %s", err)
- }
- })
+ terminateContainerOnEnd(t, ctx, registryContainer)
+ require.NoError(t, err)
- registryPort, err := registryContainer.MappedPort(context.Background(), "5000/tcp")
- if err != nil {
- t.Fatalf("failed to get mapped port: %s", err)
- }
- strPort := registryPort.Port()
+ registryHost, err := registryContainer.HostAddress(ctx)
+ require.NoError(t, err)
- // dGVzdHVzZXI6dGVzdHBhc3N3b3Jk is base64 for testuser:testpassword
- t.Setenv("DOCKER_AUTH_CONFIG", `{
- "auths": {
- "localhost:`+strPort+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" }
- },
- "credsStore": "desktop"
- }`)
+ setAuthConfig(t, registryHost, "testuser", "testpassword")
// build a custom redis image from the private registry,
// using RegistryName of the container as the registry.
@@ -298,7 +197,7 @@ func TestRunContainer_wrongData(t *testing.T) {
FromDockerfile: testcontainers.FromDockerfile{
Context: filepath.Join("testdata", "redis"),
BuildArgs: map[string]*string{
- "REGISTRY_PORT": &strPort,
+ "REGISTRY_HOST": ®istryHost,
},
},
AlwaysPullImage: true, // make sure the authentication takes place
@@ -307,19 +206,33 @@ func TestRunContainer_wrongData(t *testing.T) {
},
Started: true,
})
- if err == nil {
- t.Fatalf("expected to fail to start container, but it did not")
- }
- if redisC != nil {
- t.Fatal("redis container should not be running")
- t.Cleanup(func() {
- if err := redisC.Terminate(context.Background()); err != nil {
- t.Fatalf("failed to terminate container: %s", err)
- }
- })
- }
+ terminateContainerOnEnd(t, ctx, redisC)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "manifest unknown")
+}
+
+// setAuthConfig sets the DOCKER_AUTH_CONFIG environment variable with
+// authentication for with the given host, username and password.
+func setAuthConfig(t *testing.T, host, username, password string) {
+ t.Helper()
- if !strings.Contains(err.Error(), "manifest unknown") {
- t.Fatalf("expected error to be 'manifest unknown' but got '%s'", err.Error())
+ authConfigs, err := registry.DockerAuthConfig(host, username, password)
+ require.NoError(t, err)
+ auth, err := json.Marshal(dockercfg.Config{AuthConfigs: authConfigs})
+ require.NoError(t, err)
+
+ t.Setenv("DOCKER_AUTH_CONFIG", string(auth))
+}
+
+// terminateContainerOnEnd terminates the container when the test ends if it is not nil.
+func terminateContainerOnEnd(tb testing.TB, ctx context.Context, container testcontainers.Container) {
+ tb.Helper()
+
+ if container == nil {
+ return
}
+
+ tb.Cleanup(func() {
+ require.NoError(tb, container.Terminate(ctx))
+ })
}
diff --git a/modules/registry/testdata/redis/Dockerfile b/modules/registry/testdata/redis/Dockerfile
index 502db64261b..280c33c8277 100644
--- a/modules/registry/testdata/redis/Dockerfile
+++ b/modules/registry/testdata/redis/Dockerfile
@@ -1,3 +1,3 @@
-ARG REGISTRY_PORT=5000
+ARG REGISTRY_HOST=localhost:5000
-FROM localhost:${REGISTRY_PORT}/redis:5.0-alpine
\ No newline at end of file
+FROM ${REGISTRY_HOST}/redis:5.0-alpine
diff --git a/modules/surrealdb/go.mod b/modules/surrealdb/go.mod
index dca93f7a970..d3d2d049d62 100644
--- a/modules/surrealdb/go.mod
+++ b/modules/surrealdb/go.mod
@@ -1,10 +1,10 @@
module github.com/testcontainers/testcontainers-go/modules/surrealdb
-go 1.21
+go 1.22
require (
github.com/surrealdb/surrealdb.go v0.2.1
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
)
require (
@@ -17,7 +17,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/surrealdb/go.sum b/modules/surrealdb/go.sum
index 0b708c46c3a..4a82bd32171 100644
--- a/modules/surrealdb/go.sum
+++ b/modules/surrealdb/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/valkey/go.mod b/modules/valkey/go.mod
index a92530c5764..0c1173f987a 100644
--- a/modules/valkey/go.mod
+++ b/modules/valkey/go.mod
@@ -1,11 +1,11 @@
module github.com/testcontainers/testcontainers-go/modules/valkey
-go 1.21
+go 1.22
require (
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
github.com/valkey-io/valkey-go v1.0.41
)
@@ -22,7 +22,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/valkey/go.sum b/modules/valkey/go.sum
index 18eacee2e98..f92f0dbcc77 100644
--- a/modules/valkey/go.sum
+++ b/modules/valkey/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/vault/go.mod b/modules/vault/go.mod
index f3086a358b4..064a23bccec 100644
--- a/modules/vault/go.mod
+++ b/modules/vault/go.mod
@@ -1,12 +1,12 @@
module github.com/testcontainers/testcontainers-go/modules/vault
-go 1.21
+go 1.22
require (
- github.com/docker/docker v27.1.0+incompatible
+ github.com/docker/docker v27.1.1+incompatible
github.com/hashicorp/vault-client-go v0.4.3
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
github.com/tidwall/gjson v1.17.1
)
diff --git a/modules/vault/go.sum b/modules/vault/go.sum
index 4cf0f339d1b..5bed30736ed 100644
--- a/modules/vault/go.sum
+++ b/modules/vault/go.sum
@@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/vearch/examples_test.go b/modules/vearch/examples_test.go
index 116484c5832..d75bd8e66ab 100644
--- a/modules/vearch/examples_test.go
+++ b/modules/vearch/examples_test.go
@@ -9,6 +9,7 @@ import (
)
func ExampleRun() {
+ // runVearchContainer {
ctx := context.Background()
vearchContainer, err := vearch.Run(ctx, "vearch/vearch:3.5.1")
diff --git a/modules/vearch/go.mod b/modules/vearch/go.mod
index 2d385f66487..eaad237db70 100644
--- a/modules/vearch/go.mod
+++ b/modules/vearch/go.mod
@@ -2,7 +2,7 @@ module github.com/testcontainers/testcontainers-go/modules/vearch
go 1.22.0
-require github.com/testcontainers/testcontainers-go v0.32.0
+require github.com/testcontainers/testcontainers-go v0.33.0
require (
dario.cat/mergo v1.0.0 // indirect
@@ -14,7 +14,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/vearch/go.sum b/modules/vearch/go.sum
index 4de62e95bc7..85338720c8a 100644
--- a/modules/vearch/go.sum
+++ b/modules/vearch/go.sum
@@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/modules/weaviate/go.mod b/modules/weaviate/go.mod
index 9cd6cafa72f..6572fb3607b 100644
--- a/modules/weaviate/go.mod
+++ b/modules/weaviate/go.mod
@@ -1,9 +1,9 @@
module github.com/testcontainers/testcontainers-go/modules/weaviate
-go 1.21
+go 1.22
require (
- github.com/testcontainers/testcontainers-go v0.32.0
+ github.com/testcontainers/testcontainers-go v0.33.0
github.com/weaviate/weaviate-go-client/v4 v4.13.1
google.golang.org/grpc v1.64.1
)
@@ -21,7 +21,7 @@ require (
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker v27.1.0+incompatible // indirect
+ github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/modules/weaviate/go.sum b/modules/weaviate/go.sum
index b65325ee655..4d55fd59afb 100644
--- a/modules/weaviate/go.sum
+++ b/modules/weaviate/go.sum
@@ -32,8 +32,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/docker v27.1.0+incompatible h1:rEHVQc4GZ0MIQKifQPHSFGV/dVgaZafgRf8fCPtDYBs=
-github.com/docker/docker v27.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
+github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/options.go b/options.go
index 6b5dcb1293d..2849b156678 100644
--- a/options.go
+++ b/options.go
@@ -3,6 +3,7 @@ package testcontainers
import (
"context"
"fmt"
+ "net/url"
"time"
"dario.cat/mergo"
@@ -155,7 +156,12 @@ func (c CustomHubSubstitutor) Substitute(image string) (string, error) {
}
}
- return fmt.Sprintf("%s/%s", c.hub, image), nil
+ result, err := url.JoinPath(c.hub, image)
+ if err != nil {
+ return "", err
+ }
+
+ return result, nil
}
// prependHubRegistry represents a way to prepend a custom Hub registry to the image name,
@@ -198,7 +204,12 @@ func (p prependHubRegistry) Substitute(image string) (string, error) {
}
}
- return fmt.Sprintf("%s/%s", p.prefix, image), nil
+ result, err := url.JoinPath(p.prefix, image)
+ if err != nil {
+ return "", err
+ }
+
+ return result, nil
}
// WithImageSubstitutors sets the image substitutors for a container
diff --git a/provider.go b/provider.go
index c563503036d..b5e5ffa997b 100644
--- a/provider.go
+++ b/provider.go
@@ -147,7 +147,7 @@ func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error
return &DockerProvider{
DockerProviderOptions: o,
- host: core.ExtractDockerHost(ctx),
+ host: core.MustExtractDockerHost(ctx),
client: c,
config: config.Read(),
}, nil
diff --git a/provider_test.go b/provider_test.go
index 2448d84c07f..097c83a02c8 100644
--- a/provider_test.go
+++ b/provider_test.go
@@ -8,7 +8,7 @@ import (
)
func TestProviderTypeGetProviderAutodetect(t *testing.T) {
- dockerHost := core.ExtractDockerHost(context.Background())
+ dockerHost := core.MustExtractDockerHost(context.Background())
const podmanSocket = "unix://$XDG_RUNTIME_DIR/podman/podman.sock"
tests := []struct {
diff --git a/reaper.go b/reaper.go
index 77b6cb79f4f..6b277127a55 100644
--- a/reaper.go
+++ b/reaper.go
@@ -354,7 +354,7 @@ func (r *reaperSpawner) fromContainer(ctx context.Context, sessionID string, pro
//
// Do not call this directly, use reuseOrCreateReaper instead.
func (r *reaperSpawner) newReaper(ctx context.Context, sessionID string, provider ReaperProvider) (reaper *Reaper, err error) { //nolint:nonamedreturns // Needed for deferred error check.
- dockerHostMount := core.ExtractDockerSocket(ctx)
+ dockerHostMount := core.MustExtractDockerSocket(ctx)
tcConfig := provider.Config().Config
req := ContainerRequest{
diff --git a/reaper_test.go b/reaper_test.go
index 0b53c1d525a..3dbfafa8ac5 100644
--- a/reaper_test.go
+++ b/reaper_test.go
@@ -73,7 +73,7 @@ func createContainerRequest(customize func(ContainerRequest) ContainerRequest) C
ExposedPorts: []string{"8080/tcp"},
Labels: core.DefaultLabels(testSessionID),
HostConfigModifier: func(hostConfig *container.HostConfig) {
- hostConfig.Binds = []string{core.ExtractDockerSocket(context.Background()) + ":/var/run/docker.sock"}
+ hostConfig.Binds = []string{core.MustExtractDockerSocket(context.Background()) + ":/var/run/docker.sock"}
},
WaitingFor: wait.ForListeningPort(nat.Port("8080/tcp")),
Env: map[string]string{
@@ -354,7 +354,7 @@ func Test_NewReaper(t *testing.T) {
name: "docker-host in context",
req: createContainerRequest(func(req ContainerRequest) ContainerRequest {
req.HostConfigModifier = func(hostConfig *container.HostConfig) {
- hostConfig.Binds = []string{core.ExtractDockerSocket(context.Background()) + ":/var/run/docker.sock"}
+ hostConfig.Binds = []string{core.MustExtractDockerSocket(context.Background()) + ":/var/run/docker.sock"}
}
return req
}),
diff --git a/scripts/bump-go.sh b/scripts/bump-go.sh
index 7b949ee89be..5ff33a5e6e2 100755
--- a/scripts/bump-go.sh
+++ b/scripts/bump-go.sh
@@ -21,7 +21,7 @@ readonly CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
readonly DRY_RUN="${DRY_RUN:-true}"
readonly ROOT_DIR="$(dirname "$CURRENT_DIR")"
readonly GO_MOD_FILE="${ROOT_DIR}/go.mod"
-readonly DEVCONTAINER_IMAGE_PREFIX="go:0-"
+readonly DEVCONTAINER_IMAGE_PREFIX="go:"
function main() {
echo "Updating Go version:"
diff --git a/sonar-project.properties b/sonar-project.properties
index d93e935f5fc..aaa203e905a 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -7,7 +7,7 @@ sonar.projectKey=testcontainers_testcontainers-go
sonar.projectName=testcontainers-go
-sonar.projectVersion=v0.32.0
+sonar.projectVersion=v0.33.0
sonar.sources=.
diff --git a/testcontainers.go b/testcontainers.go
index 5b52e09a22d..7ae4a40c14a 100644
--- a/testcontainers.go
+++ b/testcontainers.go
@@ -6,7 +6,12 @@ import (
"github.com/testcontainers/testcontainers-go/internal/core"
)
-// ExtractDockerSocket Extracts the docker socket from the different alternatives, removing the socket schema.
+// Deprecated: use MustExtractDockerHost instead.
+func ExtractDockerSocket() string {
+ return MustExtractDockerSocket(context.Background())
+}
+
+// MustExtractDockerSocket Extracts the docker socket from the different alternatives, removing the socket schema.
// Use this function to get the docker socket path, not the host (e.g. mounting the socket in a container).
// This function does not consider Windows containers at the moment.
// The possible alternatives are:
@@ -14,13 +19,13 @@ import (
// 1. Docker host from the "tc.host" property in the ~/.testcontainers.properties file.
// 2. The TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE environment variable.
// 3. Using a Docker client, check if the Info().OperativeSystem is "Docker Desktop" and return the default docker socket path for rootless docker.
-// 4. Else, Get the current Docker Host from the existing strategies: see ExtractDockerHost.
+// 4. Else, Get the current Docker Host from the existing strategies: see MustExtractDockerHost.
// 5. If the socket contains the unix schema, the schema is removed (e.g. unix:///var/run/docker.sock -> /var/run/docker.sock)
// 6. Else, the default location of the docker socket is used (/var/run/docker.sock)
//
-// In any case, if the docker socket schema is "tcp://", the default docker socket path will be returned.
-func ExtractDockerSocket() string {
- return core.ExtractDockerSocket(context.Background())
+// It panics if a Docker client cannot be created, or the Docker host cannot be discovered.
+func MustExtractDockerSocket(ctx context.Context) string {
+ return core.MustExtractDockerSocket(ctx)
}
// SessionID returns a unique session ID for the current test session. Because each Go package
diff --git a/testdata/auth.Dockerfile b/testdata/auth.Dockerfile
index 367e228a67f..5fcebe0baf6 100644
--- a/testdata/auth.Dockerfile
+++ b/testdata/auth.Dockerfile
@@ -1,3 +1,3 @@
-ARG REGISTRY_PORT=5001
+ARG REGISTRY_HOST=localhost:5001
-FROM localhost:${REGISTRY_PORT}/redis:5.0-alpine
+FROM ${REGISTRY_HOST}/redis:5.0-alpine
diff --git a/testdata/echoserver.go b/testdata/echoserver.go
index db5798699a7..a62c783f5d5 100644
--- a/testdata/echoserver.go
+++ b/testdata/echoserver.go
@@ -10,9 +10,8 @@ import (
func envHandler() http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
- _, _ = rw.Write([]byte(os.Getenv("FOO")))
-
rw.WriteHeader(http.StatusAccepted)
+ rw.Write([]byte(os.Getenv("FOO"))) //nolint:errcheck // Nothing we can usefully do with the error here.
}
}
@@ -21,9 +20,7 @@ func echoHandler(destination *os.File) http.HandlerFunc {
echo := req.URL.Query()["echo"][0]
l := log.New(destination, "echo ", 0)
-
l.Println(echo)
- _ = destination.Sync()
rw.WriteHeader(http.StatusAccepted)
}
@@ -39,10 +36,12 @@ func main() {
ln, err := net.Listen("tcp", ":8080")
if err != nil {
- panic(err)
+ log.Fatal(err)
}
fmt.Println("ready")
- _ = http.Serve(ln, mux)
+ if err := http.Serve(ln, mux); err != nil {
+ log.Fatal(err)
+ }
}
diff --git a/testhelpers_test.go b/testhelpers_test.go
index 47bbcb54c3b..3be3b7c50d2 100644
--- a/testhelpers_test.go
+++ b/testhelpers_test.go
@@ -20,7 +20,6 @@ func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr testcontain
return
}
tb.Cleanup(func() {
- tb.Log("terminating container")
require.NoError(tb, ctr.Terminate(ctx))
})
}
diff --git a/wait/exec_test.go b/wait/exec_test.go
index f6723a317c3..13e3e47511c 100644
--- a/wait/exec_test.go
+++ b/wait/exec_test.go
@@ -102,6 +102,10 @@ func (st mockExecTarget) State(_ context.Context) (*types.ContainerState, error)
return nil, errors.New("not implemented")
}
+func (st mockExecTarget) CopyFileFromContainer(_ context.Context, _ string) (io.ReadCloser, error) {
+ return nil, errors.New("not implemented")
+}
+
func TestExecStrategyWaitUntilReady(t *testing.T) {
target := mockExecTarget{}
wg := wait.NewExecStrategy([]string{"true"}).
@@ -209,5 +213,4 @@ func TestExecStrategyWaitUntilReady_CustomResponseMatcher(t *testing.T) {
t.Fatalf("failed to terminate container: %s", err)
}
})
- // }
}
diff --git a/wait/exit_test.go b/wait/exit_test.go
index 137c4f276b2..4795fd4ad64 100644
--- a/wait/exit_test.go
+++ b/wait/exit_test.go
@@ -2,6 +2,7 @@ package wait
import (
"context"
+ "errors"
"io"
"testing"
"time"
@@ -45,6 +46,10 @@ func (st exitStrategyTarget) State(ctx context.Context) (*types.ContainerState,
return &types.ContainerState{Running: st.isRunning}, nil
}
+func (st exitStrategyTarget) CopyFileFromContainer(context.Context, string) (io.ReadCloser, error) {
+ return nil, errors.New("not implemented")
+}
+
func TestWaitForExit(t *testing.T) {
target := exitStrategyTarget{
isRunning: false,
diff --git a/wait/file.go b/wait/file.go
new file mode 100644
index 00000000000..148907f3dbf
--- /dev/null
+++ b/wait/file.go
@@ -0,0 +1,112 @@
+package wait
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "time"
+
+ "github.com/docker/docker/errdefs"
+)
+
+var (
+ _ Strategy = (*FileStrategy)(nil)
+ _ StrategyTimeout = (*FileStrategy)(nil)
+)
+
+// FileStrategy waits for a file to exist in the container.
+type FileStrategy struct {
+ timeout *time.Duration
+ file string
+ pollInterval time.Duration
+ matcher func(io.Reader) error
+}
+
+// NewFileStrategy constructs an FileStrategy strategy.
+func NewFileStrategy(file string) *FileStrategy {
+ return &FileStrategy{
+ file: file,
+ pollInterval: defaultPollInterval(),
+ }
+}
+
+// WithStartupTimeout can be used to change the default startup timeout
+func (ws *FileStrategy) WithStartupTimeout(startupTimeout time.Duration) *FileStrategy {
+ ws.timeout = &startupTimeout
+ return ws
+}
+
+// WithPollInterval can be used to override the default polling interval of 100 milliseconds
+func (ws *FileStrategy) WithPollInterval(pollInterval time.Duration) *FileStrategy {
+ ws.pollInterval = pollInterval
+ return ws
+}
+
+// WithMatcher can be used to consume the file content.
+// The matcher can return an errdefs.ErrNotFound to indicate that the file is not ready.
+// Any other error will be considered a failure.
+// Default: nil, will only wait for the file to exist.
+func (ws *FileStrategy) WithMatcher(matcher func(io.Reader) error) *FileStrategy {
+ ws.matcher = matcher
+ return ws
+}
+
+// ForFile is a convenience method to assign FileStrategy
+func ForFile(file string) *FileStrategy {
+ return NewFileStrategy(file)
+}
+
+// Timeout returns the timeout for the strategy
+func (ws *FileStrategy) Timeout() *time.Duration {
+ return ws.timeout
+}
+
+// WaitUntilReady waits until the file exists in the container and copies it to the target.
+func (ws *FileStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error {
+ timeout := defaultStartupTimeout()
+ if ws.timeout != nil {
+ timeout = *ws.timeout
+ }
+
+ ctx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+
+ timer := time.NewTicker(ws.pollInterval)
+ defer timer.Stop()
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-timer.C:
+ if err := ws.matchFile(ctx, target); err != nil {
+ if errdefs.IsNotFound(err) {
+ // Not found, continue polling.
+ continue
+ }
+
+ return fmt.Errorf("copy from container: %w", err)
+ }
+ return nil
+ }
+ }
+}
+
+// matchFile tries to copy the file from the container and match it.
+func (ws *FileStrategy) matchFile(ctx context.Context, target StrategyTarget) error {
+ rc, err := target.CopyFileFromContainer(ctx, ws.file)
+ if err != nil {
+ return fmt.Errorf("copy from container: %w", err)
+ }
+ defer rc.Close() //nolint: errcheck // Read close error can't tell us anything useful.
+
+ if ws.matcher == nil {
+ // No matcher, just check if the file exists.
+ return nil
+ }
+
+ if err = ws.matcher(rc); err != nil {
+ return fmt.Errorf("matcher: %w", err)
+ }
+
+ return nil
+}
diff --git a/wait/file_test.go b/wait/file_test.go
new file mode 100644
index 00000000000..a25d8aa65ff
--- /dev/null
+++ b/wait/file_test.go
@@ -0,0 +1,104 @@
+package wait_test
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "testing"
+ "time"
+
+ "github.com/docker/docker/api/types"
+ "github.com/docker/docker/errdefs"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
+
+ "github.com/testcontainers/testcontainers-go"
+ "github.com/testcontainers/testcontainers-go/wait"
+)
+
+const testFilename = "/tmp/file"
+
+var anyContext = mock.AnythingOfType("*context.timerCtx")
+
+// newRunningTarget creates a new mockStrategyTarget that is running.
+func newRunningTarget() *mockStrategyTarget {
+ target := &mockStrategyTarget{}
+ target.EXPECT().State(anyContext).
+ Return(&types.ContainerState{Running: true}, nil)
+
+ return target
+}
+
+// testForFile creates a new FileStrategy for testing.
+func testForFile() *wait.FileStrategy {
+ return wait.ForFile(testFilename).
+ WithStartupTimeout(time.Millisecond * 50).
+ WithPollInterval(time.Millisecond)
+}
+
+func TestForFile(t *testing.T) {
+ errNotFound := errdefs.NotFound(errors.New("file not found"))
+ ctx := context.Background()
+
+ t.Run("not-found", func(t *testing.T) {
+ target := newRunningTarget()
+ target.EXPECT().CopyFileFromContainer(anyContext, testFilename).Return(nil, errNotFound)
+ err := testForFile().WaitUntilReady(ctx, target)
+ require.EqualError(t, err, context.DeadlineExceeded.Error())
+ })
+
+ t.Run("other-error", func(t *testing.T) {
+ otherErr := errors.New("other error")
+ target := newRunningTarget()
+ target.EXPECT().CopyFileFromContainer(anyContext, testFilename).Return(nil, otherErr)
+ err := testForFile().WaitUntilReady(ctx, target)
+ require.ErrorIs(t, err, otherErr)
+ })
+
+ t.Run("valid", func(t *testing.T) {
+ data := "my content\nwibble"
+ file := bytes.NewBufferString(data)
+ target := newRunningTarget()
+ target.EXPECT().CopyFileFromContainer(anyContext, testFilename).Once().Return(nil, errNotFound)
+ target.EXPECT().CopyFileFromContainer(anyContext, testFilename).Return(io.NopCloser(file), nil)
+ var out bytes.Buffer
+ err := testForFile().WithMatcher(func(r io.Reader) error {
+ if _, err := io.Copy(&out, r); err != nil {
+ return fmt.Errorf("copy: %w", err)
+ }
+ return nil
+ }).WaitUntilReady(ctx, target)
+ require.NoError(t, err)
+ require.Equal(t, data, out.String())
+ })
+}
+
+func TestFileStrategyWaitUntilReady_WithMatcher(t *testing.T) {
+ // waitForFileWithMatcher {
+ var out bytes.Buffer
+ dockerReq := testcontainers.ContainerRequest{
+ Image: "docker.io/nginx:latest",
+ WaitingFor: wait.ForFile("/etc/nginx/nginx.conf").
+ WithStartupTimeout(time.Second * 10).
+ WithPollInterval(time.Second).
+ WithMatcher(func(r io.Reader) error {
+ if _, err := io.Copy(&out, r); err != nil {
+ return fmt.Errorf("copy: %w", err)
+ }
+ return nil
+ }),
+ }
+ // }
+
+ ctx := context.Background()
+ container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: true})
+ if container != nil {
+ t.Cleanup(func() {
+ require.NoError(t, container.Terminate(context.Background()))
+ })
+ }
+ require.NoError(t, err)
+ require.Contains(t, out.String(), "worker_processes")
+}
diff --git a/wait/health_test.go b/wait/health_test.go
index 8a06ab07f81..4141988905d 100644
--- a/wait/health_test.go
+++ b/wait/health_test.go
@@ -2,6 +2,7 @@ package wait
import (
"context"
+ "errors"
"io"
"sync"
"testing"
@@ -60,6 +61,10 @@ func (st *healthStrategyTarget) setState(health *types.Health) {
st.state.Health = health
}
+func (st *healthStrategyTarget) CopyFileFromContainer(_ context.Context, _ string) (io.ReadCloser, error) {
+ return nil, errors.New("not implemented")
+}
+
// TestWaitForHealthTimesOutForUnhealthy confirms that an unhealthy container will eventually
// time out.
func TestWaitForHealthTimesOutForUnhealthy(t *testing.T) {
diff --git a/wait/log_test.go b/wait/log_test.go
index 78c5019ae81..7c767c0e250 100644
--- a/wait/log_test.go
+++ b/wait/log_test.go
@@ -8,8 +8,11 @@ import (
"time"
"github.com/docker/docker/api/types"
+ "github.com/stretchr/testify/require"
)
+const logTimeout = time.Second
+
const loremIpsum = `Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Donec a diam lectus.
@@ -26,11 +29,9 @@ func TestWaitForLog(t *testing.T) {
target := NopStrategyTarget{
ReaderCloser: io.NopCloser(bytes.NewReader([]byte("docker"))),
}
- wg := NewLogStrategy("docker").WithStartupTimeout(100 * time.Microsecond)
+ wg := NewLogStrategy("docker").WithStartupTimeout(100 * time.Millisecond)
err := wg.WaitUntilReady(context.Background(), target)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
})
t.Run("no regexp", func(t *testing.T) {
@@ -39,11 +40,9 @@ func TestWaitForLog(t *testing.T) {
}
// get all words that start with "ip", end with "m" and has a whitespace before the "ip"
- wg := NewLogStrategy(`\sip[\w]+m`).WithStartupTimeout(100 * time.Microsecond).AsRegexp()
+ wg := NewLogStrategy(`\sip[\w]+m`).WithStartupTimeout(100 * time.Millisecond).AsRegexp()
err := wg.WaitUntilReady(context.Background(), target)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
})
}
@@ -53,12 +52,10 @@ func TestWaitWithExactNumberOfOccurrences(t *testing.T) {
ReaderCloser: io.NopCloser(bytes.NewReader([]byte("kubernetes\r\ndocker\n\rdocker"))),
}
wg := NewLogStrategy("docker").
- WithStartupTimeout(100 * time.Microsecond).
+ WithStartupTimeout(100 * time.Millisecond).
WithOccurrence(2)
err := wg.WaitUntilReady(context.Background(), target)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
})
t.Run("as regexp", func(t *testing.T) {
@@ -69,11 +66,9 @@ func TestWaitWithExactNumberOfOccurrences(t *testing.T) {
// get texts from "ip" to the next "m".
// there are three occurrences of this pattern in the string:
// one "ipsum mauris" and two "ipsum dolor sit am"
- wg := NewLogStrategy(`ip(.*)m`).WithStartupTimeout(100 * time.Microsecond).AsRegexp().WithOccurrence(3)
+ wg := NewLogStrategy(`ip(.*)m`).WithStartupTimeout(100 * time.Millisecond).AsRegexp().WithOccurrence(3)
err := wg.WaitUntilReady(context.Background(), target)
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
})
}
@@ -83,12 +78,10 @@ func TestWaitWithExactNumberOfOccurrencesButItWillNeverHappen(t *testing.T) {
ReaderCloser: io.NopCloser(bytes.NewReader([]byte("kubernetes\r\ndocker"))),
}
wg := NewLogStrategy("containerd").
- WithStartupTimeout(100 * time.Microsecond).
+ WithStartupTimeout(logTimeout).
WithOccurrence(2)
err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("expected error")
- }
+ require.Error(t, err)
})
t.Run("as regexp", func(t *testing.T) {
@@ -98,11 +91,9 @@ func TestWaitWithExactNumberOfOccurrencesButItWillNeverHappen(t *testing.T) {
// get texts from "ip" to the next "m".
// there are only three occurrences matching
- wg := NewLogStrategy(`do(.*)ck.+`).WithStartupTimeout(100 * time.Microsecond).AsRegexp().WithOccurrence(4)
+ wg := NewLogStrategy(`do(.*)ck.+`).WithStartupTimeout(100 * time.Millisecond).AsRegexp().WithOccurrence(4)
err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("expected error")
- }
+ require.Error(t, err)
})
}
@@ -112,12 +103,10 @@ func TestWaitShouldFailWithExactNumberOfOccurrences(t *testing.T) {
ReaderCloser: io.NopCloser(bytes.NewReader([]byte("kubernetes\r\ndocker"))),
}
wg := NewLogStrategy("docker").
- WithStartupTimeout(100 * time.Microsecond).
+ WithStartupTimeout(logTimeout).
WithOccurrence(2)
err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("expected error")
- }
+ require.Error(t, err)
})
t.Run("as regexp", func(t *testing.T) {
@@ -127,11 +116,9 @@ func TestWaitShouldFailWithExactNumberOfOccurrences(t *testing.T) {
// get "Maecenas".
// there are only one occurrence matching
- wg := NewLogStrategy(`^Mae[\w]?enas\s`).WithStartupTimeout(100 * time.Microsecond).AsRegexp().WithOccurrence(2)
+ wg := NewLogStrategy(`^Mae[\w]?enas\s`).WithStartupTimeout(100 * time.Millisecond).AsRegexp().WithOccurrence(2)
err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("expected error")
- }
+ require.Error(t, err)
})
}
@@ -148,37 +135,19 @@ func TestWaitForLogFailsDueToOOMKilledContainer(t *testing.T) {
}
t.Run("no regexp", func(t *testing.T) {
- wg := ForLog("docker").
- WithStartupTimeout(100 * time.Microsecond)
+ wg := ForLog("docker").WithStartupTimeout(logTimeout)
- {
- err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("no error")
- }
-
- expected := "container crashed with out-of-memory (OOMKilled)"
- if err.Error() != expected {
- t.Fatalf("expected %q, got %q", expected, err.Error())
- }
- }
+ err := wg.WaitUntilReady(context.Background(), target)
+ expected := "container crashed with out-of-memory (OOMKilled)"
+ require.EqualError(t, err, expected)
})
t.Run("as regexp", func(t *testing.T) {
- wg := ForLog("docker").
- WithStartupTimeout(100 * time.Microsecond).AsRegexp()
-
- {
- err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("no error")
- }
+ wg := ForLog("docker").WithStartupTimeout(logTimeout).AsRegexp()
- expected := "container crashed with out-of-memory (OOMKilled)"
- if err.Error() != expected {
- t.Fatalf("expected %q, got %q", expected, err.Error())
- }
- }
+ err := wg.WaitUntilReady(context.Background(), target)
+ expected := "container crashed with out-of-memory (OOMKilled)"
+ require.EqualError(t, err, expected)
})
}
@@ -196,37 +165,19 @@ func TestWaitForLogFailsDueToExitedContainer(t *testing.T) {
}
t.Run("no regexp", func(t *testing.T) {
- wg := ForLog("docker").
- WithStartupTimeout(100 * time.Microsecond)
-
- {
- err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("no error")
- }
+ wg := ForLog("docker").WithStartupTimeout(logTimeout)
- expected := "container exited with code 1"
- if err.Error() != expected {
- t.Fatalf("expected %q, got %q", expected, err.Error())
- }
- }
+ err := wg.WaitUntilReady(context.Background(), target)
+ expected := "container exited with code 1"
+ require.EqualError(t, err, expected)
})
t.Run("as regexp", func(t *testing.T) {
- wg := ForLog("docker").
- WithStartupTimeout(100 * time.Microsecond).AsRegexp()
+ wg := ForLog("docker").WithStartupTimeout(logTimeout).AsRegexp()
- {
- err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("no error")
- }
-
- expected := "container exited with code 1"
- if err.Error() != expected {
- t.Fatalf("expected %q, got %q", expected, err.Error())
- }
- }
+ err := wg.WaitUntilReady(context.Background(), target)
+ expected := "container exited with code 1"
+ require.EqualError(t, err, expected)
})
}
@@ -243,36 +194,18 @@ func TestWaitForLogFailsDueToUnexpectedContainerStatus(t *testing.T) {
}
t.Run("no regexp", func(t *testing.T) {
- wg := ForLog("docker").
- WithStartupTimeout(100 * time.Microsecond)
+ wg := ForLog("docker").WithStartupTimeout(logTimeout)
- {
- err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("no error")
- }
-
- expected := "unexpected container status \"dead\""
- if err.Error() != expected {
- t.Fatalf("expected %q, got %q", expected, err.Error())
- }
- }
+ err := wg.WaitUntilReady(context.Background(), target)
+ expected := "unexpected container status \"dead\""
+ require.EqualError(t, err, expected)
})
t.Run("as regexp", func(t *testing.T) {
- wg := ForLog("docker").
- WithStartupTimeout(100 * time.Microsecond).AsRegexp()
+ wg := ForLog("docker").WithStartupTimeout(logTimeout).AsRegexp()
- {
- err := wg.WaitUntilReady(context.Background(), target)
- if err == nil {
- t.Fatal("no error")
- }
-
- expected := "unexpected container status \"dead\""
- if err.Error() != expected {
- t.Fatalf("expected %q, got %q", expected, err.Error())
- }
- }
+ err := wg.WaitUntilReady(context.Background(), target)
+ expected := "unexpected container status \"dead\""
+ require.EqualError(t, err, expected)
})
}
diff --git a/wait/nop.go b/wait/nop.go
index 7b8e918abda..4206eefc1e9 100644
--- a/wait/nop.go
+++ b/wait/nop.go
@@ -75,3 +75,7 @@ func (st NopStrategyTarget) Exec(_ context.Context, _ []string, _ ...exec.Proces
func (st NopStrategyTarget) State(_ context.Context) (*types.ContainerState, error) {
return &st.ContainerState, nil
}
+
+func (st NopStrategyTarget) CopyFileFromContainer(context.Context, string) (io.ReadCloser, error) {
+ return st.ReaderCloser, nil
+}
diff --git a/wait/strategytarget_mock_test.go b/wait/strategytarget_mock_test.go
new file mode 100644
index 00000000000..525ff08e1d3
--- /dev/null
+++ b/wait/strategytarget_mock_test.go
@@ -0,0 +1,528 @@
+// Code generated by mockery. DO NOT EDIT.
+
+package wait_test
+
+import (
+ context "context"
+ io "io"
+
+ exec "github.com/testcontainers/testcontainers-go/exec"
+
+ mock "github.com/stretchr/testify/mock"
+
+ nat "github.com/docker/go-connections/nat"
+
+ types "github.com/docker/docker/api/types"
+)
+
+// mockStrategyTarget is an autogenerated mock type for the StrategyTarget type
+type mockStrategyTarget struct {
+ mock.Mock
+}
+
+type mockStrategyTarget_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *mockStrategyTarget) EXPECT() *mockStrategyTarget_Expecter {
+ return &mockStrategyTarget_Expecter{mock: &_m.Mock}
+}
+
+// CopyFileFromContainer provides a mock function with given fields: ctx, filePath
+func (_m *mockStrategyTarget) CopyFileFromContainer(ctx context.Context, filePath string) (io.ReadCloser, error) {
+ ret := _m.Called(ctx, filePath)
+
+ if len(ret) == 0 {
+ panic("no return value specified for CopyFileFromContainer")
+ }
+
+ var r0 io.ReadCloser
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, string) (io.ReadCloser, error)); ok {
+ return rf(ctx, filePath)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, string) io.ReadCloser); ok {
+ r0 = rf(ctx, filePath)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(io.ReadCloser)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
+ r1 = rf(ctx, filePath)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// mockStrategyTarget_CopyFileFromContainer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CopyFileFromContainer'
+type mockStrategyTarget_CopyFileFromContainer_Call struct {
+ *mock.Call
+}
+
+// CopyFileFromContainer is a helper method to define mock.On call
+// - ctx context.Context
+// - filePath string
+func (_e *mockStrategyTarget_Expecter) CopyFileFromContainer(ctx interface{}, filePath interface{}) *mockStrategyTarget_CopyFileFromContainer_Call {
+ return &mockStrategyTarget_CopyFileFromContainer_Call{Call: _e.mock.On("CopyFileFromContainer", ctx, filePath)}
+}
+
+func (_c *mockStrategyTarget_CopyFileFromContainer_Call) Run(run func(ctx context.Context, filePath string)) *mockStrategyTarget_CopyFileFromContainer_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(string))
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_CopyFileFromContainer_Call) Return(_a0 io.ReadCloser, _a1 error) *mockStrategyTarget_CopyFileFromContainer_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *mockStrategyTarget_CopyFileFromContainer_Call) RunAndReturn(run func(context.Context, string) (io.ReadCloser, error)) *mockStrategyTarget_CopyFileFromContainer_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Exec provides a mock function with given fields: _a0, _a1, _a2
+func (_m *mockStrategyTarget) Exec(_a0 context.Context, _a1 []string, _a2 ...exec.ProcessOption) (int, io.Reader, error) {
+ _va := make([]interface{}, len(_a2))
+ for _i := range _a2 {
+ _va[_i] = _a2[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _a0, _a1)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Exec")
+ }
+
+ var r0 int
+ var r1 io.Reader
+ var r2 error
+ if rf, ok := ret.Get(0).(func(context.Context, []string, ...exec.ProcessOption) (int, io.Reader, error)); ok {
+ return rf(_a0, _a1, _a2...)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, []string, ...exec.ProcessOption) int); ok {
+ r0 = rf(_a0, _a1, _a2...)
+ } else {
+ r0 = ret.Get(0).(int)
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, []string, ...exec.ProcessOption) io.Reader); ok {
+ r1 = rf(_a0, _a1, _a2...)
+ } else {
+ if ret.Get(1) != nil {
+ r1 = ret.Get(1).(io.Reader)
+ }
+ }
+
+ if rf, ok := ret.Get(2).(func(context.Context, []string, ...exec.ProcessOption) error); ok {
+ r2 = rf(_a0, _a1, _a2...)
+ } else {
+ r2 = ret.Error(2)
+ }
+
+ return r0, r1, r2
+}
+
+// mockStrategyTarget_Exec_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Exec'
+type mockStrategyTarget_Exec_Call struct {
+ *mock.Call
+}
+
+// Exec is a helper method to define mock.On call
+// - _a0 context.Context
+// - _a1 []string
+// - _a2 ...exec.ProcessOption
+func (_e *mockStrategyTarget_Expecter) Exec(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *mockStrategyTarget_Exec_Call {
+ return &mockStrategyTarget_Exec_Call{Call: _e.mock.On("Exec",
+ append([]interface{}{_a0, _a1}, _a2...)...)}
+}
+
+func (_c *mockStrategyTarget_Exec_Call) Run(run func(_a0 context.Context, _a1 []string, _a2 ...exec.ProcessOption)) *mockStrategyTarget_Exec_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ variadicArgs := make([]exec.ProcessOption, len(args)-2)
+ for i, a := range args[2:] {
+ if a != nil {
+ variadicArgs[i] = a.(exec.ProcessOption)
+ }
+ }
+ run(args[0].(context.Context), args[1].([]string), variadicArgs...)
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_Exec_Call) Return(_a0 int, _a1 io.Reader, _a2 error) *mockStrategyTarget_Exec_Call {
+ _c.Call.Return(_a0, _a1, _a2)
+ return _c
+}
+
+func (_c *mockStrategyTarget_Exec_Call) RunAndReturn(run func(context.Context, []string, ...exec.ProcessOption) (int, io.Reader, error)) *mockStrategyTarget_Exec_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Host provides a mock function with given fields: _a0
+func (_m *mockStrategyTarget) Host(_a0 context.Context) (string, error) {
+ ret := _m.Called(_a0)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Host")
+ }
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok {
+ return rf(_a0)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) string); ok {
+ r0 = rf(_a0)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+ r1 = rf(_a0)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// mockStrategyTarget_Host_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Host'
+type mockStrategyTarget_Host_Call struct {
+ *mock.Call
+}
+
+// Host is a helper method to define mock.On call
+// - _a0 context.Context
+func (_e *mockStrategyTarget_Expecter) Host(_a0 interface{}) *mockStrategyTarget_Host_Call {
+ return &mockStrategyTarget_Host_Call{Call: _e.mock.On("Host", _a0)}
+}
+
+func (_c *mockStrategyTarget_Host_Call) Run(run func(_a0 context.Context)) *mockStrategyTarget_Host_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_Host_Call) Return(_a0 string, _a1 error) *mockStrategyTarget_Host_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *mockStrategyTarget_Host_Call) RunAndReturn(run func(context.Context) (string, error)) *mockStrategyTarget_Host_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Inspect provides a mock function with given fields: _a0
+func (_m *mockStrategyTarget) Inspect(_a0 context.Context) (*types.ContainerJSON, error) {
+ ret := _m.Called(_a0)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Inspect")
+ }
+
+ var r0 *types.ContainerJSON
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context) (*types.ContainerJSON, error)); ok {
+ return rf(_a0)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) *types.ContainerJSON); ok {
+ r0 = rf(_a0)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*types.ContainerJSON)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+ r1 = rf(_a0)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// mockStrategyTarget_Inspect_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Inspect'
+type mockStrategyTarget_Inspect_Call struct {
+ *mock.Call
+}
+
+// Inspect is a helper method to define mock.On call
+// - _a0 context.Context
+func (_e *mockStrategyTarget_Expecter) Inspect(_a0 interface{}) *mockStrategyTarget_Inspect_Call {
+ return &mockStrategyTarget_Inspect_Call{Call: _e.mock.On("Inspect", _a0)}
+}
+
+func (_c *mockStrategyTarget_Inspect_Call) Run(run func(_a0 context.Context)) *mockStrategyTarget_Inspect_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_Inspect_Call) Return(_a0 *types.ContainerJSON, _a1 error) *mockStrategyTarget_Inspect_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *mockStrategyTarget_Inspect_Call) RunAndReturn(run func(context.Context) (*types.ContainerJSON, error)) *mockStrategyTarget_Inspect_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Logs provides a mock function with given fields: _a0
+func (_m *mockStrategyTarget) Logs(_a0 context.Context) (io.ReadCloser, error) {
+ ret := _m.Called(_a0)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Logs")
+ }
+
+ var r0 io.ReadCloser
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context) (io.ReadCloser, error)); ok {
+ return rf(_a0)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) io.ReadCloser); ok {
+ r0 = rf(_a0)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(io.ReadCloser)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+ r1 = rf(_a0)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// mockStrategyTarget_Logs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Logs'
+type mockStrategyTarget_Logs_Call struct {
+ *mock.Call
+}
+
+// Logs is a helper method to define mock.On call
+// - _a0 context.Context
+func (_e *mockStrategyTarget_Expecter) Logs(_a0 interface{}) *mockStrategyTarget_Logs_Call {
+ return &mockStrategyTarget_Logs_Call{Call: _e.mock.On("Logs", _a0)}
+}
+
+func (_c *mockStrategyTarget_Logs_Call) Run(run func(_a0 context.Context)) *mockStrategyTarget_Logs_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_Logs_Call) Return(_a0 io.ReadCloser, _a1 error) *mockStrategyTarget_Logs_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *mockStrategyTarget_Logs_Call) RunAndReturn(run func(context.Context) (io.ReadCloser, error)) *mockStrategyTarget_Logs_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// MappedPort provides a mock function with given fields: _a0, _a1
+func (_m *mockStrategyTarget) MappedPort(_a0 context.Context, _a1 nat.Port) (nat.Port, error) {
+ ret := _m.Called(_a0, _a1)
+
+ if len(ret) == 0 {
+ panic("no return value specified for MappedPort")
+ }
+
+ var r0 nat.Port
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, nat.Port) (nat.Port, error)); ok {
+ return rf(_a0, _a1)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, nat.Port) nat.Port); ok {
+ r0 = rf(_a0, _a1)
+ } else {
+ r0 = ret.Get(0).(nat.Port)
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, nat.Port) error); ok {
+ r1 = rf(_a0, _a1)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// mockStrategyTarget_MappedPort_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MappedPort'
+type mockStrategyTarget_MappedPort_Call struct {
+ *mock.Call
+}
+
+// MappedPort is a helper method to define mock.On call
+// - _a0 context.Context
+// - _a1 nat.Port
+func (_e *mockStrategyTarget_Expecter) MappedPort(_a0 interface{}, _a1 interface{}) *mockStrategyTarget_MappedPort_Call {
+ return &mockStrategyTarget_MappedPort_Call{Call: _e.mock.On("MappedPort", _a0, _a1)}
+}
+
+func (_c *mockStrategyTarget_MappedPort_Call) Run(run func(_a0 context.Context, _a1 nat.Port)) *mockStrategyTarget_MappedPort_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(nat.Port))
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_MappedPort_Call) Return(_a0 nat.Port, _a1 error) *mockStrategyTarget_MappedPort_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *mockStrategyTarget_MappedPort_Call) RunAndReturn(run func(context.Context, nat.Port) (nat.Port, error)) *mockStrategyTarget_MappedPort_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// Ports provides a mock function with given fields: ctx
+func (_m *mockStrategyTarget) Ports(ctx context.Context) (nat.PortMap, error) {
+ ret := _m.Called(ctx)
+
+ if len(ret) == 0 {
+ panic("no return value specified for Ports")
+ }
+
+ var r0 nat.PortMap
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context) (nat.PortMap, error)); ok {
+ return rf(ctx)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) nat.PortMap); ok {
+ r0 = rf(ctx)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(nat.PortMap)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+ r1 = rf(ctx)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// mockStrategyTarget_Ports_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Ports'
+type mockStrategyTarget_Ports_Call struct {
+ *mock.Call
+}
+
+// Ports is a helper method to define mock.On call
+// - ctx context.Context
+func (_e *mockStrategyTarget_Expecter) Ports(ctx interface{}) *mockStrategyTarget_Ports_Call {
+ return &mockStrategyTarget_Ports_Call{Call: _e.mock.On("Ports", ctx)}
+}
+
+func (_c *mockStrategyTarget_Ports_Call) Run(run func(ctx context.Context)) *mockStrategyTarget_Ports_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_Ports_Call) Return(_a0 nat.PortMap, _a1 error) *mockStrategyTarget_Ports_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *mockStrategyTarget_Ports_Call) RunAndReturn(run func(context.Context) (nat.PortMap, error)) *mockStrategyTarget_Ports_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// State provides a mock function with given fields: _a0
+func (_m *mockStrategyTarget) State(_a0 context.Context) (*types.ContainerState, error) {
+ ret := _m.Called(_a0)
+
+ if len(ret) == 0 {
+ panic("no return value specified for State")
+ }
+
+ var r0 *types.ContainerState
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context) (*types.ContainerState, error)); ok {
+ return rf(_a0)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) *types.ContainerState); ok {
+ r0 = rf(_a0)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*types.ContainerState)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+ r1 = rf(_a0)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// mockStrategyTarget_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State'
+type mockStrategyTarget_State_Call struct {
+ *mock.Call
+}
+
+// State is a helper method to define mock.On call
+// - _a0 context.Context
+func (_e *mockStrategyTarget_Expecter) State(_a0 interface{}) *mockStrategyTarget_State_Call {
+ return &mockStrategyTarget_State_Call{Call: _e.mock.On("State", _a0)}
+}
+
+func (_c *mockStrategyTarget_State_Call) Run(run func(_a0 context.Context)) *mockStrategyTarget_State_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *mockStrategyTarget_State_Call) Return(_a0 *types.ContainerState, _a1 error) *mockStrategyTarget_State_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *mockStrategyTarget_State_Call) RunAndReturn(run func(context.Context) (*types.ContainerState, error)) *mockStrategyTarget_State_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// newMockStrategyTarget creates a new instance of mockStrategyTarget. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func newMockStrategyTarget(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *mockStrategyTarget {
+ mock := &mockStrategyTarget{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/wait/testdata/go.mod b/wait/testdata/go.mod
index 0b51175838e..ab1b280416a 100644
--- a/wait/testdata/go.mod
+++ b/wait/testdata/go.mod
@@ -1,3 +1,3 @@
module httptest
-go 1.21
+go 1.22
diff --git a/wait/testdata/main.go b/wait/testdata/main.go
index b18008db75a..f6f965fe6bb 100644
--- a/wait/testdata/main.go
+++ b/wait/testdata/main.go
@@ -5,6 +5,7 @@ import (
"context"
"encoding/base64"
"errors"
+ "fmt"
"io"
"log"
"net/http"
@@ -15,7 +16,7 @@ import (
"time"
)
-func main() {
+func run() error {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
@@ -93,5 +94,15 @@ func main() {
log.Println("stopping...")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
- _ = server.Shutdown(ctx)
+ if err := server.Shutdown(ctx); err != nil {
+ return fmt.Errorf("shutdown: %w", err)
+ }
+
+ return nil
+}
+
+func main() {
+ if err := run(); err != nil {
+ log.Fatal(err)
+ }
}
diff --git a/wait/wait.go b/wait/wait.go
index ccfd4735e24..7211d49b2df 100644
--- a/wait/wait.go
+++ b/wait/wait.go
@@ -31,6 +31,7 @@ type StrategyTarget interface {
Logs(context.Context) (io.ReadCloser, error)
Exec(context.Context, []string, ...exec.ProcessOption) (int, io.Reader, error)
State(context.Context) (*types.ContainerState, error)
+ CopyFileFromContainer(ctx context.Context, filePath string) (io.ReadCloser, error)
}
func checkTarget(ctx context.Context, target StrategyTarget) error {
diff --git a/wait/wait_test.go b/wait/wait_test.go
index 079e64c2b34..b8c62487030 100644
--- a/wait/wait_test.go
+++ b/wait/wait_test.go
@@ -14,13 +14,14 @@ import (
var ErrPortNotFound = errors.New("port not found")
type MockStrategyTarget struct {
- HostImpl func(context.Context) (string, error)
- InspectImpl func(context.Context) (*types.ContainerJSON, error)
- PortsImpl func(context.Context) (nat.PortMap, error)
- MappedPortImpl func(context.Context, nat.Port) (nat.Port, error)
- LogsImpl func(context.Context) (io.ReadCloser, error)
- ExecImpl func(context.Context, []string, ...tcexec.ProcessOption) (int, io.Reader, error)
- StateImpl func(context.Context) (*types.ContainerState, error)
+ HostImpl func(context.Context) (string, error)
+ InspectImpl func(context.Context) (*types.ContainerJSON, error)
+ PortsImpl func(context.Context) (nat.PortMap, error)
+ MappedPortImpl func(context.Context, nat.Port) (nat.Port, error)
+ LogsImpl func(context.Context) (io.ReadCloser, error)
+ ExecImpl func(context.Context, []string, ...tcexec.ProcessOption) (int, io.Reader, error)
+ StateImpl func(context.Context) (*types.ContainerState, error)
+ CopyFileFromContainerImpl func(context.Context, string) (io.ReadCloser, error)
}
func (st MockStrategyTarget) Host(ctx context.Context) (string, error) {
@@ -56,3 +57,7 @@ func (st MockStrategyTarget) Exec(ctx context.Context, cmd []string, options ...
func (st MockStrategyTarget) State(ctx context.Context) (*types.ContainerState, error) {
return st.StateImpl(ctx)
}
+
+func (st MockStrategyTarget) CopyFileFromContainer(ctx context.Context, filePath string) (io.ReadCloser, error) {
+ return st.CopyFileFromContainerImpl(ctx, filePath)
+}