mirror of
https://github.com/thib8956/nginx-proxy
synced 2025-08-23 16:01:57 +00:00
Merge pull request #2591 from SchoNie/tests-contextmanager-fixes
tests: DockerComposer contextmanager improvements
This commit is contained in:
@@ -321,11 +321,11 @@ def __prepare_and_execute_compose_cmd(compose_files: List[str], project_name: st
|
|||||||
compose_cmd.write(f" --file {compose_file}")
|
compose_cmd.write(f" --file {compose_file}")
|
||||||
compose_cmd.write(f" {cmd}")
|
compose_cmd.write(f" {cmd}")
|
||||||
|
|
||||||
logging.info(compose_cmd.getvalue())
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(shlex.split(compose_cmd.getvalue()), stderr=subprocess.STDOUT)
|
subprocess.check_output(shlex.split(compose_cmd.getvalue()), stderr=subprocess.STDOUT)
|
||||||
|
logging.info(f"Executed '{compose_cmd.getvalue()}'")
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
pytest.fail(f"Error while running '{compose_cmd.getvalue()}':\n{e.output}", pytrace=False)
|
logging.error(f"Error while running '{compose_cmd.getvalue()}'")
|
||||||
|
|
||||||
|
|
||||||
def docker_compose_up(compose_files: List[str], project_name: str):
|
def docker_compose_up(compose_files: List[str], project_name: str):
|
||||||
@@ -428,9 +428,15 @@ def connect_to_network(network: Network) -> Optional[Network]:
|
|||||||
# Make sure our container is connected to the nginx-proxy's network,
|
# Make sure our container is connected to the nginx-proxy's network,
|
||||||
# but avoid connecting to `none` network (not valid) with `test_server-down` tests
|
# but avoid connecting to `none` network (not valid) with `test_server-down` tests
|
||||||
if network.name not in my_networks and network.name != 'none':
|
if network.name not in my_networks and network.name != 'none':
|
||||||
logging.info(f"Connecting to docker network: {network.name}")
|
try:
|
||||||
network.connect(my_container)
|
logging.info(f"Connecting to docker network: {network.name}")
|
||||||
return network
|
network.connect(my_container)
|
||||||
|
return network
|
||||||
|
except docker.errors.APIError as e:
|
||||||
|
logging.warning(f"Failed to connect to network {network.name}: {e}")
|
||||||
|
return network # Ensure the network is still tracked for later removal
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def disconnect_from_network(network: Network = None):
|
def disconnect_from_network(network: Network = None):
|
||||||
@@ -471,34 +477,62 @@ def connect_to_all_networks() -> List[Network]:
|
|||||||
|
|
||||||
class DockerComposer(contextlib.AbstractContextManager):
|
class DockerComposer(contextlib.AbstractContextManager):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
logging.debug("DockerComposer __init__")
|
||||||
self._networks = None
|
self._networks = None
|
||||||
self._docker_compose_files = None
|
self._docker_compose_files = None
|
||||||
self._project_name = None
|
self._project_name = None
|
||||||
|
|
||||||
def __exit__(self, *exc_info):
|
def __exit__(self, *exc_info):
|
||||||
|
logging.debug("DockerComposer __exit__")
|
||||||
self._down()
|
self._down()
|
||||||
|
|
||||||
def _down(self):
|
def _down(self):
|
||||||
|
logging.debug(f"DockerComposer _down {self._docker_compose_files} {self._project_name} {self._networks}")
|
||||||
if self._docker_compose_files is None:
|
if self._docker_compose_files is None:
|
||||||
|
logging.debug("docker_compose_files is None, nothing to cleanup")
|
||||||
return
|
return
|
||||||
for network in self._networks:
|
if self._networks:
|
||||||
disconnect_from_network(network)
|
for network in self._networks:
|
||||||
|
disconnect_from_network(network)
|
||||||
docker_compose_down(self._docker_compose_files, self._project_name)
|
docker_compose_down(self._docker_compose_files, self._project_name)
|
||||||
self._docker_compose_file = None
|
self._docker_compose_files = None
|
||||||
self._project_name = None
|
self._project_name = None
|
||||||
|
self._networks = []
|
||||||
|
|
||||||
def compose(self, docker_compose_files: List[str], project_name: str):
|
def compose(self, docker_compose_files: List[str], project_name: str):
|
||||||
if docker_compose_files == self._docker_compose_files and project_name == self._project_name:
|
if docker_compose_files == self._docker_compose_files and project_name == self._project_name:
|
||||||
|
logging.info(f"Skipping compose: {docker_compose_files} (already running under project {project_name})")
|
||||||
|
return
|
||||||
|
if docker_compose_files is None or project_name is None:
|
||||||
|
logging.info(f"Skipping compose: no compose file specified")
|
||||||
return
|
return
|
||||||
self._down()
|
self._down()
|
||||||
if docker_compose_files is None or project_name is None:
|
|
||||||
return
|
|
||||||
docker_compose_up(docker_compose_files, project_name)
|
|
||||||
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_files = docker_compose_files
|
self._docker_compose_files = docker_compose_files
|
||||||
self._project_name = project_name
|
self._project_name = project_name
|
||||||
|
logging.debug(f"DockerComposer compose {self._docker_compose_files} {self._project_name} {self._networks}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
docker_compose_up(docker_compose_files, project_name)
|
||||||
|
self._networks = connect_to_all_networks()
|
||||||
|
wait_for_nginxproxy_to_be_ready()
|
||||||
|
time.sleep(3) # give time to containers to be ready
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logging.warning("KeyboardInterrupt detected! Force cleanup...")
|
||||||
|
self._down() # Ensure proper shutdown
|
||||||
|
raise # Re-raise to allow pytest to exit cleanly
|
||||||
|
|
||||||
|
except docker.errors.APIError as e:
|
||||||
|
logging.error(f"Docker API error ({e.status_code}): {e.explanation}")
|
||||||
|
logging.debug(f"Full error message: {str(e)}")
|
||||||
|
self._down() # Ensure proper cleanup even on failure
|
||||||
|
pytest.fail(f"Docker Compose setup failed due to Docker API error: {e.explanation}")
|
||||||
|
|
||||||
|
except RuntimeError as e:
|
||||||
|
logging.error(f"RuntimeEror encountered in: {project_name}")
|
||||||
|
logging.debug(f"Full error message: {str(e)}")
|
||||||
|
self._down() # Ensure proper cleanup even on failure
|
||||||
|
pytest.fail(f"Docker Compose setup failed due to RuntimeError in: {project_name}")
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@@ -27,4 +27,4 @@ services:
|
|||||||
- "83"
|
- "83"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "83"
|
WEB_PORTS: "83"
|
||||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$$
|
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$$ # we need to double the `$` because of docker compose variable interpolation
|
||||||
|
@@ -27,4 +27,4 @@ services:
|
|||||||
- "83"
|
- "83"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "83"
|
WEB_PORTS: "83"
|
||||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$$
|
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$$ # we need to double the `$` because of docker compose variable interpolation
|
||||||
|
@@ -1,5 +1,15 @@
|
|||||||
# Note: on Docker Desktop, host networking must be manually enabled.
|
# Note: on Docker Desktop, host networking must be manually enabled.
|
||||||
# See https://docs.docker.com/engine/network/drivers/host/
|
# See https://docs.docker.com/engine/network/drivers/host/
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "1"
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.skipif(
|
||||||
|
PYTEST_RUNNING_IN_CONTAINER,
|
||||||
|
reason="Connecting to host network not supported when pytest is running in container"
|
||||||
|
)
|
||||||
|
|
||||||
def test_forwards_to_host_network_container_1(docker_compose, nginxproxy):
|
def test_forwards_to_host_network_container_1(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://host-network-1.nginx-proxy.tld:8888/port")
|
r = nginxproxy.get("http://host-network-1.nginx-proxy.tld:8888/port")
|
||||||
|
@@ -5,4 +5,4 @@ services:
|
|||||||
- "80"
|
- "80"
|
||||||
environment:
|
environment:
|
||||||
WEB_PORTS: "80"
|
WEB_PORTS: "80"
|
||||||
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$$
|
VIRTUAL_HOST: ~^regex.*\.nginx-proxy\.example$$ # we need to double the `$` because of docker compose variable interpolation
|
||||||
|
Reference in New Issue
Block a user