From 555cb764ee8dbd9b8ce54349d230a402aa439b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 8 Nov 2023 13:23:12 +0100 Subject: [PATCH] chore: deprecate BindMount APIs (#1907) * chore: deprecate BindMount APIs * fix: lint * chore: update deprecation message --- container.go | 26 +++++++ container_test.go | 23 ++++-- docker.go | 8 +-- docker_auth_test.go | 20 +++--- docker_mounts.go | 10 ++- docker_test.go | 51 ++++++-------- docs/features/copy_file.md | 113 ------------------------------ docs/features/files_and_mounts.md | 96 ++++++++++++++++++++++--- lifecycle_test.go | 16 ++--- mkdocs.yml | 1 - mounts.go | 17 ++++- mounts_test.go | 84 ---------------------- reaper.go | 2 +- reaper_test.go | 10 ++- 14 files changed, 201 insertions(+), 276 deletions(-) delete mode 100644 docs/features/copy_file.md diff --git a/container.go b/container.go index df05d1d246..d8e2c7c2f3 100644 --- a/container.go +++ b/container.go @@ -305,6 +305,8 @@ func (c *ContainerRequest) validateContextOrImageIsSpecified() error { return nil } +// validateMounts ensures that the mounts do not have duplicate targets. +// It will check the Mounts and HostConfigModifier.Binds fields. func (c *ContainerRequest) validateMounts() error { targets := make(map[string]bool, len(c.Mounts)) @@ -317,5 +319,29 @@ func (c *ContainerRequest) validateMounts() error { targets[targetPath] = true } } + + if c.HostConfigModifier == nil { + return nil + } + + hostConfig := container.HostConfig{} + + c.HostConfigModifier(&hostConfig) + + if hostConfig.Binds != nil && len(hostConfig.Binds) > 0 { + for _, bind := range hostConfig.Binds { + parts := strings.Split(bind, ":") + if len(parts) != 2 { + return fmt.Errorf("%w: %s", ErrInvalidBindMount, bind) + } + targetPath := parts[1] + if targets[targetPath] { + return fmt.Errorf("%w: %s", ErrDuplicateMountTarget, targetPath) + } else { + targets[targetPath] = true + } + } + } + return nil } diff --git a/container_test.go b/container_test.go index 4f44af9fc8..0de6f33a19 100644 --- a/container_test.go +++ b/container_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/docker/docker/api/types/container" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -55,16 +56,30 @@ func Test_ContainerValidation(t *testing.T) { Name: "Can mount same source to multiple targets", ExpectedError: nil, ContainerRequest: ContainerRequest{ - Image: "redis:latest", - Mounts: Mounts(BindMount("/data", "/srv"), BindMount("/data", "/data")), + Image: "redis:latest", + HostConfigModifier: func(hc *container.HostConfig) { + hc.Binds = []string{"/data:/srv", "/data:/data"} + }, }, }, { Name: "Cannot mount multiple sources to same target", ExpectedError: errors.New("duplicate mount target detected: /data"), ContainerRequest: ContainerRequest{ - Image: "redis:latest", - Mounts: Mounts(BindMount("/srv", "/data"), BindMount("/data", "/data")), + Image: "redis:latest", + HostConfigModifier: func(hc *container.HostConfig) { + hc.Binds = []string{"/data:/data", "/data:/data"} + }, + }, + }, + { + Name: "Invalid bind mount", + ExpectedError: errors.New("invalid bind mount: /data:/data:/data"), + ContainerRequest: ContainerRequest{ + Image: "redis:latest", + HostConfigModifier: func(hc *container.HostConfig) { + hc.Binds = []string{"/data:/data:/data"} + }, }, }, } diff --git a/docker.go b/docker.go index cc84b31fc3..0ad7f3702e 100644 --- a/docker.go +++ b/docker.go @@ -36,12 +36,8 @@ import ( "github.com/testcontainers/testcontainers-go/wait" ) -var ( - // Implement interfaces - _ Container = (*DockerContainer)(nil) - - ErrDuplicateMountTarget = errors.New("duplicate mount target detected") -) +// Implement interfaces +var _ Container = (*DockerContainer)(nil) const ( Bridge = "bridge" // Bridge network name (as well as driver) diff --git a/docker_auth_test.go b/docker_auth_test.go index 6563e9de34..d6352bdecc 100644 --- a/docker_auth_test.go +++ b/docker_auth_test.go @@ -243,7 +243,7 @@ func prepareLocalRegistryWithAuth(t *testing.T) { ctx := context.Background() wd, err := os.Getwd() assert.NoError(t, err) - // bindMounts { + // copyDirectoryToContainer { req := ContainerRequest{ Image: "registry:2", ExposedPorts: []string{"5001:5000/tcp"}, @@ -253,18 +253,14 @@ func prepareLocalRegistryWithAuth(t *testing.T) { "REGISTRY_AUTH_HTPASSWD_PATH": "/auth/htpasswd", "REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY": "/data", }, - Mounts: ContainerMounts{ - ContainerMount{ - Source: GenericBindMountSource{ - HostPath: fmt.Sprintf("%s/testdata/auth", wd), - }, - Target: "/auth", + Files: []ContainerFile{ + { + HostFilePath: fmt.Sprintf("%s/testdata/auth", wd), + ContainerFilePath: "/auth", }, - ContainerMount{ - Source: GenericBindMountSource{ - HostPath: fmt.Sprintf("%s/testdata/data", wd), - }, - Target: "/data", + { + HostFilePath: fmt.Sprintf("%s/testdata/data", wd), + ContainerFilePath: "/data", }, }, WaitingFor: wait.ForExposedPort(), diff --git a/docker_mounts.go b/docker_mounts.go index fa3e825c4b..d5d075c053 100644 --- a/docker_mounts.go +++ b/docker_mounts.go @@ -3,12 +3,12 @@ package testcontainers import "github.com/docker/docker/api/types/mount" var mountTypeMapping = map[MountType]mount.Type{ - MountTypeBind: mount.TypeBind, MountTypeVolume: mount.TypeVolume, MountTypeTmpfs: mount.TypeTmpfs, MountTypePipe: mount.TypeNamedPipe, } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments // BindMounter can optionally be implemented by mount sources // to support advanced scenarios based on mount.BindOptions type BindMounter interface { @@ -27,6 +27,7 @@ type TmpfsMounter interface { GetTmpfsOptions() *mount.TmpfsOptions } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments type DockerBindMountSource struct { *mount.BindOptions @@ -35,14 +36,17 @@ type DockerBindMountSource struct { HostPath string } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments func (s DockerBindMountSource) Source() string { return s.HostPath } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments func (DockerBindMountSource) Type() MountType { return MountTypeBind } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments func (s DockerBindMountSource) GetBindOptions() *mount.BindOptions { return s.BindOptions } @@ -99,12 +103,12 @@ func mapToDockerMounts(containerMounts ContainerMounts) []mount.Mount { } switch typedMounter := m.Source.(type) { - case BindMounter: - containerMount.BindOptions = typedMounter.GetBindOptions() case VolumeMounter: containerMount.VolumeOptions = typedMounter.GetVolumeOptions() case TmpfsMounter: containerMount.TmpfsOptions = typedMounter.GetTmpfsOptions() + default: + Logger.Printf("Mount type %s is not supported by Testcontainers for Go", m.Source.Type()) } mounts = append(mounts, containerMount) diff --git a/docker_test.go b/docker_test.go index e66dfdaf76..6d234cd4c7 100644 --- a/docker_test.go +++ b/docker_test.go @@ -18,7 +18,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" - "github.com/docker/docker/api/types/volume" "github.com/docker/docker/errdefs" "github.com/docker/go-units" "github.com/stretchr/testify/assert" @@ -142,8 +141,13 @@ func TestContainerWithHostNetworkOptions(t *testing.T) { gcr := GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: nginxAlpineImage, - Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), + Image: nginxAlpineImage, + Files: []ContainerFile{ + { + HostFilePath: absPath, + ContainerFilePath: "/etc/nginx/conf.d/default.conf", + }, + }, ExposedPorts: []string{ nginxHighPort, }, @@ -258,7 +262,12 @@ func TestContainerWithHostNetwork(t *testing.T) { ContainerRequest: ContainerRequest{ Image: nginxAlpineImage, WaitingFor: wait.ForListeningPort(nginxHighPort), - Mounts: Mounts(BindMount(absPath, "/etc/nginx/conf.d/default.conf")), + Files: []ContainerFile{ + { + HostFilePath: absPath, + ContainerFilePath: "/etc/nginx/conf.d/default.conf", + }, + }, HostConfigModifier: func(hc *container.HostConfig) { hc.NetworkMode = "host" }, @@ -1187,43 +1196,29 @@ func ExampleContainer_MappedPort() { // } } -func TestContainerCreationWithBindAndVolume(t *testing.T) { +func TestContainerCreationWithVolumeAndFileWritingToIt(t *testing.T) { absPath, err := filepath.Abs(filepath.Join(".", "testdata", "hello.sh")) if err != nil { t.Fatal(err) } ctx, cnl := context.WithTimeout(context.Background(), 30*time.Second) defer cnl() - // Create a Docker client. - dockerCli, err := NewDockerClientWithOpts(context.Background()) - if err != nil { - t.Fatal(err) - } // Create the volume. - vol, err := dockerCli.VolumeCreate(ctx, volume.CreateOptions{ - Driver: "local", - }) - if err != nil { - t.Fatal(err) - } - volumeName := vol.Name - t.Cleanup(func() { - ctx, cnl := context.WithTimeout(context.Background(), 5*time.Second) - defer cnl() - defer dockerCli.Close() + volumeName := "volumeName" - err := dockerCli.VolumeRemove(ctx, volumeName, true) - if err != nil { - t.Fatal(err) - } - }) // Create the container that writes into the mounted volume. bashC, err := GenericContainer(ctx, GenericContainerRequest{ ProviderType: providerType, ContainerRequest: ContainerRequest{ - Image: "docker.io/bash", - Mounts: Mounts(BindMount(absPath, "/hello.sh"), VolumeMount(volumeName, "/data")), + Image: "docker.io/bash", + Files: []ContainerFile{ + { + HostFilePath: absPath, + ContainerFilePath: "/hello.sh", + }, + }, + Mounts: Mounts(VolumeMount(volumeName, "/data")), Cmd: []string{"bash", "/hello.sh"}, WaitingFor: wait.ForLog("done"), }, diff --git a/docs/features/copy_file.md b/docs/features/copy_file.md deleted file mode 100644 index 351667af03..0000000000 --- a/docs/features/copy_file.md +++ /dev/null @@ -1,113 +0,0 @@ -# Copy Files To Container - -If you would like to copy a file to a container, you can do it using the `CopyFileToContainer` method... - -```go -ctx := context.Background() - -nginxC, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ - Image: "nginx:1.17.6", - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), - }, - Started: true, - }) - -nginxC.CopyFileToContainer(ctx, "./testdata/hello.sh", "/hello_copy.sh", 0o700) -``` - -Or you can add a list of files in the `ContainerRequest` initialization, which can be copied before the container starts: - -```go -ctx := context.Background() - -nginxC, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ - Image: "nginx:1.17.6", - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), - Files: []ContainerFile{ - { - HostFilePath: "./testdata/hello.sh", - ContainerFilePath: "/copies-hello.sh", - FileMode: 0o700, - }, - }, - }, - Started: false, - }) -``` - -## Copy Directories To Container - -It's also possible to copy an entire directory to a container, and that can happen before and/or after the container gets into the "Running" state. As an example, you could need to bulk-copy a set of files, such as a configuration directory that does not exist in the underlying Docker image. - -It's important to notice that, when copying the directory to the container, the container path must exist in the Docker image. And this is a strong requirement for files to be copied _before_ the container is started, as we cannot create the full path at that time. - -There are two ways to copy directories to a container. The first way uses the existing `CopyFileToContainer` method, which will internally check if the host path is a directory, internally calling the new `CopyDirToContainer` method if needed: - -```go -ctx := context.Background() - -// copy a directory before the container is started, using Files field -nginxC, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ - Image: "nginx:1.17.6", - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), - Files: []ContainerFile{ - { - HostFilePath: "./testdata", // a directory - ContainerFilePath: "/tmp/testdata", // important! its parent already exists - FileMode: 0o700, - }, - }, - }, - Started: true, - }) -if err != nil { - // handle error -} - -// as the container is started, we can create the directory first -_, _, err = nginxC.Exec(ctx, []string{"mkdir", "-p", "/usr/lib/my-software/config"}) -// because the container path is a directory, it will use the copy dir method as fallback -err = nginxC.CopyFileToContainer(ctx, "./files", "/usr/lib/my-software/config/files", 0o700) -if err != nil { - // handle error -} -``` - -And the second way uses the `CopyDirToContainer` method which, as you probably know, needs the existence of the parent directory in order to copy the directory: - -```go -ctx := context.Background() - -// copy a directory before the container is started, using Files field -nginxC, err := GenericContainer(ctx, GenericContainerRequest{ - ContainerRequest: ContainerRequest{ - Image: "nginx:1.17.6", - ExposedPorts: []string{"80/tcp"}, - WaitingFor: wait.ForListeningPort("80/tcp"), - Files: []ContainerFile{ - { - HostFilePath: "./testdata", // a directory - ContainerFilePath: "/tmp/testdata", // important! its parent already exists - FileMode: 0o700, - }, - }, - }, - Started: true, - }) -if err != nil { - // handle error -} - -// as the container is started, we can create the directory first -_, _, err = nginxC.Exec(ctx, []string{"mkdir", "-p", "/usr/lib/my-software/config"}) -err = nginxC.CopyDirToContainer(ctx, "./plugins", "/usr/lib/my-software/config/plugins", 0o700) -if err != nil { - // handle error -} -``` diff --git a/docs/features/files_and_mounts.md b/docs/features/files_and_mounts.md index 12adae3081..9c0b3711d2 100644 --- a/docs/features/files_and_mounts.md +++ b/docs/features/files_and_mounts.md @@ -1,20 +1,96 @@ -# Files and volumes +# Copying data into a container -## File mapping - -It is possible to map a file or directory from your FileSystem into the container as a volume using the `Mounts` attribute at the container request struct: - - -[Bind mounts](../../docker_auth_test.go) inside_block:bindMounts - +Copying data of any type into a container is a very common practice when working with containers. This section will show you how to do it using _Testcontainers for Go_. ## Volume mapping -It is also possible to map a volume from your Docker host into the container using the `Mounts` attribute at the container request struct: +It is possible to map a Docker volume into the container using the `Mounts` attribute at the `ContainerRequest` struct. For that, please pass an instance of the `GenericVolumeMountSource` type, which allows you to specify the name of the volume to be mapped, and the path inside the container where it should be mounted: [Volume mounts](../../mounts_test.go) inside_block:volumeMounts !!!tip - This ability of creating volumes is also available for remote Docker hosts. + This ability of creating volumes is also available for remote Docker hosts. + +!!!warning + Bind mounts are not supported, as it could not work with remote Docker hosts. + +## Copying files to a container + +If you would like to copy a file to a container, you can do it in two different manners: + +1. Adding a list of files in the `ContainerRequest`, which will be copied before the container starts: + +```go +ctx := context.Background() + +nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + ContainerRequest: ContainerRequest{ + Image: "nginx:1.17.6", + ExposedPorts: []string{"80/tcp"}, + WaitingFor: wait.ForListeningPort("80/tcp"), + Files: []ContainerFile{ + { + HostFilePath: "./testdata/hello.sh", + ContainerFilePath: "/copies-hello.sh", + FileMode: 0o700, + }, + }, + }, + Started: false, + }) +``` + +2. Using the `CopyFileToContainer` method on a `running` container: + +```go +ctx := context.Background() + +nginxC, err := GenericContainer(ctx, GenericContainerRequest{ + ContainerRequest: ContainerRequest{ + Image: "nginx:1.17.6", + ExposedPorts: []string{"80/tcp"}, + WaitingFor: wait.ForListeningPort("80/tcp"), + }, + Started: true, + }) + +nginxC.CopyFileToContainer(ctx, "./testdata/hello.sh", "/hello_copy.sh", 0o700) +``` + +## Copying directories to a container + +It's also possible to copy an entire directory to a container, and that can happen before and/or after the container gets into the `Running` state. As an example, you could need to bulk-copy a set of files, such as a configuration directory that does not exist in the underlying Docker image. + +It's important to notice that, when copying the directory to the container, the container path must exist in the Docker image. And this is a strong requirement for files to be copied _before_ the container is started, as we cannot create the full path at that time. + +You can leverage the very same mechanism used for copying files to a container, but for directories.: + +1. The first way is using the `Files` field in the `ContainerRequest` struct, as shown in the previous section, but using the path of a directory as `HostFilePath`. + +2. The second way uses the existing `CopyFileToContainer` method, which will internally check if the host path is a directory, calling the `CopyDirToContainer` method if needed: + +```go +ctx := context.Background() +// as the container is started, we can create the directory first +_, _, err = myContainer.Exec(ctx, []string{"mkdir", "-p", "/usr/lib/my-software/config"}) +// because the container path is a directory, it will use the copy dir method as fallback +err = myContainer.CopyFileToContainer(ctx, "./files", "/usr/lib/my-software/config/files", 0o700) +if err != nil { + // handle error +} +``` + +3. The last third way uses the `CopyDirToContainer` method, directly, which, as you probably know, needs the existence of the parent directory in order to copy the directory: + +```go +ctx := context.Background() + +// as the container is started, we can create the directory first +_, _, err = nginxC.Exec(ctx, []string{"mkdir", "-p", "/usr/lib/my-software/config"}) +err = nginxC.CopyDirToContainer(ctx, "./plugins", "/usr/lib/my-software/config/plugins", 0o700) +if err != nil { + // handle error +} +``` diff --git a/lifecycle_test.go b/lifecycle_test.go index 8676cb43d6..6d3de161f5 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -35,10 +35,10 @@ func TestPreCreateModifierHook(t *testing.T) { }, Mounts: ContainerMounts{ { - Source: DockerBindMountSource{ - HostPath: "/var/lib/app/data", - BindOptions: &mount.BindOptions{ - Propagation: mount.PropagationPrivate, + Source: DockerVolumeMountSource{ + Name: "appdata", + VolumeOptions: &mount.VolumeOptions{ + Labels: GenericLabels(), }, }, Target: "/data", @@ -90,11 +90,11 @@ func TestPreCreateModifierHook(t *testing.T) { t, []mount.Mount{ { - Type: mount.TypeBind, - Source: "/var/lib/app/data", + Type: mount.TypeVolume, + Source: "appdata", Target: "/data", - BindOptions: &mount.BindOptions{ - Propagation: mount.PropagationPrivate, + VolumeOptions: &mount.VolumeOptions{ + Labels: GenericLabels(), }, }, }, diff --git a/mkdocs.yml b/mkdocs.yml index a849b9d8df..5524330a4a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,7 +51,6 @@ nav: - features/docker_compose.md - features/follow_logs.md - features/override_container_command.md - - features/copy_file.md - Wait Strategies: - Introduction: features/wait/introduction.md - Exec: features/wait/exec.md diff --git a/mounts.go b/mounts.go index 85fcfa8b63..a68e468b39 100644 --- a/mounts.go +++ b/mounts.go @@ -1,14 +1,21 @@ package testcontainers +import "errors" + const ( - MountTypeBind MountType = iota + MountTypeBind MountType = iota // Deprecated: Use MountTypeVolume instead MountTypeVolume MountTypeTmpfs MountTypePipe ) var ( - _ ContainerMountSource = (*GenericBindMountSource)(nil) + ErrDuplicateMountTarget = errors.New("duplicate mount target detected") + ErrInvalidBindMount = errors.New("invalid bind mount") +) + +var ( + _ ContainerMountSource = (*GenericBindMountSource)(nil) // Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments _ ContainerMountSource = (*GenericVolumeMountSource)(nil) _ ContainerMountSource = (*GenericTmpfsMountSource)(nil) ) @@ -30,6 +37,7 @@ type ContainerMountSource interface { Type() MountType } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments // GenericBindMountSource implements ContainerMountSource and represents a bind mount // Optionally mount.BindOptions might be added for advanced scenarios type GenericBindMountSource struct { @@ -38,10 +46,12 @@ type GenericBindMountSource struct { HostPath string } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments func (s GenericBindMountSource) Source() string { return s.HostPath } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments func (GenericBindMountSource) Type() MountType { return MountTypeBind } @@ -81,6 +91,7 @@ func (t ContainerMountTarget) Target() string { return string(t) } +// Deprecated: use Files or HostConfigModifier in the ContainerRequest, or copy files container APIs to make containers portable across Docker environments // BindMount returns a new ContainerMount with a GenericBindMountSource as source // This is a convenience method to cover typical use cases. func BindMount(hostPath string, mountTarget ContainerMountTarget) ContainerMount { @@ -106,7 +117,7 @@ func Mounts(mounts ...ContainerMount) ContainerMounts { // ContainerMount models a mount into a container type ContainerMount struct { - // Source is typically either a GenericBindMountSource or a GenericVolumeMountSource + // Source is typically either a GenericVolumeMountSource, as BindMount is not supported by all Docker environments Source ContainerMountSource // Target is the path where the mount should be mounted within the container Target ContainerMountTarget diff --git a/mounts_test.go b/mounts_test.go index 26ce4f6ed5..1dd1bbf646 100644 --- a/mounts_test.go +++ b/mounts_test.go @@ -6,45 +6,8 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/stretchr/testify/assert" - - "github.com/testcontainers/testcontainers-go/internal/testcontainersdocker" ) -func TestBindMount(t *testing.T) { - t.Parallel() - - dockerSocket := testcontainersdocker.ExtractDockerSocket(context.Background()) - t.Log("Docker Socket Path: ", dockerSocket) - - type args struct { - hostPath string - mountTarget ContainerMountTarget - } - tests := []struct { - name string - args args - want ContainerMount - }{ - { - name: dockerSocket + ":" + dockerSocket, - args: args{hostPath: dockerSocket, mountTarget: "/var/run/docker.sock"}, - want: ContainerMount{Source: GenericBindMountSource{HostPath: dockerSocket}, Target: "/var/run/docker.sock"}, - }, - { - name: "/var/lib/app/data:/data", - args: args{hostPath: "/var/lib/app/data", mountTarget: "/data"}, - want: ContainerMount{Source: GenericBindMountSource{HostPath: "/var/lib/app/data"}, Target: "/data"}, - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - assert.Equalf(t, tt.want, BindMount(tt.args.hostPath, tt.args.mountTarget), "BindMount(%v, %v)", tt.args.hostPath, tt.args.mountTarget) - }) - } -} - func TestVolumeMount(t *testing.T) { t.Parallel() type args struct { @@ -88,53 +51,6 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { mounts: nil, want: make([]mount.Mount, 0), }, - { - name: "Single bind mount", - mounts: ContainerMounts{{Source: GenericBindMountSource{HostPath: "/var/lib/app/data"}, Target: "/data"}}, - want: []mount.Mount{ - { - Type: mount.TypeBind, - Source: "/var/lib/app/data", - Target: "/data", - }, - }, - }, - { - name: "Single bind mount - read-only", - mounts: ContainerMounts{{Source: GenericBindMountSource{HostPath: "/var/lib/app/data"}, Target: "/data", ReadOnly: true}}, - want: []mount.Mount{ - { - Type: mount.TypeBind, - Source: "/var/lib/app/data", - Target: "/data", - ReadOnly: true, - }, - }, - }, - { - name: "Single bind mount - with options", - mounts: ContainerMounts{ - { - Source: DockerBindMountSource{ - HostPath: "/var/lib/app/data", - BindOptions: &mount.BindOptions{ - Propagation: mount.PropagationPrivate, - }, - }, - Target: "/data", - }, - }, - want: []mount.Mount{ - { - Type: mount.TypeBind, - Source: "/var/lib/app/data", - Target: "/data", - BindOptions: &mount.BindOptions{ - Propagation: mount.PropagationPrivate, - }, - }, - }, - }, { name: "Single volume mount", mounts: ContainerMounts{{Source: GenericVolumeMountSource{Name: "app-data"}, Target: "/data"}}, diff --git a/reaper.go b/reaper.go index dda10c281e..3521a8827d 100644 --- a/reaper.go +++ b/reaper.go @@ -225,13 +225,13 @@ func newReaper(ctx context.Context, sessionID string, provider ReaperProvider, o Image: reaperImage(reaperOpts.ImageName), ExposedPorts: []string{string(listeningPort)}, Labels: testcontainersdocker.DefaultLabels(sessionID), - Mounts: Mounts(BindMount(dockerHostMount, "/var/run/docker.sock")), Privileged: tcConfig.RyukPrivileged, WaitingFor: wait.ForListeningPort(listeningPort), Name: reaperContainerNameFromSessionID(sessionID), ReaperOptions: opts, HostConfigModifier: func(hc *container.HostConfig) { hc.AutoRemove = true + hc.Binds = []string{dockerHostMount + ":/var/run/docker.sock"} hc.NetworkMode = Bridge }, Env: map[string]string{}, diff --git a/reaper_test.go b/reaper_test.go index 22572b375a..a1c91dbe5b 100644 --- a/reaper_test.go +++ b/reaper_test.go @@ -89,8 +89,10 @@ func createContainerRequest(customize func(ContainerRequest) ContainerRequest) C ReaperImage: "reaperImage", ExposedPorts: []string{"8080/tcp"}, Labels: testcontainersdocker.DefaultLabels(testSessionID), - Mounts: Mounts(BindMount(testcontainersdocker.ExtractDockerSocket(context.Background()), "/var/run/docker.sock")), - WaitingFor: wait.ForListeningPort(nat.Port("8080/tcp")), + HostConfigModifier: func(hostConfig *container.HostConfig) { + hostConfig.Binds = []string{testcontainersdocker.ExtractDockerSocket(context.Background()) + ":/var/run/docker.sock"} + }, + WaitingFor: wait.ForListeningPort(nat.Port("8080/tcp")), ReaperOptions: []ContainerOption{ WithImageName("reaperImage"), }, @@ -355,7 +357,9 @@ func Test_NewReaper(t *testing.T) { { name: "docker-host in context", req: createContainerRequest(func(req ContainerRequest) ContainerRequest { - req.Mounts = Mounts(BindMount(testcontainersdocker.ExtractDockerSocket(context.Background()), "/var/run/docker.sock")) + req.HostConfigModifier = func(hostConfig *container.HostConfig) { + hostConfig.Binds = []string{testcontainersdocker.ExtractDockerSocket(context.Background()) + ":/var/run/docker.sock"} + } return req }), config: TestcontainersConfig{Config: config.Config{