diff --git a/test/conftest.py b/test/conftest.py index 1121e96..e7133e7 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 +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__)) @@ -419,34 +416,72 @@ 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 # ############################################################################### -@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 - A custom docker compose file name can be defined in a variable named `docker_compose_file`. +@pytest.fixture(scope="module") +def docker_composer(): + with DockerComposer() as d: + yield d + + +@pytest.fixture +def ca_root_certificate(): + return CA_ROOT_CERTIFICATE + + +@pytest.fixture +def monkey_patched_dns(): + original_dns_resolver = monkey_patch_urllib_dns_resolver() + yield + restore_urllib_dns_resolver(original_dns_resolver) + + +@pytest.fixture +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` + 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) - 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) - restore_urllib_dns_resolver(original_dns_resolver) @pytest.fixture() 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' )