style: gpu instance service
This commit is contained in:
@@ -165,7 +165,7 @@ export default {
|
|||||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||||
'clusters.systemDefaultContainerRegistry.tip':
|
'clusters.systemDefaultContainerRegistry.tip':
|
||||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -180,7 +180,7 @@ export default {
|
|||||||
'clusters.namespace.title': 'Namespace',
|
'clusters.namespace.title': 'Namespace',
|
||||||
'clusters.namespace.tip':
|
'clusters.namespace.tip':
|
||||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'clusters.gpuInstances.title': 'GPU Instances',
|
'clusters.gpuInstances.title': 'GPU Instance Service',
|
||||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||||
'clusters.gpuInstances.staticAddress.tip':
|
'clusters.gpuInstances.staticAddress.tip':
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export default {
|
|||||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||||
'clusters.systemDefaultContainerRegistry.tip':
|
'clusters.systemDefaultContainerRegistry.tip':
|
||||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -180,7 +180,7 @@ export default {
|
|||||||
'clusters.namespace.title': 'Namespace',
|
'clusters.namespace.title': 'Namespace',
|
||||||
'clusters.namespace.tip':
|
'clusters.namespace.tip':
|
||||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'clusters.gpuInstances.title': 'GPU Instances',
|
'clusters.gpuInstances.title': 'GPU Instance Service',
|
||||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||||
'clusters.gpuInstances.staticAddress.tip':
|
'clusters.gpuInstances.staticAddress.tip':
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export default {
|
|||||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||||
'clusters.systemDefaultContainerRegistry.tip':
|
'clusters.systemDefaultContainerRegistry.tip':
|
||||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -181,7 +181,7 @@ export default {
|
|||||||
'clusters.namespace.title': 'Namespace',
|
'clusters.namespace.title': 'Namespace',
|
||||||
'clusters.namespace.tip':
|
'clusters.namespace.tip':
|
||||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'clusters.gpuInstances.title': 'GPU Instances',
|
'clusters.gpuInstances.title': 'GPU Instance Service',
|
||||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||||
'clusters.gpuInstances.staticAddress.tip':
|
'clusters.gpuInstances.staticAddress.tip':
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export default {
|
|||||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||||
'clusters.systemDefaultContainerRegistry.tip':
|
'clusters.systemDefaultContainerRegistry.tip':
|
||||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -181,7 +181,7 @@ export default {
|
|||||||
'clusters.namespace.title': 'Namespace',
|
'clusters.namespace.title': 'Namespace',
|
||||||
'clusters.namespace.tip':
|
'clusters.namespace.tip':
|
||||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'clusters.gpuInstances.title': 'GPU Instances',
|
'clusters.gpuInstances.title': 'GPU Instance Service',
|
||||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||||
'clusters.gpuInstances.staticAddress.tip':
|
'clusters.gpuInstances.staticAddress.tip':
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export default {
|
|||||||
'clusters.systemDefaultContainerRegistry.title': '默认容器镜像仓库',
|
'clusters.systemDefaultContainerRegistry.title': '默认容器镜像仓库',
|
||||||
'clusters.systemDefaultContainerRegistry.tip':
|
'clusters.systemDefaultContainerRegistry.tip':
|
||||||
'用于解析该集群 GPUStack 镜像的默认镜像仓库。未设置时回退到服务端默认值。',
|
'用于解析该集群 GPUStack 镜像的默认镜像仓库。未设置时回退到服务端默认值。',
|
||||||
'clusters.k8sOptions.title': 'K8s 部署选项',
|
'clusters.k8sOptions.title': 'Kubernetes 部署选项',
|
||||||
'clusters.imageCredentials.title': '镜像仓库凭证',
|
'clusters.imageCredentials.title': '镜像仓库凭证',
|
||||||
'clusters.imageCredentials.add': '添加凭证',
|
'clusters.imageCredentials.add': '添加凭证',
|
||||||
'clusters.imageCredentials.registry': '镜像仓库地址',
|
'clusters.imageCredentials.registry': '镜像仓库地址',
|
||||||
@@ -173,7 +173,7 @@ export default {
|
|||||||
'clusters.namespace.title': '命名空间',
|
'clusters.namespace.title': '命名空间',
|
||||||
'clusters.namespace.tip':
|
'clusters.namespace.tip':
|
||||||
'集群清单渲染所使用的 Kubernetes 命名空间。留空则使用 gpustack-system。',
|
'集群清单渲染所使用的 Kubernetes 命名空间。留空则使用 gpustack-system。',
|
||||||
'clusters.gpuInstances.title': 'GPU 实例',
|
'clusters.gpuInstances.title': 'GPU 实例服务',
|
||||||
'clusters.gpuInstances.tip': '为该集群启用 GPU 实例支持。',
|
'clusters.gpuInstances.tip': '为该集群启用 GPU 实例支持。',
|
||||||
'clusters.gpuInstances.staticAddress': '静态访问地址',
|
'clusters.gpuInstances.staticAddress': '静态访问地址',
|
||||||
'clusters.gpuInstances.staticAddress.tip':
|
'clusters.gpuInstances.staticAddress.tip':
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [activeKey, setActiveKey] = React.useState<string[]>([]);
|
const [activeKey, setActiveKey] = React.useState<string[]>([]);
|
||||||
// K8s deployment options is its own top-level section (sibling of Advanced),
|
|
||||||
// open by default so the fields are visible without an extra click.
|
|
||||||
const [k8sActiveKey, setK8sActiveKey] = React.useState<string[]>([
|
const [k8sActiveKey, setK8sActiveKey] = React.useState<string[]>([
|
||||||
'k8sOptions'
|
'k8sOptions'
|
||||||
]);
|
]);
|
||||||
@@ -59,20 +57,8 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
}
|
}
|
||||||
}, [activeKey, action]);
|
}, [activeKey, action]);
|
||||||
|
|
||||||
// The backend models the optional k8s_options string knobs as
|
|
||||||
// Optional[str] and treats null/absent as "use the server default" or
|
|
||||||
// "no auth". Coerce empty form values to null before sending so a blank
|
|
||||||
// input is unambiguous rather than an empty string that defeats fallbacks.
|
|
||||||
const normalizeOutgoing = (values: any): any => {
|
const normalizeOutgoing = (values: any): any => {
|
||||||
const base: any = { ...values };
|
const base: any = { ...values };
|
||||||
// Top-level cluster field shared by Docker and K8s. Trim then coerce a
|
|
||||||
// blank input to null so clearing it on edit (or a whitespace-only
|
|
||||||
// value) falls back to the server default rather than persisting an
|
|
||||||
// empty string.
|
|
||||||
if (base.system_default_container_registry !== undefined) {
|
|
||||||
base.system_default_container_registry =
|
|
||||||
base.system_default_container_registry?.trim() || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const opts = base.k8s_options;
|
const opts = base.k8s_options;
|
||||||
if (!opts) return base;
|
if (!opts) return base;
|
||||||
@@ -88,32 +74,14 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
next.operatorImage = opts.operatorImage || null;
|
|
||||||
next.namespace = opts.namespace || null;
|
|
||||||
|
|
||||||
// Presence of gpuInstanceOptions is the enable flag; keep it only when
|
|
||||||
// the toggle left an object behind, coercing a blank address to null.
|
|
||||||
if (opts.gpuInstanceOptions) {
|
|
||||||
next.gpuInstanceOptions = {
|
|
||||||
gpuInstancesAccessStaticAddress:
|
|
||||||
opts.gpuInstanceOptions.gpuInstancesAccessStaticAddress || null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...base, k8s_options: next };
|
return { ...base, k8s_options: next };
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnFinish = (_values: FormData) => {
|
const handleOnFinish = (values: FormData) => {
|
||||||
const workerConfig = yaml2Json(advanceConfigRef.current?.getYamlValue());
|
const workerConfig = yaml2Json(advanceConfigRef.current?.getYamlValue());
|
||||||
// antd's onFinish only delivers values for registered Form.Items.
|
|
||||||
// Spreading those on top of `getFieldsValue(true)` clobbers nested
|
|
||||||
// objects (e.g. `k8s_options` would lose values set via setFieldValue),
|
|
||||||
// so we go straight to the full store.
|
|
||||||
const fullValues = form.getFieldsValue(true);
|
|
||||||
|
|
||||||
onFinish(
|
onFinish(
|
||||||
normalizeOutgoing({
|
normalizeOutgoing({
|
||||||
...fullValues,
|
...values,
|
||||||
worker_config: {
|
worker_config: {
|
||||||
...workerConfig
|
...workerConfig
|
||||||
}
|
}
|
||||||
@@ -203,10 +171,6 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
validateFields: async () => {
|
validateFields: async () => {
|
||||||
// Run validation first to display any field errors. Then read the
|
|
||||||
// FULL store via `getFieldsValue(true)` so values that were set via
|
|
||||||
// setFieldValue on non-registered paths are still included in what we
|
|
||||||
// hand to the API.
|
|
||||||
await form.validateFields();
|
await form.validateFields();
|
||||||
const values = form.getFieldsValue(true);
|
const values = form.getFieldsValue(true);
|
||||||
|
|
||||||
@@ -268,7 +232,7 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
style={{ marginBottom: 8 }}
|
style={{ marginBottom: 8 }}
|
||||||
>
|
>
|
||||||
<SealTextArea
|
<SealTextArea
|
||||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
scaleSize
|
||||||
label={intl.formatMessage({ id: 'common.table.description' })}
|
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||||
></SealTextArea>
|
></SealTextArea>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -286,11 +250,6 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
label: intl.formatMessage({ id: 'clusters.k8sOptions.title' }),
|
label: intl.formatMessage({ id: 'clusters.k8sOptions.title' }),
|
||||||
forceRender: true,
|
forceRender: true,
|
||||||
children: (
|
children: (
|
||||||
// Key by cluster id so the section fully remounts when the
|
|
||||||
// active cluster changes. GpuInstanceOptionsForm seeds its
|
|
||||||
// local state from initialValue only once (initializedRef),
|
|
||||||
// so without a remount a reused form instance could carry a
|
|
||||||
// previous cluster's GPU instance config into the next one.
|
|
||||||
<K8sPodSpec
|
<K8sPodSpec
|
||||||
key={currentData?.id ?? 'new'}
|
key={currentData?.id ?? 'new'}
|
||||||
action={action}
|
action={action}
|
||||||
|
|||||||
@@ -4,10 +4,15 @@ import {
|
|||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
QuestionCircleOutlined
|
QuestionCircleOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Input as CInput, LabelSelector, useAppUtils } from '@gpustack/core-ui';
|
import {
|
||||||
|
Input as CInput,
|
||||||
|
LabelSelector,
|
||||||
|
SwitchCard,
|
||||||
|
useAppUtils
|
||||||
|
} from '@gpustack/core-ui';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Form, Switch, Tooltip } from 'antd';
|
import { Button, Form, Tooltip } from 'antd';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { GpuInstanceOptions } from '../config/types';
|
import { GpuInstanceOptions } from '../config/types';
|
||||||
import K8SVolumeMount from './k8s-volume-mount';
|
import K8SVolumeMount from './k8s-volume-mount';
|
||||||
@@ -180,6 +185,7 @@ const NamespaceForm: React.FC = () => {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
name={['k8s_options', 'namespace']}
|
name={['k8s_options', 'namespace']}
|
||||||
style={{ marginBottom: 0 }}
|
style={{ marginBottom: 0 }}
|
||||||
|
normalize={(value) => value || null}
|
||||||
>
|
>
|
||||||
<CInput.Input
|
<CInput.Input
|
||||||
label={intl.formatMessage({ id: 'clusters.namespace.title' })}
|
label={intl.formatMessage({ id: 'clusters.namespace.title' })}
|
||||||
@@ -201,6 +207,7 @@ const OperatorImageForm: React.FC = () => {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
name={['k8s_options', 'operatorImage']}
|
name={['k8s_options', 'operatorImage']}
|
||||||
style={{ marginBottom: 0 }}
|
style={{ marginBottom: 0 }}
|
||||||
|
normalize={(value) => value || null}
|
||||||
>
|
>
|
||||||
<CInput.Input
|
<CInput.Input
|
||||||
label={intl.formatMessage({ id: 'clusters.operatorImage.title' })}
|
label={intl.formatMessage({ id: 'clusters.operatorImage.title' })}
|
||||||
@@ -211,92 +218,40 @@ const OperatorImageForm: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// GPU-instance support. The backend treats the mere presence of
|
|
||||||
// `gpuInstanceOptions` as the enable flag, so the switch toggles the whole
|
|
||||||
// object in/out of the form rather than setting a boolean field; the static
|
|
||||||
// address (optional even when enabled) is nested underneath.
|
|
||||||
//
|
|
||||||
// We drive the toggle from local state (not Form.useWatch) because the
|
|
||||||
// gpuInstanceOptions path has no registered Form.Item of its own — useWatch
|
|
||||||
// doesn't reliably re-render on setFieldValue for such paths, which left the
|
|
||||||
// switch unresponsive. Local state owns the visible state and we mirror it
|
|
||||||
// into the form via setFieldValue so submit still collects it.
|
|
||||||
const GpuInstanceOptionsForm: React.FC<{
|
const GpuInstanceOptionsForm: React.FC<{
|
||||||
initialValue?: GpuInstanceOptions;
|
initialValue?: GpuInstanceOptions;
|
||||||
}> = ({ initialValue }) => {
|
}> = ({ initialValue }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const form = Form.useFormInstance();
|
|
||||||
const [enabled, setEnabled] = useState<boolean>(!!initialValue);
|
const [enabled, setEnabled] = useState<boolean>(!!initialValue);
|
||||||
const [address, setAddress] = useState<string>(
|
|
||||||
initialValue?.gpuInstancesAccessStaticAddress || ''
|
|
||||||
);
|
|
||||||
const initializedRef = useRef<boolean>(!!initialValue);
|
|
||||||
|
|
||||||
const writeForm = (en: boolean, addr: string) => {
|
|
||||||
form.setFieldValue(
|
|
||||||
['k8s_options', 'gpuInstanceOptions'],
|
|
||||||
en ? { gpuInstancesAccessStaticAddress: addr } : undefined
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mirror a seeded initial value into the form on mount so submit collects it.
|
|
||||||
useEffect(() => {
|
|
||||||
if (initialValue) {
|
|
||||||
writeForm(true, initialValue.gpuInstancesAccessStaticAddress || '');
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Adopt currentData arriving after mount (async edit load), once. After the
|
|
||||||
// user has interacted (`initializedRef`), local state owns the section.
|
|
||||||
useEffect(() => {
|
|
||||||
if (initializedRef.current) return;
|
|
||||||
if (initialValue) {
|
|
||||||
setEnabled(true);
|
|
||||||
setAddress(initialValue.gpuInstancesAccessStaticAddress || '');
|
|
||||||
writeForm(true, initialValue.gpuInstancesAccessStaticAddress || '');
|
|
||||||
initializedRef.current = true;
|
|
||||||
}
|
|
||||||
}, [initialValue]);
|
|
||||||
|
|
||||||
const handleToggle = (checked: boolean) => {
|
const handleToggle = (checked: boolean) => {
|
||||||
initializedRef.current = true;
|
|
||||||
setEnabled(checked);
|
setEnabled(checked);
|
||||||
if (!checked) {
|
|
||||||
setAddress('');
|
|
||||||
}
|
|
||||||
writeForm(checked, checked ? address : '');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddressChange = (e: any) => {
|
|
||||||
const next = typeof e === 'string' ? e : (e?.target?.value ?? '');
|
|
||||||
setAddress(next);
|
|
||||||
writeForm(true, next);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionWrap>
|
<SwitchCard
|
||||||
<Title>
|
styles={{
|
||||||
<div className="flex-center gap-8">
|
wrapper: {
|
||||||
<span className="flex-center gap-4">
|
borderRadius: 'var(--ant-border-radius-lg)',
|
||||||
<span>
|
paddingInline: 14,
|
||||||
{intl.formatMessage({ id: 'clusters.gpuInstances.title' })}
|
marginBottom: 24
|
||||||
</span>
|
}
|
||||||
<Tooltip
|
}}
|
||||||
title={intl.formatMessage({ id: 'clusters.gpuInstances.tip' })}
|
value={enabled}
|
||||||
>
|
onChange={handleToggle}
|
||||||
<QuestionCircleOutlined
|
label={intl.formatMessage({ id: 'clusters.gpuInstances.title' })}
|
||||||
style={{ color: 'var(--ant-color-text-secondary)' }}
|
description={intl.formatMessage({ id: 'clusters.gpuInstances.tip' })}
|
||||||
/>
|
>
|
||||||
</Tooltip>
|
<Form.Item
|
||||||
</span>
|
name={[
|
||||||
<Switch checked={enabled} onChange={handleToggle} />
|
'k8s_options',
|
||||||
</div>
|
'gpuInstanceOptions',
|
||||||
</Title>
|
'gpuInstancesAccessStaticAddress'
|
||||||
{enabled && (
|
]}
|
||||||
|
normalize={(value) => value || null}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
<CInput.Input
|
<CInput.Input
|
||||||
isInFormItems={false}
|
|
||||||
value={address}
|
|
||||||
onChange={handleAddressChange}
|
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'clusters.gpuInstances.staticAddress'
|
id: 'clusters.gpuInstances.staticAddress'
|
||||||
})}
|
})}
|
||||||
@@ -304,8 +259,8 @@ const GpuInstanceOptionsForm: React.FC<{
|
|||||||
id: 'clusters.gpuInstances.staticAddress.tip'
|
id: 'clusters.gpuInstances.staticAddress.tip'
|
||||||
})}
|
})}
|
||||||
></CInput.Input>
|
></CInput.Input>
|
||||||
)}
|
</Form.Item>
|
||||||
</SectionWrap>
|
</SwitchCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -315,12 +270,12 @@ const K8sPodSpec: React.FC<{
|
|||||||
}> = ({ action, initialGpuInstanceOptions }) => {
|
}> = ({ action, initialGpuInstanceOptions }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<GpuInstanceOptionsForm initialValue={initialGpuInstanceOptions} />
|
||||||
<NamespaceForm />
|
<NamespaceForm />
|
||||||
<K8SVolumeMount action={action}></K8SVolumeMount>
|
<K8SVolumeMount action={action}></K8SVolumeMount>
|
||||||
<ImageCredentialsForm />
|
<ImageCredentialsForm />
|
||||||
<NodeSelectorForm />
|
<NodeSelectorForm />
|
||||||
<OperatorImageForm />
|
<OperatorImageForm />
|
||||||
<GpuInstanceOptionsForm initialValue={initialGpuInstanceOptions} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ const ClusterAdvanceConfig: React.FC<{
|
|||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="system_default_container_registry"
|
name="system_default_container_registry"
|
||||||
style={{ marginBottom: 16 }}
|
style={{ marginBottom: 16 }}
|
||||||
|
normalize={(value) => value?.trim?.() || null}
|
||||||
>
|
>
|
||||||
<CInput.Input
|
<CInput.Input
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
|
|||||||
Reference in New Issue
Block a user