|
| 1 | +#!/bin/sh |
| 2 | + |
| 3 | +# |
| 4 | +# `wait-host.sh` is an sh script that will wait on the availability of a host and TCP port. |
| 5 | +# Specially designed to be used in Alpine container, It is useful for synchronizing |
| 6 | +# interdependent services, such as linked docker containers. |
| 7 | +# |
| 8 | +# You can use it to wait for a database to be ready, for php-fpm connection, ... |
| 9 | +# |
| 10 | + |
| 11 | + |
| 12 | +# set -e : Exit the script if any statement returns a non-true return value. |
| 13 | +# set -u : Exit the script when using uninitialised variable. |
| 14 | +#set -eu |
| 15 | + |
| 16 | +# Default values |
| 17 | +readonly progname=$(basename $0) |
| 18 | + |
| 19 | +# Display help message |
| 20 | +function getHelp() { |
| 21 | + cat << USAGE >&2 |
| 22 | +
|
| 23 | +Usage: $progname [host:port] [OPTIONS] [-- command args] |
| 24 | +
|
| 25 | + -h | --host Host or IP under test |
| 26 | + -p | --port TCP port under test |
| 27 | + Alternatively, you can specify the host and port as host:port |
| 28 | + -d | --delay Delay in seconds, before trying to contact the host. |
| 29 | + -m | --message Delay message |
| 30 | + -s | --strict Only execute subcommand if the test succeeds |
| 31 | + -q | --quiet Don't output any status messages |
| 32 | + -t | --timeout Timeout in seconds, zero for no timeout |
| 33 | +
|
| 34 | + -- command args Execute command with args after the test finishes |
| 35 | +
|
| 36 | +Examples: |
| 37 | + $progname mysql:3306 Wait indefinitely for port 3306 to be available on host mysql |
| 38 | + $progname google.com:80 Wait indefinitely for port 80 to be available on host google.com |
| 39 | + $progname mysql:3306 -t 15 Wait a maximum of 15s for port 3306 to be available on host mysql |
| 40 | +
|
| 41 | + Use echo \$? to get the result |
| 42 | +
|
| 43 | +Full example: |
| 44 | + $progname mysql:3306 \\ |
| 45 | + -t 120 \\ |
| 46 | + -d 2 \\ |
| 47 | + -m 'Waiting for database connection on host:\$HOST and port:\$PORT' \\ |
| 48 | + -s \\ |
| 49 | + -- echo "Database connection established" \\ |
| 50 | + || echo "Error while trying to connect to the database" |
| 51 | +
|
| 52 | + Wait 120 seconds for port 3306 to be available on host mysql |
| 53 | + Display a custom message every 2 seconds |
| 54 | + Display a custom message on success |
| 55 | + Display a custom message on error (strict mode) |
| 56 | +
|
| 57 | +USAGE |
| 58 | +} |
| 59 | + |
| 60 | +# echo the message if not in quiet mode |
| 61 | +function echoerr() { |
| 62 | + [ "$QUIET" -ne 1 ] && printf "%s\n" "$*" 1>&2 |
| 63 | +} |
| 64 | + |
| 65 | +function do_wait() { |
| 66 | + until nc -z "$HOST" "$PORT"; do |
| 67 | + result=$? |
| 68 | + echoerr "$MESSAGE" |
| 69 | + sleep $DELAY |
| 70 | + done |
| 71 | + |
| 72 | + return $result |
| 73 | +} |
| 74 | + |
| 75 | +function wait_for_host() { |
| 76 | + if [ "$TIMEOUT" -gt 0 ]; then |
| 77 | + # Waiting message |
| 78 | + echoerr "Waiting $TIMEOUT seconds for $HOST:$PORT" |
| 79 | + # trap SIGINT SIGTERM |
| 80 | + trap 'kill -SIGTERM $TIMEOUT_ID 2>/dev/null; return 1' SIGINT SIGTERM |
| 81 | + # use timeout function to rerun the script without timeout |
| 82 | + timeout -t "$TIMEOUT" -s TERM "$0" -h=$HOST -p=$PORT -d=$DELAY -m="$MESSAGE" -s=$STRICT -q=$QUIET -t=0 & |
| 83 | + # get the process id |
| 84 | + TIMEOUT_ID=$! |
| 85 | + # wait to the end of the timeout unless it is killed |
| 86 | + while kill -0 $TIMEOUT_ID > /dev/null 2>&1; do |
| 87 | + wait |
| 88 | + done |
| 89 | + # Last check to get an error at the end of the timeout |
| 90 | + nc -z "$HOST" "$PORT" |
| 91 | + result=$? |
| 92 | + else |
| 93 | + do_wait |
| 94 | + # get the result of the wait function |
| 95 | + result=$? |
| 96 | + fi |
| 97 | + |
| 98 | + return $result |
| 99 | +} |
| 100 | + |
| 101 | +# Default values |
| 102 | +STRICT=0 |
| 103 | +QUIET=0 |
| 104 | +TIMEOUT=0 |
| 105 | +DELAY=2 |
| 106 | +MESSAGE='Waiting connection for $HOST:$PORT' |
| 107 | + |
| 108 | +# Get input parameters |
| 109 | +while [ $# -gt 0 ]; do |
| 110 | + case "$1" in |
| 111 | + |
| 112 | + [!-]*:* ) |
| 113 | + THOST=$(printf "%s\n" "$1"| cut -d : -f 1) |
| 114 | + TPORT=$(printf "%s\n" "$1"| cut -d : -f 2) |
| 115 | + [ -z "$HOST" ] && HOST=$THOST |
| 116 | + case $TPORT in |
| 117 | + ''|*[!0-9]*) break ;; |
| 118 | + *) [ -z "$PORT" ] && PORT=$TPORT ;; |
| 119 | + esac |
| 120 | + shift 1 |
| 121 | + ;; |
| 122 | + |
| 123 | + -h|--host) |
| 124 | + HOST="$2" |
| 125 | + [ -z "$HOST" ] && break |
| 126 | + shift 2 |
| 127 | + ;; |
| 128 | + |
| 129 | + -h=*|--host=*) |
| 130 | + HOST=$(printf "%s" "$1" | cut -d = -f 2) |
| 131 | + shift 1 |
| 132 | + ;; |
| 133 | + |
| 134 | + -p|--port) |
| 135 | + case $2 in |
| 136 | + ''|*[!0-9]*) break ;; |
| 137 | + *) PORT="$2" ;; |
| 138 | + esac |
| 139 | + shift 2 |
| 140 | + ;; |
| 141 | + |
| 142 | + -p=*|--port=*) |
| 143 | + case ${1#*=} in |
| 144 | + ''|*[!0-9]*) break ;; |
| 145 | + *) PORT="${1#*=}" ;; |
| 146 | + esac |
| 147 | + shift 1 |
| 148 | + ;; |
| 149 | + |
| 150 | + -d|--delay) |
| 151 | + case $2 in |
| 152 | + ''|*[!0-9]*) break ;; |
| 153 | + *) DELAY="$2" ;; |
| 154 | + esac |
| 155 | + shift 2 |
| 156 | + ;; |
| 157 | + |
| 158 | + -d=*|--delay=*) |
| 159 | + case ${1#*=} in |
| 160 | + ''|*[!0-9]*) break ;; |
| 161 | + *) DELAY="${1#*=}" ;; |
| 162 | + esac |
| 163 | + shift 1 |
| 164 | + ;; |
| 165 | + |
| 166 | + -m|--message) |
| 167 | + MESSAGE="$2" |
| 168 | + shift 2 |
| 169 | + ;; |
| 170 | + |
| 171 | + -m=*|--message=*) |
| 172 | + MESSAGE=$(printf "%s" "$1" | cut -d = -f 2) |
| 173 | + shift 1 |
| 174 | + ;; |
| 175 | + |
| 176 | + -s|--strict) |
| 177 | + STRICT=1 |
| 178 | + shift 1 |
| 179 | + ;; |
| 180 | + |
| 181 | + -s=*|--strict=*) |
| 182 | + case ${1#*=} in |
| 183 | + ''|[!0-1]) break ;; |
| 184 | + *) STRICT="${1#*=}" ;; |
| 185 | + esac |
| 186 | + shift 1 |
| 187 | + ;; |
| 188 | + |
| 189 | + -q|--quiet) |
| 190 | + QUIET=1 |
| 191 | + shift 1 |
| 192 | + ;; |
| 193 | + |
| 194 | + -q=*|--quiet=*) |
| 195 | + case ${1#*=} in |
| 196 | + ''|[!0-1]) break ;; |
| 197 | + *) QUIET="${1#*=}" ;; |
| 198 | + esac |
| 199 | + shift 1 |
| 200 | + ;; |
| 201 | + |
| 202 | + -t|--timeout) |
| 203 | + case $2 in |
| 204 | + ''|*[!0-9]*) break ;; |
| 205 | + *) TIMEOUT="$2" ;; |
| 206 | + esac |
| 207 | + shift 2 |
| 208 | + ;; |
| 209 | + |
| 210 | + -t=*|--timeout=*) |
| 211 | + case ${1#*=} in |
| 212 | + ''|*[!0-9]*) break ;; |
| 213 | + *) TIMEOUT="${1#*=}" ;; |
| 214 | + esac |
| 215 | + shift 1 |
| 216 | + ;; |
| 217 | + |
| 218 | + --) |
| 219 | + shift |
| 220 | + break |
| 221 | + ;; |
| 222 | + |
| 223 | + --help) |
| 224 | + getHelp |
| 225 | + exit 0 |
| 226 | + ;; |
| 227 | + |
| 228 | + *) |
| 229 | + echoerr "Invalid argument '$1'. Use --help to see the valid options" |
| 230 | + exit 1 |
| 231 | + ;; |
| 232 | + |
| 233 | + esac |
| 234 | +done |
| 235 | + |
| 236 | +# check for host and port |
| 237 | +if [ -z "$HOST" -o -z "$PORT" ]; then |
| 238 | + echoerr "Invalid host or port. Use --help to see the valid options." |
| 239 | + exit 2 |
| 240 | +fi |
| 241 | + |
| 242 | +# Update the HOST and PORT in the default message |
| 243 | +MESSAGE=$(eval "echo $MESSAGE") |
| 244 | + |
| 245 | +# Start waiting for the host |
| 246 | +wait_for_host |
| 247 | +WAIT_RESULT=$? |
| 248 | + |
| 249 | +# If not in strict mode, execute the subcommand whatever |
| 250 | +# the result of wait_for_host |
| 251 | +if [ "$*" != "" ]; then |
| 252 | + if [ $WAIT_RESULT -ne 0 -a $STRICT -eq 1 ]; then |
| 253 | + echoerr "Error while in strict mode. Refusing to execute subcommand" |
| 254 | + else |
| 255 | + exec "$@" |
| 256 | + fi |
| 257 | +fi |
| 258 | + |
| 259 | +# Exit with the result of wait_for_host |
| 260 | +exit $WAIT_RESULT |
0 commit comments