wait_for_it.sh 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/usr/bin/env bash
  2. # Use this script to test if a given TCP host/port are available
  3. WAITFORIT_cmdname=${0##*/}
  4. echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
  5. usage()
  6. {
  7. cat << USAGE >&2
  8. Usage:
  9. $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
  10. -h HOST | --host=HOST Host or IP under test
  11. -p PORT | --port=PORT TCP port under test
  12. Alternatively, you specify the host and port as host:port
  13. -s | --strict Only execute subcommand if the test succeeds
  14. -q | --quiet Don't output any status messages
  15. -t TIMEOUT | --timeout=TIMEOUT
  16. Timeout in seconds, zero for no timeout
  17. -- COMMAND ARGS Execute command with args after the test finishes
  18. USAGE
  19. exit 1
  20. }
  21. wait_for()
  22. {
  23. if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
  24. echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
  25. else
  26. echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
  27. fi
  28. WAITFORIT_start_ts=$(date +%s)
  29. while :
  30. do
  31. if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
  32. nc -z $WAITFORIT_HOST $WAITFORIT_PORT
  33. WAITFORIT_result=$?
  34. else
  35. (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
  36. WAITFORIT_result=$?
  37. fi
  38. if [[ $WAITFORIT_result -eq 0 ]]; then
  39. WAITFORIT_end_ts=$(date +%s)
  40. echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
  41. break
  42. fi
  43. sleep 1
  44. done
  45. return $WAITFORIT_result
  46. }
  47. wait_for_wrapper()
  48. {
  49. # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
  50. if [[ $WAITFORIT_QUIET -eq 1 ]]; then
  51. timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
  52. else
  53. timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
  54. fi
  55. WAITFORIT_PID=$!
  56. trap "kill -INT -$WAITFORIT_PID" INT
  57. wait $WAITFORIT_PID
  58. WAITFORIT_RESULT=$?
  59. if [[ $WAITFORIT_RESULT -ne 0 ]]; then
  60. echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
  61. fi
  62. return $WAITFORIT_RESULT
  63. }
  64. # process arguments
  65. while [[ $# -gt 0 ]]
  66. do
  67. case "$1" in
  68. *:* )
  69. WAITFORIT_hostport=(${1//:/ })
  70. WAITFORIT_HOST=${WAITFORIT_hostport[0]}
  71. WAITFORIT_PORT=${WAITFORIT_hostport[1]}
  72. shift 1
  73. ;;
  74. --child)
  75. WAITFORIT_CHILD=1
  76. shift 1
  77. ;;
  78. -q | --quiet)
  79. WAITFORIT_QUIET=1
  80. shift 1
  81. ;;
  82. -s | --strict)
  83. WAITFORIT_STRICT=1
  84. shift 1
  85. ;;
  86. -h)
  87. WAITFORIT_HOST="$2"
  88. if [[ $WAITFORIT_HOST == "" ]]; then break; fi
  89. shift 2
  90. ;;
  91. --host=*)
  92. WAITFORIT_HOST="${1#*=}"
  93. shift 1
  94. ;;
  95. -p)
  96. WAITFORIT_PORT="$2"
  97. if [[ $WAITFORIT_PORT == "" ]]; then break; fi
  98. shift 2
  99. ;;
  100. --port=*)
  101. WAITFORIT_PORT="${1#*=}"
  102. shift 1
  103. ;;
  104. -t)
  105. WAITFORIT_TIMEOUT="$2"
  106. if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
  107. shift 2
  108. ;;
  109. --timeout=*)
  110. WAITFORIT_TIMEOUT="${1#*=}"
  111. shift 1
  112. ;;
  113. --)
  114. shift
  115. WAITFORIT_CLI=("$@")
  116. break
  117. ;;
  118. --help)
  119. usage
  120. ;;
  121. *)
  122. echoerr "Unknown argument: $1"
  123. usage
  124. ;;
  125. esac
  126. done
  127. if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
  128. echoerr "Error: you need to provide a host and port to test."
  129. usage
  130. fi
  131. WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
  132. WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
  133. WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
  134. WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
  135. # Check to see if timeout is from busybox?
  136. WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
  137. WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
  138. WAITFORIT_BUSYTIMEFLAG=""
  139. if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
  140. WAITFORIT_ISBUSY=1
  141. # Check if busybox timeout uses -t flag
  142. # (recent Alpine versions don't support -t anymore)
  143. if timeout &>/dev/stdout | grep -q -e '-t '; then
  144. WAITFORIT_BUSYTIMEFLAG="-t"
  145. fi
  146. else
  147. WAITFORIT_ISBUSY=0
  148. fi
  149. if [[ $WAITFORIT_CHILD -gt 0 ]]; then
  150. wait_for
  151. WAITFORIT_RESULT=$?
  152. exit $WAITFORIT_RESULT
  153. else
  154. if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
  155. wait_for_wrapper
  156. WAITFORIT_RESULT=$?
  157. else
  158. wait_for
  159. WAITFORIT_RESULT=$?
  160. fi
  161. fi
  162. if [[ $WAITFORIT_CLI != "" ]]; then
  163. if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
  164. echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
  165. exit $WAITFORIT_RESULT
  166. fi
  167. exec "${WAITFORIT_CLI[@]}"
  168. else
  169. exit $WAITFORIT_RESULT
  170. fi