|
1 | | -import os |
2 | 1 | import pytest |
3 | 2 | import subprocess |
4 | 3 | import testinfra |
5 | | - |
6 | | -local_host = testinfra.get_host("local://") |
7 | | -check_output = local_host.check_output |
8 | | - |
9 | | -TAIL_DEV_NULL = "tail -f /dev/null" |
| 4 | +import testinfra.backend.docker |
| 5 | +import os |
10 | 6 |
|
11 | 7 |
|
12 | | -@pytest.fixture() |
13 | | -def run_and_stream_command_output(): |
14 | | - def run_and_stream_command_output_inner(command, verbose=False): |
15 | | - print("Running", command) |
16 | | - build_env = os.environ.copy() |
17 | | - build_env["PIHOLE_DOCKER_TAG"] = version |
18 | | - build_result = subprocess.Popen( |
19 | | - command.split(), |
20 | | - env=build_env, |
21 | | - stdout=subprocess.PIPE, |
22 | | - stderr=subprocess.STDOUT, |
23 | | - bufsize=1, |
24 | | - universal_newlines=True, |
| 8 | +# Monkeypatch sh to bash, if they ever support non hard code /bin/sh this can go away |
| 9 | +# https://github.com/pytest-dev/pytest-testinfra/blob/master/testinfra/backend/docker.py |
| 10 | +def run_bash(self, command, *args, **kwargs): |
| 11 | + cmd = self.get_command(command, *args) |
| 12 | + if self.user is not None: |
| 13 | + out = self.run_local( |
| 14 | + "docker exec -u %s %s /bin/bash -c %s", self.user, self.name, cmd |
25 | 15 | ) |
26 | | - if verbose: |
27 | | - while build_result.poll() is None: |
28 | | - for line in build_result.stdout: |
29 | | - print(line, end="") |
30 | | - build_result.wait() |
31 | | - if build_result.returncode != 0: |
32 | | - print(f" [i] Error running: {command}") |
33 | | - print(build_result.stderr) |
34 | | - |
35 | | - return run_and_stream_command_output_inner |
36 | | - |
37 | | - |
38 | | -@pytest.fixture() |
39 | | -def args_env(): |
40 | | - return '-e TZ="Europe/London" -e FTLCONF_dns_upstreams="8.8.8.8"' |
41 | | - |
42 | | - |
43 | | -@pytest.fixture() |
44 | | -def args(args_env): |
45 | | - return "{}".format(args_env) |
46 | | - |
47 | | - |
48 | | -@pytest.fixture() |
49 | | -def test_args(): |
50 | | - """test override fixture to provide arguments separate from our core args""" |
51 | | - return "" |
52 | | - |
53 | | - |
54 | | -def docker_generic(request, _test_args, _args, _image, _cmd, _entrypoint): |
55 | | - # assert 'docker' in check_output('id'), "Are you in the docker group?" |
56 | | - # Always appended PYTEST arg to tell pihole we're testing |
57 | | - if "pihole" in _image and "PYTEST=1" not in _args: |
58 | | - _args = "{} -e PYTEST=1".format(_args) |
59 | | - docker_run = "docker run -d -t {args} {test_args} {entry} {image} {cmd}".format( |
60 | | - args=_args, test_args=_test_args, entry=_entrypoint, image=_image, cmd=_cmd |
61 | | - ) |
62 | | - # Print a human runable version of the container run command for faster debugging |
63 | | - print(docker_run.replace("-d -t", "--rm -it").replace(TAIL_DEV_NULL, "bash")) |
64 | | - docker_id = check_output(docker_run) |
65 | | - |
66 | | - def teardown(): |
67 | | - check_output("docker logs {}".format(docker_id)) |
68 | | - check_output("docker rm -f {}".format(docker_id)) |
69 | | - |
70 | | - request.addfinalizer(teardown) |
71 | | - docker_container = testinfra.backend.get_backend( |
72 | | - "docker://" + docker_id, sudo=False |
73 | | - ) |
74 | | - docker_container.id = docker_id |
75 | | - |
76 | | - return docker_container |
77 | | - |
78 | | - |
79 | | -@pytest.fixture |
80 | | -def docker(request, test_args, args, image, cmd, entrypoint): |
81 | | - """One-off Docker container run""" |
82 | | - return docker_generic(request, test_args, args, image, cmd, entrypoint) |
83 | | - |
84 | | - |
85 | | -@pytest.fixture |
86 | | -def entrypoint(): |
87 | | - return "" |
| 16 | + else: |
| 17 | + out = self.run_local("docker exec %s /bin/bash -c %s", self.name, cmd) |
| 18 | + out.command = self.encode(cmd) |
| 19 | + return out |
88 | 20 |
|
89 | 21 |
|
90 | | -@pytest.fixture() |
91 | | -def version(): |
92 | | - return os.environ.get("GIT_TAG", None) |
| 22 | +testinfra.backend.docker.DockerBackend.run = run_bash |
93 | 23 |
|
94 | 24 |
|
95 | | -@pytest.fixture() |
96 | | -def tag(version): |
97 | | - return "{}".format(version) |
| 25 | +# scope='session' uses the same container for all the tests; |
| 26 | +# scope='function' uses a new container per test function. |
| 27 | +@pytest.fixture(scope="function") |
| 28 | +def docker(request): |
| 29 | + # Get platform from environment variable, default to None if not set |
| 30 | + platform = os.environ.get("CIPLATFORM") |
98 | 31 |
|
| 32 | + # build the docker run command with args |
| 33 | + cmd = ["docker", "run", "-d", "-t"] |
99 | 34 |
|
100 | | -@pytest.fixture() |
101 | | -def image(tag): |
102 | | - image = "pihole" |
103 | | - return "{}:{}".format(image, tag) |
| 35 | + # Only add platform flag if CIPLATFORM is set |
| 36 | + if platform: |
| 37 | + cmd.extend(["--platform", platform]) |
104 | 38 |
|
| 39 | + # Get env vars from parameterization |
| 40 | + env_vars = getattr(request, "param", []) |
| 41 | + if isinstance(env_vars, str): |
| 42 | + env_vars = [env_vars] |
105 | 43 |
|
106 | | -@pytest.fixture() |
107 | | -def cmd(): |
108 | | - return TAIL_DEV_NULL |
| 44 | + # add parameterized environment variables |
| 45 | + for env_var in env_vars: |
| 46 | + cmd.extend(["-e", env_var]) |
109 | 47 |
|
| 48 | + # ensure PYTEST=1 is set |
| 49 | + if not any("PYTEST=1" in arg for arg in cmd): |
| 50 | + cmd.extend(["-e", "PYTEST=1"]) |
110 | 51 |
|
111 | | -@pytest.fixture |
112 | | -def slow(): |
113 | | - """ |
114 | | - Run a slow check, check if the state is correct for `timeout` seconds. |
115 | | - """ |
116 | | - import time |
| 52 | + # add default TZ if not already set |
| 53 | + if not any("TZ=" in arg for arg in cmd): |
| 54 | + cmd.extend(["-e", 'TZ="Europe/London"']) |
117 | 55 |
|
118 | | - def _slow(check, timeout=20): |
119 | | - timeout_at = time.time() + timeout |
120 | | - while True: |
121 | | - try: |
122 | | - assert check() |
123 | | - except AssertionError as e: |
124 | | - if time.time() < timeout_at: |
125 | | - time.sleep(1) |
126 | | - else: |
127 | | - raise e |
128 | | - else: |
129 | | - return |
| 56 | + # add the image name |
| 57 | + cmd.append("pihole:CI_container") |
130 | 58 |
|
131 | | - return _slow |
| 59 | + # run a container |
| 60 | + docker_id = subprocess.check_output(cmd).decode().strip() |
| 61 | + # return a testinfra connection to the container |
| 62 | + yield testinfra.get_host("docker://" + docker_id) |
| 63 | + # at the end of the test suite, destroy the container |
| 64 | + subprocess.check_call(["docker", "rm", "-f", docker_id]) |
0 commit comments