From ef92efbffb05a5a0cfdb85ad426e2a91073afc1d Mon Sep 17 00:00:00 2001 From: Gareth Randall Date: Fri, 3 Jan 2020 23:38:39 +0400 Subject: [PATCH 1/9] Avoid sending a newline character which confuses servers. --- wait-for-it.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wait-for-it.sh b/wait-for-it.sh index 071c2be..ed8602f 100755 --- a/wait-for-it.sh +++ b/wait-for-it.sh @@ -36,7 +36,7 @@ wait_for() nc -z $WAITFORIT_HOST $WAITFORIT_PORT WAITFORIT_result=$? else - (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 WAITFORIT_result=$? fi if [[ $WAITFORIT_result -eq 0 ]]; then From 782bcf693e4ddb598c4bc8cd6188b4a8f30a9b62 Mon Sep 17 00:00:00 2001 From: Sergey Kolomenkin Date: Thu, 30 Jan 2020 19:03:27 +0300 Subject: [PATCH 2/9] README: fix markdown formatting - limit line length (does not affect rendering) - add first-level header first - converted google domain from hyperlinks into plain text - add language specification for code fragments Here is a command to run Markdown syntax check: ``` docker run --rm --network none -v "/$PWD:/markdown:ro" 06kellyjac/markdownlint-cli:0.21.0-alpine //markdown ``` All the changes above fixed the following list of errors: ``` /markdown/README.md:1 MD041/first-line-heading/first-line-h1 First line in file should be a top level heading [Context: "## wait-for-it"] /markdown/README.md:3 MD013/line-length Line length [Expected: 80; Actual: 280] /markdown/README.md:7 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] /markdown/README.md:21 MD013/line-length Line length [Expected: 80; Actual: 131] /markdown/README.md:23 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] /markdown/README.md:30 MD013/line-length Line length [Expected: 80; Actual: 124] /markdown/README.md:32 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] /markdown/README.md:39 MD013/line-length Line length [Expected: 80; Actual: 239] /markdown/README.md:41 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] /markdown/README.md:48 MD013/line-length Line length [Expected: 80; Actual: 185] /markdown/README.md:50 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] ``` --- README.md | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4891736..d08d5a2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -## wait-for-it +# wait-for-it -`wait-for-it.sh` is a pure bash script that will wait on the availability of a host and TCP port. It is useful for synchronizing the spin-up of interdependent services, such as linked docker containers. Since it is a pure bash script, it does not have any external dependencies. +`wait-for-it.sh` is a pure bash script that will wait on the availability of a +host and TCP port. It is useful for synchronizing the spin-up of +interdependent services, such as linked docker containers. Since it is a pure +bash script, it does not have any external dependencies. ## Usage -``` +```text wait-for-it.sh host:port [-s] [-t timeout] [-- command args] -h HOST | --host=HOST Host or IP under test -p PORT | --port=PORT TCP port under test @@ -18,36 +21,43 @@ wait-for-it.sh host:port [-s] [-t timeout] [-- command args] ## Examples -For example, let's test to see if we can access port 80 on www.google.com, and if it is available, echo the message `google is up`. +For example, let's test to see if we can access port 80 on `www.google.com`, +and if it is available, echo the message `google is up`. -``` +```text $ ./wait-for-it.sh www.google.com:80 -- echo "google is up" wait-for-it.sh: waiting 15 seconds for www.google.com:80 wait-for-it.sh: www.google.com:80 is available after 0 seconds google is up ``` -You can set your own timeout with the `-t` or `--timeout=` option. Setting the timeout value to 0 will disable the timeout: +You can set your own timeout with the `-t` or `--timeout=` option. Setting +the timeout value to 0 will disable the timeout: -``` +```text $ ./wait-for-it.sh -t 0 www.google.com:80 -- echo "google is up" wait-for-it.sh: waiting for www.google.com:80 without a timeout wait-for-it.sh: www.google.com:80 is available after 0 seconds google 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: +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: -``` +```text $ ./wait-for-it.sh www.google.com:81 --timeout=1 --strict -- echo "google is up" wait-for-it.sh: waiting 1 seconds for www.google.com:81 wait-for-it.sh: timeout occurred after waiting 1 seconds for www.google.com:81 wait-for-it.sh: strict mode, refusing to execute subprocess ``` -If you don't want to execute a subcommand, leave off the `--` argument. This way, you can test the exit condition of `wait-for-it.sh` in your own scripts, and determine how to proceed: +If you don't want to execute a subcommand, leave off the `--` argument. This +way, you can test the exit condition of `wait-for-it.sh` in your own scripts, +and determine how to proceed: -``` +```text $ ./wait-for-it.sh www.google.com:80 wait-for-it.sh: waiting 15 seconds for www.google.com:80 wait-for-it.sh: www.google.com:80 is available after 0 seconds From ed77b63706ea721766a62ff22d3a251d8b4a6a30 Mon Sep 17 00:00:00 2001 From: Fabio Kruger Date: Sat, 1 Feb 2020 13:39:58 +0100 Subject: [PATCH 3/9] Added support for new busybox timeout version --- wait-for-it.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/wait-for-it.sh b/wait-for-it.sh index 071c2be..5e8679e 100755 --- a/wait-for-it.sh +++ b/wait-for-it.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Use this script to test if a given TCP host/port are available +# Use this script to test if a given TCP host/port are available WAITFORIT_cmdname=${0##*/} @@ -141,16 +141,20 @@ WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} -# check to see if timeout is from busybox? +# Check to see if timeout is from busybox? WAITFORIT_TIMEOUT_PATH=$(type -p timeout) WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then - WAITFORIT_ISBUSY=1 + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then WAITFORIT_BUSYTIMEFLAG="-t" - + fi else - WAITFORIT_ISBUSY=0 - WAITFORIT_BUSYTIMEFLAG="" + WAITFORIT_ISBUSY=0 fi if [[ $WAITFORIT_CHILD -gt 0 ]]; then From 742b16ec5c6f1618fb9dee9a989aa0f53a531b87 Mon Sep 17 00:00:00 2001 From: renanbr Date: Mon, 3 Feb 2020 21:24:52 +0100 Subject: [PATCH 4/9] Initial Composer setup --- .gitignore | 2 +- composer.json | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 composer.json diff --git a/.gitignore b/.gitignore index 3e7e969..fcad8de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ **/*.pyc .pydevproject - +/vendor/ diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..ea892d7 --- /dev/null +++ b/composer.json @@ -0,0 +1,7 @@ +{ + "name": "vishnubob/wait-for-it", + "description": "Pure bash script to test and wait on the availability of a TCP host and port", + "type": "library", + "license": "MIT", + "bin": ["wait-for-it.sh"] +} From ea95fd208b9ab4d715624811b84c6e267e580cdd Mon Sep 17 00:00:00 2001 From: Douglas Gibbons Date: Mon, 3 Feb 2020 20:30:41 -0800 Subject: [PATCH 5/9] * Python 3 compatibility * New port for every test --- test/wait-for-it.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) mode change 100644 => 100755 test/wait-for-it.py diff --git a/test/wait-for-it.py b/test/wait-for-it.py old mode 100644 new mode 100755 index e06fb8c..f63990d --- a/test/wait-for-it.py +++ b/test/wait-for-it.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + import unittest import shlex from subprocess import Popen, PIPE @@ -24,13 +26,13 @@ def execute(self, cmd): proc = Popen(args, stdout=PIPE, stderr=PIPE) out, err = proc.communicate() exitcode = proc.returncode - return exitcode, out, err + return exitcode, out.decode('utf-8'), err.decode('utf-8') - def open_local_port(self, host="localhost", port=8929, timeout=5): + def open_local_port(self, timeout=5): s = socket.socket() - s.bind((host, port)) + s.bind(('', 0)) s.listen(timeout) - return s + return s, s.getsockname()[1] def check_args(self, args, stdout_regex, stderr_regex, exitcode): command = self.wait_script + " " + args @@ -102,11 +104,11 @@ def test_no_host(self): def test_host_port(self): """ Check that --host and --port args work correctly """ - soc = self.open_local_port(port=8929) + soc, port = self.open_local_port() self.check_args( - "--host=localhost --port=8929 --timeout=1", + "--host=localhost --port={0} --timeout=1".format(port), "", - "wait-for-it.sh: waiting 1 seconds for localhost:8929", + "wait-for-it.sh: waiting 1 seconds for localhost:{0}".format(port), 0 ) soc.close() @@ -116,15 +118,16 @@ def test_combined_host_port(self): Tests that wait-for-it.sh returns correctly after establishing a connectionm using combined host and ports """ - soc = self.open_local_port(port=8929) + soc, port = self.open_local_port() self.check_args( - "localhost:8929 --timeout=1", + "localhost:{0} --timeout=1".format(port), "", - "wait-for-it.sh: waiting 1 seconds for localhost:8929", + "wait-for-it.sh: waiting 1 seconds for localhost:{0}".format(port), 0 ) soc.close() + def test_port_failure_with_timeout(self): """ Note exit status of 124 is exected, passed from the timeout command @@ -140,11 +143,11 @@ def test_command_execution(self): """ Checks that a command executes correctly after a port test passes """ - soc = self.open_local_port(port=8929) + soc, port = self.open_local_port() self.check_args( - "localhost:8929 -- echo \"CMD OUTPUT\"", + "localhost:{0} -- echo \"CMD OUTPUT\"".format(port), "CMD OUTPUT", - ".*wait-for-it.sh: localhost:8929 is available after 0 seconds", + ".*wait-for-it.sh: localhost:{0} is available after 0 seconds".format(port), 0 ) soc.close() @@ -154,9 +157,9 @@ def test_failed_command_execution(self): Check command failure. The command in question outputs STDERR and an exit code of 2 """ - soc = self.open_local_port(port=8929) + soc, port = self.open_local_port() self.check_args( - "localhost:8929 -- ls not_real_file", + "localhost:{0} -- ls not_real_file".format(port), "", ".*No such file or directory\n", 2 From 035b80087a1467bb90702d5b511b09386575c00f Mon Sep 17 00:00:00 2001 From: Douglas Gibbons Date: Mon, 3 Feb 2020 20:38:49 -0800 Subject: [PATCH 6/9] Added container-runners.py to test against different environments --- test/README.md | 18 ++++++++++++++++++ test/container-runners.py | 36 ++++++++++++++++++++++++++++++++++++ test/requirements.txt | 2 ++ test/wait-for-it.py | 26 +++++++++++++------------- 4 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 test/README.md create mode 100755 test/container-runners.py create mode 100644 test/requirements.txt diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..59cd8db --- /dev/null +++ b/test/README.md @@ -0,0 +1,18 @@ +# Tests for wait-for-it + +* wait-for-it.py - pytests for wait-for-it.sh +* container-runners.py - Runs wait-for-it.py tests in multiple containers +* requirements.txt - pip requirements for container-runners.py + +To run the basic tests: + +``` +python wait-for-it.py +``` + +Many of the issues encountered have been related to differences between operating system versions. The container-runners.py script provides an easy way to run the python wait-for-it.py tests against multiple system configurations: + +``` +pip install -r requirements.txt +python container-runners.py +``` diff --git a/test/container-runners.py b/test/container-runners.py new file mode 100755 index 0000000..bf76b9d --- /dev/null +++ b/test/container-runners.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# Unit tests to run wait-for-it.py unit tests in several different docker images + +import unittest +from ddt import ddt, data +import os +import docker + +client = docker.from_env() +app_path = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..')) +volumes = {app_path: {'bind': '/app', 'mode': 'ro'}} + +@ddt +class TestContainers(unittest.TestCase): + """ + Test multiple container types with the test cases in wait-for-it.py + """ + + @data( + "python:3.5-buster", + "python:3.5-stretch", + "dougg/alpine-busybox:alpine-3.11.3_busybox-1.30.1", + "dougg/alpine-busybox:alpine-3.11.3_busybox-1.31.1", + ) + def test_image(self, image): + print(image) + command="/app/test/wait-for-it.py" + container = client.containers.run(image, command=command, volumes=volumes, detach=True) + result = container.wait() + logs = container.logs() + container.remove() + self.assertEqual(result, 0, logs) + +if __name__ == '__main__': + unittest.main() diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..58bb0c5 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,2 @@ +docker>=2.0.0 +dtt>=1.2.0 diff --git a/test/wait-for-it.py b/test/wait-for-it.py index f63990d..de7530e 100755 --- a/test/wait-for-it.py +++ b/test/wait-for-it.py @@ -34,9 +34,9 @@ def open_local_port(self, timeout=5): s.listen(timeout) return s, s.getsockname()[1] - def check_args(self, args, stdout_regex, stderr_regex, exitcode): + def check_args(self, args, stdout_regex, stderr_regex, should_succeed): command = self.wait_script + " " + args - actual_exitcode, out, err = self.execute(command) + exitcode, out, err = self.execute(command) # Check stderr msg = ("Failed check that STDERR:\n" + @@ -53,7 +53,7 @@ def check_args(self, args, stdout_regex, stderr_regex, exitcode): self.assertIsNotNone(re.match(stdout_regex, out, re.DOTALL), msg) # Check exit code - self.assertEqual(actual_exitcode, exitcode) + self.assertEqual(should_succeed, exitcode == 0) def setUp(self): script_path = os.path.dirname(sys.argv[0]) @@ -69,7 +69,7 @@ def test_no_args(self): "", "^$", MISSING_ARGS_TEXT, - 1 + False ) # Return code should be 1 when called with no args exitcode, out, err = self.execute(self.wait_script) @@ -81,7 +81,7 @@ def test_help(self): "--help", "", HELP_TEXT, - 1 + False ) def test_no_port(self): @@ -90,7 +90,7 @@ def test_no_port(self): "--host=localhost", "", MISSING_ARGS_TEXT, - 1 + False ) def test_no_host(self): @@ -99,7 +99,7 @@ def test_no_host(self): "--port=80", "", MISSING_ARGS_TEXT, - 1 + False ) def test_host_port(self): @@ -109,7 +109,7 @@ def test_host_port(self): "--host=localhost --port={0} --timeout=1".format(port), "", "wait-for-it.sh: waiting 1 seconds for localhost:{0}".format(port), - 0 + True ) soc.close() @@ -123,7 +123,7 @@ def test_combined_host_port(self): "localhost:{0} --timeout=1".format(port), "", "wait-for-it.sh: waiting 1 seconds for localhost:{0}".format(port), - 0 + True ) soc.close() @@ -136,7 +136,7 @@ def test_port_failure_with_timeout(self): "localhost:8929 --timeout=1", "", ".*timeout occurred after waiting 1 seconds for localhost:8929", - 124 + False ) def test_command_execution(self): @@ -148,7 +148,7 @@ def test_command_execution(self): "localhost:{0} -- echo \"CMD OUTPUT\"".format(port), "CMD OUTPUT", ".*wait-for-it.sh: localhost:{0} is available after 0 seconds".format(port), - 0 + True ) soc.close() @@ -162,7 +162,7 @@ def test_failed_command_execution(self): "localhost:{0} -- ls not_real_file".format(port), "", ".*No such file or directory\n", - 2 + False ) soc.close() @@ -175,7 +175,7 @@ def test_command_after_connection_failure(self): "localhost:8929 --timeout=1 -- echo \"CMD OUTPUT\"", "CMD OUTPUT", ".*timeout occurred after waiting 1 seconds for localhost:8929", - 0 + True ) if __name__ == '__main__': From 301eb66592d00d502a9cc5f8aa735e223efa430a Mon Sep 17 00:00:00 2001 From: Douglas Gibbons Date: Sat, 22 Aug 2020 11:14:00 -0700 Subject: [PATCH 7/9] Removed dtt from tests --- test/container-runners.py | 11 +++++------ test/requirements.txt | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/test/container-runners.py b/test/container-runners.py index bf76b9d..3f8f358 100755 --- a/test/container-runners.py +++ b/test/container-runners.py @@ -3,26 +3,25 @@ # Unit tests to run wait-for-it.py unit tests in several different docker images import unittest -from ddt import ddt, data import os import docker +from parameterized import parameterized client = docker.from_env() app_path = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..')) volumes = {app_path: {'bind': '/app', 'mode': 'ro'}} -@ddt class TestContainers(unittest.TestCase): """ Test multiple container types with the test cases in wait-for-it.py """ - @data( + @parameterized.expand([ "python:3.5-buster", "python:3.5-stretch", "dougg/alpine-busybox:alpine-3.11.3_busybox-1.30.1", - "dougg/alpine-busybox:alpine-3.11.3_busybox-1.31.1", - ) + "dougg/alpine-busybox:alpine-3.11.3_busybox-1.31.1" + ]) def test_image(self, image): print(image) command="/app/test/wait-for-it.py" @@ -30,7 +29,7 @@ def test_image(self, image): result = container.wait() logs = container.logs() container.remove() - self.assertEqual(result, 0, logs) + self.assertEqual(result["StatusCode"], 0) if __name__ == '__main__': unittest.main() diff --git a/test/requirements.txt b/test/requirements.txt index 58bb0c5..9ba1e52 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1,2 +1,2 @@ -docker>=2.0.0 -dtt>=1.2.0 +docker>=4.0.0 +parameterized>=0.7.0 From d9b93e83cff8c7b00a9e85d66026b31ee0a25d67 Mon Sep 17 00:00:00 2001 From: Ian Turgeon Date: Mon, 4 Nov 2019 12:04:51 -0500 Subject: [PATCH 8/9] detects busybox 1.30.0+ with updated timeout flags Busybox updated timeout's flags to match coreutils. This broke the script that was specifically dealing with BusyBox being different from coreutils. To fix the problem without breaking support for older versions of busy box, this a version check of busybox to determine which version of the flags to use. Note, the changes use bash's regular expression's BASH_REMATCH to get the job done. For Alpine users, this means you'll have to install bash and execute wait-for-it using bash. --- wait-for-it.sh | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/wait-for-it.sh b/wait-for-it.sh index d990e0d..d147813 100755 --- a/wait-for-it.sh +++ b/wait-for-it.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Use this script to test if a given TCP host/port are available +# Use this script to test if a given TCP host/port are available WAITFORIT_cmdname=${0##*/} @@ -36,7 +36,7 @@ wait_for() nc -z $WAITFORIT_HOST $WAITFORIT_PORT WAITFORIT_result=$? else - (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 WAITFORIT_result=$? fi if [[ $WAITFORIT_result -eq 0 ]]; then @@ -140,21 +140,23 @@ WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} - -# Check to see if timeout is from busybox? +WAITFORIT_ISBUSY=0 +WAITFORIT_BUSYTIMEFLAG="" WAITFORIT_TIMEOUT_PATH=$(type -p timeout) WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) -WAITFORIT_BUSYTIMEFLAG="" +# check to see if we're using busybox? if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then WAITFORIT_ISBUSY=1 - # Check if busybox timeout uses -t flag - # (recent Alpine versions don't support -t anymore) - if timeout &>/dev/stdout | grep -q -e '-t '; then +fi + +# see if timeout.c args have been updated in busybox v1.30.0 or newer +# note: this requires the use of bash on Alpine +if [[ $WAITFORIT_ISBUSY && $(busybox | head -1) =~ ^.*v([[:digit:]]+)\.([[:digit:]]+)\..+$ ]]; then + if [[ ${BASH_REMATCH[1]} -le 1 && ${BASH_REMATCH[2]} -lt 30 ]]; then + # using pre 1.30.0 version with `-t SEC` arg WAITFORIT_BUSYTIMEFLAG="-t" fi -else - WAITFORIT_ISBUSY=0 fi if [[ $WAITFORIT_CHILD -gt 0 ]]; then From 913b6b3e7a0e0d9607862d7949c6dc2e340dfec9 Mon Sep 17 00:00:00 2001 From: "Alexander J. Lallier" Date: Fri, 18 Dec 2020 14:58:19 -0500 Subject: [PATCH 9/9] Fix if statement to handle Bash Boolean checking correctly --- wait-for-it.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wait-for-it.sh b/wait-for-it.sh index d147813..c0d6a9b 100755 --- a/wait-for-it.sh +++ b/wait-for-it.sh @@ -152,7 +152,7 @@ fi # see if timeout.c args have been updated in busybox v1.30.0 or newer # note: this requires the use of bash on Alpine -if [[ $WAITFORIT_ISBUSY && $(busybox | head -1) =~ ^.*v([[:digit:]]+)\.([[:digit:]]+)\..+$ ]]; then +if [[ $WAITFORIT_ISBUSY -eq 1 && $(busybox | head -1) =~ ^.*v([[:digit:]]+)\.([[:digit:]]+)\..+$ ]]; then if [[ ${BASH_REMATCH[1]} -le 1 && ${BASH_REMATCH[2]} -lt 30 ]]; then # using pre 1.30.0 version with `-t SEC` arg WAITFORIT_BUSYTIMEFLAG="-t"