Skip to content

Commit

Permalink
Merge pull request #884 from jiridanek/jd_disable_ryuk
Browse files Browse the repository at this point in the history
NO-JIRA: chore(tests/containers): detect socket for ryuk's use with rootless podman on macOS
  • Loading branch information
openshift-merge-bot[bot] authored Feb 5, 2025
2 parents 5888b6c + 89dcb89 commit 09d0130
Show file tree
Hide file tree
Showing 10 changed files with 620 additions and 47 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock poetry run pytest tests/con
# Mac OS
brew install podman
podman machine init
podman machine set --rootful
podman machine set --rootful=false
sudo podman-mac-helper install
podman machine start
poetry run pytest tests/containers --image quay.io/opendatahub/workbench-images@sha256:e98d19df346e7abb1fa3053f6d41f0d1fa9bab39e49b4cb90b510ca33452c2e4
Expand Down
21 changes: 20 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pytest-subtests = "^0.14.1"
pyfakefs = "^5.7.4"
testcontainers = "^4.9.1"
docker = "^7.1.0"
podman = "^5.2.0"
pydantic = "^2.10.6"
requests = "^2.32.3"

Expand Down
12 changes: 11 additions & 1 deletion tests/containers/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import logging
import os
import platform
from typing import Iterable, Callable, TYPE_CHECKING

import testcontainers.core.config
Expand Down Expand Up @@ -64,7 +65,16 @@ def pytest_sessionstart(session: Session) -> None:

# set that socket path for ryuk's use, unless user overrode that
if TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE not in os.environ:
testcontainers.core.config.testcontainers_config.ryuk_docker_socket = socket_path
logging.info(f"Env variable TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE not set, setting it now")
if platform.system().lower() == 'linux':
logging.info(f"We are on Linux, setting {socket_path=} for TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE")
testcontainers.core.config.testcontainers_config.ryuk_docker_socket = socket_path
elif platform.system().lower() == 'darwin':
podman_machine_socket_path = docker_utils.get_podman_machine_socket_path(client.client)
logging.info(f"We are on macOS, setting {podman_machine_socket_path=} for TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE")
testcontainers.core.config.testcontainers_config.ryuk_docker_socket = podman_machine_socket_path
else:
raise RuntimeError(f"Unsupported platform {platform.system()=}, cannot set TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE")

# second preflight check: start the Reaper container
if not testcontainers.core.config.testcontainers_config.ryuk_disabled:
Expand Down
19 changes: 19 additions & 0 deletions tests/containers/docker_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
import time
from typing import Iterable, TYPE_CHECKING

import podman
import docker.client

import testcontainers.core.container

import tests.containers.pydantic_schemas

if TYPE_CHECKING:
from docker.models.containers import Container

Expand Down Expand Up @@ -158,13 +161,29 @@ def communicate(self, line_prefix=b"") -> int:
raise RuntimeError("Hm could that really happen?")
return self.poll()


def get_socket_path(client: docker.client.DockerClient) -> str:
"""Determine the local socket path.
This works even when `podman machine` with its own host-mounts is involved
NOTE: this will not work for remote docker, but we will cross the bridge when we come to it"""
socket_path = _the_one(adapter.socket_path for adapter in client.api.adapters.values())
return socket_path


def get_podman_machine_socket_path(docker_client: docker.client.DockerClient) -> str:
"""Determine the podman socket path that's valid from inside Podman Machine.
* rootful podman: both the host (`ls`) and podman machine (`podman machine ssh ls`) have it at `/var/run/docker.sock`.
* rootless podman: the location on host is still the same while podman machine has it in `/var/run/user/${PID}/podman/podman.sock`.
"""
socket_path = get_socket_path(docker_client)
podman_client = podman.PodmanClient(base_url="http+unix://" + socket_path)
info = tests.containers.pydantic_schemas.PodmanInfo.model_validate(podman_client.info())
assert info.host.remoteSocket.exists, "Failed to determine the podman remote socket"
assert info.host.remoteSocket.path.startswith("unix://"), "Unexpected remote socket path"
machine_socket_path = info.host.remoteSocket.path[len("unix://"):]
return machine_socket_path


def get_container_pid(container: Container) -> int | None:
"""Get the network namespace of a Docker container."""
container.reload()
Expand Down
6 changes: 3 additions & 3 deletions tests/containers/podman_machine_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
import subprocess
from typing import Callable

import tests.containers.schemas
import tests.containers.pydantic_schemas

logging.basicConfig(level=logging.DEBUG)

def open_ssh_tunnel(machine_predicate: Callable[[tests.containers.schemas.PodmanMachine], bool],
def open_ssh_tunnel(machine_predicate: Callable[[tests.containers.pydantic_schemas.PodmanMachine], bool],
local_port: int, remote_port: int, remote_interface: str = "localhost") -> subprocess.Popen:
# Load and parse the Podman machine data
machine_names = subprocess.check_output(["podman", "machine", "list", "--quiet"], text=True).splitlines()
json_data = subprocess.check_output(["podman", "machine", "inspect", *machine_names], text=True)
inspect = tests.containers.schemas.PodmanMachineInspect(machines=json.loads(json_data))
inspect = tests.containers.pydantic_schemas.PodmanMachineInspect(machines=json.loads(json_data))
machines = inspect.machines

machine = next((m for m in machines if machine_predicate(m)), None)
Expand Down
8 changes: 8 additions & 0 deletions tests/containers/pydantic_schemas/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from .podman_info import PodmanInfo
from .podman_machine_inspect import PodmanMachineInspect, PodmanMachine

__all__ = [
PodmanInfo,
PodmanMachineInspect,
PodmanMachine,
]
Loading

0 comments on commit 09d0130

Please sign in to comment.