Skip to content

Commit

Permalink
Add support for disallowing notebook spawns
Browse files Browse the repository at this point in the history
Add a flag to the notebook quota indicating whether the user should be
allowed to spawn a notebook, defaulting to true. This may be set to
false for a specific group (or, later, a quota override) to indicate
that user should not be allowed to spawn notebooks.

This field is calculated and returned by Gafaelfawr but is not enforced
by it. It will be read by Nublado (specifically the Nublado controller).
  • Loading branch information
rra committed Jan 15, 2025
1 parent 30d7ed3 commit 2308a2b
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 3 deletions.
3 changes: 3 additions & 0 deletions changelog.d/20250114_155651_rra_DM_48390.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### New features

- Add a flag to notebook quotas, defaulting to true, that indicates whether the user is allowed to spawn a new lab. This is not enforced by Gafaelfawr; it will be read and acted on by [Nublado](https://nublado.lsst.io).
14 changes: 13 additions & 1 deletion docs/user-guide/helm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -523,14 +523,26 @@ Here is an example:
groups:
g_developers:
notebook:
cpu: 8.0
cpu: 0.0
memory: 4.0
g_limited:
notebook:
cpu: 0.0
memory: 0.0
spawn: false
bypass:
- "g_admins"
API quotas are in requests per 15 minutes.
Notebook quotas are in CPU equivalents and GiB of memory.
If spawn is set to false, users should not be allowed to spawn a new user notebook.
Members of groups listed in ``bypass`` ignore all quota restrictions.

The above example sets an API quota for the ``datalinker`` service of 1000 requests per 15 minutes, and a default quota for user notebooks of 2.0 CPU equivalents and 4.0GiB of memory.
Users who are members of the ``g_developers`` group get an additional 4.0GiB of memory for their notebooks.
Users who are members of the ``g_limited`` group are not allowed to spawn notebooks.
(Note that the CPU and memory quota additions must be specified, even if they are zero.)
Users who are members of the ``g_admins`` group ignore all quota restrictions.

The keys for API quotas are names of services.
This is the same name the service should use in the ``config.service`` key of a ``GafaelfawrIngress`` resource (see :ref:`ingress`).
Expand Down
1 change: 1 addition & 0 deletions src/gafaelfawr/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ def calculate_quota(self, groups: set[str]) -> Quota | None:
if notebook:
notebook.cpu += extra.notebook.cpu
notebook.memory += extra.notebook.memory
notebook.spawn &= extra.notebook.spawn
else:
notebook = extra.notebook.model_copy()
for service, quota in extra.api.items():
Expand Down
6 changes: 6 additions & 0 deletions src/gafaelfawr/models/userinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ class NotebookQuota(BaseModel):
..., title="Maximum memory use (GiB)", examples=[16.0]
)

spawn: bool = Field(
True,
title="Spawning allowed",
description="Whether the user is allowed to spawn a notebook",
)


class Quota(BaseModel):
"""Quota information for a user."""
Expand Down
5 changes: 5 additions & 0 deletions tests/data/config/github-quota.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ quota:
cpu: 8
memory: 4.0
groups:
blocked:
notebook:
cpu: 0
memory: 0
spawn: false
foo:
api:
test: 1
Expand Down
35 changes: 33 additions & 2 deletions tests/handlers/quota_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from gafaelfawr.models.userinfo import Group

from ..support.config import reconfigure
from ..support.tokens import create_session_token


@pytest.mark.asyncio
Expand All @@ -32,7 +33,7 @@ async def test_info(client: AsyncClient, factory: Factory) -> None:
"groups": [{"name": "bar", "id": 12312}],
"quota": {
"api": {"datalinker": 1000, "test": 1},
"notebook": {"cpu": 8.0, "memory": 4.0},
"notebook": {"cpu": 8.0, "memory": 4.0, "spawn": True},
},
}

Expand All @@ -50,6 +51,36 @@ async def test_info(client: AsyncClient, factory: Factory) -> None:
"groups": [{"name": "foo", "id": 12313}],
"quota": {
"api": {"datalinker": 1000, "test": 2},
"notebook": {"cpu": 8.0, "memory": 8.0},
"notebook": {"cpu": 8.0, "memory": 8.0, "spawn": True},
},
}


@pytest.mark.asyncio
async def test_no_spawn(client: AsyncClient, factory: Factory) -> None:
await reconfigure("github-quota", factory)
token_data = await create_session_token(
factory, group_names=["blocked", "bar"], scopes={"read:all"}
)
assert token_data.groups

r = await client.get(
"/auth/api/v1/user-info",
headers={"Authorization": f"bearer {token_data.token}"},
)
assert r.status_code == 200
assert r.json() == {
"username": token_data.username,
"name": token_data.name,
"email": token_data.email,
"uid": token_data.uid,
"gid": token_data.gid,
"groups": [
g.model_dump(mode="json")
for g in sorted(token_data.groups, key=lambda g: g.name)
],
"quota": {
"api": {"datalinker": 1000, "test": 1},
"notebook": {"cpu": 8.0, "memory": 4.0, "spawn": False},
},
}

0 comments on commit 2308a2b

Please sign in to comment.