Skip to content

Commit

Permalink
Support 'repeat' in steps
Browse files Browse the repository at this point in the history
Allows retrying exec/run commands until they succeed. API for 'repeat'
inspired by docker compose's 'healthcheck' options.

Supplying 'repeat' without setting 'retries' will retry indefinitely.
  • Loading branch information
jonsmock committed May 9, 2024
1 parent c82d193 commit c783c9c
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 17 deletions.
1 change: 1 addition & 0 deletions examples/00-intro.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ tests:
FOO: bar
run: |
[ "${FOO}" == "bar" ]
repeat: { retries: 3, interval: '1s' }

test-2:
name: Failing test
Expand Down
147 changes: 147 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"license": "SEE LICENSE IN LICENSE",
"description": "docker-compose integration test runner",
"dependencies": {
"@lonocloud/cljs-utils": "0.1.3",
"dockerode": "^3.3.1",
"ebnf": "1.9.1",
"js-yaml": "^4.1.0",
Expand Down
4 changes: 3 additions & 1 deletion scripts/nbb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ die() { echo >&2 "${*}"; exit 1; }

[ -e "${NBB}" ] || die "Missing ${NBB}. Maybe run 'npm install' in ${ROOT_DIR}?"

NODE_PATH="${ROOT_DIR}/node_modules" exec ${NBB} -cp "${ROOT_DIR}/src:${ROOT_DIR}/test" "${TARGET_DIR}/${TARGET_NAME}.cljs" "${@}"
NODE_PATH="${ROOT_DIR}/node_modules" exec ${NBB} \
-cp "${ROOT_DIR}/src:${ROOT_DIR}/test:${ROOT_DIR}/node_modules/@lonocloud/cljs-utils/src" \
"${TARGET_DIR}/${TARGET_NAME}.cljs" "${@}"
2 changes: 1 addition & 1 deletion shadow-cljs.edn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{:source-paths ["src/" "test/"]
{:source-paths ["src/" "test/" "node_modules/@lonocloud/cljs-utils/src"]

:dependencies [[funcool/promesa "8.0.450"]
[cljs-bean "1.8.0"]]
Expand Down
56 changes: 41 additions & 15 deletions src/dctest/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
;; Licensed under EPL 2.0

(ns dctest.core
(:require [clojure.string :as S]
(:require [clojure.edn :as edn]
[clojure.string :as S]
[clojure.pprint :refer [pprint]]
[promesa.core :as P]
[cljs-bean.core :refer [->clj]]
[viasat.retry :as retry]
["fs" :as fs]
["util" :refer [promisify]]
["stream" :as stream]
Expand Down Expand Up @@ -70,6 +72,7 @@ Options:
;; Docker/Compose

(def WAIT-EXEC-SLEEP 200)
(def INTERVAL-RE #"^\s*((\d+)m)?((\d+)s)?\s*$") ; 1m20s

(defn wait-exec
"[Async] Wait for docker exec to complete and when complete resolve
Expand Down Expand Up @@ -165,26 +168,49 @@ Options:
(update :tests #(mapv load-test %)))]
suite))

;; TODO: Support more than exec :-)
(defn parse-interval [time-str]
(let [[_ _ minutes _ seconds] (re-matches INTERVAL-RE time-str)
minutes (edn/read-string (or minutes "0"))
seconds (edn/read-string (or seconds "0"))]
(-> (* minutes 60)
(+ seconds)
(* 1000))))

(defn execute-step* [context step]
(P/let [{:keys [docker opts]} context
{:keys [project]} opts
{service :exec index :index command :run} step
env (merge (:env context) (:env step))
index (or index 1)
max-retries (if (:repeat step)
(get-in step [:repeat :retries])
0)
interval (get-in step [:repeat :interval] "1s")
env (merge (:env context) (:env step))

container (dc-service docker project service index)
_ (when-not container
(throw (ex-info (str "No container found for service '" service "' (index=" index ")")
{})))

env (mapv (fn [[k v]] (str k "=" v)) env)
exec (P/then (docker-exec container command {:Env env})
->clj)]

(when-not (zero? (:ExitCode exec))
(throw (ex-info (str "Non-zero exit code for command: " command)
{})))
run-expect! (fn [result]
(when-not (zero? (:ExitCode result))
(throw (ex-info (str "Non-zero exit code for command: " command)
{}))))
run-exec (fn []
(P/catch
(P/let [container (dc-service docker project service index)
_ (when-not container
(throw (ex-info (str "No container found for service '" service "' (index=" index ")")
{})))
env (mapv (fn [[k v]] (str k "=" v)) env)
result (P/then (docker-exec container command {:Env env})
->clj)]
(run-expect! result)
{:result result})
(fn [err]
{:error (.-message err)})))
exec (retry/retry-times run-exec
max-retries
{:delay-ms (parse-interval interval)
:check-fn :result})]

(when-let [msg (:error exec)]
(throw (ex-info msg {})))

context))

Expand Down

0 comments on commit c783c9c

Please sign in to comment.