Adds disable_host_mount suboption to container-engine
This commit is contained in:
@@ -61,9 +61,9 @@ properties:
|
|||||||
oneOf:
|
oneOf:
|
||||||
- enum: [docker, podman]
|
- enum: [docker, podman]
|
||||||
- type: string
|
- type: string
|
||||||
pattern: '^docker; ?create_args:'
|
pattern: '^docker; ?(create_args|disable_host_mount):'
|
||||||
- type: string
|
- type: string
|
||||||
pattern: '^podman; ?create_args:'
|
pattern: '^podman; ?(create_args|disable_host_mount):'
|
||||||
- type: object
|
- type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
required: [name]
|
required: [name]
|
||||||
@@ -74,6 +74,8 @@ properties:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
|
disable-host-mount:
|
||||||
|
type: boolean
|
||||||
dependency-versions:
|
dependency-versions:
|
||||||
default: pinned
|
default: pinned
|
||||||
description: Specify how cibuildwheel controls the versions of the tools it uses
|
description: Specify how cibuildwheel controls the versions of the tools it uses
|
||||||
|
|||||||
@@ -32,11 +32,14 @@ ContainerEngineName = Literal["docker", "podman"]
|
|||||||
class OCIContainerEngineConfig:
|
class OCIContainerEngineConfig:
|
||||||
name: ContainerEngineName
|
name: ContainerEngineName
|
||||||
create_args: Sequence[str] = ()
|
create_args: Sequence[str] = ()
|
||||||
|
disable_host_mount: bool = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_config_string(config_string: str) -> OCIContainerEngineConfig:
|
def from_config_string(config_string: str) -> OCIContainerEngineConfig:
|
||||||
config_dict = parse_key_value_string(
|
config_dict = parse_key_value_string(
|
||||||
config_string, ["name"], ["create_args", "create-args"]
|
config_string,
|
||||||
|
["name"],
|
||||||
|
["create_args", "create-args", "disable_host_mount", "disable-host-mount"],
|
||||||
)
|
)
|
||||||
name = " ".join(config_dict["name"])
|
name = " ".join(config_dict["name"])
|
||||||
if name not in {"docker", "podman"}:
|
if name not in {"docker", "podman"}:
|
||||||
@@ -44,15 +47,28 @@ class OCIContainerEngineConfig:
|
|||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
name = typing.cast(ContainerEngineName, name)
|
name = typing.cast(ContainerEngineName, name)
|
||||||
# some flexibility in the option name to cope with TOML conventions
|
# some flexibility in the option names to cope with TOML conventions
|
||||||
create_args = config_dict.get("create_args") or config_dict.get("create-args") or []
|
create_args = config_dict.get("create_args") or config_dict.get("create-args") or []
|
||||||
return OCIContainerEngineConfig(name=name, create_args=create_args)
|
disable_host_mount_options = (
|
||||||
|
config_dict.get("disable_host_mount") or config_dict.get("disable-host-mount") or []
|
||||||
|
)
|
||||||
|
disable_host_mount = (
|
||||||
|
strtobool(disable_host_mount_options[-1]) if disable_host_mount_options else False
|
||||||
|
)
|
||||||
|
|
||||||
|
return OCIContainerEngineConfig(
|
||||||
|
name=name, create_args=create_args, disable_host_mount=disable_host_mount
|
||||||
|
)
|
||||||
|
|
||||||
def options_summary(self) -> str | dict[str, str]:
|
def options_summary(self) -> str | dict[str, str]:
|
||||||
if not self.create_args:
|
if not self.create_args:
|
||||||
return self.name
|
return self.name
|
||||||
else:
|
else:
|
||||||
return {"name": self.name, "create_args": repr(self.create_args)}
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"create_args": repr(self.create_args),
|
||||||
|
"disable_host_mount": str(self.disable_host_mount),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_ENGINE = OCIContainerEngineConfig("docker")
|
DEFAULT_ENGINE = OCIContainerEngineConfig("docker")
|
||||||
@@ -136,7 +152,7 @@ class OCIContainer:
|
|||||||
"--env=SOURCE_DATE_EPOCH",
|
"--env=SOURCE_DATE_EPOCH",
|
||||||
f"--name={self.name}",
|
f"--name={self.name}",
|
||||||
"--interactive",
|
"--interactive",
|
||||||
"--volume=/:/host", # ignored on CircleCI
|
*(["--volume=/:/host"] if not self.engine.disable_host_mount else []),
|
||||||
*network_args,
|
*network_args,
|
||||||
*self.engine.create_args,
|
*self.engine.create_args,
|
||||||
self.image,
|
self.image,
|
||||||
|
|||||||
@@ -378,6 +378,9 @@ def _inner_fmt(k: str, v: Any, table: TableFmt) -> Iterator[str]:
|
|||||||
for inner_v in v:
|
for inner_v in v:
|
||||||
qv = quote_function(inner_v)
|
qv = quote_function(inner_v)
|
||||||
yield table["item"].format(k=k, v=qv)
|
yield table["item"].format(k=k, v=qv)
|
||||||
|
elif isinstance(v, bool):
|
||||||
|
qv = quote_function(str(v))
|
||||||
|
yield table["item"].format(k=k, v=qv)
|
||||||
else:
|
else:
|
||||||
qv = quote_function(v)
|
qv = quote_function(v)
|
||||||
yield table["item"].format(k=k, v=qv)
|
yield table["item"].format(k=k, v=qv)
|
||||||
|
|||||||
@@ -172,11 +172,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^docker; ?create_args:"
|
"pattern": "^docker; ?(create_args|disable_host_mount):"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^podman; ?create_args:"
|
"pattern": "^podman; ?(create_args|disable_host_mount):"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -196,6 +196,9 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"disable-host-mount": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -741,7 +741,7 @@ def parse_key_value_string(
|
|||||||
# split by semicolon
|
# split by semicolon
|
||||||
fields = [list(group) for k, group in itertools.groupby(parts, lambda x: x == ";") if not k]
|
fields = [list(group) for k, group in itertools.groupby(parts, lambda x: x == ";") if not k]
|
||||||
|
|
||||||
result: dict[str, list[str]] = defaultdict(list)
|
result: defaultdict[str, list[str]] = defaultdict(list)
|
||||||
for field_i, field in enumerate(fields):
|
for field_i, field in enumerate(fields):
|
||||||
# check to see if the option name is specified
|
# check to see if the option name is specified
|
||||||
field_name, sep, first_value = field[0].partition(":")
|
field_name, sep, first_value = field[0].partition(":")
|
||||||
@@ -762,4 +762,4 @@ def parse_key_value_string(
|
|||||||
|
|
||||||
result[field_name] += values
|
result[field_name] += values
|
||||||
|
|
||||||
return result
|
return dict(result)
|
||||||
|
|||||||
+15
-7
@@ -1069,8 +1069,8 @@ Auditwheel detects the version of the manylinux / musllinux standard in the imag
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
- `docker[;create_args: ...]`
|
- `docker[;create_args: ...][;disable_host_mount: true/false]`
|
||||||
- `podman[;create_args: ...]`
|
- `podman[;create_args: ...][;disable_host_mount: true/false]`
|
||||||
|
|
||||||
Default: `docker`
|
Default: `docker`
|
||||||
|
|
||||||
@@ -1079,11 +1079,13 @@ Set the container engine to use. Docker is the default, or you can switch to
|
|||||||
running and `docker` available on PATH. To use Podman, it needs to be
|
running and `docker` available on PATH. To use Podman, it needs to be
|
||||||
installed and `podman` available on PATH.
|
installed and `podman` available on PATH.
|
||||||
|
|
||||||
Arguments can be supplied to the container engine. Currently, the only option
|
Options can be supplied after the name.
|
||||||
that's customisable is 'create_args'. Parameters to create_args are
|
|
||||||
space-separated strings, which are passed to the container engine on the
|
| Option name | Description
|
||||||
command line when it's creating the container. If you want to include spaces
|
|---|---
|
||||||
inside a parameter, use shell-style quoting.
|
| `create_args` | Space-separated strings, which are passed to the container engine on the command line when it's creating the container. If you want to include spaces inside a parameter, use shell-style quoting.
|
||||||
|
| `disable_host_mount` | By default, cibuildwheel will mount the root of the host filesystem as a volume at `/host` in the container. If this is undesirable, pass `true` to this option.
|
||||||
|
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
|
||||||
@@ -1104,6 +1106,9 @@ inside a parameter, use shell-style quoting.
|
|||||||
|
|
||||||
# pass command line options to 'docker create'
|
# pass command line options to 'docker create'
|
||||||
CIBW_CONTAINER_ENGINE: "docker; create_args: --gpus all"
|
CIBW_CONTAINER_ENGINE: "docker; create_args: --gpus all"
|
||||||
|
|
||||||
|
# disable the /host mount
|
||||||
|
CIBW_CONTAINER_ENGINE: "docker; disable_host_mount: true"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! tab examples "pyproject.toml"
|
!!! tab examples "pyproject.toml"
|
||||||
@@ -1115,6 +1120,9 @@ inside a parameter, use shell-style quoting.
|
|||||||
|
|
||||||
# pass command line options to 'docker create'
|
# pass command line options to 'docker create'
|
||||||
container-engine = { name = "docker", create-args = ["--gpus", "all"]}
|
container-engine = { name = "docker", create-args = ["--gpus", "all"]}
|
||||||
|
|
||||||
|
# disable the /host mount
|
||||||
|
container-engine = { name = "docker", disable-host-mount = true }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import tomli_w
|
|||||||
|
|
||||||
from cibuildwheel.environment import EnvironmentAssignmentBash
|
from cibuildwheel.environment import EnvironmentAssignmentBash
|
||||||
from cibuildwheel.oci_container import OCIContainer, OCIContainerEngineConfig
|
from cibuildwheel.oci_container import OCIContainer, OCIContainerEngineConfig
|
||||||
from cibuildwheel.util import detect_ci_provider
|
from cibuildwheel.util import CIProvider, detect_ci_provider
|
||||||
|
|
||||||
# Test utilities
|
# Test utilities
|
||||||
|
|
||||||
@@ -415,3 +415,29 @@ def test_enforce_32_bit(container_engine, image, shell_args):
|
|||||||
text=True,
|
text=True,
|
||||||
).stdout
|
).stdout
|
||||||
assert json.loads(container_args) == shell_args
|
assert json.loads(container_args) == shell_args
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("config", "should_have_host_mount"),
|
||||||
|
[
|
||||||
|
("{name}", True),
|
||||||
|
("{name}; disable_host_mount: false", True),
|
||||||
|
("{name}; disable_host_mount: true", False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_disable_host_mount(tmp_path: Path, container_engine, config, should_have_host_mount):
|
||||||
|
if detect_ci_provider() is CIProvider.circle_ci:
|
||||||
|
pytest.skip("Skipping test on CircleCI because docker there does not support --volume")
|
||||||
|
|
||||||
|
engine = OCIContainerEngineConfig.from_config_string(config.format(name=container_engine.name))
|
||||||
|
|
||||||
|
sentinel_file = tmp_path / "sentinel"
|
||||||
|
sentinel_file.write_text("12345")
|
||||||
|
|
||||||
|
with OCIContainer(engine=engine, image=DEFAULT_IMAGE) as container:
|
||||||
|
host_mount_path = "/host" + str(sentinel_file)
|
||||||
|
if should_have_host_mount:
|
||||||
|
assert container.call(["cat", host_mount_path], capture_output=True) == "12345"
|
||||||
|
else:
|
||||||
|
with pytest.raises(subprocess.CalledProcessError):
|
||||||
|
container.call(["cat", host_mount_path], capture_output=True)
|
||||||
|
|||||||
@@ -201,41 +201,61 @@ def test_toml_environment_quoting(tmp_path: Path, toml_assignment, result_value)
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("toml_assignment", "result_name", "result_create_args"),
|
("toml_assignment", "result_name", "result_create_args", "result_disable_host_mount"),
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
'container-engine = "podman"',
|
'container-engine = "podman"',
|
||||||
"podman",
|
"podman",
|
||||||
[],
|
[],
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'container-engine = {name = "podman"}',
|
'container-engine = {name = "podman"}',
|
||||||
"podman",
|
"podman",
|
||||||
[],
|
[],
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'container-engine = "docker; create_args: --some-option"',
|
'container-engine = "docker; create_args: --some-option"',
|
||||||
"docker",
|
"docker",
|
||||||
["--some-option"],
|
["--some-option"],
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'container-engine = {name = "docker", create-args = ["--some-option"]}',
|
'container-engine = {name = "docker", create-args = ["--some-option"]}',
|
||||||
"docker",
|
"docker",
|
||||||
["--some-option"],
|
["--some-option"],
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'container-engine = {name = "docker", create-args = ["--some-option", "value that contains spaces"]}',
|
'container-engine = {name = "docker", create-args = ["--some-option", "value that contains spaces"]}',
|
||||||
"docker",
|
"docker",
|
||||||
["--some-option", "value that contains spaces"],
|
["--some-option", "value that contains spaces"],
|
||||||
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'container-engine = {name = "docker", create-args = ["--some-option", "value;that;contains;semicolons"]}',
|
'container-engine = {name = "docker", create-args = ["--some-option", "value;that;contains;semicolons"]}',
|
||||||
"docker",
|
"docker",
|
||||||
["--some-option", "value;that;contains;semicolons"],
|
["--some-option", "value;that;contains;semicolons"],
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'container-engine = {name = "docker", disable-host-mount = true}',
|
||||||
|
"docker",
|
||||||
|
[],
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'container-engine = {name = "docker", disable_host_mount = true}',
|
||||||
|
"docker",
|
||||||
|
[],
|
||||||
|
True,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_container_engine_option(tmp_path: Path, toml_assignment, result_name, result_create_args):
|
def test_container_engine_option(
|
||||||
|
tmp_path: Path, toml_assignment, result_name, result_create_args, result_disable_host_mount
|
||||||
|
):
|
||||||
args = CommandLineArguments.defaults()
|
args = CommandLineArguments.defaults()
|
||||||
args.package_dir = tmp_path
|
args.package_dir = tmp_path
|
||||||
|
|
||||||
@@ -253,6 +273,7 @@ def test_container_engine_option(tmp_path: Path, toml_assignment, result_name, r
|
|||||||
|
|
||||||
assert parsed_container_engine.name == result_name
|
assert parsed_container_engine.name == result_name
|
||||||
assert parsed_container_engine.create_args == result_create_args
|
assert parsed_container_engine.create_args == result_create_args
|
||||||
|
assert parsed_container_engine.disable_host_mount == result_disable_host_mount
|
||||||
|
|
||||||
|
|
||||||
def test_environment_pass_references():
|
def test_environment_pass_references():
|
||||||
|
|||||||
Reference in New Issue
Block a user