From 01745a836fc6d5687c26efff26c5713b8e4b5d2f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 02:44:12 -0500 Subject: [PATCH 1/5] tests: Fix path to `ca-root.crt` `os.getcwd()` is not guaranteed to always return the `test/` directory. --- test/conftest.py | 6 ++++++ test/test_ssl/test_dhparam.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 1121e96..128f57d 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -425,6 +425,12 @@ def connect_to_all_networks(): # ############################################################################### + +@pytest.fixture +def ca_root_certificate(): + return CA_ROOT_CERTIFICATE + + @pytest.fixture(scope="module") def docker_compose(request): """ diff --git a/test/test_ssl/test_dhparam.py b/test/test_ssl/test_dhparam.py index 2f69ad3..d4d64a3 100644 --- a/test/test_ssl/test_dhparam.py +++ b/test/test_ssl/test_dhparam.py @@ -1,6 +1,5 @@ import re import subprocess -import os import backoff import docker @@ -219,7 +218,7 @@ def test_custom_dhparam_is_supported(docker_compose): # Only `web2` has a site-specific DH param file (which overrides all other DH config) # Other tests here use `web5` explicitly, or implicitly (via ENV `DEFAULT_HOST`, otherwise first HTTPS server) -def test_custom_dhparam_is_supported_per_site(docker_compose): +def test_custom_dhparam_is_supported_per_site(docker_compose, ca_root_certificate): container_name="dh-file" sut_container = docker_client.containers.get(container_name) assert sut_container.status == "running" @@ -242,7 +241,7 @@ def test_custom_dhparam_is_supported_per_site(docker_compose): # - `web2` has it's own cert provisioned at `/etc/nginx/certs/web2.nginx-proxy.tld.crt`. can_verify_chain_of_trust( sut_container, - ca_cert = f"{os.getcwd()}/certs/ca-root.crt", + ca_cert = ca_root_certificate, fqdn = 'web2.nginx-proxy.tld' ) From 09a2f40633a9c77179b3e26523fda04aab86c9ea Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 6 Jan 2023 01:25:35 -0500 Subject: [PATCH 2/5] tests: Turn helper function into `docker_compose_file` fixture This makes it easier for tests to override the filename. --- test/conftest.py | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 128f57d..3cffe9b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -322,31 +322,28 @@ def wait_for_nginxproxy_to_be_ready(): logging.debug("nginx-proxy ready") break -def find_docker_compose_file(request): - """ - helper for fixture functions to figure out the name of the docker-compose file to consider. - - if the test module provides a `docker_compose_file` variable, take that - - else, if a yaml file exists with the same name as the test module (but for the `.yml` extension), use that - - otherwise use `docker-compose.yml`. +@pytest.fixture(scope="module") +def docker_compose_file(request): + """Fixture naming the docker-compose file to consider. + + If a YAML file exists with the same name as the test module (with the `.py` extension replaced + with `.yml` or `.yaml`), use that. Otherwise, use `docker-compose.yml` in the same directory + as the test module. + + Tests can override this fixture to specify a custom location. """ test_module_dir = os.path.dirname(request.module.__file__) yml_file = os.path.join(test_module_dir, request.module.__name__ + '.yml') yaml_file = os.path.join(test_module_dir, request.module.__name__ + '.yaml') default_file = os.path.join(test_module_dir, 'docker-compose.yml') - docker_compose_file_module_variable = getattr(request.module, "docker_compose_file", None) - if docker_compose_file_module_variable is not None: - docker_compose_file = os.path.join( test_module_dir, docker_compose_file_module_variable) - if not os.path.isfile(docker_compose_file): - raise ValueError(f"docker compose file {docker_compose_file!r} could not be found. Check your test module `docker_compose_file` variable value.") + if os.path.isfile(yml_file): + docker_compose_file = yml_file + elif os.path.isfile(yaml_file): + docker_compose_file = yaml_file else: - if os.path.isfile(yml_file): - docker_compose_file = yml_file - elif os.path.isfile(yaml_file): - docker_compose_file = yaml_file - else: - docker_compose_file = default_file + docker_compose_file = default_file if not os.path.isfile(docker_compose_file): logging.error("Could not find any docker-compose file named either '{0}.yml', '{0}.yaml' or 'docker-compose.yml'".format(request.module.__name__)) @@ -432,16 +429,15 @@ def ca_root_certificate(): @pytest.fixture(scope="module") -def docker_compose(request): - """ - pytest fixture providing containers described in a docker compose file. After the tests, remove the created containers +def docker_compose(docker_compose_file): + """Ensures containers described in a docker compose file are started. - A custom docker compose file name can be defined in a variable named `docker_compose_file`. + A custom docker compose file name can be specified by overriding the `docker_compose_file` + fixture. Also, in the case where pytest is running from a docker container, this fixture makes sure our container will be attached to all the docker networks. """ - docker_compose_file = find_docker_compose_file(request) original_dns_resolver = monkey_patch_urllib_dns_resolver() remove_all_containers() docker_compose_up(docker_compose_file) From f5a3492926985b45a6e1f2e5d477d3fc007fc012 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 19:28:30 -0500 Subject: [PATCH 3/5] tests: Factor out DNS monkey patching to its own fixture --- test/conftest.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 3cffe9b..8e80bf4 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -429,7 +429,14 @@ def ca_root_certificate(): @pytest.fixture(scope="module") -def docker_compose(docker_compose_file): +def monkey_patched_dns(): + original_dns_resolver = monkey_patch_urllib_dns_resolver() + yield + restore_urllib_dns_resolver(original_dns_resolver) + + +@pytest.fixture(scope="module") +def docker_compose(monkey_patched_dns, docker_compose_file): """Ensures containers described in a docker compose file are started. A custom docker compose file name can be specified by overriding the `docker_compose_file` @@ -438,7 +445,6 @@ def docker_compose(docker_compose_file): Also, in the case where pytest is running from a docker container, this fixture makes sure our container will be attached to all the docker networks. """ - original_dns_resolver = monkey_patch_urllib_dns_resolver() remove_all_containers() docker_compose_up(docker_compose_file) networks = connect_to_all_networks() @@ -448,7 +454,6 @@ def docker_compose(docker_compose_file): for network in networks: disconnect_from_network(network) docker_compose_down(docker_compose_file) - restore_urllib_dns_resolver(original_dns_resolver) @pytest.fixture() From 4d8f878ba7e25c25f0f781bf2f89291b2a2e599e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 19:38:55 -0500 Subject: [PATCH 4/5] tests: Fixture that simplifies Docker compose file changes --- test/conftest.py | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 8e80bf4..13ff689 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -416,6 +416,35 @@ def connect_to_all_networks(): return [connect_to_network(network) for network in networks] +class DockerComposer(contextlib.AbstractContextManager): + def __init__(self): + self._docker_compose_file = None + + def __exit__(self, *exc_info): + self._down() + + def _down(self): + if self._docker_compose_file is None: + return + for network in self._networks: + disconnect_from_network(network) + docker_compose_down(self._docker_compose_file) + self._docker_compose_file = None + + def compose(self, docker_compose_file): + if docker_compose_file == self._docker_compose_file: + return + self._down() + if docker_compose_file is None: + return + remove_all_containers() + docker_compose_up(docker_compose_file) + self._networks = connect_to_all_networks() + wait_for_nginxproxy_to_be_ready() + time.sleep(3) # give time to containers to be ready + self._docker_compose_file = docker_compose_file + + ############################################################################### # # Py.test fixtures @@ -423,6 +452,12 @@ def connect_to_all_networks(): ############################################################################### +@pytest.fixture(scope="module") +def docker_composer(): + with DockerComposer() as d: + yield d + + @pytest.fixture def ca_root_certificate(): return CA_ROOT_CERTIFICATE @@ -436,7 +471,7 @@ def monkey_patched_dns(): @pytest.fixture(scope="module") -def docker_compose(monkey_patched_dns, docker_compose_file): +def docker_compose(monkey_patched_dns, docker_composer, docker_compose_file): """Ensures containers described in a docker compose file are started. A custom docker compose file name can be specified by overriding the `docker_compose_file` @@ -445,15 +480,8 @@ def docker_compose(monkey_patched_dns, docker_compose_file): Also, in the case where pytest is running from a docker container, this fixture makes sure our container will be attached to all the docker networks. """ - remove_all_containers() - docker_compose_up(docker_compose_file) - networks = connect_to_all_networks() - wait_for_nginxproxy_to_be_ready() - time.sleep(3) # give time to containers to be ready + docker_composer.compose(docker_compose_file) yield docker_client - for network in networks: - disconnect_from_network(network) - docker_compose_down(docker_compose_file) @pytest.fixture() From b5a54ac2191041dfa06c151fea6f5a832862c0cb Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 5 Feb 2023 19:56:14 -0500 Subject: [PATCH 5/5] tests: Reduce scope of `docker_compose` fixture (and friends) This makes it possible to bring up different compose files for different tests in the same test module. This change does not negatively affect performance because the fixture is a no-op if the docker compose filename is unchanged between tests. --- test/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 13ff689..e7133e7 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -323,7 +323,7 @@ def wait_for_nginxproxy_to_be_ready(): break -@pytest.fixture(scope="module") +@pytest.fixture def docker_compose_file(request): """Fixture naming the docker-compose file to consider. @@ -463,14 +463,14 @@ def ca_root_certificate(): return CA_ROOT_CERTIFICATE -@pytest.fixture(scope="module") +@pytest.fixture def monkey_patched_dns(): original_dns_resolver = monkey_patch_urllib_dns_resolver() yield restore_urllib_dns_resolver(original_dns_resolver) -@pytest.fixture(scope="module") +@pytest.fixture def docker_compose(monkey_patched_dns, docker_composer, docker_compose_file): """Ensures containers described in a docker compose file are started.