Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small improvements: GPU detection, relative bind mounts, bind mounts with spaces #9

Merged
merged 5 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ In general, you can pass the same arguments to `docker-run` as you would pass to
docker-run --volume $(pwd):/volume ubuntu ls /volume
```

In addition to the arguments you are passing, `docker-run` however also enables the following features by default. Each of these default features can be disabled, see [Usage](#usage).
In addition to the arguments you are passing, `docker-run` however also enables the following features by default. Most of these default features can be disabled, see [Usage](#usage).
- container removal after exit (`--rm`)
- interactive tty (`--interactive --tty`)
- current directory name as container name (`--name`)
- relative bind mounts (`--volume [./RELATIVE_PATH>]:[TARGET_PATH]`)
- GPU support (`--gpus all` / `--runtime nvidia`)
- X11 GUI forwarding

Expand Down
10 changes: 5 additions & 5 deletions docker-run-cli/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "docker-run-cli"
version = "0.9.6"
version = "0.9.7"
description = "'docker run' and 'docker exec' with useful defaults"
license = {file = "LICENSE"}
readme = "README.md"
Expand All @@ -23,14 +23,14 @@ classifiers = [
"Operating System :: POSIX :: Linux",
]
keywords = ["docker", "container"]
dependencies = []
dependencies = ["GPUtil~=1.4.0"]
requires-python = ">=3.7"

[project.optional-dependencies]
dev = ["build", "twine"]
docker-ros = ["docker-run-docker-ros>=1.0.4"]
plugins = ["docker-run-docker-ros>=1.0.4"]
all = ["docker-run-docker-ros>=1.0.4", "build", "twine"]
docker-ros = ["docker-run-docker-ros>=1.0.5"]
plugins = ["docker-run-docker-ros>=1.0.5"]
all = ["docker-run-docker-ros>=1.0.5", "build", "twine"]

[project.urls]
"Repository" = "https://github.com/ika-rwth-aachen/docker-run"
Expand Down
15 changes: 14 additions & 1 deletion docker-run-cli/scripts/docker-run
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,21 @@ python3 -m docker_run "${@}" 2>&1 >$CMD_FILE
CMD=$(cat $CMD_FILE)
rm $CMD_FILE

# convert command string to array to allow for escaped characters, e.g. "docker run -v /path\ with\ spaces:/path\ with\ spaces ..."
CMD_ARRAY=()
while IFS= read -r -d ' ' part; do
while [[ $part == *"\\" ]]; do
part+=" "
part="${part//\\/}"
IFS= read -r -d ' ' next_part
part+=$next_part
done
CMD_ARRAY+=("$part")
done <<< "$CMD"
CMD_ARRAY+=("${part%$'\n'}")

# execute command
if [[ ! -z "$CMD" ]]; then
echo -e "================================================================================\n"
exec $CMD
exec "${CMD_ARRAY[@]}"
fi
2 changes: 1 addition & 1 deletion docker-run-cli/src/docker_run/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__name__ = "docker-run"
__version__ = "0.9.6"
__version__ = "0.9.7"
4 changes: 4 additions & 0 deletions docker-run-cli/src/docker_run/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ def buildDockerCommand(args: Dict[str, Any], unknown_args: List[str] = [], cmd_a
else:
docker_cmd += ["bash"] # default exec command

# plugin modifications
for plugin in PLUGINS:
docker_cmd = plugin.modifyFinalCommand(docker_cmd, args, unknown_args)

return " ".join(docker_cmd)


Expand Down
45 changes: 38 additions & 7 deletions docker-run-cli/src/docker_run/plugins/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import tempfile
from typing import Any, Dict, List

import GPUtil

from docker_run.utils import log, runCommand
from docker_run.plugins.plugin import Plugin

Expand Down Expand Up @@ -55,6 +57,13 @@ def getExecFlags(cls, args: Dict[str, Any], unknown_args: List[str]) -> List[str
flags += cls.interactiveFlags()
return flags

@classmethod
def modifyFinalCommand(cls, cmd: List[str], args: Dict[str, Any], unknown_args: List[str]) -> List[str]:
if "-v" in cmd or "--volume" in cmd:
cmd = cls.resolveRelativeVolumeFlags(cmd)
cmd = cls.fixSpacesInVolumeFlags(cmd)
return cmd

@classmethod
def removeFlags(cls) -> List[str]:
return ["--rm"]
Expand All @@ -78,12 +87,16 @@ def localeFlags(cls) -> List[str]:

@classmethod
def gpuSupportFlags(cls) -> List[str]:
if cls.ARCH == "x86_64":
return ["--gpus all"]
elif cls.ARCH == "aarch64" and cls.OS == "Linux":
return ["--runtime nvidia"]
if len(GPUtil.getGPUs()) > 0:
if cls.ARCH == "x86_64":
return ["--gpus all"]
elif cls.ARCH == "aarch64" and cls.OS == "Linux":
return ["--runtime nvidia"]
else:
log(f"GPU not supported by `docker-run` on {cls.OS} with {cls.ARCH} architecture")
return []
else:
log(f"GPU not supported by `docker-run` on {cls.OS} with {cls.ARCH} architecture")
log(f"No GPU detected")
return []

@classmethod
Expand All @@ -92,7 +105,7 @@ def x11GuiForwardingFlags(cls, docker_network: str = "bridge") -> List[str]:
display = os.environ.get("DISPLAY")
if display is None:
return []

if cls.OS == "Darwin":
runCommand(f"xhost +local:")

Expand All @@ -119,4 +132,22 @@ def x11GuiForwardingFlags(cls, docker_network: str = "bridge") -> List[str]:

@classmethod
def currentDirMountFlags(cls) -> List[str]:
return [f"--volume {os.getcwd()}:{os.getcwd()}", f"--workdir {os.getcwd()}"]
cwd = os.getcwd().replace(" ", "\\ ")
return [f"--volume {cwd}:{cwd}", f"--workdir {cwd}"]

@classmethod
def resolveRelativeVolumeFlags(cls, cmd: List[str]) -> List[str]:
for i, arg in enumerate(cmd):
if arg in ["-v", "--volume"]:
mount_path = cmd[i + 1].split(":")[0]
if mount_path.startswith("."):
absolute_mount_path = os.path.abspath(mount_path)
cmd[i + 1] = absolute_mount_path + cmd[i + 1][len(mount_path):]
return cmd

@classmethod
def fixSpacesInVolumeFlags(cls, cmd: List[str]) -> List[str]:
for i, arg in enumerate(cmd):
if arg in ["-v", "--volume"]:
cmd[i + 1] = cmd[i + 1].replace(" ", "\\ ")
return cmd
4 changes: 4 additions & 0 deletions docker-run-cli/src/docker_run/plugins/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ def getRunFlags(cls, args: Dict[str, Any], unknown_args: List[str]) -> List[str]
@abstractmethod
def getExecFlags(cls, args: Dict[str, Any], unknown_args: List[str]) -> List[str]:
raise NotImplementedError()

@classmethod
def modifyFinalCommand(cls, cmd: List[str], args: Dict[str, Any], unknown_args: List[str]) -> List[str]:
return cmd
4 changes: 2 additions & 2 deletions docker-run-docker-ros/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "docker-run-docker-ros"
version = "1.0.4"
version = "1.0.5"
description = "docker-run plugin for Docker images built by docker-ros"
license = {file = "LICENSE"}
readme = "README.md"
Expand All @@ -23,7 +23,7 @@ classifiers = [
"Operating System :: POSIX :: Linux",
]
keywords = ["docker", "container", "ros"]
dependencies = ["docker-run-cli>=0.9.4"]
dependencies = ["docker-run-cli>=0.9.7"]
requires-python = ">=3.7"

[project.urls]
Expand Down
3 changes: 2 additions & 1 deletion docker-run-docker-ros/src/docker_run/plugins/docker_ros.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ def userExecFlags(cls, user: str) -> List[str]:

@classmethod
def currentDirMountWorkspaceFlags(cls) -> List[str]:
return [f"--volume {os.getcwd()}:{cls.TARGET_MOUNT}", f"--workdir {cls.WORKSPACE}"]
cwd = os.getcwd().replace(" ", "\\ ")
return [f"--volume {cwd}:{cls.TARGET_MOUNT}", f"--workdir {cls.WORKSPACE}"]
Loading