From c8c62dda9d7c7f481e87e4ec581ef6d57fdd36f8 Mon Sep 17 00:00:00 2001 From: Sanne Raymaekers Date: Thu, 23 Jan 2025 13:18:32 +0100 Subject: [PATCH] store/cockpitApi: compose status support Finds the relevant compose, asks for its status and reads the original compose request from disk. --- src/Components/ImagesTable/ImageDetails.tsx | 35 +++++++++++++++- src/Components/ImagesTable/ImagesTable.tsx | 39 ++++++++++++++---- src/Components/ImagesTable/Instance.tsx | 30 +++++++++++++- src/Components/ImagesTable/Status.tsx | 29 +++++++++++++- src/store/backendApi.ts | 4 ++ src/store/cockpitApi.ts | 44 +++++++++++++++++++++ 6 files changed, 171 insertions(+), 10 deletions(-) diff --git a/src/Components/ImagesTable/ImageDetails.tsx b/src/Components/ImagesTable/ImageDetails.tsx index f735be8d..6d051e38 100644 --- a/src/Components/ImagesTable/ImageDetails.tsx +++ b/src/Components/ImagesTable/ImageDetails.tsx @@ -15,11 +15,11 @@ import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import ClonesTable from './ClonesTable'; +import { useGetComposeStatusQuery } from '../../store/backendApi'; import { extractProvisioningList } from '../../store/helpers'; import { ComposesResponseItem, GcpUploadRequestOptions, - useGetComposeStatusQuery, } from '../../store/imageBuilderApi'; import { useGetSourceListQuery } from '../../store/provisioningApi'; import { @@ -478,3 +478,36 @@ export const AwsS3Details = ({ compose }: AwsS3DetailsPropTypes) => { ); }; + +type LocalDetailsPropTypes = { + compose: ComposesResponseItem; +}; + +export const LocalDetails = ({ compose }: LocalDetailsPropTypes) => { + return ( + <> +
+ Build Information +
+ + + UUID + + + {compose.id} + + + Architecture + + {compose.request.image_requests[0].architecture} + + + + + ); +}; diff --git a/src/Components/ImagesTable/ImagesTable.tsx b/src/Components/ImagesTable/ImagesTable.tsx index a105155b..0221ac84 100644 --- a/src/Components/ImagesTable/ImagesTable.tsx +++ b/src/Components/ImagesTable/ImagesTable.tsx @@ -33,12 +33,18 @@ import { AwsS3Details, AzureDetails, GcpDetails, + LocalDetails, OciDetails, } from './ImageDetails'; import ImagesTableToolbar from './ImagesTableToolbar'; -import { AwsS3Instance, CloudInstance, OciInstance } from './Instance'; +import { + AwsS3Instance, + CloudInstance, + OciInstance, + LocalInstance, +} from './Instance'; import Release from './Release'; -import { ExpiringStatus, CloudStatus } from './Status'; +import { ExpiringStatus, CloudStatus, LocalStatus } from './Status'; import { AwsTarget, Target } from './Target'; import { @@ -50,6 +56,7 @@ import { STATUS_POLLING_INTERVAL, } from '../../constants'; import { + useGetComposeStatusQuery, useGetComposesQuery, useGetBlueprintsQuery, useGetBlueprintComposesQuery, @@ -70,7 +77,6 @@ import { ComposeStatus, GetBlueprintComposesApiArg, GetBlueprintsApiArg, - useGetComposeStatusQuery, } from '../../store/imageBuilderApi'; import { resolveRelPath } from '../../Utilities/path'; import { @@ -300,10 +306,9 @@ const ImagesTableRow = ({ compose, rowIndex }: ImagesTableRowPropTypes) => { } }, [setPollingInterval, composeStatus]); - const type = - compose.request?.image_requests[0]?.upload_request?.type || 'local'; + const type = compose.request?.image_requests[0]?.upload_request?.type; - switch (type) { + switch (type as string) { case 'aws': return ( { case 'aws.s3': return ; case 'local': - return ; + return ; } }; @@ -463,6 +468,26 @@ const AwsRow = ({ compose, composeStatus, rowIndex }: AwsRowPropTypes) => { ); }; +type LocalRowPropTypes = { + compose: ComposesResponseItem; + rowIndex: number; +}; + +const LocalRow = ({ compose, rowIndex }: LocalRowPropTypes) => { + const details = ; + const instance = ; + const status = ; + return ( + + ); +}; + type RowPropTypes = { compose: ComposesResponseItem; rowIndex: number; diff --git a/src/Components/ImagesTable/Instance.tsx b/src/Components/ImagesTable/Instance.tsx index 24f1cc92..ddccbe92 100644 --- a/src/Components/ImagesTable/Instance.tsx +++ b/src/Components/ImagesTable/Instance.tsx @@ -27,6 +27,7 @@ import { SEARCH_INPUT, } from '../../constants'; import { useGetBlueprintsQuery } from '../../store/backendApi'; +import { useGetComposeStatusQuery } from '../../store/backendApi'; import { selectSelectedBlueprintId, selectBlueprintSearchInput, @@ -37,7 +38,6 @@ import { ComposesResponseItem, ComposeStatus, ImageTypes, - useGetComposeStatusQuery, } from '../../store/imageBuilderApi'; import { isAwsUploadRequestOptions, @@ -403,3 +403,31 @@ export const AwsS3Instance = ({ ); } }; + +type LocalInstancePropTypes = { + compose: ComposesResponseItem; +}; + +export const LocalInstance = ({ compose }: LocalInstancePropTypes) => { + const { data: composeStatus, isSuccess } = useGetComposeStatusQuery({ + composeId: compose.id, + }); + if (!isSuccess) { + return ; + } + + // Hacky to define the type here, but local upload is not available in + // the image builder api, only in the composer api. + type LocalUploadStatusOptions = { + filename: string; + }; + const status = composeStatus?.image_status.status; + const options = composeStatus?.image_status.upload_status + ?.options as unknown as LocalUploadStatusOptions; + + if (status !== 'success') { + return <>; + } + + return
Filepath to disk: {options.filename}
; +}; diff --git a/src/Components/ImagesTable/Status.tsx b/src/Components/ImagesTable/Status.tsx index 454398fc..82faf489 100644 --- a/src/Components/ImagesTable/Status.tsx +++ b/src/Components/ImagesTable/Status.tsx @@ -27,13 +27,13 @@ import { AWS_S3_EXPIRATION_TIME_IN_HOURS, OCI_STORAGE_EXPIRATION_TIME_IN_DAYS, } from '../../constants'; +import { useGetComposeStatusQuery } from '../../store/backendApi'; import { ClonesResponseItem, ComposeStatus, ComposeStatusError, ComposesResponseItem, UploadStatus, - useGetComposeStatusQuery, } from '../../store/imageBuilderApi'; type StatusClonePropTypes = { @@ -213,6 +213,33 @@ export const ExpiringStatus = ({ } }; +type LocalStatusPropTypes = { + compose: ComposesResponseItem; +}; + +export const LocalStatus = ({ compose }: LocalStatusPropTypes) => { + const { data: composeStatus, isSuccess } = useGetComposeStatusQuery({ + composeId: compose.id, + }); + + if (!isSuccess) { + return ; + } + + const status = composeStatus?.image_status.status || 'failure'; + + if (status === 'failure') { + return ( + + ); + } + return ; +}; + const statuses = { failure: { icon: , diff --git a/src/store/backendApi.ts b/src/store/backendApi.ts index 3abb6c0f..792ec36f 100644 --- a/src/store/backendApi.ts +++ b/src/store/backendApi.ts @@ -48,6 +48,10 @@ export const useGetBlueprintComposesQuery = process.env.IS_ON_PREMISE ? cockpitQueries.useGetBlueprintComposesQuery : imageBuilderQueries.useGetBlueprintComposesQuery; +export const useGetComposeStatusQuery = process.env.IS_ON_PREMISE + ? cockpitQueries.useGetComposeStatusQuery + : imageBuilderQueries.useGetComposeStatusQuery; + export const useBackendPrefetch = process.env.IS_ON_PREMISE ? cockpitApi.usePrefetch : imageBuilderApi.usePrefetch; diff --git a/src/store/cockpitApi.ts b/src/store/cockpitApi.ts index 801a6c80..9dccb41c 100644 --- a/src/store/cockpitApi.ts +++ b/src/store/cockpitApi.ts @@ -27,6 +27,8 @@ import { GetBlueprintComposesApiResponse, GetComposesApiArg, GetComposesApiResponse, + GetComposeStatusApiArg, + GetComposeStatusApiResponse, DeleteBlueprintApiResponse, DeleteBlueprintApiArg, BlueprintItem, @@ -418,6 +420,47 @@ export const cockpitApi = emptyCockpitApi.injectEndpoints({ } }, }), + getComposeStatus: builder.query< + GetComposeStatusApiResponse, + GetComposeStatusApiArg + >({ + queryFn: async (queryArg) => { + try { + const cloudapi = cockpit.http('/run/cloudapi/api.socket', { + superuser: 'require', + }); + const resp = JSON.parse( + await cloudapi.get( + `/api/image-builder-composer/v2/composes/${queryArg.composeId}` + ) + ); + const blueprintsDir = await getBlueprintsPath(); + const info = await fsinfo(blueprintsDir, ['entries'], { + superuser: 'try', + }); + const entries = Object.entries(info?.entries || {}); + for (const bpEntry of entries) { + const request = await cockpit + .file(path.join(blueprintsDir, bpEntry[0], queryArg.composeId)) + .read(); + return { + data: { + image_status: resp.image_status, + request: JSON.parse(request), + }, + }; + } + return { + data: { + image_status: '', + request: {}, + }, + }; + } catch (error) { + return { error }; + } + }, + }), }; }, }); @@ -434,4 +477,5 @@ export const { useComposeBlueprintMutation, useGetComposesQuery, useGetBlueprintComposesQuery, + useGetComposeStatusQuery, } = cockpitApi;