From d3b6914dd93965c7eff60c93f78b222023294bf8 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Thu, 13 Feb 2025 08:52:23 +0100 Subject: [PATCH 1/6] update API --- .../api/sessionLaunchersV2.generated-api.ts | 99 +++++++ .../api/sessionLaunchersV2.openapi.json | 271 ++++++++++++++++++ 2 files changed, 370 insertions(+) diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts b/client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts index 532afd2c6..28fd4527d 100644 --- a/client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.generated-api.ts @@ -96,6 +96,45 @@ const injectedRtkApi = api.injectEndpoints({ url: `/projects/${queryArg.projectId}/session_launchers`, }), }), + getBuildsByBuildId: build.query< + GetBuildsByBuildIdApiResponse, + GetBuildsByBuildIdApiArg + >({ + query: (queryArg) => ({ url: `/builds/${queryArg.buildId}` }), + }), + patchBuildsByBuildId: build.mutation< + PatchBuildsByBuildIdApiResponse, + PatchBuildsByBuildIdApiArg + >({ + query: (queryArg) => ({ + url: `/builds/${queryArg.buildId}`, + method: "PATCH", + body: queryArg.buildPatch, + }), + }), + getBuildsByBuildIdLogs: build.query< + GetBuildsByBuildIdLogsApiResponse, + GetBuildsByBuildIdLogsApiArg + >({ + query: (queryArg) => ({ url: `/builds/${queryArg.buildId}/logs` }), + }), + getEnvironmentsByEnvironmentIdBuilds: build.query< + GetEnvironmentsByEnvironmentIdBuildsApiResponse, + GetEnvironmentsByEnvironmentIdBuildsApiArg + >({ + query: (queryArg) => ({ + url: `/environments/${queryArg.environmentId}/builds`, + }), + }), + postEnvironmentsByEnvironmentIdBuilds: build.mutation< + PostEnvironmentsByEnvironmentIdBuildsApiResponse, + PostEnvironmentsByEnvironmentIdBuildsApiArg + >({ + query: (queryArg) => ({ + url: `/environments/${queryArg.environmentId}/builds`, + method: "POST", + }), + }), }), overrideExisting: false, }); @@ -158,6 +197,32 @@ export type GetProjectsByProjectIdSessionLaunchersApiResponse = export type GetProjectsByProjectIdSessionLaunchersApiArg = { projectId: Ulid; }; +export type GetBuildsByBuildIdApiResponse = + /** status 200 The container image build */ Build; +export type GetBuildsByBuildIdApiArg = { + buildId: Ulid; +}; +export type PatchBuildsByBuildIdApiResponse = + /** status 200 The updated container image build */ Build; +export type PatchBuildsByBuildIdApiArg = { + buildId: Ulid; + buildPatch: BuildPatch; +}; +export type GetBuildsByBuildIdLogsApiResponse = + /** status 200 The build logs */ BuildLogs; +export type GetBuildsByBuildIdLogsApiArg = { + buildId: Ulid; +}; +export type GetEnvironmentsByEnvironmentIdBuildsApiResponse = + /** status 200 List of container image builds */ BuildList; +export type GetEnvironmentsByEnvironmentIdBuildsApiArg = { + environmentId: Ulid; +}; +export type PostEnvironmentsByEnvironmentIdBuildsApiResponse = + /** status 201 The build was created */ Build; +export type PostEnvironmentsByEnvironmentIdBuildsApiArg = { + environmentId: Ulid; +}; export type Ulid = string; export type SessionName = string; export type CreationDate = string; @@ -311,6 +376,35 @@ export type SessionLauncherPatch = { disk_storage?: DiskStoragePatch; environment?: EnvironmentPatchInLauncher | EnvironmentIdOnlyPatch; }; +export type BuildCommonPart = { + id: Ulid; + environment_id: Ulid; + created_at: CreationDate; +}; +export type BuildNotCompletedPart = { + status: "in_progress" | "failed" | "cancelled"; +}; +export type BuildResult = { + image: ContainerImage; + completed_at: CreationDate; + repository_url: string; + repository_git_commit_sha: string; +}; +export type BuildCompletedPart = { + status: "succeeded"; + result: BuildResult; +}; +export type Build = BuildCommonPart & + (BuildNotCompletedPart | BuildCompletedPart); +export type BuildPatch = { + status?: "cancelled"; +}; +export type BuildLogs = { + process_name?: string; + stdout?: string; + stderr?: string; +}[]; +export type BuildList = Build[]; export const { useGetEnvironmentsQuery, usePostEnvironmentsMutation, @@ -323,4 +417,9 @@ export const { usePatchSessionLaunchersByLauncherIdMutation, useDeleteSessionLaunchersByLauncherIdMutation, useGetProjectsByProjectIdSessionLaunchersQuery, + useGetBuildsByBuildIdQuery, + usePatchBuildsByBuildIdMutation, + useGetBuildsByBuildIdLogsQuery, + useGetEnvironmentsByEnvironmentIdBuildsQuery, + usePostEnvironmentsByEnvironmentIdBuildsMutation, } = injectedRtkApi; diff --git a/client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json b/client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json index a6acfb17d..290ff646b 100644 --- a/client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json +++ b/client/src/features/sessionsV2/api/sessionLaunchersV2.openapi.json @@ -386,6 +386,147 @@ }, "tags": ["session_launchers"] } + }, + "/builds/{build_id}": { + "parameters": [ + { + "in": "path", + "name": "build_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "get": { + "summary": "Get the details of a container image build", + "responses": { + "200": { + "description": "The container image build", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Build" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["builds"] + }, + "patch": { + "summary": "Update a container image build", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BuildPatch" + } + } + } + }, + "responses": { + "200": { + "description": "The updated container image build", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Build" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["builds"] + } + }, + "/builds/{build_id}/logs": { + "parameters": [ + { + "in": "path", + "name": "build_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "get": { + "summary": "Get the logs of a container image build", + "responses": { + "200": { + "description": "The build logs", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BuildLogs" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["builds"] + } + }, + "/environments/{environment_id}/builds": { + "parameters": [ + { + "in": "path", + "name": "environment_id", + "required": true, + "schema": { + "$ref": "#/components/schemas/Ulid" + } + } + ], + "get": { + "summary": "Get a session environment's list of builds", + "responses": { + "200": { + "description": "List of container image builds", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BuildList" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["builds"] + }, + "post": { + "summary": "Create a new container image build", + "responses": { + "201": { + "description": "The build was created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Build" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "tags": ["builds"] + } } }, "components": { @@ -1040,6 +1181,136 @@ "description": "Whether this environment is archived and not for use in new projects or not", "default": false }, + "Build": { + "description": "A container image build", + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/BuildCommonPart" + }, + { + "oneOf": [ + { + "$ref": "#/components/schemas/BuildNotCompletedPart" + }, + { + "$ref": "#/components/schemas/BuildCompletedPart" + } + ] + } + ] + }, + "BuildCommonPart": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Ulid" + }, + "environment_id": { + "$ref": "#/components/schemas/Ulid" + }, + "created_at": { + "$ref": "#/components/schemas/CreationDate" + } + }, + "required": ["id", "environment_id", "created_at"], + "additionalProperties": false + }, + "BuildNotCompletedPart": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": ["in_progress", "failed", "cancelled"], + "example": "in_progress" + } + }, + "required": ["status"], + "additionalProperties": false + }, + "BuildCompletedPart": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": ["succeeded"], + "example": "succeeded" + }, + "result": { + "$ref": "#/components/schemas/BuildResult" + } + }, + "required": ["status", "result"], + "additionalProperties": false + }, + "BuildList": { + "description": "A list of container image builds", + "type": "array", + "items": { + "$ref": "#/components/schemas/Build" + } + }, + "BuildPatch": { + "description": "The requested update of a container image build", + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": ["cancelled"] + } + }, + "additionalProperties": false + }, + "BuildLogs": { + "description": "The logs of a container image build", + "type": "array", + "items": { + "description": "The logs of build step", + "type": "object", + "properties": { + "process_name": { + "type": "string" + }, + "stdout": { + "type": "string" + }, + "stderr": { + "type": "string" + } + } + } + }, + "BuildResult": { + "description": "The result of a container image build", + "type": "object", + "properties": { + "image": { + "$ref": "#/components/schemas/ContainerImage" + }, + "completed_at": { + "$ref": "#/components/schemas/CreationDate" + }, + "repository_url": { + "type": "string" + }, + "repository_git_commit_sha": { + "type": "string" + } + }, + "required": [ + "image", + "completed_at", + "repository_url", + "repository_git_commit_sha" + ], + "additionalProperties": false + }, + "BuildStatus": { + "description": "The status of a container image build", + "type": "string", + "enum": ["in_progress", "succeeded", "failed", "cancelled"], + "example": "succeeded" + }, "ErrorResponse": { "type": "object", "properties": { From 3f2af50b5cf9ab04ddad45f9c7c86be1823fba49 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Thu, 13 Feb 2025 09:55:40 +0100 Subject: [PATCH 2/6] wip: trigger and status --- .../SessionView/EnvironmentCard.tsx | 43 ++++++++++++++++++- client/src/features/sessionsV2/SessionsV2.tsx | 23 ++++++++-- .../sessionsV2/api/sessionLaunchersV2.api.ts | 27 +++++++++++- 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx b/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx index 0af553b4c..113644b22 100644 --- a/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx +++ b/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx @@ -16,20 +16,24 @@ * limitations under the License. */ +import { skipToken } from "@reduxjs/toolkit/query"; import cx from "classnames"; import { ReactNode } from "react"; import { + Bricks, CircleFill, Clock, Globe2, Link45deg, - Tools, } from "react-bootstrap-icons"; import { Badge, Card, CardBody, Col, Row } from "reactstrap"; +import { Loader } from "../../../components/Loader"; +import { RtkOrNotebooksError } from "../../../components/errors/RtkErrorAlert"; import { ErrorLabel } from "../../../components/formlabels/FormLabels"; import { toHumanDateTime } from "../../../utils/helpers/DateTimeUtils"; import type { SessionLauncher } from "../api/sessionLaunchersV2.api"; +import { useGetEnvironmentsByEnvironmentIdBuildsQuery as useGetBuildsQuery } from "../api/sessionLaunchersV2.api"; import { BUILDER_IMAGE_NOT_READY_VALUE } from "../session.constants"; import { safeStringify } from "../session.utils"; @@ -57,7 +61,7 @@ export function EnvironmentCard({ launcher }: { launcher: SessionLauncher }) { ) : environment.environment_image_source === "build" ? ( <> - + Built by RenkuLab ) : ( @@ -163,6 +167,17 @@ function CustomBuildEnvironmentValues({ }) { const { environment } = launcher; + const { + data: builds, + isLoading, + error, + } = useGetBuildsQuery( + environment.environment_image_source === "build" + ? { environmentId: environment.id } + : skipToken + ); + const lastBuild = builds?.at(0); + if (environment.environment_image_source !== "build") { return null; } @@ -179,6 +194,30 @@ function CustomBuildEnvironmentValues({ )} + + {isLoading ? ( + + + Loading build status... + + ) : error || !builds ? ( +
+

Error: could not load build status

+ {error && } +
+ ) : lastBuild == null ? ( + + This session environment does not have a build yet. + + ) : ( +
+ + {lastBuild.status} +
+ )} +
!open); }, []); + const [postBuild /*, result*/] = usePostBuildMutation(); + const triggerBuild = useCallback(() => { + postBuild({ environmentId: launcher.environment.id }); + }, [launcher.environment.id, postBuild]); + const defaultAction = ( + + + ); +} From 042c2e955d2e2f31aa680eff9489d319424353e8 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Mon, 24 Feb 2025 13:36:22 +0100 Subject: [PATCH 6/6] use constant --- client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx b/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx index 6caf0c28f..439ed24d2 100644 --- a/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx +++ b/client/src/features/sessionsV2/SessionView/EnvironmentCard.tsx @@ -257,7 +257,7 @@ function CustomBuildEnvironmentValues({ value={frontend_variant || ""} /> - {environment.container_image !== "image:unknown-at-the-moment" && ( + {environment.container_image !== BUILDER_IMAGE_NOT_READY_VALUE && ( )}