Jc2k/pytest-docker-tools

Container is not running with host network

nandamller opened this issue · 2 comments

Hi, I am trying to use your library. However, I can't get it to work with containers on network = 'host'

Here is my minimalist test.py:

import pytest
import os

from http.client import HTTPConnection
from pytest_docker_tools import build, container

app_image = build(path=os.path.join(os.path.dirname(__file__), 'app'),)
app = container(image='{app_image.id}', network='host')

def test_app(app):
    assert app.status == 'running'

@pytest.fixture
def app_connection(app):
    return HTTPConnection('127.0.0.1:8080')

def test_app_connection(app_connection):
    app_connection.request('GET', '/')
    response = app_connection.getresponse()
    
    assert response.status == 200

My minimalist app.py:

import logging
from flask import Flask

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

app = Flask(__name__)

@app.route("/", methods=['GET'])
def main():
    logger.info(f"\nIt's here!\n")

    return 'ok'

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080, debug=True)

My Dockerfile:

FROM python:3.8.12-bullseye

COPY . ./python

WORKDIR python

RUN pip install -r requirements.txt

CMD ["python3", "app.py" ]

The requirements.txt
Flask==2.0.2

Versions:
Python 3.9.7, pytest-7.1.2, pluggy-1.0.0
plugins: docker-tools-3.1.3, docker-0.12.0

And this is the error I'm getting:

___________________________________________________ ERROR at setup of test_app ____________________________________________________
request = <SubRequest 'app' for <Function test_app>>, docker_client = <docker.client.DockerClient object at 0x7ff643dbd2b0>
wrapper_class = <class 'pytest_docker_tools.wrappers.container.Container'>
kwargs = {'detach': True, 'image': 'sha256:dc01bb5db190457b853253c7e117799f4190a133e4f30ba3136398ca377e2540', 'labels': {'creat...pytest-docker-tools.signature': 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96'}, 'network': 'host'}
signature = 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96', timeout = 30
container = <pytest_docker_tools.wrappers.container.Container object at 0x7ff643dbd850>

    @fixture_factory()
    def container(request, docker_client, wrapper_class, **kwargs):
        """ Docker container: image={image} """
    
        wrapper_class = wrapper_class or Container
    
        kwargs.update({"detach": True})
        set_reusable_labels(kwargs, request)
    
        signature = hash_params(kwargs)
        set_signature(kwargs, signature)
    
        if request.config.option.reuse_containers:
            if "name" not in kwargs.keys():
                pytest.fail(
                    "Tried to use '--reuse-containers' command line argument without "
                    "setting 'name' attribute on container"
                )
    
            name = kwargs["name"]
    
            try:
                current = docker_client.containers.get(name)
            except NotFound:
                pass
            else:
                # Found a container with the right name, but it doesn't have pytest-docker-tools labels
                # We shouldn't just clobber it, its not ours. Bail out.
                if not is_reusable_container(current):
                    pytest.fail(
                        f"Tried to reuse {name} but it does not appear to be a reusable container"
                    )
    
                # It's ours, and its not stale. Reuse it!
                if check_signature(current.labels, signature):
                    return wrapper_class(current)
    
                # It's ours and it is stale. Clobber it.
                print(f"Removing stale reusable container: {name}")
                current.remove(force=True)
    
        timeout = kwargs.pop("timeout", 30)
    
        raw_container = docker_client.containers.run(**kwargs)
        if not request.config.option.reuse_containers:
            request.addfinalizer(
                lambda: raw_container.remove(force=True) and raw_container.wait(timeout=10)
            )
    
        container = wrapper_class(raw_container)
    
        try:
>           wait_for_callable("Waiting for container to be ready", container.ready, timeout)

../.local/lib/python3.9/site-packages/pytest_docker_tools/factories/container.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

message = 'Waiting for container to be ready'
func = <bound method Container.ready of <pytest_docker_tools.wrappers.container.Container object at 0x7ff643dbd850>>, timeout = 30

    def wait_for_callable(message: str, func: Callable, timeout: int = 30) -> None:
        """
        Runs a callable once a second until it returns True or we hit the timeout.
        """
        sys.stdout.write(message)
        try:
            for i in range(timeout):
                sys.stdout.write(".")
                sys.stdout.flush()
    
                if func():
                    return
    
                time.sleep(1)
        finally:
            sys.stdout.write("\n")
    
>       raise TimeoutError(f"Timeout of {timeout}s exceeded")
E       pytest_docker_tools.exceptions.TimeoutError: Timeout of 30s exceeded

../.local/lib/python3.9/site-packages/pytest_docker_tools/utils.py:37: TimeoutError

During handling of the above exception, another exception occurred:

request = <SubRequest 'app' for <Function test_app>>, docker_client = <docker.client.DockerClient object at 0x7ff643dbd2b0>
wrapper_class = <class 'pytest_docker_tools.wrappers.container.Container'>
kwargs = {'detach': True, 'image': 'sha256:dc01bb5db190457b853253c7e117799f4190a133e4f30ba3136398ca377e2540', 'labels': {'creat...pytest-docker-tools.signature': 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96'}, 'network': 'host'}
signature = 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96', timeout = 30
container = <pytest_docker_tools.wrappers.container.Container object at 0x7ff643dbd850>

    @fixture_factory()
    def container(request, docker_client, wrapper_class, **kwargs):
        """ Docker container: image={image} """
    
        wrapper_class = wrapper_class or Container
    
        kwargs.update({"detach": True})
        set_reusable_labels(kwargs, request)
    
        signature = hash_params(kwargs)
        set_signature(kwargs, signature)
    
        if request.config.option.reuse_containers:
            if "name" not in kwargs.keys():
                pytest.fail(
                    "Tried to use '--reuse-containers' command line argument without "
                    "setting 'name' attribute on container"
                )
    
            name = kwargs["name"]
    
            try:
                current = docker_client.containers.get(name)
            except NotFound:
                pass
            else:
                # Found a container with the right name, but it doesn't have pytest-docker-tools labels
                # We shouldn't just clobber it, its not ours. Bail out.
                if not is_reusable_container(current):
                    pytest.fail(
                        f"Tried to reuse {name} but it does not appear to be a reusable container"
                    )
    
                # It's ours, and its not stale. Reuse it!
                if check_signature(current.labels, signature):
                    return wrapper_class(current)
    
                # It's ours and it is stale. Clobber it.
                print(f"Removing stale reusable container: {name}")
                current.remove(force=True)
    
        timeout = kwargs.pop("timeout", 30)
    
        raw_container = docker_client.containers.run(**kwargs)
        if not request.config.option.reuse_containers:
            request.addfinalizer(
                lambda: raw_container.remove(force=True) and raw_container.wait(timeout=10)
            )
    
        container = wrapper_class(raw_container)
    
        try:
            wait_for_callable("Waiting for container to be ready", container.ready, timeout)
        except TimeoutError:
>           raise ContainerNotReady(
                container, "Timeout while waiting for container to be ready"
            )
E           pytest_docker_tools.exceptions.ContainerNotReady: Timeout while waiting for container to be ready

../.local/lib/python3.9/site-packages/pytest_docker_tools/factories/container.py:71: ContainerNotReady
------------------------------------------------------ Captured stdout setup ------------------------------------------------------
Building /home/fernanda/conectionTest/app.....................
Waiting for container to be ready..............................
------------------------------------------------------- nostalgic_goldberg --------------------------------------------------------
 * Serving Flask app 'app' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
INFO:werkzeug: * Running on all addresses (0.0.0.0)
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://127.0.0.1:8080
 * Running on http://192.168.2.109:8080 (Press CTRL+C to quit)
INFO:werkzeug: * Restarting with stat
WARNING:werkzeug: * Debugger is active!
INFO:werkzeug: * Debugger PIN: 982-840-186
______________________________________________ ERROR at setup of test_app_connection ______________________________________________

request = <SubRequest 'app' for <Function test_app_connection>>
docker_client = <docker.client.DockerClient object at 0x7ff643dbd2b0>
wrapper_class = <class 'pytest_docker_tools.wrappers.container.Container'>
kwargs = {'detach': True, 'image': 'sha256:dc01bb5db190457b853253c7e117799f4190a133e4f30ba3136398ca377e2540', 'labels': {'creat...pytest-docker-tools.signature': 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96'}, 'network': 'host'}
signature = 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96', timeout = 30
container = <pytest_docker_tools.wrappers.container.Container object at 0x7ff643d71970>

    @fixture_factory()
    def container(request, docker_client, wrapper_class, **kwargs):
        """ Docker container: image={image} """
    
        wrapper_class = wrapper_class or Container
    
        kwargs.update({"detach": True})
        set_reusable_labels(kwargs, request)
    
        signature = hash_params(kwargs)
        set_signature(kwargs, signature)
    
        if request.config.option.reuse_containers:
            if "name" not in kwargs.keys():
                pytest.fail(
                    "Tried to use '--reuse-containers' command line argument without "
                    "setting 'name' attribute on container"
                )
    
            name = kwargs["name"]
    
            try:
                current = docker_client.containers.get(name)
            except NotFound:
                pass
            else:
                # Found a container with the right name, but it doesn't have pytest-docker-tools labels
                # We shouldn't just clobber it, its not ours. Bail out.
                if not is_reusable_container(current):
                    pytest.fail(
                        f"Tried to reuse {name} but it does not appear to be a reusable container"
                    )
    
                # It's ours, and its not stale. Reuse it!
                if check_signature(current.labels, signature):
                    return wrapper_class(current)
    
                # It's ours and it is stale. Clobber it.
                print(f"Removing stale reusable container: {name}")
                current.remove(force=True)
    
        timeout = kwargs.pop("timeout", 30)
    
        raw_container = docker_client.containers.run(**kwargs)
        if not request.config.option.reuse_containers:
            request.addfinalizer(
                lambda: raw_container.remove(force=True) and raw_container.wait(timeout=10)
            )
    
        container = wrapper_class(raw_container)
    
        try:
>           wait_for_callable("Waiting for container to be ready", container.ready, timeout)

../.local/lib/python3.9/site-packages/pytest_docker_tools/factories/container.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

message = 'Waiting for container to be ready'
func = <bound method Container.ready of <pytest_docker_tools.wrappers.container.Container object at 0x7ff643d71970>>, timeout = 30

    def wait_for_callable(message: str, func: Callable, timeout: int = 30) -> None:
        """
        Runs a callable once a second until it returns True or we hit the timeout.
        """
        sys.stdout.write(message)
        try:
            for i in range(timeout):
                sys.stdout.write(".")
                sys.stdout.flush()
    
                if func():
                    return
    
                time.sleep(1)
        finally:
            sys.stdout.write("\n")
    
>       raise TimeoutError(f"Timeout of {timeout}s exceeded")
E       pytest_docker_tools.exceptions.TimeoutError: Timeout of 30s exceeded

../.local/lib/python3.9/site-packages/pytest_docker_tools/utils.py:37: TimeoutError

During handling of the above exception, another exception occurred:

request = <SubRequest 'app' for <Function test_app_connection>>
docker_client = <docker.client.DockerClient object at 0x7ff643dbd2b0>
wrapper_class = <class 'pytest_docker_tools.wrappers.container.Container'>
kwargs = {'detach': True, 'image': 'sha256:dc01bb5db190457b853253c7e117799f4190a133e4f30ba3136398ca377e2540', 'labels': {'creat...pytest-docker-tools.signature': 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96'}, 'network': 'host'}
signature = 'f2725c8390dd6369525756e85c5595fd6eec803dc9867a90aaf8174c435bac96', timeout = 30
container = <pytest_docker_tools.wrappers.container.Container object at 0x7ff643d71970>

    @fixture_factory()
    def container(request, docker_client, wrapper_class, **kwargs):
        """ Docker container: image={image} """
    
        wrapper_class = wrapper_class or Container
    
        kwargs.update({"detach": True})
        set_reusable_labels(kwargs, request)
    
        signature = hash_params(kwargs)
        set_signature(kwargs, signature)
    
        if request.config.option.reuse_containers:
            if "name" not in kwargs.keys():
                pytest.fail(
                    "Tried to use '--reuse-containers' command line argument without "
                    "setting 'name' attribute on container"
                )
    
            name = kwargs["name"]
    
            try:
                current = docker_client.containers.get(name)
            except NotFound:
                pass
            else:
                # Found a container with the right name, but it doesn't have pytest-docker-tools labels
                # We shouldn't just clobber it, its not ours. Bail out.
                if not is_reusable_container(current):
                    pytest.fail(
                        f"Tried to reuse {name} but it does not appear to be a reusable container"
                    )
    
                # It's ours, and its not stale. Reuse it!
                if check_signature(current.labels, signature):
                    return wrapper_class(current)
    
                # It's ours and it is stale. Clobber it.
                print(f"Removing stale reusable container: {name}")
                current.remove(force=True)
    
        timeout = kwargs.pop("timeout", 30)
    
        raw_container = docker_client.containers.run(**kwargs)
        if not request.config.option.reuse_containers:
            request.addfinalizer(
                lambda: raw_container.remove(force=True) and raw_container.wait(timeout=10)
            )
    
        container = wrapper_class(raw_container)
    
        try:
            wait_for_callable("Waiting for container to be ready", container.ready, timeout)
        except TimeoutError:
>           raise ContainerNotReady(
                container, "Timeout while waiting for container to be ready"
            )
E           pytest_docker_tools.exceptions.ContainerNotReady: Timeout while waiting for container to be ready

../.local/lib/python3.9/site-packages/pytest_docker_tools/factories/container.py:71: ContainerNotReady
------------------------------------------------------ Captured stdout setup ------------------------------------------------------
Waiting for container to be ready..............................
---------------------------------------------------------- angry_volhard ----------------------------------------------------------
 * Serving Flask app 'app' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
INFO:werkzeug: * Running on all addresses (0.0.0.0)
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://127.0.0.1:8080
 * Running on http://192.168.2.109:8080 (Press CTRL+C to quit)
INFO:werkzeug: * Restarting with stat
WARNING:werkzeug: * Debugger is active!
INFO:werkzeug: * Debugger PIN: 982-840-186
===================================================== short test summary info =====================================================
ERROR test.py::test_app - pytest_docker_tools.exceptions.ContainerNotReady: Timeout while waiting for container to be ready
ERROR test.py::test_app_connection - pytest_docker_tools.exceptions.ContainerNotReady: Timeout while waiting for container to be...

What am I doing wrong?

AFAIK running containers in host network mode currently is not supported. I was trying to do the same a couple of months ago. I ened up starting to hack the plugin to integrate proper support. However this wasn`t strait forward, because other issues showed up on the way that needed to be addressed as well. In the end I was able to continue without using host networking

Jc2k commented

Have you tried using network_mode="host" instead of network="host"?

Note that I only use this with docker networks myself (so i can use pytest-xdist safely), so you are kinda on your own here.