From 0e2d483715d63429278c1c9287a11e1e366b2d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Z=C3=B6ller?= Date: Sun, 14 Apr 2024 13:54:00 +0200 Subject: [PATCH] Added :reuse flag (#72) Included a new flag for the container reuse feature --- README.md | 47 ++++++++++++++++------- src/testcontainers_clj/core.clj | 9 ++++- src/testcontainers_clj/spec/container.clj | 3 ++ src/testcontainers_clj/spec/core.clj | 4 +- test/testcontainers_clj/core_test.clj | 14 ++++++- 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 0ad5d06..87ae16f 100644 --- a/README.md +++ b/README.md @@ -68,19 +68,21 @@ Creates a testcontainers instance from a given Docker label and returns them | ------------- | :------------- |:----------------------------------------------------------------------------------------------------| | `:image-name` | String, mandatory | The name and label of an image, e.g. `postgres:12.2` | | `:exposed-ports` | Vector with ints, mandatory | All ports which should be exposed and mapped to a local port | +| `:reuse` | Boolean | Should the container be reused, if another Testcontainer with identical config is started? | | `:env-vars` | Map | A map with environment variables | | `:command` | Vector with strings | The start command of the container | | `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) | | `:network-aliases` | Map | A list of alias names for the container on the network | -| `:wait-for` | Map | A map containing the [wait strategy](doc/wait-strategies.md) to use and the condition to check for | -| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} | +| `:wait-for` | Map | A map containing the [wait strategy](doc/wait-strategies.md) to use and the condition to check for | +| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} | #### Result: | Key | Type | Description | -| ------------- | :------------- | :----- | +|------------------|:------------------------------------------|:------------------------------------------------------------------------------------------| | `:container` | `org.testcontainers.containers.Container` | The Testcontainers instance, accessible for everything this library doesn't provide (yet) | | `:exposed-ports` | Vector with ints | Value of the same input parameter | +| `:reuse` | Boolean | Is this container reusable? | | `:env-vars` | Map | Value of the same input parameter | | `:host` | String | The host for the Docker Container | | `:network` | Map | The network configuration of the Container, if provided | @@ -99,6 +101,20 @@ Creates a testcontainers instance from a given Docker label and returns them "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"]}) ``` +#### Example with reuse + +```clojure +(create {:image-name "alpine:3.2" + :exposed-ports [80] + :reuse true + :env-vars {"MAGIC_NUMBER" "42"} + :network (create-network) + :network-aliases ["api-server"] + :command ["/bin/sh" + "-c" + "while true; do echo \"$MAGIC_NUMBER\" | nc -l -p 80; done"]}) +``` + #### Example using wait-for and healthcheck: ```clojure @@ -119,17 +135,18 @@ Initializes a given Testcontainer, which was e.g. provided by a library #### Config parameters: -| Key | Type | Description | -| ------------- | :------------- |:----------------------------------------------------------------------------------------------------| -| `:container` | `org.testcontainers.containers.GenericContainer`, mandatory | The name and label of an image, e.g. `postgres:12.2` | -| `:exposed-ports` | Vector with ints, mandatory | All ports which should be exposed and mapped to a local port | -| `:env-vars` | Map | A map with environment variables | -| `:command` | Vector with strings | The start command of the container | -| `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) | -| `:network-aliases` | Map | A list of alias names for the container on the network | +| Key | Type | Description | +|--------------------|:------------------------------------------------------------|:---------------------------------------------------------------------------------------------------| +| `:container` | `org.testcontainers.containers.GenericContainer`, mandatory | The name and label of an image, e.g. `postgres:12.2` | +| `:exposed-ports` | Vector with ints, mandatory | All ports which should be exposed and mapped to a local port | +| `:reuse` | Boolean | Should the container be reused, if another Testcontainer with identical config is started? | +| `:env-vars` | Map | A map with environment variables | +| `:command` | Vector with strings | The start command of the container | +| `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) | +| `:network-aliases` | Map | A list of alias names for the container on the network | | `:wait-for` | Map | A map containing the [wait strategy](doc/wait-strategies.md) to use and the condition to check for | -| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} | -| | | | +| `:log-to` | Map | A map containing the [log strategy](doc/log-strategies.md) to use, e.g. {:log-strategy string} | +| | | | #### Result: @@ -137,6 +154,7 @@ Initializes a given Testcontainer, which was e.g. provided by a library | ------------- | :------------- |:------------------------------------------------------------------------------------------| | `:container` | `org.testcontainers.containers.Container` | The Testcontainers instance, accessible for everything this library doesn't provide (yet) | | `:exposed-ports` | Vector with ints | Value of the same input parameter | +| `:reuse` | Boolean | Is this container reusable? | | `:env-vars` | Map | Value of the same input parameter | | `:host` | String | The host for the Docker Container | | `:network` | Map | The network configuration of the Container, if provided | @@ -177,6 +195,7 @@ Creates a testcontainer from a Dockerfile | ------------- | :------------- | :----- | | `:docker-file` | String, mandatory | String containing a path to a Dockerfile | | `:exposed-ports` | Vector with ints, mandatory | All ports which should be exposed and mapped to a local port | +| `:reuse` | Boolean | Should the container be reused, if another Testcontainer with identical config is started? | | `:env-vars` | Map | A map with environment variables | | `:command` | Vector with strings | The start command of the container | | `:network` | Map | A map containing the configuration of a Docker Network (see: `create-network`) | @@ -191,6 +210,7 @@ Creates a testcontainer from a Dockerfile | ------------- | :------------- | :----- | | `:container` | `org.testcontainers.containers.Container` | The Testcontainers instance, accessible for everything this library doesn't provide (yet) | | `:exposed-ports` | Vector with ints | Value of the same input parameter | +| `:reuse` | Boolean | Is this container reusable? | | `:env-vars` | Map | Value of the same input parameter | | `:host` | String | The host for the Docker Container | | `:network` | Map | The network configuration of the Container, if provided | @@ -227,6 +247,7 @@ Starts the Testcontainer, which was defined by `create` | ------------- | :------------- | :----- | | `:container` | `org.testcontainers.containers.Container` | The Testcontainers instance, accessible for everything this library doesn't provide (yet) | | `:exposed-ports` | Vector with ints | Value of the same input parameter | +| `:reuse` | Boolean | Is this container reusable? | | `:env-vars` | Map | Value of the same input parameter | | `:host` | String | The host for the Docker Container | | `:id` | String | The ID of the started docker container | diff --git a/src/testcontainers_clj/core.clj b/src/testcontainers_clj/core.clj index cbb2343..72af6c2 100644 --- a/src/testcontainers_clj/core.clj +++ b/src/testcontainers_clj/core.clj @@ -183,6 +183,7 @@ "Sets the properties for a testcontainer instance" [{:keys [^GenericContainer container exposed-ports + reuse env-vars command network @@ -194,6 +195,9 @@ (doseq [[k v] env-vars] (.addEnv container k v)) + (when reuse + (.withReuse container true)) + (when command (.setCommand container ^"[Ljava.lang.String;" (into-array String command))) @@ -425,5 +429,6 @@ ;; REPL Helpers (comment - (start! (create {:image-name "postgres:12.1"})) - (perform-cleanup!)) + (start! (create {:image-name "postgres:12.1" :reuse true})) + (perform-cleanup!) + ) diff --git a/src/testcontainers_clj/spec/container.clj b/src/testcontainers_clj/spec/container.clj index 1fab7b8..b4cb95e 100644 --- a/src/testcontainers_clj/spec/container.clj +++ b/src/testcontainers_clj/spec/container.clj @@ -14,6 +14,9 @@ (s/def ::exposed-ports (s/coll-of (s/int-in 1 65535))) +(s/def ::reuse + boolean?) + (s/def ::env-vars (s/map-of string? string?)) diff --git a/src/testcontainers_clj/spec/core.clj b/src/testcontainers_clj/spec/core.clj index 45b47fa..cc1098a 100644 --- a/src/testcontainers_clj/spec/core.clj +++ b/src/testcontainers_clj/spec/core.clj @@ -25,13 +25,15 @@ ::csc/exposed-ports ::csc/env-vars ::csc/host] - :opt-un [::network + :opt-un [::csc/reuse + ::network ::wait-for ::log-to])) (s/def ::init-options (s/keys :req-un [::csc/container] :opt-un [::csc/exposed-ports + ::csc/reuse ::csc/env-vars ::csc/command ::network diff --git a/test/testcontainers_clj/core_test.clj b/test/testcontainers_clj/core_test.clj index 1aaa877..239e8f0 100644 --- a/test/testcontainers_clj/core_test.clj +++ b/test/testcontainers_clj/core_test.clj @@ -115,7 +115,19 @@ result (sut/execute-command! initialized-container ["whoami"]) _stopped-container (sut/stop! container)] (is (= 0 (:exit-code result))) - (is (= "root\n" (:stdout result)))))) + (is (= "root\n" (:stdout result))))) + + (testing "Reusing a container with the :reuse flag" + (let [container-1 (sut/init {:container (PostgreSQLContainer. "postgres:15.3") + :exposed-ports [5432] + :reuse true}) + container-2 (sut/init {:container (PostgreSQLContainer. "postgres:15.3") + :exposed-ports [5432] + :reuse true}) + initialized-container-1 (sut/start! container-1) + initialized-container-2 (sut/start! container-2)] + (is (= (.getContainerId (:container initialized-container-1)) + (.getContainerId (:container initialized-container-2))))))) (deftest execute-command-in-container