Add type hints to silence reflection warnings (#38)

* Run lein check on CI

* Add type hints as appropriate; readability cleanups
This commit is contained in:
Rob Hanlon 2020-10-23 12:07:43 -07:00 committed by GitHub
parent 167fa385a2
commit dac2b1ba4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 114 additions and 90 deletions

View file

@ -36,6 +36,9 @@ jobs:
- ~/.m2 - ~/.m2
key: v1-dependencies-{{ checksum "project.clj" }} key: v1-dependencies-{{ checksum "project.clj" }}
# check syntax and warn on reflection
- run: lein check
# run tests! # run tests!
- run: lein test - run: lein test

View file

@ -20,20 +20,22 @@
MountableFile))) MountableFile)))
(defn- resolve-bind-mode (defn- resolve-bind-mode
[bind-mode] (^BindMode [bind-mode]
(if (= :read-write bind-mode) (if (= :read-write bind-mode)
BindMode/READ_WRITE BindMode/READ_WRITE
BindMode/READ_ONLY)) BindMode/READ_ONLY)))
(defmulti wait (defmulti wait
"Sets a wait strategy to the container. "Sets a wait strategy to the container. Supports :http, :health and :log as
Supports :http, :health and :log as strategies. strategies.
## HTTP Strategy ## HTTP Strategy
The :http strategy will only accept the container as initialized if it can be accessed The :http strategy will only accept the container as initialized if it can be
via HTTP. It accepts a path, a port, a vector of status codes, a boolean that specifies accessed via HTTP. It accepts a path, a port, a vector of status codes, a
if TLS is enabled, a read timeout in seconds and a map with basic credentials, containing boolean that specifies if TLS is enabled, a read timeout in seconds and a map
username and password. Only the path is required, all others are optional. with basic credentials, containing username and password. Only the path is
required, all others are optional.
Example: Example:
```clojure ```clojure
@ -47,10 +49,12 @@
:password \"password\"}} :password \"password\"}}
container) container)
``` ```
## Health Strategy ## Health Strategy
The :health strategy only accepts a true or false value. This enables support for Docker's The :health strategy only accepts a true or false value. This enables support
healthcheck feature, whereby you can directly leverage the healthy state of your container for Docker's healthcheck feature, whereby you can directly leverage the
as your wait condition. healthy state of your container as your wait condition.
Example: Example:
```clojure ```clojure
@ -58,9 +62,10 @@
``` ```
## Log Strategy ## Log Strategy
The :log strategy accepts a message which simply causes the output of your container's log The :log strategy accepts a message which simply causes the output of your
to be used in determining if the container is ready or not. The output is `grepped` against container's log to be used in determining if the container is ready or not.
the log message. The output is `grepped` against the log message.
Example: Example:
```clojure ```clojure
@ -70,7 +75,13 @@
:wait-strategy) :wait-strategy)
(defmethod wait :http (defmethod wait :http
[{:keys [path port status-codes tls read-timeout basic-credentials] :as options} container] [{:keys [path
port
status-codes
tls
read-timeout
basic-credentials] :as options}
^GenericContainer container]
(let [for-http (Wait/forHttp path)] (let [for-http (Wait/forHttp path)]
(when port (when port
(.forPort for-http port)) (.forPort for-http port))
@ -82,23 +93,23 @@
(.usingTls for-http)) (.usingTls for-http))
(when read-timeout (when read-timeout
(.withReadTimeout (java.time.Duration/ofSeconds read-timeout))) (.withReadTimeout for-http (java.time.Duration/ofSeconds read-timeout)))
(when basic-credentials (when basic-credentials
(let [{username :username password :password} basic-credentials] (let [{username :username password :password} basic-credentials]
(.withBasicCredentials username password))) (.withBasicCredentials for-http username password)))
(.waitingFor container for-http) (.waitingFor container for-http)
{:wait-for-http (dissoc options :strategy)})) {:wait-for-http (dissoc options :strategy)}))
(defmethod wait :health (defmethod wait :health
[_ container] [_ ^GenericContainer container]
(.waitingFor container (Wait/forHealthcheck)) (.waitingFor container (Wait/forHealthcheck))
{:wait-for-healthcheck true}) {:wait-for-healthcheck true})
(defmethod wait :log (defmethod wait :log
[{:keys [message]} container] [{:keys [message]} ^GenericContainer container]
(let [log-message (str ".*" message ".*\\n")] (let [log-message (str ".*" message ".*\\n")]
(.waitingFor container (Wait/forLogMessage log-message 1)) (.waitingFor container (Wait/forLogMessage log-message 1))
{:wait-for-log-message log-message})) {:wait-for-log-message log-message}))
@ -111,20 +122,27 @@
(defn init (defn init
"Sets the properties for a testcontainer instance" "Sets the properties for a testcontainer instance"
[{:keys [container exposed-ports env-vars command network network-aliases wait-for] :as init-options}] [{:keys [^GenericContainer container
exposed-ports
env-vars
command
network
network-aliases
wait-for] :as init-options}]
(.setExposedPorts container (map int exposed-ports)) (.setExposedPorts container (map int exposed-ports))
(run! (fn [[k v]] (.addEnv container k v)) env-vars) (doseq [[k v] env-vars]
(.addEnv container k v))
(when command (when command
(.setCommand container (into-array String command))) (.setCommand container ^"[Ljava.lang.String;" (into-array String command)))
(when network (when network
(.setNetwork container (:network network))) (.setNetwork container (:network network)))
(when network-aliases (when network-aliases
(.setNetworkAliases container (java.util.ArrayList. network-aliases))) (.setNetworkAliases container network-aliases))
(merge init-options {:container container (merge init-options {:container container
:exposed-ports (vec (.getExposedPorts container)) :exposed-ports (vec (.getExposedPorts container))
@ -139,89 +157,91 @@
(defn create (defn create
"Creates a generic testcontainer and sets its properties" "Creates a generic testcontainer and sets its properties"
[{:keys [image-name] :as options}] [{:keys [image-name] :as options}]
(->> (GenericContainer. image-name) (->> (GenericContainer. ^String image-name)
(assoc options :container) (assoc options :container)
init)) init))
(defn create-from-docker-file (defn create-from-docker-file
"Creates a testcontainer from a provided Dockerfile" "Creates a testcontainer from a provided Dockerfile"
[{:keys [docker-file] :as options}] [{:keys [docker-file] :as options}]
(->> (.withDockerfile (ImageFromDockerfile.) (Paths/get "." (into-array [docker-file]))) (->> (.withDockerfile (ImageFromDockerfile.)
(Paths/get "." (into-array [docker-file])))
(GenericContainer.) (GenericContainer.)
(assoc options :container) (assoc options :container)
init)) init))
(defn map-classpath-resource! (defn map-classpath-resource!
"Maps a resource in the classpath to the given container path. Should be called before starting the container!" "Maps a resource in the classpath to the given container path. Should be
[container-config called before starting the container!"
{:keys [resource-path container-path mode]}] [{:keys [^GenericContainer container] :as container-config}
(assoc container-config :container (.withClasspathResourceMapping (:container container-config) {:keys [^String resource-path ^String container-path mode]}]
resource-path (assoc container-config
container-path :container
(resolve-bind-mode mode)))) (.withClasspathResourceMapping container
resource-path
container-path
(resolve-bind-mode mode))))
(defn bind-filesystem! (defn bind-filesystem!
"Binds a source from the filesystem to the given container path. Should be called before starting the container!" "Binds a source from the filesystem to the given container path. Should be
[container-config {:keys [host-path container-path mode]}] called before starting the container!"
[{:keys [^GenericContainer container] :as container-config}
{:keys [^String host-path ^String container-path mode]}]
(assoc container-config (assoc container-config
:container (.withFileSystemBind (:container container-config) :container
host-path (.withFileSystemBind container
container-path host-path
(resolve-bind-mode mode)))) container-path
(resolve-bind-mode mode))))
(defn copy-file-to-container! (defn copy-file-to-container!
"Copies a file into the running container" "Copies a file into the running container"
[container-config [{:keys [^GenericContainer container] :as container-config}
{:keys [container-path path type]}] {:keys [^String container-path ^String path type]}]
(let [mountable-file (cond (let [^MountableFile mountable-file
(= :classpath-resource type) (case type
(MountableFile/forClasspathResource path) :classpath-resource (MountableFile/forClasspathResource path)
:host-path (MountableFile/forHostPath path))]
(= :host-path type)
(MountableFile/forHostPath path)
:else
:error)]
(assoc container-config (assoc container-config
:container :container
(.withCopyFileToContainer (:container container-config) (.withCopyFileToContainer container
mountable-file mountable-file
container-path)))) container-path))))
(defn execute-command! (defn execute-command!
"Executes a command in the container, and returns the result" "Executes a command in the container, and returns the result"
[container-config command] [{:keys [^GenericContainer container]} command]
(let [container (:container container-config) (let [result (.execInContainer container (into-array command))]
result (.execInContainer container
(into-array command))]
{:exit-code (.getExitCode result) {:exit-code (.getExitCode result)
:stdout (.getStdout result) :stdout (.getStdout result)
:stderr (.getStderr result)})) :stderr (.getStderr result)}))
(defmulti log (defmulti log
"Sets a log strategy on the container as a means of accessing the container logs. "Sets a log strategy on the container as a means of accessing the container
It currently only supports a :string as the strategy to use. logs. It currently only supports a :string as the strategy to use.
## String Strategy ## String Strategy
The :string strategy sets up a function in the returned map, under the `string-log` The :string strategy sets up a function in the returned map, under the
key. This function enables the dumping of the logs when passed to the `dump-logs` `string-log` key. This function enables the dumping of the logs when passed to
function. the `dump-logs` function.
Example:
```clojure Example:
{:log-strategy :string}
```
Then, later in your program, you can access the logs thus: ```clojure
{:log-strategy :string}
```
```clojure Then, later in your program, you can access the logs thus:
(def container-config (tc/start! container))
(tc/dump-logs container-config) ```clojure
``` (def container-config (tc/start! container))
(tc/dump-logs container-config)
```
" "
:log-strategy) :log-strategy)
(defmethod log :string (defmethod log :string
[_ container] [_ ^GenericContainer container]
(let [to-string-consumer (ToStringConsumer.)] (let [to-string-consumer (ToStringConsumer.)]
(.followOutput container to-string-consumer) (.followOutput container to-string-consumer)
{:string-log (fn [] {:string-log (fn []
@ -238,26 +258,26 @@
((:string-log container-config))) ((:string-log container-config)))
(defn start! (defn start!
"Starts the underlying testcontainer instance and adds new values to the response map, e.g. :id and :first-mapped-port" "Starts the underlying testcontainer instance and adds new values to the
[container-config] response map, e.g. :id and :first-mapped-port"
(let [{:keys [container log-to]} container-config] [{:keys [^GenericContainer container
(.start container) log-to
(-> (merge container-config exposed-ports] :as container-config}]
{:id (.getContainerId container) (.start container)
:mapped-ports (into {} (let [map-port (fn map-port
(map (fn [port] [port (.getMappedPort container port)]) [port]
(:exposed-ports container-config)))} [port (.getMappedPort container port)])
mapped-ports (into {} (map map-port) exposed-ports)]
(-> container-config
(merge {:id (.getContainerId container) :mapped-ports mapped-ports}
(log log-to container)) (log log-to container))
(dissoc :log-to)))) (dissoc :log-to))))
(defn stop! (defn stop!
"Stops the underlying container" "Stops the underlying container"
[container-config] [{:keys [^GenericContainer container] :as container-config}]
(.stop (:container container-config)) (.stop container)
(-> container-config (dissoc container-config :id :string-log :mapped-ports))
(dissoc :id)
(dissoc :string-log)
(dissoc :mapped-ports)))
(s/fdef create-network (s/fdef create-network
:args (s/alt :nullary (s/cat) :args (s/alt :nullary (s/cat)
@ -266,7 +286,8 @@
:ret ::cs/network) :ret ::cs/network)
(defn create-network (defn create-network
"Creates a network. The optional map accepts config values for enabling ipv6 and setting the driver" "Creates a network. The optional map accepts config values for enabling ipv6
and setting the driver"
([] ([]
(create-network {})) (create-network {}))
([{:keys [ipv6 driver]}] ([{:keys [ipv6 driver]}]

View file

@ -8,7 +8,7 @@
(s/def ::container (s/def ::container
(s/with-gen #(instance? GenericContainer %) (s/with-gen #(instance? GenericContainer %)
#(gen/fmap (fn [image-name] (GenericContainer. image-name)) #(gen/fmap (fn [^String image-name] (GenericContainer. image-name))
(gen/string-alphanumeric)))) (gen/string-alphanumeric))))
(s/def ::exposed-ports (s/def ::exposed-ports