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:
parent
e9949cf229
commit
22dd4484fa
6 changed files with 154 additions and 72 deletions
48
README.md
48
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
69
src/babashka/wait.clj
Normal 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})
|
||||
|
||||
)
|
||||
|
|
@ -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 "
|
||||
|
|
|
|||
Loading…
Reference in a new issue