Skip to content

Commit 9cbb465

Browse files
feat(core): pass docker run args to session start (#3487)
1 parent b78c784 commit 9cbb465

File tree

6 files changed

+215
-31
lines changed

6 files changed

+215
-31
lines changed

docs/spelling_wordlist.txt

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ blog
2626
BMP
2727
bugfix
2828
Calamus
29+
cgroup
2930
chartpress
3031
Chartpress
3132
checksum
@@ -51,16 +52,17 @@ CWL
5152
datadir
5253
dataset
5354
datasets
54-
datetimes
5555
dataverse
5656
Dataverse
57+
datetimes
5758
deployer
5859
deserialization
5960
deserialize
6061
Deserialize
6162
deserialized
6263
Deserialized
6364
deserializing
65+
dev
6466
discoverable
6567
Dockerfile
6668
dockerfiles
@@ -79,10 +81,6 @@ filesystem
7981
FilterFlights
8082
findable
8183
Fortran
82-
GitLab
83-
GitPython
84-
GraphQL
85-
graphviz
8684
gapped
8785
git-lfs
8886
gitattributes
@@ -91,8 +89,12 @@ github
9189
gitignore
9290
gitignored
9391
gitkeep
92+
GitLab
9493
gitlab
9594
gitlabClientSecret
95+
GitPython
96+
GraphQL
97+
graphviz
9698
hexsha
9799
Homebrew
98100
hostname
@@ -138,6 +140,7 @@ Matlab
138140
md5
139141
mergetool
140142
metadata
143+
metavar
141144
microservices
142145
middleware
143146
migrationscheck
@@ -162,11 +165,12 @@ OpenID
162165
openssl
163166
papermill
164167
param
165-
params
166168
parameterizable
167169
parametrization
168170
parametrize
169171
parametrized
172+
params
173+
PIDs
170174
pipenv
171175
PNG
172176
Postgresql
@@ -193,8 +197,8 @@ refactored
193197
Renga
194198
renku
195199
Renku
196-
renkulab
197200
renku-mls
201+
renkulab
198202
renv
199203
repo
200204
reproducibility
@@ -218,6 +222,7 @@ scala
218222
serializer
219223
sha
220224
shacl
225+
shm
221226
Slurm
222227
Snyk
223228
SPARQL
@@ -252,6 +257,7 @@ subsubcommands
252257
sudo
253258
supertype
254259
supertypes
260+
swappiness
255261
symlink
256262
symlinks
257263
templated
@@ -260,11 +266,11 @@ Tensorflow
260266
timestamp
261267
tinkerpop
262268
toolchain
269+
TTY
263270
tutorialLink
264271
txt
265272
typesystem
266273
Ubuntu
267-
Unmount
268274
ui
269275
Unescape
270276
unhandled
@@ -274,12 +280,13 @@ Unlink
274280
unlinking
275281
unmapped
276282
unmerged
283+
Unmount
277284
unpushed
278285
untracked
279286
untracked
280287
updatable
281-
url
282288
uri
289+
url
283290
urls
284291
username
285292
validator

renku/core/dataset/providers/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class ProviderParameter(NamedTuple):
103103
is_flag: bool = False
104104
multiple: bool = False
105105
type: Optional[Type] = None
106+
metavar: Optional[str] = None
106107

107108

108109
class ProviderDataset(Dataset):

renku/core/session/docker.py

Lines changed: 124 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import webbrowser
2121
from datetime import datetime
2222
from pathlib import Path
23-
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, cast
23+
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, Union, cast
2424
from uuid import uuid4
2525

2626
import docker
@@ -126,6 +126,97 @@ def get_start_parameters(self) -> List["ProviderParameter"]:
126126
return [
127127
ProviderParameter("port", help="Local port to use (random if not specified).", type=int),
128128
ProviderParameter("force-build", help="Always build image and don't check if it exists.", is_flag=True),
129+
ProviderParameter(
130+
"blkio-weight", help="Block IO (relative weight), between 10 and 1000, or 0 to disable.", type=int
131+
),
132+
ProviderParameter("cap-add", help="Add Linux capabilities.", multiple=True),
133+
ProviderParameter("cap-drop", help="Drop Linux capabilities.", multiple=True),
134+
ProviderParameter("cgroup-parent", help="Override the default parent cgroup.", type=str),
135+
ProviderParameter("cpu-count", help="Number of usable CPUs.", type=int),
136+
ProviderParameter("cpu-percent", help="Usable percentage of the available CPUs.", type=int),
137+
ProviderParameter("cpu-period", help="The length of a CPU period in microseconds.", type=int),
138+
ProviderParameter(
139+
"cpu-quota", help="Microseconds of CPU time that the container can get in a CPU period.", type=int
140+
),
141+
ProviderParameter("cpu-rt-period", help="Limit CPU real-time period in microseconds.", type=int),
142+
ProviderParameter("cpu-rt-runtime", help="Limit CPU real-time runtime in microseconds.", type=int),
143+
ProviderParameter("cpu-shares", help="CPU shares (relative weight).", type=int),
144+
ProviderParameter("cpuset-cpus", help="CPUs in which to allow execution ('0-3', '0,1').", type=str),
145+
ProviderParameter(
146+
"cpuset-mems", help="Memory nodes (MEMs) in which to allow execution ('0-3', '0,1').", type=str
147+
),
148+
ProviderParameter(
149+
"device-cgroup-rules",
150+
help="A list of cgroup rules to apply to the container.",
151+
multiple=True,
152+
flags=["device-cgroup-rule"],
153+
),
154+
ProviderParameter("devices", help="Expose host devices to the container.", multiple=True, flags=["device"]),
155+
ProviderParameter("dns", help="Set custom DNS servers.", multiple=True),
156+
ProviderParameter(
157+
"dns-opt",
158+
help="Additional options to be added to the container's ``resolv.conf`` file.",
159+
type=str,
160+
flags=["dns-opt", "dns-option"],
161+
),
162+
ProviderParameter("dns-search", help="DNS search domains.", multiple=True),
163+
ProviderParameter("domainname", help="Container NIS domain name.", type=str),
164+
ProviderParameter("entrypoint", help="The entrypoint for the container.", type=str),
165+
ProviderParameter(
166+
"environment",
167+
help="Environment variables to set inside the container, in the format 'VAR=VAL'",
168+
multiple=True,
169+
flags=["env"],
170+
),
171+
ProviderParameter(
172+
"group-add",
173+
help="List of additional group names and/or IDs that the container process will run as.",
174+
multiple=True,
175+
),
176+
ProviderParameter("hostname", help="Optional hostname for the container.", type=str),
177+
ProviderParameter(
178+
"init", help="Run an init inside the container that forwards signals and reaps processes", is_flag=True
179+
),
180+
ProviderParameter("isolation", help="Isolation technology to use.", type=str),
181+
ProviderParameter("kernel-memory", help="Kernel memory limit (bytes).", type=int, metavar="<bytes>"),
182+
ProviderParameter("mac-address", help="MAC address to assign to the container.", type=str),
183+
ProviderParameter("mem-reservation", help="Memory soft limit.", type=int, flags=["memory-reservation"]),
184+
ProviderParameter(
185+
"mem-swappiness",
186+
help="Tune container memory swappiness (0 to 100).",
187+
type=int,
188+
flags=["memory-swappiness"],
189+
),
190+
ProviderParameter("memswap-limit", help="Swap limit equal to memory plus swap.", flags=["memory-swap"]),
191+
ProviderParameter("name", help="The name for this container.", type=str),
192+
ProviderParameter("network", help="Connect a container to a network.", type=str),
193+
ProviderParameter("oom-kill-disable", help="Disable OOM Killer.", is_flag=True),
194+
ProviderParameter("oom-score-adj", help="Tune host's OOM preferences (-1000 to 1000).", type=int),
195+
ProviderParameter("pids-limit", help="Tune a container's PIDs limit.", type=int),
196+
ProviderParameter("platform", help="Set platform if server is multi-platform capable.", type=str),
197+
ProviderParameter("privileged", help="Give extended privileges to this container.", is_flag=True),
198+
ProviderParameter(
199+
"publish-all-ports", help="Publish all ports to the host.", is_flag=True, flags=["publish-all"]
200+
),
201+
ProviderParameter("read-only", help="Mount the container's root filesystem as read-only", is_flag=True),
202+
ProviderParameter("remove", help="Automatically remove the container when it exits.", flags=["rm"]),
203+
ProviderParameter("runtime", help="Runtime to use with this container.", type=str),
204+
ProviderParameter("security-opt", help="Security Options.", multiple=True),
205+
ProviderParameter("shm-size", help="Size of /dev/shm (bytes).", type=int, metavar="<bytes>"),
206+
ProviderParameter(
207+
"stdin-open", help="Keep STDIN open even if not attached.", is_flag=True, flags=["interactive"]
208+
),
209+
ProviderParameter("stop-signal", help="Signal to stop the container.", type=str),
210+
ProviderParameter("tty", help="Allocate a pseudo-TTY.", is_flag=True),
211+
ProviderParameter("user", help="Username or UID", type=str),
212+
ProviderParameter("volume-driver", help="The name of a volume driver/plugin.", type=str),
213+
ProviderParameter(
214+
"volumes",
215+
help="A list of volume mounts (e.g. '/host/path/:/mount/path/in/container')",
216+
multiple=True,
217+
flags=["volume"],
218+
),
219+
ProviderParameter("volumes-from", help="Mount volumes from the specified container(s)", multiple=True),
129220
]
130221

131222
def get_open_parameters(self) -> List["ProviderParameter"]:
@@ -162,7 +253,7 @@ def session_start(
162253
cpu_request: Optional[float] = None,
163254
mem_request: Optional[str] = None,
164255
disk_request: Optional[str] = None,
165-
gpu_request: Optional[str] = None,
256+
gpu_request: Optional[Union[str, int]] = None,
166257
**kwargs,
167258
) -> Tuple[str, str]:
168259
"""Creates an interactive session.
@@ -173,6 +264,8 @@ def session_start(
173264
show_non_standard_user_warning = True
174265

175266
def session_start_helper(consider_disk_request: bool):
267+
nonlocal gpu_request
268+
176269
try:
177270
docker_is_running = self.docker_client().ping()
178271
if not docker_is_running:
@@ -201,8 +294,15 @@ def session_start_helper(consider_disk_request: bool):
201294
docker.types.DeviceRequest(count=-1, capabilities=[["compute", "utility"]])
202295
]
203296
else:
297+
if not isinstance(gpu_request, int):
298+
try:
299+
gpu_request = int(gpu_request)
300+
except ValueError:
301+
raise errors.ParameterError(
302+
f"Invalid value for 'gpu': '{gpu_request}'. Valid values are integers or 'all'"
303+
)
204304
resource_requests["device_requests"] = [
205-
docker.types.DeviceRequest(count=[gpu_request], capabilities=[["compute", "utility"]])
305+
docker.types.DeviceRequest(count=gpu_request, capabilities=[["compute", "utility"]])
206306
]
207307

208308
# NOTE: set git user
@@ -214,15 +314,27 @@ def session_start_helper(consider_disk_request: bool):
214314

215315
work_dir = Path(working_dir) / "work" / project_name.split("/")[-1]
216316

217-
volumes = [f"{str(project_context.path.resolve())}:{work_dir}"]
317+
volumes = kwargs.pop("volumes", [])
318+
volumes = list(volumes)
319+
volumes.append(f"{str(project_context.path.resolve())}:{work_dir}")
320+
321+
environment = {}
322+
passed_env_vars = kwargs.pop("environment", [])
323+
for env_var in passed_env_vars:
324+
var, _, value = env_var.partition("=")
325+
if not var:
326+
raise errors.ParameterError(f"Invalid environment variable: '{env_var}'")
327+
environment[var] = value
218328

219329
user = project_context.repository.get_user()
220-
environment = {
221-
"GIT_AUTHOR_NAME": user.name,
222-
"GIT_AUTHOR_EMAIL": user.email,
223-
"GIT_COMMITTER_EMAIL": user.email,
224-
"EMAIL": user.email,
225-
}
330+
environment.update(
331+
{
332+
"GIT_AUTHOR_NAME": user.name,
333+
"GIT_AUTHOR_EMAIL": user.email,
334+
"GIT_COMMITTER_EMAIL": user.email,
335+
"EMAIL": user.email,
336+
}
337+
)
226338

227339
additional_options: Dict[str, Any] = {}
228340

@@ -239,7 +351,7 @@ def session_start_helper(consider_disk_request: bool):
239351
)
240352
show_non_standard_user_warning = False
241353

242-
additional_options["user"] = "root"
354+
additional_options["user"] = kwargs.pop("user", "root")
243355
environment["NB_UID"] = str(os.getuid())
244356
environment["CHOWN_HOME"] = "yes"
245357
environment["CHOWN_HOME_OPTS"] = "-R"
@@ -267,6 +379,7 @@ def session_start_helper(consider_disk_request: bool):
267379
working_dir=str(work_dir),
268380
**resource_requests,
269381
**additional_options,
382+
**kwargs,
270383
)
271384

272385
if not container.ports:

renku/ui/cli/session.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
#
2-
# Copyright 2018-2023 - Swiss Data Science Center (SDSC)
3-
# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
1+
# Copyright Swiss Data Science Center (SDSC). A partnership between
2+
# École Polytechnique Fédérale de Lausanne (EPFL) and
43
# Eidgenössische Technische Hochschule Zürich (ETHZ).
54
#
65
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -59,6 +58,11 @@
5958
Finally, it prompts the user to build the image locally if no image is found. You
6059
can force the image to always be built by using the ``--force-build`` flag.
6160
61+
This command accepts a subset of arguments of the ``docker run`` command. See
62+
its help for the list of supported arguments: ``renku session start --help``.
63+
Accepted values are the same as the ``docker run`` command unless stated
64+
otherwise.
65+
6266
Renkulab provider
6367
~~~~~~~~~~~~~~~~~
6468
@@ -109,7 +113,8 @@
109113
$ renku session start -p renkulab --ssh
110114
Your system is not set up for SSH connections to Renkulab. Would you like to set it up? [y/N]: y
111115
[...]
112-
Session sessionid successfully started, use 'renku session open --ssh sessionid' or 'ssh sessionid' to connect to it
116+
Session <session-id> successfully started, use 'renku session open --ssh <session-id>' or 'ssh <session-id>' to
117+
connect to it
113118
114119
This will create SSH keys for you and setup SSH configuration for connecting to the renku deployment.
115120
You can then use the SSH connection name (``ssh renkulab.io-myproject-sessionid`` in the example)
@@ -277,13 +282,20 @@ def session_start_provider_options(*param_decls, **attrs):
277282
@click.option("--image", type=click.STRING, metavar="<image_name>", help="Docker image to use for the session.")
278283
@click.option("--cpu", type=click.FLOAT, metavar="<cpu quota>", help="CPUs quota for the session.")
279284
@click.option("--disk", type=click.STRING, metavar="<disk size>", help="Amount of disk space required for the session.")
280-
@click.option("--gpu", type=click.STRING, metavar="<GPU quota>", help="GPU quota for the session.")
285+
@click.option(
286+
"--gpu",
287+
type=click.STRING,
288+
metavar="<GPU quota>",
289+
help="Number of GPU devices to add to the container ('all' to pass all GPUs).",
290+
)
281291
@click.option("--memory", type=click.STRING, metavar="<memory size>", help="Amount of memory required for the session.")
282292
@session_start_provider_options()
283293
def start(provider, config, image, cpu, disk, gpu, memory, **kwargs):
284294
"""Start an interactive session."""
285295
from renku.command.session import session_start_command
286296

297+
kwargs = {k: v for k, v in kwargs.items() if v is not None}
298+
287299
communicator = ClickCallback()
288300
session_start_command().with_communicator(communicator).build().execute(
289301
provider=provider,

renku/ui/cli/utils/click.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def wrapper(f):
129129
param_help = f"\b\n{param.help}\n " if j == 0 else param.help # NOTE: add newline after a group
130130

131131
args = (
132-
[f"-{a}" if len(a) == 1 else f"--{a}" for a in param.flags if a] + [param.name]
132+
[f"-{a}" if len(a) == 1 else f"--{a}" for a in param.flags if a] + [param.name.replace("-", "_")]
133133
if param.flags
134134
else [f"--{param.name}"]
135135
)
@@ -141,6 +141,7 @@ def wrapper(f):
141141
is_flag=param.is_flag,
142142
default=param.default,
143143
multiple=param.multiple,
144+
metavar=param.metavar,
144145
)(f)
145146

146147
name = f"{provider.name} configuration"

0 commit comments

Comments
 (0)