From bb66e6d409937ea24a21fd0e54d5d402cd011105 Mon Sep 17 00:00:00 2001 From: Nony Dutton Date: Fri, 8 Dec 2023 09:50:14 +0100 Subject: [PATCH] Allow for optionally passing options to `.remove` The `Docker::Container` `remove` method in [docker-api conditionally accepts a hash of options](https://github.com/upserve/docker-api/blob/6f7b7cd2b790ec0ca69f805d72ae0c504c8b2f49/lib/docker/container.rb#L248) Furthermore, removing a docker container, by default, will [not remove the volumes](https://docs.docker.com/engine/api/v1.43/#tag/Container/operation/ContainerDelete) unless you pass `v: true` as a parameter. Without the ability to pass `v: true` we can end up with many dangling volumes that take up disk space. This commit adds an optional hash argument to the `DockerContainer` `#remove` method, it defaults to an empty hash. To assist with tests I've also added: - `DockerContainer#mounts` method which returns an array of the mount hashes from the container `#info` - `DockerContainer#mount_names` which returns an array of the mount names (ids) --- core/lib/testcontainers/docker_container.rb | 23 +++++++++++++++++++-- core/test/docker_container_test.rb | 22 +++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/core/lib/testcontainers/docker_container.rb b/core/lib/testcontainers/docker_container.rb index be6e9fb..e1a1154 100644 --- a/core/lib/testcontainers/docker_container.rb +++ b/core/lib/testcontainers/docker_container.rb @@ -530,11 +530,12 @@ def kill(signal: "SIGKILL") # Removes the container. # + # @param options [Hash] Additional options to send to the container remove command. # @return [DockerContainer] The DockerContainer instance. # @return [nil] If the container does not exist. # @raise [ConnectionError] If the connection to the Docker daemon fails. - def remove - @_container&.remove + def remove(options = {}) + @_container&.remove(options) @_container = nil self rescue Excon::Error::Socket => e @@ -732,6 +733,24 @@ def first_mapped_port container_ports.map { |port| mapped_port(port) }.first end + # Returns the container's mounts. + # + # @return [Array] An array of the container's mounts. + # @raise [ConnectionError] If the connection to the Docker daemon fails. + # @raise [ContainerNotStartedError] If the container has not been started. + def mounts + info["Mounts"] + end + + # Returns the container's mount names. + # + # @return [Array] The container's mount names. + # @raise [ConnectionError] If the connection to the Docker daemon fails. + # @raise [ContainerNotStartedError] If the container has not been started. + def mount_names + mounts.map { |mount| mount["Name"] } + end + # Returns the value for the given environment variable. # # @param key [String] The environment variable's key. diff --git a/core/test/docker_container_test.rb b/core/test/docker_container_test.rb index b535756..3481cb0 100644 --- a/core/test/docker_container_test.rb +++ b/core/test/docker_container_test.rb @@ -82,9 +82,16 @@ def test_it_returns_the_container_port_bindings assert_equal({"80/tcp" => [{"HostPort" => "8080"}], "8080/tcp" => [{"HostPort" => "8081"}], "443/tcp" => [{"HostPort" => "443"}]}, container.port_bindings) end - def test_it_returns_the_container_volumes + def test_it_creates_and_returns_the_container_volumes container = Testcontainers::DockerContainer.new("hello-world", volumes: ["/tmp"]) + container.start + mount_name = container.mount_names.first + + assert_equal mount_name, Docker::Volume.get(mount_name).id assert_equal({"/tmp" => {}}, container.volumes) + ensure + container.stop if container.exists? && container.running? + container.remove({v: true}) if container.exists? end def test_it_returns_the_container_labels @@ -271,6 +278,19 @@ def test_it_removes_a_container container.remove if container.exists? end + def test_it_removes_a_container_and_its_volumes + container = Testcontainers::DockerContainer.new("hello-world", volumes: ["/tmp"]) + container.start + mount_name = container.mount_names.first + container.remove({v: true}) + + refute container.exists? + assert_raises(Docker::Error::NotFoundError) { Docker::Volume.get(mount_name) } + ensure + container.stop if container.exists? && container.running? + container.remove({v: true}) if container.exists? + end + def test_it_restarts_a_container @long_running_container.start @long_running_container.restart