Skip to content

Commit

Permalink
Merge pull request #9 from ika-rwth-aachen/small-improvements
Browse files Browse the repository at this point in the history
Small improvements: GPU detection, relative bind mounts, bind mounts with spaces
  • Loading branch information
lreiher committed Apr 9, 2024
2 parents 97b47ba + 3c98e68 commit 49d2cf3
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 18 deletions.
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}"]

0 comments on commit 49d2cf3

Please sign in to comment.