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) +}