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

allow extra_host_config and extra_create_kwargs to be callable #500

Merged
merged 1 commit into from
Nov 14, 2023
Merged
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
60 changes: 34 additions & 26 deletions dockerspawner/dockerspawner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
A Spawner for JupyterHub that runs each user's server in a separate docker container
"""
import asyncio
import inspect
import os
import string
import warnings
Expand Down Expand Up @@ -580,7 +581,8 @@ def will_resume(self):
# so JupyterHub >= 0.7.1 won't cleanup our API token
return not self.remove

extra_create_kwargs = Dict(
extra_create_kwargs = Union(
[Callable(), Dict()],
config=True,
help="""Additional args to pass for container create

Expand All @@ -590,11 +592,29 @@ def will_resume(self):
"user": "root" # Can also be an integer UID
}

The above is equivalent to ``docker run --user root``
The above is equivalent to ``docker run --user root``.

If a callable, will be called with the Spawner as the only argument,
must return the same dictionary structure, and may be async.

.. versionchanged:: 13

Added callable support.
""",
)
extra_host_config = Dict(
config=True, help="Additional args to create_host_config for container create"
extra_host_config = Union(
[Callable(), Dict()],
config=True,
help="""
Additional args to create_host_config for container create.

If a callable, will be called with the Spawner as the only argument,
must return the same dictionary structure, and may be async.

.. versionchanged:: 13

Added callable support.
""",
)

escape = Any(
Expand Down Expand Up @@ -1139,11 +1159,17 @@ async def create_object(self):
name=self.container_name,
command=(await self.get_command()),
)
extra_create_kwargs = self._eval_if_callable(self.extra_create_kwargs)
if inspect.isawaitable(extra_create_kwargs):
extra_create_kwargs = await extra_create_kwargs
extra_host_config = self._eval_if_callable(self.extra_host_config)
if inspect.isawaitable(extra_host_config):
extra_host_config = await extra_host_config

# ensure internal port is exposed
create_kwargs["ports"] = {"%i/tcp" % self.port: None}

create_kwargs.update(self._render_templates(self.extra_create_kwargs))
create_kwargs.update(self._render_templates(extra_create_kwargs))

# build the dictionary of keyword arguments for host_config
host_config = dict(
Expand All @@ -1160,14 +1186,14 @@ async def create_object(self):
# docker cpu units are in microseconds
# cpu_period default is 100ms
# cpu_quota is cpu_period * cpu_limit
cpu_period = host_config["cpu_period"] = self.extra_host_config.get(
cpu_period = host_config["cpu_period"] = extra_host_config.get(
"cpu_period", 100_000
)
host_config["cpu_quota"] = int(self.cpu_limit * cpu_period)

if not self.use_internal_ip:
host_config["port_bindings"] = {self.port: (self.host_ip,)}
host_config.update(self._render_templates(self.extra_host_config))
host_config.update(self._render_templates(extra_host_config))
host_config.setdefault("network_mode", self.network_name)

self.log.debug("Starting host with config: %s", host_config)
Expand Down Expand Up @@ -1243,31 +1269,13 @@ async def pull_image(self, image):
self.log.info("pulling image %s", image)
await self.docker('pull', repo, tag)

async def start(self, image=None, extra_create_kwargs=None, extra_host_config=None):
async def start(self):
"""Start the single-user server in a docker container.

Additional arguments to create/host config/etc. can be specified
via .extra_create_kwargs and .extra_host_config attributes.

If the container exists and ``c.DockerSpawner.remove`` is ``True``, then
the container is removed first. Otherwise, the existing containers
will be restarted.
"""

if image:
self.log.warning("Specifying image via .start args is deprecated")
self.image = image
if extra_create_kwargs:
self.log.warning(
"Specifying extra_create_kwargs via .start args is deprecated"
)
self.extra_create_kwargs.update(extra_create_kwargs)
if extra_host_config:
self.log.warning(
"Specifying extra_host_config via .start args is deprecated"
)
self.extra_host_config.update(extra_host_config)

# image priority:
# 1. user options (from spawn options form)
# 2. self.image from config
Expand Down
Loading