Skip to content

Commit

Permalink
Merge pull request #1216 from lsst-sqre:tickets/DM-48390
Browse files Browse the repository at this point in the history
DM-48390: Add support for bypassing quota
  • Loading branch information
rra authored Jan 14, 2025
2 parents edb8b49 + 5f91b42 commit 27d25e0
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 17 deletions.
3 changes: 3 additions & 0 deletions changelog.d/20250114_135624_rra_DM_48390.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### New features

- Add a `bypass` key to the quota configuration containing a group list. Any member of one of those groups ignores all quota restrictions.
6 changes: 6 additions & 0 deletions src/gafaelfawr/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,12 @@ class QuotaConfig(BaseModel):
description="Additional quota grants by group name",
)

bypass: set[str] = Field(
set(),
title="Groups without quotas",
description="Groups whose members bypass all quota restrictions",
)


class GitHubGroupTeam(BaseModel):
"""Specification for a GitHub team."""
Expand Down
44 changes: 27 additions & 17 deletions src/gafaelfawr/services/userinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,30 +225,40 @@ def _calculate_quota(self, groups: list[Group]) -> Quota | None:
"""
if not self._config.quota:
return None
group_names = {g.name for g in groups}
if group_names & self._config.quota.bypass:
return Quota()

# Start with the defaults.
api = dict(self._config.quota.default.api)
notebook = None
if self._config.quota.default.notebook:
notebook = NotebookQuota(
cpu=self._config.quota.default.notebook.cpu,
memory=self._config.quota.default.notebook.memory,
)
for group in groups or []:
if group.name in self._config.quota.groups:
extra = self._config.quota.groups[group.name]
if extra.notebook:
if notebook:
notebook.cpu += extra.notebook.cpu
notebook.memory += extra.notebook.memory
else:
notebook = NotebookQuota(
cpu=extra.notebook.cpu,
memory=extra.notebook.memory,
)
for service in extra.api:
if service in api:
api[service] += extra.api[service]
else:
api[service] = extra.api[service]

# Look for group-specific rules.
for group in group_names:
if group not in self._config.quota.groups:
continue
extra = self._config.quota.groups[group]
if extra.notebook:
if notebook:
notebook.cpu += extra.notebook.cpu
notebook.memory += extra.notebook.memory
else:
notebook = NotebookQuota(
cpu=extra.notebook.cpu,
memory=extra.notebook.memory,
)
for service in extra.api:
if service in api:
api[service] += extra.api[service]
else:
api[service] = extra.api[service]

# Return the results.
return Quota(api=api, notebook=notebook)

async def _get_groups_from_ldap(
Expand Down
2 changes: 2 additions & 0 deletions tests/data/config/github-quota.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ quota:
notebook:
cpu: 0.0
memory: 4.0
bypass:
- "admin"
metrics:
enabled: false
application: "gafaelfawr"
23 changes: 23 additions & 0 deletions tests/handlers/ingress_rate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,26 @@ async def test_rate_limit(client: AsyncClient, factory: Factory) -> None:
assert r.headers["X-RateLimit-Resource"] == "test"
reset = int(r.headers["X-RateLimit-Reset"])
assert expected.timestamp() <= reset <= expected.timestamp() + 5


@pytest.mark.asyncio
async def test_rate_limit_bypass(
client: AsyncClient, factory: Factory
) -> None:
await reconfigure("github-quota", factory)
token_data = await create_session_token(
factory, group_names=["admin"], scopes={"read:all"}
)
r = await client.get(
"/ingress/auth",
params={"scope": "read:all", "service": "test"},
headers={"Authorization": f"Bearer {token_data.token}"},
)
assert r.status_code == 200

# There should be no quota set since the user is in the bypass group.
assert "X-RateLimit-Limit" not in r.headers
assert "X-RateLimit-Remaining" not in r.headers
assert "X-RateLimit-Used" not in r.headers
assert "X-RateLimit-Resource" not in r.headers
assert "X-RateLimit-Reset" not in r.headers

0 comments on commit 27d25e0

Please sign in to comment.