diff --git a/Makefile b/Makefile index 20331e859..c1e96c3d6 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ components/renku_data_services/connected_services/apispec.py: components/renku_d components/renku_data_services/repositories/apispec.py: components/renku_data_services/repositories/api.spec.yaml poetry run datamodel-codegen --input components/renku_data_services/repositories/api.spec.yaml --input-file-type openapi --output-model-type pydantic_v2.BaseModel --output components/renku_data_services/repositories/apispec.py --use-double-quotes --target-python-version 3.12 --collapse-root-models --field-constraints --strict-nullable --base-class renku_data_services.repositories.apispec_base.BaseAPISpec -schemas: components/renku_data_services/crc/apispec.py components/renku_data_services/storage/apispec.py components/renku_data_services/users/apispec.py components/renku_data_services/project/apispec.py components/renku_data_services/user_preferences/apispec.py components/renku_data_services/namespace/apispec.py components/renku_data_services/secrets/apispec.py components/renku_data_services/connected_services/apispec.py components/renku_data_services/repositories/apispec.py +schemas: components/renku_data_services/crc/apispec.py components/renku_data_services/storage/apispec.py components/renku_data_services/users/apispec.py components/renku_data_services/project/apispec.py components/renku_data_services/user_preferences/apispec.py components/renku_data_services/namespace/apispec.py components/renku_data_services/secrets/apispec.py components/renku_data_services/connected_services/apispec.py components/renku_data_services/repositories/apispec.py components/renku_data_services/session/apispec.py @echo "generated classes based on ApiSpec" download_avro: @@ -68,6 +68,8 @@ style_checks: @$(call test_apispec_up_to_date,"connected_services") @echo "checking repositories apispec is up to date" @$(call test_apispec_up_to_date,"repositories") + @echo "checking session apispec is up to date" + @$(call test_apispec_up_to_date,"session") poetry run mypy poetry run ruff format --check poetry run ruff check . diff --git a/components/renku_data_services/session/api.spec.yaml b/components/renku_data_services/session/api.spec.yaml index 0ef09676b..d0c78fb21 100644 --- a/components/renku_data_services/session/api.spec.yaml +++ b/components/renku_data_services/session/api.spec.yaml @@ -11,10 +11,10 @@ servers: paths: /environments: get: - summary: Get all environments + summary: Get all global environments responses: "200": - description: List of environments + description: List of global environments content: application/json: schema: @@ -24,7 +24,7 @@ paths: tags: - environments post: - summary: Create a new session environment + summary: Create a new global session environment description: Requires admin permissions requestBody: required: true @@ -45,7 +45,7 @@ paths: - environments /environments/{environment_id}: get: - summary: Get a session environment + summary: Get a global session environment parameters: - in: path name: environment_id @@ -70,7 +70,7 @@ paths: tags: - environments patch: - summary: Update specific fields of an existing session environment + summary: Update specific fields of an existing global session environment description: Requires admin permissions parameters: - in: path @@ -102,7 +102,7 @@ paths: tags: - environments delete: - summary: Remove a session environment + summary: Remove a global session environment parameters: - in: path name: environment_id @@ -176,7 +176,7 @@ paths: tags: - session_launchers patch: - summary: Update specific fields of an existing session + summary: Update specific fields of an existing session launcher parameters: - in: path name: launcher_id @@ -221,32 +221,6 @@ paths: $ref: "#/components/responses/Error" tags: - session_launchers - /session_launchers/{launcher_id}/start: - post: - summary: Use a session launcher to start a session - parameters: - - in: path - name: launcher_id - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/SessionStart" - responses: - "201": - description: The started session - content: - application/json: - schema: - $ref: "#/components/schemas/Session" - default: - $ref: "#/components/responses/Error" - tags: - - sessions /projects/{project_id}/session_launchers: get: summary: Get a project's session launchers @@ -290,11 +264,27 @@ components: $ref: "#/components/schemas/ContainerImage" default_url: $ref: "#/components/schemas/DefaultUrl" + uid: + $ref: "#/components/schemas/EnvironmentUid" + gid: + $ref: "#/components/schemas/EnvironmentGid" + working_directory: + $ref: "#/components/schemas/EnvironmentWorkingDirectory" + mount_directory: + $ref: "#/components/schemas/EnvironmentMountDirectory" + port: + $ref: "#/components/schemas/EnvironmentPort" required: - id - name - creation_date - container_image + - port + - working_directory + - mount_directory + - uid + - gid + - default_url example: id: 01AN4Z79ZS6XX96588FDX0H099 name: JupyterLab environment @@ -302,6 +292,33 @@ components: description: JupyterLab session environment container_image: renku-jupyter:latest default_url: "/lab" + port: 8080 + working_directory: /home/jovyan/work + mount_directory: /home/jovyan/work + uid: 1000 + gid: 1000 + EnvironmentGetInLauncher: + allOf: + - $ref: "#/components/schemas/Environment" + - type: object + properties: + environment_kind: + $ref: "#/components/schemas/EnvironmentKind" + required: + - environment_kind + example: + environment_kind: global_environment + EnvironmentPostInLauncher: + allOf: + - $ref: "#/components/schemas/EnvironmentPost" + - type: object + properties: + environment_kind: + $ref: "#/components/schemas/EnvironmentKind" + required: + - environment_kind + example: + environment_kind: global_environment EnvironmentPost: description: Data required to create a session environment type: object @@ -313,10 +330,41 @@ components: container_image: $ref: "#/components/schemas/ContainerImage" default_url: - $ref: "#/components/schemas/DefaultUrl" + allOf: + - $ref: "#/components/schemas/DefaultUrl" + - default: /lab + uid: + allOf: + - $ref: "#/components/schemas/EnvironmentUid" + - default: 1000 + gid: + allOf: + - $ref: "#/components/schemas/EnvironmentGid" + - default: 1000 + working_directory: + allOf: + - $ref: "#/components/schemas/EnvironmentWorkingDirectory" + - default: /home/jovyan/work + mount_directory: + allOf: + - $ref: "#/components/schemas/EnvironmentMountDirectory" + - default: /home/jovyan/work + port: + allOf: + - $ref: "#/components/schemas/EnvironmentPort" + - default: 8080 required: - name - container_image + EnvironmentPatchInLauncher: + allOf: + - $ref: "#/components/schemas/EnvironmentPatch" + - type: object + properties: + environment_kind: + $ref: "#/components/schemas/EnvironmentKind" + example: + environment_kind: global_environment EnvironmentPatch: type: object description: Update a session environment @@ -330,6 +378,16 @@ components: $ref: "#/components/schemas/ContainerImage" default_url: $ref: "#/components/schemas/DefaultUrl" + uid: + $ref: "#/components/schemas/EnvironmentUid" + gid: + $ref: "#/components/schemas/EnvironmentGid" + working_directory: + $ref: "#/components/schemas/EnvironmentWorkingDirectory" + mount_directory: + $ref: "#/components/schemas/EnvironmentMountDirectory" + port: + $ref: "#/components/schemas/EnvironmentPort" SessionLaunchersList: description: A list of Renku session launchers type: array @@ -350,30 +408,33 @@ components: $ref: "#/components/schemas/CreationDate" description: $ref: "#/components/schemas/Description" - environment_kind: - $ref: "#/components/schemas/EnvironmentKind" - environment_id: - $ref: "#/components/schemas/EnvironmentId" - resource_class_id: - $ref: "#/components/schemas/ResourceClassId" - container_image: - $ref: "#/components/schemas/ContainerImage" - default_url: - $ref: "#/components/schemas/DefaultUrl" + environment: + $ref: "#/components/schemas/EnvironmentGetInLauncher" required: - id - project_id - name - creation_date - - environment_kind + - environment example: id: 01AN4Z79ZS5XN0F25N3DB94T4R project_id: 01AN4Z79ZS5XN0F25N3DB94T4R name: Renku R Session creation_date: "2023-11-01T17:32:28Z" description: R compute session - environment_kind: global_environment - environment_id: 01AN4Z79ZS6XX96588FDX0H099 + environment: + id: 01AN4Z79ZS6XX96588FDX0H099 + name: Rstudio + creation_date: "2023-11-01T17:32:28Z" + description: JupyterLab session environment + environment_kind: global_environment + container_image: rocker/rstudio + default_url: "/rstudio" + port: 8080 + working_directory: /home/rstudio/work + mount_directory: /home/rstudio/work + uid: 1000 + gid: 1000 SessionLauncherPost: description: Data required to create a session launcher type: object @@ -385,20 +446,16 @@ components: $ref: "#/components/schemas/Ulid" description: $ref: "#/components/schemas/Description" - environment_kind: - $ref: "#/components/schemas/EnvironmentKind" - environment_id: - $ref: "#/components/schemas/EnvironmentId" resource_class_id: $ref: "#/components/schemas/ResourceClassId" - container_image: - $ref: "#/components/schemas/ContainerImage" - default_url: - $ref: "#/components/schemas/DefaultUrl" + environment: + oneOf: + - $ref: "#/components/schemas/EnvironmentPostInLauncher" + - $ref: "#/components/schemas/EnvironmentIdOnlyPost" required: - name - project_id - - environment_kind + - environment SessionLauncherPatch: type: object description: Update a session launcher @@ -408,23 +465,12 @@ components: $ref: "#/components/schemas/SessionName" description: $ref: "#/components/schemas/Description" - environment_kind: - $ref: "#/components/schemas/EnvironmentKind" - environment_id: - $ref: "#/components/schemas/EnvironmentId" - resource_class_id: - $ref: "#/components/schemas/ResourceClassId" - container_image: - $ref: "#/components/schemas/ContainerImage" - default_url: - $ref: "#/components/schemas/DefaultUrl" - SessionStart: - type: object - description: Start a session - additionalProperties: true - properties: resource_class_id: $ref: "#/components/schemas/ResourceClassId" + environment: + oneOf: + - $ref: "#/components/schemas/EnvironmentPatchInLauncher" + - $ref: "#/components/schemas/EnvironmentIdOnlyPatch" Ulid: description: ULID identifier type: string @@ -437,6 +483,18 @@ components: minLength: 1 maxLength: 99 example: My Renku Session :) + EnvironmentIdOnlyPatch: + type: object + properties: + id: + $ref: "#/components/schemas/EnvironmentId" + EnvironmentIdOnlyPost: + type: object + properties: + id: + $ref: "#/components/schemas/EnvironmentId" + required: + - id EnvironmentKind: description: Kind of environment to use type: string @@ -473,15 +531,32 @@ components: type: integer default: null nullable: true - Session: - description: A Renku session - type: object - additionalProperties: true - properties: - name: - $ref: "#/components/schemas/SessionName" - url: - type: string + EnvironmentPort: + type: integer + minimum: 0 + exclusiveMinimum: true + maximum: 65535 + description: The TCP port (on any container in the session) where user requests will be routed to from the ingress + EnvironmentUid: + type: integer + minimum: 0 + exclusiveMinimum: true + maximum: 65535 + description: The user ID used to run the session + EnvironmentGid: + type: integer + minimum: 0 + exclusiveMinimum: true + maximum: 65535 + description: The group ID used to run the session + EnvironmentWorkingDirectory: + type: string + description: The location where the session will start + minLength: 1 + EnvironmentMountDirectory: + type: string + description: The location where the persistent storage for the session will be mounted, usually it should be identical to or a parent of the working directory + minLength: 1 ErrorResponse: type: object properties: diff --git a/components/renku_data_services/session/apispec.py b/components/renku_data_services/session/apispec.py index 6be545731..1a266cf10 100644 --- a/components/renku_data_services/session/apispec.py +++ b/components/renku_data_services/session/apispec.py @@ -1,12 +1,12 @@ # generated by datamodel-codegen: # filename: api.spec.yaml -# timestamp: 2024-06-10T13:14:40+00:00 +# timestamp: 2024-07-12T08:57:02+00:00 from __future__ import annotations from datetime import datetime from enum import Enum -from typing import List, Optional +from typing import List, Optional, Union from pydantic import ConfigDict, Field, RootModel from renku_data_services.session.apispec_base import BaseAPISpec @@ -17,20 +17,6 @@ class EnvironmentKind(Enum): container_image = "container_image" -class Session(BaseAPISpec): - model_config = ConfigDict( - extra="allow", - ) - name: Optional[str] = Field( - None, - description="Renku session name", - example="My Renku Session :)", - max_length=99, - min_length=1, - ) - url: Optional[str] = None - - class Error(BaseAPISpec): code: int = Field(..., example=1404, gt=0) detail: Optional[str] = Field( @@ -72,12 +58,36 @@ class Environment(BaseAPISpec): example="renku/renkulab-py:3.10-0.18.1", max_length=500, ) - default_url: Optional[str] = Field( - None, + default_url: str = Field( + ..., description="The default path to open in a session", example="/lab", max_length=200, ) + uid: int = Field( + ..., description="The user ID used to run the session", gt=0, le=65535 + ) + gid: int = Field( + ..., description="The group ID used to run the session", gt=0, le=65535 + ) + working_directory: str = Field( + ..., description="The location where the session will start", min_length=1 + ) + mount_directory: str = Field( + ..., + description="The location where the persistent storage for the session will be mounted, usually it should be identical to or a parent of the working directory", + min_length=1, + ) + port: int = Field( + ..., + description="The TCP port (on any container in the session) where user requests will be routed to from the ingress", + gt=0, + le=65535, + ) + + +class EnvironmentGetInLauncher(Environment): + environment_kind: EnvironmentKind class EnvironmentPost(BaseAPISpec): @@ -103,6 +113,26 @@ class EnvironmentPost(BaseAPISpec): example="/lab", max_length=200, ) + uid: Optional[int] = Field( + None, description="The user ID used to run the session", gt=0, le=65535 + ) + gid: Optional[int] = Field( + None, description="The group ID used to run the session", gt=0, le=65535 + ) + working_directory: Optional[str] = Field( + None, description="The location where the session will start", min_length=1 + ) + mount_directory: Optional[str] = Field( + None, + description="The location where the persistent storage for the session will be mounted, usually it should be identical to or a parent of the working directory", + min_length=1, + ) + port: Optional[int] = Field( + None, + description="The TCP port (on any container in the session) where user requests will be routed to from the ingress", + gt=0, + le=65535, + ) class EnvironmentPatch(BaseAPISpec): @@ -131,6 +161,26 @@ class EnvironmentPatch(BaseAPISpec): example="/lab", max_length=200, ) + uid: Optional[int] = Field( + None, description="The user ID used to run the session", gt=0, le=65535 + ) + gid: Optional[int] = Field( + None, description="The group ID used to run the session", gt=0, le=65535 + ) + working_directory: Optional[str] = Field( + None, description="The location where the session will start", min_length=1 + ) + mount_directory: Optional[str] = Field( + None, + description="The location where the persistent storage for the session will be mounted, usually it should be identical to or a parent of the working directory", + min_length=1, + ) + port: Optional[int] = Field( + None, + description="The TCP port (on any container in the session) where user requests will be routed to from the ingress", + gt=0, + le=65535, + ) class SessionLauncher(BaseAPISpec): @@ -163,27 +213,42 @@ class SessionLauncher(BaseAPISpec): description: Optional[str] = Field( None, description="A description for the resource", max_length=500 ) - environment_kind: EnvironmentKind - environment_id: Optional[str] = Field( + environment: EnvironmentGetInLauncher + + +class EnvironmentIdOnlyPatch(BaseAPISpec): + id: Optional[str] = Field( None, description="Id of the environment to use", example="01AN4Z79ZS6XX96588FDX0H099", min_length=1, ) - resource_class_id: Optional[int] = Field( - None, description="The identifier of a resource class" - ) - container_image: Optional[str] = Field( - None, - description="A container image", - example="renku/renkulab-py:3.10-0.18.1", - max_length=500, + + +class EnvironmentIdOnlyPost(BaseAPISpec): + id: str = Field( + ..., + description="Id of the environment to use", + example="01AN4Z79ZS6XX96588FDX0H099", + min_length=1, ) - default_url: Optional[str] = Field( - None, - description="The default path to open in a session", - example="/lab", - max_length=200, + + +class EnvironmentList(RootModel[List[Environment]]): + root: List[Environment] = Field(..., description="A list of session environments") + + +class EnvironmentPostInLauncher(EnvironmentPost): + environment_kind: EnvironmentKind + + +class EnvironmentPatchInLauncher(EnvironmentPatch): + environment_kind: Optional[EnvironmentKind] = None + + +class SessionLaunchersList(RootModel[List[SessionLauncher]]): + root: List[SessionLauncher] = Field( + ..., description="A list of Renku session launchers", min_length=0 ) @@ -208,28 +273,10 @@ class SessionLauncherPost(BaseAPISpec): description: Optional[str] = Field( None, description="A description for the resource", max_length=500 ) - environment_kind: EnvironmentKind - environment_id: Optional[str] = Field( - None, - description="Id of the environment to use", - example="01AN4Z79ZS6XX96588FDX0H099", - min_length=1, - ) resource_class_id: Optional[int] = Field( None, description="The identifier of a resource class" ) - container_image: Optional[str] = Field( - None, - description="A container image", - example="renku/renkulab-py:3.10-0.18.1", - max_length=500, - ) - default_url: Optional[str] = Field( - None, - description="The default path to open in a session", - example="/lab", - max_length=200, - ) + environment: Union[EnvironmentPostInLauncher, EnvironmentIdOnlyPost] class SessionLauncherPatch(BaseAPISpec): @@ -246,44 +293,9 @@ class SessionLauncherPatch(BaseAPISpec): description: Optional[str] = Field( None, description="A description for the resource", max_length=500 ) - environment_kind: Optional[EnvironmentKind] = None - environment_id: Optional[str] = Field( - None, - description="Id of the environment to use", - example="01AN4Z79ZS6XX96588FDX0H099", - min_length=1, - ) - resource_class_id: Optional[int] = Field( - None, description="The identifier of a resource class" - ) - container_image: Optional[str] = Field( - None, - description="A container image", - example="renku/renkulab-py:3.10-0.18.1", - max_length=500, - ) - default_url: Optional[str] = Field( - None, - description="The default path to open in a session", - example="/lab", - max_length=200, - ) - - -class SessionStart(BaseAPISpec): - model_config = ConfigDict( - extra="allow", - ) resource_class_id: Optional[int] = Field( None, description="The identifier of a resource class" ) - - -class EnvironmentList(RootModel[List[Environment]]): - root: List[Environment] = Field(..., description="A list of session environments") - - -class SessionLaunchersList(RootModel[List[SessionLauncher]]): - root: List[SessionLauncher] = Field( - ..., description="A list of Renku session launchers", min_length=0 + environment: Optional[Union[EnvironmentPatchInLauncher, EnvironmentIdOnlyPatch]] = ( + None )