summaryrefslogtreecommitdiff
path: root/vendor/github.com/testcontainers
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/testcontainers')
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/CONTRIBUTING.md2
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/Pipfile2
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/Pipfile.lock60
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/RELEASING.md18
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/container.go27
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/docker.go139
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/exec/processor.go1
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/generic.go5
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/internal/config/config.go2
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/internal/core/labels.go5
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/internal/version.go2
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/lifecycle.go79
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/mkdocs.yml29
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/options.go112
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/parallel.go7
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/port_forwarding.go11
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/reaper.go4
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/requirements.txt2
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/testing.go12
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/wait/file.go2
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/wait/host_port.go94
-rw-r--r--vendor/github.com/testcontainers/testcontainers-go/wait/walk.go3
22 files changed, 351 insertions, 267 deletions
diff --git a/vendor/github.com/testcontainers/testcontainers-go/CONTRIBUTING.md b/vendor/github.com/testcontainers/testcontainers-go/CONTRIBUTING.md
index c8194c2..4736297 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/CONTRIBUTING.md
+++ b/vendor/github.com/testcontainers/testcontainers-go/CONTRIBUTING.md
@@ -2,7 +2,7 @@
Please see the [main contributing guidelines](./docs/contributing.md).
-There are additional docs describing [contributing documentation changes](./docs/contributing_docs.md).
+There are additional docs describing [contributing documentation changes](./docs/contributing.md).
### GitHub Sponsorship
diff --git a/vendor/github.com/testcontainers/testcontainers-go/Pipfile b/vendor/github.com/testcontainers/testcontainers-go/Pipfile
index f35e8eb..58e8ace 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/Pipfile
+++ b/vendor/github.com/testcontainers/testcontainers-go/Pipfile
@@ -8,7 +8,7 @@ verify_ssl = true
[packages]
mkdocs = "==1.5.3"
mkdocs-codeinclude-plugin = "==0.2.1"
-mkdocs-include-markdown-plugin = "==7.1.5"
+mkdocs-include-markdown-plugin = "==7.1.6"
mkdocs-material = "==9.5.18"
mkdocs-markdownextradata-plugin = "==0.2.6"
diff --git a/vendor/github.com/testcontainers/testcontainers-go/Pipfile.lock b/vendor/github.com/testcontainers/testcontainers-go/Pipfile.lock
index e3b2e97..a8a6ab2 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/Pipfile.lock
+++ b/vendor/github.com/testcontainers/testcontainers-go/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "4a9599a9c2db79998493adaaa691752f995ed409e15840df5aae155c83963d51"
+ "sha256": "160280c0c2d9beaa296833b5f5e754123020e5a15854a5ee6201df9cf5761554"
},
"pipfile-spec": 6,
"requires": {
@@ -26,11 +26,11 @@
},
"bracex": {
"hashes": [
- "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6",
- "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6"
+ "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952",
+ "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7"
],
- "markers": "python_version >= '3.8'",
- "version": "==2.5.post1"
+ "markers": "python_version >= '3.9'",
+ "version": "==2.6"
},
"certifi": {
"hashes": [
@@ -139,11 +139,11 @@
},
"click": {
"hashes": [
- "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2",
- "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"
+ "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202",
+ "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"
],
- "markers": "python_version >= '3.7'",
- "version": "==8.1.8"
+ "markers": "python_version >= '3.10'",
+ "version": "==8.2.1"
},
"colorama": {
"hashes": [
@@ -186,11 +186,11 @@
},
"markdown": {
"hashes": [
- "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc",
- "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"
+ "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45",
+ "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"
],
"markers": "python_version >= '3.9'",
- "version": "==3.8"
+ "version": "==3.8.2"
},
"markupsafe": {
"hashes": [
@@ -287,12 +287,12 @@
},
"mkdocs-include-markdown-plugin": {
"hashes": [
- "sha256:a986967594da6789226798e3c41c70bc17130fadb92b4313f42bd3defdac0adc",
- "sha256:d0b96edee45e7fda5eb189e63331cfaf1bf1fbdbebbd08371f1daa77045d3ae9"
+ "sha256:7975a593514887c18ecb68e11e35c074c5499cfa3e51b18cd16323862e1f7345",
+ "sha256:a0753cb82704c10a287f1e789fc9848f82b6beb8749814b24b03dd9f67816677"
],
"index": "pypi",
"markers": "python_version >= '3.9'",
- "version": "==7.1.5"
+ "version": "==7.1.6"
},
"mkdocs-markdownextradata-plugin": {
"hashes": [
@@ -344,11 +344,11 @@
},
"platformdirs": {
"hashes": [
- "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94",
- "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"
+ "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc",
+ "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"
],
"markers": "python_version >= '3.9'",
- "version": "==4.3.7"
+ "version": "==4.3.8"
},
"pygments": {
"hashes": [
@@ -443,11 +443,11 @@
},
"pyyaml-env-tag": {
"hashes": [
- "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb",
- "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"
+ "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04",
+ "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"
],
- "markers": "python_version >= '3.6'",
- "version": "==0.1"
+ "markers": "python_version >= '3.9'",
+ "version": "==1.1"
},
"regex": {
"hashes": [
@@ -553,12 +553,12 @@
},
"urllib3": {
"hashes": [
- "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472",
- "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"
+ "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760",
+ "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"
],
"index": "pypi",
- "markers": "python_version >= '3.8'",
- "version": "==2.2.2"
+ "markers": "python_version >= '3.9'",
+ "version": "==2.5.0"
},
"watchdog": {
"hashes": [
@@ -598,11 +598,11 @@
},
"wcmatch": {
"hashes": [
- "sha256:0dd927072d03c0a6527a20d2e6ad5ba8d0380e60870c383bc533b71744df7b7a",
- "sha256:e72f0de09bba6a04e0de70937b0cf06e55f36f37b3deb422dfaf854b867b840a"
+ "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a",
+ "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af"
],
- "markers": "python_version >= '3.8'",
- "version": "==10.0"
+ "markers": "python_version >= '3.9'",
+ "version": "==10.1"
},
"zipp": {
"hashes": [
diff --git a/vendor/github.com/testcontainers/testcontainers-go/RELEASING.md b/vendor/github.com/testcontainers/testcontainers-go/RELEASING.md
index 31a9954..a35e243 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/RELEASING.md
+++ b/vendor/github.com/testcontainers/testcontainers-go/RELEASING.md
@@ -93,23 +93,23 @@ go mod tidy
go mod tidy
go mod tidy
go mod tidy
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" couchbase.md > couchbase.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" couchbase.md > couchbase.md.tmp
mv couchbase.md.tmp couchbase.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" localstack.md > localstack.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" localstack.md > localstack.md.tmp
mv localstack.md.tmp localstack.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" mysql.md > mysql.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" mysql.md > mysql.md.tmp
mv mysql.md.tmp mysql.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" neo4j.md > neo4j.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" neo4j.md > neo4j.md.tmp
mv neo4j.md.tmp neo4j.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" postgres.md > postgres.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" postgres.md > postgres.md.tmp
mv postgres.md.tmp postgres.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" pulsar.md > pulsar.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" pulsar.md > pulsar.md.tmp
mv pulsar.md.tmp pulsar.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" redis.md > redis.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" redis.md > redis.md.tmp
mv redis.md.tmp redis.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" redpanda.md > redpanda.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" redpanda.md > redpanda.md.tmp
mv redpanda.md.tmp redpanda.md
-sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" vault.md > vault.md.tmp
+sed "s/Not available until the next release <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" vault.md > vault.md.tmp
mv vault.md.tmp vault.md
```
diff --git a/vendor/github.com/testcontainers/testcontainers-go/container.go b/vendor/github.com/testcontainers/testcontainers-go/container.go
index 1977632..b0f2273 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/container.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/container.go
@@ -6,19 +6,20 @@ import (
"errors"
"fmt"
"io"
+ "maps"
"os"
"path/filepath"
"strings"
"time"
"github.com/cpuguy83/dockercfg"
- "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
- "github.com/docker/docker/pkg/archive"
"github.com/docker/go-connections/nat"
"github.com/google/uuid"
+ "github.com/moby/go-archive"
"github.com/moby/patternmatcher/ignorefile"
tcexec "github.com/testcontainers/testcontainers-go/exec"
@@ -73,7 +74,7 @@ type Container interface {
// ImageBuildInfo defines what is needed to build an image
type ImageBuildInfo interface {
- BuildOptions() (types.ImageBuildOptions, error) // converts the ImageBuildInfo to a types.ImageBuildOptions
+ BuildOptions() (build.ImageBuildOptions, error) // converts the ImageBuildInfo to a build.ImageBuildOptions
GetContext() (io.Reader, error) // the path to the build context
GetDockerfile() string // the relative path to the Dockerfile, including the file itself
GetRepo() string // get repo label for image
@@ -103,7 +104,7 @@ type FromDockerfile struct {
// BuildOptionsModifier Modifier for the build options before image build. Use it for
// advanced configurations while building the image. Please consider that the modifier
// is called after the default build options are set.
- BuildOptionsModifier func(*types.ImageBuildOptions)
+ BuildOptionsModifier func(*build.ImageBuildOptions)
}
type ContainerFile struct {
@@ -433,8 +434,8 @@ func (c *ContainerRequest) BuildLogWriter() io.Writer {
// BuildOptions returns the image build options when building a Docker image from a Dockerfile.
// It will apply some defaults and finally call the BuildOptionsModifier from the FromDockerfile struct,
// if set.
-func (c *ContainerRequest) BuildOptions() (types.ImageBuildOptions, error) {
- buildOptions := types.ImageBuildOptions{
+func (c *ContainerRequest) BuildOptions() (build.ImageBuildOptions, error) {
+ buildOptions := build.ImageBuildOptions{
Remove: true,
ForceRemove: true,
}
@@ -450,16 +451,14 @@ func (c *ContainerRequest) BuildOptions() (types.ImageBuildOptions, error) {
// Make sure the auth configs from the Dockerfile are set right after the user-defined build options.
authsFromDockerfile, err := getAuthConfigsFromDockerfile(c)
if err != nil {
- return types.ImageBuildOptions{}, fmt.Errorf("auth configs from Dockerfile: %w", err)
+ return build.ImageBuildOptions{}, fmt.Errorf("auth configs from Dockerfile: %w", err)
}
if buildOptions.AuthConfigs == nil {
buildOptions.AuthConfigs = map[string]registry.AuthConfig{}
}
- for registry, authConfig := range authsFromDockerfile {
- buildOptions.AuthConfigs[registry] = authConfig
- }
+ maps.Copy(buildOptions.AuthConfigs, authsFromDockerfile)
// make sure the first tag is the one defined in the ContainerRequest
tag := fmt.Sprintf("%s:%s", c.GetRepo(), c.GetTag())
@@ -468,7 +467,7 @@ func (c *ContainerRequest) BuildOptions() (types.ImageBuildOptions, error) {
for _, is := range c.ImageSubstitutors {
modifiedTag, err := is.Substitute(tag)
if err != nil {
- return types.ImageBuildOptions{}, fmt.Errorf("failed to substitute image %s with %s: %w", tag, is.Description(), err)
+ return build.ImageBuildOptions{}, fmt.Errorf("failed to substitute image %s with %s: %w", tag, is.Description(), err)
}
if modifiedTag != tag {
@@ -487,10 +486,10 @@ func (c *ContainerRequest) BuildOptions() (types.ImageBuildOptions, error) {
if !c.ShouldKeepBuiltImage() {
dst := GenericLabels()
if err = core.MergeCustomLabels(dst, c.Labels); err != nil {
- return types.ImageBuildOptions{}, err
+ return build.ImageBuildOptions{}, err
}
if err = core.MergeCustomLabels(dst, buildOptions.Labels); err != nil {
- return types.ImageBuildOptions{}, err
+ return build.ImageBuildOptions{}, err
}
buildOptions.Labels = dst
}
@@ -498,7 +497,7 @@ 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 types.ImageBuildOptions{}, err
+ return build.ImageBuildOptions{}, err
}
buildOptions.Context = buildContext
diff --git a/vendor/github.com/testcontainers/testcontainers-go/docker.go b/vendor/github.com/testcontainers/testcontainers-go/docker.go
index c578796..e20026c 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/docker.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/docker.go
@@ -5,6 +5,7 @@ import (
"bufio"
"context"
"encoding/base64"
+ "encoding/binary"
"encoding/json"
"errors"
"fmt"
@@ -15,18 +16,19 @@ import (
"os"
"path/filepath"
"regexp"
+ "slices"
"sync"
"time"
"github.com/cenkalti/backoff/v4"
+ "github.com/containerd/errdefs"
"github.com/containerd/platforms"
- "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
- "github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/go-connections/nat"
@@ -79,6 +81,7 @@ type DockerContainer struct {
provider *DockerProvider
sessionID string
terminationSignal chan bool
+ consumersMtx sync.Mutex // protects consumers
consumers []LogConsumer
// TODO: Remove locking and wait group once the deprecated StartLogProducer and
@@ -139,7 +142,8 @@ func (c *DockerContainer) Endpoint(ctx context.Context, proto string) (string, e
}
// PortEndpoint gets proto://host:port string for the given exposed port
-// Will returns just host:port if proto is ""
+// It returns proto://host:port or proto://[IPv6host]:port string for the given exposed port.
+// It returns just host:port or [IPv6host]:port if proto is blank.
func (c *DockerContainer) PortEndpoint(ctx context.Context, port nat.Port, proto string) (string, error) {
host, err := c.Host(ctx)
if err != nil {
@@ -151,12 +155,12 @@ func (c *DockerContainer) PortEndpoint(ctx context.Context, port nat.Port, proto
return "", err
}
- protoFull := ""
- if proto != "" {
- protoFull = proto + "://"
+ hostPort := net.JoinHostPort(host, outerPort.Port())
+ if proto == "" {
+ return hostPort, nil
}
- return fmt.Sprintf("%s%s:%s", protoFull, host, outerPort.Port()), nil
+ return proto + "://" + hostPort, nil
}
// Host gets host (ip or name) of the docker daemon where the container port is exposed
@@ -205,7 +209,7 @@ func (c *DockerContainer) MappedPort(ctx context.Context, port nat.Port) (nat.Po
return nat.NewPort(k.Proto(), p[0].HostPort)
}
- return "", errdefs.NotFound(fmt.Errorf("port %q not found", port))
+ return "", errdefs.ErrNotFound.WithMessage(fmt.Sprintf("port %q not found", port))
}
// Deprecated: use c.Inspect(ctx).NetworkSettings.Ports instead.
@@ -364,8 +368,6 @@ func (c *DockerContainer) inspectRawContainer(ctx context.Context) (*container.I
// Logs will fetch both STDOUT and STDERR from the current container. Returns a
// ReadCloser and leaves it up to the caller to extract what it wants.
func (c *DockerContainer) Logs(ctx context.Context) (io.ReadCloser, error) {
- const streamHeaderSize = 8
-
options := container.LogsOptions{
ShowStdout: true,
ShowStderr: true,
@@ -377,42 +379,43 @@ func (c *DockerContainer) Logs(ctx context.Context) (io.ReadCloser, error) {
}
defer c.provider.Close()
+ resp, err := c.Inspect(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.Config.Tty {
+ return rc, nil
+ }
+
+ return c.parseMultiplexedLogs(rc), nil
+}
+
+// parseMultiplexedLogs handles the multiplexed log format used when TTY is disabled
+func (c *DockerContainer) parseMultiplexedLogs(rc io.ReadCloser) io.ReadCloser {
+ const streamHeaderSize = 8
+
pr, pw := io.Pipe()
r := bufio.NewReader(rc)
go func() {
- lineStarted := true
- for err == nil {
- line, isPrefix, err := r.ReadLine()
-
- if lineStarted && len(line) >= streamHeaderSize {
- line = line[streamHeaderSize:] // trim stream header
- lineStarted = false
- }
- if !isPrefix {
- lineStarted = true
- }
-
- _, errW := pw.Write(line)
- if errW != nil {
+ header := make([]byte, streamHeaderSize)
+ for {
+ _, errH := io.ReadFull(r, header)
+ if errH != nil {
+ _ = pw.CloseWithError(errH)
return
}
- if !isPrefix {
- _, errW := pw.Write([]byte("\n"))
- if errW != nil {
- return
- }
- }
-
- if err != nil {
- _ = pw.CloseWithError(err)
+ frameSize := binary.BigEndian.Uint32(header[4:])
+ if _, err := io.CopyN(pw, r, int64(frameSize)); err != nil {
+ pw.CloseWithError(err)
return
}
}
}()
- return pr, nil
+ return pr
}
// Deprecated: use the ContainerRequest.LogConsumerConfig field instead.
@@ -423,9 +426,29 @@ func (c *DockerContainer) FollowOutput(consumer LogConsumer) {
// followOutput adds a LogConsumer to be sent logs from the container's
// STDOUT and STDERR
func (c *DockerContainer) followOutput(consumer LogConsumer) {
+ c.consumersMtx.Lock()
+ defer c.consumersMtx.Unlock()
+
c.consumers = append(c.consumers, consumer)
}
+// consumersCopy returns a copy of the current consumers.
+func (c *DockerContainer) consumersCopy() []LogConsumer {
+ c.consumersMtx.Lock()
+ defer c.consumersMtx.Unlock()
+
+ return slices.Clone(c.consumers)
+}
+
+// resetConsumers resets the current consumers to the provided ones.
+func (c *DockerContainer) resetConsumers(consumers []LogConsumer) {
+ c.consumersMtx.Lock()
+ defer c.consumersMtx.Unlock()
+
+ c.consumers = c.consumers[:0]
+ c.consumers = append(c.consumers, consumers...)
+}
+
// Deprecated: use c.Inspect(ctx).Name instead.
// Name gets the name of the container.
func (c *DockerContainer) Name(ctx context.Context) (string, error) {
@@ -760,8 +783,10 @@ func (c *DockerContainer) startLogProduction(ctx context.Context, opts ...LogPro
}
// Setup the log writers.
- stdout := newLogConsumerWriter(StdoutLog, c.consumers)
- stderr := newLogConsumerWriter(StderrLog, c.consumers)
+
+ consumers := c.consumersCopy()
+ stdout := newLogConsumerWriter(StdoutLog, consumers)
+ stderr := newLogConsumerWriter(StderrLog, consumers)
// Setup the log production context which will be used to stop the log production.
c.logProductionCtx, c.logProductionCancel = context.WithCancelCause(ctx)
@@ -977,22 +1002,22 @@ var _ ContainerProvider = (*DockerProvider)(nil)
// BuildImage will build and image from context and Dockerfile, then return the tag
func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (string, error) {
- var buildOptions types.ImageBuildOptions
+ var buildOptions build.ImageBuildOptions
resp, err := backoff.RetryNotifyWithData(
- func() (types.ImageBuildResponse, error) {
+ func() (build.ImageBuildResponse, error) {
var err error
buildOptions, err = img.BuildOptions()
if err != nil {
- return types.ImageBuildResponse{}, backoff.Permanent(fmt.Errorf("build options: %w", err))
+ return build.ImageBuildResponse{}, backoff.Permanent(fmt.Errorf("build options: %w", err))
}
defer tryClose(buildOptions.Context) // release resources in any case
resp, err := p.client.ImageBuild(ctx, buildOptions.Context, buildOptions)
if err != nil {
if isPermanentClientError(err) {
- return types.ImageBuildResponse{}, backoff.Permanent(fmt.Errorf("build image: %w", err))
+ return build.ImageBuildResponse{}, backoff.Permanent(fmt.Errorf("build image: %w", err))
}
- return types.ImageBuildResponse{}, err
+ return build.ImageBuildResponse{}, err
}
defer p.Close()
@@ -1037,13 +1062,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
// as container won't be attached to it automatically
// in case of Podman the bridge network is called 'podman' as 'bridge' would conflict
if defaultNetwork != p.defaultBridgeNetworkName {
- isAttached := false
- for _, net := range req.Networks {
- if net == defaultNetwork {
- isAttached = true
- break
- }
- }
+ isAttached := slices.Contains(req.Networks, defaultNetwork)
if !isAttached {
req.Networks = append(req.Networks, defaultNetwork)
@@ -1121,7 +1140,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
} else {
img, err := p.client.ImageInspect(ctx, imageName)
if err != nil {
- if !client.IsErrNotFound(err) {
+ if !errdefs.IsNotFound(err) {
return nil, err
}
shouldPullImage = true
@@ -1290,7 +1309,7 @@ func (p *DockerProvider) waitContainerCreation(ctx context.Context, name string)
}
if c == nil {
- return nil, errdefs.NotFound(fmt.Errorf("container %s not found", name))
+ return nil, errdefs.ErrNotFound.WithMessage(fmt.Sprintf("container %s not found", name))
}
return c, nil
},
@@ -1364,11 +1383,19 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
lifecycleHooks: []ContainerLifecycleHooks{combineContainerHooks(defaultHooks, req.LifecycleHooks)},
}
+ // Workaround for https://github.com/moby/moby/issues/50133.
+ // /containers/{id}/json API endpoint of Docker Engine takes data about container from master (not replica) database
+ // which is synchronized with container state after call of /containers/{id}/stop API endpoint.
+ dcState, err := dc.State(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("docker container state: %w", err)
+ }
+
// If a container was stopped programmatically, we want to ensure the container
// is running again, but only if it is not paused, as it's not possible to start
// a paused container. The Docker Engine returns the "cannot start a paused container,
// try unpause instead" error.
- switch c.State {
+ switch dcState.Status {
case "running":
// cannot re-start a running container, but we still need
// to call the startup hooks.
@@ -1401,7 +1428,7 @@ 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("No image auth found for %s. Setting empty credentials for the image: %s. This is expected for public images. Details: %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)
@@ -1437,7 +1464,7 @@ func (p *DockerProvider) attemptToPullImage(ctx context.Context, tag string, pul
defer pull.Close()
// download of docker image finishes at EOF of the pull request
- _, err = io.ReadAll(pull)
+ _, err = io.Copy(io.Discard, pull)
return err
}
@@ -1791,11 +1818,11 @@ func (p *DockerProvider) PullImage(ctx context.Context, img string) error {
var permanentClientErrors = []func(error) bool{
errdefs.IsNotFound,
- errdefs.IsInvalidParameter,
+ errdefs.IsInvalidArgument,
errdefs.IsUnauthorized,
- errdefs.IsForbidden,
+ errdefs.IsPermissionDenied,
errdefs.IsNotImplemented,
- errdefs.IsSystem,
+ errdefs.IsInternal,
}
func isPermanentClientError(err error) bool {
diff --git a/vendor/github.com/testcontainers/testcontainers-go/exec/processor.go b/vendor/github.com/testcontainers/testcontainers-go/exec/processor.go
index 9c852fb..36f1db1 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/exec/processor.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/exec/processor.go
@@ -25,7 +25,6 @@ func NewProcessOptions(cmd []string) *ProcessOptions {
return &ProcessOptions{
ExecConfig: container.ExecOptions{
Cmd: cmd,
- Detach: false,
AttachStdout: true,
AttachStderr: true,
},
diff --git a/vendor/github.com/testcontainers/testcontainers-go/generic.go b/vendor/github.com/testcontainers/testcontainers-go/generic.go
index 9663b03..dc5ee1c 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/generic.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/generic.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
+ "maps"
"strings"
"sync"
@@ -113,9 +114,7 @@ func GenericLabels() map[string]string {
// AddGenericLabels adds the generic labels to target.
func AddGenericLabels(target map[string]string) {
- for k, v := range GenericLabels() {
- target[k] = v
- }
+ maps.Copy(target, GenericLabels())
}
// Run is a convenience function that creates a new container and starts it.
diff --git a/vendor/github.com/testcontainers/testcontainers-go/internal/config/config.go b/vendor/github.com/testcontainers/testcontainers-go/internal/config/config.go
index 64f2f7f..dda7e28 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/internal/config/config.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/internal/config/config.go
@@ -11,7 +11,7 @@ import (
"github.com/magiconair/properties"
)
-const ReaperDefaultImage = "testcontainers/ryuk:0.11.0"
+const ReaperDefaultImage = "testcontainers/ryuk:0.12.0"
var (
tcConfig Config
diff --git a/vendor/github.com/testcontainers/testcontainers-go/internal/core/labels.go b/vendor/github.com/testcontainers/testcontainers-go/internal/core/labels.go
index 0814924..198fdae 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/internal/core/labels.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/internal/core/labels.go
@@ -3,6 +3,7 @@ package core
import (
"errors"
"fmt"
+ "maps"
"strings"
"github.com/testcontainers/testcontainers-go/internal"
@@ -51,9 +52,7 @@ func DefaultLabels(sessionID string) map[string]string {
// AddDefaultLabels adds the default labels for sessionID to target.
func AddDefaultLabels(sessionID string, target map[string]string) {
- for k, v := range DefaultLabels(sessionID) {
- target[k] = v
- }
+ maps.Copy(target, DefaultLabels(sessionID))
}
// MergeCustomLabels sets labels from src to dst.
diff --git a/vendor/github.com/testcontainers/testcontainers-go/internal/version.go b/vendor/github.com/testcontainers/testcontainers-go/internal/version.go
index 6dba727..bc31ad9 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/internal/version.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/internal/version.go
@@ -1,4 +1,4 @@
package internal
// Version is the next development version of the application
-const Version = "0.37.0"
+const Version = "0.38.0"
diff --git a/vendor/github.com/testcontainers/testcontainers-go/lifecycle.go b/vendor/github.com/testcontainers/testcontainers-go/lifecycle.go
index 72363cc..7887ebe 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/lifecycle.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/lifecycle.go
@@ -9,7 +9,6 @@ import (
"strings"
"time"
- "github.com/cenkalti/backoff/v4"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
@@ -190,10 +189,7 @@ var defaultLogConsumersHook = func(cfg *LogConsumerConfig) ContainerLifecycleHoo
}
dockerContainer := c.(*DockerContainer)
- dockerContainer.consumers = dockerContainer.consumers[:0]
- for _, consumer := range cfg.Consumers {
- dockerContainer.followOutput(consumer)
- }
+ dockerContainer.resetConsumers(cfg.Consumers)
return dockerContainer.startLogProduction(ctx, cfg.Opts...)
},
@@ -213,71 +209,10 @@ var defaultLogConsumersHook = func(cfg *LogConsumerConfig) ContainerLifecycleHoo
}
}
-func checkPortsMapped(exposedAndMappedPorts nat.PortMap, exposedPorts []string) error {
- portMap, _, err := nat.ParsePortSpecs(exposedPorts)
- if err != nil {
- return fmt.Errorf("parse exposed ports: %w", err)
- }
-
- for exposedPort := range portMap {
- // having entries in exposedAndMappedPorts, where the key is the exposed port,
- // and the value is the mapped port, means that the port has been already mapped.
- if _, ok := exposedAndMappedPorts[exposedPort]; ok {
- continue
- }
-
- // check if the port is mapped with the protocol (default is TCP)
- if strings.Contains(string(exposedPort), "/") {
- return fmt.Errorf("port %s is not mapped yet", exposedPort)
- }
-
- // Port didn't have a type, default to tcp and retry.
- exposedPort += "/tcp"
- if _, ok := exposedAndMappedPorts[exposedPort]; !ok {
- return fmt.Errorf("port %s is not mapped yet", exposedPort)
- }
- }
-
- return nil
-}
-
// defaultReadinessHook is a hook that will wait for the container to be ready
var defaultReadinessHook = func() ContainerLifecycleHooks {
return ContainerLifecycleHooks{
PostStarts: []ContainerHook{
- func(ctx context.Context, c Container) error {
- // wait until all the exposed ports are mapped:
- // it will be ready when all the exposed ports are mapped,
- // checking every 50ms, up to 1s, and failing if all the
- // exposed ports are not mapped in 5s.
- dockerContainer := c.(*DockerContainer)
-
- b := backoff.NewExponentialBackOff()
-
- b.InitialInterval = 50 * time.Millisecond
- b.MaxElapsedTime = 5 * time.Second
- b.MaxInterval = time.Duration(float64(time.Second) * backoff.DefaultRandomizationFactor)
-
- err := backoff.RetryNotify(
- func() error {
- jsonRaw, err := dockerContainer.inspectRawContainer(ctx)
- if err != nil {
- return err
- }
-
- return checkPortsMapped(jsonRaw.NetworkSettings.Ports, dockerContainer.exposedPorts)
- },
- b,
- func(err error, _ time.Duration) {
- dockerContainer.logger.Printf("All requested ports were not exposed: %v", err)
- },
- )
- if err != nil {
- return fmt.Errorf("all exposed ports, %s, were not mapped in 5s: %w", dockerContainer.exposedPorts, err)
- }
-
- return nil
- },
// wait for the container to be ready
func(ctx context.Context, c Container) error {
dockerContainer := c.(*DockerContainer)
@@ -373,7 +308,11 @@ func (c *DockerContainer) printLogs(ctx context.Context, cause error) {
b, err := io.ReadAll(reader)
if err != nil {
- c.logger.Printf("failed reading container logs: %v\n", err)
+ if len(b) > 0 {
+ c.logger.Printf("failed reading container logs: %v\npartial container logs (%s):\n%s", err, cause, b)
+ } else {
+ c.logger.Printf("failed reading container logs: %v\n", err)
+ }
return
}
@@ -620,7 +559,7 @@ func combineContainerHooks(defaultHooks, userDefinedHooks []ContainerLifecycleHo
hooksType := reflect.TypeOf(hooks)
for _, defaultHook := range defaultHooks {
defaultVal := reflect.ValueOf(defaultHook)
- for i := 0; i < hooksType.NumField(); i++ {
+ for i := range hooksType.NumField() {
if strings.HasPrefix(hooksType.Field(i).Name, "Pre") {
field := hooksVal.Field(i)
field.Set(reflect.AppendSlice(field, defaultVal.Field(i)))
@@ -633,7 +572,7 @@ func combineContainerHooks(defaultHooks, userDefinedHooks []ContainerLifecycleHo
// post-hooks will be the first ones to be executed.
for _, userDefinedHook := range userDefinedHooks {
userVal := reflect.ValueOf(userDefinedHook)
- for i := 0; i < hooksType.NumField(); i++ {
+ for i := range hooksType.NumField() {
field := hooksVal.Field(i)
field.Set(reflect.AppendSlice(field, userVal.Field(i)))
}
@@ -642,7 +581,7 @@ func combineContainerHooks(defaultHooks, userDefinedHooks []ContainerLifecycleHo
// Finally, append the default post-hooks.
for _, defaultHook := range defaultHooks {
defaultVal := reflect.ValueOf(defaultHook)
- for i := 0; i < hooksType.NumField(); i++ {
+ for i := range hooksType.NumField() {
if strings.HasPrefix(hooksType.Field(i).Name, "Post") {
field := hooksVal.Field(i)
field.Set(reflect.AppendSlice(field, defaultVal.Field(i)))
diff --git a/vendor/github.com/testcontainers/testcontainers-go/mkdocs.yml b/vendor/github.com/testcontainers/testcontainers-go/mkdocs.yml
index 99e8f4a..60fa87c 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/mkdocs.yml
+++ b/vendor/github.com/testcontainers/testcontainers-go/mkdocs.yml
@@ -40,19 +40,6 @@ nav:
- Quickstart: quickstart.md
- Features:
- features/creating_container.md
- - features/configuration.md
- - features/image_name_substitution.md
- - features/files_and_mounts.md
- - features/creating_networks.md
- - features/networking.md
- - features/tls.md
- - features/test_session_semantics.md
- - features/garbage_collector.md
- - features/build_from_dockerfile.md
- - features/docker_auth.md
- - features/docker_compose.md
- - features/follow_logs.md
- - features/override_container_command.md
- Wait Strategies:
- Introduction: features/wait/introduction.md
- Exec: features/wait/exec.md
@@ -66,6 +53,18 @@ nav:
- SQL: features/wait/sql.md
- TLS: features/wait/tls.md
- Walk: features/wait/walk.md
+ - features/files_and_mounts.md
+ - features/follow_logs.md
+ - features/garbage_collector.md
+ - features/build_from_dockerfile.md
+ - features/override_container_command.md
+ - features/networking.md
+ - features/configuration.md
+ - features/image_name_substitution.md
+ - features/test_session_semantics.md
+ - features/docker_auth.md
+ - features/docker_compose.md
+ - features/tls.md
- Modules:
- modules/index.md
- modules/aerospike.md
@@ -86,7 +85,6 @@ nav:
- modules/dynamodb.md
- modules/elasticsearch.md
- modules/etcd.md
- - modules/firebase.md
- modules/gcloud.md
- modules/grafana-lgtm.md
- modules/inbucket.md
@@ -97,6 +95,7 @@ nav:
- modules/localstack.md
- modules/mariadb.md
- modules/meilisearch.md
+ - modules/memcached.md
- modules/milvus.md
- modules/minio.md
- modules/mockserver.md
@@ -150,4 +149,4 @@ nav:
- Getting help: getting_help.md
edit_uri: edit/main/docs/
extra:
- latest_version: v0.37.0
+ latest_version: v0.38.0
diff --git a/vendor/github.com/testcontainers/testcontainers-go/options.go b/vendor/github.com/testcontainers/testcontainers-go/options.go
index 9afbcd7..f7775f8 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/options.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/options.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
+ "maps"
"net/url"
"time"
@@ -77,9 +78,7 @@ func WithEnv(envs map[string]string) CustomizeRequestOption {
req.Env = map[string]string{}
}
- for key, val := range envs {
- req.Env[key] = val
- }
+ maps.Copy(req.Env, envs)
return nil
}
@@ -106,14 +105,33 @@ func WithHostPortAccess(ports ...int) CustomizeRequestOption {
}
}
+// WithName will set the name of the container.
+func WithName(containerName string) CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ if containerName == "" {
+ return errors.New("container name must be provided")
+ }
+ req.Name = containerName
+ return nil
+ }
+}
+
+// WithNoStart will prevent the container from being started after creation.
+func WithNoStart() CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ req.Started = false
+ return nil
+ }
+}
+
// WithReuseByName will mark a container to be reused if it exists or create a new one if it doesn't.
// A container name must be provided to identify the container to be reused.
func WithReuseByName(containerName string) CustomizeRequestOption {
return func(req *GenericContainerRequest) error {
- if containerName == "" {
- return errors.New("container name must be provided for reuse")
+ if err := WithName(containerName)(req); err != nil {
+ return err
}
- req.Name = containerName
+
req.Reuse = true
return nil
}
@@ -255,6 +273,17 @@ func WithLogConsumers(consumer ...LogConsumer) CustomizeRequestOption {
}
}
+// WithLogConsumerConfig sets the log consumer config for a container.
+// Beware that this option completely replaces the existing log consumer config,
+// including the log consumers and the log production options,
+// so it should be used with care.
+func WithLogConsumerConfig(config *LogConsumerConfig) CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ req.LogConsumerCfg = config
+ return nil
+ }
+}
+
// Executable represents an executable command to be sent to a container, including options,
// as part of the different lifecycle hooks.
type Executable interface {
@@ -281,11 +310,11 @@ type RawCommand struct {
cmds []string
}
-func NewRawCommand(cmds []string) RawCommand {
+func NewRawCommand(cmds []string, opts ...tcexec.ProcessOption) RawCommand {
return RawCommand{
cmds: cmds,
ExecOptions: ExecOptions{
- opts: []tcexec.ProcessOption{},
+ opts: opts,
},
}
}
@@ -343,12 +372,17 @@ func WithAfterReadyCommand(execs ...Executable) CustomizeRequestOption {
}
}
-// WithWaitStrategy sets the wait strategy for a container, using 60 seconds as deadline
+// WithWaitStrategy replaces the wait strategy for a container, using 60 seconds as deadline
func WithWaitStrategy(strategies ...wait.Strategy) CustomizeRequestOption {
return WithWaitStrategyAndDeadline(60*time.Second, strategies...)
}
-// WithWaitStrategyAndDeadline sets the wait strategy for a container, including deadline
+// WithAdditionalWaitStrategy appends the wait strategy for a container, using 60 seconds as deadline
+func WithAdditionalWaitStrategy(strategies ...wait.Strategy) CustomizeRequestOption {
+ return WithAdditionalWaitStrategyAndDeadline(60*time.Second, strategies...)
+}
+
+// WithWaitStrategyAndDeadline replaces the wait strategy for a container, including deadline
func WithWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Strategy) CustomizeRequestOption {
return func(req *GenericContainerRequest) error {
req.WaitingFor = wait.ForAll(strategies...).WithDeadline(deadline)
@@ -357,6 +391,24 @@ func WithWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Stra
}
}
+// WithAdditionalWaitStrategyAndDeadline appends the wait strategy for a container, including deadline
+func WithAdditionalWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Strategy) CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ if req.WaitingFor == nil {
+ req.WaitingFor = wait.ForAll(strategies...).WithDeadline(deadline)
+ return nil
+ }
+
+ wss := make([]wait.Strategy, 0, len(strategies)+1)
+ wss = append(wss, req.WaitingFor)
+ wss = append(wss, strategies...)
+
+ req.WaitingFor = wait.ForAll(wss...).WithDeadline(deadline)
+
+ return nil
+ }
+}
+
// WithImageMount mounts an image to a container, passing the source image name,
// the relative subpath to mount in that image, and the mount point in the target container.
// This option validates that the subpath is a relative path, raising an error otherwise.
@@ -376,6 +428,22 @@ func WithImageMount(source string, subpath string, target ContainerMountTarget)
}
}
+// WithAlwaysPull will pull the image before starting the container
+func WithAlwaysPull() CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ req.AlwaysPullImage = true
+ return nil
+ }
+}
+
+// WithImagePlatform sets the platform for a container
+func WithImagePlatform(platform string) CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ req.ImagePlatform = platform
+ return nil
+ }
+}
+
// WithEntrypoint completely replaces the entrypoint of a container
func WithEntrypoint(entrypoint ...string) CustomizeRequestOption {
return func(req *GenericContainerRequest) error {
@@ -422,9 +490,23 @@ func WithLabels(labels map[string]string) CustomizeRequestOption {
if req.Labels == nil {
req.Labels = make(map[string]string)
}
- for k, v := range labels {
- req.Labels[k] = v
- }
+ maps.Copy(req.Labels, labels)
+ return nil
+ }
+}
+
+// WithLifecycleHooks completely replaces the lifecycle hooks for a container
+func WithLifecycleHooks(hooks ...ContainerLifecycleHooks) CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ req.LifecycleHooks = hooks
+ return nil
+ }
+}
+
+// WithAdditionalLifecycleHooks appends lifecycle hooks to the existing ones for a container
+func WithAdditionalLifecycleHooks(hooks ...ContainerLifecycleHooks) CustomizeRequestOption {
+ return func(req *GenericContainerRequest) error {
+ req.LifecycleHooks = append(req.LifecycleHooks, hooks...)
return nil
}
}
@@ -443,9 +525,7 @@ func WithTmpfs(tmpfs map[string]string) CustomizeRequestOption {
if req.Tmpfs == nil {
req.Tmpfs = make(map[string]string)
}
- for k, v := range tmpfs {
- req.Tmpfs[k] = v
- }
+ maps.Copy(req.Tmpfs, tmpfs)
return nil
}
}
diff --git a/vendor/github.com/testcontainers/testcontainers-go/parallel.go b/vendor/github.com/testcontainers/testcontainers-go/parallel.go
index 0349023..a75d011 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/parallel.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/parallel.go
@@ -61,10 +61,7 @@ func ParallelContainers(ctx context.Context, reqs ParallelContainerRequest, opt
opt.WorkersCount = defaultWorkersCount
}
- tasksChanSize := opt.WorkersCount
- if tasksChanSize > len(reqs) {
- tasksChanSize = len(reqs)
- }
+ tasksChanSize := min(opt.WorkersCount, len(reqs))
tasksChan := make(chan GenericContainerRequest, tasksChanSize)
resultsChan := make(chan parallelContainersResult, tasksChanSize)
@@ -74,7 +71,7 @@ func ParallelContainers(ctx context.Context, reqs ParallelContainerRequest, opt
wg.Add(tasksChanSize)
// run workers
- for i := 0; i < tasksChanSize; i++ {
+ for range tasksChanSize {
go parallelContainersRunner(ctx, tasksChan, resultsChan, &wg)
}
diff --git a/vendor/github.com/testcontainers/testcontainers-go/port_forwarding.go b/vendor/github.com/testcontainers/testcontainers-go/port_forwarding.go
index cd82361..107bd42 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/port_forwarding.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/port_forwarding.go
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net"
+ "slices"
"sync"
"time"
@@ -19,7 +20,7 @@ import (
const (
// hubSshdImage {
- sshdImage string = "testcontainers/sshd:1.2.0"
+ sshdImage string = "testcontainers/sshd:1.3.0"
// }
// HostInternal is the internal hostname used to reach the host from the container,
@@ -135,13 +136,7 @@ func exposeHostPorts(ctx context.Context, req *ContainerRequest, ports ...int) (
modes := []container.NetworkMode{container.NetworkMode(sshdFirstNetwork), "none", "host"}
// if the container is not in one of the modes, attach it to the first network of the SSHD container
- found := false
- for _, mode := range modes {
- if hostConfig.NetworkMode == mode {
- found = true
- break
- }
- }
+ found := slices.Contains(modes, hostConfig.NetworkMode)
if !found {
req.Networks = append(req.Networks, sshdFirstNetwork)
}
diff --git a/vendor/github.com/testcontainers/testcontainers-go/reaper.go b/vendor/github.com/testcontainers/testcontainers-go/reaper.go
index 26cac14..4e46f0e 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/reaper.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/reaper.go
@@ -14,10 +14,10 @@ import (
"time"
"github.com/cenkalti/backoff/v4"
+ "github.com/containerd/errdefs"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
- "github.com/docker/docker/errdefs"
"github.com/docker/go-connections/nat"
"github.com/testcontainers/testcontainers-go/internal/config"
@@ -224,7 +224,7 @@ func (r *reaperSpawner) isRunning(ctx context.Context, ctr Container) error {
if !state.Running {
// Use NotFound error to indicate the container is not running
// and should be recreated.
- return errdefs.NotFound(fmt.Errorf("container state: %s", state.Status))
+ return errdefs.ErrNotFound.WithMessage("container state: " + state.Status)
}
return nil
diff --git a/vendor/github.com/testcontainers/testcontainers-go/requirements.txt b/vendor/github.com/testcontainers/testcontainers-go/requirements.txt
index e4db882..8027e7d 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/requirements.txt
+++ b/vendor/github.com/testcontainers/testcontainers-go/requirements.txt
@@ -1,5 +1,5 @@
mkdocs==1.5.3
mkdocs-codeinclude-plugin==0.2.1
-mkdocs-include-markdown-plugin==6.2.2
+mkdocs-include-markdown-plugin==7.1.5
mkdocs-material==9.5.18
mkdocs-markdownextradata-plugin==0.2.6
diff --git a/vendor/github.com/testcontainers/testcontainers-go/testing.go b/vendor/github.com/testcontainers/testcontainers-go/testing.go
index 1f41913..704af99 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/testing.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/testing.go
@@ -7,7 +7,7 @@ import (
"regexp"
"testing"
- "github.com/docker/docker/errdefs"
+ "github.com/containerd/errdefs"
"github.com/stretchr/testify/require"
)
@@ -147,15 +147,19 @@ func isCleanupSafe(err error) bool {
return true
}
- switch x := err.(type) { //nolint:errorlint // We need to check for interfaces.
- case errdefs.ErrNotFound:
+ // First try with containerd's errdefs
+ switch {
+ case errdefs.IsNotFound(err):
return true
- case errdefs.ErrConflict:
+ case errdefs.IsConflict(err):
// Terminating a container that is already terminating.
if errAlreadyInProgress.MatchString(err.Error()) {
return true
}
return false
+ }
+
+ switch x := err.(type) { //nolint:errorlint // We need to check for interfaces.
case causer:
return isCleanupSafe(x.Cause())
case wrapErr:
diff --git a/vendor/github.com/testcontainers/testcontainers-go/wait/file.go b/vendor/github.com/testcontainers/testcontainers-go/wait/file.go
index d9cab7a..4f6d38c 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/wait/file.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/wait/file.go
@@ -6,7 +6,7 @@ import (
"io"
"time"
- "github.com/docker/docker/errdefs"
+ "github.com/containerd/errdefs"
)
var (
diff --git a/vendor/github.com/testcontainers/testcontainers-go/wait/host_port.go b/vendor/github.com/testcontainers/testcontainers-go/wait/host_port.go
index 2070bf1..8d97ccb 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/wait/host_port.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/wait/host_port.go
@@ -6,7 +6,6 @@ import (
"fmt"
"net"
"os"
- "strconv"
"time"
"github.com/docker/go-connections/nat"
@@ -42,6 +41,11 @@ type HostPortStrategy struct {
// a shell is not available in the container or when the container doesn't bind
// the port internally until additional conditions are met.
skipInternalCheck bool
+
+ // skipExternalCheck is a flag to skip the external check, which, if used with
+ // skipInternalCheck, makes strategy waiting only for port mapping completion
+ // without accessing port.
+ skipExternalCheck bool
}
// NewHostPortStrategy constructs a default host port strategy that waits for the given
@@ -70,6 +74,12 @@ func ForExposedPort() *HostPortStrategy {
return NewHostPortStrategy("")
}
+// ForMappedPort returns a host port strategy that waits for the given port
+// to be mapped without accessing the port itself.
+func ForMappedPort(port nat.Port) *HostPortStrategy {
+ return NewHostPortStrategy(port).SkipInternalCheck().SkipExternalCheck()
+}
+
// SkipInternalCheck changes the host port strategy to skip the internal check,
// which is useful when a shell is not available in the container or when the
// container doesn't bind the port internally until additional conditions are met.
@@ -79,6 +89,15 @@ func (hp *HostPortStrategy) SkipInternalCheck() *HostPortStrategy {
return hp
}
+// SkipExternalCheck changes the host port strategy to skip the external check,
+// which, if used with SkipInternalCheck, makes strategy waiting only for port
+// mapping completion without accessing port.
+func (hp *HostPortStrategy) SkipExternalCheck() *HostPortStrategy {
+ hp.skipExternalCheck = true
+
+ return hp
+}
+
// WithStartupTimeout can be used to change the default startup timeout
func (hp *HostPortStrategy) WithStartupTimeout(startupTimeout time.Duration) *HostPortStrategy {
hp.timeout = &startupTimeout
@@ -95,6 +114,25 @@ func (hp *HostPortStrategy) Timeout() *time.Duration {
return hp.timeout
}
+// detectInternalPort returns the lowest internal port that is currently bound.
+// If no internal port is found, it returns the zero nat.Port value which
+// can be checked against an empty string.
+func (hp *HostPortStrategy) detectInternalPort(ctx context.Context, target StrategyTarget) (nat.Port, error) {
+ var internalPort nat.Port
+ inspect, err := target.Inspect(ctx)
+ if err != nil {
+ return internalPort, fmt.Errorf("inspect: %w", err)
+ }
+
+ for port := range inspect.NetworkSettings.Ports {
+ if internalPort == "" || port.Int() < internalPort.Int() {
+ internalPort = port
+ }
+ }
+
+ return internalPort, nil
+}
+
// WaitUntilReady implements Strategy.WaitUntilReady
func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error {
timeout := defaultStartupTimeout()
@@ -105,34 +143,37 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
- ipAddress, err := target.Host(ctx)
- if err != nil {
- return err
- }
-
waitInterval := hp.PollInterval
internalPort := hp.Port
+ i := 0
if internalPort == "" {
- inspect, err := target.Inspect(ctx)
+ var err error
+ // Port is not specified, so we need to detect it.
+ internalPort, err = hp.detectInternalPort(ctx, target)
if err != nil {
- return err
+ return fmt.Errorf("detect internal port: %w", err)
}
- for port := range inspect.NetworkSettings.Ports {
- if internalPort == "" || port.Int() < internalPort.Int() {
- internalPort = port
+ for internalPort == "" {
+ select {
+ case <-ctx.Done():
+ return fmt.Errorf("detect internal port: retries: %d, last err: %w, ctx err: %w", i, err, ctx.Err())
+ case <-time.After(waitInterval):
+ if err := checkTarget(ctx, target); err != nil {
+ return fmt.Errorf("detect internal port: check target: retries: %d, last err: %w", i, err)
+ }
+
+ internalPort, err = hp.detectInternalPort(ctx, target)
+ if err != nil {
+ return fmt.Errorf("detect internal port: %w", err)
+ }
}
}
}
- if internalPort == "" {
- return errors.New("no port to wait for")
- }
-
- var port nat.Port
- port, err = target.MappedPort(ctx, internalPort)
- i := 0
+ port, err := target.MappedPort(ctx, internalPort)
+ i = 0
for port == "" {
i++
@@ -142,7 +183,7 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT
return fmt.Errorf("mapped port: retries: %d, port: %q, last err: %w, ctx err: %w", i, port, err, ctx.Err())
case <-time.After(waitInterval):
if err := checkTarget(ctx, target); err != nil {
- return fmt.Errorf("check target: retries: %d, port: %q, last err: %w", i, port, err)
+ return fmt.Errorf("mapped port: check target: retries: %d, port: %q, last err: %w", i, port, err)
}
port, err = target.MappedPort(ctx, internalPort)
if err != nil {
@@ -151,8 +192,15 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT
}
}
- if err := externalCheck(ctx, ipAddress, port, target, waitInterval); err != nil {
- return fmt.Errorf("external check: %w", err)
+ if !hp.skipExternalCheck {
+ ipAddress, err := target.Host(ctx)
+ if err != nil {
+ return fmt.Errorf("host: %w", err)
+ }
+
+ if err := externalCheck(ctx, ipAddress, port, target, waitInterval); err != nil {
+ return fmt.Errorf("external check: %w", err)
+ }
}
if hp.skipInternalCheck {
@@ -177,11 +225,9 @@ func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyT
func externalCheck(ctx context.Context, ipAddress string, port nat.Port, target StrategyTarget, waitInterval time.Duration) error {
proto := port.Proto()
- portNumber := port.Int()
- portString := strconv.Itoa(portNumber)
dialer := net.Dialer{}
- address := net.JoinHostPort(ipAddress, portString)
+ address := net.JoinHostPort(ipAddress, port.Port())
for i := 0; ; i++ {
if err := checkTarget(ctx, target); err != nil {
return fmt.Errorf("check target: retries: %d address: %s: %w", i, address, err)
diff --git a/vendor/github.com/testcontainers/testcontainers-go/wait/walk.go b/vendor/github.com/testcontainers/testcontainers-go/wait/walk.go
index 009e563..98f5755 100644
--- a/vendor/github.com/testcontainers/testcontainers-go/wait/walk.go
+++ b/vendor/github.com/testcontainers/testcontainers-go/wait/walk.go
@@ -2,6 +2,7 @@ package wait
import (
"errors"
+ "slices"
)
var (
@@ -63,7 +64,7 @@ func walk(root *Strategy, visit VisitFunc) error {
for range s.Strategies {
if err := walk(&s.Strategies[i], visit); err != nil {
if errors.Is(err, ErrVisitRemove) {
- s.Strategies = append(s.Strategies[:i], s.Strategies[i+1:]...)
+ s.Strategies = slices.Delete(s.Strategies, i, i+1)
if errors.Is(err, VisitStop) {
return VisitStop
}