From fe7fc6a13a033ca8c8b2565dd93a07b42762919a Mon Sep 17 00:00:00 2001 From: winzlieb Date: Tue, 29 Sep 2020 20:51:28 +0000 Subject: [PATCH] initial commit --- Dockerfile | 149 ++++++++++++++++++++++++++++ docker-compose.multicast.yml | 25 +++++ docker-compose.yml | 85 ++++++++++++++++ entrypoint.sh | 54 +++++++++++ env_initial_setup.py | 80 +++++++++++++++ supervisord.conf | 110 +++++++++++++++++++++ wait-for-it.sh | 182 +++++++++++++++++++++++++++++++++++ 7 files changed, 685 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.multicast.yml create mode 100644 docker-compose.yml create mode 100644 entrypoint.sh create mode 100644 env_initial_setup.py create mode 100644 supervisord.conf create mode 100644 wait-for-it.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d294770 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,149 @@ +FROM alpine:latest +MAINTAINER David Lockwood + +ARG OSP_VERSION="beta6d" + +ARG NGINX_VERSION=1.17.3 +ARG NGINX_RTMP_VERSION=1.2.1 + +ARG DEFAULT_DB_URL="sqlite:///db/database.db" +ARG DEFAULT_REDIS_HOST="localhost" +ARG DEFAULT_REDIS_PORT=6379 +ARG DEFAULT_REDIS_PASSWORD="" +ARG DEFAULT_FLASK_SECRET="CHANGEME" +ARG DEFAULT_FLASK_SALT="CHANGEME" +ARG DEFAULT_OSP_ALLOWREGISTRATION="True" +ARG DEFAULT_OSP_REQUIREVERIFICATION="True" +ARG DEFAULT_TZ="ETC/UTC" + +ENV DB_URL=$DEFAULT_DB_URL +ENV REDIS_HOST=$DEFAULT_REDIS_HOST +ENV REDIS_PORT=$DEFAULT_REDIS_PORT +ENV REDIS_PASSWORD=$DEFAULT_REDIS_PASSWORD +ENV FLASK_SECRET=$DEFAULT_FLASK_SECRET +ENV FLASK_SALT=$DEFAULT_FLASK_SALT +ENV OSP_ALLOWREGISTRATION=$DEFAULT_OSP_ALLOWREGISTRATION +ENV OSP_REQUIREVERIFICATION=$DEFAULT_OSP_REQUIREVERIFICATION + +EXPOSE 80/tcp +EXPOSE 443/tcp +EXPOSE 1935/tcp + + +# Get initial dependancies +RUN apk update +RUN apk add alpine-sdk \ + pcre-dev \ + libressl-dev \ + openssl-dev \ + libffi-dev \ + wget \ + git \ + linux-headers \ + zlib-dev \ + postgresql-dev \ + gcc \ + libgcc \ + musl-dev \ + jpeg-dev \ + zlib-dev + +RUN apk add --no-cache tzdata + +ENV TZ=$DEFAULT_TZ + +RUN apk add --no-cache bash + +# Download OSP from Repo +RUN cd /tmp && \ + wget "https://gitlab.com/Deamos/flask-nginx-rtmp-manager/-/archive/${OSP_VERSION}/flask-nginx-rtmp-manager-${OSP_VERSION}.tar.gz" && \ + tar zxf flask-nginx-rtmp-manager-${OSP_VERSION}.tar.gz && \ + rm flask-nginx-rtmp-manager-${OSP_VERSION}.tar.gz + +# Make OSP Install Directory +RUN mv /tmp/flask-nginx-rtmp-manager-${OSP_VERSION} /opt/osp/ + +# Transfer OSP Docker Files +COPY entrypoint.sh /opt/osp/setup/ +COPY env_initial_setup.py /opt/osp/setup +COPY supervisord.conf /opt/osp/setup +COPY wait-for-it.sh /opt/osp/setup + +# Create the www-data user +RUN set -x ; \ + addgroup -g 82 -S www-data ; \ + adduser -u 82 -D -S -G www-data www-data && exit 0 ; exit 1 + +# Set the OSP directory to www-data +RUN chown -R www-data:www-data /opt/osp + +# Download NGINX +RUN cd /tmp && \ + wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \ + tar zxf nginx-${NGINX_VERSION}.tar.gz && \ + rm nginx-${NGINX_VERSION}.tar.gz + +# Download the NGINX-RTMP Module +RUN cd /tmp && \ + wget https://github.com/arut/nginx-rtmp-module/archive/v${NGINX_RTMP_VERSION}.tar.gz && \ + tar zxf v${NGINX_RTMP_VERSION}.tar.gz && rm v${NGINX_RTMP_VERSION}.tar.gz + +RUN cd /tmp && \ + wget "https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/master.tar.gz" && \ + tar xxf master.tar.gz + +# Compile NGINX with the NGINX-RTMP Module +RUN cd /tmp/nginx-${NGINX_VERSION} && \ + ./configure \ + --with-http_ssl_module \ + --with-http_v2_module \ + --with-http_auth_request_module \ + --with-cc-opt="-Wimplicit-fallthrough=0" \ + --add-module=../nginx-rtmp-module-${NGINX_RTMP_VERSION} \ + --add-module=../nginx-goodies-nginx-sticky-module-ng-08a395c66e42 && \ + cd /tmp/nginx-${NGINX_VERSION} && make && make install + +RUN rm -rf /tmp/nginx-${NGINX_VERSION} + +# Configure NGINX +RUN cp /opt/osp/setup/nginx/*.conf /usr/local/nginx/conf/ +RUN cp /opt/osp/setup/nginx/mime.types /usr/local/nginx/conf/ + +# Send NGINX logs to stdout +RUN mkdir -p /usr/local/nginx/logs +RUN ln -sf /dev/stdout /usr/local/nginx/logs/access.log +RUN ln -sf /dev/stderr /usr/local/nginx/logs/error.log + +# Install Python, Gunicorn, and uWSGI +RUN apk add python3 \ + py3-pip \ + py3-setuptools \ + python3-dev \ + py3-gunicorn \ + uwsgi-python3 + +# Upgrade PIP +RUN pip3 install --upgrade pip + +# Install OSP Dependancies +RUN pip3 install -r /opt/osp/setup/requirements.txt +RUN pip3 install cryptography + +# Setup FFMPEG for recordings and Thumbnails +RUN apk add ffmpeg + +# Add Dialog (used in osp-config.sh) +RUN apk add dialog + +# Setup Wait-For-It Script +RUN chmod +x /opt/osp/setup/wait-for-it.sh + +# Install Supervisor +RUN apk add supervisor +RUN mkdir -p /var/log/supervisor + +VOLUME ["/var/www", "/usr/local/nginx/conf", "/opt/osp/db", "/opt/osp/conf"] + +RUN chmod +x /opt/osp/osp-config.sh +RUN chmod +x /opt/osp/setup/entrypoint.sh +ENTRYPOINT ["/bin/sh","-c", "/opt/osp/setup/entrypoint.sh"] diff --git a/docker-compose.multicast.yml b/docker-compose.multicast.yml new file mode 100644 index 0000000..d9e01e5 --- /dev/null +++ b/docker-compose.multicast.yml @@ -0,0 +1,25 @@ +version: '3.4' + +services: + rtmp-multi: + image: kamprath/multistreaming-server:latest + networks: + - traefik + volumes: + - ./config/rtmp-configuration.json:/rtmp-configuration.json + environment: + - MULTISTREAMING_PASSWORD=k-otk-ot + labels: + - "traefik.docker.network=traefik" + - "traefik.http.services.rtmpmulti.loadbalancer.server.port=80" + - "traefik.http.routers.rtmpmulti.rule=Host(`multi.streaming.live.datenspuren.de`)" + - "traefik.http.routers.rtmpmulti.entryPoints=websecure" + - "traefik.http.routers.rtmpmulti.tls=true" + - "traefik.http.routers.rtmpmulti.tls.certresolver=le" + - "traefik.http.routers.rtmpmulti.service=rtmpmulti" + + + +networks: + traefik: + external: true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..bab7a80 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,85 @@ +version: '3.4' +services: + db: + image: mariadb + restart: unless-stopped + command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW + volumes: + - ./data/mariadb/db:/var/lib/mysql + - ./setup/mariadb/osp-mariadb-additions.cnf:/etc/mysql/conf.d/osp-mariadb-additions.cnf + environment: + - MYSQL_ROOT_PASSWORD=internalanyway0u4F4C7gBF + - MYSQL_PASSWORD=internalanywayDSfagWgmUUnF + - MYSQL_DATABASE=osp + - MYSQL_USER=osp + networks: + internal: + healthcheck: + test: "/usr/bin/mysql --user=root --password=internalanyway0u4F4C7gBF --execute \"SHOW DATABASES;\"" + interval: 1m + timeout: 1s + retries: 5 + start_period: 30s + labels: + - "traefik.enable=true" + + redis: + image: redis + restart: unless-stopped + expose: + - 6379 + networks: + internal: + labels: + - "traefik.enable=true" + + app: + ## default: use prebuilt image of this project + image: deamos/openstreamingplatform:beta6d + ## use build instead, if you have custom modifications in osp + # build: . + restart: unless-stopped + ports: + - '1935:1935' + # - '8585:80' + # - '8553:443' + environment: + - REDIS_HOST=redis + - REDIS_PORT=6379 + - REDIS_PASSWORD= + - DB_URL=mysql+pymysql://osp:internalanywayDSfagWgmUUnF@db/osp + - FLASK_SECRET=CHANGEME + - FLASK_SALT=CHANGEME + - OSP_ALLOWREGISTRATION=True + - OSP_REQUIREVERIFICATION=False + - TZ=Europe/Berlin + volumes: + - "./data/www:/var/www" + - "./data/nginx/conf:/usr/local/nginx/conf" + tmpfs: + - /var/www/live + - /var/www/live-rec + - /var/www/live-adapt + - /var/www/stream-thumb + networks: + internal: + traefik: + labels: + - "traefik.enable=true" + - "traefik.docker.network=traefik" + - "traefik.http.services.osp.loadbalancer.server.port=80" + - "traefik.http.routers.osp.rule=Host(`osp.streaming.live.datenspuren.de`)" + - "traefik.http.routers.osp.entrypoints=websecure" + - "traefik.http.routers.osp.tls=true" + - "traefik.http.routers.osp.tls.certresolver=le" + - "traefik.http.routers.osp.service=osp" + depends_on: + - db + - redis + entrypoint: ["/opt/osp/setup/docker/wait-for-it.sh", "db:3306", "-t", "60", "--", "/opt/osp/setup/docker/entrypoint.sh"] + +networks: + internal: + ## if you use traefik, also enable it here. + traefik: + external: true diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..77209df --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +echo 'Placing Configuration Files' +cp -R -u -p /opt/osp/setup/nginx/*.conf /usr/local/nginx/conf/ +cp -u -p /opt/osp/setup/nginx/mime.types /usr/local/nginx/conf/ +echo 'Setting up Directories' +mkdir -p /var/www && \ + mkdir -p /var/www/live && \ + mkdir -p /var/www/videos && \ + mkdir -p /var/www/live-rec && \ + mkdir -p /var/www/live-adapt && \ + mkdir -p /var/www/stream-thumb && \ + mkdir -p /var/www/images && \ + mkdir -p /var/log/gunicorn && \ + chown -R www-data:www-data /var/www && \ + chown -R www-data:www-data /var/log/gunicorn +echo 'Setting up OSP Configuration' + +export DB_URL +echo "dbLocation='$DB_URL'" > /opt/osp/conf/config.py +export REDIS_HOST +echo "redisHost='$REDIS_HOST'" >> /opt/osp/conf/config.py +export REDIS_PORT +echo "redisPort=$REDIS_PORT" >> /opt/osp/conf/config.py +export REDIS_PASSWORD +echo "redisPassword='$REDIS_PASSWORD'" >> /opt/osp/conf/config.py +export FLASK_SECRET +echo "secretKey='$FLASK_SECRET'" >> /opt/osp/conf/config.py +export FLASK_SALT +echo "passwordSalt='$FLASK_SALT'" >> /opt/osp/conf/config.py +export OSP_ALLOWREGISTRATION +echo "allowRegistration=$OSP_ALLOWREGISTRATION" >> /opt/osp/conf/config.py +export OSP_REQUIREVERIFICATION +echo "requireEmailRegistration=$OSP_REQUIREVERIFICATION" >> /opt/osp/conf/config.py +echo "debugMode=False" >> /opt/osp/conf/config.py + +chown -R www-data:www-data /opt/osp/conf/config.py +echo 'Performing DB Migrations' +cd /opt/osp + +if [[ ! -d /opt/osp/migrations ]]; then + python3 manage.py db init +fi +python3 manage.py db migrate +python3 manage.py db upgrade +cd / + +echo 'Fixing OSP Permissions Post Migration' +chown -R www-data:www-data /opt/osp + +echo 'Attempting initial provisioning by environment variables (if needed)' +python3 /opt/osp/setup/env_initial_setup.py & + +echo 'Starting OSP' +supervisord --nodaemon --configuration /opt/osp/setup/supervisord.conf \ No newline at end of file diff --git a/env_initial_setup.py b/env_initial_setup.py new file mode 100644 index 0000000..f481da9 --- /dev/null +++ b/env_initial_setup.py @@ -0,0 +1,80 @@ +#! /usr/bin/env python +import os +import sys +import time +import requests + +wait_time = 30 +url = "http://localhost" +initial_endpoint = "/settings/initialSetup" + + +def wait_for_ready(timeout=30): + timeout_time = time.time() + timeout + while True: + try: + r = requests.get(url) + except requests.exceptions.ConnectionError: + time.sleep(1) + continue + if r.ok: + return True + if timeout_time <= time.time(): + return False + time.sleep(1) + +def to_bool(value): + try: + ret_value = int(value) + except ValueError: + ret_value = 0 + +if __name__ == '__main__': + username = os.environ.get("OSP_ADMIN_USER") + email = os.environ.get("OSP_ADMIN_EMAIL") + password = os.environ.get("OSP_ADMIN_PASSWORD") + if username and password and email: + data = {"username": username, + "password1": password, + "password2": password, + "email": email} + try: + data["serverName"] = os.environ["OSP_SERVER_NAME"] + data["siteProtocol"] = os.environ.get("OSP_SERVER_PROTOCOL", "http") + data["serverAddress"] = os.environ["OSP_SERVER_ADDRESS"] + data["smtpSendAs"] = os.environ.get("OSP_SMTP_SEND_AS", "") + data["smtpAddress"] = os.environ["OSP_SMTP_SERVER"] + data["smtpPort"] = os.environ.get("OSP_SMTP_PORT", 25) + data["smtpUser"] = os.environ.get("OSP_SMTP_USER", "") + data["smtpPassword"] = os.environ.get("OSP_SMTP_PASSWORD", "") + data["smtpTLS"] = os.environ.get("OSP_SMTP_TLS") + data["smtpSSL"] = os.environ.get("OSP_SMTP_SSL") + data["recordSelect"] = os.environ.get("OSP_ALLOW_RECORDING") + data["uploadSelect"] = os.environ.get("OSP_ALLOW_UPLOAD") + data["adaptiveStreaming"] = os.environ.get("OSP_ADAPTIVE_STREAMING") + data["allowComments"] = os.environ.get("OSP_ALLOW_COMMENT") + data["showEmptyTables"] = os.environ.get("OSP_DISPLAY_EMPTY") + except KeyError as e: + print("""Environment provisioning: + When OSP_ADMIN_USER, OSP_ADMIN_EMAIL and OSP_ADMIN_PASSWORD are set I'm expecting at least the following other variables to be also set: + OSP_SERVER_NAME + OSP_SERVER_PROTOCOL + OSP_SERVER_ADDRESS + OSP_SMTP_SERVER + """) + sys.exit(1) + if wait_for_ready(wait_time): + r = requests.post( + url=f"{url}{initial_endpoint}", + data=data + ) + if r.ok: + print("Environment provisioning: Provisioning OK!") + else: + print(f"Environment provisioning: Prov failed! Make sure all environment variables are provided: {r.status_code} - {r.text}") + sys.exit(3) + else: + print(f"Environment provisioning: Unable to perform provisioning via environment - http server not up in {wait_time} seconds") + sys.exit(2) + else: + print("Environment provisioning: vars not set, not doing anything") \ No newline at end of file diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..93f38de --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,110 @@ +[supervisord] +nodaemon=true +logfile=/dev/fd/1 +logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5000] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5000 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5001] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5001 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5002] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5002 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5003] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5003 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5004] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5004 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5005] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5005 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5006] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5006 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5007] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5007 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5008] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5008 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5009] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5009 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:ospworker5010] +directory=/opt/osp +user=www-data +group=www-data +command=/usr/bin/gunicorn app:app -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 --bind 0.0.0.0:5010 --reload --access-logfile - --error-logfile - +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:nginx] +command=/usr/local/nginx/sbin/nginx -g "daemon off;" +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true diff --git a/wait-for-it.sh b/wait-for-it.sh new file mode 100644 index 0000000..5e8679e --- /dev/null +++ b/wait-for-it.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +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_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 + # 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 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi