diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..b99aa884 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +docker/ +!docker/joex-entrypoint.sh +!docker/restserver-entrypoint.sh +!docker/docspell.conf diff --git a/.gitignore b/.gitignore index feff6262..fa587c4d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ _site/ /website/site/templates/shortcodes/server.conf /website/site/templates/shortcodes/sample-exim.conf /website/site/templates/shortcodes/joex.conf +/docker/docs +/docker/dev-log diff --git a/docker/.env b/docker/.env new file mode 100644 index 00000000..3bdcc089 --- /dev/null +++ b/docker/.env @@ -0,0 +1,8 @@ +TZ=Europe/Berlin +DOCSPELL_HEADER_VALUE=none +DB_TYPE=postgresql +DB_HOST=db +DB_PORT=5432 +DB_NAME=dbname +DB_USER=dbuser +DB_PASS=dbpass diff --git a/docker/base-binaries.dockerfile b/docker/base-binaries.dockerfile new file mode 100644 index 00000000..b9c3a7d4 --- /dev/null +++ b/docker/base-binaries.dockerfile @@ -0,0 +1,51 @@ +FROM alpine:latest + +LABEL maintainer="eikek0 " + +ARG ELM_VERSION=0.19.1 +ARG SBT_VERSION= + +RUN apk add --virtual .build-dependencies --no-cache git curl bash openjdk8 + +# ELM +RUN curl -L -o elm.gz https://github.com/elm/compiler/releases/download/${ELM_VERSION}/binary-for-linux-64-bit.gz +RUN gunzip elm.gz +RUN chmod +x elm +RUN mv elm /usr/local/bin/ + +# SBT (Scala) +ENV PATH /sbt/bin:$PATH +RUN wget https://github.com/sbt/sbt/releases/download/v${SBT_VERSION}/sbt-${SBT_VERSION}.tgz +RUN tar -xzvf sbt-$SBT_VERSION.tgz +RUN rm sbt-$SBT_VERSION.tgz + +# DOCSPELL +RUN mkdir -p /src/docspell +COPY . /src/docspell/ +# for a build without cloned project the following line would replace the one above +# RUN git -C /src clone https://github.com/eikek/docspell + + +#RUN SBT_OPTS="-Xms1024M -Xmx8G -Xss2M -XX:MaxMetaspaceSize=8G" && \ +WORKDIR /src/docspell +RUN sbt -mem 4096 make +RUN sbt -mem 4096 make-zip +RUN sbt -mem 4096 make-tools +#RUN SBT_OPTS= && \ + +RUN mkdir -p /opt +RUN find "/src/docspell/modules/joex/target/universal/" -name "docspell-joex*.zip" -exec unzip {} -d "/opt/" \; +RUN mv /opt/docspell-joex-* /opt/docspell-joex +RUN find "/src/docspell/modules/restserver/target/universal/" -name "docspell-restserver*.zip" -exec unzip {} -d "/opt/" \; +RUN mv /opt/docspell-restserver-* /opt/docspell-restserver +RUN find "/src/docspell/tools/target/" -name "docspell-tools-*.zip" -exec unzip {} -d "/opt/" \; +RUN mv /opt/docspell-tools-* /opt/docspell-tools +RUN chmod 755 /opt/docspell-tools/*.sh + +COPY ./docker/docspell.conf /opt/docspell.conf + +# CLEANUP +WORKDIR / +RUN rm -r /src +RUN apk del .build-dependencies +RUN rm -r /root/.cache diff --git a/docker/base.dockerfile b/docker/base.dockerfile new file mode 100644 index 00000000..b3b33072 --- /dev/null +++ b/docker/base.dockerfile @@ -0,0 +1,9 @@ +FROM alpine:latest +LABEL maintainer="eikek0 " + +ENV DB_TYPE=postgresql \ + DB_HOST=db \ + DB_PORT=5432 \ + DB_NAME=dbname \ + DB_USER=dbuser \ + DB_PASS=dbpass diff --git a/docker/build-images.sh b/docker/build-images.sh deleted file mode 100755 index cd403a57..00000000 --- a/docker/build-images.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# Update the versions in joex.dockerfile and restserver.dockerfile, -# docker-compose.yml and joex/entrypoint.sh; update versions here - -docker build -t eikek0/docspell:joex-0.12.0 -f joex.dockerfile . -docker build -t eikek0/docspell:restserver-0.12.0 -f restserver.dockerfile . -docker build -t eikek0/docspell:consumedir-0.12.0 -f consumedir.dockerfile . - -docker tag eikek0/docspell:restserver-0.12.0 eikek0/docspell:restserver-latest -docker tag eikek0/docspell:joex-0.12.0 eikek0/docspell:joex-latest -docker tag eikek0/docspell:consumedir-0.12.0 eikek0/docspell:consumedir-latest - - - -# test with docker-compose up diff --git a/docker/build-joex-base.sh b/docker/build-joex-base.sh deleted file mode 100755 index 03c5dd75..00000000 --- a/docker/build-joex-base.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -e - -docker build -t eikek0/docspell:joex-base-1 -f joex-base.dockerfile . -docker tag eikek0/docspell:joex-base-1 eikek0/docspell:joex-base-latest diff --git a/docker/consumedir.dockerfile b/docker/consumedir.dockerfile index 88dd1af1..dbdc61ca 100644 --- a/docker/consumedir.dockerfile +++ b/docker/consumedir.dockerfile @@ -1,15 +1,19 @@ -FROM alpine:latest +## CONSUMEDIR -LABEL maintainer="eikek0 " +ARG VERSION= +ARG REPO= -RUN apk add --no-cache unzip curl bash inotify-tools +# hack to use args in from +FROM ${REPO}:base-binaries-${VERSION} as docspell-base-binaries -RUN mkdir -p /opt \ - && cd /opt \ - && curl -L -o docspell.zip https://github.com/eikek/docspell/releases/download/v0.12.0/docspell-tools-0.12.0.zip \ - && unzip docspell.zip \ - && rm docspell.zip \ - && apk del unzip \ - && chmod 755 /opt/docspell-tools-0.12.0/*.sh -ENTRYPOINT ["/opt/docspell-tools-0.12.0/consumedir.sh"] +FROM ${REPO}:base-${VERSION} + +RUN apk add --no-cache curl bash inotify-tools + +COPY --from=docspell-base-binaries /opt/docspell-tools /opt/docspell-tools + +ENTRYPOINT /opt/docspell-tools/consumedir.sh --path /opt/docs -i --iheader Docspell-Integration:$DOCSPELL_HEADER_VALUE -m http://docspell-restserver:7880/api/v1/open/integration/item -v + +HEALTHCHECK --interval=1m --timeout=10s --retries=2 --start-period=10s \ + CMD pgrep inotifywait diff --git a/docker/dev-build-images.sh b/docker/dev-build-images.sh new file mode 100755 index 00000000..e3c09f43 --- /dev/null +++ b/docker/dev-build-images.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +REPO="eikek0/docspell" +if [ $# -eq 1 ]; then + REPO=$1 +fi + +SBT_VERSION=$(grep sbt.version ../project/build.properties) +SBT_VERSION=${SBT_VERSION:12:99} + +TMP_VERSION=$(cat ../version.sbt) +TMP_VERSION=${TMP_VERSION:25:99} +VERSION=${TMP_VERSION%\"} + +if [[ $VERSION == *"SNAPSHOT" ]]; then + VERSION=SNAPSHOT +else + VERSION=v$VERSION +fi + +# if automated build by docker, don't spool log to file +if [[ $LOG_TO_FILE -eq 1 ]]; then + logfile=./dev-log/build_$(date +%Y%m%d_%H%M).log + echo logging to logfile: $logfile + echo In order to log to console set 'LOG_TO_CONSOLE' to 1 + mkdir -p ./dev-log + exec 1>>"$logfile" 2>&1 +else + echo "logging to console..." && echo +fi + +echo "########################################################" +date +echo && echo building docker images for version: $VERSION && echo +echo "(Repo: $REPO, SBT-Version: $SBT_VERSION)" +echo "########################################################" && echo && echo && echo + +echo building base-binaries +time docker build -f ./base-binaries.dockerfile --build-arg SBT_VERSION=${SBT_VERSION} --tag ${REPO}:base-binaries-$VERSION .. +status=$? + +if [[ $status -eq 0 ]]; then + echo && echo && echo && echo && echo "########################################################" + echo building base + time docker build -f ./base.dockerfile --tag ${REPO}:base-$VERSION . + status=$? +fi + +if [[ $status -eq 0 ]]; then + echo && echo && echo && echo && echo "########################################################" + echo building restserver + time docker build -f ./restserver.dockerfile --tag ${REPO}:restserver-$VERSION --build-arg REPO=$REPO --build-arg VERSION=$VERSION . + status=$? + + if [[ $status -eq 0 ]] && [[ "$VERSION" != "SNAPSHOT" ]]; then + docker tag ${REPO}:restserver-$VERSION ${REPO}:restserver-LATEST + fi +fi + +if [[ $status -eq 0 ]]; then + echo && echo && echo && echo && echo "########################################################" + echo building joex base + time docker build -f ./joex-base.dockerfile --tag ${REPO}:joex-base-$VERSION --build-arg REPO=$REPO --build-arg VERSION=$VERSION . + status=$? +fi +if [[ $status -eq 0 ]]; then + echo && echo && echo && echo && echo "########################################################" + echo building joex + time docker build -f ./joex.dockerfile --tag ${REPO}:joex-$VERSION --build-arg REPO=$REPO --build-arg VERSION=$VERSION . + status=$? + + if [[ $status -eq 0 ]] && [[ "$VERSION" != "SNAPSHOT" ]]; then + docker tag ${REPO}:joex-$VERSION ${REPO}:joex-LATEST + fi +fi + +if [[ $status -eq 0 ]]; then + echo && echo && echo && echo && echo "########################################################" + echo building consumedir + time docker build -f ./consumedir.dockerfile --tag ${REPO}:consumedir-$VERSION --build-arg REPO=$REPO --build-arg VERSION=$VERSION . + status=$? + + if [[ $status -eq 0 ]] && [[ "$VERSION" != "SNAPSHOT" ]]; then + docker tag ${REPO}:consumedir-$VERSION ${REPO}:consumedir-LATEST + fi +fi + +echo && echo && echo +echo "######################## done ########################" +date diff --git a/docker/dev-push-images.sh b/docker/dev-push-images.sh new file mode 100755 index 00000000..60c6aaa0 --- /dev/null +++ b/docker/dev-push-images.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +REPO="eikek0/docspell" +if [ $# -eq 1 ]; then + REPO=$1 +fi + +TMP_VERSION=$(cat ../version.sbt) +TMP_VERSION=${TMP_VERSION:25:99} +VERSION=${TMP_VERSION%\"} + +if [[ $VERSION == *"SNAPSHOT" ]]; then + VERSION=SNAPSHOT +else + VERSION=v$VERSION +fi + +echo && echo pushing docker images for version: $VERSION && echo && echo + +# disabled as this doesn't to be on Docker Hub +# echo pushing base +# docker ${REPO}-base:$VERSION . + +echo pushing restserver +docker push ${REPO} + +exit 0 + +## still needs to be tested for a tagged version - that's why old version below is kept! + +if [[ $? -eq 0 ]]; then + echo pushing restserver + docker push ${REPO}:restserver-$VERSION +fi + +if [[ $? -eq 0 ]]; then + echo pushing joex base + docker push ${REPO}:joex-base-$VERSION +fi +if [[ $? -eq 0 ]]; then + echo pushing joex + docker push ${REPO}:joex-$VERSION +fi + +if [[ $? -eq 0 ]]; then + echo pushing consumedir + docker push ${REPO}:consumedir-$VERSION +fi diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f5a1f81a..d909b887 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,59 +1,69 @@ version: '3.7' services: + restserver: - image: eikek0/docspell:restserver-latest + image: eikek0/docspell:restserver-LATEST container_name: docspell-restserver - command: ${DOCSPELL_RESTSERVER_JVM_ARGS} -- /opt/docspell.conf + restart: unless-stopped ports: - "7880:7880" volumes: - - ${PWD}/docspell.conf:/opt/docspell.conf - environment: - - DOCSPELL_HEADER_VALUE=${DOCSPELL_HEADER_VALUE:-none} + - ./docspell.conf:/opt/docspell.conf + env_file: ./.env depends_on: - - db - solr + joex: - image: eikek0/docspell:joex-latest + image: eikek0/docspell:joex-LATEST container_name: docspell-joex - command: ${DOCSPELL_JOEX_JVM_ARGS:--J-Xmx1536M} -- /opt/docspell.conf + restart: unless-stopped + env_file: ./.env ports: - "7878:7878" volumes: - - ${PWD}/docspell.conf:/opt/docspell.conf + - ./docspell.conf:/opt/docspell.conf depends_on: - - db - solr + + consumedir: - image: eikek0/docspell:consumedir-latest + image: eikek0/docspell:consumedir-LATEST container_name: docspell-consumedir - command: --path /opt/docs -i --iheader "Docspell-Integration:${DOCSPELL_HEADER_VALUE}" -m http://restserver:7880/api/v1/open/integration/item - environment: - - DOCSPELL_HEADER_VALUE=${DOCSPELL_HEADER_VALUE:-none} + restart: unless-stopped + env_file: ./.env volumes: - - ${PWD}/docs:/opt/docs + - ./docs:/opt/docs depends_on: - restserver + db: image: postgres:11.7 container_name: postgres_db + restart: unless-stopped volumes: - - postgres_data:/var/lib/postgresql/data/ + - docspell-postgres_data:/var/lib/postgresql/data/ environment: - - POSTGRES_USER=dbuser - - POSTGRES_PASSWORD=dbpass - - POSTGRES_DB=dbname + - POSTGRES_USER=$DB_USER + - POSTGRES_PASSWORD=$DB_PASS + - POSTGRES_DB=$DB_NAME + solr: image: solr:8 container_name: docspell-solr - ports: - - "8983:8983" + restart: unless-stopped volumes: - - solr_data:/var/solr + - docspell-solr_data:/var/solr command: - solr-precreate - docspell + healthcheck: + test: ["CMD", "curl", "f", "http://localhost:8983/solr/docspell/admin/ping"] + interval: 1m + timeout: 10s + retries: 2 + start_period: 30s + volumes: - postgres_data: - solr_data: + docspell-postgres_data: + docspell-solr_data: diff --git a/docker/docspell.conf b/docker/docspell.conf index 9086345a..a1c35bd7 100644 --- a/docker/docspell.conf +++ b/docker/docspell.conf @@ -20,9 +20,9 @@ docspell.server { } backend { jdbc { - url = "jdbc:postgresql://db:5432/dbname" - user = "dbuser" - password = "dbpass" + url = "jdbc:"${DB_TYPE}"://"${DB_HOST}":"${DB_PORT}"/"${DB_NAME} + user = ${DB_USER} + password = ${DB_PASS} } } } @@ -33,9 +33,9 @@ docspell.joex { address = "0.0.0.0" } jdbc { - url = "jdbc:postgresql://db:5432/dbname" - user = "dbuser" - password = "dbpass" + url = "jdbc:"${DB_TYPE}"://"${DB_HOST}":"${DB_PORT}"/"${DB_NAME} + user = ${DB_USER} + password = ${DB_PASS} } full-text-search { enabled = true diff --git a/docker/hooks/build b/docker/hooks/build new file mode 100644 index 00000000..e0a249cf --- /dev/null +++ b/docker/hooks/build @@ -0,0 +1,50 @@ +#!/bin/bash + +# remove trailing slash if exists +DOCKER_REPO=${DOCKER_REPO%/} + +echo "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" +date +echo building image \"$IMAGE_NAME\" in repository \"$DOCKER_REPO\" using dockerfile \"$DOCKERFILE_PATH\" +echo image was triggered by automated build for tag \"$DOCKER_TAG\" +echo " based on branch \"$SOURCE_BRANCH\" and commit \"$SOURCE_COMMIT\"" +echo " commit message was \"$COMMIT_MSG\"" +echo && echo && echo +echo "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" + +# verify that version.sbt and tag match for non-snapshot (ENV variable DOCKER_VERIFY_TAG must be set to 1) +if [ $DOCKER_VERIFY_TAG -eq 1 ] && [[ "$DOCKER_TAG" != *"-SNAPSHOT" ]]; then + echo validating version number... + TMP_VERSION=$(cat ../version.sbt) + TMP_VERSION=${TMP_VERSION:25:99} + VERSION=${TMP_VERSION%\"} + if [ "$DOCKER_TAG" != "base-v$VERSION" ]; then + echo "version number mismatch (Docker/Tag: $DOCKER_TAG, Project: $VERSION), aborting!" + exit 1 + fi +fi + + +./dev-build-images.sh "$DOCKER_REPO" +status=$? + +if [[ $status -eq 0 ]]; then + echo "#### pushing images ####" + ./dev-push-images.sh "$DOCKER_REPO" +fi + + +echo && echo && date +echo "||||||||||||||||||||||||||||||||||||||||||||| done |||||||||||||||||||||||||||||||||||||||||||||" + + +################################### +# available variables +## SOURCE_BRANCH: the name of the branch or the tag that is currently being tested. +## SOURCE_COMMIT: the SHA1 hash of the commit being tested. +## COMMIT_MSG: the message from the commit being tested and built. +## DOCKER_REPO: the name of the Docker repository being built. +## DOCKERFILE_PATH: the dockerfile currently being built. +## DOCKER_TAG: the Docker repository tag being built. +## IMAGE_NAME: the name and tag of the Docker repository being built. (This variable is a combination of DOCKER_REPO:DOCKER_TAG.) +################################### diff --git a/docker/joex-base.dockerfile b/docker/joex-base.dockerfile index 783b6053..caa894bc 100644 --- a/docker/joex-base.dockerfile +++ b/docker/joex-base.dockerfile @@ -1,11 +1,14 @@ -FROM alpine:latest +## JOEX-BASE +ARG VERSION= +ARG REPO= -ENV UNO_URL https://raw.githubusercontent.com/unoconv/unoconv/0.9.0/unoconv -LABEL maintainer="eikek0 " +FROM ${REPO}:base-${VERSION} + +ARG UNO_URL=https://raw.githubusercontent.com/unoconv/unoconv/0.9.0/unoconv +ENV JAVA_OPTS="-Xmx1536M" RUN apk add --no-cache openjdk11-jre \ - unzip \ bash \ curl \ ghostscript \ @@ -35,7 +38,5 @@ RUN apk add --no-cache openjdk11-jre \ && pip3 install ocrmypdf \ && curl -Ls $UNO_URL -o /usr/local/bin/unoconv \ && chmod +x /usr/local/bin/unoconv \ - && apk del curl unzip libxml2-dev libxslt-dev zlib-dev g++ python3-dev py3-pip libffi-dev qpdf-dev openssl-dev - -# Required for unoconv -RUN ln -s /usr/bin/python3 /usr/bin/python + && apk del curl libxml2-dev libxslt-dev zlib-dev g++ python3-dev py3-pip libffi-dev qpdf-dev openssl-dev \ + && ln -s /usr/bin/python3 /usr/bin/python diff --git a/docker/entrypoint-joex.sh b/docker/joex-entrypoint.sh similarity index 54% rename from docker/entrypoint-joex.sh rename to docker/joex-entrypoint.sh index f1d95857..3ed3df29 100755 --- a/docker/entrypoint-joex.sh +++ b/docker/joex-entrypoint.sh @@ -3,4 +3,4 @@ echo "Starting unoconv listener" unoconv -l & -/opt/docspell-joex-0.12.0/bin/docspell-joex "$@" +/opt/docspell-joex/bin/docspell-joex "$@" diff --git a/docker/joex.dockerfile b/docker/joex.dockerfile index aec6d522..2c14613f 100644 --- a/docker/joex.dockerfile +++ b/docker/joex.dockerfile @@ -1,21 +1,20 @@ -FROM eikek0/docspell:joex-base-latest +## JOEX -LABEL maintainer="eikek0 " +ARG VERSION= +ARG REPO= -RUN apk add --no-cache openjdk11-jre \ - unzip \ - bash \ - curl \ - && mkdir -p /opt \ - && cd /opt \ - && curl -L -o docspell.zip https://github.com/eikek/docspell/releases/download/v0.12.0/docspell-joex-0.12.0.zip \ - && unzip docspell.zip \ - && rm docspell.zip \ - && apk del curl unzip +# hack to use args in from +FROM ${REPO}:base-binaries-${VERSION} as docspell-base-binaries -COPY entrypoint-joex.sh /opt/entrypoint.sh +FROM ${REPO}:joex-base-${VERSION} +COPY --from=docspell-base-binaries /opt/docspell-joex /opt/docspell-joex +COPY joex-entrypoint.sh /opt/joex-entrypoint.sh + +ENTRYPOINT ["/opt/joex-entrypoint.sh"] +CMD ["/opt/docspell.conf"] EXPOSE 7878 -ENTRYPOINT ["/opt/entrypoint.sh"] +HEALTHCHECK --interval=1m --timeout=10s --retries=2 --start-period=10s \ + CMD pgrep -f joex/lib diff --git a/docker/push-images.sh b/docker/push-images.sh deleted file mode 100755 index ab76dffe..00000000 --- a/docker/push-images.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# Update the versions in joex.dockerfile and restserver.dockerfile, -# docker-compose.yml and joex/entrypoint.sh; update versions here -docker push eikek0/docspell:joex-0.12.0 -docker push eikek0/docspell:restserver-0.12.0 -docker push eikek0/docspell:consumedir-0.12.0 - -docker push eikek0/docspell:restserver-latest -docker push eikek0/docspell:joex-latest -docker push eikek0/docspell:consumedir-latest - -# test with docker-compose up diff --git a/docker/push-joex-base.sh b/docker/push-joex-base.sh deleted file mode 100755 index 90ce8893..00000000 --- a/docker/push-joex-base.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -e - -docker push eikek0/docspell:joex-base-1 -docker push eikek0/docspell:joex-base-latest diff --git a/docker/restserver-entrypoint.sh b/docker/restserver-entrypoint.sh new file mode 100755 index 00000000..88ce5cb3 --- /dev/null +++ b/docker/restserver-entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/opt/docspell-restserver/bin/docspell-restserver $@ diff --git a/docker/restserver.dockerfile b/docker/restserver.dockerfile index 4c2c6dae..53bd62ee 100644 --- a/docker/restserver.dockerfile +++ b/docker/restserver.dockerfile @@ -1,16 +1,21 @@ -FROM alpine:latest +## RESTSERVER -LABEL maintainer="eikek0 " +ARG VERSION= +ARG REPO= -RUN apk add --no-cache openjdk11-jre unzip curl bash +# hack to use args in from +FROM ${REPO}:base-binaries-${VERSION} as docspell-base-binaries -RUN mkdir -p /opt \ - && cd /opt \ - && curl -L -o docspell.zip https://github.com/eikek/docspell/releases/download/v0.12.0/docspell-restserver-0.12.0.zip \ - && unzip docspell.zip \ - && rm docspell.zip \ - && apk del unzip curl +FROM ${REPO}:base-${VERSION} + +RUN apk add --no-cache --virtual .restserver-dependencies openjdk11-jre bash +COPY --from=docspell-base-binaries /opt/docspell-restserver /opt/docspell-restserver +COPY restserver-entrypoint.sh /opt/restserver-entrypoint.sh + +ENTRYPOINT ["/opt/restserver-entrypoint.sh"] +CMD ["/opt/docspell.conf"] EXPOSE 7880 -ENTRYPOINT ["/opt/docspell-restserver-0.12.0/bin/docspell-restserver"] +HEALTHCHECK --interval=1m --timeout=10s --retries=2 --start-period=30s \ + CMD wget --spider http://localhost:7880