Refactor testinfra docker to support parametrize

This accomplishes the same thing as the previous tests, without using testinfra docker's boilerplate example, which had some drawbacks: Adding @parametrize to the previous version didn't allow overriding the pytest.mark docker arg, image:tag, or cmd with a parametrized test containing different expected results associated with either the alpine/debian tag.  Isntead it'd run all the parameters against both tags...e.g. nginx and lighttpd test would run agains both alpine and docker when parametizing on the test level.
This commit is contained in:
diginc
2016-08-02 22:29:52 -05:00
parent e801bfb5ba
commit d40ee18689
3 changed files with 70 additions and 48 deletions
+43 -41
View File
@@ -1,53 +1,55 @@
import pytest
import testinfra
# Use testinfra to get a handy function to run commands locally
check_output = testinfra.get_backend(
"local://"
).get_module("Command").check_output
@pytest.fixture()
def args(request):
return '-e ServerIP="192.168.100.2"'
@pytest.fixture(params=['alpine', 'debian'])
def tag(request):
return request.param
@pytest.fixture
def TestinfraBackend(request):
docker_run = "docker run -d {}".format(request.param)
print docker_run
@pytest.fixture()
def image(request, tag):
return 'diginc/pi-hole:{}'.format(tag)
docker_id = check_output(docker_run)
check_output("docker exec %s sed -i 's/^gravity_spinup/#donotcurl/g' /usr/local/bin/gravity.sh", docker_id)
@pytest.fixture()
def cmd(request):
return '/start.sh'
DEBUG = []
@pytest.fixture()
def Docker(request, LocalCommand, args, image, cmd):
assert 'docker' in LocalCommand.check_output('id'), "Are you in the docker group?"
docker_run = "docker run -d {} {} {}".format(args, image, cmd)
if 'run' in DEBUG:
assert docker_run == 'docker run -d -e ServerIP="192.168.100.2" diginc/pi-hole:alpine /start.sh'
docker_id = LocalCommand.check_output(docker_run)
LocalCommand.check_output("docker exec %s sed -i 's/^gravity_spinup/#donotcurl/g' /usr/local/bin/gravity.sh", docker_id)
def teardown():
check_output("docker rm -f %s", docker_id)
LocalCommand.check_output("docker rm -f %s", docker_id)
request.addfinalizer(teardown)
return testinfra.get_backend("docker://" + docker_id)
def pytest_generate_tests(metafunc):
if "TestinfraBackend" in metafunc.fixturenames:
mark_args = getattr(metafunc.function, "docker_args", None)
docker_args = []
if mark_args is not None:
docker_args = docker_args + list(mark_args.args)
mark_images = getattr(metafunc.function, "docker_images", None)
images = ['diginc/pi-hole:alpine', 'diginc/pi-hole:debian']
if mark_images is not None:
images = mark_images.args
mark_cmd = getattr(metafunc.function, "docker_cmd", None)
command = 'tail -f /dev/null'
if mark_cmd is not None:
command = " ".join(mark_cmd.args)
docker_run_args = []
for img in images:
docker_run_args.append('{} {} {}'.format(" ".join(docker_args),
img, command))
if getattr(metafunc.function, "persistent", None) is not None:
scope = "session"
else:
scope = "function"
metafunc.parametrize(
"TestinfraBackend", docker_run_args, indirect=True, scope=scope)
@pytest.fixture
def Slow():
"""
Run a slow check, check if the state is correct for `timeout` seconds.
"""
import time
def slow(check, timeout=30):
timeout_at = time.time() + timeout
while True:
try:
assert check()
except AssertionError, e:
if timeout_at < time.time():
time.sleep(1)
else:
raise e
else:
return
return slow
+26 -6
View File
@@ -1,12 +1,32 @@
import pytest
''' conftest.py provides the defaults through fixtures '''
''' Note, testinfra builtins don't seem fully compatible with
docker containers (esp. alpine) stripped down nature '''
def test_ServerIP_missing_env_triggers_error(Command):
start = Command.run('/start.sh')
def test_pihole_default_run_command(Docker):
expected_proc = '/sbin/tini -- /start.sh'
pgrep = 'pgrep -f "{}" | wc -l || echo 0'.format(expected_proc)
find_proc = Docker.run(pgrep).stdout
if int(find_proc) < 1:
print Docker.run('ps -ef')
print "{} : {}".format(pgrep, find_proc)
assert False, '{}: Couldn\'t find proc {}'.format(tag, expected_proc)
@pytest.mark.parametrize('args', [ '' ])
@pytest.mark.parametrize('cmd', [ 'tail -f /dev/null' ])
def test_ServerIP_missing_triggers_start_error(Docker):
''' When args to docker are empty start.sh exits saying ServerIP is required '''
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
@pytest.mark.docker_args('-e ServerIP="192.168.1.2"')
@pytest.mark.docker_cmd('/start.sh')
def test_ServerIP_allows_normal_startup(Command):
assert Command.run('pgrep -f /start.sh | wc') != 0
@pytest.mark.parametrize('tag,webserver', [
( 'alpine', 'nginx' ),
( 'debian', 'lighttpd' )
])
def test_start_launches_dns_and_a_webserver(Docker, webserver, Slow):
''' after we wait for start to finish '''
import time
Socket = Docker.get_module("Socket")
Slow(lambda: Docker.run( 'ps -ef | grep -q "{}"'.format(webserver) ).rc == 0)