Implement create_args in the oci container class
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user