From d62fcc3960450399a2f41f50ec84a490956b1e87 Mon Sep 17 00:00:00 2001 From: Omar Bouzoumita Date: Thu, 6 Sep 2018 14:38:48 +0100 Subject: [PATCH 01/11] add a backup for the environment variables named `TIMEOUT`, `QUIT`, `PORT`, `HOST` --- wait-for | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wait-for b/wait-for index ddfc39e..aa8188d 100755 --- a/wait-for +++ b/wait-for @@ -1,5 +1,10 @@ #!/bin/sh +OLD_TIMEOUT=$TIMEOUT +OLD_QUIET=$QUIET +OLD_PORT=$PORT +OLD_HOST=$HOST + TIMEOUT=15 QUIET=0 @@ -26,7 +31,7 @@ wait_for() { result=$? if [ $result -eq 0 ] ; then if [ $# -gt 0 ] ; then - exec "$@" + TIMEOUT=$OLD_TIMEOUT QUIET=$OLD_QUIET PORT=$OLD_PORT HOST=$OLD_HOST exec "$@" fi exit 0 fi From ae37d72731b9141fb5bb2cb48d790e4a489a1d8d Mon Sep 17 00:00:00 2001 From: Rahmat Budiharso Date: Wed, 11 Oct 2017 11:32:27 +0700 Subject: [PATCH 02/11] preserve existing environment variable --- wait-for.bats | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/wait-for.bats b/wait-for.bats index cbea6a4..c720419 100644 --- a/wait-for.bats +++ b/wait-for.bats @@ -2,7 +2,7 @@ @test "google should be immediately found" { run ./wait-for google.com:80 -- echo 'success' - + [ "$output" = "success" ] } @@ -12,3 +12,12 @@ [ "$status" -ne 0 ] [ "$output" != "success" ] } + +@test "preserve existing environment variable" { + HOST=myweb.com + PORT=8080 + run ./wait-for google.com:80 -- echo 'success' + + [ "$(echo $HOST)" = 'myweb.com' ] + [ "$(echo $PORT)" = '8080' ] +} From 1e47fb8ae35e95ef3683d3cc1e9b183b048a32c7 Mon Sep 17 00:00:00 2001 From: Wilson Silva Date: Wed, 7 Feb 2018 12:48:49 +0000 Subject: [PATCH 03/11] Fix the silent timeout error when netcat is not installed --- wait-for | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wait-for b/wait-for index aa8188d..6949f3f 100755 --- a/wait-for +++ b/wait-for @@ -8,6 +8,11 @@ OLD_HOST=$HOST TIMEOUT=15 QUIET=0 +if ! which nc >/dev/null; then + echo "Netcat is not installed. This script requires netcat to work correctly." + exit 1 +fi + echoerr() { if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi } From 65664639c80c181e897ceb6e22809b20d4f8aea8 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 7 May 2018 13:15:51 -0300 Subject: [PATCH 04/11] Add strict option --- wait-for | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/wait-for b/wait-for index 6949f3f..d9a9b99 100755 --- a/wait-for +++ b/wait-for @@ -23,6 +23,7 @@ usage() { Usage: $cmdname host:port [-t timeout] [-- command args] -q | --quiet Do not output any status messages + -s | --strict Only execute subcommand if the test succeeds -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout -- COMMAND ARGS Execute command with args after the test finishes USAGE @@ -43,6 +44,12 @@ wait_for() { sleep 1 done echo "Operation timed out" >&2 + if [ $result -ne 0 ] && [ $STRICT -ne 1 ] ; then + if [ $# -gt 0 ] ; then + exec "$@" + fi + exit 0 + fi exit 1 } @@ -58,6 +65,10 @@ do QUIET=1 shift 1 ;; + -s | --strict) + STRICT=1 + shift 1 + ;; -t) TIMEOUT="$2" if [ "$TIMEOUT" = "" ]; then break; fi @@ -86,4 +97,6 @@ if [ "$HOST" = "" -o "$PORT" = "" ]; then usage 2 fi +STRICT=${STRICT:-0} + wait_for "$@" From 40f22acd975f58dede50884b04782e80e144f162 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 7 May 2018 13:41:28 -0300 Subject: [PATCH 05/11] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f1956f7..f169999 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ When using this tool, you only need to pick the `wait-for` file as part of your ``` ./wait-for host:port [-t timeout] [-- command args] -q | --quiet Do not output any status messages + -s | --strict Only execute subcommand if the test succeeds -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout -- COMMAND ARGS Execute command with args after the test finishes ``` @@ -26,6 +27,15 @@ Connection to www.eficode.com port 80 [tcp/http] succeeded! Eficode site is up ``` +The subcommand will be executed regardless if the service is up or not. If you wish to execute the subcommand only if the service is up, add the --strict argument. In this example, we will test port 81 on www.google.com which will fail: + +``` +$ ./wait-for www.eficode.com:80 -- echo "Eficode site is up" +$ ./wait-for www.google.com:81 --timeout=1 --strict -- echo "google is up" +Operation timed out +google is up +``` + To wait for database container to become available: From 4a309ccd13e03f32517f28ce9df595abfc8d9082 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 25 Oct 2018 09:15:03 -0400 Subject: [PATCH 06/11] fix representation of cmd name in usage()/help --- wait-for | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wait-for b/wait-for index d9a9b99..6c892cf 100755 --- a/wait-for +++ b/wait-for @@ -21,7 +21,7 @@ usage() { exitcode="$1" cat << USAGE >&2 Usage: - $cmdname host:port [-t timeout] [-- command args] + $(basename $0) host:port [-t timeout] [-- command args] -q | --quiet Do not output any status messages -s | --strict Only execute subcommand if the test succeeds -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout From d101d613f0f56b2b15bf250e52f008b9c6b94d9d Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 25 Oct 2018 09:18:18 -0400 Subject: [PATCH 07/11] Make nc time out in a known amount of time instead of hanging --- wait-for | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wait-for b/wait-for index 6c892cf..c93c054 100755 --- a/wait-for +++ b/wait-for @@ -32,7 +32,8 @@ USAGE wait_for() { for i in `seq $TIMEOUT` ; do - nc -z "$HOST" "$PORT" > /dev/null 2>&1 + # use a 1-second timeout + nc -w 1 -z "$HOST" "$PORT" > /dev/null 2>&1 result=$? if [ $result -eq 0 ] ; then From f08d8d668996a29d731e6a3a311a0a586ce528a2 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 25 Oct 2018 09:57:06 -0400 Subject: [PATCH 08/11] when on OSX, use all relevant nc timeout options --- wait-for | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/wait-for b/wait-for index c93c054..2ce8535 100755 --- a/wait-for +++ b/wait-for @@ -30,11 +30,21 @@ USAGE exit "$exitcode" } +test_connection() { + # force a 1-second timeout on darwin (https://stackoverflow.com/a/20460402/2063546) + # POSIX-compliant string inclusion test https://stackoverflow.com/a/8811800/2063546 + if [ "${OSTYPE#*darwin*}" != "$OSTYPE" ] ; then + nc -z -w 1 -G 1 "$1" "$2" + else + nc -z -w 1 "$1" "$2" > /dev/null 2>&1 + fi +} + wait_for() { for i in `seq $TIMEOUT` ; do - # use a 1-second timeout - nc -w 1 -z "$HOST" "$PORT" > /dev/null 2>&1 - + # use a 1-second timeout, but still sleep 0.1 seconds after just to be safe + test_connection "$HOST" "$PORT" + result=$? if [ $result -eq 0 ] ; then if [ $# -gt 0 ] ; then From ae407317ac2e5a8dcd96006ffdd586695256fecc Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 25 Oct 2018 10:44:00 -0400 Subject: [PATCH 09/11] Change strict option to loose option to avoid breaking existing behavior --- README.md | 13 ++++++------- wait-for | 30 +++++++++++------------------- wait-for.bats | 26 +++++++++++++++++++++----- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index f169999..ca2d3a9 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ When using this tool, you only need to pick the `wait-for` file as part of your ## Usage ``` -./wait-for host:port [-t timeout] [-- command args] +wait-for host:port [-t timeout] [-- command args] -q | --quiet Do not output any status messages - -s | --strict Only execute subcommand if the test succeeds + -l | --loose Execute subcommand even if the test times out -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout -- COMMAND ARGS Execute command with args after the test finishes ``` @@ -22,18 +22,17 @@ To check if [eficode.com](https://eficode.com) is available: ``` $ ./wait-for www.eficode.com:80 -- echo "Eficode site is up" - -Connection to www.eficode.com port 80 [tcp/http] succeeded! Eficode site is up ``` The subcommand will be executed regardless if the service is up or not. If you wish to execute the subcommand only if the service is up, add the --strict argument. In this example, we will test port 81 on www.google.com which will fail: ``` -$ ./wait-for www.eficode.com:80 -- echo "Eficode site is up" -$ ./wait-for www.google.com:81 --timeout=1 --strict -- echo "google is up" +$ ./wait-for www.google.com:81 --timeout=1 -- echo "google is up" +Operation timed out +$ ./wait-for www.google.com:81 --timeout=1 --loose -- echo "waited for google" Operation timed out -google is up +waited for google ``` To wait for database container to become available: diff --git a/wait-for b/wait-for index 2ce8535..a8f6708 100755 --- a/wait-for +++ b/wait-for @@ -4,9 +4,11 @@ OLD_TIMEOUT=$TIMEOUT OLD_QUIET=$QUIET OLD_PORT=$PORT OLD_HOST=$HOST +OLD_LOOSE=$LOOSE TIMEOUT=15 QUIET=0 +LOOSE=0 if ! which nc >/dev/null; then echo "Netcat is not installed. This script requires netcat to work correctly." @@ -23,7 +25,7 @@ usage() { Usage: $(basename $0) host:port [-t timeout] [-- command args] -q | --quiet Do not output any status messages - -s | --strict Only execute subcommand if the test succeeds + -l | --loose Execute subcommand even if the test times out -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout -- COMMAND ARGS Execute command with args after the test finishes USAGE @@ -41,27 +43,19 @@ test_connection() { } wait_for() { + local result for i in `seq $TIMEOUT` ; do # use a 1-second timeout, but still sleep 0.1 seconds after just to be safe test_connection "$HOST" "$PORT" - result=$? - if [ $result -eq 0 ] ; then - if [ $# -gt 0 ] ; then - TIMEOUT=$OLD_TIMEOUT QUIET=$OLD_QUIET PORT=$OLD_PORT HOST=$OLD_HOST exec "$@" - fi - exit 0 - fi + if [ $result -eq 0 ] ; then break ; fi sleep 1 done - echo "Operation timed out" >&2 - if [ $result -ne 0 ] && [ $STRICT -ne 1 ] ; then - if [ $# -gt 0 ] ; then - exec "$@" - fi - exit 0 + [ $result -ne 0 ] && echo "Operation timed out" >&2 + if [ $result -eq 0 -o $LOOSE -eq 1 -a $# -gt 0 ] ; then + TIMEOUT=$OLD_TIMEOUT QUIET=$OLD_QUIET PORT=$OLD_PORT HOST=$OLD_HOST LOOSE=$OLD_LOOSE exec "$@" fi - exit 1 + exit $result } while [ $# -gt 0 ] @@ -76,8 +70,8 @@ do QUIET=1 shift 1 ;; - -s | --strict) - STRICT=1 + -l | --loose) + LOOSE=1 shift 1 ;; -t) @@ -108,6 +102,4 @@ if [ "$HOST" = "" -o "$PORT" = "" ]; then usage 2 fi -STRICT=${STRICT:-0} - wait_for "$@" diff --git a/wait-for.bats b/wait-for.bats index c720419..fa873ac 100644 --- a/wait-for.bats +++ b/wait-for.bats @@ -13,11 +13,27 @@ [ "$output" != "success" ] } -@test "preserve existing environment variable" { - HOST=myweb.com - PORT=8080 +@test "nonexistent server should start command if loose option is specified" { + run ./wait-for -t 1 -l noserver:9999 -- echo 'passable' 2>&1 + + [ "$status" -eq 0 ] + + [ "${lines[0]}" = "Operation timed out" ] + [ "${lines[1]}" = "passable" ] +} + +@test "preserve existing environment variables" { + TIMEOUT=mytimeout + QUIET=myquiet + HOST=myhost + PORT=myport + LOOSE=myloose + run ./wait-for google.com:80 -- echo 'success' - [ "$(echo $HOST)" = 'myweb.com' ] - [ "$(echo $PORT)" = '8080' ] + [ "$(echo $TIMEOUT)" = 'mytimeout' ] + [ "$(echo $QUIET)" = 'myquiet' ] + [ "$(echo $HOST)" = 'myhost' ] + [ "$(echo $PORT)" = 'myport' ] + [ "$(echo $LOOSE)" = 'myloose' ] } From 9102543a4e2287ab5a779f5b79ca0f842936307d Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Thu, 25 Oct 2018 11:37:08 -0400 Subject: [PATCH 10/11] use npm to test via node --- .travis.yml | 4 ++++ Dockerfile | 3 ++- package.json | 2 +- run_tests.sh | 8 ++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100755 run_tests.sh diff --git a/.travis.yml b/.travis.yml index 392b52e..e8a4731 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,8 @@ services: before_install: - docker build -t eficode/wait-for . + +script: + - npm install + - ./run_tests.sh - docker run eficode/wait-for diff --git a/Dockerfile b/Dockerfile index 412d8e1..f636844 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,4 +8,5 @@ WORKDIR /app COPY . /app RUN npm install -CMD ./node_modules/.bin/bats wait-for.bats +# On launch, run the test suite via npm +CMD npm test diff --git a/package.json b/package.json index 977ac4a..7aea0e7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "wait-for", "version": "0.1.0", "scripts": { - "test": "./node_modules/.bin/bats wait-for.bats" + "test": "./run_tests.sh" }, "dependencies": { "bats": "^0.4.2" diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..7bc5257 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Although it would be possible to just call this directly from the Dockerfile, +# centralizing tests in this file allows both the docker container and the +# CI machine to run the same set of tests for an additional datapoint -- +# which gives a better chance of turning up POSIX noncompliance + +./node_modules/.bin/bats wait-for.bats From 96511d65c6578d4866591283fdec6e2fba7e6770 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Wed, 31 Oct 2018 14:37:52 -0400 Subject: [PATCH 11/11] add more verbosity if not in quiet mode, enforce it if we are --- wait-for | 16 +++++++++++++--- wait-for.bats | 9 ++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/wait-for b/wait-for index a8f6708..3b2241c 100755 --- a/wait-for +++ b/wait-for @@ -19,6 +19,14 @@ echoerr() { if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi } +conditionally_output() { + if [ "$QUIET" -ne 1 ]; then + "$@" + else + "$@" > /dev/null 2>&1 + fi +} + usage() { exitcode="$1" cat << USAGE >&2 @@ -33,12 +41,14 @@ USAGE } test_connection() { + conditionally_output echo "Testing connection to $1:$2..." + # force a 1-second timeout on darwin (https://stackoverflow.com/a/20460402/2063546) # POSIX-compliant string inclusion test https://stackoverflow.com/a/8811800/2063546 if [ "${OSTYPE#*darwin*}" != "$OSTYPE" ] ; then - nc -z -w 1 -G 1 "$1" "$2" + conditionally_output nc -z -w 1 -G 1 "$1" "$2" else - nc -z -w 1 "$1" "$2" > /dev/null 2>&1 + conditionally_output nc -z -w 1 "$1" "$2" fi } @@ -51,7 +61,7 @@ wait_for() { if [ $result -eq 0 ] ; then break ; fi sleep 1 done - [ $result -ne 0 ] && echo "Operation timed out" >&2 + [ $result -ne 0 ] && echoerr "Operation timed out" if [ $result -eq 0 -o $LOOSE -eq 1 -a $# -gt 0 ] ; then TIMEOUT=$OLD_TIMEOUT QUIET=$OLD_QUIET PORT=$OLD_PORT HOST=$OLD_HOST LOOSE=$OLD_LOOSE exec "$@" fi diff --git a/wait-for.bats b/wait-for.bats index fa873ac..a95f889 100644 --- a/wait-for.bats +++ b/wait-for.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bats -@test "google should be immediately found" { - run ./wait-for google.com:80 -- echo 'success' +@test "google should be immediately found, no output other than our own" { + run ./wait-for -q google.com:80 -- echo 'success' [ "$output" = "success" ] } @@ -14,12 +14,11 @@ } @test "nonexistent server should start command if loose option is specified" { - run ./wait-for -t 1 -l noserver:9999 -- echo 'passable' 2>&1 + run ./wait-for -q -t 1 -l noserver:9999 -- echo 'passable' 2>&1 [ "$status" -eq 0 ] - [ "${lines[0]}" = "Operation timed out" ] - [ "${lines[1]}" = "passable" ] + [ "$output" = "passable" ] } @test "preserve existing environment variables" {