Skip to content

Commit

Permalink
[FR-246] feat: add container commit in the Session Detail Panel (#3012)
Browse files Browse the repository at this point in the history
<!--
Please precisely, concisely, and concretely describe what this PR changes, the rationale behind codes,
and how it affects the users and other developers.
-->

### This PR resolves [#3013](#3013) issue

**feature:**
- Add container commit functionality to the session detail panel.
- The image name is a combination of alphabets, numbers, -, and _ from 4 to 32 characters.

**How to test:**
- Connect to an environment that supports image commit
- Enable image commit in config.toml and create a session with a non-system role.
- Access the session detail panel and click the commit feature in the top right corner. You can access via query string (?sessionDetail=<sessionID>, or endpoint detail page).
- Set a session name and click the OK button.
- Verify that the notification tracks the background task properly
- Verify that the commit image is registered properly.

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/lSyr8xXz1wdXALkJKzVx/84cb9b64-e73f-41b6-abed-e4d2293b72f6.png)

**Checklist:** (if applicable)

- [ ] Mention to the original issue
- [ ] Documentation
- [ ] Minium required manager version
- [ ] Specific setting for review (eg., KB link, endpoint or how to setup)
- [ ] Minimum requirements to check during review
- [ ] Test case(s) to demonstrate the difference of before/after
  • Loading branch information
ironAiken2 committed Jan 14, 2025
1 parent 38b29bd commit 1efdc0c
Show file tree
Hide file tree
Showing 25 changed files with 231 additions and 36 deletions.
141 changes: 141 additions & 0 deletions react/src/components/ComputeSessionNodeItems/ContainerCommitModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { useSuspendedBackendaiClient } from '../../hooks';
import { useSetBAINotification } from '../../hooks/useBAINotification';
import BAIModal, { BAIModalProps } from '../BAIModal';
import Flex from '../Flex';
import { ContainerCommitModalFragment$key } from './__generated__/ContainerCommitModalFragment.graphql';
import {
Descriptions,
Divider,
Form,
FormInstance,
Input,
Typography,
} from 'antd';
import graphql from 'babel-plugin-relay/macro';
import { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useFragment } from 'react-relay';

interface ContainerCommitModalProps extends BAIModalProps {
sessionFrgmt: ContainerCommitModalFragment$key | null;
onRequestClose: () => void;
}

const ContainerCommitModal: React.FC<ContainerCommitModalProps> = ({
sessionFrgmt,
onRequestClose,
...modalProps
}) => {
const { t } = useTranslation();
const formRef = useRef<FormInstance>(null);
const baiClient = useSuspendedBackendaiClient();

const { upsertNotification } = useSetBAINotification();
const [isConfirmLoading, setIsConfirmLoading] = useState<boolean>(false);

const session = useFragment(
graphql`
fragment ContainerCommitModalFragment on ComputeSessionNode {
id
name
row_id @required(action: NONE)
}
`,
sessionFrgmt,
);

const convertSessionToImage = () => {
setIsConfirmLoading(true);
formRef?.current
?.validateFields()
.then((values: { imageName: string }) => {
baiClient.computeSession
.convertSessionToImage(session?.name ?? '', values.imageName)
.then((res: { task_id: string }) => {
onRequestClose();
upsertNotification({
key: 'commitSession:' + session?.name,
backgroundTask: {
taskId: res.task_id,
status: 'pending',
statusDescriptions: {
pending: t('session.CommitOnGoing'),
resolved: t('session.CommitFinished'),
rejected: t('session.CommitFailed'),
},
},
duration: 0,
message: 'commitSession: ' + session?.name,
open: true,
});
})
.catch((err: any) => {
if (err?.message) {
throw new Error(err.message);
} else {
throw err;
}
})
.finally(() => {
setIsConfirmLoading(false);
});
})
.catch((err) => {
console.log(err);
});
};

return (
<BAIModal
title={t('session.CommitSession')}
onOk={() => convertSessionToImage()}
okButtonProps={{ loading: isConfirmLoading }}
onCancel={onRequestClose}
{...modalProps}
destroyOnClose
>
<Flex
direction="column"
gap={'xs'}
align="stretch"
style={{ overflow: 'hidden' }}
>
<Typography.Text>{t('session.DescCommitSession')}</Typography.Text>
<Descriptions bordered size="small" column={1}>
<Descriptions.Item label={t('session.SessionName')}>
{session?.name}
</Descriptions.Item>
<Descriptions.Item label={t('session.SessionId')}>
{session?.row_id}
</Descriptions.Item>
{/* FIXME: need to use legacy_session */}
{/* <Descriptions.Item label={t('session.launcher.Environments')}>
</Descriptions.Item> */}
</Descriptions>
<Divider style={{ marginTop: 12, marginBottom: 12 }} />
<Form ref={formRef}>
<Form.Item
label={t('session.CommitImageName')}
name="imageName"
required
rules={[
{ required: true },
{
min: 4,
max: 32,
},
{
pattern: /^[a-zA-Z0-9-_.]+$/,
message: t('session.Validation.EnterValidSessionName'),
},
]}
>
<Input placeholder={t('inputLimit.4to32chars')} />
</Form.Item>
</Form>
</Flex>
</BAIModal>
);
};

export default ContainerCommitModal;
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useSuspendedBackendaiClient } from '../../hooks';
import { useBackendAIAppLauncher } from '../../hooks/useBackendAIAppLauncher';
import ContainerCommitModal from './ContainerCommitModal';
import ContainerLogModal from './ContainerLogModal';
import TerminateSessionModal from './TerminateSessionModal';
import {
SessionActionButtonsFragment$data,
SessionActionButtonsFragment$key,
} from './__generated__/SessionActionButtonsFragment.graphql';
import { DeliveredProcedureOutlined } from '@ant-design/icons';
import { Tooltip, Button, theme } from 'antd';
import graphql from 'babel-plugin-relay/macro';
import { TerminalIcon, PowerOffIcon, ScrollTextIcon } from 'lucide-react';
Expand Down Expand Up @@ -41,28 +44,32 @@ const isActive = (session: SessionActionButtonsFragment$data) => {
// };

const SessionActionButtons: React.FC<SessionActionButtonsProps> = (props) => {
const { t } = useTranslation();
const { token } = theme.useToken();
const appLauncher = useBackendAIAppLauncher();

const { t } = useTranslation();
const baiClient = useSuspendedBackendaiClient();

const session = useFragment(
graphql`
fragment SessionActionButtonsFragment on ComputeSessionNode {
id
row_id @required(action: NONE)
type
status
access_key
service_ports
commit_status
...TerminateSessionModalFragment
...ContainerLogModalFragment
...ContainerCommitModalFragment
}
`,
props.sessionFrgmt,
);
const [openTerminateModal, setOpenTerminateModal] = useState(false);
const [openLogModal, setOpenLogModal] = useState(false);
const [openContainerCommitModal, setOpenContainerCommitModal] =
useState(false);

// const isDisabledTermination = !['PENDING'].includes(session?.status || '') && session?.commit_status === 'ongoing'
// ${(this._isRunning && !this._isPreparing(rowData.item.status)) ||
Expand Down Expand Up @@ -111,10 +118,24 @@ const SessionActionButtons: React.FC<SessionActionButtonsProps> = (props) => {
setOpenLogModal(false);
}}
/>
{/*
<Tooltip title={t('session.RequestContainerCommit')}>
<Button icon={<ContainerIcon />} />
</Tooltip> */}
<Button
disabled={
(!baiClient.supports('image-commit') ||
!baiClient._config.enableContainerCommit) &&
session.type !== 'system'
}
icon={<DeliveredProcedureOutlined style={{ fontSize: 14 }} />}
onClick={() => {
setOpenContainerCommitModal(true);
}}
/>
</Tooltip>
<ContainerCommitModal
sessionFrgmt={session}
open={openContainerCommitModal}
onRequestClose={() => setOpenContainerCommitModal(false)}
/>
<Tooltip title={t('session.TerminateSession')}>
<Button
disabled={!isActive(session)}
Expand Down
31 changes: 21 additions & 10 deletions react/src/components/CustomizedImageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -356,28 +356,39 @@ const CustomizedImageList: React.FC<PropsWithChildren> = ({ children }) => {
id: row.id,
},
onCompleted(res, errors) {
if (!res?.forget_image_by_id?.ok) {
if (
!_.isNil(res?.forget_image_by_id) &&
!res?.forget_image_by_id?.ok
) {
message.error(res?.forget_image_by_id?.msg);
return;
} else if (!res?.untag_image_from_registry?.ok) {
}

if (
!_.isNil(res?.untag_image_from_registry) &&
!res?.untag_image_from_registry?.ok
) {
message.error(res?.untag_image_from_registry?.msg);
return;
} else if (errors && errors?.length > 0) {
}

if (errors && errors?.length > 0) {
const errorMsgList = _.map(
errors,
(error) => error.message,
);
for (const error of errorMsgList) {
message.error(error, 2.5);
}
} else {
startRefetchTransition(() => {
updateCustomizedImageListFetchKey();
});
message.success(
t('environment.CustomizedImageSuccessfullyDeleted'),
);
return;
}

startRefetchTransition(() => {
updateCustomizedImageListFetchKey();
});
message.success(
t('environment.CustomizedImageSuccessfullyDeleted'),
);
},
onError(err) {
message.error(err?.message);
Expand Down
1 change: 1 addition & 0 deletions react/src/components/SessionDetailContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const SessionDetailContent: React.FC<{
# It might be a bug in relay
...ContainerLogModalFragment
...SessionUsageMonitorFragment
...ContainerCommitModalFragment
}
legacy_session: compute_session(id: $uuid) {
image
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@
"ExportCSV": "CSV-Datei exportieren",
"FGPU": "fGPU",
"ReadLess": "Weniger lesen",
"ReadMore": "Mehr lesen..."
"ReadMore": "Mehr lesen...",
"CommitImageName": "Festgeschriebener Bildname"
},
"button": {
"Cancel": "Stornieren",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@
"ExportCSV": "εξαγωγή CSV",
"FGPU": "fGPU",
"ReadLess": "Διαβάστε λιγότερα",
"ReadMore": "Διαβάστε Περισσότερα..."
"ReadMore": "Διαβάστε Περισσότερα...",
"CommitImageName": "Όνομα δεσμευμένης εικόνας"
},
"button": {
"Cancel": "Ματαίωση",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,8 @@
"ExportCSV": "export CSV",
"FGPU": "fGPU",
"ReadLess": "Read less",
"ReadMore": "Read More..."
"ReadMore": "Read More...",
"CommitImageName": "Committed image name"
},
"modelService": {
"Services": "Services",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,8 @@
"ExportCSV": "exportar CSV",
"FGPU": "fGPU",
"ReadLess": "Leer menos",
"ReadMore": "Leer más..."
"ReadMore": "Leer más...",
"CommitImageName": "Nombre de la imagen confirmada"
},
"settings": {
"AllowAgentSideRegistration": "Permitir el registro del agente",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,8 @@
"ExportCSV": "viedä CSV",
"FGPU": "fGPU",
"ReadLess": "Lue vähemmän",
"ReadMore": "Lue lisää..."
"ReadMore": "Lue lisää...",
"CommitImageName": "Sitoutunut kuvan nimi"
},
"settings": {
"AllowAgentSideRegistration": "Salli agenttipuolen rekisteröinti",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@
"ExportCSV": "exporter CSV",
"FGPU": "fGPU",
"ReadLess": "Lire moins",
"ReadMore": "Lire la suite..."
"ReadMore": "Lire la suite...",
"CommitImageName": "Nom de l'image validée"
},
"button": {
"Cancel": "Annuler",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/id.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@
"ExportCSV": "ekspor CSV",
"FGPU": "fGPU",
"ReadLess": "Baca lebih sedikit",
"ReadMore": "Baca Selengkapnya..."
"ReadMore": "Baca Selengkapnya...",
"CommitImageName": "Nama gambar yang berkomitmen"
},
"button": {
"Cancel": "Batalkan",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@
"ExportCSV": "esporta CSV",
"FGPU": "fGPU",
"ReadLess": "Leggi di più",
"ReadMore": "Per saperne di più..."
"ReadMore": "Per saperne di più...",
"CommitImageName": "Nome dell'immagine confermata"
},
"button": {
"Cancel": "Annulla",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@
"ExportCSV": "CSVをエクスポート",
"FGPU": "fGPU",
"ReadLess": "内容折りたたみ",
"ReadMore": "もっと詳しく..."
"ReadMore": "もっと詳しく...",
"CommitImageName": "コミットされたイメージ名"
},
"button": {
"Cancel": "キャンセル",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@
"ExportCSV": "CSV로 내보내기",
"FGPU": "fGPU",
"ReadLess": "내용 접기",
"ReadMore": "더 알아보기..."
"ReadMore": "더 알아보기...",
"CommitImageName": "커밋 이미지 이름"
},
"modelService": {
"Services": "모델 서비스",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/mn.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@
"ExportCSV": "CSV экспортлох",
"FGPU": "fGPU",
"ReadLess": "Бага унш",
"ReadMore": "Цааш унших..."
"ReadMore": "Цааш унших...",
"CommitImageName": "Оруулсан зургийн нэр"
},
"button": {
"Cancel": "Цуцлах",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/ms.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@
"ExportCSV": "eksport CSV",
"FGPU": "fGPU",
"ReadLess": "Kurangkan membaca",
"ReadMore": "Baca Lagi..."
"ReadMore": "Baca Lagi...",
"CommitImageName": "Nama imej komited"
},
"button": {
"Cancel": "Batal",
Expand Down
Loading

0 comments on commit 1efdc0c

Please sign in to comment.