From 55c045e69d5c8b10d49d0bd31e58dd2cfaa41dab Mon Sep 17 00:00:00 2001 From: Judah Rand <17158624+judahrand@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:14:56 +0100 Subject: [PATCH 1/6] Add ability to set a container to `privileged` --- tox_docker/config.py | 2 ++ tox_docker/plugin.py | 1 + tox_docker/tox3/config.py | 21 +++++++++++++++++++++ tox_docker/tox4/config.py | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/tox_docker/config.py b/tox_docker/config.py index 1721a1d..26df3a2 100644 --- a/tox_docker/config.py +++ b/tox_docker/config.py @@ -109,6 +109,7 @@ def __init__( name: str, image: Image, stop: bool, + privileged: bool = False, environment: Optional[Mapping[str, str]] = None, healthcheck_cmd: Optional[str] = None, healthcheck_interval: Optional[float] = None, @@ -123,6 +124,7 @@ def __init__( self.runas_name = runas_name(name) self.image = image self.stop = stop + self.privileged = privileged self.environment: Mapping[str, str] = environment or {} self.ports: Collection[Port] = ports or {} self.links: Collection[Link] = links or {} diff --git a/tox_docker/plugin.py b/tox_docker/plugin.py index eec57fd..6348613 100644 --- a/tox_docker/plugin.py +++ b/tox_docker/plugin.py @@ -108,6 +108,7 @@ def docker_run( ports=ports, publish_all_ports=len(ports) == 0, mounts=container_config.mounts, + privileged=container_config.privileged, ) container.reload() # TODO: why do we need this? return container diff --git a/tox_docker/tox3/config.py b/tox_docker/tox3/config.py index eaa4850..3f49494 100644 --- a/tox_docker/tox3/config.py +++ b/tox_docker/tox3/config.py @@ -53,6 +53,22 @@ def getint(reader: SectionReader, key: str) -> Optional[int]: return val +def getboolean(reader: SectionReader, key: str) -> Optional[bool]: + val = reader.getstring(key) + if val is None: + return None + + # Same implementation as: + # https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.getboolean + lower_val = val.lower().strip() + if lower_val in ("yes", "on", "true", "1"): + return True + if lower_val in ("no", "off", "false", "0"): + return False + msg = f"{val!r} is not a boolean (for {key} in [{reader.section_name}])" + raise ValueError(msg) + + def getenvdict(reader: SectionReader, key: str) -> Mapping[str, str]: environment = {} for value in reader.getlist(key): @@ -105,6 +121,10 @@ def parse_container_config( "stop": container_name not in config.option.docker_dont_stop, } + privileged = False + if reader.getstring("privileged"): + privileged = getboolean(reader, "privileged") + environment = None if reader.getstring("environment"): environment = getenvdict(reader, "environment") @@ -137,6 +157,7 @@ def parse_container_config( name=container_name, image=Image(reader.getstring("image")), stop=container_name not in config.option.docker_dont_stop, + privileged=privileged, environment=environment, healthcheck_cmd=hc_cmd, healthcheck_interval=hc_interval, diff --git a/tox_docker/tox4/config.py b/tox_docker/tox4/config.py index 8beb390..ffa4c68 100644 --- a/tox_docker/tox4/config.py +++ b/tox_docker/tox4/config.py @@ -19,6 +19,17 @@ def image_required(image: Image) -> Image: return image +def getboolean(val: str) -> bool: + # Same implementation as: + # https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.getboolean + lower_val = val.lower().strip() + if lower_val in ("yes", "on", "true", "1"): + return True + if lower_val in ("no", "off", "false", "0"): + return False + raise ValueError(f"{val!r} is not a boolean") + + class DockerConfigSet(ConfigSet): def register_config(self) -> None: self.add_config( @@ -28,6 +39,13 @@ def register_config(self) -> None: desc="docker image to run", post_process=image_required, ) + self.add_config( + keys=["privileged"], + of_type=bool, + default=False, + desc="give extended privileges to this container", + post_process=getboolean, + ) self.add_config( keys=["environment"], of_type=Dict[str, str], @@ -93,6 +111,7 @@ def parse_container_config(docker_config: DockerConfigSet) -> ContainerConfig: name=docker_config.name, image=docker_config["image"], stop=docker_config.name not in docker_config._conf.options.docker_dont_stop, + privileged=docker_config["privileged"], environment=docker_config["environment"], healthcheck_cmd=docker_config["healthcheck_cmd"], healthcheck_interval=docker_config["healthcheck_interval"], From b802fa956c3fbdf2232be87991134dddb802c357 Mon Sep 17 00:00:00 2001 From: Judah Rand <17158624+judahrand@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:24:55 +0100 Subject: [PATCH 2/6] Add tests for `privileged` flag --- tox.ini | 2 ++ tox_docker/tests/test_privileged.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tox_docker/tests/test_privileged.py diff --git a/tox.ini b/tox.ini index b8bacf5..b00d3c8 100644 --- a/tox.ini +++ b/tox.ini @@ -52,6 +52,7 @@ ports = [docker:networking-two] # along with networking-one, proves we can run multiple copies of the same image image = ksdn117/tcp-udp-test +privileged = off ports = 2345:1234/tcp links = networking-one:linked_host @@ -65,6 +66,7 @@ healthcheck_start_period = 1 [docker:healthcheck-custom] image = docker.io/toxdocker/healthcheck:latest +privileged = true healthcheck_cmd = touch /healthcheck/web/healthy healthcheck_interval = 1 healthcheck_timeout = 1 diff --git a/tox_docker/tests/test_privileged.py b/tox_docker/tests/test_privileged.py new file mode 100644 index 0000000..29634ed --- /dev/null +++ b/tox_docker/tests/test_privileged.py @@ -0,0 +1,19 @@ +from tox_docker.tests.util import find_container + + +def test_can_grant_privileged() -> None: + container = find_container("healthcheck-custom") + + assert container.attrs["HostConfig"]["Privileged"] is True + + +def test_can_deny_privileged() -> None: + container = find_container("networking-two") + + assert container.attrs["HostConfig"]["Privileged"] is False + + +def test_not_privileged_by_default() -> None: + container = find_container("healthcheck-builtin") + + assert container.attrs["HostConfig"]["Privileged"] is False From 7eb2e7ed93afefcdac36f12d98d30bebfc392900 Mon Sep 17 00:00:00 2001 From: Judah Rand <17158624+judahrand@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:58:42 +0100 Subject: [PATCH 3/6] Add `privileged` to README --- README.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.rst b/README.rst index 64c9246..94a2c3b 100644 --- a/README.rst +++ b/README.rst @@ -51,6 +51,13 @@ The ``[docker:container-name]`` section may contain the following directives: This value is passed directly to Docker, and may be of any of the forms that Docker accepts in eg ``docker run``. +``privileged`` + A boolean (``true || false``) to set `runtime privilege + `__. + This value is coerced to a boolean based of the same rules as + Python's default `ConfigParser + `__. + ``environment`` A multi-line list of ``KEY=value`` settings which is used to set environment variables for the container. The variables are only available From 17cdf4189722ff63564c7d648e5710b84dad1f08 Mon Sep 17 00:00:00 2001 From: Judah Rand <17158624+judahrand@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:21:49 +0100 Subject: [PATCH 4/6] Fix Tox 4.x parser --- tox_docker/tox4/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox_docker/tox4/config.py b/tox_docker/tox4/config.py index ffa4c68..3554a97 100644 --- a/tox_docker/tox4/config.py +++ b/tox_docker/tox4/config.py @@ -41,8 +41,8 @@ def register_config(self) -> None: ) self.add_config( keys=["privileged"], - of_type=bool, - default=False, + of_type=str, + default="false", desc="give extended privileges to this container", post_process=getboolean, ) From e7bd271a8e57b86e04f16648220573758a957bef Mon Sep 17 00:00:00 2001 From: Judah Rand <17158624+judahrand@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:42:04 +0100 Subject: [PATCH 5/6] Fix `mypy` linting error --- tox_docker/tox3/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox_docker/tox3/config.py b/tox_docker/tox3/config.py index 3f49494..47a8e2f 100644 --- a/tox_docker/tox3/config.py +++ b/tox_docker/tox3/config.py @@ -123,7 +123,7 @@ def parse_container_config( privileged = False if reader.getstring("privileged"): - privileged = getboolean(reader, "privileged") + privileged = bool(getboolean(reader, "privileged")) environment = None if reader.getstring("environment"): From 2f7446de5b8d932338e5dbba8c5d024880a5a7e5 Mon Sep 17 00:00:00 2001 From: Judah Rand <17158624+judahrand@users.noreply.github.com> Date: Fri, 8 Jul 2022 14:47:57 +0100 Subject: [PATCH 6/6] Trust tox4's type conversion --- tox_docker/tox4/config.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/tox_docker/tox4/config.py b/tox_docker/tox4/config.py index 3554a97..3299608 100644 --- a/tox_docker/tox4/config.py +++ b/tox_docker/tox4/config.py @@ -19,17 +19,6 @@ def image_required(image: Image) -> Image: return image -def getboolean(val: str) -> bool: - # Same implementation as: - # https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.getboolean - lower_val = val.lower().strip() - if lower_val in ("yes", "on", "true", "1"): - return True - if lower_val in ("no", "off", "false", "0"): - return False - raise ValueError(f"{val!r} is not a boolean") - - class DockerConfigSet(ConfigSet): def register_config(self) -> None: self.add_config( @@ -41,10 +30,9 @@ def register_config(self) -> None: ) self.add_config( keys=["privileged"], - of_type=str, - default="false", + of_type=bool, + default=False, desc="give extended privileges to this container", - post_process=getboolean, ) self.add_config( keys=["environment"],