diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 7ef1f32..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,98 +0,0 @@ -version: 2 - -.job_template: &job_template - machine: - enabled: true - steps: - - checkout - - run: - command: ./circle-test.sh - - persist_to_workspace: - root: . - paths: [ 'ci-workspace' ] - -jobs: - amd64: - <<: *job_template - arm64: - <<: *job_template - armhf: - <<: *job_template - armel: - <<: *job_template - deploy: - docker: - - image: circleci/python:latest - steps: - - setup_remote_docker: - version: 18.06.0-ce - - checkout - - attach_workspace: - at: . - - run: - command: ./circle-deploy.sh - - - -workflows: - version: 2 - build: - jobs: - - amd64: - filters: - tags: - only: /^v.*/ - - arm64: - filters: - tags: - only: /^v.*/ - - armhf: - filters: - tags: - only: /^v.*/ - #- armel: - # filters: - # tags: - # only: /^v.*/ - - deploy: - requires: - - amd64 - - arm64 - - armhf - #- armel - filters: - tags: - only: /^v.*/ - nightly_build: - triggers: - - schedule: - cron: "0 0 * * *" - filters: - branches: - only: 'beta-v5.0' - jobs: - - amd64: - filters: - tags: - only: /^v.*/ - - arm64: - filters: - tags: - only: /^v.*/ - - armhf: - filters: - tags: - only: /^v.*/ - #- armel: - # filters: - # tags: - # only: /^v.*/ - - deploy: - requires: - - amd64 - - arm64 - - armhf - #- armel - filters: - tags: - only: /^v.*/ diff --git a/.github/workflows/test-and-build.yaml b/.github/workflows/test-and-build.yaml new file mode 100644 index 0000000..d25362e --- /dev/null +++ b/.github/workflows/test-and-build.yaml @@ -0,0 +1,64 @@ +name: Test & Build +on: + push: + branches: + - master + - dev + - v* + - beta-v* + - release/* + tags: + pull_request: + +#env: +# DOCKER_HUB_REPO: pihole + +jobs: + test-and-build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ARCH: [amd64, armhf, arm64] + DEBIAN_VERSION: [stretch, buster] + env: + ARCH: ${{matrix.ARCH}} + DEBIAN_VERSION: ${{matrix.DEBIAN_VERSION}} + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + - name: Run Tests + run: | + echo "Building ${ARCH}-${DEBIAN_VERSION}" + ./gh-actions-test.sh + - name: Push the ARCH image + if: github.event_name != 'pull_request' + run: | + . gh-actions-vars.sh + echo "${{ secrets.DOCKERHUB_PASS }}" | docker login --username="${{ secrets.DOCKERHUB_USER }}" --password-stdin + docker push "${ARCH_IMAGE}" + - name: Upload gh-workspace + if: github.event_name != 'pull_request' + uses: actions/upload-artifact@v1 + with: + name: gh-workspace + path: .gh-workspace + + publish: + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + needs: test-and-build + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + - name: Download workspace files + uses: actions/download-artifact@v1 + with: + name: gh-workspace + path: .gh-workspace + - name: Tag and Publish multi-arch images + env: + DOCKERHUB_PASS: ${{ secrets.DOCKERHUB_PASS }} + DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} + run: | + ./gh-actions-deploy.sh diff --git a/.gitignore b/.gitignore index fd32835..4e7bc94 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __pycache__ UNKNOWN.egg-info .env ci-workspace +.gh-workspace # WIP/test stuff doco.yml diff --git a/Dockerfile.template b/Dockerfile similarity index 62% rename from Dockerfile.template rename to Dockerfile index cce6af4..9fff81c 100644 --- a/Dockerfile.template +++ b/Dockerfile @@ -1,7 +1,11 @@ -FROM {{ pihole.base }} +ARG PIHOLE_BASE +FROM $PIHOLE_BASE -ENV ARCH {{ pihole.arch }} -ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/{{ pihole.s6_version }}/s6-overlay-{{ pihole.s6arch }}.tar.gz +ARG PIHOLE_ARCH +ENV PIHOLE_ARCH "${PIHOLE_ARCH}" +ARG S6_ARCH +ARG S6_VERSION +ENV S6OVERLAY_RELEASE "https://github.com/just-containers/s6-overlay/releases/download/${S6_VERSION}/s6-overlay-${S6_ARCH}.tar.gz" COPY install.sh /usr/local/bin/install.sh COPY VERSION /etc/docker-pi-hole-version @@ -16,8 +20,10 @@ ADD s6/debian-root / COPY s6/service /usr/local/bin/service # php config start passes special ENVs into -ENV PHP_ENV_CONFIG '{{ pihole.php_env_config }}' -ENV PHP_ERROR_LOG '{{ pihole.php_error_log }}' +ARG PHP_ENV_CONFIG +ENV PHP_ENV_CONFIG "${PHP_ENV_CONFIG}" +ARG PHP_ERROR_LOG +ENV PHP_ERROR_LOG "${PHP_ERROR_LOG}" COPY ./start.sh / COPY ./bash_functions.sh / @@ -37,11 +43,14 @@ ENV ServerIP 0.0.0.0 ENV FTL_CMD no-daemon ENV DNSMASQ_USER root -ENV VERSION {{ pihole.version }} +ARG PIHOLE_VERSION +ENV VERSION "${PIHOLE_VERSION}" ENV PATH /opt/pihole:${PATH} -LABEL image="{{ pihole.name }}:{{ pihole.version }}_{{ pihole.arch }}" -LABEL maintainer="{{ pihole.maintainer }}" +ARG NAME +LABEL image="${NAME}:${PIHOLE_VERSION}_${PIHOLE_ARCH}" +ARG MAINTAINER +LABEL maintainer="${MAINTAINER}" LABEL url="https://www.github.com/pi-hole/docker-pi-hole" HEALTHCHECK CMD dig +norecurse +retry=0 @127.0.0.1 pi.hole || exit 1 diff --git a/Dockerfile.py b/Dockerfile.py index 0db8d92..44efac4 100755 --- a/Dockerfile.py +++ b/Dockerfile.py @@ -2,39 +2,24 @@ """ Dockerfile.py - generates and build dockerfiles Usage: - Dockerfile.py [--hub_tag=] [--arch= ...] [-v] [-t] [--no-build | --no-generate] [--no-cache] + Dockerfile.py [--hub_tag=] [--arch= ...] [--debian= ...] [-v] [-t] [--no-build] [--no-cache] [--fail-fast] Options: - --no-build Skip building the docker images - --no-cache Build without using any cache data - --no-generate Skip generating Dockerfiles from template - --hub_tag= What the Docker Hub Image should be tagged as [default: None] - --arch= What Architecture(s) to build [default: amd64 armel armhf arm64] - -v Print docker's command output [default: False] - -t Print docker's build time [default: False] + --no-build Skip building the docker images + --no-cache Build without using any cache data + --fail-fast Exit on first build error + --hub_tag= What the Docker Hub Image should be tagged as [default: None] + --arch= What Architecture(s) to build [default: amd64 armel armhf arm64] + --debian= What debian version(s) to build [default: stretch buster] + -v Print docker's command output [default: False] + -t Print docker's build time [default: False] Examples: """ - - -from jinja2 import Environment, FileSystemLoader from docopt import docopt import os -import subprocess import sys - -THIS_DIR = os.path.dirname(os.path.abspath(__file__)) - -base_vars = { - 'name': 'pihole/pihole', - 'maintainer' : 'adam@diginc.us', - 's6_version' : 'v1.22.1.0', -} - -os_base_vars = { - 'php_env_config': '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf', - 'php_error_log': '/var/log/lighttpd/error.log' -} +import subprocess __version__ = None dot = os.path.abspath('.') @@ -42,109 +27,60 @@ with open('{}/VERSION'.format(dot), 'r') as v: raw_version = v.read().strip() __version__ = raw_version.replace('release/', 'release-') -images = { - __version__: [ - { - 'base': 'pihole/debian-base:latest', - 'arch': 'amd64', - 's6arch': 'amd64', - }, - { - 'base': 'multiarch/debian-debootstrap:armel-stretch-slim', - 'arch': 'armel', - 's6arch': 'arm', - }, - { - 'base': 'multiarch/debian-debootstrap:armhf-stretch-slim', - 'arch': 'armhf', - 's6arch' : 'arm', - }, - { - 'base': 'multiarch/debian-debootstrap:arm64-stretch-slim', - 'arch': 'arm64', - 's6arch' : 'aarch64', - } - ] -} -def generate_dockerfiles(args): - if args['--no-generate']: - print(" ::: Skipping Dockerfile generation") - return - - for version, archs in images.items(): - for image in archs: - if image['arch'] not in args['--arch']: - continue - s6arch = image['s6arch'] if image['s6arch'] else image['arch'] - merged_data = dict( - list({ 'version': version }.items()) + - list(base_vars.items()) + - list(os_base_vars.items()) + - list(image.items()) + - list({ 's6arch': s6arch }.items()) - ) - j2_env = Environment(loader=FileSystemLoader(THIS_DIR), - trim_blocks=True) - template = j2_env.get_template('Dockerfile.template') - - dockerfile = 'Dockerfile_{}'.format(image['arch']) - with open(dockerfile, 'w') as f: - f.write(template.render(pihole=merged_data)) - - -def build_dockerfiles(args): +def build_dockerfiles(args) -> bool: + all_success = True + if args['-v']: + print(args) if args['--no-build']: print(" ::: Skipping Dockerfile building") - return + return all_success for arch in args['--arch']: - build('pihole', arch, args) + for debian_version in args['--debian']: + all_success = build('pihole', arch, debian_version, args['--hub_tag'], args['-t'], args['--no-cache'], args['-v']) and all_success + if not all_success and args['--fail-fast']: + return False + return all_success -def run_and_stream_command_output(command, args): +def run_and_stream_command_output(command, environment_vars, verbose) -> bool: print("Running", command) - build_result = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - bufsize=1, universal_newlines=True) - if args['-v']: + build_result = subprocess.Popen(command.split(), env=environment_vars, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True) + if verbose: while build_result.poll() is None: for line in build_result.stdout: print(line, end='') build_result.wait() if build_result.returncode != 0: - print(" ::: Error running {}".format(command)) + print(" ::: Error running".format(command)) print(build_result.stderr) - sys.exit(build_resuilt.returncode) + return build_result.returncode == 0 -def build(docker_repo, arch, args): - dockerfile = 'Dockerfile_{}'.format(arch) - repo_tag = '{}:{}_{}'.format(docker_repo, __version__, arch) - cached_image = '{}/{}'.format('pihole', repo_tag) - print(" ::: Building {}".format(repo_tag)) - time='' - if args['-t']: - time='time ' - no_cache = '' - if args['--no-cache']: - no_cache = '--no-cache' - build_command = '{time}docker build {no_cache} --pull --cache-from="{cache},{create_tag}" -f {dockerfile} -t {create_tag} .'\ - .format(time=time, no_cache=no_cache, cache=cached_image, dockerfile=dockerfile, create_tag=repo_tag) - print(" ::: Building {} into {}".format(dockerfile, repo_tag)) - run_and_stream_command_output(build_command, args) - if args['-v']: +def build(docker_repo: str, arch: str, debian_version: str, hub_tag: str, show_time: bool, no_cache: bool, verbose: bool) -> bool: + create_tag = f'{docker_repo}:{__version__}-{arch}-{debian_version}' + print(f' ::: Building {create_tag}') + time_arg = 'time' if show_time else '' + cache_arg = '--no-cache' if no_cache else '' + build_env = os.environ.copy() + build_env['PIHOLE_VERSION'] = __version__ + build_env['DEBIAN_VERSION'] = debian_version + build_command = f'{time_arg} docker-compose -f build.yml build {cache_arg} --pull {arch}' + print(f' ::: Building {arch} into {create_tag}') + success = run_and_stream_command_output(build_command, build_env, verbose) + if verbose: print(build_command, '\n') - if args['--hub_tag']: - hub_tag_command = "{time}docker tag {create_tag} {hub_tag}"\ - .format(time=time, create_tag=repo_tag, hub_tag=args['--hub_tag']) - print(" ::: Tagging {} into {}".format(repo_tag, args['--hub_tag'])) - run_and_stream_command_output(hub_tag_command, args) + if success and hub_tag: + hub_tag_command = f'{time_arg} docker tag {create_tag} {hub_tag}' + print(f' ::: Tagging {create_tag} into {hub_tag}') + success = run_and_stream_command_output(hub_tag_command, build_env, verbose) + return success if __name__ == '__main__': args = docopt(__doc__, version='Dockerfile 1.1') - if args['-v']: - print(args) - - generate_dockerfiles(args) - build_dockerfiles(args) + success = build_dockerfiles(args) + exit_code = 0 if success else 1 + sys.exit(exit_code) diff --git a/Dockerfile.sh b/Dockerfile.sh index 1446b0f..76ae6e0 100755 --- a/Dockerfile.sh +++ b/Dockerfile.sh @@ -1,9 +1,13 @@ #!/usr/bin/env bash -# alpine sh only +# @param ${ARCH} The architecture to build. Example: amd64 +# @param ${DEBIAN_VERSION} The debian version to build. Example: buster +# @param ${ARCH_IMAGE} What the Docker Hub Image should be tagged as [default: None] set -eux -./Dockerfile.py -v --no-cache --arch="${ARCH}" --hub_tag="${ARCH_IMAGE}" -# TODO: Add junitxml output and have circleci consume it +./Dockerfile.py -v --no-cache --arch="${ARCH}" --debian="${DEBIAN_VERSION}" --hub_tag="${ARCH_IMAGE}" +docker images + +# TODO: Add junitxml output and have something consume it # 2 parallel max b/c race condition with docker fixture (I think?) py.test -vv -n 2 -k "${ARCH}" ./test/ diff --git a/Dockerfile_amd64 b/Dockerfile_amd64 deleted file mode 100644 index f2f1532..0000000 --- a/Dockerfile_amd64 +++ /dev/null @@ -1,49 +0,0 @@ -FROM pihole/debian-base:latest - -ENV ARCH amd64 -ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-amd64.tar.gz - -COPY install.sh /usr/local/bin/install.sh -COPY VERSION /etc/docker-pi-hole-version -ENV PIHOLE_INSTALL /root/ph_install.sh - -RUN bash -ex install.sh 2>&1 && \ - rm -rf /var/cache/apt/archives /var/lib/apt/lists/* - -ENTRYPOINT [ "/s6-init" ] - -ADD s6/debian-root / -COPY s6/service /usr/local/bin/service - -# php config start passes special ENVs into -ENV PHP_ENV_CONFIG '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf' -ENV PHP_ERROR_LOG '/var/log/lighttpd/error.log' -COPY ./start.sh / -COPY ./bash_functions.sh / - -# IPv6 disable flag for networks/devices that do not support it -ENV IPv6 True - -EXPOSE 53 53/udp -EXPOSE 67/udp -EXPOSE 80 -EXPOSE 443 - -ENV S6_LOGGING 0 -ENV S6_KEEP_ENV 1 -ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2 - -ENV ServerIP 0.0.0.0 -ENV FTL_CMD no-daemon -ENV DNSMASQ_USER root - -ENV VERSION v5.1.1 -ENV PATH /opt/pihole:${PATH} - -LABEL image="pihole/pihole:v5.1.1_amd64" -LABEL maintainer="adam@diginc.us" -LABEL url="https://www.github.com/pi-hole/docker-pi-hole" - -HEALTHCHECK CMD dig +norecurse +retry=0 @127.0.0.1 pi.hole || exit 1 - -SHELL ["/bin/bash", "-c"] \ No newline at end of file diff --git a/Dockerfile_arm64 b/Dockerfile_arm64 deleted file mode 100644 index dd7a66c..0000000 --- a/Dockerfile_arm64 +++ /dev/null @@ -1,49 +0,0 @@ -FROM multiarch/debian-debootstrap:arm64-stretch-slim - -ENV ARCH arm64 -ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-aarch64.tar.gz - -COPY install.sh /usr/local/bin/install.sh -COPY VERSION /etc/docker-pi-hole-version -ENV PIHOLE_INSTALL /root/ph_install.sh - -RUN bash -ex install.sh 2>&1 && \ - rm -rf /var/cache/apt/archives /var/lib/apt/lists/* - -ENTRYPOINT [ "/s6-init" ] - -ADD s6/debian-root / -COPY s6/service /usr/local/bin/service - -# php config start passes special ENVs into -ENV PHP_ENV_CONFIG '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf' -ENV PHP_ERROR_LOG '/var/log/lighttpd/error.log' -COPY ./start.sh / -COPY ./bash_functions.sh / - -# IPv6 disable flag for networks/devices that do not support it -ENV IPv6 True - -EXPOSE 53 53/udp -EXPOSE 67/udp -EXPOSE 80 -EXPOSE 443 - -ENV S6_LOGGING 0 -ENV S6_KEEP_ENV 1 -ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2 - -ENV ServerIP 0.0.0.0 -ENV FTL_CMD no-daemon -ENV DNSMASQ_USER root - -ENV VERSION v5.1.1 -ENV PATH /opt/pihole:${PATH} - -LABEL image="pihole/pihole:v5.1.1_arm64" -LABEL maintainer="adam@diginc.us" -LABEL url="https://www.github.com/pi-hole/docker-pi-hole" - -HEALTHCHECK CMD dig +norecurse +retry=0 @127.0.0.1 pi.hole || exit 1 - -SHELL ["/bin/bash", "-c"] \ No newline at end of file diff --git a/Dockerfile_armel b/Dockerfile_armel deleted file mode 100644 index 687f964..0000000 --- a/Dockerfile_armel +++ /dev/null @@ -1,49 +0,0 @@ -FROM multiarch/debian-debootstrap:armel-stretch-slim - -ENV ARCH armel -ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-arm.tar.gz - -COPY install.sh /usr/local/bin/install.sh -COPY VERSION /etc/docker-pi-hole-version -ENV PIHOLE_INSTALL /root/ph_install.sh - -RUN bash -ex install.sh 2>&1 && \ - rm -rf /var/cache/apt/archives /var/lib/apt/lists/* - -ENTRYPOINT [ "/s6-init" ] - -ADD s6/debian-root / -COPY s6/service /usr/local/bin/service - -# php config start passes special ENVs into -ENV PHP_ENV_CONFIG '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf' -ENV PHP_ERROR_LOG '/var/log/lighttpd/error.log' -COPY ./start.sh / -COPY ./bash_functions.sh / - -# IPv6 disable flag for networks/devices that do not support it -ENV IPv6 True - -EXPOSE 53 53/udp -EXPOSE 67/udp -EXPOSE 80 -EXPOSE 443 - -ENV S6_LOGGING 0 -ENV S6_KEEP_ENV 1 -ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2 - -ENV ServerIP 0.0.0.0 -ENV FTL_CMD no-daemon -ENV DNSMASQ_USER root - -ENV VERSION v5.1.1 -ENV PATH /opt/pihole:${PATH} - -LABEL image="pihole/pihole:v5.1.1_armel" -LABEL maintainer="adam@diginc.us" -LABEL url="https://www.github.com/pi-hole/docker-pi-hole" - -HEALTHCHECK CMD dig +norecurse +retry=0 @127.0.0.1 pi.hole || exit 1 - -SHELL ["/bin/bash", "-c"] \ No newline at end of file diff --git a/Dockerfile_armhf b/Dockerfile_armhf deleted file mode 100644 index e626156..0000000 --- a/Dockerfile_armhf +++ /dev/null @@ -1,49 +0,0 @@ -FROM multiarch/debian-debootstrap:armhf-stretch-slim - -ENV ARCH armhf -ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.22.1.0/s6-overlay-arm.tar.gz - -COPY install.sh /usr/local/bin/install.sh -COPY VERSION /etc/docker-pi-hole-version -ENV PIHOLE_INSTALL /root/ph_install.sh - -RUN bash -ex install.sh 2>&1 && \ - rm -rf /var/cache/apt/archives /var/lib/apt/lists/* - -ENTRYPOINT [ "/s6-init" ] - -ADD s6/debian-root / -COPY s6/service /usr/local/bin/service - -# php config start passes special ENVs into -ENV PHP_ENV_CONFIG '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf' -ENV PHP_ERROR_LOG '/var/log/lighttpd/error.log' -COPY ./start.sh / -COPY ./bash_functions.sh / - -# IPv6 disable flag for networks/devices that do not support it -ENV IPv6 True - -EXPOSE 53 53/udp -EXPOSE 67/udp -EXPOSE 80 -EXPOSE 443 - -ENV S6_LOGGING 0 -ENV S6_KEEP_ENV 1 -ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 2 - -ENV ServerIP 0.0.0.0 -ENV FTL_CMD no-daemon -ENV DNSMASQ_USER root - -ENV VERSION v5.1.1 -ENV PATH /opt/pihole:${PATH} - -LABEL image="pihole/pihole:v5.1.1_armhf" -LABEL maintainer="adam@diginc.us" -LABEL url="https://www.github.com/pi-hole/docker-pi-hole" - -HEALTHCHECK CMD dig +norecurse +retry=0 @127.0.0.1 pi.hole || exit 1 - -SHELL ["/bin/bash", "-c"] \ No newline at end of file diff --git a/Dockerfile_build b/Dockerfile_build index fdf3c4c..70e5a28 100644 --- a/Dockerfile_build +++ b/Dockerfile_build @@ -3,15 +3,15 @@ FROM python:buster # Only works for docker CLIENT (bind mounted socket) COPY --from=docker:18.09.3 /usr/local/bin/docker /usr/local/bin/ -# Based on https://github.com/Ilhicas/alpine-pipenv ARG packages RUN apt-get update && \ apt-get install -y python3-dev curl gcc make \ libffi-dev libssl-dev ${packages} \ && pip3 install -U pip pipenv +RUN curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose && \ + chmod +x /usr/local/bin/docker-compose -# -v "$(pwd):/$(pwd)" -w "$(pwd)" to prevent nested docker path confusion COPY ./Dockerfile.sh /usr/local/bin/ COPY Pipfile* /root/ WORKDIR /root @@ -19,7 +19,6 @@ WORKDIR /root RUN pipenv install --system \ && sed -i 's|/bin/sh|/bin/bash|g' /usr/local/lib/python3.8/site-packages/testinfra/backend/docker.py - RUN echo "set -ex && Dockerfile.sh && \$@" > /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh ENTRYPOINT entrypoint.sh diff --git a/Pipfile.lock b/Pipfile.lock index d6cfad0..80ca574 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -324,13 +324,16 @@ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", @@ -347,7 +350,9 @@ "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" ], "index": "pypi", "version": "==1.1.1" @@ -363,10 +368,10 @@ }, "packaging": { "hashes": [ - "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb", - "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8" + "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", + "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" ], - "version": "==20.0" + "version": "==20.4" }, "pathlib2": { "hashes": [ diff --git a/README.md b/README.md index 81d9f5a..9a05db6 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,6 @@ services: volumes: - './etc-pihole/:/etc/pihole/' - './etc-dnsmasq.d/:/etc/dnsmasq.d/' - dns: - - 127.0.0.1 - - 1.1.1.1 # Recommended but not required (DHCP needs NET_ADMIN) # https://github.com/pi-hole/docker-pi-hole#note-on-capabilities cap_add: @@ -69,7 +66,7 @@ A [Docker](https://www.docker.com/what-docker) project to make a lightweight x86 2) Use the above quick start example, customize if desired. 3) Enjoy! -[![Build Status](https://api.travis-ci.org/pi-hole/docker-pi-hole.svg?branch=master)](https://travis-ci.org/pi-hole/docker-pi-hole) [![Docker Stars](https://img.shields.io/docker/stars/pihole/pihole.svg?maxAge=604800)](https://store.docker.com/community/images/pihole/pihole) [![Docker Pulls](https://img.shields.io/docker/pulls/pihole/pihole.svg?maxAge=604800)](https://store.docker.com/community/images/pihole/pihole) +[![Build Status](https://github.com/pi-hole/docker-pi-hole/workflows/Test%20&%20Build/badge.svg)](https://github.com/pi-hole/docker-pi-hole/actions?query=workflow%3A%22Test+%26+Build%22) [![Docker Stars](https://img.shields.io/docker/stars/pihole/pihole.svg?maxAge=604800)](https://store.docker.com/community/images/pihole/pihole) [![Docker Pulls](https://img.shields.io/docker/pulls/pihole/pihole.svg?maxAge=604800)](https://store.docker.com/community/images/pihole/pihole) ## Running Pi-hole Docker @@ -84,6 +81,8 @@ If you're using a Red Hat based distribution with an SELinux Enforcing policy ad Volumes are recommended for persisting data across container re-creations for updating images. The IP lookup variables may not work for everyone, please review their values and hard code IP and IPv6 if necessary. +You can customize where to store persistent data by setting the `PIHOLE_BASE` environment variable when invoking `docker_run.sh` (e.g. `PIHOLE_BASE=/opt/pihole-storage ./docker_run.sh`). If `PIHOLE_BASE` is not set, files are stored in your current directory when you invoke the script. + Port 443 is to provide a sinkhole for ads that use SSL. If only port 80 is used, then blocked HTTPS queries will fail to connect to port 443 and may cause long loading times. Rejecting 443 on your firewall can also serve this same purpose. Ubuntu firewall example: `sudo ufw reject https` **Automatic Ad List Updates** - since the 3.0+ release, `cron` is baked into the container and will grab the newest versions of your lists and flush your logs. **Set your TZ** environment variable to make sure the midnight log rotation syncs up with your timezone's midnight. @@ -98,25 +97,28 @@ There are other environment variables if you want to customize various things in | Docker Environment Var. | Description | | ----------------------- | ----------- | +| `ADMIN_EMAIL: `
*Optional Default: ''* | Set an administrative contact address for the Block Page | `TZ: `
**Recommended** *Default: UTC* | Set your [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) to make sure logs rotate at local midnight instead of at UTC midnight. | `WEBPASSWORD: `
**Recommended** *Default: random* | http://pi.hole/admin password. Run `docker logs pihole \| grep random` to find your random pass. | `DNS1: `
*Optional* *Default: 8.8.8.8* | Primary upstream DNS provider, default is google DNS | `DNS2: `
*Optional* *Default: 8.8.4.4* | Secondary upstream DNS provider, default is google DNS, `no` if only one DNS should used -| `DNSSEC: `
*Optional* *Default: false* | Enable DNSSEC support -| `DNS_BOGUS_PRIV: `
*Optional* *Default: true* | Enable forwarding of reverse lookups for private ranges -| `DNS_FQDN_REQUIRED: `
*Optional* *Default: true* | Never forward non-FQDNs -| `CONDITIONAL_FORWARDING: `
*Optional* *Default: False* | Enable DNS conditional forwarding for device name resolution +| `DNSSEC: <"true"\|"false">`
*Optional* *Default: "false"* | Enable DNSSEC support +| `DNS_BOGUS_PRIV: <"true"\|"false">`
*Optional* *Default: "true"* | Enable forwarding of reverse lookups for private ranges +| `DNS_FQDN_REQUIRED: <"true"\|"false">`
*Optional* *Default: true* | Never forward non-FQDNs +| `CONDITIONAL_FORWARDING: <"true"\|"false">`
*Optional* *Default: "false"* | Enable DNS conditional forwarding for device name resolution | `CONDITIONAL_FORWARDING_IP: `
*Optional* | If conditional forwarding is enabled, set the IP of the local network router | `CONDITIONAL_FORWARDING_DOMAIN: `
*Optional* | If conditional forwarding is enabled, set the domain of the local network router | `CONDITIONAL_FORWARDING_REVERSE: `
*Optional* | If conditional forwarding is enabled, set the reverse DNS of the local network router (e.g. `0.168.192.in-addr.arpa`) | `ServerIP: `
**Recommended** | **--net=host mode requires** Set to your server's LAN IP, used by web block modes and lighttpd bind address | `ServerIPv6: `
*Required if using IPv6* | **If you have a v6 network** set to your server's LAN IPv6 to block IPv6 ads fully | `VIRTUAL_HOST: `
*Optional* *Default: $ServerIP* | What your web server 'virtual host' is, accessing admin through this Hostname/IP allows you to make changes to the whitelist / blacklists in addition to the default 'http://pi.hole/admin/' address -| `IPv6: `
*Optional* *Default: True* | For unraid compatibility, strips out all the IPv6 configuration from DNS/Web services when false. +| `IPv6: <"true"\|"false">`
*Optional* *Default: "true"* | For unraid compatibility, strips out all the IPv6 configuration from DNS/Web services when false. | `INTERFACE: `
*Advanced/Optional* | The default works fine with our basic example docker run commands. If you're trying to use DHCP with `--net host` mode then you may have to customize this or DNSMASQ_LISTENING. | `DNSMASQ_LISTENING: `
*Advanced/Optional* | `local` listens on all local subnets, `all` permits listening on internet origin subnets in addition to local. | `WEB_PORT: `
*Advanced/Optional* | **This will break the 'webpage blocked' functionality of Pi-hole** however it may help advanced setups like those running synology or `--net=host` docker argument. This guide explains how to restore webpage blocked functionality using a linux router DNAT rule: [Alternative Synology installation method](https://discourse.pi-hole.net/t/alternative-synology-installation-method/5454?u=diginc) | `DNSMASQ_USER: `
*Experimental Default: root* | Allows running FTLDNS as non-root. +| `TEMPERATUREUNIT`:
*Optional Default: c* | Set preferred temperature unit to `c`: Celsius, `k`: Kelvin, or `f` Fahrenheit units. +| `WEBUIBOXEDLAYOUT: `
*Optional Default: boxed* | Use boxed layout (helpful when working on large screens) To use these env vars in docker run format style them like: `-e DNS1=1.1.1.1` @@ -174,14 +176,15 @@ Users of older Ubuntu releases (circa 17.04) will need to disable dnsmasq. ## Docker tags and versioning -The primary docker tags / versions are explained in the following table. [Click here to see the full list of tags](https://store.docker.com/community/images/pihole/pihole/tags) ([arm tags are here](https://store.docker.com/community/images/pihole/pihole/tags)), I also try to tag with the specific version of Pi-hole Core for version archival purposes, the web version that comes with the core releases should be in the [GitHub Release notes](https://github.com/pi-hole/docker-pi-hole/releases). +The primary docker tags / versions are explained in the following table. [Click here to see the full list of tags](https://store.docker.com/community/images/pihole/pihole/tags), I also try to tag with the specific version of Pi-hole Core for version archival purposes, the web version that comes with the core releases should be in the [GitHub Release notes](https://github.com/pi-hole/docker-pi-hole/releases). -| tag | architecture | description | Dockerfile | -| --- | ------------ | ----------- | ---------- | -| `latest` | auto detect | x86, arm, or arm64 container, docker auto detects your architecture. | [Dockerfile](https://github.com/pi-hole/docker-pi-hole/blob/master/Dockerfile_amd64) | -| `v4.0.0-1` | auto detect | Versioned tags, if you want to pin against a specific version, use one of these | | -| `v4.0.0-1_` | based on tag | Specific architectures tags | | -| `dev` | auto detect | like latest tag, but for the development branch (pushed occasionally) | | +| tag | architecture | description | Dockerfile | +| --- | ------------ | ----------- | ---------- | +| `latest` | auto detect | x86, arm, or arm64 container, docker auto detects your architecture. | [Dockerfile](https://github.com/pi-hole/docker-pi-hole/blob/master/Dockerfile) | +| `v5.0` | auto detect | Versioned tags, if you want to pin against a specific Pi-hole version, use one of these | | +| `v5.0-stretch` | auto detect | Versioned tags, if you want to pin against a specific Pi-hole and Debian version, use one of these | | +| `v5.0--stretch` | based on tag | Specific architectures and Debian version tags | | +| `dev` | auto detect | like latest tag, but for the development branch (pushed occasionally) | | ### `pihole/pihole:latest` [![](https://images.microbadger.com/badges/image/pihole/pihole:latest.svg)](https://microbadger.com/images/pihole/pihole "Get your own image badge on microbadger.com") [![](https://images.microbadger.com/badges/version/pihole/pihole:latest.svg)](https://microbadger.com/images/pihole/pihole "Get your own version badge on microbadger.com") [![](https://images.microbadger.com/badges/version/pihole/pihole:latest.svg)](https://microbadger.com/images/pihole/pihole "Get your own version badge on microbadger.com") diff --git a/TESTING.md b/TESTING.md index 88d10cc..75e8090 100644 --- a/TESTING.md +++ b/TESTING.md @@ -4,7 +4,7 @@ Make sure you have bash, docker. Python and some test hacks are crammed into th # Running tests locally -`ARCH=amd64 ./circle-test.sh` +`ARCH=amd64 ./gh-actions-test.sh` Should result in : diff --git a/bash_functions.sh b/bash_functions.sh index f2567dc..025f845 100644 --- a/bash_functions.sh +++ b/bash_functions.sh @@ -2,31 +2,6 @@ # Some of the bash_functions use variables these core pi-hole/web scripts . /opt/pihole/webpage.sh -docker_checks() { - warn_msg='WARNING Misconfigured DNS in /etc/resolv.conf' - ns_count="$(grep -c nameserver /etc/resolv.conf)" - ns_primary="$(grep nameserver /etc/resolv.conf | head -1)" - ns_primary="${ns_primary/nameserver /}" - warned=false - - if [ "$ns_count" -lt 2 ] ; then - echo "$warn_msg: Two DNS servers are recommended, 127.0.0.1 and any backup server" - warned=true - fi - - if [ "$ns_primary" != "127.0.0.1" ] ; then - echo "$warn_msg: Primary DNS should be 127.0.0.1 (found ${ns_primary})" - warned=true - fi - - if ! $warned ; then - echo "OK: Checks passed for /etc/resolv.conf DNS servers" - fi - - echo - cat /etc/resolv.conf -} - fix_capabilities() { setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN+ei $(which pihole-FTL) || ret=$? @@ -48,7 +23,7 @@ prepare_configs() { # Also similar to preflights for FTL https://github.com/pi-hole/pi-hole/blob/master/advanced/Templates/pihole-FTL.service chown pihole:root /etc/lighttpd chown pihole:pihole "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" "/var/log/pihole" "${regexFile}" - chmod 644 "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" + chmod 644 "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" # not sure why pihole:pihole user/group write perms are not enough for web to write...dirty fix: chmod 777 "${regexFile}" touch /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log @@ -60,34 +35,18 @@ prepare_configs() { # Update version numbers pihole updatechecker # Re-write all of the setupVars to ensure required ones are present (like QUERY_LOGGING) - + # If the setup variable file exists, if [[ -e "${setupVars}" ]]; then - # update the variables in the file - local USERWEBPASSWORD="${WEBPASSWORD}" - . "${setupVars}" - # Stash and pop the user password to avoid setting the password to the hashed setupVar variable - WEBPASSWORD="${USERWEBPASSWORD}" - # Clean up old before re-writing the required setupVars - sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/QUERY_LOGGING/d;/INSTALL_WEB_SERVER/d;/INSTALL_WEB_INTERFACE/d;/LIGHTTPD_ENABLED/d;' "${setupVars}" + cp -f "${setupVars}" "${setupVars}.update.bak" fi - # echo the information to the user - { - echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" - echo "IPV4_ADDRESS=${IPV4_ADDRESS}" - echo "IPV6_ADDRESS=${IPV6_ADDRESS}" - echo "QUERY_LOGGING=${QUERY_LOGGING}" - echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}" - echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}" - echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" - }>> "${setupVars}" } validate_env() { # Optional ServerIP is a valid IP # nc won't throw any text based errors when it times out connecting to a valid IP, otherwise it complains about the DNS name being garbage # if nc doesn't behave as we expect on a valid IP the routing table should be able to look it up and return a 0 retcode - if [[ "$(nc -4 -w1 -z "$ServerIP" 53 2>&1)" != "" ]] || ! ip route get "$ServerIP" > /dev/null ; then + if [[ "$(nc -4 -w1 -z "$ServerIP" 53 2>&1)" != "" ]] && ! ip route get "$ServerIP" > /dev/null ; then echo "ERROR: ServerIP Environment variable ($ServerIP) doesn't appear to be a valid IPv4 address" exit 1 fi @@ -99,7 +58,7 @@ validate_env() { unset ServerIPv6 exit 1 fi - if [[ "$(nc -6 -w1 -z "$ServerIPv6" 53 2>&1)" != "" ]] || ! ip route get "$ServerIPv6" > /dev/null ; then + if [[ "$(nc -6 -w1 -z "$ServerIPv6" 53 2>&1)" != "" ]] && ! ip route get "$ServerIPv6" > /dev/null ; then echo "ERROR: ServerIPv6 Environment variable ($ServerIPv6) doesn't appear to be a valid IPv6 address" echo " TIP: If your server is not IPv6 enabled just remove '-e ServerIPv6' from your docker container" exit 1 @@ -123,7 +82,7 @@ setup_dnsmasq_dns() { setupDNS1="${setupDNS1/PIHOLE_DNS_1=/}" setupDNS2="${setupDNS2/PIHOLE_DNS_2=/}" if [[ -n "$DNS1" && -n "$setupDNS1" ]] || \ - [[ -n "$DNS2" && -n "$setupDNS2" ]] ; then + [[ -n "$DNS2" && -n "$setupDNS2" ]] ; then echo "Docker DNS variables not used" fi echo "Existing DNS servers used (${setupDNS1:-unset} & ${setupDNS2:-unset})" @@ -174,9 +133,9 @@ setup_dnsmasq() { local dns2="$2" local interface="$3" local dnsmasq_listening_behaviour="$4" - # Coordinates + # Coordinates setup_dnsmasq_config_if_missing - setup_dnsmasq_dns "$dns1" "$dns2" + setup_dnsmasq_dns "$dns1" "$dns2" setup_dnsmasq_interface "$interface" setup_dnsmasq_listening_behaviour "$dnsmasq_listening_behaviour" setup_dnsmasq_user "${DNSMASQ_USER}" @@ -237,7 +196,7 @@ setup_dnsmasq_hostnames() { setup_lighttpd_bind() { local serverip="$1" # if using '--net=host' only bind lighttpd on $ServerIP and localhost - if grep -q "docker" /proc/net/dev ; then #docker (docker0 by default) should only be present on the host system + if grep -q "docker" /proc/net/dev && [[ $serverip != 0.0.0.0 ]]; then #docker (docker0 by default) should only be present on the host system if ! grep -q "server.bind" /etc/lighttpd/lighttpd.conf ; then # if the declaration is already there, don't add it again sed -i -E "s/server\.port\s+\=\s+([0-9]+)/server.bind\t\t = \"${serverip}\"\nserver.port\t\t = \1\n"\$SERVER"\[\"socket\"\] == \"127\.0\.0\.1:\1\" \{\}/" /etc/lighttpd/lighttpd.conf fi @@ -269,7 +228,7 @@ setup_web_port() { # Quietly exit early for empty or default if [[ -z "${1}" || "${1}" == '80' ]] ; then return ; fi - if ! echo $1 | grep -q '^[0-9][0-9]*$' ; then + if ! echo $1 | grep -q '^[0-9][0-9]*$' ; then echo "$warning - $1 is not an integer" return fi @@ -345,9 +304,8 @@ test_configs() { echo "::: All config checks passed, cleared for startup ..." } - setup_blocklists() { - local blocklists="$1" + local blocklists="$1" # Exit/return early without setting up adlists with defaults for any of the following conditions: # 1. skip_setup_blocklists env is set exit_string="(exiting ${FUNCNAME[0]} early)" @@ -385,3 +343,32 @@ setup_var_exists() { fi } +setup_temp_unit() { + local UNIT="$1" + # check if var is empty + if [[ "$UNIT" != "" ]] ; then + # check if we have valid units + if [[ "$UNIT" == "c" || "$UNIT" == "k" || $UNIT == "f" ]] ; then + pihole -a -${UNIT} + fi + fi +} + +setup_ui_layout() { + local LO=$1 + # check if var is empty + if [[ "$LO" != "" ]] ; then + # check if we have valid types boxed | traditional + if [[ "$LO" == "traditional" || "$LO" == "boxed" ]] ; then + change_setting "WEBUIBOXEDLAYOUT" "$WEBUIBOXEDLAYOUT" + fi + fi +} + +setup_admin_email() { + local EMAIL=$1 + # check if var is empty + if [[ "$EMAIL" != "" ]] ; then + pihole -a -e "$EMAIL" + fi +} diff --git a/build.yml b/build.yml new file mode 100644 index 0000000..0a33a7c --- /dev/null +++ b/build.yml @@ -0,0 +1,49 @@ +# Docker Compose build file: docker-compose -f build.yml build +version: "3.7" + +x-common-args: &common-args + PIHOLE_VERSION: ${PIHOLE_VERSION} + NAME: pihole/pihole + MAINTAINER: adam@diginc.us + S6_VERSION: v1.22.1.0 + PHP_ENV_CONFIG: /etc/lighttpd/conf-enabled/15-fastcgi-php.conf + PHP_ERROR_LOG: /var/log/lighttpd/error.log + + +services: + amd64: + image: pihole:${PIHOLE_VERSION}-amd64-${DEBIAN_VERSION:-stretch} + build: + context: . + args: + <<: *common-args + PIHOLE_BASE: pihole/debian-base:${DEBIAN_VERSION:-stretch} + PIHOLE_ARCH: amd64 + S6_ARCH: amd64 + armel: + image: pihole:${PIHOLE_VERSION}-armel-${DEBIAN_VERSION:-stretch} + build: + context: . + args: + <<: *common-args + PIHOLE_BASE: multiarch/debian-debootstrap:armel-${DEBIAN_VERSION:-stretch}-slim + PIHOLE_ARCH: armel + S6_ARCH: arm + armhf: + image: pihole:${PIHOLE_VERSION}-armhf-${DEBIAN_VERSION:-stretch} + build: + context: . + args: + <<: *common-args + PIHOLE_BASE: multiarch/debian-debootstrap:armhf-${DEBIAN_VERSION:-stretch}-slim + PIHOLE_ARCH: arm + S6_ARCH: arm + arm64: + image: pihole:${PIHOLE_VERSION}-arm64-${DEBIAN_VERSION:-stretch} + build: + context: . + args: + <<: *common-args + PIHOLE_BASE: multiarch/debian-debootstrap:arm64-${DEBIAN_VERSION:-stretch}-slim + PIHOLE_ARCH: arm64 + S6_ARCH: aarch64 diff --git a/circle-deploy.sh b/circle-deploy.sh deleted file mode 100755 index 884383a..0000000 --- a/circle-deploy.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash -set -ex -# Circle CI Job for merging/deploying all architectures (post-test passing) -. circle-vars.sh - -annotate() { - local base=$1 - local image=$2 - local arch=$3 - local annotate_flags="${annotate_map[$arch]}" - - $dry docker manifest annotate ${base} ${image} --os linux ${annotate_flags} -} - -# Keep in sync with circle-ci job names -declare -A annotate_map=( - ["amd64"]="--arch amd64" - ["armel"]="--arch arm --variant v6" - ["armhf"]="--arch arm --variant v7" - ["arm64"]="--arch arm64 --variant v8" -) - -# push image when not running a PR -mkdir -p ~/.docker -export DOCKER_CLI_EXPERIMENTAL='enabled' -echo "{}" | jq '.experimental="enabled"' | tee ~/.docker/config.json -docker info -if [[ "$CIRCLE_PR_NUMBER" == "" ]]; then - images=() - echo $DOCKERHUB_PASS | docker login --username=$DOCKERHUB_USER --password-stdin - ls -lat ./ci-workspace/ - cd ci-workspace - - for arch in *; do - arch_image=$(cat $arch) - docker pull $arch_image - images+=($arch_image) - done - - for docker_tag in $MULTIARCH_IMAGE $LATEST_IMAGE; do - docker manifest create $docker_tag ${images[*]} - for arch in *; do - arch_image=$(cat $arch) - docker pull $arch_image - annotate "$docker_tag" "$arch_image" "$arch" - done - - docker manifest inspect "$docker_tag" - docker manifest push --purge "$docker_tag" - done; -fi diff --git a/circle-test.sh b/circle-test.sh deleted file mode 100755 index f3ce7fe..0000000 --- a/circle-test.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -set -ex - -# Circle CI Job for single architecture - -# setup qemu/variables -docker run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null -. circle-vars.sh - -if [[ "$1" == "enter" ]]; then - enter="-it --entrypoint=sh" -fi - -# generate and build dockerfile -docker pull python:buster -docker build -t image_pipenv -f Dockerfile_build . -env > /tmp/env -docker run --rm \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v "$(pwd):/$(pwd)" \ - -w "$(pwd)" \ - -e PIPENV_CACHE_DIR="$(pwd)/.pipenv" \ - --env-file /tmp/env \ - $enter image_pipenv - -docker images - -test -z "${CIRCLE_PROJECT_REPONAME}" && exit 0 -# The rest is circle-ci only -echo $DOCKERHUB_PASS | docker login --username=$DOCKERHUB_USER --password-stdin -docker push $ARCH_IMAGE -mkdir -p ci-workspace -echo "$ARCH_IMAGE" | tee ./ci-workspace/$ARCH diff --git a/circle-vars.sh b/circle-vars.sh deleted file mode 100755 index fbe4db1..0000000 --- a/circle-vars.sh +++ /dev/null @@ -1,48 +0,0 @@ -set -a - -CIRCLE_JOB="${CIRCLE_JOB:-}" -ARCH="${ARCH:-$CIRCLE_JOB}" -if [[ -z "$ARCH" ]] ; then - echo "Defaulting arch to amd64" - ARCH="amd64" -fi -BASE_IMAGE="${BASE_IMAGE:-${CIRCLE_PROJECT_REPONAME}}" -if [[ -z "$BASE_IMAGE" ]] ; then - echo "Defaulting image name to pihole" - BASE_IMAGE="pihole" -fi - -# The docker image will match the github repo path by default but is overrideable with CircleCI environment -# BASE_IMAGE Overridable by Circle environment, including namespace (e.g. BASE_IMAGE=bobsmith/test-img:latest) -CIRCLE_PROJECT_USERNAME="${CIRCLE_PROJECT_USERNAME:-unset}" -HUB_NAMESPACE="${HUB_NAMESPACE:-$CIRCLE_PROJECT_USERNAME}" -[[ $CIRCLE_PROJECT_USERNAME == "pi-hole" ]] && HUB_NAMESPACE="pihole" # Custom mapping for namespace -[[ $BASE_IMAGE != *"/"* ]] && BASE_IMAGE="${HUB_NAMESPACE}/${BASE_IMAGE}" # If missing namespace, add one - -# Secondary docker tag info (origin github branch/tag) will get prepended also -ARCH_IMAGE="$BASE_IMAGE" -[[ $ARCH_IMAGE != *":"* ]] && ARCH_IMAGE="${BASE_IMAGE}:$ARCH" # If tag missing, add circle job name as a tag (architecture here) - -DOCKER_TAG="${CIRCLE_TAG:-$CIRCLE_BRANCH}" -if [[ -n "$DOCKER_TAG" ]]; then - # remove latest tag if used (as part of a user provided image variable) - ARCH_IMAGE="${ARCH_IMAGE/:latest/:}" - # Prepend the github tag(version) or branch. image:arch = image:v1.0-arch - ARCH_IMAGE="${ARCH_IMAGE/:/:${DOCKER_TAG}-}" - # latest- sometimes has a trailing slash, remove it - ARCH_IMAGE="${ARCH_IMAGE/%-/}" -fi - -# To get latest released, cut a release on https://github.com/pi-hole/docker-pi-hole/releases (manually gated for quality control) -latest_tag='' -if ! latest_tag=$(curl -sI https://github.com/pi-hole/docker-pi-hole/releases/latest | grep --color=never -i Location | awk -F / '{print $NF}' | tr -d '[:cntrl:]'); then - print "Failed to retrieve latest docker-pi-hole release metadata" -else - if [[ "$DOCKER_TAG" == "$latest_tag" ]] ; then - LATEST_IMAGE="$BASE_IMAGE:latest" - fi -fi - -MULTIARCH_IMAGE="$BASE_IMAGE:$DOCKER_TAG" - -set +a diff --git a/docker-compose-jwilder-proxy.yml b/docker-compose-jwilder-proxy.yml index 47ab2f7..ee9c9e4 100644 --- a/docker-compose-jwilder-proxy.yml +++ b/docker-compose-jwilder-proxy.yml @@ -15,9 +15,6 @@ services: pihole: image: pihole/pihole:latest - dns: - - 127.0.0.1 - - 1.1.1.1 ports: - '53:53/tcp' - '53:53/udp' @@ -60,4 +57,4 @@ services: # PROXY_LOCATION: ghost # VIRTUAL_HOST: ghost.yourDomain.lan # VIRTUAL_PORT: 2368 -# restart: always \ No newline at end of file +# restart: always diff --git a/docker-compose-traefik-proxy.md b/docker-compose-traefik-proxy.md index 4b8c034..48e7662 100644 --- a/docker-compose-traefik-proxy.md +++ b/docker-compose-traefik-proxy.md @@ -51,9 +51,6 @@ services: image: pihole/pihole:latest networks: - discovery - dns: - - 127.0.0.1 - - 1.1.1.1 ports: - '0.0.0.0:53:53/tcp' - '0.0.0.0:53:53/udp' diff --git a/docker-compose.yml b/docker-compose.yml index 06cd155..031eb49 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,9 +22,6 @@ services: - './etc-dnsmasq.d/:/etc/dnsmasq.d/' # run `touch ./var-log/pihole.log` first unless you like errors # - './var-log/pihole.log:/var/log/pihole.log' - dns: - - 127.0.0.1 - - 1.1.1.1 # Recommended but not required (DHCP needs NET_ADMIN) # https://github.com/pi-hole/docker-pi-hole#note-on-capabilities cap_add: diff --git a/docker_run.sh b/docker_run.sh index dc7ba7f..cc0cd6e 100755 --- a/docker_run.sh +++ b/docker_run.sh @@ -2,6 +2,9 @@ # https://github.com/pi-hole/docker-pi-hole/blob/master/README.md +PIHOLE_BASE="${PIHOLE_BASE:-$(pwd)}" +[[ -d "$PIHOLE_BASE" ]] || mkdir -p "$PIHOLE_BASE" || { echo "Couldn't create storage directory: $PIHOLE_BASE"; exit 1; } + # Note: ServerIP should be replaced with your external ip. docker run -d \ --name pihole \ @@ -9,8 +12,8 @@ docker run -d \ -p 80:80 \ -p 443:443 \ -e TZ="America/Chicago" \ - -v "$(pwd)/etc-pihole/:/etc/pihole/" \ - -v "$(pwd)/etc-dnsmasq.d/:/etc/dnsmasq.d/" \ + -v "${PIHOLE_BASE}/etc-pihole/:/etc/pihole/" \ + -v "${PIHOLE_BASE}/etc-dnsmasq.d/:/etc/dnsmasq.d/" \ --dns=127.0.0.1 --dns=1.1.1.1 \ --restart=unless-stopped \ --hostname pi.hole \ diff --git a/gh-actions-deploy.sh b/gh-actions-deploy.sh new file mode 100755 index 0000000..51e3240 --- /dev/null +++ b/gh-actions-deploy.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -ex +# Github Actions Job for merging/deploying all architectures (post-test passing) +. gh-actions-vars.sh + +function annotate() { + local base=$1 + local image=$2 + local arch=$3 + local annotate_flags="${annotate_map[$arch]}" + + $dry docker manifest annotate ${base} ${image} --os linux ${annotate_flags} +} + +function create_manifest() { + local debian_version=$1 + local images=() + cd "${debian_version}" + + for arch in *; do + arch_image=$(cat "${arch}") + docker pull "${arch_image}" + images+=("${arch_image}") + done + + multiarch_images=$(get_multiarch_images) + for docker_tag in ${multiarch_images}; do + docker manifest create ${docker_tag} ${images[*]} + for arch in *; do + arch_image=$(cat "${arch}") + annotate "${docker_tag}" "${arch_image}" "${arch}" + done + + docker manifest inspect "${docker_tag}" + docker manifest push --purge "${docker_tag}" + done + cd ../ +} + +function get_multiarch_images() { + multiarch_images="${MULTIARCH_IMAGE}-${debian_version}" + if [[ "${debian_version}" == "${DEFAULT_DEBIAN_VERSION}" ]] ; then + # default debian version gets a non-debian tag as well as latest tag + multiarch_images="${multiarch_images} ${MULTIARCH_IMAGE} ${LATEST_IMAGE}" + fi + echo "${multiarch_images}" +} + + +# Keep in sync with build.yml names +declare -A annotate_map=( + ["amd64"]="--arch amd64" + ["armel"]="--arch arm --variant v6" + ["armhf"]="--arch arm --variant v7" + ["arm64"]="--arch arm64 --variant v8" +) + +mkdir -p ~/.docker +export DOCKER_CLI_EXPERIMENTAL='enabled' +echo "{}" | jq '.experimental="enabled"' | tee ~/.docker/config.json +# I tried to keep this login command outside of this script +# but for some reason auth would always fail in Github Actions. +# I think setting up a cred store would fix it +# https://docs.docker.com/engine/reference/commandline/login/#credentials-store +echo "${DOCKERHUB_PASS}" | docker login --username="${DOCKERHUB_USER}" --password-stdin +docker info + +ls -lat ./.gh-workspace/ +cd .gh-workspace + +for debian_version in *; do + create_manifest "${debian_version}" +done diff --git a/gh-actions-test.sh b/gh-actions-test.sh new file mode 100755 index 0000000..691ab16 --- /dev/null +++ b/gh-actions-test.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -ex + +# Script ran by Github actions for tests +# +# @environment ${ARCH} The architecture to build. Example: amd64. +# @environment ${DEBIAN_VERSION} Debian version to build. ('buster' or 'stretch'). +# @environment ${ARCH_IMAGE} What the Docker Hub Image should be tagged as. Example: pihole/pihole:master-amd64-stretch + +# setup qemu/variables +docker run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null +. gh-actions-vars.sh + +if [[ "$1" == "enter" ]]; then + enter="-it --entrypoint=sh" +fi + +# generate and build dockerfile +docker build --tag image_pipenv --file Dockerfile_build . +docker run --rm \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + --volume "$(pwd):/$(pwd)" \ + --workdir "$(pwd)" \ + --env PIPENV_CACHE_DIR="$(pwd)/.pipenv" \ + --env ARCH="${ARCH}" \ + --env ARCH_IMAGE="${ARCH_IMAGE}" \ + --env DEBIAN_VERSION="${DEBIAN_VERSION}" \ + ${enter} image_pipenv + +mkdir -p ".gh-workspace/${DEBIAN_VERSION}/" +echo "${ARCH_IMAGE}" | tee "./.gh-workspace/${DEBIAN_VERSION}/${ARCH}" diff --git a/gh-actions-vars.sh b/gh-actions-vars.sh new file mode 100755 index 0000000..6361698 --- /dev/null +++ b/gh-actions-vars.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -a + +# @environment ${ARCH} The architecture to build. Defaults to 'amd64'. +# @environment ${DEBIAN_VERSION} Debian version to build. Defaults to 'stretch'. +# @environment ${DOCKER_HUB_REPO} The docker hub repo to tag images for. Defaults to 'pihole'. +# @environment ${DOCKER_HUB_IMAGE_NAME} The name of the resulting image. Defaults to 'pihole'. + +GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD | sed "s/\//-/g") +GIT_TAG=$(git describe --tags --exact-match 2> /dev/null || true) + +DEFAULT_DEBIAN_VERSION="stretch" + +if [[ -z "${ARCH}" ]]; then + ARCH="amd64" + echo "Defaulting arch to ${ARCH}" +fi + +if [[ -z "${DEBIAN_VERSION}" ]]; then + DEBIAN_VERSION="${DEFAULT_DEBIAN_VERSION}" + echo "Defaulting DEBIAN_VERSION to ${DEBIAN_VERSION}" +fi + +if [[ -z "${DOCKER_HUB_REPO}" ]]; then + DOCKER_HUB_REPO="pihole" + echo "Defaulting DOCKER_HUB_REPO to ${DOCKER_HUB_REPO}" +fi + +if [[ -z "${DOCKER_HUB_IMAGE_NAME}" ]]; then + DOCKER_HUB_IMAGE_NAME="pihole" + echo "Defaulting DOCKER_HUB_IMAGE_NAME to ${DOCKER_HUB_IMAGE_NAME}" +fi + +BASE_IMAGE="${DOCKER_HUB_REPO}/${DOCKER_HUB_IMAGE_NAME}" + +GIT_TAG="${GIT_TAG:-$GIT_BRANCH}" +ARCH_IMAGE="${BASE_IMAGE}:${GIT_TAG}-${ARCH}-${DEBIAN_VERSION}" +MULTIARCH_IMAGE="${BASE_IMAGE}:${GIT_TAG}" + + + +# To get latest released, cut a release on https://github.com/pi-hole/docker-pi-hole/releases (manually gated for quality control) +latest_tag='UNKNOWN' +if ! latest_tag=$(curl -sI https://github.com/pi-hole/docker-pi-hole/releases/latest | grep --color=never -i Location | awk -F / '{print $NF}' | tr -d '[:cntrl:]'); then + print "Failed to retrieve latest docker-pi-hole release metadata" +else + if [[ "${GIT_TAG}" == "${latest_tag}" ]] ; then + LATEST_IMAGE="${BASE_IMAGE}:latest" + fi +fi + + +set +a diff --git a/install.sh b/install.sh index 3de2723..11fe442 100644 --- a/install.sh +++ b/install.sh @@ -15,6 +15,9 @@ fi apt-get update apt-get install --no-install-recommends -y curl procps ca-certificates +# curl in armhf-buster's image has SSL issues. Running c_rehash fixes it. +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923479 +c_rehash ln -s `which echo` /usr/local/bin/whiptail curl -L -s $S6OVERLAY_RELEASE | tar xvzf - -C / mv /init /s6-init diff --git a/start.sh b/start.sh index 815da70..16b8c79 100755 --- a/start.sh +++ b/start.sh @@ -5,7 +5,7 @@ export ServerIP export ServerIPv6 export PYTEST export PHP_ENV_CONFIG -export PHP_ERROR_LOG +export PHP_ERROR_LOG export HOSTNAME export WEBLOGDIR export DNS1 @@ -21,19 +21,23 @@ export CONDITIONAL_FORWARDING export CONDITIONAL_FORWARDING_IP export CONDITIONAL_FORWARDING_DOMAIN export CONDITIONAL_FORWARDING_REVERSE +export TEMPERATUREUNIT +export ADMIN_EMAIL +export WEBUIBOXEDLAYOUT export adlistFile='/etc/pihole/adlists.list' # The below functions are all contained in bash_functions.sh . /bash_functions.sh +# Ensure we have all functions available to update our configurations +. /opt/pihole/webpage.sh + # PH_TEST prevents the install from actually running (someone should rename that) PH_TEST=true . $PIHOLE_INSTALL echo " ::: Starting docker specific checks & setup for docker pihole/pihole" -docker_checks - # TODO: #if [ ! -f /.piholeFirstBoot ] ; then # echo " ::: Not first container startup so not running docker's setup, re-create container to run setup again" @@ -46,6 +50,12 @@ load_web_password_secret generate_password validate_env || exit 1 prepare_configs +change_setting "PIHOLE_INTERFACE" "$PIHOLE_INTERFACE" +change_setting "IPV4_ADDRESS" "$IPV4_ADDRESS" +change_setting "QUERY_LOGGING" "$QUERY_LOGGING" +change_setting "INSTALL_WEB_SERVER" "$INSTALL_WEB_SERVER" +change_setting "INSTALL_WEB_INTERFACE" "$INSTALL_WEB_INTERFACE" +change_setting "LIGHTTPD_ENABLED" "$LIGHTTPD_ENABLED" change_setting "IPV4_ADDRESS" "$ServerIP" change_setting "IPV6_ADDRESS" "$ServerIPv6" change_setting "DNS_BOGUS_PRIV" "$DNS_BOGUS_PRIV" @@ -57,6 +67,9 @@ change_setting "CONDITIONAL_FORWARDING_DOMAIN" "$CONDITIONAL_FORWARDING_DOMAIN" change_setting "CONDITIONAL_FORWARDING_REVERSE" "$CONDITIONAL_FORWARDING_REVERSE" setup_web_port "$WEB_PORT" setup_web_password "$WEBPASSWORD" +setup_temp_unit "$TEMPERATUREUNIT" +setup_ui_layout "$WEBUIBOXEDLAYOUT" +setup_admin_email "$ADMIN_EMAIL" setup_dnsmasq "$DNS1" "$DNS2" "$INTERFACE" "$DNSMASQ_LISTENING_BEHAVIOUR" setup_php_env setup_dnsmasq_hostnames "$ServerIP" "$ServerIPv6" "$HOSTNAME" diff --git a/test/conftest.py b/test/conftest.py index 546fde6..61d68fd 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -2,12 +2,14 @@ import functools import os import pytest +import subprocess import testinfra import types local_host = testinfra.get_host('local://') check_output = local_host.check_output +DEBIAN_VERSION = os.environ.get('DEBIAN_VERSION', 'stretch') __version__ = None dotdot = os.path.abspath(os.path.join(os.path.abspath(__file__), os.pardir, os.pardir)) with open('{}/VERSION'.format(dotdot), 'r') as v: @@ -15,8 +17,22 @@ with open('{}/VERSION'.format(dotdot), 'r') as v: __version__ = raw_version.replace('release/', 'release-') @pytest.fixture() -def args_dns(): - return '--dns 127.0.0.1 --dns 1.1.1.1' +def run_and_stream_command_output(): + def run_and_stream_command_output_inner(command, verbose=False): + print("Running", command) + build_env = os.environ.copy() + build_env['PIHOLE_VERSION'] = __version__ + build_result = subprocess.Popen(command.split(), env=build_env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + bufsize=1, universal_newlines=True) + if verbose: + while build_result.poll() is None: + for line in build_result.stdout: + print(line, end='') + build_result.wait() + if build_result.returncode != 0: + print(" ::: Error running".format(command)) + print(build_result.stderr) + return run_and_stream_command_output_inner @pytest.fixture() def args_volumes(): @@ -27,8 +43,8 @@ def args_env(): return '-e ServerIP="127.0.0.1"' @pytest.fixture() -def args(args_dns, args_volumes, args_env): - return "{} {} {}".format(args_dns, args_volumes, args_env) +def args(args_volumes, args_env): + return "{} {}".format(args_volumes, args_env) @pytest.fixture() def test_args(): @@ -84,8 +100,12 @@ def version(): return __version__ @pytest.fixture() -def tag(version, arch): - return '{}_{}'.format(version, arch) +def debian_version(): + return DEBIAN_VERSION + +@pytest.fixture() +def tag(version, arch, debian_version): + return '{}-{}-{}'.format(version, arch, debian_version) @pytest.fixture def webserver(tag): @@ -110,6 +130,10 @@ def persist_arch(): def persist_version(): return __version__ +@pytest.fixture(scope='module') +def persist_debian_version(): + return DEBIAN_VERSION + @pytest.fixture(scope='module') def persist_args_dns(): return '--dns 127.0.0.1 --dns 1.1.1.1' @@ -123,8 +147,8 @@ def persist_args_env(): return '-e ServerIP="127.0.0.1"' @pytest.fixture(scope='module') -def persist_args(persist_args_dns, persist_args_volumes, persist_args_env): - return "{} {} {}".format(args_dns, args_volumes, args_env) +def persist_args(persist_args_volumes, persist_args_env): + return "{} {}".format(persist_args_volumes, persist_args_env) @pytest.fixture(scope='module') def persist_test_args(): @@ -132,8 +156,8 @@ def persist_test_args(): return '' @pytest.fixture(scope='module') -def persist_tag(persist_version, persist_arch): - return '{}_{}'.format(persist_version, persist_arch) +def persist_tag(persist_version, persist_arch, persist_debian_version): + return '{}_{}_{}'.format(persist_version, persist_arch, persist_debian_version) @pytest.fixture(scope='module') def persist_webserver(persist_tag): diff --git a/test/test_bash_functions.py b/test/test_bash_functions.py index d917a50..8773d41 100644 --- a/test/test_bash_functions.py +++ b/test/test_bash_functions.py @@ -179,20 +179,3 @@ def test_webPassword_pre_existing_trumps_all_envs(Docker, args_env, test_args): assert '::: Pre existing WEBPASSWORD found' in function.stdout assert Docker.run('grep -q \'{}\' {}'.format('WEBPASSWORD=volumepass', '/etc/pihole/setupVars.conf')).rc == 0 - - -@pytest.mark.skip('broke, needs investigation in v5.0 beta') -@pytest.mark.parametrize('args_dns, expected_stdout', [ - # No DNS passed will vary by the host this is ran on, bad idea for a test - #('', 'WARNING Misconfigured DNS in /etc/resolv.conf: Primary DNS should be 127.0.0.1'), - ('--dns 1.1.1.1', 'WARNING Misconfigured DNS in /etc/resolv.conf: Two DNS servers are recommended, 127.0.0.1 and any backup server\n' - 'WARNING Misconfigured DNS in /etc/resolv.conf: Primary DNS should be 127.0.0.1 (found 1.1.1.1)'), - ('--dns 127.0.0.1', 'WARNING Misconfigured DNS in /etc/resolv.conf: Two DNS servers are recommended, 127.0.0.1 and any backup server'), - ('--dns 1.1.1.1 --dns 127.0.0.1', 'WARNING Misconfigured DNS in /etc/resolv.conf: Primary DNS should be 127.0.0.1 (found 1.1.1.1)'), - ('--dns 127.0.0.1 --dns 1.1.1.1', 'OK: Checks passed for /etc/resolv.conf DNS servers'), -]) -def test_docker_checks_for_resolvconf_misconfiguration(Docker, args_dns, expected_stdout): - ''' The container checks for misconfigured resolv.conf ''' - function = Docker.run('. /bash_functions.sh ; eval `grep docker_checks /start.sh`') - print(function.stdout) - assert expected_stdout in function.stdout diff --git a/test/test_volume_data.sh b/test/test_volume_data.sh new file mode 100755 index 0000000..0acf9b2 --- /dev/null +++ b/test/test_volume_data.sh @@ -0,0 +1,104 @@ +#!/bin/bash +set -ex +# Trying something different from the python test, this is a big integration test in bash +# Tests multiple volume settings and how they are impacted by the complete startup scripts + restart/re-creation of container +# Maybe a bit easier to read the workflow/debug in bash than python for others? +# This workflow is VERY similar to python's tests, but in bash so not object-oriented/pytest fixture based + +# Debug can be added anywhere to check current state mid-test +RED='\033[0;31m' +NC='\033[0m' # No Color +if [ $(id -u) != 0 ] ; then + sudo=sudo # do not need if root (in docker) +fi +debug() { + $sudo grep -r . "$VOL_PH" + $sudo grep -r . "$VOL_DM" +} +# Cleanup at the end, print debug on fail +cleanup() { + retcode=$? + { set +x; } 2>/dev/null + if [ $retcode != 0 ] ; then + printf "${RED}ERROR / FAILURE${NC} - printing all volume info" + debug + fi + docker rm -f $CONTAINER + $sudo rm -rf $VOLUMES + exit $retcode +} +trap "cleanup" INT TERM EXIT + + +# VOLUME TESTS + +# Given... +DEBIAN_VERSION="$(DEBIAN_VERSION:-stretch)" +IMAGE="${1:-pihole:v5.0-amd64}-${DEBIAN_VERSION}" # Default is latest build test image (generic, non release/branch tag) +VOLUMES="$(mktemp -d)" # A fresh volume directory +VOL_PH="$VOLUMES/pihole" +VOL_DM="$VOLUMES/dnsmasq.d" +tty -s && TTY='-t' || TTY='' + +echo "Testing $IMAGE with volumes base path $VOLUMES" + +# When +# Running stock+empty volumes (no ports to avoid conflicts) +CONTAINER="$( + docker run -d \ + -v "$VOL_PH:/etc/pihole/" \ + -v "$VOL_DM:/etc/dnsmasq.d/" \ + -v "/dev/null:/etc/pihole/adlists.list" \ + --entrypoint='' \ + $IMAGE \ + tail -f /dev/null +)" # container backgrounded for multipiple operations over time + +EXEC() { + local container="$1" + # Must quote for complex commands + docker exec $TTY $container bash -c "$2" +} +EXEC $CONTAINER /start.sh # run all the startup scripts + +# Then default are present +grep "PIHOLE_DNS_1=8.8.8.8" "$VOL_PH/setupVars.conf" +grep "PIHOLE_DNS_2=8.8.4.4" "$VOL_PH/setupVars.conf" +grep "IPV4_ADDRESS=0.0.0.0" "$VOL_PH/setupVars.conf" +grep -E "WEBPASSWORD=.+" "$VOL_PH/setupVars.conf" + +# Given the settings are manually changed (not good settings, just for testing changes) +EXEC $CONTAINER 'pihole -a setdns 127.1.1.1,127.2.2.2,127.3.3.3,127.4.4.4' +EXEC $CONTAINER '. /opt/pihole/webpage.sh ; change_setting IPV4_ADDRESS 10.0.0.0' +EXEC $CONTAINER 'pihole -a -p login' +assert_new_settings() { + grep "PIHOLE_DNS_1=127.1.1.1" "$VOL_PH/setupVars.conf" + grep "PIHOLE_DNS_2=127.2.2.2" "$VOL_PH/setupVars.conf" + grep "PIHOLE_DNS_3=127.3.3.3" "$VOL_PH/setupVars.conf" + grep "PIHOLE_DNS_4=127.4.4.4" "$VOL_PH/setupVars.conf" + grep "IPV4_ADDRESS=10.0.0.0" "$VOL_PH/setupVars.conf" + grep "WEBPASSWORD=6060d59351e8c2f48140f01b2c3f3b61652f396c53a5300ae239ebfbe7d5ff08" "$VOL_PH/setupVars.conf" + grep "server=127.1.1.1" $VOL_DM/01-pihole.conf + grep "server=127.2.2.2" $VOL_DM/01-pihole.conf +} +assert_new_settings + +# When Restarting +docker restart $CONTAINER +# Then settings are still manual changed values +assert_new_settings + +# When removing/re-creating the container +docker rm -f $CONTAINER +CONTAINER="$( + docker run -d \ + -v "$VOL_PH:/etc/pihole/" \ + -v "$VOL_DM:/etc/dnsmasq.d/" \ + -v "/dev/null:/etc/pihole/adlists.list" \ + --entrypoint='' \ + $IMAGE \ + tail -f /dev/null +)" # container backgrounded for multipiple operations over time + +# Then settings are still manual changed values +assert_new_settings diff --git a/test/test_volumes.py b/test/test_volumes.py new file mode 100644 index 0000000..1207f27 --- /dev/null +++ b/test/test_volumes.py @@ -0,0 +1,4 @@ +def test_volume_shell_script(arch, run_and_stream_command_output): + # only one arch should be necessary + if arch == 'amd64': + run_and_stream_command_output('./test/test_volume_data.sh') diff --git a/tox.ini b/tox.ini index 9d3164d..9342d6b 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = py38 [testenv] -commands = echo "Use ./circle-test.sh instead for now" +commands = echo "Use ./gh-actions-test.sh instead for now" # Currently out of comission post-python3 upgrade due to failed monkey patch of testinfra sh -> bash #[testenv]