From e8be131a6bf28ec5e5dd7e6996d6c0dd374bd8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= Date: Wed, 13 Mar 2024 13:48:46 +0100 Subject: [PATCH] Expose the container's name & IP via ContainerData.inspect --- CHANGELOG.rst | 4 ++++ pytest_container/inspect.py | 8 +++++++- pytest_container/runtime.py | 9 ++++++++- tests/test_inspect.py | 28 ++++++++++++++++++++-------- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 54da14b..a6369b0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,10 @@ Breaking changes: Improvements and new features: +- Add attributes :py:attr:`~pytest_container.inspect.ContainerInspect.name` and + :py:attr:`~pytest_container.inspect.ContainerNetworkSettings.ip_address` + exposing the container's name & IP + - Add property :py:attr:`~pytest_container.container.ContainerBase.extra_entrypoint_args` to support appending arguments to the container launch command diff --git a/pytest_container/inspect.py b/pytest_container/inspect.py index f118553..b71fd68 100644 --- a/pytest_container/inspect.py +++ b/pytest_container/inspect.py @@ -242,6 +242,9 @@ class ContainerNetworkSettings: #: list of ports forwarded from the container to the host ports: List[PortForwarding] = field(default_factory=list) + #: IP Address of the container, if it has one + ip_address: Optional[str] = None + @dataclass(frozen=True) class Mount: @@ -280,9 +283,12 @@ class ContainerInspect: """ - #: The Container's ID + #: The container's ID id: str + #: the container's name + name: str + #: program that has been launched inside the container path: str diff --git a/pytest_container/runtime.py b/pytest_container/runtime.py index 3b7fb7f..20aace0 100644 --- a/pytest_container/runtime.py +++ b/pytest_container/runtime.py @@ -386,7 +386,11 @@ def _network_settings_from_inspect( host_port=int(bindings[0]["HostPort"]), ) ) - return ContainerNetworkSettings(ports=ports) + + net_settings = container_inspect["NetworkSettings"] + ip = net_settings.get("IPAddress") or None + + return ContainerNetworkSettings(ports=ports, ip_address=ip) @staticmethod def _mounts_from_inspect( @@ -517,6 +521,7 @@ def inspect_container(self, container_id: str) -> ContainerInspect: return ContainerInspect( config=conf, state=state, + name=inspect["Name"], id=inspect["Id"], path=inspect["Path"], args=inspect["Args"], @@ -600,6 +605,8 @@ def inspect_container(self, container_id: str) -> ContainerInspect: return ContainerInspect( config=conf, state=state, + # docker prefixes the name with a / for reasons… + name=inspect["Name"].lstrip("/"), id=inspect["Id"], path=inspect["Path"], args=inspect["Args"], diff --git a/tests/test_inspect.py b/tests/test_inspect.py index 8d8c4eb..cd5e5d0 100644 --- a/tests/test_inspect.py +++ b/tests/test_inspect.py @@ -7,14 +7,17 @@ from .test_container_build import LEAP +_CTR_NAME = "foobar-12345" IMAGE_WITH_EVERYTHING = DerivedContainer( + singleton=True, + extra_launch_args=["--name", _CTR_NAME], base=LEAP, containerfile="""VOLUME /src/ EXPOSE 8080 666 RUN useradd opensuse USER opensuse -ENTRYPOINT /bin/false +ENTRYPOINT /bin/bash ENV HOME=/src/ ENV MY_VAR= ENV SUFFIX_NAME=dc=example,dc=com @@ -23,13 +26,18 @@ ) -@pytest.mark.parametrize("container", [IMAGE_WITH_EVERYTHING], indirect=True) -def test_inspect(container: ContainerData, container_runtime: OciRuntimeBase): - inspect = container.inspect +@pytest.mark.parametrize( + "container_per_test", [IMAGE_WITH_EVERYTHING], indirect=True +) +def test_inspect( + container_per_test: ContainerData, container_runtime: OciRuntimeBase, host +) -> None: + inspect = container_per_test.inspect - assert inspect.id == container.container_id + assert inspect.id == container_per_test.container_id + assert inspect.name == _CTR_NAME assert inspect.config.user == "opensuse" - assert inspect.config.entrypoint == ["/bin/sh", "-c", "/bin/false"] + assert inspect.config.entrypoint == ["/bin/sh", "-c", "/bin/bash"] assert ( "HOME" in inspect.config.env and inspect.config.env["HOME"] == "/src/" @@ -39,9 +47,9 @@ def test_inspect(container: ContainerData, container_runtime: OciRuntimeBase): # prefixes it with `localhost` and the full build tag # (i.e. `pytest_container:$digest`), while docker just uses the digest expected_img = ( - str(container.container) + str(container_per_test.container) if container_runtime.runner_binary == "docker" - else f"localhost/pytest_container:{container.container}" + else f"localhost/pytest_container:{container_per_test.container}" ) assert inspect.config.image == expected_img @@ -59,3 +67,7 @@ def test_inspect(container: ContainerData, container_runtime: OciRuntimeBase): and isinstance(inspect.mounts[0], VolumeMount) and inspect.mounts[0].destination == "/src" ) + + assert inspect.network.ip_address or "" == host.check_output( + f'{container_runtime.runner_binary} inspect --format "{{{{ .NetworkSettings.IPAddress }}}}" {_CTR_NAME}' + )