LabManager#

class controller.services.lab.LabManager(*, config, image_service, lab_builder, metadata_storage, lab_storage, slack_client, logger)#

Bases: object

Manage user labs.

The lab manager is a process singleton that manages user labs. This includes spawning them, deleting them, and tracking their state.

Parameters:
  • config (LabConfig) – Configuration for user labs.

  • image_service (ImageService) – Tracks all available images and resolves the parameters of a request for a new lab to a specific Docker image.

  • lab_builder (LabBuilder) – Builder for Kubernetes lab objects.

  • metadata_storage (MetadataStorage) – Storage for metadata about the running controller.

  • lab_storage (LabStorage) – Kubernetes storage layer for user labs.

  • slack_client (SlackWebhookClient | None) – Optional Slack webhook client for alerts.

  • logger (BoundLogger) – Logger to use.

Methods Summary

create_lab(user, spec)

Schedules creation of user lab objects/resources.

delete_lab(username)

Delete the lab environment for the given user.

events_for_user(username)

Construct an iterator over the events for a user.

get_lab_state(username)

Get lab state for a user.

list_lab_users(*[, only_running])

List all users with labs.

reap_spawners()

Wait for spawner tasks to complete and record their status.

reconcile()

Reconcile user lab state with Kubernetes.

stop_monitor_tasks()

Stop any tasks that are waiting for labs to spawn.

Methods Documentation

async create_lab(user, spec)#

Schedules creation of user lab objects/resources.

Parameters:
Raises:
Return type:

None

async delete_lab(username)#

Delete the lab environment for the given user.

This may be called multiple times for the same user, and all deletions will wait for the same underlying Kubernetes operation.

Parameters:

username (str) – Username whose environment should be deleted.

Raises:
Return type:

None

events_for_user(username)#

Construct an iterator over the events for a user.

Parameters:

username (str) – Username for which to retrieve events.

Yields:

bytes – Next encoded server-sent event.

Raises:

UnknownUserError – Raised if there is no event stream for this user.

Return type:

AsyncIterator[bytes]

async get_lab_state(username)#

Get lab state for a user.

This method underlies the API called by JupyterHub to track whether the user’s lab still exists. We want to update that state after Kubernetes changes faster than our reconciliation cycle, so ask Kubernetes directly for the pod phase each time we’re asked for the lab state.

Parameters:

username (str) – Username to retrieve lab state for.

Returns:

Lab state for that user, or None if that user doesn’t have a lab.

Return type:

LabState or None

Raises:

UnknownUserError – Raised if the given user has no lab.

async list_lab_users(*, only_running=False)#

List all users with labs.

Parameters:

only_running (bool, default: False) – If set to True, only list users with running labs, not labs in other states.

Returns:

Users with labs.

Return type:

list of str

async reap_spawners()#

Wait for spawner tasks to complete and record their status.

When a user spawns a lab, the lab controller creates a background task to create the Kubernetes objects and then wait for the pod to finish starting. Something needs to await those tasks so that they can be cleanly finalized and to catch any uncaught exceptions. That function is performed by a background task running this method.

This method is also responsible for updating the last modified time in the lab state.

Return type:

None

Notes

Doing this properly is a bit tricky, since we have to avoid both busy-waiting when no operations are in progress and not reaping anything if one operation keeps running forever. The approach used here is to have every spawn set the _spawner_done asyncio.Event when it is complete, and use that as a trigger for doing a reaper pass. Deletes do not do this since they’re normally awaited by the caller and thus don’t need to be reaped separately.

async reconcile()#

Reconcile user lab state with Kubernetes.

This method is called on startup and then periodically from a background thread to check Kubernetes and ensure the in-memory record of the user’s lab state matches reality. On startup, it also needs to recreate the internal state from the contents of Kubernetes.

Raises:

KubernetesError – Raised if there is some failure in a Kubernetes API call.

Return type:

None

async stop_monitor_tasks()#

Stop any tasks that are waiting for labs to spawn. :rtype: None