From f1bb305cbc248d14cca93253bdb63031b7b8ed1a Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 11 Sep 2025 16:39:16 +0800 Subject: [PATCH] fix: add pool not work --- src/components/seal-form/wrapper/select.ts | 5 ++ src/locales/en-US/clusters.ts | 7 ++- src/locales/ja-JP/clusters.ts | 13 ++++- src/locales/ru-RU/clusters.ts | 13 ++++- src/locales/zh-CN/clusters.ts | 6 +- .../cluster-management/cluster-create.tsx | 6 +- src/pages/cluster-management/clusters.tsx | 57 ++++--------------- .../components/add-credential.tsx | 4 +- .../components/add-pool.tsx | 20 ++++++- .../components/add-worker-command.tsx | 35 ++++++++++++ .../components/add-worker-step.tsx | 14 ++++- .../components/cloud-provider-form.tsx | 9 ++- .../components/pool-form.tsx | 42 ++++++++++++-- src/pages/cluster-management/credentials.tsx | 8 +-- .../hooks/use-cluster-columns.tsx | 4 +- .../hooks/use-provider-regions.ts | 12 ++-- .../step-forms/worker-pools-form.tsx | 5 +- src/pages/resources/config/index.ts | 4 +- 18 files changed, 185 insertions(+), 79 deletions(-) create mode 100644 src/pages/cluster-management/components/add-worker-command.tsx diff --git a/src/components/seal-form/wrapper/select.ts b/src/components/seal-form/wrapper/select.ts index 3842276c..46952010 100644 --- a/src/components/seal-form/wrapper/select.ts +++ b/src/components/seal-form/wrapper/select.ts @@ -101,6 +101,11 @@ const SelectWrapper = styled.div` // top: 20px !important; // inset-inline-start: ${INPUT_INNER_PADDING}px; } + &.ant-select-auto-complete { + .ant-select-selection-search { + inset-inline-start: ${INPUT_INNER_PADDING}px; + } + } &.ant-select-multiple.ant-cascader .ant-select-selection-search { top: 0 !important; } diff --git a/src/locales/en-US/clusters.ts b/src/locales/en-US/clusters.ts index a5df979c..ce390aa9 100644 --- a/src/locales/en-US/clusters.ts +++ b/src/locales/en-US/clusters.ts @@ -32,5 +32,10 @@ export default { 'clusters.create.configBasic': 'Basic Configuration', 'clusters.create.execCommand': 'Execute Command', 'clusters.create.supportedGpu': 'Supported GPUs', - 'clusters.create.skipfornow': 'Skip for now' + 'clusters.create.skipfornow': 'Skip for now', + 'clusters.create.noImages': 'No images available', + 'clusters.create.noInstanceTypes': 'No instance types available', + 'clusters.create.noRegions': 'No regions available', + 'clusters.workerpool.batchSize.desc': + 'Number of workers created simultaneously in the Worker pool' }; diff --git a/src/locales/ja-JP/clusters.ts b/src/locales/ja-JP/clusters.ts index b71e39c3..14f9efff 100644 --- a/src/locales/ja-JP/clusters.ts +++ b/src/locales/ja-JP/clusters.ts @@ -32,7 +32,12 @@ export default { 'clusters.create.configBasic': 'Basic Configuration', 'clusters.create.execCommand': 'Execute Command', 'clusters.create.supportedGpu': 'Supported GPUs', - 'clusters.create.skipfornow': 'Skip for now' + 'clusters.create.skipfornow': 'Skip for now', + 'clusters.create.noImages': 'No images available', + 'clusters.create.noInstanceTypes': 'No instance types available', + 'clusters.create.noRegions': 'No regions available', + 'clusters.workerpool.batchSize.desc': + 'Number of workers created simultaneously in the Worker pool' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -69,6 +74,10 @@ export default { // 31. 'clusters.create.configBasic': 'Basic Configuration', // 32. 'clusters.create.execCommand': 'Execute Command', // 33. 'clusters.create.supportedGpu': 'Supported GPUs', -// 34. 'clusters.create.skipfornow': 'Skip for now' +// 34. 'clusters.create.skipfornow': 'Skip for now', +// 35. 'clusters.create.noImages': 'No images available', +// 36. 'clusters.create.noInstanceTypes': 'No instance types available', +// 37. 'clusters.create.noRegions': 'No regions available', +// 38. 'clusters.workerpool.batchSize.desc': 'Number of workers created simultaneously in the Worker pool' // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/clusters.ts b/src/locales/ru-RU/clusters.ts index 89ebe868..e767460b 100644 --- a/src/locales/ru-RU/clusters.ts +++ b/src/locales/ru-RU/clusters.ts @@ -32,7 +32,12 @@ export default { 'clusters.create.configBasic': 'Basic Configuration', 'clusters.create.execCommand': 'Execute Command', 'clusters.create.supportedGpu': 'Supported GPUs', - 'clusters.create.skipfornow': 'Skip for now' + 'clusters.create.skipfornow': 'Skip for now', + 'clusters.create.noImages': 'No images available', + 'clusters.create.noInstanceTypes': 'No instance types available', + 'clusters.create.noRegions': 'No regions available', + 'clusters.workerpool.batchSize.desc': + 'Number of workers created simultaneously in the Worker pool' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -69,6 +74,10 @@ export default { // 31. 'clusters.create.configBasic': 'Basic Configuration', // 32. 'clusters.create.execCommand': 'Execute Command', // 33. 'clusters.create.supportedGpu': 'Supported GPUs', -// 34. 'clusters.create.skipfornow': 'Skip for now' +// 34. 'clusters.create.skipfornow': 'Skip for now', +// 35. 'clusters.create.noImages': 'No images available', +// 36. 'clusters.create.noInstanceTypes': 'No instance types available', +// 37. 'clusters.create.noRegions': 'No regions available', +// 38. 'clusters.workerpool.batchSize.desc': 'Number of workers created simultaneously in the Worker pool' // ========== End of To-Do List ========== diff --git a/src/locales/zh-CN/clusters.ts b/src/locales/zh-CN/clusters.ts index c81792d6..518dcfdd 100644 --- a/src/locales/zh-CN/clusters.ts +++ b/src/locales/zh-CN/clusters.ts @@ -32,5 +32,9 @@ export default { 'clusters.create.configBasic': '基本配置', 'clusters.create.execCommand': '执行命令', 'clusters.create.supportedGpu': '支持的 GPU', - 'clusters.create.skipfornow': '暂时跳过' + 'clusters.create.skipfornow': '暂时跳过', + 'clusters.create.noImages': '没有可用的镜像', + 'clusters.create.noInstanceTypes': '没有可用的实例类型', + 'clusters.create.noRegions': '没有可用的区域', + 'clusters.workerpool.batchSize.desc': 'Worker池中同时创建的worker数量' }; diff --git a/src/pages/cluster-management/cluster-create.tsx b/src/pages/cluster-management/cluster-create.tsx index 0c99bcb0..d614c1b0 100644 --- a/src/pages/cluster-management/cluster-create.tsx +++ b/src/pages/cluster-management/cluster-create.tsx @@ -244,7 +244,11 @@ const ClusterCreate = () => { return moduleKeys.map((key) => { const ModuleComponent = moduleRegistry[key]; return ModuleComponent ? ( - + ) : null; }); }; diff --git a/src/pages/cluster-management/clusters.tsx b/src/pages/cluster-management/clusters.tsx index b85ee4cc..9d0705d4 100644 --- a/src/pages/cluster-management/clusters.tsx +++ b/src/pages/cluster-management/clusters.tsx @@ -17,7 +17,6 @@ import { useAtom } from 'jotai'; import { useEffect, useState } from 'react'; import { CLUSTERS_API, - createCluster, createWorkerPool, deleteCluster, queryClusterList, @@ -31,7 +30,7 @@ import AddCluster from './components/add-cluster'; import AddPool from './components/add-pool'; import PoolRows from './components/pool-rows'; import RegisterCluster from './components/register-cluster'; -import { ProviderLabelMap, ProviderType, ProviderValueMap } from './config'; +import { ProviderType, ProviderValueMap } from './config'; import { ClusterListItem, ClusterFormData as FormData, @@ -50,7 +49,6 @@ const Credentials: React.FC = () => { handleDeleteBatch, fetchData, handlePageChange, - handleTableChange, handleSearch, handleNameChange } = useTableFetch({ @@ -129,40 +127,24 @@ const Credentials: React.FC = () => { title: string; provider: ProviderType; clusterId: number; + clusterData: ClusterListItem | null; }>({ open: false, action: PageAction.CREATE, title: '', provider: ProviderValueMap.DigitalOcean as ProviderType, - clusterId: 0 + clusterId: 0, + clusterData: null }); - const handleAddCluster = (value: string) => { - const label = ProviderLabelMap[value]; - const clusterLabel = - value === ProviderValueMap.Custom - ? intl.formatMessage({ id: 'clusters.provider.custom' }) - : label; - - setOpenAddModal({ - open: true, - action: PageAction.CREATE, - currentData: undefined, - title: intl.formatMessage( - { id: 'clusters.add.cluster' }, - { cluster: clusterLabel } - ), - provider: value as ProviderType - }); - }; - const handleAddPool = (row: ListItem) => { setAddPoolStatus({ open: true, action: PageAction.CREATE, title: intl.formatMessage({ id: 'clusters.button.addNodePool' }), provider: row.provider as ProviderType, - clusterId: row.id + clusterId: row.id, + clusterData: row }); }; @@ -180,27 +162,11 @@ const Credentials: React.FC = () => { data: params, id: openAddModal.currentData!.id }); - } else { - await createCluster({ data: params }); } fetchData(); - setOpenAddModal({ - open: false, - action: PageAction.CREATE, - currentData: undefined, - title: '', - provider: null - }); + message.success(intl.formatMessage({ id: 'common.message.success' })); - } catch (error) { - setOpenAddModal({ - open: false, - action: PageAction.CREATE, - currentData: undefined, - title: '', - provider: null - }); - } + } catch (error) {} }; const handleModalCancel = () => { @@ -377,10 +343,7 @@ const Credentials: React.FC = () => { > { open={addPoolStatus.open} action={addPoolStatus.action} title={addPoolStatus.title} + clusterData={addPoolStatus.clusterData} onCancel={() => { setAddPoolStatus({ open: false, action: PageAction.CREATE, title: '', provider: ProviderValueMap.DigitalOcean as ProviderType, - clusterId: 0 + clusterId: 0, + clusterData: null }); }} onOk={handleSubmitWorkerPool} diff --git a/src/pages/cluster-management/components/add-credential.tsx b/src/pages/cluster-management/components/add-credential.tsx index 7c2b9330..5157cf07 100644 --- a/src/pages/cluster-management/components/add-credential.tsx +++ b/src/pages/cluster-management/components/add-credential.tsx @@ -7,7 +7,7 @@ import useAppUtils from '@/hooks/use-app-utils'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; import React, { useEffect } from 'react'; -import { ProviderValueMap } from '../config'; +import { ProviderType, ProviderValueMap } from '../config'; import { CredentialFormData as FormData, CredentialListItem as ListItem @@ -20,7 +20,7 @@ type AddModalProps = { onOk: (values: FormData) => void; currentData?: ListItem; onCancel: () => void; - provider: string; // 'kubernetes' | 'digitalocean'; + provider: ProviderType; }; const AddModal: React.FC = ({ title, diff --git a/src/pages/cluster-management/components/add-pool.tsx b/src/pages/cluster-management/components/add-pool.tsx index 17cc3e48..0681ed8e 100644 --- a/src/pages/cluster-management/components/add-pool.tsx +++ b/src/pages/cluster-management/components/add-pool.tsx @@ -1,12 +1,14 @@ import ModalFooter from '@/components/modal-footer'; import ScrollerModal from '@/components/scroller-modal'; import { PageActionType } from '@/config/types'; -import React, { useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import { ProviderType } from '../config'; import { + ClusterListItem, NodePoolFormData as FormData, NodePoolListItem as ListItem } from '../config/types'; +import { useProviderRegions } from '../hooks/use-provider-regions'; import PoolForm from './pool-form'; type AddModalProps = { @@ -15,6 +17,7 @@ type AddModalProps = { open: boolean; provider: ProviderType; // 'kubernetes' | 'custom' | 'digitalocean'; currentData?: ListItem | null; + clusterData?: ClusterListItem | null; onOk: (values: FormData) => void; onCancel: () => void; }; @@ -25,8 +28,11 @@ const AddPool: React.FC = ({ provider, onOk, currentData, + clusterData, onCancel }) => { + const { getInstanceTypes, getOSImages, updateInstanceTypes, updateOSImages } = + useProviderRegions(); const formRef = useRef(null); const handleSubmit = async () => { @@ -42,6 +48,18 @@ const AddPool: React.FC = ({ onCancel(); }; + useEffect(() => { + const init = async (clusterData: ClusterListItem) => { + const allTyps = await getInstanceTypes(clusterData.credential_id); + const allImages = await getOSImages(clusterData.credential_id); + updateInstanceTypes(clusterData.region, allTyps); + updateOSImages(clusterData.region, allImages); + }; + if (open && clusterData && clusterData.credential_id) { + init(clusterData); + } + }, [clusterData, open]); + return ( = ({ registrationInfo }) => { + const code = React.useMemo(() => { + const commandCode = addWorkerGuide['cuda']; + return commandCode?.registerWorker({ + server: registrationInfo.server_url || origin, + tag: '', + image: registrationInfo.image, + token: registrationInfo.token || '${token}', + workerip: '${workerip}' + }); + }, [registrationInfo]); + + return ( + + ); +}; + +export default AddWorkerCommand; diff --git a/src/pages/cluster-management/components/add-worker-step.tsx b/src/pages/cluster-management/components/add-worker-step.tsx index 28373eca..f0e8b8b1 100644 --- a/src/pages/cluster-management/components/add-worker-step.tsx +++ b/src/pages/cluster-management/components/add-worker-step.tsx @@ -1,6 +1,8 @@ import { useIntl } from '@umijs/max'; import React from 'react'; import styled from 'styled-components'; +import { ProviderType, ProviderValueMap } from '../config'; +import AddWorkerCommand from './add-worker-command'; import RegisterClusterInner from './register-cluster-inner'; import SupportedHardware from './support-hardware'; @@ -11,6 +13,7 @@ const Title = styled.div` `; type AddModalProps = { + provider: ProviderType; registrationInfo: { token: string; image: string; @@ -18,12 +21,19 @@ type AddModalProps = { cluster_id: number; }; }; -const AddWorkerStep: React.FC = ({ registrationInfo }) => { +const AddWorkerStep: React.FC = ({ + provider, + registrationInfo +}) => { const intl = useIntl(); return (
{intl.formatMessage({ id: 'clusters.create.execCommand' })} - + {provider === ProviderValueMap.Kubernetes ? ( + + ) : ( + + )} {intl.formatMessage({ id: 'clusters.create.supportedGpu' })} diff --git a/src/pages/cluster-management/components/cloud-provider-form.tsx b/src/pages/cluster-management/components/cloud-provider-form.tsx index 4eb6de29..65103fc5 100644 --- a/src/pages/cluster-management/components/cloud-provider-form.tsx +++ b/src/pages/cluster-management/components/cloud-provider-form.tsx @@ -55,6 +55,7 @@ interface CloudProviderProps { } const NotFoundContent: React.FC<{ loading: boolean }> = ({ loading }) => { + const intl = useIntl(); if (loading) { return ( @@ -62,7 +63,11 @@ const NotFoundContent: React.FC<{ loading: boolean }> = ({ loading }) => { ); } - return No regions available; + return ( + + {intl.formatMessage({ id: 'clusters.create.noRegions' })} + + ); }; const optionRender = ( @@ -159,6 +164,7 @@ const CloudProvider: React.FC = (props) => { ]} > = (props) => { > { - return No instance type available; + const intl = useIntl(); + return ( + + {intl.formatMessage({ id: 'clusters.create.noInstanceTypes' })} + + ); +}; + +const NotFoundImageContent = () => { + const intl = useIntl(); + return ( + + {intl.formatMessage({ id: 'clusters.create.noImages' })} + + ); }; export const RenderInstanceOption = (option: any) => { @@ -171,7 +186,8 @@ const PoolForm: React.FC = forwardRef((props, ref) => { } }, [currentData]); - const labelRender = (data: { label: string; value: string }) => { + const imageLabelRender = (data: { label: string; value: string }) => { + console.log('imageLabelRender========', data); if (action === PageAction.EDIT) { return currentData?.image_name || currentData?.os_image; } @@ -187,7 +203,8 @@ const PoolForm: React.FC = forwardRef((props, ref) => { const handleOsImageChange = (value: string) => { form.setFieldsValue({ - image_name: osImageList.find((item) => item.value === value)?.label + image_name: + osImageList.find((item) => item.value === value)?.label || value }); }; @@ -215,6 +232,14 @@ const PoolForm: React.FC = forwardRef((props, ref) => { ); }; + const filterImageOption = (inputValue: string, option: any) => { + return ( + option.label.toLowerCase().includes(inputValue.toLowerCase()) || + option.value.toLowerCase().includes(inputValue.toLowerCase()) || + option.description.toLowerCase().includes(inputValue.toLowerCase()) + ); + }; + useImperativeHandle(ref, () => ({ resetFields: () => { form.resetFields(); @@ -337,6 +362,9 @@ const PoolForm: React.FC = forwardRef((props, ref) => { ]} > = forwardRef((props, ref) => { } ]} > - } onChange={handleOsImageChange} + filterOption={filterImageOption} optionRender={RenderInstanceOption} - labelRender={labelRender} + labelRender={imageLabelRender} options={osImageList} disabled={action === PageAction.EDIT} label={intl.formatMessage({ id: 'clusters.workerpool.osImage' })} required - > + > diff --git a/src/pages/cluster-management/credentials.tsx b/src/pages/cluster-management/credentials.tsx index c4610b1f..958b12e2 100644 --- a/src/pages/cluster-management/credentials.tsx +++ b/src/pages/cluster-management/credentials.tsx @@ -16,7 +16,7 @@ import { updateCredential } from './apis'; import AddModal from './components/add-credential'; -import { ProviderLabelMap, ProviderValueMap } from './config'; +import { ProviderLabelMap, ProviderType, ProviderValueMap } from './config'; import { CredentialFormData as FormData, CredentialListItem as ListItem @@ -55,13 +55,13 @@ const Credentials: React.FC = () => { const intl = useIntl(); const [openModalStatus, setOpenModalStatus] = useState<{ - provider: string; + provider: ProviderType; open: boolean; action: PageActionType; title: string; currentData: ListItem | undefined; }>({ - provider: '', + provider: null, open: false, action: PageAction.CREATE, title: '', @@ -70,7 +70,7 @@ const Credentials: React.FC = () => { const handleAddCredential = (item: { key: string; label: string }) => { setOpenModalStatus({ - provider: item.key, + provider: item.key as ProviderType, open: true, action: PageAction.CREATE, title: intl.formatMessage( diff --git a/src/pages/cluster-management/hooks/use-cluster-columns.tsx b/src/pages/cluster-management/hooks/use-cluster-columns.tsx index 0c6c2fe1..cc8537e6 100644 --- a/src/pages/cluster-management/hooks/use-cluster-columns.tsx +++ b/src/pages/cluster-management/hooks/use-cluster-columns.tsx @@ -1,9 +1,9 @@ // columns.ts import AutoTooltip from '@/components/auto-tooltip'; import DropdownButtons from '@/components/drop-down-buttons'; +import { SealColumnProps } from '@/components/seal-table/types'; import StatusTag from '@/components/status-tag'; import { Link, useIntl } from '@umijs/max'; -import { ColumnsType } from 'antd/es/table'; import dayjs from 'dayjs'; import { useMemo } from 'react'; import { @@ -25,7 +25,7 @@ const setActionsItems = (row: ClusterListItem) => { const useClusterColumns = ( handleSelect: (val: string, record: ClusterListItem) => void -): ColumnsType => { +): SealColumnProps[] => { const intl = useIntl(); return useMemo(() => { diff --git a/src/pages/cluster-management/hooks/use-provider-regions.ts b/src/pages/cluster-management/hooks/use-provider-regions.ts index 5b815af8..cd202066 100644 --- a/src/pages/cluster-management/hooks/use-provider-regions.ts +++ b/src/pages/cluster-management/hooks/use-provider-regions.ts @@ -130,6 +130,7 @@ export const useProviderRegions = () => { }; }); setAllInstanceTypes(list); + return list; } catch (error) { setAllInstanceTypes([]); } @@ -155,18 +156,21 @@ export const useProviderRegions = () => { }; }); setAllOSImageList(list); + return list; } catch (error) {} }; - const updateInstanceTypes = (region: string) => { - const sizes = allInstanceTypes.filter((item) => + const updateInstanceTypes = (region: string, allTypes?: any[]) => { + const sizes = (allTypes || allInstanceTypes).filter((item) => item.regions.includes(region) ); setInstanceTypes(sizes); }; - const updateOSImages = (region: string) => { - const list = allOSImageList.filter((item) => item.regions.includes(region)); + const updateOSImages = (region: string, allImages?: any[]) => { + const list = (allImages || allOSImageList).filter((item) => + item.regions.includes(region) + ); setOSImageList(list); }; diff --git a/src/pages/cluster-management/step-forms/worker-pools-form.tsx b/src/pages/cluster-management/step-forms/worker-pools-form.tsx index 7e110f17..b0eb6062 100644 --- a/src/pages/cluster-management/step-forms/worker-pools-form.tsx +++ b/src/pages/cluster-management/step-forms/worker-pools-form.tsx @@ -100,7 +100,6 @@ const WorkerPoolsForm = forwardRef((props: WorkerPoolsFormProps, ref) => { const gatherFormValues = (results: PromiseSettledResult[]) => { const resultList = results.map((result: PromiseSettledResult) => { - console.log('gatherFormValues========', results); if (result.status === 'fulfilled') { return result.value; } @@ -109,8 +108,8 @@ const WorkerPoolsForm = forwardRef((props: WorkerPoolsFormProps, ref) => { } return {}; }); - - return resultList; + console.log('gatherFormValues========', resultList); + return resultList.filter((item) => item); }; const validateFields = async () => { diff --git a/src/pages/resources/config/index.ts b/src/pages/resources/config/index.ts index ddbb03a2..5a4b6d50 100644 --- a/src/pages/resources/config/index.ts +++ b/src/pages/resources/config/index.ts @@ -17,7 +17,9 @@ export const WorkerStatusMapValue = { [WorkerStatusMap.unreachable]: 'Unreachable', [WorkerStatusMap.provisioning]: 'Provisioning', [WorkerStatusMap.deleting]: 'Deleting', - [WorkerStatusMap.error]: 'Error' + [WorkerStatusMap.error]: 'Error', + [WorkerStatusMap.provisioned]: 'Provisioned', + [WorkerStatusMap.pending]: 'Pending' }; export const status: any = {