Merge pull request #1131 from pi-hole/dev

Dev -> master before tagging
This commit is contained in:
Adam Warner 2022-07-08 19:19:24 +01:00 committed by GitHub
commit 2f0fc391c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 203 additions and 165 deletions

View File

@ -2,7 +2,7 @@ name: Mark stale issues
on:
schedule:
- cron: '0 * * * *'
- cron: '0 8 * * *'
workflow_dispatch:
jobs:
@ -23,4 +23,4 @@ jobs:
exempt-issue-labels: 'pinned, Fixed in next release, bug, never-stale, documentation, investigating'
exempt-all-issue-assignees: true
operations-per-run: 300
stale-issue-reason: 'not_planned'
close-issue-reason: 'not_planned'

View File

@ -21,7 +21,7 @@ RUN bash -ex install.sh 2>&1 && \
ARG PHP_ENV_CONFIG
ENV PHP_ENV_CONFIG /etc/lighttpd/conf-enabled/15-fastcgi-php.conf
ARG PHP_ERROR_LOG
ENV PHP_ERROR_LOG /var/log/lighttpd/error.log
ENV PHP_ERROR_LOG /var/log/lighttpd/error-pihole.log
COPY ./start.sh /
COPY ./bash_functions.sh /

View File

@ -40,7 +40,7 @@ pytest = "==4.6.8"
pytest-cov = "==2.8.1"
pytest-forked = "==1.1.3"
pytest-xdist = "==1.31.0"
requests = "==2.22.0"
requests = "==2.28.1"
scandir = "==1.10.0"
six = "==1.13.0"
subprocess32 = "==3.5.4"
@ -48,7 +48,7 @@ testinfra = "==3.3.0"
texttable = "==1.6.2"
toml = "==0.10.0"
tox = "==3.14.3"
urllib3 = "==1.25.8"
urllib3 = "==1.26.5"
virtualenv = "==16.7.9"
wcwidth = "==0.1.7"
zipp = "==0.6.0"

28
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "2c7f1fb7f001bf70bba7309859b06dc323040f21518b32ee8993aa823c27df15"
"sha256": "3acc0cd5a21768bc88eb864826f4309a3fe26d525f14dddd17d33a0c2490e57c"
},
"pipfile-spec": 6,
"requires": {
@ -143,6 +143,13 @@
"index": "pypi",
"version": "==3.0.4"
},
"charset-normalizer": {
"hashes": [
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
],
"version": "==2.1.0"
},
"configparser": {
"hashes": [
"sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c",
@ -381,11 +388,10 @@
},
"packaging": {
"hashes": [
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.9"
"version": "==21.3"
},
"pathlib2": {
"hashes": [
@ -502,11 +508,11 @@
},
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
"sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
"sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
],
"index": "pypi",
"version": "==2.22.0"
"version": "==2.28.1"
},
"scandir": {
"hashes": [
@ -577,11 +583,11 @@
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
],
"index": "pypi",
"version": "==1.25.8"
"version": "==1.26.5"
},
"virtualenv": {
"hashes": [

View File

@ -47,7 +47,7 @@ services:
# Volumes store your data between container upgrades
volumes:
- './etc-pihole:/etc/pihole'
- './etc-dnsmasq.d:/etc/dnsmasq.d'
- './etc-dnsmasq.d:/etc/dnsmasq.d'
# https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
cap_add:
- NET_ADMIN # Required if you are using Pi-hole as your DHCP server, else not needed

View File

@ -6,12 +6,12 @@ fix_capabilities() {
# Testing on Docker 20.10.14 with no caps set shows the following caps available to the container:
# Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
# FTL can also use CAP_NET_ADMIN and CAP_SYS_NICE. If we try to set them when they haven't been explicitly enabled, FTL will not start. Test for them first:
/sbin/capsh --has-p=cap_chown && CAP_STR+=',CAP_CHOWN'
/sbin/capsh --has-p=cap_net_bind_service && CAP_STR+=',CAP_NET_BIND_SERVICE'
/sbin/capsh --has-p=cap_net_raw && CAP_STR+=',CAP_NET_RAW'
/sbin/capsh --has-p=cap_net_admin && CAP_STR+=',CAP_NET_ADMIN' || DHCP_READY='false'
/sbin/capsh --has-p=cap_sys_nice && CAP_STR+=',CAP_SYS_NICE'
/sbin/capsh --has-p=cap_sys_nice && CAP_STR+=',CAP_SYS_NICE'
if [[ ${CAP_STR} ]]; then
# We have the (some of) the above caps available to us - apply them to pihole-FTL
@ -24,12 +24,12 @@ fix_capabilities() {
DHCP_ACTIVE='false'
change_setting "DHCP_ACTIVE" "false"
fi
if [[ $ret -ne 0 && "${DNSMASQ_USER:-pihole}" != "root" ]]; then
echo "ERROR: Unable to set capabilities for pihole-FTL. Cannot run as non-root."
echo " If you are seeing this error, please set the environment variable 'DNSMASQ_USER' to the value 'root'"
exit 1
fi
fi
else
echo "WARNING: Unable to set capabilities for pihole-FTL."
echo " Please ensure that the container has the required capabilities."
@ -45,21 +45,21 @@ prepare_configs() {
LIGHTTPD_GROUP="www-data"
LIGHTTPD_CFG="lighttpd.conf.debian"
installConfigs
if [ ! -f "${setupVars}" ]; then
install -m 644 /dev/null "${setupVars}"
echo "Creating empty ${setupVars} file."
fi
set +e
mkdir -p /var/run/pihole /var/log/pihole
chown pihole:root /etc/lighttpd
# In case of `pihole` UID being changed, re-chown the pihole scripts and pihole command
chown -R pihole:root "${PI_HOLE_INSTALL_DIR}"
chown pihole:root "${PI_HOLE_BIN_DIR}/pihole"
set -e
# Update version numbers
pihole updatechecker
@ -270,18 +270,25 @@ load_web_password_secret() {
fi;
}
generate_password() {
if [ -z "${WEBPASSWORD+x}" ] ; then
# Not set at all, give the user a random pass
WEBPASSWORD=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
echo "Assigning random password: $WEBPASSWORD"
fi;
}
setup_web_password() {
setup_var_exists "WEBPASSWORD" && return
if [ -z "${WEBPASSWORD+x}" ] ; then
# ENV WEBPASSWORD is not set
# Exit if setupvars already has a password
setup_var_exists "WEBPASSWORD" && return
# Generate new random password
WEBPASSWORD=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
echo "Assigning random password: $WEBPASSWORD"
else
# ENV WEBPASSWORD is set an will be used
echo "::: Assigning password defined by Environment Variable"
fi
PASS="$WEBPASSWORD"
PASS="$1"
# Explicitly turn off bash printing when working with secrets
{ set +x; } 2>/dev/null

View File

@ -24,7 +24,7 @@ services:
- './etc-pihole:/etc/pihole'
- './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'
# - './var-log/pihole.log:/var/log/pihole/pihole.log'
# Recommended but not required (DHCP needs NET_ADMIN)
# https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
cap_add:

View File

@ -47,7 +47,7 @@ testinfra==3.3.0
texttable==1.6.2
toml==0.10.0
tox==3.14.3
urllib3==1.25.8
urllib3==1.25.9
virtualenv==16.7.9
wcwidth==0.1.7
websocket-client==0.57.0

View File

@ -28,7 +28,7 @@ if [ -z "$SKIPGRAVITYONBOOT" ] || [ ! -e "${gravityDBfile}" ]; then
echo " Ignoring SKIPGRAVITYONBOOT on this occaision."
fi
echo '@reboot root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log' > /etc/cron.d/gravity-on-boot
echo '@reboot root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updateGravity >/var/log/pihole/pihole_updateGravity.log || cat /var/log/pihole/pihole_updateGravity.log' > /etc/cron.d/gravity-on-boot
else
echo " Skipping Gravity Database Update."
[ ! -e /etc/cron.d/gravity-on-boot ] || rm /etc/cron.d/gravity-on-boot &>/dev/null

View File

@ -2,4 +2,4 @@
s6-echo "Starting lighttpd-access-log"
s6-setuidgid www-data cat /var/log/lighttpd/access.log 2>&1
s6-setuidgid www-data cat /var/log/lighttpd/access-pihole.log 2>&1

View File

@ -2,4 +2,4 @@
s6-echo "Starting lighttpd-error-log"
s6-setuidgid www-data cat /var/log/lighttpd/error.log 2>&1
s6-setuidgid www-data cat /var/log/lighttpd/error-pihole.log 2>&1

View File

@ -4,7 +4,7 @@ s6-echo "Starting lighttpd"
if [[ 1 -eq ${WEBLOGS_STDOUT:-0} ]]; then
#lighthttpd cannot use /dev/stdout https://redmine.lighttpd.net/issues/2731
for fi in /var/log/lighttpd/access.log /var/log/lighttpd/error.log
for fi in /var/log/lighttpd/access-pihole.log /var/log/lighttpd/error-pihole.log
do
if [[ ! -p ${fi} ]]; then
rm -f ${fi}
@ -17,16 +17,16 @@ if [[ 1 -eq ${WEBLOGS_STDOUT:-0} ]]; then
sleep 2
else
#remove fifo if exists
[[ -p /var/log/lighttpd/access.log ]] && rm -Rf /var/log/lighttpd/access.log
[[ -p /var/log/lighttpd/error.log ]] && rm -Rf /var/log/lighttpd/error.log
[[ -p /var/log/lighttpd/access-pihole.log ]] && rm -Rf /var/log/lighttpd/access-pihole.log
[[ -p /var/log/lighttpd/error-pihole.log ]] && rm -Rf /var/log/lighttpd/error-pihole.log
# install /dev/null log files to ensure they exist (create if non-existing, preserve if existing)
[[ ! -f /var/log/lighttpd/access.log ]] && install /dev/null /var/log/lighttpd/access.log
[[ ! -f /var/log/lighttpd/error.log ]] && install /dev/null /var/log/lighttpd/error.log
[[ ! -f /var/log/lighttpd/access-pihole.log ]] && install /dev/null /var/log/lighttpd/access-pihole.log
[[ ! -f /var/log/lighttpd/error-pihole.log ]] && install /dev/null /var/log/lighttpd/error-pihole.log
# Ensure that permissions are set so that lighttpd can write to the logs
chown -R www-data:www-data /var/log/lighttpd
chmod 0644 /var/log/lighttpd/access.log /var/log/lighttpd/error.log
chmod 0644 /var/log/lighttpd/access-pihole.log /var/log/lighttpd/error-pihole.log
fi
lighttpd -D -f /etc/lighttpd/lighttpd.conf

View File

@ -6,16 +6,16 @@ rm -f /dev/shm/FTL-* 2> /dev/null
rm /run/pihole/FTL.sock 2> /dev/null
# install /dev/null files to ensure they exist (create if non-existing, preserve if existing)
mkdir -pm 0755 /run/pihole
mkdir -pm 0755 /run/pihole /var/log/pihole
[[ ! -f /run/pihole-FTL.pid ]] && install /dev/null /run/pihole-FTL.pid
[[ ! -f /run/pihole-FTL.port ]] && install /dev/null /run/pihole-FTL.port
[[ ! -f /var/log/pihole-FTL.log ]] && install /dev/null /var/log/pihole-FTL.log
[[ ! -f /var/log/pihole.log ]] && install /dev/null /var/log/pihole.log
[[ ! -f /var/log/pihole/FTL.log ]] && install /dev/null /var/log/pihole/FTL.log
[[ ! -f /var/log/pihole/pihole.log ]] && install /dev/null /var/log/pihole/pihole.log
[[ ! -f /etc/pihole/dhcp.leases ]] && install /dev/null /etc/pihole/dhcp.leases
# Ensure that permissions are set so that pihole-FTL can edit all necessary files
chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases /run/pihole /etc/pihole
chmod 0644 /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases
chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole/FTL.log /var/log/pihole/pihole.log /etc/pihole/dhcp.leases /run/pihole /etc/pihole
chmod 0644 /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole/FTL.log /var/log/pihole/pihole.log /etc/pihole/dhcp.leases
# Ensure that permissions are set so that pihole-FTL can edit the files. We ignore errors as the file may not (yet) exist
chmod -f 0644 /etc/pihole/macvendor.db
@ -24,10 +24,22 @@ chown -f pihole:pihole /etc/pihole/pihole-FTL.db /etc/pihole/gravity.db /etc/pih
# Chown database file permissions so that the pihole group (web interface) can edit the file. We ignore errors as the files may not (yet) exist
chmod -f 0664 /etc/pihole/pihole-FTL.db
# Backward compatibility for user-scripts that still expect log files in /var/log instead of /var/log/pihole/
# Should be removed with Pi-hole v6.0
if [ ! -f /var/log/pihole.log ]; then
ln -s /var/log/pihole/pihole.log /var/log/pihole.log
chown -h pihole:pihole /var/log/pihole.log
fi
if [ ! -f /var/log/pihole-FTL.log ]; then
ln -s /var/log/pihole/FTL.log /var/log/pihole-FTL.log
chown -h pihole:pihole /var/log/pihole-FTL.log
fi
# Call capsh with the detected capabilities
capsh --inh=${CAP_STR:1} --addamb=${CAP_STR:1} --user=$DNSMASQ_USER --keep=1 -- -c "/usr/bin/pihole-FTL $FTL_CMD >/dev/null 2>&1"
# Notes on above:
# - DNSMASQ_USER default of pihole is in Dockerfile & can be overwritten by runtime container env
# - /var/log/pihole*.log has FTL's output that no-daemon would normally print in FG too
# - /var/log/pihole/pihole*.log has FTL's output that no-daemon would normally print in FG too
# prevent duplicating it in docker logs by sending to dev null

View File

@ -39,6 +39,7 @@ export PIHOLE_DOMAIN
export DHCP_IPv6
export DHCP_rapid_commit
export WEBTHEME
export WEBPASSWORD
export CUSTOM_CACHE_SIZE
export adlistFile='/etc/pihole/adlists.list'
@ -67,7 +68,6 @@ echo " ::: Starting docker specific checks & setup for docker pihole/pihole"
fix_capabilities
load_web_password_secret
generate_password
validate_env || exit 1
prepare_configs
@ -185,7 +185,7 @@ fi
[[ -n "${DHCP_ACTIVE}" && ${DHCP_ACTIVE} == "true" ]] && echo "Setting DHCP server" && setup_dhcp
setup_web_port "$WEB_PORT"
setup_web_password "$WEBPASSWORD"
setup_web_password
setup_temp_unit "$TEMPERATUREUNIT"
setup_ui_layout "$WEBUIBOXEDLAYOUT"
setup_admin_email "$ADMIN_EMAIL"

View File

@ -8,6 +8,7 @@ local_host = testinfra.get_host('local://')
check_output = local_host.check_output
DEBIAN_VERSION = os.environ.get('DEBIAN_VERSION', 'bullseye')
TAIL_DEV_NULL='tail -f /dev/null'
@pytest.fixture()
def run_and_stream_command_output():
@ -44,7 +45,7 @@ def test_args():
''' test override fixture to provide arguments separate from our core args '''
return ''
def DockerGeneric(request, _test_args, _args, _image, _cmd, _entrypoint):
def docker_generic(request, _test_args, _args, _image, _cmd, _entrypoint):
#assert 'docker' in check_output('id'), "Are you in the docker group?"
# Always appended PYTEST arg to tell pihole we're testing
if 'pihole' in _image and 'PYTEST=1' not in _args:
@ -52,7 +53,7 @@ def DockerGeneric(request, _test_args, _args, _image, _cmd, _entrypoint):
docker_run = 'docker run -d -t {args} {test_args} {entry} {image} {cmd}'\
.format(args=_args, test_args=_test_args, entry=_entrypoint, image=_image, cmd=_cmd)
# Print a human runable version of the container run command for faster debugging
print(docker_run.replace('-d -t', '--rm -it').replace('tail -f /dev/null', 'bash'))
print(docker_run.replace('-d -t', '--rm -it').replace(TAIL_DEV_NULL, 'bash'))
docker_id = check_output(docker_run)
def teardown():
@ -67,17 +68,17 @@ def DockerGeneric(request, _test_args, _args, _image, _cmd, _entrypoint):
@pytest.fixture
def Docker(request, test_args, args, image, cmd, entrypoint):
def docker(request, test_args, args, image, cmd, entrypoint):
''' One-off Docker container run '''
return DockerGeneric(request, test_args, args, image, cmd, entrypoint)
return docker_generic(request, test_args, args, image, cmd, entrypoint)
@pytest.fixture(scope='module')
def DockerPersist(request, persist_test_args, persist_args, persist_image, persist_cmd, persist_entrypoint, Dig):
def docker_persist(request, persist_test_args, persist_args, persist_image, persist_cmd, persist_entrypoint, dig):
''' Persistent Docker container for multiple tests, instead of stopping container after one test '''
''' Uses DUP'd module scoped fixtures because smaller scoped fixtures won't mix with module scope '''
persistent_container = DockerGeneric(request, persist_test_args, persist_args, persist_image, persist_cmd, persist_entrypoint)
persistent_container = docker_generic(request, persist_test_args, persist_args, persist_image, persist_cmd, persist_entrypoint)
''' attach a dig container for lookups '''
persistent_container.dig = Dig(persistent_container.id)
persistent_container.dig = dig(persistent_container.id)
return persistent_container
@pytest.fixture
@ -112,7 +113,7 @@ def image(tag):
@pytest.fixture()
def cmd():
return 'tail -f /dev/null'
return TAIL_DEV_NULL
@pytest.fixture(scope='module')
def persist_arch():
@ -164,19 +165,19 @@ def persist_image(persist_tag):
@pytest.fixture(scope='module')
def persist_cmd():
return 'tail -f /dev/null'
return TAIL_DEV_NULL
@pytest.fixture(scope='module')
def persist_entrypoint():
return ''
@pytest.fixture
def Slow():
def slow():
"""
Run a slow check, check if the state is correct for `timeout` seconds.
"""
import time
def slow(check, timeout=20):
def _slow(check, timeout=20):
timeout_at = time.time() + timeout
while True:
try:
@ -188,26 +189,27 @@ def Slow():
raise e
else:
return
return slow
return _slow
@pytest.fixture(scope='module')
def Dig():
def dig():
''' separate container to link to pi-hole and perform lookups '''
''' a docker pull is faster than running an install of dnsutils '''
def dig(docker_id):
def _dig(docker_id):
args = '--link {}:test_pihole'.format(docker_id)
image = 'azukiapp/dig'
cmd = 'tail -f /dev/null'
dig_container = DockerGeneric(request, '', args, image, cmd, '')
cmd = TAIL_DEV_NULL
dig_container = docker_generic(request, '', args, image, cmd, '')
return dig_container
return dig
return _dig
'''
Persistent Docker container for testing service post start.sh
'''
@pytest.fixture
def RunningPiHole(DockerPersist, Slow, persist_webserver):
def running_pihole(docker_persist, slow, persist_webserver):
''' Persist a fully started docker-pi-hole to help speed up subsequent tests '''
Slow(lambda: DockerPersist.run('pgrep pihole-FTL').rc == 0)
Slow(lambda: DockerPersist.run('pgrep lighttpd').rc == 0)
return DockerPersist
slow(lambda: docker_persist.run('pgrep pihole-FTL').rc == 0)
slow(lambda: docker_persist.run('pgrep lighttpd').rc == 0)
return docker_persist

View File

@ -3,6 +3,16 @@ import os
import pytest
import re
SETUPVARS_LOC='/etc/pihole/setupVars.conf'
DNSMASQ_CONFIG_LOC = '/etc/dnsmasq.d/01-pihole.conf'
EVAL_SETUP_DNSMASQ='. /bash_functions.sh ; eval `grep "^setup_dnsmasq " /start.sh`'
EVAL_SETUP_WEB_PASSWORD='. /bash_functions.sh ; eval `grep setup_web_password /start.sh`'
def _cat(file):
return 'cat {}'.format(file)
def _grep(string, file):
return 'grep -q \'{}\' {}'.format(string,file)
@pytest.mark.parametrize('test_args,expected_ipv6,expected_stdout', [
('', True, 'IPv4 and IPv6'),
@ -10,32 +20,32 @@ import re
('-e "IPv6=False"', False, 'IPv4'),
('-e "IPv6=foobar"', False, 'IPv4'),
])
def test_IPv6_not_True_removes_ipv6(Docker, Slow, test_args, expected_ipv6, expected_stdout):
def test_ipv6_not_true_removes_ipv6(docker, slow, test_args, expected_ipv6, expected_stdout):
''' When a user overrides IPv6=True they only get IPv4 listening webservers '''
IPV6_LINE = 'use-ipv6.pl'
WEB_CONFIG = '/etc/lighttpd/lighttpd.conf'
function = Docker.run('. /bash_functions.sh ; setup_ipv4_ipv6')
function = docker.run('. /bash_functions.sh ; setup_ipv4_ipv6')
assert "Using {}".format(expected_stdout) in function.stdout
if expected_stdout == 'IPv4':
assert 'IPv6' not in function.stdout
# On overlay2(?) docker sometimes writes to disk are slow enough to break some tests...
expected_ipv6_check = lambda: (\
IPV6_LINE in Docker.run('grep \'use-ipv6.pl\' {}'.format(WEB_CONFIG)).stdout
IPV6_LINE in docker.run('grep \'use-ipv6.pl\' {}'.format(WEB_CONFIG)).stdout
) == expected_ipv6
Slow(expected_ipv6_check)
slow(expected_ipv6_check)
@pytest.mark.parametrize('test_args', ['-e "WEB_PORT=999"'])
def test_overrides_default_WEB_PORT(Docker, Slow, test_args):
def test_overrides_default_web_port(docker, slow, test_args):
''' When a --net=host user sets WEB_PORT to avoid synology's 80 default IPv4 and or IPv6 ports are updated'''
CONFIG_LINE = r'server.port\s*=\s*999'
WEB_CONFIG = '/etc/lighttpd/lighttpd.conf'
function = Docker.run('. /bash_functions.sh ; eval `grep setup_web_port /start.sh`')
function = docker.run('. /bash_functions.sh ; eval `grep setup_web_port /start.sh`')
assert "Custom WEB_PORT set to 999" in function.stdout
assert "INFO: Without proper router DNAT forwarding to 127.0.0.1:999, you may not get any blocked websites on ads" in function.stdout
Slow(lambda: re.search(CONFIG_LINE, Docker.run('cat {}'.format(WEB_CONFIG)).stdout) != None)
slow(lambda: re.search(CONFIG_LINE, docker.run(_cat(WEB_CONFIG)).stdout) != None)
@pytest.mark.parametrize('test_args,expected_error', [
@ -43,42 +53,39 @@ def test_overrides_default_WEB_PORT(Docker, Slow, test_args):
('-e WEB_PORT="1,000"', 'WARNING: Custom WEB_PORT not used - 1,000 is not an integer'),
('-e WEB_PORT="99999"', 'WARNING: Custom WEB_PORT not used - 99999 is not within valid port range of 1-65535'),
])
def test_bad_input_to_WEB_PORT(Docker, test_args, expected_error):
function = Docker.run('. /bash_functions.sh ; eval `grep setup_web_port /start.sh`')
def test_bad_input_to_web_port(docker, test_args, expected_error):
function = docker.run('. /bash_functions.sh ; eval `grep setup_web_port /start.sh`')
assert expected_error in function.stdout
@pytest.mark.parametrize('test_args,cache_size', [('-e CUSTOM_CACHE_SIZE="0"', '0'), ('-e CUSTOM_CACHE_SIZE="20000"', '20000')])
def test_overrides_default_CUSTOM_CACHE_SIZE(Docker, Slow, test_args, cache_size):
def test_overrides_default_custom_cache_size(docker, slow, test_args, cache_size):
''' Changes the cache_size setting to increase or decrease the cache size for dnsmasq'''
CONFIG_LINE = r'cache-size\s*=\s*{}'.format(cache_size)
DNSMASQ_CONFIG = '/etc/dnsmasq.d/01-pihole.conf'
function = Docker.run('echo ${CUSTOM_CACHE_SIZE};. ./bash_functions.sh; echo ${CUSTOM_CACHE_SIZE}; eval `grep setup_dnsmasq /start.sh`')
function = docker.run('echo ${CUSTOM_CACHE_SIZE};. ./bash_functions.sh; echo ${CUSTOM_CACHE_SIZE}; eval `grep setup_dnsmasq /start.sh`')
assert "Custom CUSTOM_CACHE_SIZE set to {}".format(cache_size) in function.stdout
Slow(lambda: re.search(CONFIG_LINE, Docker.run('cat {}'.format(DNSMASQ_CONFIG)).stdout) != None)
slow(lambda: re.search(CONFIG_LINE, docker.run(_cat(DNSMASQ_CONFIG_LOC)).stdout) != None)
@pytest.mark.parametrize('test_args', [
'-e CUSTOM_CACHE_SIZE="-1"',
'-e CUSTOM_CACHE_SIZE="1,000"',
])
def test_bad_input_to_CUSTOM_CACHE_SIZE(Docker, Slow, test_args):
def test_bad_input_to_custom_cache_size(docker, slow, test_args):
CONFIG_LINE = r'cache-size\s*=\s*10000'
DNSMASQ_CONFIG = '/etc/dnsmasq.d/01-pihole.conf'
Docker.run('. ./bash_functions.sh; eval `grep setup_dnsmasq /start.sh`')
Slow(lambda: re.search(CONFIG_LINE, Docker.run('cat {}'.format(DNSMASQ_CONFIG)).stdout) != None)
docker.run('. ./bash_functions.sh; eval `grep setup_dnsmasq /start.sh`')
slow(lambda: re.search(CONFIG_LINE, docker.run(_cat(DNSMASQ_CONFIG_LOC)).stdout) != None)
@pytest.mark.parametrize('test_args', [
'-e DNSSEC="true" -e CUSTOM_CACHE_SIZE="0"',
])
def test_dnssec_enabled_with_CUSTOM_CACHE_SIZE(Docker, Slow, test_args):
def test_dnssec_enabled_with_custom_cache_size(docker, slow, test_args):
CONFIG_LINE = r'cache-size\s*=\s*10000'
DNSMASQ_CONFIG = '/etc/dnsmasq.d/01-pihole.conf'
Docker.run('. ./bash_functions.sh; eval `grep setup_dnsmasq /start.sh`')
Slow(lambda: re.search(CONFIG_LINE, Docker.run('cat {}'.format(DNSMASQ_CONFIG)).stdout) != None)
docker.run('. ./bash_functions.sh; eval `grep setup_dnsmasq /start.sh`')
slow(lambda: re.search(CONFIG_LINE, docker.run(_cat(DNSMASQ_CONFIG_LOC)).stdout) != None)
# DNS Environment Variable behavior in combinations of modified pihole LTE settings
@ -91,14 +98,14 @@ def test_dnssec_enabled_with_CUSTOM_CACHE_SIZE(Docker, Slow, test_args):
('-e DNS1="1.2.3.4" -e DNS2="no"', 'custom DNS', '1.2.3.4', None ),
('-e DNS2="no"', 'custom DNS', '8.8.8.8', None ),
])
def test_override_default_servers_with_DNS_EnvVars(Docker, Slow, args_env, expected_stdout, dns1, dns2):
def test_override_default_servers_with_dns_envvars(docker, slow, args_env, expected_stdout, dns1, dns2):
''' on first boot when DNS vars are NOT set explain default google DNS settings are used
or when DNS vars are set override the pihole DNS settings '''
assert Docker.run('test -f /.piholeFirstBoot').rc == 0
function = Docker.run('. /bash_functions.sh ; eval `grep "^setup_dnsmasq " /start.sh`')
assert docker.run('test -f /.piholeFirstBoot').rc == 0
function = docker.run(EVAL_SETUP_DNSMASQ)
assert expected_stdout in function.stdout
expected_servers = 'server={}\n'.format(dns1) if dns2 == None else 'server={}\nserver={}\n'.format(dns1, dns2)
Slow(lambda: expected_servers == Docker.run('grep "^server=[^/]" /etc/dnsmasq.d/01-pihole.conf').stdout)
slow(lambda: expected_servers == docker.run('grep "^server=[^/]" /etc/dnsmasq.d/01-pihole.conf').stdout)
#@pytest.mark.skipif(os.environ.get('CI') == 'true',
@ -115,31 +122,29 @@ def test_override_default_servers_with_DNS_EnvVars(Docker, Slow, args_env, expec
('-e DNS1="1.2.3.4" -e DNS2="2.2.3.4"', '1.2.3.4', '2.2.3.4',
'Docker DNS variables not used\nExisting DNS servers used (1.2.3.4 & 2.2.3.4'),
])
def test_DNS_Envs_are_secondary_to_setupvars(Docker, Slow, args_env, expected_stdout, dns1, dns2):
def test_dns_envs_are_secondary_to_setupvars(docker, slow, args_env, expected_stdout, dns1, dns2):
''' on second boot when DNS vars are set just use pihole DNS settings
or when DNS vars and FORCE_DNS var are set override the pihole DNS settings '''
# Given we are not booting for the first time
assert Docker.run('rm /.piholeFirstBoot').rc == 0
assert docker.run('rm /.piholeFirstBoot').rc == 0
# and a user already has custom pihole dns variables in setup vars
dns_count = 1
setupVars = '/etc/pihole/setupVars.conf'
Docker.run('sed -i "/^PIHOLE_DNS/ d" {}'.format(setupVars))
Docker.run('echo "PIHOLE_DNS_1={}" | tee -a {}'.format(dns1, setupVars))
# and a user already has custom pihole dns variables in setup vars
docker.run('sed -i "/^PIHOLE_DNS/ d" {}'.format(SETUPVARS_LOC))
docker.run('echo "PIHOLE_DNS_1={}" | tee -a {}'.format(dns1, SETUPVARS_LOC))
if dns2:
Docker.run('echo "PIHOLE_DNS_2={}" | tee -a {}'.format(dns2, setupVars))
Docker.run('sync {}'.format(setupVars))
Slow(lambda: 'PIHOLE_DNS' in Docker.run('cat {}'.format(setupVars)).stdout)
docker.run('echo "PIHOLE_DNS_2={}" | tee -a {}'.format(dns2, SETUPVARS_LOC))
docker.run('sync {}'.format(SETUPVARS_LOC))
slow(lambda: 'PIHOLE_DNS' in docker.run(_cat(SETUPVARS_LOC)).stdout)
# When we run setup dnsmasq during startup of the container
function = Docker.run('. /bash_functions.sh ; eval `grep "^setup_dnsmasq " /start.sh`')
function = docker.run(EVAL_SETUP_DNSMASQ)
assert expected_stdout in function.stdout
# Then the servers are still what the user had customized if forced dnsmasq is not set
expected_servers = ['server={}'.format(dns1)]
if dns2:
expected_servers.append('server={}'.format(dns2))
Slow(lambda: Docker.run('grep "^server=[^/]" /etc/dnsmasq.d/01-pihole.conf').stdout.strip().split('\n') == \
slow(lambda: docker.run('grep "^server=[^/]" /etc/dnsmasq.d/01-pihole.conf').stdout.strip().split('\n') == \
expected_servers)
@ -147,17 +152,17 @@ def test_DNS_Envs_are_secondary_to_setupvars(Docker, Slow, args_env, expected_st
('', 'binding to default interface: eth0', 'PIHOLE_INTERFACE=eth0'),
('-e INTERFACE="br0"', 'binding to custom interface: br0', 'PIHOLE_INTERFACE=br0'),
])
def test_DNS_interface_override_defaults(Docker, Slow, args_env, expected_stdout, expected_config_line):
def test_dns_interface_override_defaults(docker, slow, args_env, expected_stdout, expected_config_line):
''' When INTERFACE environment var is passed in, overwrite dnsmasq interface '''
function = Docker.run('. /bash_functions.sh ; eval `grep "^setup_dnsmasq " /start.sh`')
function = docker.run(EVAL_SETUP_DNSMASQ)
assert expected_stdout in function.stdout
Slow(lambda: expected_config_line + '\n' == Docker.run('grep "^PIHOLE_INTERFACE" /etc/pihole/setupVars.conf').stdout)
slow(lambda: expected_config_line + '\n' == docker.run('grep "^PIHOLE_INTERFACE" {}'.format(SETUPVARS_LOC)).stdout)
expected_debian_lines = [
'"VIRTUAL_HOST" => "127.0.0.1"',
'"ServerIP" => "127.0.0.1"',
'"PHP_ERROR_LOG" => "/var/log/lighttpd/error.log"'
'"PHP_ERROR_LOG" => "/var/log/lighttpd/error-pihole.log"'
]
@ -165,49 +170,56 @@ expected_debian_lines = [
(expected_debian_lines, 1),
(expected_debian_lines, 2)
])
def test_debian_setup_php_env(Docker, expected_lines, repeat_function):
def test_debian_setup_php_env(docker, expected_lines, repeat_function):
''' confirm all expected output is there and nothing else '''
stdout = ''
for _ in range(repeat_function):
stdout = Docker.run('. /bash_functions.sh ; eval `grep setup_php_env /start.sh`').stdout
docker.run('. /bash_functions.sh ; eval `grep setup_php_env /start.sh`').stdout
for expected_line in expected_lines:
search_config_cmd = "grep -c '{}' /etc/lighttpd/conf-enabled/15-fastcgi-php.conf".format(expected_line)
search_config_count = Docker.run(search_config_cmd)
search_config_count = docker.run(search_config_cmd)
found_lines = int(search_config_count.stdout.rstrip('\n'))
if found_lines > 1:
assert False, f'Found line {expected_line} times (more than once): {found_lines}'
def test_webPassword_random_generation(Docker):
def test_webpassword_random_generation(docker):
''' When a user sets webPassword env the admin password gets set to that '''
function = Docker.run('. /bash_functions.sh ; eval `grep generate_password /start.sh`')
function = docker.run(EVAL_SETUP_WEB_PASSWORD)
assert 'assigning random password' in function.stdout.lower()
@pytest.mark.parametrize('entrypoint,cmd', [('--entrypoint=tail','-f /dev/null')])
@pytest.mark.parametrize('args_env,secure,setupVarsHash', [
@pytest.mark.parametrize('args_env,secure,setupvars_hash', [
('-e ServerIP=1.2.3.4 -e WEBPASSWORD=login', True, 'WEBPASSWORD=6060d59351e8c2f48140f01b2c3f3b61652f396c53a5300ae239ebfbe7d5ff08'),
('-e ServerIP=1.2.3.4 -e WEBPASSWORD=""', False, ''),
])
def test_webPassword_env_assigns_password_to_file_or_removes_if_empty(Docker, args_env, secure, setupVarsHash):
def test_webpassword_env_assigns_password_to_file_or_removes_if_empty(docker, args_env, secure, setupvars_hash):
''' When a user sets webPassword env the admin password gets set or removed if empty '''
function = Docker.run('. /bash_functions.sh ; eval `grep setup_web_password /start.sh`')
function = docker.run(EVAL_SETUP_WEB_PASSWORD)
if secure:
assert 'new password set' in function.stdout.lower()
assert Docker.run('grep -q \'{}\' {}'.format(setupVarsHash, '/etc/pihole/setupVars.conf')).rc == 0
assert docker.run(_grep(setupvars_hash, SETUPVARS_LOC)).rc == 0
else:
assert 'password removed' in function.stdout.lower()
assert Docker.run('grep -q \'^WEBPASSWORD=$\' /etc/pihole/setupVars.conf').rc == 0
assert docker.run(_grep('^WEBPASSWORD=$', SETUPVARS_LOC)).rc == 0
@pytest.mark.parametrize('entrypoint,cmd', [('--entrypoint=tail','-f /dev/null')])
@pytest.mark.parametrize('test_args', ['-e WEBPASSWORD=login', '-e WEBPASSWORD=""'])
def test_webPassword_pre_existing_trumps_all_envs(Docker, args_env, test_args):
'''When a user setup webPassword in the volume prior to first container boot,
during prior container boot, the prior volume password is left intact / setup skipped'''
Docker.run('. /opt/pihole/webpage.sh ; add_setting WEBPASSWORD volumepass')
function = Docker.run('. /bash_functions.sh ; eval `grep setup_web_password /start.sh`')
def test_env_always_updates_password(docker, args_env, test_args):
'''When a user sets the WEBPASSWORD environment variable, ensure it always sets the password'''
function = docker.run(EVAL_SETUP_WEB_PASSWORD)
assert '::: Pre existing WEBPASSWORD found' in function.stdout
assert Docker.run('grep -q \'{}\' {}'.format('WEBPASSWORD=volumepass', '/etc/pihole/setupVars.conf')).rc == 0
assert '::: Assigning password defined by Environment Variable' in function.stdout
@pytest.mark.parametrize('entrypoint,cmd', [('--entrypoint=tail','-f /dev/null')])
def test_setupvars_trumps_random_password_if_set(docker, args_env, test_args):
'''If a password is already set in setupvars, and no password is set in the environment variable, do not generate a random password'''
docker.run('. /opt/pihole/utils.sh ; addOrEditKeyValPair {} WEBPASSWORD volumepass'.format(SETUPVARS_LOC))
function = docker.run(EVAL_SETUP_WEB_PASSWORD)
assert 'Pre existing WEBPASSWORD found' in function.stdout
assert docker.run(_grep('WEBPASSWORD=volumepass', SETUPVARS_LOC)).rc == 0

View File

@ -8,47 +8,46 @@ def start_cmd():
@pytest.fixture
def RunningPiHole(DockerPersist, Slow, persist_webserver, persist_tag, start_cmd):
''' Override the RunningPiHole to run and check for success of a
def running_pihole(docker_persist, slow, persist_webserver, persist_tag, start_cmd):
''' Override the running_pihole to run and check for success of a
pihole-FTL start based `pihole` script command
Individual tests all must override start_cmd'''
#print DockerPersist.run('ps -ef').stdout
assert DockerPersist.dig.run('ping -c 1 test_pihole').rc == 0
Slow(lambda: DockerPersist.run('pgrep pihole-FTL').rc == 0)
Slow(lambda: DockerPersist.run('pgrep {}'.format(persist_webserver)).rc == 0)
oldpid = DockerPersist.run('pidof pihole-FTL')
cmd = DockerPersist.run('pihole {}'.format(start_cmd))
Slow(lambda: DockerPersist.run('pgrep pihole-FTL').rc == 0)
newpid = DockerPersist.run('pidof pihole-FTL')
assert docker_persist.dig.run('ping -c 1 test_pihole').rc == 0
slow(lambda: docker_persist.run('pgrep pihole-FTL').rc == 0)
slow(lambda: docker_persist.run('pgrep {}'.format(persist_webserver)).rc == 0)
oldpid = docker_persist.run('pidof pihole-FTL')
cmd = docker_persist.run('pihole {}'.format(start_cmd))
slow(lambda: docker_persist.run('pgrep pihole-FTL').rc == 0)
newpid = docker_persist.run('pidof pihole-FTL')
for pid in [oldpid, newpid]:
assert pid != ''
# ensure a new pid for pihole-FTL appeared due to service restart
assert oldpid != newpid
assert cmd.rc == 0
# Save out cmd result to check different stdout of start/enable/disable
DockerPersist.cmd = cmd
return DockerPersist
docker_persist.cmd = cmd
return docker_persist
@pytest.mark.parametrize('start_cmd,hostname,expected_ip, expected_messages', [
('enable', 'pi.hole', '127.0.0.1', ['Blocking already enabled,','nothing to do']),
('disable', 'pi.hole', '127.0.0.1', ['Disabling blocking','Pi-hole Disabled']),
])
def test_pihole_enable_disable_command(RunningPiHole, Dig, persist_tag, start_cmd, hostname, expected_ip, expected_messages):
''' the start_cmd tests are all built into the RunningPiHole fixture in this file '''
def test_pihole_enable_disable_command(running_pihole, dig, persist_tag, start_cmd, hostname, expected_ip, expected_messages):
''' the start_cmd tests are all built into the running_pihole fixture in this file '''
dig_cmd = "dig +time=1 +noall +answer {} @test_pihole".format(hostname)
lookup = RunningPiHole.dig.run(dig_cmd)
lookup = running_pihole.dig.run(dig_cmd)
assert lookup.rc == 0
lookup_ip = lookup.stdout.split()[4]
assert lookup_ip == expected_ip
for part_of_output in expected_messages:
assert part_of_output in RunningPiHole.cmd.stdout
assert part_of_output in running_pihole.cmd.stdout
@pytest.mark.parametrize('start_cmd,expected_message', [
('-up', 'Function not supported in Docker images')
])
def test_pihole_update_command(RunningPiHole, start_cmd, expected_message):
assert RunningPiHole.cmd.stdout.strip() == expected_message
def test_pihole_update_command(running_pihole, start_cmd, expected_message):
assert running_pihole.cmd.stdout.strip() == expected_message

View File

@ -7,9 +7,9 @@ import time
# If the test runs /start.sh, do not let s6 run it too! Kill entrypoint to avoid race condition/duplicated execution
@pytest.mark.parametrize('persist_entrypoint,persist_cmd,persist_args_env', [('--entrypoint=tail','-f /dev/null','')])
def test_ServerIP_missing_is_not_required_anymore(RunningPiHole):
def test_serverip_missing_is_not_required_anymore(running_pihole):
''' When args to docker are empty start.sh exits saying ServerIP is required '''
start = Docker.run('/start.sh')
start = docker.run('/start.sh')
error_msg = "ERROR: To function correctly you must pass an environment variables of 'ServerIP' into the docker container"
assert start.rc == 1
assert error_msg in start.stdout
@ -21,9 +21,9 @@ def test_ServerIP_missing_is_not_required_anymore(RunningPiHole):
('-e ServerIP="1.2.3.4" -e ServerIPv6="1234:1234:1234:ZZZZ"', "Environment variable (1234:1234:1234:ZZZZ) doesn't appear to be a valid IPv6 address",1),
('-e ServerIP="1.2.3.4" -e ServerIPv6="kernel"', "ERROR: You passed in IPv6 with a value of 'kernel'",1),
])
def test_ServerIP_invalid_IPs_triggers_exit_error(Docker, error_msg, expect_rc):
def test_serverip_invalid_ips_triggers_exit_error(docker, error_msg, expect_rc):
''' When args to docker are empty start.sh exits saying ServerIP is required '''
start = Docker.run('/start.sh')
start = docker.run('/start.sh')
assert start.rc == expect_rc
assert 'ERROR' in start.stdout
assert error_msg in start.stdout
@ -33,15 +33,15 @@ def test_ServerIP_invalid_IPs_triggers_exit_error(Docker, error_msg, expect_rc):
('google-public-dns-a.google.com', '8.8.8.8'),
('b.resolvers.Level3.net', '4.2.2.2')
])
def test_dns_responses(RunningPiHole, hostname, expected_ip):
def test_dns_responses(running_pihole, hostname, expected_ip):
dig_cmd = "dig +time=1 +noall +answer {} @test_pihole | awk '{{ print $5 }}'".format(hostname)
lookup = RunningPiHole.dig.run(dig_cmd).stdout.rstrip('\n')
lookup = running_pihole.dig.run(dig_cmd).stdout.rstrip('\n')
assert lookup == expected_ip
def test_indecies_are_present(RunningPiHole):
File = RunningPiHole.get_module('File')
File('/var/www/html/pihole/index.html').exists
File('/var/www/html/pihole/index.js').exists
def test_indecies_are_present(running_pihole):
file = running_pihole.get_module('File')
file('/var/www/html/pihole/index.html').exists
file('/var/www/html/pihole/index.js').exists
def validate_curl(http_rc, expected_http_code, page_contents):
if int(http_rc.rc) != 0 or int(http_rc.stdout) != expected_http_code:
@ -53,10 +53,10 @@ def validate_curl(http_rc, expected_http_code, page_contents):
@pytest.mark.parametrize('addr', [ 'localhost' ] )
@pytest.mark.parametrize('url', [ '/admin/', '/admin/index.php' ] )
def test_admin_requests_load_as_expected(RunningPiHole, version, addr, url):
def test_admin_requests_load_as_expected(running_pihole, version, addr, url):
command = 'curl -L -s -o /tmp/curled_file -w "%{{http_code}}" http://{}{}'.format(addr, url)
http_rc = RunningPiHole.run(command)
page_contents = RunningPiHole.run('cat /tmp/curled_file ').stdout
http_rc = running_pihole.run(command)
page_contents = running_pihole.run('cat /tmp/curled_file ').stdout
expected_http_code = 200
validate_curl(http_rc, expected_http_code, page_contents)