Diese GitLab Instanz ist nicht für den produktiven Einsatz gedacht!
Bitte https://gitlab.ost.ch benutzen!
Diese Instanz ist nur nuch zur Datenmigration online.

Vielen Dank für euer Verständnis.

Commit d7b3d6de authored by Marcel Huber's avatar Marcel Huber
Browse files

First release to work on plain sh

Merge branch 'release/1.0.0'
parents 54d1f0bf 486a69c2
Pipeline #19161 passed with stage
in 33 seconds
# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: python:2-slim
# Change pip's cache directory to be inside the project directory since we can
# only cache local items.
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/reference/pip_install/#caching
#
# If you want to also cache the installed packages, you have to install
# them in a virtualenv and cache it as well.
cache:
paths:
- .cache/pip
- venv/
before_script:
- python -V # Print out python version for debugging
- pip install tox
test:
script:
- tox
language: python
python:
- "2.7"
script:
- python test/wait-for-it.py
= Changelog
== tag: 1.0.0
* updated changelog
* Bumped version to 1.0.0 (c84ea77)
* Converted script to be plain sh compatible (1fb0fec)
* adapted documentation including note about docker port testing (118384e)
* explicitly select one of the backend testing methods (78c2006)
* updated changelog (058b2b8)
* disabled subprocess32 module to speed up tests (b2e9519)
* improved checking for utilities using type (108b9db)
* added missing python spec (49b3638)
* replaced bash style if statements (856aa68)
* improved socket related tests (c5e0b41)
* run tests using tox (9dac192)
* try fixing stuck builds (0fe8833)
* adjusted readme, setup ci (4f55896)
* fixed busybox >=1.30 behavior with timeout command (4969156)
* replaced bash specifics with shell compatible commands (9bd77e1)
* Merge branch 'fwoelffel-master' (54d1f0b)
* Merge branch 'master' of https://github.com/fwoelffel/wait-for-it into fwoelffel-master (9995b72)
* Merge pull request #60 from aviau/patch-1 (4dd67a6)
* Merge branch 'scop-deps' (4d0a46b)
* README: community section + mention Debian package (e34c502)
* Fall back to readlink -f if realpath is not available or fails (019f3bb)
* Use parameter expansion instead of basename (5fe30e7)
* Use type -p instead of which (f9a0dc4)
* Add a WAITFORIT_ prefix in front of all variables (410d77e)
* Merge pull request #40 from szczad/master (db04971)
* fix: preserve quotation when passing arguments to command. (8ed92e8)
* Merge pull request #6 from iturgeon/master (8b4051d)
* Merge pull request #32 from douglas-gibbons/master (a2acebe)
* Fixes to test script for flake8 (8ed81e3)
* Update to README.md (b638c19)
* Tidy up of vars in tests (f9b79b2)
* Modified error output for 'ls' command to cope with different test environments (1374528)
* Added some real tests (4fd1b45)
* Start of test framework (13c00e3)
* Start of test framework (7f645ce)
* Update README.md (ac37241)
* Update README.md (409b4a7)
* Update README.md (209be48)
* readme update (e686775)
* Update README.md (e1f115e)
* Update README.md (1f3eb2c)
* Update README.md (11afe7b)
* Adds support for distros that use busybox like Alpine (8f52a81)
* Merge pull request #4 from Silex/patch-1 (a454892)
* Fix invalid use of return value (55c54a5)
* license added (20c6094)
* more small doc fixes (4e94d7d)
* doc fixes (bf194c8)
* doc fixes (a150deb)
* small fixes (41deb2d)
* initial commit (e559a8e)
## 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 *sh* 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.
External dependencies are either `nc`, `python` or `bash`.
This version is a fork of https://github.com/vishnubob/wait-for-it with improvements that it works with a *plain sh*.
## Usage
== Usage
```
[source, sh]
----
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
......@@ -14,40 +18,37 @@ wait-for-it.sh host:port [-s] [-t timeout] [-- command args]
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
```
----
## Examples
== Examples
=== Basic usage
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`.
```
[source, sh]
----
$ ./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:
```
[source, sh]
----
$ ./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:
```
$ ./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:
You can test the exit condition of `wait-for-it.sh` in your own scripts, and determine how to proceed:
```
[source, sh]
----
$ ./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
......@@ -58,8 +59,39 @@ wait-for-it.sh: waiting 15 seconds for www.google.com:81
wait-for-it.sh: timeout occurred after waiting 15 seconds for www.google.com:81
$ echo $?
124
```
----
=== Subcommands
A subcommand will be executed regardless of whether 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:
[source, sh]
----
$ ./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
----
=== Docker
[NOTE]
====
Take care when using this utility to test for a running service within a docker container. When Docker is starting up a container, the ports will become available ver early even if the backing service is not running already! To account for this behavior you need to do some sort of communication with the backend to ensure it is up and running.
====
## Community
.Protocol level service test
[source, sh]
----
$ ./wait-for-it.sh --host=localhost --port=51200 --strict --timeout=20 -- \
/usr/bin/timeout 40 sh -c 'while true; do \
printf "GET / HTTP/1.0\r\n\r\n" | nc localhost 51200 | grep -q "^HTTP.*200.*OK" && break; \
printf "."; sleep 1; \
done'
*Debian*: There is a [Debian package](https://tracker.debian.org/pkg/wait-for-it).
wait-for-it.sh: waiting 20 seconds for localhost:51200
wait-for-it.sh: localhost:51200 is available after 3 seconds
...................
----
1.0.0
\ No newline at end of file
import unittest
import shlex
from subprocess import Popen, PIPE
import os
import sys
import socket
import re
has_timeout_param = True
try:
from subprocess32 import Popen, PIPE, STDOUT, TimeoutExpired
except:
try:
from subprocess import Popen, PIPE, STDOUT, TimeoutExpired
except:
from subprocess import Popen, PIPE, STDOUT
class TimeoutExpired(Exception):
def __init__(self):
pass
has_timeout_param = False
MISSING_ARGS_TEXT = "Error: you need to provide a host and port to test."
HELP_TEXT = "Usage:" # Start of help text
DIVIDE_LINE = '-'*71 # Output line of dashes
DIVIDE_LINE = '-' * 71 # Output line of dashes
_SOCKET_TIMEOUT = 5.0
socket.setdefaulttimeout(_SOCKET_TIMEOUT)
class TestWaitForIt(unittest.TestCase):
"""
TestWaitForIt tests the wait-for-it.sh shell script.
The wait-for-it.sh script is assumed to be in the parent directory to
the test script.
"""
"""TestWaitForIt tests the wait-for-it.sh shell script.
def execute(self, cmd):
"""Executes a command and returns exit code, STDOUT, STDERR"""
The wait-for-it.sh script is assumed to be in the parent directory
to the test script.
"""
def execute(self, cmd, stdincontent=None, timeout=10):
_kwargs = {'input': stdincontent}
if has_timeout_param:
_kwargs['timeout'] = timeout
args = shlex.split(cmd)
proc = Popen(args, stdout=PIPE, stderr=PIPE)
out, err = proc.communicate()
exitcode = proc.returncode
return exitcode, out, err
def open_local_port(self, host="localhost", port=8929, timeout=5):
s = socket.socket()
try:
_stdout, _stderr = proc.communicate(**_kwargs)
except TimeoutExpired:
_kwargs = {'input': None}
if has_timeout_param:
_kwargs['timeout'] = 5
proc.kill()
_stdout, _stderr = proc.communicate(**_kwargs)
except OSError:
pass
finally:
returncode = proc.poll()
return (returncode, _stdout, _stderr)
def open_local_port(self, host="localhost", port=8929, timeout=None):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if timeout:
s.settimeout(timeout)
s.bind((host, port))
s.listen(timeout)
s.listen(1)
return s
def check_args(self, args, stdout_regex, stderr_regex, exitcode):
......@@ -37,17 +67,15 @@ class TestWaitForIt(unittest.TestCase):
actual_exitcode, out, err = self.execute(command)
# Check stderr
msg = ("Failed check that STDERR:\n" +
DIVIDE_LINE + "\n" + err + "\n" + DIVIDE_LINE +
"\nmatches:\n" +
DIVIDE_LINE + "\n" + stderr_regex + "\n" + DIVIDE_LINE)
msg = ("Failed check that STDERR:\n" + DIVIDE_LINE + "\n" + err +
"\n" + DIVIDE_LINE + "\nmatches:\n" + DIVIDE_LINE + "\n" +
stderr_regex + "\n" + DIVIDE_LINE)
self.assertIsNotNone(re.match(stderr_regex, err, re.DOTALL), msg)
# Check STDOUT
msg = ("Failed check that STDOUT:\n" +
DIVIDE_LINE + "\n" + out + "\n" + DIVIDE_LINE +
"\nmatches:\n" +
DIVIDE_LINE + "\n" + stdout_regex + "\n" + DIVIDE_LINE)
msg = ("Failed check that STDOUT:\n" + DIVIDE_LINE + "\n" + out +
"\n" + DIVIDE_LINE + "\nmatches:\n" + DIVIDE_LINE + "\n" +
stdout_regex + "\n" + DIVIDE_LINE)
self.assertIsNotNone(re.match(stdout_regex, out, re.DOTALL), msg)
# Check exit code
......@@ -59,121 +87,86 @@ class TestWaitForIt(unittest.TestCase):
self.wait_script = os.path.join(parent_path, "wait-for-it.sh")
def test_no_args(self):
"""
Check that no aruments returns the missing args text and the
correct return code
"""
self.check_args(
"",
"^$",
MISSING_ARGS_TEXT,
1
)
"""Check that no aruments returns the missing args text and the correct
return code."""
self.check_args("", "^$", MISSING_ARGS_TEXT, 1)
# Return code should be 1 when called with no args
exitcode, out, err = self.execute(self.wait_script)
self.assertEqual(exitcode, 1)
self.assertEqual(exitcode, 1)
def test_help(self):
""" Check that help text is printed with --help argument """
self.check_args(
"--help",
"",
HELP_TEXT,
1
)
"""Check that help text is printed with --help argument."""
self.check_args("--help", "", HELP_TEXT, 1)
def test_no_port(self):
""" Check with missing port argument """
self.check_args(
"--host=localhost",
"",
MISSING_ARGS_TEXT,
1
)
"""Check with missing port argument."""
self.check_args("--host=localhost", "", MISSING_ARGS_TEXT, 1)
def test_no_host(self):
""" Check with missing hostname argument """
self.check_args(
"--port=80",
"",
MISSING_ARGS_TEXT,
1
)
"""Check with missing hostname argument."""
self.check_args("--port=80", "", MISSING_ARGS_TEXT, 1)
def test_host_port(self):
""" Check that --host and --port args work correctly """
soc = self.open_local_port(port=8929)
self.check_args(
"--host=localhost --port=8929 --timeout=1",
"",
"wait-for-it.sh: waiting 1 seconds for localhost:8929",
0
)
soc.close()
"""Check that --host and --port args work correctly."""
try:
soc = self.open_local_port(port=8929)
self.check_args(
"--host=localhost --port=8929 --timeout=1", "",
"wait-for-it.sh: waiting 1 seconds for localhost:8929", 0)
finally:
soc.close()
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)
self.check_args(
"localhost:8929 --timeout=1",
"",
"wait-for-it.sh: waiting 1 seconds for localhost:8929",
0
)
soc.close()
"""Tests that wait-for-it.sh returns correctly after establishing a
connectionm using combined host and ports."""
try:
soc = self.open_local_port(port=8929)
self.check_args(
"localhost:8929 --timeout=1", "",
"wait-for-it.sh: waiting 1 seconds for localhost:8929", 0)
finally:
soc.close()
def test_port_failure_with_timeout(self):
"""
Note exit status of 124 is exected, passed from the timeout command
"""
"""Note exit status of 124 is expected, passed from the timeout
command."""
self.check_args(
"localhost:8929 --timeout=1",
"",
"localhost:8929 --timeout=1", "",
".*timeout occurred after waiting 1 seconds for localhost:8929",
124
)
124)
def test_command_execution(self):
"""
Checks that a command executes correctly after a port test passes
"""
soc = self.open_local_port(port=8929)
self.check_args(
"localhost:8929 -- echo \"CMD OUTPUT\"",
"CMD OUTPUT",
".*wait-for-it.sh: localhost:8929 is available after 0 seconds",
0
)
soc.close()
"""Checks that a command executes correctly after a port test
passes."""
try:
soc = self.open_local_port(port=8929)
self.check_args(
"localhost:8929 -- echo \"CMD OUTPUT\"", "CMD OUTPUT",
".*wait-for-it.sh: localhost:8929 is available after 0 seconds",
0)
finally:
soc.close()
def test_failed_command_execution(self):
"""Check command failure.
The command in question outputs STDERR and an exit code of 2
"""
Check command failure. The command in question outputs STDERR and
an exit code of 2
"""
soc = self.open_local_port(port=8929)
self.check_args(
"localhost:8929 -- ls not_real_file",
"",
".*No such file or directory\n",
2
)
soc.close()
expected_exitcode, _, _ = self.execute('ls not_real_file')
try:
soc = self.open_local_port(port=8929)
self.check_args("localhost:8929 -- ls not_real_file", "",
".*No such file or directory\n", expected_exitcode)
finally:
soc.close()
def test_command_after_connection_failure(self):
"""
Test that a command still runs even if a connection times out
and that the return code is correct for the comand being run
"""
"""Test that a command still runs even if a connection times out and
that the return code is correct for the comand being run."""
self.check_args(
"localhost:8929 --timeout=1 -- echo \"CMD OUTPUT\"",
"CMD OUTPUT",
".*timeout occurred after waiting 1 seconds for localhost:8929",
0
)
"localhost:8929 --timeout=1 -- echo \"CMD OUTPUT\"", "CMD OUTPUT",
".*timeout occurred after waiting 1 seconds for localhost:8929", 0)
if __name__ == '__main__':
unittest.main()
# Tox (http://tox.testrun.org/) is a tool for running tests
# in multiple virtualenvs. This configuration file will run the
# test suite on all supported python versions. To use it, "pip install tox"
# and then run "tox" from this directory.
[tox]
envlist = testenv
skip_missing_interpreters = True
# do not fail on missing setup.py
skipsdist = true
#pip_pre=False
[testenv]
basepython =
python2.7
deps =
; subprocess32
commands =
python test/wait-for-it.py
[testenv:reformat]
basepython =
python2.7
deps =
yapf
docformatter
whitelist_externals =
find
bash
commands =
- bash -c \
"for n in $(find test '(' -name '*.py' ')'); do \
yapf --in-place $n || echo reformat failed at $n; \
docformatter --in-place $n; \
done"
#!/usr/bin/env bash
#!/usr/bin/env sh
# Use this script to test if a given TCP host/port are available
# Get it here: https://gitlab.dev.ifs.hsr.ch/ifs/wait-for-it/raw/master/wait-for-it.sh
WAITFORIT_cmdname=${0##*/}
WAITFORIT_TIMEOUT=15
WAITFORIT_STRICT=0