OutpostSpawner#
- class outpostspawner.OutpostSpawner(*args: t.Any, **kwargs: t.Any)#
A JupyterHub spawner that spawns services on remote locations in combination with a JupyterHub Outpost service.
- additional_cafile c.OutpostSpawner.additional_cafile = Any(None)#
Additional certificate authorities can be added. Required if JUPYTERHUB_API_URL is an external URL and c.JupyterHub.internal_ssl is True.
- apply_user_options c.OutpostSpawner.apply_user_options = Union(None)#
Hook to apply inputs from user_options to the Spawner.
Typically takes values in user_options, validates them, and updates Spawner attributes:
def apply_user_options(spawner, user_options): if "image" in user_options and isinstance(user_options["image"], str): spawner.image = user_options["image"] c.Spawner.apply_user_options = apply_user_options
apply_user_optionsmay be async.Default: do nothing.
Typically a callable which takes
(spawner: Spawner, user_options: dict), but for simple cases this can be a dict mapping user option fields to Spawner attribute names, e.g.:c.Spawner.apply_user_options = {"image_input": "image"} c.Spawner.options_from_form = "simple"
allows users to specify the image attribute, but not any others. Because
user_optionsgenerally comes in as strings in form data, the dictionary mode uses traitletsfrom_stringto coerce strings to values, which allows setting simple values from strings (e.g. numbers) without needing to implement callable hooks.Note
Because
user_optionsis user input and may be set directly via the REST API, no assumptions should be made on its structure or contents. An empty dict should always be supported. Make sure to validate any inputs before applying them, either in this callable, or in whatever is consuming the value if this is a dict.New in version 5.3: Prior to 5.3, applying user options must be done in
Spawner.start()orSpawner.pre_spawn_hook().
- cancelling_event c.OutpostSpawner.cancelling_event = Union({'failed': False, 'ready': False, 'progress': 99, 'message': '', 'html_message': 'JupyterLab is cancelling the start.'})#
Event shown when a singleuser server was cancelled. Can be a function or a dict.
This may be a coroutine.
Example:
from datetime import datetime async def cancel_click_event(spawner): now = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] return { "failed": False, "ready": False, "progress": 99, "message": "", "html_message": f"<details><summary>{now}: Cancelling start ...</summary>We're stopping the start process.</details>", } c.ForwardBaseSpawner.cancelling_event = cancel_click_event
- check_allowed c.OutpostSpawner.check_allowed = Any(None)#
An optional hook function you can implement to double check if the given user_options allow a start. If the start is not allowed, it should raise an exception.
This may be a coroutine.
Example:
def custom_check_allowed(spawner, user_options): if not user_options.get("allowed", True): raise Exception("This is not allowed") c.OutpostSpawner.check_allowed = custom_check_allowed
- collect_logs c.OutpostSpawner.collect_logs = Bool(False)#
Whether to collect logs when stopping the service.
- collect_logs_polling c.OutpostSpawner.collect_logs_polling = Bool(False)#
Whether to collect logs when polling the service.
- custom_env c.OutpostSpawner.custom_env = Union()#
An optional hook function, or dict, you can implement to add extra environment variables to send to the JupyterHub Outpost service.
This may be a coroutine.
Example:
async def custom_env(spawner, user_options, jupyterhub_api_url): system = user_options.get("system", "") env = { "JUPYTERHUB_STAGE": os.environ.get("JUPYTERHUB_STAGE", ""), "JUPYTERHUB_DOMAIN": os.environ.get("JUPYTERHUB_DOMAIN", ""), "JUPYTERHUB_OPTION1": user_options.get("option1", "") } if system: env["JUPYTERHUB_FLAVORS_UPDATE_URL"] = f"{jupyterhub_api_url.rstrip('/')}/outpostflavors/{system}" return env c.OutpostSpawner.custom_env = custom_env
- custom_misc c.OutpostSpawner.custom_misc = Union()#
An optional hook function, or dict, you can implement to add extra configurations to send to the JupyterHub Outpost service. This will override the Spawner configuration set at the Outpost.
keycan be anything you would normally use in your Spawner configuration:c.OutpostSpawner.<key> = <value>This may be a coroutine.
Example:
async def custom_misc(spawner, user_options): return { "image": "jupyter/base-notebook:latest" } c.OutpostSpawner.custom_misc = custom_misc
will override the image configured at the Outpost:
c.JupyterHubOutpost.spawner_class = KubeSpawner c.KubeSpawner.image = "default_image:1.0"
and spawn a JupyterLab using the
jupyter/base-notebook:latestimage.
- custom_misc_disable_default c.OutpostSpawner.custom_misc_disable_default = Bool(False)#
By default, these
miscoptions will be send to the Outpost service to override the corresponding values of the Spawner configured at the Outpost. You can disable this behaviour by setting this value to true.Default
custom_miscoptions:extra_labels = await self.get_extra_labels() custom_misc.update({ "dns_name_template": self.dns_name_template, "pod_name_template": self.svc_name_template, "internal_ssl": self.internal_ssl, "ip": "0.0.0.0", "port": self.port, "services_enabled": True, "extra_labels": extra_labels }
- custom_poll_interval c.OutpostSpawner.custom_poll_interval = Union(0)#
An optional hook function, or dict, you can implement to define the poll interval (in milliseconds). This allows you to have to different intervals for different Outpost services. You can use this to randomize the poll interval for each spawner object.
Example:
import random def custom_poll_interval(spawner, user_options): system = user_options.get("system", "None") if system == "A": base_poll_interval = 30 poll_interval_randomizer = 10 poll_interval = 1e3 * base_poll_interval + random.randint( 0, 1e3 * poll_interval_randomizer ) else: poll_interval = 0 return poll_interval c.OutpostSpawner.custom_poll_interval = custom_poll_interval
- custom_port c.OutpostSpawner.custom_port = Union(0)#
An optional hook function, or dict, you can implement to define a port depending on the spawner object.
Example:
from jupyterhub.utils import random_potr def custom_port(spawner, user_options): if user_options.get("system", "") == "A": return 8080 return random_port() c.OutpostSpawner.custom_port = custom_port
- custom_user_options c.OutpostSpawner.custom_user_options = Union()#
An optional hook function, or dict, you can implement to add extra user_options to send to the JupyterHub Outpost service.
This may be a coroutine.
Example:
async def custom_user_options(spawner, user_options): user_options["image"] = "jupyter/minimal-notebook:latest" return user_options c.OutpostSpawner.custom_user_options = custom_user_options
- dns_name_template c.OutpostSpawner.dns_name_template = Unicode('{name}.{namespace}.svc.cluster.local')#
Template to use to form the dns name for the pod.
- extra_labels c.OutpostSpawner.extra_labels = Union()#
An optional hook function, or dict, you can implement to add extra labels to the service created when using port-forwarding. Will also be forwarded to the Outpost service (see self.custom_misc_disable_default)
This may be a coroutine.
Example:
def extra_labels(spawner): labels = { "hub.jupyter.org/username": spawner.user.name, "hub.jupyter.org/servername": spawner.name, "sidecar.istio.io/inject": "false" } return labels c.ForwardBaseSpawner.extra_labels = extra_labels
- failed_spawn_request_hook c.OutpostSpawner.failed_spawn_request_hook = Any(None)#
An optional hook function you can implement to handle a failed start attempt properly. This will be called if the POST request to the Outpost service was not successful.
This may be a coroutine.
Example:
def custom_failed_spawn_request_hook(Spawner, exception_thrown): ... return c.OutpostSpawner.failed_spawn_request_hook = custom_failed_spawn_request_hook
- filter_events c.OutpostSpawner.filter_events = Callable(None)#
Different JupyterHub single-user servers may send different events. This filter allows you to unify all events. Should always return a dict. If the dict should not be shown, return an empty dict.
Example:
def custom_filter_events(spawner, event): event["html_message"] = event.get("message", "No message available") return event c.ForwardBaseSpawner.filter_events = custom_filter_events
- group_overrides c.OutpostSpawner.group_overrides = Union()#
Override specific traitlets based on group membership of the user.
This can be a dict, or a callable that returns a dict. The keys of the dict are only used for lexicographical sorting, to guarantee consistent ordering of the overrides. If it is a callable, it may be async, and will be passed one parameter - the spawner instance. It should return a dictionary.
The values of the dict are dicts with the following keys:
"groups"- If the user belongs to any of these groups, these overrides are applied to their server before spawning."spawner_override"- a dictionary with overrides to apply to the Spawner settings. Each value can be either the final value to change or a callable that take theSpawnerinstance as parameter and returns the final value. If the traitlet being overriden is a dictionary, the dictionary will be recursively updated, rather than overriden. If you want to remove a key, set its value toNone.
Example
The following example config will:
Add the environment variable “AM_I_GROUP_ALPHA” to everyone in the “group-alpha” group
Add the environment variable “AM_I_GROUP_BETA” to everyone in the “group-beta” group. If a user is part of both “group-beta” and “group-alpha”, they will get both these env vars, due to the dictionary merging functionality.
Add a higher memory limit for everyone in the “group-beta” group.
c.Spawner.group_overrides = { "01-group-alpha-env-add": { "groups": ["group-alpha"], "spawner_override": {"environment": {"AM_I_GROUP_ALPHA": "yes"}}, }, "02-group-beta-env-add": { "groups": ["group-beta"], "spawner_override": {"environment": {"AM_I_GROUP_BETA": "yes"}}, }, "03-group-beta-mem-limit": { "groups": ["group-beta"], "spawner_override": {"mem_limit": "2G"} } }
- http_client_defaults c.OutpostSpawner.http_client_defaults = Dict()#
Default keyword arguments for the shared HTTP client used to communicate with the Outposts
- namespace c.OutpostSpawner.namespace = Unicode('')#
Kubernetes namespace to create services in.
Default:
ns_path = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" if os.path.exists(ns_path): with open(ns_path) as f: return f.read().strip() return "default"
- poll_jitter c.OutpostSpawner.poll_jitter = Float(0.1)#
Jitter fraction for poll_interval.
Avoids alignment of poll calls for many Spawners, e.g. when restarting JupyterHub, which restarts all polls for running Spawners.
poll_jitter=0means no jitter, 0.1 means 10%, etc.
- post_spawn_request_hook c.OutpostSpawner.post_spawn_request_hook = Any(None)#
An optional hook function you can implement to handle a successful start attempt properly. This will be called if the POST request to the Outpost service was successful.
This may be a coroutine.
Example:
def post_spawn_request_hook(Spawner, resp_json): ... return c.OutpostSpawner.post_spawn_request_hook = post_spawn_request_hook
- pre_poll_hook c.OutpostSpawner.pre_poll_hook = Any(False)#
Hook which allows to run a function before calling poll. Useful when you already know the answer of the upcoming poll call (e.g. information in auth_state is missing).
Used return values of the pre_poll_hook: Return True: Unknown status. Call self._poll() Return False: Unknown status. Do not call self._poll(). Server continues as running. Return Integer: That’s the exit code. Do not call self._poll() Return None: Server still running. Do not call self._poll()
Callable function, may be a coroutine.
- pre_stop_hook c.OutpostSpawner.pre_stop_hook = Any(False)#
Hook which allows to run a function before calling stop.
Callable function, may be a coroutine.
- progress_ready_hook c.OutpostSpawner.progress_ready_hook = Any(None)#
An optional hook function that you can implement to modify the ready event, which will be shown to the user on the spawn progress page when their server is ready.
This can be set independent of any concrete spawner implementation.
This maybe a coroutine.
Example:
async def my_ready_hook(spawner, ready_event): ready_event["html_message"] = f"Server {spawner.name} is ready for {spawner.user.name}" return ready_event c.Spawner.progress_ready_hook = my_ready_hook
- public_api_url c.OutpostSpawner.public_api_url = Any(None)#
Singleuser servers started remotely may have to use a different api_url than the default internal one. This will overwrite
JUPYTERHUB_API_URLin env. Default value is the default internalJUPYTERHUB_API_URL
- request_404_poll_keep_running c.OutpostSpawner.request_404_poll_keep_running = Bool(False)#
How to handle a 404 response from Outpost API during a singleuser poll request.
- request_failed_poll_keep_running c.OutpostSpawner.request_failed_poll_keep_running = Bool(True)#
How to handle a failed request to Outpost API during a singleuser poll request.
- request_headers c.OutpostSpawner.request_headers = Union()#
An optional hook function, or dict, you can implement to define the header used for all requests sent to the JupyterHub Outpost service. They are forwarded directly to the tornado.httpclient.HTTPRequest object.
Example:
def request_headers(spawner, user_options): if user_options.get("system", "") == "A": auth = os.environ.get("SYSTEM_A_AUTHENTICATION") else: auth = os.environ.get("SYSTEM_B_AUTHENTICATION") return { "Content-Type": "application/json", "Accept": "application/json", "Authorization": f"Basic {auth}" } c.OutpostSpawner.request_headers = request_headers
- request_kwargs c.OutpostSpawner.request_kwargs = Union({})#
An optional hook function, or dict, you can implement to define keyword arguments for all requests sent to the JupyterHub Outpost service. They are directly forwarded to the tornado.httpclient.HTTPRequest object.
Example:
def request_kwargs(spawner, user_options): return { "request_timeout": 30, "connect_timeout": 10, "ca_certs": ..., "validate_cert": ..., } c.OutpostSpawner.request_kwargs = request_kwargs
- request_kwargs_start c.OutpostSpawner.request_kwargs_start = Union(None)#
An optional hook function, or dict, you can implement to define keyword arguments for the start request sent to the JupyterHub Outpost service. They are directly forwarded to the tornado.httpclient.HTTPRequest object. If not defined, request_kwargs will be used instead. Example:
def request_kwargs(spawner, user_options): return { "request_timeout": 30, "connect_timeout": 10, "ca_certs": ..., "validate_cert": ..., } c.OutpostSpawner.request_kwargs = request_kwargs
- request_url c.OutpostSpawner.request_url = Union()#
The URL used to communicate with the JupyterHub Outpost service.
This may be a coroutine.
Example:
def request_url(spawner, user_options): if user_options.get("system", "") == "A": return "http://outpost.namespace.svc:8080/services/" else: return "https://remote-outpost.com/services/" c.OutpostSpawner.request_url = request_url
- show_first_default_event c.OutpostSpawner.show_first_default_event = Any(True)#
Hook to define if the default event at 0% should be shown.
Can be a boolean or a callable function. This may be a coroutine.
- ssh_create_remote_forward c.OutpostSpawner.ssh_create_remote_forward = Any(False)#
Whether a port forwarding process from a remote system to the hub is required or not. The remote system must be prepared properly to support this feature.
Must be a boolean or a callable function
- ssh_custom_forward c.OutpostSpawner.ssh_custom_forward = Any(None)#
An optional hook function you can implement to create your own ssh port forwarding called in the start function. This can be used to use an external pod for the port forwarding instead of having JupyterHub handle it.
Example:
from tornado.httpclient import HTTPRequest def ssh_custom_forward(spawner, port_forward_info): url = "..." headers = { ... } req = HTTPRequest( url=url, method="POST", headers=headers, body=json.dumps(port_forward_info), ) await spawner.send_request( req, action="setuptunnel" ) c.ForwardBaseSpawner.ssh_custom_forward = ssh_custom_forward
- ssh_custom_forward_remote c.OutpostSpawner.ssh_custom_forward_remote = Any(None)#
An optional hook function you can implement to create your own ssh port forwarding from remote system to hub.
- ssh_custom_forward_remote_remove c.OutpostSpawner.ssh_custom_forward_remote_remove = Any(None)#
An optional hook function you can implement to remove your own ssh port forwarding from remote system to hub.
- ssh_custom_forward_remove c.OutpostSpawner.ssh_custom_forward_remove = Any(None)#
An optional hook function you can implement to remove your own ssh port forwarding called in the stop function. This can be used to use an external pod for the port forwarding instead of having JupyterHub handle it.
Example:
from tornado.httpclient import HTTPRequest def ssh_custom_forward_remove(spawner, port_forward_info): url = "..." headers = { ... } req = HTTPRequest( url=url, method="DELETE", headers=headers, body=json.dumps(port_forward_info), ) await spawner.send_request( req, action="removetunnel" ) c.ForwardBaseSpawner.ssh_custom_forward_remove = ssh_custom_forward_remove
- ssh_custom_svc c.OutpostSpawner.ssh_custom_svc = Any(None)#
An optional hook function you can implement to create a customized kubernetes svc called in the start function.
Example:
def ssh_custom_svc(spawner, port_forward_info): ... return spawner.pod_name, spawner.port c.ForwardBaseSpawner.ssh_custom_svc = ssh_custom_svc
- ssh_custom_svc_remove c.OutpostSpawner.ssh_custom_svc_remove = Any(None)#
An optional hook function you can implement to remove a customized kubernetes svc called in the stop function.
Example:
def ssh_custom_svc_remove(spawner, port_forward_info): ... return spawner.pod_name, spawner.port c.ForwardBaseSpawner.ssh_custom_svc_remove = ssh_custom_svc_remove
- ssh_during_startup c.OutpostSpawner.ssh_during_startup = Union(False)#
An optional hook function, or boolean, you can implement to decide whether a ssh port forwarding process should be run after the POST request to the JupyterHub Outpost service.
Common Use Case: singleuser service was started remotely and is not accessible by JupyterHub (e.g. it’s running on a different K8s Cluster), but you know exactly where it is (e.g. the service address).
Example:
def ssh_during_startup(spawner): if spawner.user_options.get("system", "") == "A": return True return False c.ForwardBaseSpawner.ssh_during_startup = ssh_during_startup
- ssh_forward_options c.OutpostSpawner.ssh_forward_options = Union()#
An optional hook, or dict, to configure the ssh commands used in the spawner.ssh_default_forward function. The default configuration parameters (see below) can be overridden.
Default:
ssh_forward_options_all = { "ServerAliveInterval": "15", "StrictHostKeyChecking": "accept-new", "ControlMaster": "auto", "ControlPersist": "yes", "Port": str(ssh_port), "ControlPath": f"/tmp/control_{ssh_address_or_host}", "IdentityFile": ssh_pkey, }
- ssh_forward_remote_options c.OutpostSpawner.ssh_forward_remote_options = Union()#
An optional hook, or dict, to configure the ssh commands used in the spawner.ssh_default_forward function. The default configuration parameters (see below) can be overriden.
Default:
ssh_forward_remote_options_all = { "StrictHostKeyChecking": "accept-new", "Port": str(ssh_port), "ControlPath": f"/tmp/control_{ssh_address_or_host}", }
- ssh_key c.OutpostSpawner.ssh_key = Union('/home/jovyan/.ssh/id_rsa')#
An optional hook function, or string, you can implement to set the ssh privatekey used for ssh port forwarding.
This may be a coroutine.
Example:
def ssh_key(spawner): if spawner.user_options.get("system", "") == "A": return "/mnt/private_keys/a" return "/mnt/private_keys/b" c.ForwardBaseSpawner.ssh_key = ssh_key
- ssh_node c.OutpostSpawner.ssh_node = Union(None)#
An optional hook function, or string, you can implement to set the ssh node used for ssh port forwarding.
This may be a coroutine.
Example:
def ssh_node(spawner): if spawner.user_options.get("system", "") == "A": return "outpost.namespace.svc" else: return "<public_ip>" c.ForwardBaseSpawner.ssh_node = ssh_node
- ssh_node_mapping c.OutpostSpawner.ssh_node_mapping = Callable(None)#
An optional hook function, you can implement to set the map the given ssh node to a different avlue.
This may be a coroutine.
Example:
def ssh_node_mapping(spawner, ssh_node): if ssh_node == "<internal_hostname>": return "<external_dns_name>" return ssh_node c.ForwardBaseSpawner.ssh_node_mapping = ssh_node_mapping
- ssh_port c.OutpostSpawner.ssh_port = Union(22)#
An optional hook function, or string, you can implement to set the ssh port used for ssh port forwarding.
This may be a coroutine.
Example:
def ssh_port(spawner): if spawner.user_options.get("system", "") == "A": return 22 else: return 2222 c.ForwardBaseSpawner.ssh_port = ssh_port
- ssh_recreate_at_start c.OutpostSpawner.ssh_recreate_at_start = Union(False)#
Whether ssh tunnels should be recreated when JupyterHub starts or not. If you have outsourced the port forwarding to an extra pod, you can set this to false. Outsourcing also means, that connections to running JupyterLabs are not affected by JupyterHub restarts.
This may be a coroutine.
- ssh_remote_key c.OutpostSpawner.ssh_remote_key = Union('/home/jovyan/.ssh/id_rsa_remote')#
An optional hook function, or string, you can implement to set the ssh privatekey used for ssh port forwarding remote.
This may be a coroutine.
Example:
def ssh_remote_key(spawner): if spawner.user_options.get("system", "") == "A": return "/mnt/private_keys/a" return "/mnt/private_keys/b" c.ForwardBaseSpawner.ssh_remote_key = ssh_remote_key
- ssh_remote_node c.OutpostSpawner.ssh_remote_node = Union(None)#
An optional hook function, or string, you can implement to set the ssh node used for ssh port forwarding remote.
This may be a coroutine.
Example:
def ssh_node(spawner): if spawner.user_options.get("system", "") == "A": return "outpost.namespace.svc" else: return "<public_ip>" c.ForwardBaseSpawner.ssh_remote_node = ssh_node
- ssh_remote_port c.OutpostSpawner.ssh_remote_port = Union(22)#
An optional hook function, or string, you can implement to set the ssh port used for ssh port forwarding remote.
This may be a coroutine.
Example:
def ssh_port(spawner): if spawner.user_options.get("system", "") == "A": return 22 else: return 2222 c.ForwardBaseSpawner.ssh_remote_port = ssh_port
- ssh_remote_username c.OutpostSpawner.ssh_remote_username = Union('jhuboutpost')#
An optional hook function, or string, you can implement to set the ssh username used for ssh port forwarding remote.
This may be a coroutine.
Example:
def ssh_username(spawner): if spawner.user_options.get("system", "") == "A": return "jhuboutpost" return "ubuntu" c.ForwardBaseSpawner.ssh_remote_username = ssh_username
- ssh_username c.OutpostSpawner.ssh_username = Union('jhuboutpost')#
An optional hook function, or string, you can implement to set the ssh username used for ssh port forwarding.
This may be a coroutine.
Example:
def ssh_username(spawner): if spawner.user_options.get("system", "") == "A": return "jhuboutpost" return "ubuntu" c.ForwardBaseSpawner.ssh_username = ssh_username
- start_async c.OutpostSpawner.start_async = Union(False)#
Whether the start at the Outpost service should run in the background or not. Can be a boolean or a function.
May be a coroutine.
- stop_async c.OutpostSpawner.stop_async = Union(False)#
Whether the stop at the Outpost service should run in the background or not. Can be a boolean or a function.
May be a coroutine.
- svc_create c.OutpostSpawner.svc_create = Union(True)#
An optional hook function, or boolean, you can implement to disable the svc creation.
This may be a coroutine.
Example:
async def svc_create(spawner): if spawner.user_options.get("system", "") == "A": return False else: return True c.ForwardBaseSpawner.svc_create = svc_create
- svc_name_template c.OutpostSpawner.svc_name_template = Unicode('jupyter-{username}--{servername}')#
Template to use to form the name of user’s pods.
{username},{userid},{servername},{hubnamespace},{unescaped_username}, and{unescaped_servername}will be expanded if found within strings of this configuration. The username and servername come escaped to follow the DNS label standard.Trailing
-characters are stripped for safe handling of empty server names (user default servers).This must be unique within the namespace the pods are being spawned in, so if you are running multiple jupyterhubs spawning in the same namespace, consider setting this to be something more unique.
- update_expected_path c.OutpostSpawner.update_expected_path = Any(False)#
Hook which allows to update the return value of Spawner.start().after starting ssh forwards. Result used by JupyterHub to look for Jupyter Server
Callable function, may be a coroutine.
- update_start_response c.OutpostSpawner.update_start_response = Any(False)#
Hook which allows to update the return value of Spawner.start() before starting ssh forwards..
Callable function, may be a coroutine.