Implement create_args in the oci container class

This commit is contained in:
Joe Rickerby
2023-05-08 21:59:11 +01:00
parent de419eef44
commit 5cb0964111
4 changed files with 49 additions and 21 deletions
+3 -1
View File
@@ -379,7 +379,9 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
try: try:
# check the container engine is installed # check the container engine is installed
subprocess.run( subprocess.run(
[options.globals.container_engine, "--version"], check=True, stdout=subprocess.DEVNULL [options.globals.container_engine.name, "--version"],
check=True,
stdout=subprocess.DEVNULL,
) )
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
print( print(
+26 -15
View File
@@ -11,6 +11,7 @@ import sys
import typing import typing
import uuid import uuid
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from dataclasses import dataclass
from pathlib import Path, PurePath, PurePosixPath from pathlib import Path, PurePath, PurePosixPath
from types import TracebackType from types import TracebackType
from typing import IO, Dict from typing import IO, Dict
@@ -19,7 +20,16 @@ from ._compat.typing import Literal
from .typing import PathOrStr, PopenBytes from .typing import PathOrStr, PopenBytes
from .util import CIProvider, detect_ci_provider from .util import CIProvider, detect_ci_provider
ContainerEngine = Literal["docker", "podman"] ContainerEngineName = Literal["docker", "podman"]
@dataclass(frozen=True)
class OCIContainerEngineConfig:
name: ContainerEngineName
create_args: Sequence[str] = ()
DEFAULT_ENGINE = OCIContainerEngineConfig("docker")
class OCIContainer: class OCIContainer:
@@ -57,7 +67,7 @@ class OCIContainer:
image: str, image: str,
simulate_32_bit: bool = False, simulate_32_bit: bool = False,
cwd: PathOrStr | None = None, cwd: PathOrStr | None = None,
engine: ContainerEngine = "docker", engine: OCIContainerEngineConfig = DEFAULT_ENGINE,
): ):
if not image: if not image:
msg = "Must have a non-empty image to run." msg = "Must have a non-empty image to run."
@@ -84,13 +94,14 @@ class OCIContainer:
subprocess.run( subprocess.run(
[ [
self.engine, self.engine.name,
"create", "create",
"--env=CIBUILDWHEEL", "--env=CIBUILDWHEEL",
f"--name={self.name}", f"--name={self.name}",
"--interactive", "--interactive",
"--volume=/:/host", # ignored on CircleCI "--volume=/:/host", # ignored on CircleCI
*network_args, *network_args,
*self.engine.create_args,
self.image, self.image,
*shell_args, *shell_args,
], ],
@@ -99,7 +110,7 @@ class OCIContainer:
self.process = subprocess.Popen( self.process = subprocess.Popen(
[ [
self.engine, self.engine.name,
"start", "start",
"--attach", "--attach",
"--interactive", "--interactive",
@@ -137,7 +148,7 @@ class OCIContainer:
self.bash_stdin.close() self.bash_stdin.close()
self.bash_stdout.close() self.bash_stdout.close()
if self.engine == "podman": if self.engine.name == "podman":
# This works around what seems to be a race condition in the podman # This works around what seems to be a race condition in the podman
# backend. The full reason is not understood. See PR #966 for a # backend. The full reason is not understood. See PR #966 for a
# discussion on possible causes and attempts to remove this line. # discussion on possible causes and attempts to remove this line.
@@ -147,7 +158,7 @@ class OCIContainer:
assert isinstance(self.name, str) assert isinstance(self.name, str)
subprocess.run( subprocess.run(
[self.engine, "rm", "--force", "-v", self.name], [self.engine.name, "rm", "--force", "-v", self.name],
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
check=False, check=False,
) )
@@ -171,7 +182,7 @@ class OCIContainer:
exec_process: subprocess.Popen[bytes] exec_process: subprocess.Popen[bytes]
with subprocess.Popen( with subprocess.Popen(
[ [
self.engine, self.engine.name,
"exec", "exec",
"-i", "-i",
str(self.name), str(self.name),
@@ -198,10 +209,10 @@ class OCIContainer:
# note: we assume from_path is a dir # note: we assume from_path is a dir
to_path.mkdir(parents=True, exist_ok=True) to_path.mkdir(parents=True, exist_ok=True)
if self.engine == "podman": if self.engine.name == "podman":
subprocess.run( subprocess.run(
[ [
self.engine, self.engine.name,
"cp", "cp",
f"{self.name}:{from_path}/.", f"{self.name}:{from_path}/.",
str(to_path), str(to_path),
@@ -209,10 +220,10 @@ class OCIContainer:
check=True, check=True,
cwd=to_path, cwd=to_path,
) )
elif self.engine == "docker": elif self.engine.name == "docker":
# There is a bug in docker that prevents a simple 'cp' invocation # There is a bug in docker that prevents a simple 'cp' invocation
# from working https://github.com/moby/moby/issues/38995 # from working https://github.com/moby/moby/issues/38995
command = f"{self.engine} exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -" command = f"{self.engine.name} exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -"
subprocess.run( subprocess.run(
command, command,
shell=True, shell=True,
@@ -220,7 +231,7 @@ class OCIContainer:
cwd=to_path, cwd=to_path,
) )
else: else:
raise KeyError(self.engine) raise KeyError(self.engine.name)
def glob(self, path: PurePosixPath, pattern: str) -> list[PurePosixPath]: def glob(self, path: PurePosixPath, pattern: str) -> list[PurePosixPath]:
glob_pattern = path.joinpath(pattern) glob_pattern = path.joinpath(pattern)
@@ -338,10 +349,10 @@ class OCIContainer:
return self.call(command, env=environment, capture_output=True) return self.call(command, env=environment, capture_output=True)
def debug_info(self) -> str: def debug_info(self) -> str:
if self.engine == "podman": if self.engine.name == "podman":
command = f"{self.engine} info --debug" command = f"{self.engine.name} info --debug"
else: else:
command = f"{self.engine} info" command = f"{self.engine.name} info"
completed = subprocess.run( completed = subprocess.run(
command, command,
shell=True, shell=True,
+2 -2
View File
@@ -22,7 +22,7 @@ from ._compat.typing import Literal, NotRequired, TypedDict
from .architecture import Architecture from .architecture import Architecture
from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment
from .logger import log from .logger import log
from .oci_container import ContainerEngine from .oci_container import OCIContainerEngineConfig
from .projectfiles import get_requires_python_str from .projectfiles import get_requires_python_str
from .typing import PLATFORMS, PlatformName from .typing import PLATFORMS, PlatformName
from .util import ( from .util import (
@@ -75,7 +75,7 @@ class GlobalOptions:
build_selector: BuildSelector build_selector: BuildSelector
test_selector: TestSelector test_selector: TestSelector
architectures: set[Architecture] architectures: set[Architecture]
container_engine: ContainerEngine container_engine: OCIContainerEngineConfig
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
+18 -3
View File
@@ -12,7 +12,7 @@ import pytest
import tomli_w import tomli_w
from cibuildwheel.environment import EnvironmentAssignmentBash from cibuildwheel.environment import EnvironmentAssignmentBash
from cibuildwheel.oci_container import OCIContainer from cibuildwheel.oci_container import OCIContainer, OCIContainerEngineConfig
# Test utilities # Test utilities
@@ -21,7 +21,7 @@ from cibuildwheel.oci_container import OCIContainer
pm = platform.machine() pm = platform.machine()
if pm == "x86_64": if pm == "x86_64":
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_x86_64:2020-05-17-2f8ac3b" DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_x86_64:2020-05-17-2f8ac3b"
elif pm == "aarch64": elif pm in ["aarch64", "arm64"]:
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_aarch64:2020-05-17-2f8ac3b" DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_aarch64:2020-05-17-2f8ac3b"
elif pm == "ppc64le": elif pm == "ppc64le":
DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_ppc64le:2020-05-17-2f8ac3b" DEFAULT_IMAGE = "quay.io/pypa/manylinux2014_ppc64le:2020-05-17-2f8ac3b"
@@ -37,7 +37,7 @@ def container_engine(request):
pytest.skip("need --run-docker option to run") pytest.skip("need --run-docker option to run")
if request.param == "podman" and not request.config.getoption("--run-podman"): if request.param == "podman" and not request.config.getoption("--run-podman"):
pytest.skip("need --run-podman option to run") pytest.skip("need --run-podman option to run")
return request.param return OCIContainerEngineConfig(name=request.param)
# Tests # Tests
@@ -296,3 +296,18 @@ def test_podman_vfs(tmp_path: Path, monkeypatch, request):
# as UID 0. The reason why permission errors occur on podman is documented # as UID 0. The reason why permission errors occur on podman is documented
# in https://podman.io/blogs/2018/10/03/podman-remove-content-homedir.html # in https://podman.io/blogs/2018/10/03/podman-remove-content-homedir.html
subprocess.run(["podman", "unshare", "rm", "-rf", vfs_path], check=True) subprocess.run(["podman", "unshare", "rm", "-rf", vfs_path], check=True)
def test_create_args(tmp_path: Path):
test_mount_dir = tmp_path / "test_mount"
test_mount_dir.mkdir()
(test_mount_dir / "test_file.txt").write_text("1234")
container_engine = OCIContainerEngineConfig(
name="docker", create_args=[f"--volume={test_mount_dir}:/test_mount"]
)
with OCIContainer(
engine=container_engine,
image=DEFAULT_IMAGE,
) as container:
assert container.call(["cat", "/test_mount/test_file.txt"], capture_output=True) == "1234"