Skip to content

Commit

Permalink
add support for the --user option to docker run
Browse files Browse the repository at this point in the history
Username or UID to run commands as inside the container.
https://docs.docker.com/engine/reference/run/#user

Resolves #135
  • Loading branch information
Neile Havens committed Jun 18, 2022
1 parent 7b573c5 commit 212368b
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 1 deletion.
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ The ``[docker:container-name]`` section may contain the following directives:
test run until the container reports healthy, and will fail the test
run if it never does so (within the parameters specified).

``user``
The `user<https://docs.docker.com/engine/reference/run/#user>`__ Username
or UID to run commands as inside the container.

Command-Line Arguments
----------------------

Expand Down Expand Up @@ -182,6 +186,8 @@ Example
# testing use cases, as this could persist data between test runs
volumes =
bind:rw:/my/own/datadir:/var/lib/postgresql/data
# The uid (or username) to run commands as inside the container.
user = 1234
[docker:appserv]
# You can use any value that `docker run` would accept as the image
Expand Down
17 changes: 17 additions & 0 deletions tox_docker/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
r"$"
)

# uid or username. valid username regex from useradd(8)
USER = re.compile(r"[a-z_][a-z0-9_-]*[$]?|[0-9]+")

def runas_name(container_name: str, pid: Optional[int] = None) -> str:
"""
Expand Down Expand Up @@ -103,6 +105,19 @@ def __init__(self, config_line: str) -> None:
)


class User:
def __init__(self, config_line: str) -> None:
match = USER.match(config_line)
if not match:
raise ValueError(f"{config_line!r} is not a valid user name")
self.username = config_line
self.uid = None
try:
self.uid = int(self.username)
except ValueError:
pass


class ContainerConfig:
def __init__(
self,
Expand All @@ -118,6 +133,7 @@ def __init__(
ports: Optional[Collection[Port]] = None,
links: Optional[Collection[Link]] = None,
volumes: Optional[Collection[Volume]] = None,
user: Optional[User] = None,
) -> None:
self.name = name
self.runas_name = runas_name(name)
Expand All @@ -139,3 +155,4 @@ def __init__(
int(healthcheck_start_period) if healthcheck_start_period else None
)
self.healthcheck_retries = healthcheck_retries
self.user = user
5 changes: 5 additions & 0 deletions tox_docker/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def docker_run(
if not os.path.exists(source):
raise ValueError(f"Volume source {source!r} does not exist")

user = None
if container_config.user:
user = container_config.user.uid or container_config.user.username

log(f"run {container_config.image!r} (from {container_config.name!r})")
container = docker.containers.run(
str(container_config.image),
Expand All @@ -108,6 +112,7 @@ def docker_run(
ports=ports,
publish_all_ports=len(ports) == 0,
mounts=container_config.mounts,
user=user,
)
container.reload() # TODO: why do we need this?
return container
Expand Down
6 changes: 6 additions & 0 deletions tox_docker/tox3/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Port,
RunningContainers,
Volume,
User,
)

# nanoseconds in a second; named "SECOND" so that "1.5 * SECOND" makes sense
Expand Down Expand Up @@ -133,6 +134,10 @@ def parse_container_config(
if reader.getstring("volumes"):
volumes = [Volume(line) for line in reader.getlist("volumes")]

user = None
if reader.getstring("user"):
user = User(reader.getstring("user"))

return ContainerConfig(
name=container_name,
image=Image(reader.getstring("image")),
Expand All @@ -146,4 +151,5 @@ def parse_container_config(
ports=ports,
links=links,
volumes=volumes,
user=user
)
9 changes: 8 additions & 1 deletion tox_docker/tox4/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from tox.config.sets import ConfigSet

from tox_docker.config import ContainerConfig, Image, Link, Port, Volume
from tox_docker.config import ContainerConfig, Image, Link, Port, Volume, User

# nanoseconds in a second; named "SECOND" so that "1.5 * SECOND" makes sense
SECOND = 1000000000
Expand Down Expand Up @@ -86,6 +86,12 @@ def register_config(self) -> None:
default=0,
desc="docker healthcheck retry count",
)
self.add_config(
keys=["user"],
of_type=User,
default=None,
desc="Username or UID to run commands as inside the container",
)


def parse_container_config(docker_config: DockerConfigSet) -> ContainerConfig:
Expand All @@ -102,4 +108,5 @@ def parse_container_config(docker_config: DockerConfigSet) -> ContainerConfig:
ports=docker_config["ports"],
links=docker_config["links"],
volumes=docker_config["volumes"],
user=docker_config["user"]
)

0 comments on commit 212368b

Please sign in to comment.