Adapt net/wait-for-it as wait/wait-for-(port|path) (#78)

* Adapt net/wait-for-it as wait/wait-for-(port|path)

* Add createTempFile

* Expose as File/createTempFile
This commit is contained in:
sogaiu 2019-10-14 18:24:37 +09:00 committed by Michiel Borkent
parent e9949cf229
commit 22dd4484fa
6 changed files with 154 additions and 72 deletions

View file

@ -154,15 +154,21 @@ namespaces. If not all vars are available, they are enumerated explicitly.
From Java the following is available:
- `Integer`: `Integer/parseInt`
- `File`: `.canRead`, `.canWrite`, `.delete`, `.deleteOnExit`, `.exists`,
`.getAbsoluteFile`, `.getCanonicalFile`, `.getCanonicalPath`, `.getName`,
`.getParent`, `.getParentFile`, `.getPath`, `.isAbsolute`, `.isDirectory`,
`.isFile`, `.isHidden`, `.lastModified`, `.length`, `.list`, `.listFiles`,
`.mkdir`, `.mkdirs`, `.renameTo`, `.setLastModified`, `.setReadOnly`,
`.setReadable`, `.toPath`, `.toURI`.
- `System`: `exit`, `getProperty`, `setProperty`, `getProperties`, `getenv`
- `Thread`: `sleep`
- `Integer`:
- static methods: `parseInt`
- `File`:
- static methods: `createTempFile`
- instance methods: `.canRead`, `.canWrite`, `.delete`,
`.deleteOnExit`, `.exists`, `.getAbsoluteFile`, `.getCanonicalFile`,
`.getCanonicalPath`, `.getName`, `.getParent`, `.getParentFile`,
`.getPath`, `.isAbsolute`, `.isDirectory`, `.isFile`, `.isHidden`,
`.lastModified`, `.length`, `.list`, `.listFiles`, `.mkdir`,
`.mkdirs`, `.renameTo`, `.setLastModified`, `.setReadOnly`,
`.setReadable`, `.toPath`, `.toURI`.
- `System`:
- static methods: `exit`, `getProperty`, `setProperty`, `getProperties`, `getenv`
- `Thread`:
- static methods: `sleep`
Special vars:
@ -172,16 +178,23 @@ text with the `-i` option, or multiple EDN values with the `-I` option.
Additionally, babashka adds the following functions:
- `net/wait-for-it`. Usage:
- `wait/wait-for-port`. Usage:
``` clojure
(net/wait-for-it "localhost" 8080)
(net/wait-for-it "localhost" 8080 {:timeout 1000 :pause 1000)
(wait/wait-for-port "localhost" 8080)
(wait/wait-for-port "localhost" 8080 {:timeout 1000 :pause 1000})
```
Waits for TCP connection to be available on host and port. Options map supports
`:timeout` and `:pause`. If `:timeout` is provided and reached, exception will
be thrown. The `:pause` option determines the time waited between retries.
Waits for TCP connection to be available on host and port. Options map supports `:timeout` and `:pause`. If `:timeout` is provided and reached, `:default`'s value (if any) is returned. The `:pause` option determines the time waited between retries.
- `wait/wait-for-path`. Usage:
``` clojure
(wait/wait-for-path "/tmp/wait-path-test")
(wait/wait-for-path "/tmp/wait-path-test" {:timeout 1000 :pause 1000})
```
Waits for file path to be available. Options map supports `:default`, `:timeout` and `:pause`. If `:timeout` is provided and reached, `:default`'s value (if any) is returned. The `:pause` option determines the time waited between retries.
- `sig/pipe-signal-received?`. Usage:
@ -446,13 +459,10 @@ You need [Leiningen](https://leiningen.org/), and for building binaries you need
### Test
Test on the JVM:
Test on the JVM (for development):
script/test
Although this tool doesn't offer any benefit when running on the JVM, it is
convenient for development.
Test the native version:
BABASHKA_TEST_ENV=native script/test

View file

@ -20,6 +20,11 @@
(gen-wrapper-fn canExecute)
(gen-wrapper-fn canRead)
(gen-wrapper-fn canWrite)
(defn ^:bb/export createTempFile
([^String prefix ^String suffix]
(java.io.File/createTempFile prefix suffix))
([^String prefix ^String suffix ^java.io.File dir]
(java.io.File/createTempFile prefix suffix dir)))
(gen-wrapper-fn delete)
(gen-wrapper-fn deleteOnExit)
(gen-wrapper-fn exists)
@ -58,13 +63,16 @@
(gen-wrapper-fn toURI)
(def file-bindings
(reduce (fn [acc [k v]]
(if (-> v meta :bb/export)
(assoc acc (symbol (str "." k))
@v)
acc))
{}
(ns-publics *ns*)))
(-> (reduce (fn [acc [k v]]
(if (-> v meta :bb/export)
(assoc acc (symbol (str "." k))
@v)
acc))
{}
(ns-publics *ns*))
;; static method
(dissoc (symbol ".createTempFile"))
(assoc (symbol "File/createTempFile") createTempFile)))
(comment
(canRead (clojure.java.io/file "README.md"))

View file

@ -14,7 +14,7 @@
[babashka.impl.socket-repl :as socket-repl]
[babashka.impl.tools.cli :refer [tools-cli-namespace]]
[babashka.impl.exceptions :refer [exception-bindings]]
[babashka.net :as net]
[babashka.wait :as wait]
[clojure.edn :as edn]
[clojure.java.io :as io]
[clojure.java.shell :as shell]
@ -192,7 +192,7 @@ Everything after that is bound to *command-line-args*."))
env (atom {})
ctx {:aliases '{tools.cli 'clojure.tools.cli
edn clojure.edn
net babashka.net
wait babashka.wait
sig babashka.signal
shell clojure.java.shell
io clojure.java.io
@ -201,7 +201,8 @@ Everything after that is bound to *command-line-args*."))
:namespaces {'clojure.tools.cli tools-cli-namespace
'clojure.edn {'read-string edn/read-string}
'clojure.java.shell {'sh shell/sh}
'babashka.net {'wait-for-it net/wait-for-it}
'babashka.wait {'wait-for-port wait/wait-for-port
'wait-for-path wait/wait-for-path}
'babashka.signal {'pipe-signal-received? pipe-signal-received?}
'clojure.java.io io-namespace
'me.raynes.conch.low-level conch-namespace

View file

@ -1,37 +0,0 @@
(ns babashka.net
(:import [java.net Socket ConnectException]))
(set! *warn-on-reflection* true)
(defn wait-for-it
"Waits for TCP connection to be available on host and port. Options map
supports `:timeout` and `:pause`. If `:timeout` is provided and reached,
exception will be thrown. The `:pause` option determines the time waited
between retries."
([host port]
(wait-for-it host port nil))
([^String host ^long port {:keys [:timeout :pause] :as opts}]
(let [opts (merge {:host host
:port port}
opts)
t0 (System/currentTimeMillis)]
(loop []
(let [v (try (Socket. host port)
(- (System/currentTimeMillis) t0)
(catch ConnectException _e
(let [took (- (System/currentTimeMillis) t0)]
(if (and timeout (>= took timeout))
(throw (ex-info
(format "timeout while waiting for %s:%s" host port)
(assoc opts :took took)))
:wait-for-it.impl/try-again))))]
(if (identical? :wait-for-it.impl/try-again v)
(do (Thread/sleep (or pause 100))
(recur))
(assoc opts :took v)))))))
(comment
(wait-for-it "localhost" 80)
(wait-for-it "localhost" 80 {:timeout 1000})
(wait-for-it "google.com" 80)
)

69
src/babashka/wait.clj Normal file
View file

@ -0,0 +1,69 @@
(ns babashka.wait
(:require [clojure.java.io :as io])
(:import [java.net Socket ConnectException]))
(set! *warn-on-reflection* true)
(defn wait-for-port
"Waits for TCP connection to be available on host and port. Options map
supports `:timeout` and `:pause`. If `:timeout` is provided and reached,
`:default`'s value (if any) is returned. The `:pause` option determines
the time waited between retries."
([host port]
(wait-for-port host port nil))
([^String host ^long port {:keys [:default :timeout :pause] :as opts}]
(let [opts (merge {:host host
:port port}
opts)
t0 (System/currentTimeMillis)]
(loop []
(let [v (try (Socket. host port)
(- (System/currentTimeMillis) t0)
(catch ConnectException _e
(let [took (- (System/currentTimeMillis) t0)]
(if (and timeout (>= took timeout))
:wait-for-port.impl/timed-out
:wait-for-port.impl/try-again))))]
(cond (identical? :wait-for-port.impl/try-again v)
(do (Thread/sleep (or pause 100))
(recur))
(identical? :wait-for-port.impl/timed-out v)
default
:else
(assoc opts :took v)))))))
(defn wait-for-path
"Waits for file path to be available. Options map supports `:default`,
`:timeout` and `:pause`. If `:timeout` is provided and reached, `:default`'s
value (if any) is returned. The `:pause` option determines the time waited
between retries."
([path]
(wait-for-path path nil))
([^String path {:keys [:default :timeout :pause] :as opts}]
(let [opts (merge {:path path}
opts)
t0 (System/currentTimeMillis)]
(loop []
(let [v (when (not (.exists (io/file path)))
(let [took (- (System/currentTimeMillis) t0)]
(if (and timeout (>= took timeout))
:wait-for-path.impl/timed-out
:wait-for-path.impl/try-again)))]
(cond (identical? :wait-for-path.impl/try-again v)
(do (Thread/sleep (or pause 100))
(recur))
(identical? :wait-for-path.impl/timed-out v)
default
:else
(assoc opts :took
(- (System/currentTimeMillis) t0))))))))
(comment
(wait-for-port "localhost" 80)
(wait-for-port "localhost" 80 {:timeout 1000})
(wait-for-port "google.com" 80)
(wait-for-path "/tmp/hi")
(wait-for-path "/tmp/there" {:timeout 1000})
)

View file

@ -168,14 +168,45 @@
(is (str/includes? (bb nil "(->> (conch/proc \"ls\") (conch/stream-to-string :out))")
"LICENSE")))
(deftest wait-for-it-test
(is (thrown-with-msg?
Exception
#"timeout"
(deftest create-temp-file-test
(let [temp-dir-path (System/getProperty "java.io.tmpdir")]
(is (= true
(bb nil (format "(let [tdir (io/file \"%s\")
tfile
(File/createTempFile \"ctf\" \"tmp\" tdir)]
(.deleteOnExit tfile) ; for cleanup
(.exists tfile))"
temp-dir-path))))))
(deftest wait-for-port-test
(is (= :timed-out
(bb nil "(def web-server (conch/proc \"python\" \"-m\" \"SimpleHTTPServer\" \"7171\"))
(net/wait-for-it \"127.0.0.1\" 7171)
(wait/wait-for-port \"127.0.0.1\" 7171)
(conch/destroy web-server)
(net/wait-for-it \"localhost\" 7172 {:timeout 50})"))))
(wait/wait-for-port \"localhost\" 7172 {:default :timed-out :timeout 50})"))))
(deftest wait-for-path-test
(let [temp-dir-path (System/getProperty "java.io.tmpdir")]
(is (not= :timed-out
(bb nil (format "(let [tdir (io/file \"%s\")
tfile
(File/createTempFile \"wfp\" \"tmp\" tdir)
tpath (.getPath tfile)]
(.delete tfile) ; delete now, but re-create it in a future
(future (Thread/sleep 50) (shell/sh \"touch\" tpath))
(wait/wait-for-path tpath
{:default :timed-out :timeout 100})
(.delete tfile))"
temp-dir-path))))
(is (= :timed-out
(bb nil (format "(let [tdir (io/file \"%s\")
tfile
(File/createTempFile \"wfp-to\" \"tmp\" tdir)
tpath (.getPath tfile)]
(.delete tfile) ; for timing out test and cleanup
(wait/wait-for-path tpath
{:default :timed-out :timeout 100}))"
temp-dir-path))))))
(deftest async-test
(is (= "process 2\n" (test-utils/bb nil "