From 20955a41fe049f9f5cd6dbd0f56e82f6c0c86142 Mon Sep 17 00:00:00 2001 From: diginc Date: Thu, 4 May 2017 22:37:26 -0500 Subject: [PATCH] alpine converted to s6 --- .dockerignore | 1 + alpine.docker | 33 ++++++----- alpine/root/etc/cont-init.d/20-start.sh | 4 ++ .../root/etc/fix-attrs.d/01-resolver-resolv | 1 + alpine/root/etc/services.d/cron/run | 7 +++ alpine/root/etc/services.d/dnsmasq/finish | 3 + alpine/root/etc/services.d/dnsmasq/run | 5 ++ alpine/root/etc/services.d/nginx/run | 6 ++ alpine/root/etc/services.d/php-fpm/run | 5 ++ alpine/root/usr/bin/host-ip | 10 ++++ alpine/root/usr/bin/set-contenv | 12 ++++ alpine/service | 56 ++++++------------- bash_functions.sh | 16 ++---- install.sh | 2 + test/conftest.py | 10 ++-- test/test_pihole_scripts.py | 7 ++- test/test_shellcheck.py | 2 +- 17 files changed, 110 insertions(+), 70 deletions(-) create mode 100644 .dockerignore create mode 100644 alpine/root/etc/cont-init.d/20-start.sh create mode 100644 alpine/root/etc/fix-attrs.d/01-resolver-resolv create mode 100644 alpine/root/etc/services.d/cron/run create mode 100644 alpine/root/etc/services.d/dnsmasq/finish create mode 100644 alpine/root/etc/services.d/dnsmasq/run create mode 100644 alpine/root/etc/services.d/nginx/run create mode 100644 alpine/root/etc/services.d/php-fpm/run create mode 100755 alpine/root/usr/bin/host-ip create mode 100755 alpine/root/usr/bin/set-contenv diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..78fd378 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +**/*.sw* diff --git a/alpine.docker b/alpine.docker index dd5caf0..6d55c89 100644 --- a/alpine.docker +++ b/alpine.docker @@ -4,24 +4,23 @@ MAINTAINER adam@diginc.us ENV IMAGE alpine ENV PATH /opt/pihole:${PATH} -COPY install.sh /install.sh -COPY ./alpine/service /usr/local/bin/service +COPY install.sh /usr/local/bin/docker-install.sh ENV setupVars /etc/pihole/setupVars.conf ENV PIHOLE_INSTALL /tmp/ph_install.sh +ENV S6OVERLAY_RELEASE https://github.com/just-containers/s6-overlay/releases/download/v1.18.1.5/s6-overlay-amd64.tar.gz -ENV TINI_VERSION v0.13.0 -ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini -ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static.asc /tini.asc - -# Tini and package requirements -RUN apk add --update 'gnupg<2.1.17-r0' && \ - gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 0527A9B7 && \ - gpg --verify /tini.asc && \ - chmod +x /tini && \ - apk add wget bash && \ - /install.sh && \ +RUN apk upgrade --update && \ + apk add bind-tools wget curl bash libcap && \ + curl -L -s $S6OVERLAY_RELEASE \ + | tar xvzf - -C / && \ + docker-install.sh && \ rm -rf /var/cache/apk/* +ENTRYPOINT [ "/init" ] + +ADD alpine/root / +COPY ./alpine/service /usr/local/bin/service + # Customized from submodules COPY ./alpine/nginx.conf /etc/nginx/nginx.conf @@ -40,6 +39,7 @@ RUN mkdir -p /etc/pihole/ && \ chmod 644 /var/log/pihole.log && \ chown dnsmasq:root /var/log/pihole.log && \ sed -i "s/@INT@/eth0/" /etc/dnsmasq.d/01-pihole.conf && \ + setcap CAP_NET_BIND_SERVICE=+eip `which dnsmasq` && \ echo 'Done!' #sed -i 's|"cd /etc/.pihole/ && git describe --tags --abbrev=0"|"cat /etc/pi-hole_version.txt"|g' /var/www/html/admin/footer.php && \ @@ -58,6 +58,9 @@ ENV IPv6 True EXPOSE 53 53/udp EXPOSE 80 +ENV S6_LOGGING 0 +ENV S6_KEEP_ENV 1 +#ENV S6_LOGGING_SCRIPT "n20 s1000000 T" + SHELL ["/bin/bash", "-c"] -ENTRYPOINT ["/tini", "--"] -CMD [ "/start.sh" ] +#CMD [ "/start.sh" ] diff --git a/alpine/root/etc/cont-init.d/20-start.sh b/alpine/root/etc/cont-init.d/20-start.sh new file mode 100644 index 0000000..4b09fe9 --- /dev/null +++ b/alpine/root/etc/cont-init.d/20-start.sh @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash + +/start.sh +gravity.sh diff --git a/alpine/root/etc/fix-attrs.d/01-resolver-resolv b/alpine/root/etc/fix-attrs.d/01-resolver-resolv new file mode 100644 index 0000000..84e9986 --- /dev/null +++ b/alpine/root/etc/fix-attrs.d/01-resolver-resolv @@ -0,0 +1 @@ +/etc/resolv.conf false doesntexist,0:1000 0664 0664 \ No newline at end of file diff --git a/alpine/root/etc/services.d/cron/run b/alpine/root/etc/services.d/cron/run new file mode 100644 index 0000000..410119d --- /dev/null +++ b/alpine/root/etc/services.d/cron/run @@ -0,0 +1,7 @@ +#!/usr/bin/with-contenv bash +s6-echo "Starting crond" + +exec -c +fdmove -c 2 1 + +/usr/sbin/crond -f -L /var/log/cron -l 0 -c /etc/crontabs diff --git a/alpine/root/etc/services.d/dnsmasq/finish b/alpine/root/etc/services.d/dnsmasq/finish new file mode 100644 index 0000000..77aec95 --- /dev/null +++ b/alpine/root/etc/services.d/dnsmasq/finish @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash + +kill -9 $(pgrep dnsmasq) diff --git a/alpine/root/etc/services.d/dnsmasq/run b/alpine/root/etc/services.d/dnsmasq/run new file mode 100644 index 0000000..9822237 --- /dev/null +++ b/alpine/root/etc/services.d/dnsmasq/run @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash + +s6-echo "Starting dnsmasq" + +s6-setuidgid dnsmasq dnsmasq -7 /etc/dnsmasq.d --no-daemon diff --git a/alpine/root/etc/services.d/nginx/run b/alpine/root/etc/services.d/nginx/run new file mode 100644 index 0000000..1a07812 --- /dev/null +++ b/alpine/root/etc/services.d/nginx/run @@ -0,0 +1,6 @@ +#!/usr/bin/with-contenv bash + +s6-svwait -u -t 5000 /var/run/s6/services/php-fpm +s6-echo "Starting nginx" + +nginx -g "daemon off;" diff --git a/alpine/root/etc/services.d/php-fpm/run b/alpine/root/etc/services.d/php-fpm/run new file mode 100644 index 0000000..7572f76 --- /dev/null +++ b/alpine/root/etc/services.d/php-fpm/run @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash + +s6-echo "Starting php-fpm" + +php-fpm -d daemonize=no diff --git a/alpine/root/usr/bin/host-ip b/alpine/root/usr/bin/host-ip new file mode 100755 index 0000000..ff2f67b --- /dev/null +++ b/alpine/root/usr/bin/host-ip @@ -0,0 +1,10 @@ +#!/usr/bin/with-contenv sh + +# +# This script will determine the network IP of the container. +# +# Return format should be a single IP address. +# + +# Default to using the value of the $HOSTNAME ENV variable. +getent hosts ${1:-$HOSTNAME} | awk '{print $1}' diff --git a/alpine/root/usr/bin/set-contenv b/alpine/root/usr/bin/set-contenv new file mode 100755 index 0000000..7cb86c9 --- /dev/null +++ b/alpine/root/usr/bin/set-contenv @@ -0,0 +1,12 @@ +#!/usr/bin/execlineb -S0 + +if { s6-test $# -eq 2 } + +backtick -in FILENAME { + pipeline { s6-echo "${1}" } + tr "a-z" "A-Z" +} +import -u FILENAME + +redirfd -w 1 /var/run/s6/container_environment/${FILENAME} +s6-echo -n -- ${2} diff --git a/alpine/service b/alpine/service index bf5335d..4360442 100755 --- a/alpine/service +++ b/alpine/service @@ -1,55 +1,35 @@ #!/bin/bash -# lazy cheap service script patch for alpine -dnsmasq_start() { - dnsmasq -7 /etc/dnsmasq.d +# This script patches all service commands into the appropriate s6- commands +# pi-hole upstream scripts need a 'service' interface. why not systemd? docker said so. +start() { + s6-svc -wU -u -T2500 /var/run/s6/services/$service } -dnsmasq_stop() { - kill -9 $(pidof dnsmasq) +stop() { + s6-svc -wD -d -T2500 /var/run/s6/services/$service } -dnsmasq_restart() { - dnsmasq_stop; dnsmasq_start; +restart() { + stop + start + #s6-svc -t -wR -T5000 /var/run/s6/services/$service } status() { - if pidof $service 2&>1 > /dev/null ; then - echo "$service running" - else - echo "$service not running" - fi; -} - -nginx_start() { - nginx -t && \ - nginx -} -nginx_stop() { - nginx -t && \ - kill -9 $(pidof nginx) -} -nginx_restart() { - nginx_stop - nginx_start -} -dnsmasq_status() { - status -} -nginx_status() { - status + s6-svstat /var/run/s6/services/$service } service="$1" command="$2" + +if [[ ! -d "/var/run/s6/services/$service" ]] ; then + echo "s6 service not found for $service, exiting..." + exit +fi; + if [[ "$service" == 'lighttpd' ]] ; then echo -e "Lighttpd replaced by nginx in diginc/pi-hole:alpine\nrunning service nginx $command instead"; service='nginx' fi; - -if [[ "$service" == 'dnsmasq' ]] || [[ "$service" == 'nginx' ]] ; then - ${service}_${command} || echo "Unknown option $command" -else - echo "$service service wrapper not patched into alpine container" - exit 1 -fi +${command} "${service}" diff --git a/bash_functions.sh b/bash_functions.sh index 3c83de6..bdc42cb 100644 --- a/bash_functions.sh +++ b/bash_functions.sh @@ -134,7 +134,9 @@ setup_web_password() { WEBPASSWORD=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) echo "Assigning random password: $WEBPASSWORD" fi; + set -x pihole -a -p "$WEBPASSWORD" + { set +x; } 2>/dev/null } setup_ipv4_ipv6() { local ip_versions="IPv4 and IPv6" @@ -182,20 +184,14 @@ test_framework_stubbing() { } docker_main() { - echo -n '::: Starting up DNS and Webserver ...' - service dnsmasq restart # Just get DNS up. The webserver is down!!! - IMAGE="$1" case $IMAGE in # Setup webserver - "alpine") - php-fpm - nginx - ;; "debian") + echo -n '::: Starting up DNS and Webserver ...' + service dnsmasq restart service lighttpd start + gravity.sh + tail -F "${WEBLOGDIR}"/*.log /var/log/pihole.log ;; esac - - gravity.sh # Finally lets update and be awesome. - tail -F "${WEBLOGDIR}"/*.log /var/log/pihole.log } diff --git a/install.sh b/install.sh index edc910b..b16ce65 100755 --- a/install.sh +++ b/install.sh @@ -43,6 +43,8 @@ elif [[ "$IMAGE" == 'alpine' ]] ; then ca-certificates \ php5-fpm php5-json php5-openssl php5-zip libxml2 \ bc bash curl perl sudo git + # S6 service like to be blocking/foreground + sed -i 's|^;daemonize = yes|daemonize = no|' /etc/php5/php-fpm.conf fi piholeGitUrl="${piholeGitUrl}" diff --git a/test/conftest.py b/test/conftest.py index 9a4ef49..70b299f 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -10,12 +10,14 @@ check_output = testinfra.get_backend( def DockerGeneric(request, args, image, cmd): assert 'docker' in check_output('id'), "Are you in the docker group?" if 'diginc/pi-hole' in image: - args += " -v /dev/null:/etc/pihole/adlists.default -e PYTEST=\"True\"" + #args += " -v /dev/null:/etc/pihole/adlists.default -e PYTEST=\"True\"" + args += " -e PYTEST=\"True\"" docker_run = "docker run -d {} {} {}".format(args, image, cmd) docker_id = check_output(docker_run) def teardown(): - check_output("docker rm -f %s", docker_id) + check_output("docker logs {}".format(docker_id)) + check_output("docker rm -f {}".format(docker_id)) request.addfinalizer(teardown) docker_container = testinfra.get_backend("docker://" + docker_id) @@ -68,7 +70,7 @@ def image(request, tag): @pytest.fixture() def cmd(request): - return '/start.sh' + return '' @pytest.fixture(scope='session') def persist_args(request): @@ -88,7 +90,7 @@ def persist_image(request, persist_tag): @pytest.fixture(scope='session') def persist_cmd(request): - return '/start.sh' + return '' @pytest.fixture def Slow(): diff --git a/test/test_pihole_scripts.py b/test/test_pihole_scripts.py index 868dc43..3b03e66 100644 --- a/test/test_pihole_scripts.py +++ b/test/test_pihole_scripts.py @@ -13,15 +13,18 @@ START_DNS_STDOUT = { def RunningPiHole(DockerPersist, Slow, persist_webserver, persist_tag, start_cmd): ''' Override the RunningPiHole to run and check for success of a dnsmasq start based `pihole` script command ''' + #print DockerPersist.run('ps -ef').stdout Slow(lambda: DockerPersist.run('pgrep dnsmasq').rc == 0) - Slow(lambda: DockerPersist.run('pgrep {}'.format(persist_webserver) ).rc == 0) + Slow(lambda: DockerPersist.run('pgrep {}'.format(persist_webserver)).rc == 0) oldpid = DockerPersist.run('pidof dnsmasq') + for service in [ 'dnsmasq', 'nginx', ]: + print DockerPersist.run('service {} status'.format(service)) cmd = DockerPersist.run('pihole {}'.format(start_cmd)) Slow(lambda: DockerPersist.run('pgrep dnsmasq').rc == 0) newpid = DockerPersist.run('pidof dnsmasq') for pid in [oldpid, newpid]: assert pid != '' - # ensure a new pid for dnsmasq appeared + # ensure a new pid for dnsmasq appeared due to service restart assert oldpid != newpid assert cmd.rc == 0 # Save out cmd result to check different stdout of start/enable/disable diff --git a/test/test_shellcheck.py b/test/test_shellcheck.py index f76d388..7206a13 100644 --- a/test/test_shellcheck.py +++ b/test/test_shellcheck.py @@ -7,7 +7,7 @@ run_local = testinfra.get_backend( def test_scripts_pass_shellcheck(): ''' Make sure shellcheck does not find anything wrong with our shell scripts ''' - shellcheck = "find . -name '*.sh' | while read file; do shellcheck $file; done;" + shellcheck = "find . -name '*.sh' | while read file; do shellcheck -e SC1008 $file; done;" results = run_local(shellcheck) print results.stdout assert '' == results.stdout