diff --git a/CHANGELOG.md b/CHANGELOG.md index bef8aaf..fd9da89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # Change Log All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). +## [0.6.0] - 2022-03-26 +### Changed +- [#55](https://github.com/javahippie/clj-test-containers/issues/55): Upgrade to latest Testcontainers version + +### Added +- [#42](https://github.com/javahippie/clj-test-containers/issues/42): Extend wait strategies + ## [0.5.0] - 2021-08-18 ### Changed - [#49](https://github.com/javahippie/clj-test-containers/issues/49): Updated to latest Testcontainers version diff --git a/README.md b/README.md index eb1b36a..b026cb4 100644 --- a/README.md +++ b/README.md @@ -68,16 +68,16 @@ Creates a testcontainers instance from a given Docker label and returns them #### Config parameters: -| Key | Type | Description | -| ------------- | :------------- | :----- | -| `: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 | -| `: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 to use and the condition to check for | -| `:log-to` | Map | A map containing the log strategy to use, e.g. {:log-strategy string} | +| Key | Type | Description | +| ------------- | :------------- |:----------------------------------------------------------------------------------------------------| +| `: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 | +| `: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](docs/wait-strategies.md) to use and the condition to check for | +| `:log-to` | Map | A map containing the [log strategy](docs/log-strategies.md) to use, e.g. {:log-strategy string} | #### Result: @@ -123,22 +123,22 @@ 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 | -| `:wait-for` | Map | A map containing the wait strategy to use and the condition to check for | -| `:log-to` | Map | A map containing the log strategy to use, e.g. {:log-strategy string} | -| | | | +| 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 | +| `:wait-for` | Map | A map containing the [wait strategy](docs/wait-strategies.md) to use and the condition to check for | +| `:log-to` | Map | A map containing the [log strategy](docs/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 | | `:env-vars` | Map | Value of the same input parameter | @@ -185,8 +185,8 @@ Creates a testcontainer from a Dockerfile | `: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 to use and the condition to check for | -| `:log-to` | Map | A map containing the log strategy to use, e.g. {:log-strategy string} | +| `:wait-for` | Map | A map containing the [wait strategy](docs/wait-strategies.md) to use and the condition to check for | +| `:log-to` | Map | A map containing the [log strategy](docs/log-strategies.md) to use, e.g. {:log-strategy string} | | | | | #### Result: diff --git a/doc/log-strategies.md b/doc/log-strategies.md new file mode 100644 index 0000000..c443d64 --- /dev/null +++ b/doc/log-strategies.md @@ -0,0 +1,34 @@ +# Log strategies + +This library offers two ways to access the logs of the running container: The :string strategy and the :fn strategy. + +## String Strategy + +The `:string` strategy sets up a function in the returned map, under the `string-log` key. This function enables the +dumping of the logs when passed to the `dump-logs` function. + +Example: + +```clojure +{:log-strategy :string} +``` + +Then, later in your program, you can access the logs thus: + +```clojure +(def container-config (tc/start! container)) +(tc/dump-logs container-config) +``` + +## Function Strategy + +The `:fn` strategy accepts an additional parameter `:function` in the configuration map, which allows you to pass a +function to the Testcontainers log mechanism which accepts a single String parameter and gets called for every log line. +This way you can pass the container logging on to the logging library of your choice. + +Example: + +```clojure +{:log-strategy :fn + :function (fn [log-line] (println "From Container: " log-line)} +``` \ No newline at end of file diff --git a/project.clj b/project.clj index 21b648e..d45f89b 100644 --- a/project.clj +++ b/project.clj @@ -23,7 +23,8 @@ [orchestra "2021.01.01-1"] [org.clojure/tools.namespace "1.2.0"] [org.testcontainers/postgresql "1.16.3"] - [com.fzakaria/slf4j-timbre "0.3.21"]] + [com.fzakaria/slf4j-timbre "0.3.21"] + [nrepl "0.9.0"]] :source-paths ["dev-src"]}} :target-path "target/%s") diff --git a/src/clj_test_containers/core.clj b/src/clj_test_containers/core.clj index 616855d..535a7b0 100644 --- a/src/clj_test_containers/core.clj +++ b/src/clj_test_containers/core.clj @@ -13,6 +13,8 @@ GenericContainer Network) (org.testcontainers.containers.output + BaseConsumer + OutputFrame ToStringConsumer) (org.testcontainers.containers.wait.strategy Wait) @@ -34,64 +36,64 @@ (defmulti wait "Sets a wait strategy to the container. Supports :http, :health and :log as - strategies. + strategies. - ## HTTP Strategy - The :http strategy will only accept the container as initialized if it can be - accessed via HTTP. It accepts a path, a port, a vector of status codes, a - boolean that specifies if TLS is enabled, a read timeout in seconds and a map - with basic credentials, containing username and password. Only the path is - required, all others are optional. + ## HTTP Strategy + The :http strategy will only accept the container as initialized if it can be + accessed via HTTP. It accepts a path, a port, a vector of status codes, a + boolean that specifies if TLS is enabled, a read timeout in seconds and a map + with basic credentials, containing username and password. Only the path is + required, all others are optional. - Example: + Example: - ```clojure - (wait {:wait-strategy :http - :port 80 - :path \"/\" - :status-codes [200 201] - :tls true - :read-timeout 5 - :basic-credentials {:username \"user\" - :password \"password\" - :startup-timeout 60}} - container) - ``` + ```clojure + (wait {:wait-strategy :http + :port 80 + :path \"/\" + :status-codes [200 201] + :tls true + :read-timeout 5 + :basic-credentials {:username \"user\" + :password \"password\" + :startup-timeout 60}} + container) + ``` - ## Health Strategy - The :health strategy enables support for Docker's healthcheck feature, - whereby you can directly leverage the healthy state of your container as your wait condition. + ## Health Strategy + The :health strategy enables support for Docker's healthcheck feature, + whereby you can directly leverage the healthy state of your container as your wait condition. - Example: + Example: - ```clojure - (wait {:wait-strategy :health - :startup-timeout 60} container) - ``` + ```clojure + (wait {:wait-strategy :health + :startup-timeout 60} container) + ``` - ## Log Strategy - The :log strategy accepts a message which simply causes the output of your - container's log to be used in determining if the container is ready or not. - The output is `grepped` against the log message. + ## Log Strategy + The :log strategy accepts a message which simply causes the output of your + container's log to be used in determining if the container is ready or not. + The output is `grepped` against the log message. - Example: + Example: - ```clojure - (wait {:wait-strategy :log - :message \"accept connections\" - :startup-timeout 60} container) - ``` + ```clojure + (wait {:wait-strategy :log + :message \"accept connections\" + :startup-timeout 60} container) + ``` - ## Port Strategy - The port strategy waits for the first of the mapped ports to be opened. It only accepts the startup-timeout - value as a parameter. + ## Port Strategy + The port strategy waits for the first of the mapped ports to be opened. It only accepts the startup-timeout + value as a parameter. - Example: + Example: - ```clojure - (wait {:wait-strategy :port - :startup-timeout 60} container - ```" + ```clojure + (wait {:wait-strategy :port + :startup-timeout 60} container + ```" :wait-strategy) (defmethod wait :http @@ -278,27 +280,38 @@ :stderr (.getStderr result)})) (defmulti log - "Sets a log strategy on the container as a means of accessing the container - logs. It currently only supports a :string as the strategy to use. + "Sets a log strategy on the container as a means of accessing the container logs. - ## String Strategy - The :string strategy sets up a function in the returned map, under the - `string-log` key. This function enables the dumping of the logs when passed to - the `dump-logs` function. + ## String Strategy + The `:string` strategy sets up a function in the returned map, under the + `string-log` key. This function enables the dumping of the logs when passed to + the `dump-logs` function. - Example: + Example: - ```clojure - {:log-strategy :string} - ``` + ```clojure + {:log-strategy :string} + ``` - Then, later in your program, you can access the logs thus: + Then, later in your program, you can access the logs thus: - ```clojure - (def container-config (tc/start! container)) - (tc/dump-logs container-config) - ``` - " + ```clojure + (def container-config (tc/start! container)) + (tc/dump-logs container-config) + ``` + + ## Function Strategy + The `:fn` strategy accepts an additional parameter `:function` in the configuration + map, which allows you to pass a function to the Testcontainers log mechanism + which accepts a single String parameter and gets called for every log line. This + way you can pass the container logging on to the logging library of your choice. + + Example: + ```clojure + {:log-strategy :fn + :function (fn [log-line] (println \"From Container: \" log-line)} + ``` + " :log-strategy) (defmethod log :string @@ -309,9 +322,12 @@ (-> (.toUtf8String to-string-consumer) (clojure.string/replace #"\n+" "\n")))})) -(defmethod log :slf4j [_ _] nil) ; Not yet implemented +(defmethod log :fn [{:keys [function]} ^GenericContainer container] + (.followOutput container (proxy [BaseConsumer] [] + (^void accept [^OutputFrame frame] + (function (.getUtf8String frame)))))) -(defmethod log :default [_ _] nil) ; Not yet implemented +(defmethod log :default [_ _] nil) (defn dump-logs "Dumps the logs found by invoking the function on the :string-log key" diff --git a/test/clj_test_containers/core_test.clj b/test/clj_test_containers/core_test.clj index 8580555..fce9b79 100644 --- a/test/clj_test_containers/core_test.clj +++ b/test/clj_test_containers/core_test.clj @@ -20,13 +20,20 @@ (is (nil? (:id stopped-container))) (is (nil? (:mapped-ports stopped-container))))) - (testing "Testing log access to the container" + (testing "Testing log access to the container with string logs" (let [container (sut/init {:container (PostgreSQLContainer. "postgres:12.2") :log-to {:log-strategy :string}}) initialized-container (sut/start! container)] (Thread/sleep 500) (is (includes? (sut/dump-logs initialized-container) "database system is ready to accept connections")))) + (testing "Testing log access to the container with function logs" + (let [logs (atom [])] + (sut/start! (sut/init {:container (PostgreSQLContainer. "postgres:12.2") + :log-to {:log-strategy :fn + :function #(swap! logs conj %)}})) + (is (filter #(includes? "database system is ready to accept connections" %) @logs)))) + (testing "Testing basic testcontainer generic image initialisation with wait for log message" (let [container (sut/create {:image-name "postgres:12.2" :exposed-ports [5432]