Skip to content

Commit d07a613

Browse files
committed
feat: perf improvements for amalthea sessions (#411)
1 parent eb47f2d commit d07a613

File tree

22 files changed

+256
-120
lines changed

22 files changed

+256
-120
lines changed

components/renku_data_services/app_config/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
from renku_data_services.message_queue.interface import IMessageQueue
5959
from renku_data_services.message_queue.redis_queue import RedisQueue
6060
from renku_data_services.namespace.db import GroupRepository
61-
from renku_data_services.notebooks.config import _NotebooksConfig
61+
from renku_data_services.notebooks.config import NotebooksConfig
6262
from renku_data_services.platform.db import PlatformRepository
6363
from renku_data_services.project.db import ProjectMemberRepository, ProjectRepository
6464
from renku_data_services.repositories.db import GitRepositoriesRepository
@@ -151,7 +151,7 @@ class Config:
151151
kc_api: IKeycloakAPI
152152
message_queue: IMessageQueue
153153
gitlab_url: str | None
154-
nb_config: _NotebooksConfig
154+
nb_config: NotebooksConfig
155155

156156
secrets_service_public_key: rsa.RSAPublicKey
157157
"""The public key of the secrets service, used to encrypt user secrets that only it can decrypt."""
@@ -544,7 +544,7 @@ def from_env(cls, prefix: str = "") -> "Config":
544544
sentry = SentryConfig.from_env(prefix)
545545
trusted_proxies = TrustedProxiesConfig.from_env(prefix)
546546
message_queue = RedisQueue(redis)
547-
nb_config = _NotebooksConfig.from_env(db)
547+
nb_config = NotebooksConfig.from_env(db)
548548

549549
return cls(
550550
version=version,

components/renku_data_services/base_models/core.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ def is_authenticated(self) -> bool:
3131
"""Indicates whether the user has successfully logged in."""
3232
return self.id is not None
3333

34+
@property
35+
def is_anonymous(self) -> bool:
36+
"""Indicates whether the user is anonymous."""
37+
return isinstance(self, AnonymousAPIUser)
38+
3439
def get_full_name(self) -> str | None:
3540
"""Generate the closest thing to a full name if the full name field is not set."""
3641
full_name = self.full_name or " ".join(filter(None, (self.first_name, self.last_name)))

components/renku_data_services/crc/db.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,13 @@ async def get_classes(
292292
orms = res.scalars().all()
293293
return [orm.dump() for orm in orms]
294294

295+
async def get_resource_class(self, api_user: base_models.APIUser, id: int) -> models.ResourceClass:
296+
"""Get a specific resource class by its ID."""
297+
classes = await self.get_classes(api_user, id)
298+
if len(classes) == 0:
299+
raise errors.MissingResourceError(message=f"The resource class with ID {id} cannot be found", quiet=True)
300+
return classes[0]
301+
295302
@_only_admins
296303
async def insert_resource_class(
297304
self,

components/renku_data_services/migrations/versions/584598f3b769_expand_and_separate_environments_from_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""expand and separate environments from session launchers
22
33
Revision ID: 584598f3b769
4-
Revises: 9058bf0a1a12
4+
Revises: 726d5d0e1f28
55
Create Date: 2024-08-12 14:25:24.292285
66
77
"""
@@ -12,7 +12,7 @@
1212

1313
# revision identifiers, used by Alembic.
1414
revision = "584598f3b769"
15-
down_revision = "9058bf0a1a12"
15+
down_revision = "726d5d0e1f28"
1616
branch_labels = None
1717
depends_on = None
1818

components/renku_data_services/notebooks/api/amalthea_patches/git_proxy.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,53 @@
66

77
from kubernetes import client
88

9+
from renku_data_services.base_models.core import AnonymousAPIUser, AuthenticatedAPIUser
910
from renku_data_services.notebooks.api.amalthea_patches.utils import get_certificates_volume_mounts
11+
from renku_data_services.notebooks.api.classes.repository import GitProvider, Repository
12+
from renku_data_services.notebooks.config import NotebooksConfig
1013

1114
if TYPE_CHECKING:
1215
# NOTE: If these are directly imported then you get circular imports.
1316
from renku_data_services.notebooks.api.classes.server import UserServer
1417

1518

16-
async def main_container(server: "UserServer") -> client.V1Container | None:
19+
async def main_container(
20+
user: AnonymousAPIUser | AuthenticatedAPIUser,
21+
config: NotebooksConfig,
22+
repositories: list[Repository],
23+
git_providers: list[GitProvider],
24+
) -> client.V1Container | None:
1725
"""The patch that adds the git proxy container to a session statefulset."""
18-
repositories = await server.repositories()
19-
if not server.user.is_authenticated or not repositories:
26+
if not user.is_authenticated or not repositories or user.access_token is None or user.refresh_token is None:
2027
return None
2128

2229
etc_cert_volume_mount = get_certificates_volume_mounts(
23-
server.config,
30+
config,
2431
custom_certs=False,
2532
etc_certs=True,
2633
read_only_etc_certs=True,
2734
)
2835

2936
prefix = "GIT_PROXY_"
30-
git_providers = await server.git_providers()
31-
repositories = await server.repositories()
3237
env = [
33-
client.V1EnvVar(name=f"{prefix}PORT", value=str(server.config.sessions.git_proxy.port)),
34-
client.V1EnvVar(name=f"{prefix}HEALTH_PORT", value=str(server.config.sessions.git_proxy.health_port)),
38+
client.V1EnvVar(name=f"{prefix}PORT", value=str(config.sessions.git_proxy.port)),
39+
client.V1EnvVar(name=f"{prefix}HEALTH_PORT", value=str(config.sessions.git_proxy.health_port)),
3540
client.V1EnvVar(
3641
name=f"{prefix}ANONYMOUS_SESSION",
37-
value="false" if server.user.is_authenticated else "true",
42+
value="false" if user.is_authenticated else "true",
3843
),
39-
client.V1EnvVar(name=f"{prefix}RENKU_ACCESS_TOKEN", value=str(server.user.access_token)),
40-
client.V1EnvVar(name=f"{prefix}RENKU_REFRESH_TOKEN", value=str(server.user.refresh_token)),
41-
client.V1EnvVar(name=f"{prefix}RENKU_REALM", value=server.config.keycloak_realm),
44+
client.V1EnvVar(name=f"{prefix}RENKU_ACCESS_TOKEN", value=str(user.access_token)),
45+
client.V1EnvVar(name=f"{prefix}RENKU_REFRESH_TOKEN", value=str(user.refresh_token)),
46+
client.V1EnvVar(name=f"{prefix}RENKU_REALM", value=config.keycloak_realm),
4247
client.V1EnvVar(
4348
name=f"{prefix}RENKU_CLIENT_ID",
44-
value=str(server.config.sessions.git_proxy.renku_client_id),
49+
value=str(config.sessions.git_proxy.renku_client_id),
4550
),
4651
client.V1EnvVar(
4752
name=f"{prefix}RENKU_CLIENT_SECRET",
48-
value=str(server.config.sessions.git_proxy.renku_client_secret),
53+
value=str(config.sessions.git_proxy.renku_client_secret),
4954
),
50-
client.V1EnvVar(name=f"{prefix}RENKU_URL", value="https://" + server.config.sessions.ingress.host),
55+
client.V1EnvVar(name=f"{prefix}RENKU_URL", value="https://" + config.sessions.ingress.host),
5156
client.V1EnvVar(
5257
name=f"{prefix}REPOSITORIES",
5358
value=json.dumps([asdict(repo) for repo in repositories]),
@@ -60,7 +65,7 @@ async def main_container(server: "UserServer") -> client.V1Container | None:
6065
),
6166
]
6267
container = client.V1Container(
63-
image=server.config.sessions.git_proxy.image,
68+
image=config.sessions.git_proxy.image,
6469
security_context={
6570
"fsGroup": 100,
6671
"runAsGroup": 1000,
@@ -73,14 +78,14 @@ async def main_container(server: "UserServer") -> client.V1Container | None:
7378
liveness_probe={
7479
"httpGet": {
7580
"path": "/health",
76-
"port": server.config.sessions.git_proxy.health_port,
81+
"port": config.sessions.git_proxy.health_port,
7782
},
7883
"initialDelaySeconds": 3,
7984
},
8085
readiness_probe={
8186
"httpGet": {
8287
"path": "/health",
83-
"port": server.config.sessions.git_proxy.health_port,
88+
"port": config.sessions.git_proxy.health_port,
8489
},
8590
"initialDelaySeconds": 3,
8691
},
@@ -98,7 +103,8 @@ async def main(server: "UserServer") -> list[dict[str, Any]]:
98103
if not server.user.is_authenticated or not repositories:
99104
return []
100105

101-
container = await main_container(server)
106+
git_providers = await server.git_providers()
107+
container = await main_container(server.user, server.config, repositories, git_providers)
102108
if not container:
103109
return []
104110

components/renku_data_services/notebooks/api/amalthea_patches/init_containers.py

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,72 +3,77 @@
33
import json
44
import os
55
from dataclasses import asdict
6-
from pathlib import Path
6+
from pathlib import Path, PurePosixPath
77
from typing import TYPE_CHECKING, Any
88

99
from kubernetes import client
1010

11+
from renku_data_services.base_models.core import AnonymousAPIUser, AuthenticatedAPIUser
1112
from renku_data_services.notebooks.api.amalthea_patches.utils import get_certificates_volume_mounts
12-
from renku_data_services.notebooks.config import _NotebooksConfig
13+
from renku_data_services.notebooks.api.classes.repository import GitProvider, Repository
14+
from renku_data_services.notebooks.config import NotebooksConfig
1315

1416
if TYPE_CHECKING:
1517
# NOTE: If these are directly imported then you get circular imports.
1618
from renku_data_services.notebooks.api.classes.server import UserServer
1719

1820

19-
async def git_clone_container_v2(server: "UserServer") -> dict[str, Any] | None:
21+
async def git_clone_container_v2(
22+
user: AuthenticatedAPIUser | AnonymousAPIUser,
23+
config: NotebooksConfig,
24+
repositories: list[Repository],
25+
git_providers: list[GitProvider],
26+
workspace_mount_path: PurePosixPath,
27+
work_dir: PurePosixPath,
28+
lfs_auto_fetch: bool = False,
29+
) -> dict[str, Any] | None:
2030
"""Returns the specification for the container that clones the user's repositories for new operator."""
2131
amalthea_session_work_volume: str = "amalthea-volume"
22-
repositories = await server.repositories()
2332
if not repositories:
2433
return None
2534

2635
etc_cert_volume_mount = get_certificates_volume_mounts(
27-
server.config,
36+
config,
2837
custom_certs=False,
2938
etc_certs=True,
3039
read_only_etc_certs=True,
3140
)
3241

33-
user_is_anonymous = not server.user.is_authenticated
3442
prefix = "GIT_CLONE_"
3543
env = [
36-
{
37-
"name": f"{prefix}WORKSPACE_MOUNT_PATH",
38-
"value": server.workspace_mount_path.as_posix(),
39-
},
44+
{"name": f"{prefix}WORKSPACE_MOUNT_PATH", "value": workspace_mount_path.as_posix()},
4045
{
4146
"name": f"{prefix}MOUNT_PATH",
42-
"value": server.work_dir.as_posix(),
47+
"value": work_dir.as_posix(),
4348
},
4449
{
4550
"name": f"{prefix}LFS_AUTO_FETCH",
46-
"value": "1" if server.server_options.lfs_auto_fetch else "0",
51+
"value": "1" if lfs_auto_fetch else "0",
4752
},
4853
{
4954
"name": f"{prefix}USER__USERNAME",
50-
"value": server.user.email,
55+
"value": user.email,
5156
},
5257
{
5358
"name": f"{prefix}USER__RENKU_TOKEN",
54-
"value": str(server.user.access_token),
59+
"value": str(user.access_token),
5560
},
56-
{"name": f"{prefix}IS_GIT_PROXY_ENABLED", "value": "0" if user_is_anonymous else "1"},
61+
{"name": f"{prefix}IS_GIT_PROXY_ENABLED", "value": "0" if user.is_anonymous else "1"},
5762
{
5863
"name": f"{prefix}SENTRY__ENABLED",
59-
"value": str(server.config.sessions.git_clone.sentry.enabled).lower(),
64+
"value": str(config.sessions.git_clone.sentry.enabled).lower(),
6065
},
6166
{
6267
"name": f"{prefix}SENTRY__DSN",
63-
"value": server.config.sessions.git_clone.sentry.dsn,
68+
"value": config.sessions.git_clone.sentry.dsn,
6469
},
6570
{
6671
"name": f"{prefix}SENTRY__ENVIRONMENT",
67-
"value": server.config.sessions.git_clone.sentry.env,
72+
"value": config.sessions.git_clone.sentry.env,
6873
},
6974
{
7075
"name": f"{prefix}SENTRY__SAMPLE_RATE",
71-
"value": str(server.config.sessions.git_clone.sentry.sample_rate),
76+
"value": str(config.sessions.git_clone.sentry.sample_rate),
7277
},
7378
{"name": "SENTRY_RELEASE", "value": os.environ.get("SENTRY_RELEASE")},
7479
{
@@ -80,12 +85,12 @@ async def git_clone_container_v2(server: "UserServer") -> dict[str, Any] | None:
8085
"value": str(Path(etc_cert_volume_mount[0]["mountPath"]) / "ca-certificates.crt"),
8186
},
8287
]
83-
if server.user.is_authenticated:
84-
if server.user.email:
88+
if user.is_authenticated:
89+
if user.email:
8590
env.append(
86-
{"name": f"{prefix}USER__EMAIL", "value": server.user.email},
91+
{"name": f"{prefix}USER__EMAIL", "value": user.email},
8792
)
88-
full_name = server.user.get_full_name()
93+
full_name = user.get_full_name()
8994
if full_name:
9095
env.append(
9196
{
@@ -105,7 +110,8 @@ async def git_clone_container_v2(server: "UserServer") -> dict[str, Any] | None:
105110
)
106111

107112
# Set up git providers
108-
required_git_providers = await server.required_git_providers()
113+
required_provider_ids: set[str] = {r.provider for r in repositories if r.provider}
114+
required_git_providers = [p for p in git_providers if p.id in required_provider_ids]
109115
for idx, provider in enumerate(required_git_providers):
110116
obj_env = f"{prefix}GIT_PROVIDERS_{idx}_"
111117
data = dict(id=provider.id, access_token_url=provider.access_token_url)
@@ -117,7 +123,7 @@ async def git_clone_container_v2(server: "UserServer") -> dict[str, Any] | None:
117123
)
118124

119125
return {
120-
"image": server.config.sessions.git_clone.image,
126+
"image": config.sessions.git_clone.image,
121127
"name": "git-clone",
122128
"resources": {
123129
"requests": {
@@ -134,7 +140,7 @@ async def git_clone_container_v2(server: "UserServer") -> dict[str, Any] | None:
134140
},
135141
"volumeMounts": [
136142
{
137-
"mountPath": server.workspace_mount_path.as_posix(),
143+
"mountPath": workspace_mount_path.as_posix(),
138144
"name": amalthea_session_work_volume,
139145
},
140146
*etc_cert_volume_mount,
@@ -156,7 +162,6 @@ async def git_clone_container(server: "UserServer") -> dict[str, Any] | None:
156162
read_only_etc_certs=True,
157163
)
158164

159-
user_is_anonymous = not server.user.is_authenticated
160165
prefix = "GIT_CLONE_"
161166
env = [
162167
{
@@ -179,7 +184,7 @@ async def git_clone_container(server: "UserServer") -> dict[str, Any] | None:
179184
"name": f"{prefix}USER__RENKU_TOKEN",
180185
"value": str(server.user.access_token),
181186
},
182-
{"name": f"{prefix}IS_GIT_PROXY_ENABLED", "value": "0" if user_is_anonymous else "1"},
187+
{"name": f"{prefix}IS_GIT_PROXY_ENABLED", "value": "0" if server.user.is_anonymous else "1"},
183188
{
184189
"name": f"{prefix}SENTRY__ENABLED",
185190
"value": str(server.config.sessions.git_clone.sentry.enabled).lower(),
@@ -288,7 +293,7 @@ async def git_clone(server: "UserServer") -> list[dict[str, Any]]:
288293
]
289294

290295

291-
def certificates_container(config: _NotebooksConfig) -> tuple[client.V1Container, list[client.V1Volume]]:
296+
def certificates_container(config: NotebooksConfig) -> tuple[client.V1Container, list[client.V1Volume]]:
292297
"""The specification for the container that setups self signed CAs."""
293298
init_container = client.V1Container(
294299
name="init-certificates",
@@ -321,7 +326,7 @@ def certificates_container(config: _NotebooksConfig) -> tuple[client.V1Container
321326
return (init_container, [volume_etc_certs, volume_custom_certs])
322327

323328

324-
def certificates(config: _NotebooksConfig) -> list[dict[str, Any]]:
329+
def certificates(config: NotebooksConfig) -> list[dict[str, Any]]:
325330
"""Add a container that initializes custom certificate authorities for a session."""
326331
container, vols = certificates_container(config)
327332
api_client = client.ApiClient()

components/renku_data_services/notebooks/api/amalthea_patches/ssh.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from typing import Any
44

5-
from renku_data_services.notebooks.config import _NotebooksConfig
5+
from renku_data_services.notebooks.config import NotebooksConfig
66

77

8-
def main(config: _NotebooksConfig) -> list[dict[str, Any]]:
8+
def main(config: NotebooksConfig) -> list[dict[str, Any]]:
99
"""Adds the required configuration to the session statefulset for SSH access."""
1010
if not config.sessions.ssh.enabled:
1111
return []

components/renku_data_services/notebooks/api/amalthea_patches/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
from kubernetes import client
66

7-
from renku_data_services.notebooks.config import _NotebooksConfig
7+
from renku_data_services.notebooks.config import NotebooksConfig
88

99

1010
def get_certificates_volume_mounts(
11-
config: _NotebooksConfig,
11+
config: NotebooksConfig,
1212
etc_certs: bool = True,
1313
custom_certs: bool = True,
1414
read_only_etc_certs: bool = False,

0 commit comments

Comments
 (0)