Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combining multiple fixes #9

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ services:

before_install:
- docker build -t eficode/wait-for .

script:
- npm install
- ./run_tests.sh
- docker run eficode/wait-for
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +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
-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
```
Expand All @@ -21,11 +22,19 @@ 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.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
waited for google
```

To wait for database container to become available:


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 8 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -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
58 changes: 47 additions & 11 deletions wait-for
Original file line number Diff line number Diff line change
@@ -1,39 +1,71 @@
#!/bin/sh

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."
exit 1
fi

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
Usage:
$cmdname host:port [-t timeout] [-- command args]
$(basename $0) host:port [-t timeout] [-- command args]
-q | --quiet Do not output any status messages
-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
exit "$exitcode"
}

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
conditionally_output nc -z -w 1 -G 1 "$1" "$2"
else
conditionally_output nc -z -w 1 "$1" "$2"
fi
}

wait_for() {
local result
for i in `seq $TIMEOUT` ; do
nc -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
exec "$@"
fi
exit 0
fi
if [ $result -eq 0 ] ; then break ; fi
sleep 1
done
echo "Operation timed out" >&2
exit 1
[ $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
exit $result
}

while [ $# -gt 0 ]
Expand All @@ -48,6 +80,10 @@ do
QUIET=1
shift 1
;;
-l | --loose)
LOOSE=1
shift 1
;;
-t)
TIMEOUT="$2"
if [ "$TIMEOUT" = "" ]; then break; fi
Expand Down
30 changes: 27 additions & 3 deletions wait-for.bats
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/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" ]
}

Expand All @@ -12,3 +12,27 @@
[ "$status" -ne 0 ]
[ "$output" != "success" ]
}

@test "nonexistent server should start command if loose option is specified" {
run ./wait-for -q -t 1 -l noserver:9999 -- echo 'passable' 2>&1

[ "$status" -eq 0 ]

[ "$output" = "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 $TIMEOUT)" = 'mytimeout' ]
[ "$(echo $QUIET)" = 'myquiet' ]
[ "$(echo $HOST)" = 'myhost' ]
[ "$(echo $PORT)" = 'myport' ]
[ "$(echo $LOOSE)" = 'myloose' ]
}