OutpostSpawner

Contents

OutpostSpawner#

class outpostspawner.OutpostSpawner(**kwargs: Any)#

A JupyterHub spawner that spawns services on remote locations in combination with a JupyterHub Outpost service.

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
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. key can 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:latest image.

custom_misc_disable_default c.OutpostSpawner.custom_misc_disable_default = Bool(False)#

By default, these misc options 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_misc options:

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(8080)#

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
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"
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
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_URL in env. Default value is the default internal JUPYTERHUB_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_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('jupyterhuboutpost')#

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 "jupyterhuboutpost"
    return "ubuntu"

c.ForwardBaseSpawner.ssh_remote_username = ssh_username
ssh_username c.OutpostSpawner.ssh_username = Union('jupyterhuboutpost')#

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 "jupyterhuboutpost"
    return "ubuntu"

c.ForwardBaseSpawner.ssh_username = ssh_username
start_async c.OutpostSpawner.start_async = Bool(False)#

Whether the start at the Outpost service should run in the background or not.

stop_async c.OutpostSpawner.stop_async = Bool(False)#

Whether the stop at the Outpost service should run in the background or not.

stop_event c.OutpostSpawner.stop_event = Union({'failed': True, 'ready': False, 'progress': 100, 'message': '', 'html_message': 'JupyterLab was stopped.'})#

Event shown when single-user server was stopped.

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().

Callable function, may be a coroutine.