Skip to content
61 changes: 43 additions & 18 deletions components/renku_data_services/notebooks/api.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ paths:
application/json:
schema:
"$ref": "#/components/schemas/ImageCheckResponse"
description: The image check has completed successfully
'422':
content:
application/json:
Expand Down Expand Up @@ -886,15 +887,13 @@ components:
launcher_id:
$ref: "#/components/schemas/Ulid"
disk_storage:
default: 1
type: integer
description: The size of disk storage for the session, in gigabytes
resource_class_id:
default:
nullable: true
type: integer
cloudstorage:
$ref: "#/components/schemas/SessionCloudStoragePostList"
data_connectors_overrides:
$ref: "#/components/schemas/SessionDataConnectorsOverrideList"
env_variable_overrides:
$ref: "#/components/schemas/EnvVariableOverrides"
required:
Expand Down Expand Up @@ -1015,28 +1014,54 @@ components:
minLength: 26
maxLength: 26
pattern: "^[0-7][0-9A-HJKMNP-TV-Z]{25}$"
SessionCloudStoragePostList:
SessionDataConnectorsOverrideList:
type: array
items:
"$ref": "#/components/schemas/SessionCloudStoragePost"
SessionCloudStoragePost:
$ref: "#/components/schemas/SessionDataConnectorOverride"
SessionDataConnectorOverride:
type: object
properties:
configuration:
type: object
additionalProperties: true
readonly:
skip:
type: boolean
description: The corresponding data connector will not be mounted if `skip` is set to `true`.
default: false
data_connector_id:
allOf:
- $ref: "#/components/schemas/Ulid"
- description: |
The `data_connector_id` must match an existing data connector from the session launcher's project.
configuration:
$ref: "#/components/schemas/RCloneConfig"
source_path:
type: string
$ref: "#/components/schemas/SourcePath"
target_path:
type: string
storage_id:
allOf:
- "$ref": "#/components/schemas/Ulid"
- description: If the storage_id is provided then this config must replace an existing storage config in the session
$ref: "#/components/schemas/TargetPath"
readonly:
$ref: "#/components/schemas/StorageReadOnly"
required:
- storage_id
- data_connector_id
RCloneConfig:
type: object
description: Dictionary of rclone key:value pairs (based on schema from '/storage_schema')
additionalProperties:
oneOf:
- type: integer
- type: string
nullable: true
- type: boolean
- type: object
SourcePath:
description: the source path to mount, usually starts with bucket/container name
type: string
example: bucket/my/storage/folder/
TargetPath:
description: the target path relative to the working directory where the storage should be mounted
type: string
example: my/project/folder
StorageReadOnly:
description: Whether this storage should be mounted readonly or not
type: boolean
default: true
ServerName:
type: string
minLength: 5
Expand Down
84 changes: 50 additions & 34 deletions components/renku_data_services/notebooks/apispec.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# generated by datamodel-codegen:
# filename: api.spec.yaml
# timestamp: 2025-09-12T06:56:48+00:00
# timestamp: 2025-10-15T12:41:50+00:00

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Union

from pydantic import ConfigDict, Field, RootModel
from renku_data_services.notebooks.apispec_base import BaseAPISpec
Expand Down Expand Up @@ -267,20 +267,6 @@ class SessionLogsResponse(RootModel[Optional[Dict[str, str]]]):
root: Optional[Dict[str, str]] = None


class SessionCloudStoragePost(BaseAPISpec):
configuration: Optional[Dict[str, Any]] = None
readonly: Optional[bool] = None
source_path: Optional[str] = None
target_path: Optional[str] = None
storage_id: str = Field(
...,
description="ULID identifier",
max_length=26,
min_length=26,
pattern="^[0-7][0-9A-HJKMNP-TV-Z]{25}$",
)


class ImageConnectionStatus(Enum):
connected = "connected"
pending = "pending"
Expand Down Expand Up @@ -368,6 +354,36 @@ class SessionResources(BaseAPISpec):
requests: Optional[SessionResourcesRequests] = None


class SessionDataConnectorOverride(BaseAPISpec):
skip: bool = Field(
False,
description="The corresponding data connector will not be mounted if `skip` is set to `true`.",
)
data_connector_id: str = Field(
...,
description="ULID identifier",
max_length=26,
min_length=26,
pattern="^[0-7][0-9A-HJKMNP-TV-Z]{25}$",
)
configuration: Optional[
Dict[str, Union[int, Optional[str], bool, Dict[str, Any]]]
] = None
source_path: Optional[str] = Field(
None,
description="the source path to mount, usually starts with bucket/container name",
examples=["bucket/my/storage/folder/"],
)
target_path: Optional[str] = Field(
None,
description="the target path relative to the working directory where the storage should be mounted",
examples=["my/project/folder"],
)
readonly: Optional[bool] = Field(
True, description="Whether this storage should be mounted readonly or not"
)


class ImageConnection(BaseAPISpec):
id: str
provider_id: str
Expand Down Expand Up @@ -396,24 +412,6 @@ class ServersGetResponse(BaseAPISpec):
servers: Optional[Dict[str, NotebookResponse]] = None


class SessionPostRequest(BaseAPISpec):
launcher_id: str = Field(
...,
description="ULID identifier",
max_length=26,
min_length=26,
pattern="^[0-7][0-9A-HJKMNP-TV-Z]{25}$",
)
disk_storage: int = Field(
1, description="The size of disk storage for the session, in gigabytes"
)
resource_class_id: Optional[int] = None
cloudstorage: Optional[List[SessionCloudStoragePost]] = None
env_variable_overrides: Optional[List[EnvVarOverride]] = Field(
None, description="Environment variable overrides for the session pod"
)


class SessionResponse(BaseAPISpec):
image: str
name: str = Field(
Expand Down Expand Up @@ -452,3 +450,21 @@ class ImageCheckResponse(BaseAPISpec):
accessible: bool = Field(..., description="Whether the image is accessible or not.")
connection: Optional[ImageConnection] = None
provider: Optional[ImageProvider] = None


class SessionPostRequest(BaseAPISpec):
launcher_id: str = Field(
...,
description="ULID identifier",
max_length=26,
min_length=26,
pattern="^[0-7][0-9A-HJKMNP-TV-Z]{25}$",
)
disk_storage: Optional[int] = Field(
None, description="The size of disk storage for the session, in gigabytes"
)
resource_class_id: Optional[int] = None
data_connectors_overrides: Optional[List[SessionDataConnectorOverride]] = None
env_variable_overrides: Optional[List[EnvVarOverride]] = Field(
None, description="Environment variable overrides for the session pod"
)
5 changes: 4 additions & 1 deletion components/renku_data_services/notebooks/blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from renku_data_services.notebooks.core_sessions import (
patch_session,
start_session,
validate_session_post_request,
)
from renku_data_services.notebooks.errors.intermittent import AnonymousUserPatchError
from renku_data_services.project.db import ProjectRepository, ProjectSessionSecretRepository
Expand Down Expand Up @@ -219,9 +220,11 @@ async def _handler(
internal_gitlab_user: APIUser,
body: apispec.SessionPostRequest,
) -> JSONResponse:
launch_request = validate_session_post_request(body=body)
session, created = await start_session(
request=request,
body=body,
# body=body,
launch_request=launch_request,
user=user,
internal_gitlab_user=internal_gitlab_user,
nb_config=self.nb_config,
Expand Down
Loading