feat: set default cluster
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
|||||||
KubernetesOutlined,
|
KubernetesOutlined,
|
||||||
ProfileOutlined,
|
ProfileOutlined,
|
||||||
RetweetOutlined,
|
RetweetOutlined,
|
||||||
|
StarOutlined,
|
||||||
ThunderboltOutlined
|
ThunderboltOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -58,7 +59,8 @@ const icons = {
|
|||||||
LockPerson: React.createElement(IconFont, { type: 'icon-lock_person' }),
|
LockPerson: React.createElement(IconFont, { type: 'icon-lock_person' }),
|
||||||
LockOpen: React.createElement(IconFont, { type: 'icon-lock_open' }),
|
LockOpen: React.createElement(IconFont, { type: 'icon-lock_open' }),
|
||||||
Permission: React.createElement(IconFont, { type: 'icon-permission' }),
|
Permission: React.createElement(IconFont, { type: 'icon-permission' }),
|
||||||
CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' })
|
CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' }),
|
||||||
|
StarOutlined: React.createElement(StarOutlined)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default icons;
|
export default icons;
|
||||||
|
|||||||
@@ -110,5 +110,7 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.table.ip.internal': 'Internal',
|
'clusters.table.ip.internal': 'Internal',
|
||||||
'clusters.table.ip.external': 'External',
|
'clusters.table.ip.external': 'External',
|
||||||
'clusters.form.serverUrl.tips':
|
'clusters.form.serverUrl.tips':
|
||||||
'Specify the server URL accessible from your cloud provider.'
|
'Specify the server URL accessible from your cloud provider.',
|
||||||
|
'clusters.form.setDefault': 'Set as Default',
|
||||||
|
'clusters.form.setDefault.tips': 'Default for deployment.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
'clusters.table.ip.internal': 'Internal',
|
'clusters.table.ip.internal': 'Internal',
|
||||||
'clusters.table.ip.external': 'External',
|
'clusters.table.ip.external': 'External',
|
||||||
'clusters.form.serverUrl.tips':
|
'clusters.form.serverUrl.tips':
|
||||||
'Specify the server URL accessible from your cloud provider.'
|
'Specify the server URL accessible from your cloud provider.',
|
||||||
|
'clusters.form.setDefault': 'Set as Default',
|
||||||
|
'clusters.form.setDefault.tips': 'Default for deployment.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -200,4 +202,6 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
|||||||
// 80. 'clusters.table.ip.external': 'External',
|
// 80. 'clusters.table.ip.external': 'External',
|
||||||
// 81. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.',
|
// 81. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.',
|
||||||
// 82. 'clusters.addworker.externalIP.tips': 'Specify an external IP if the worker is in a VPC or private network.',
|
// 82. 'clusters.addworker.externalIP.tips': 'Specify an external IP if the worker is in a VPC or private network.',
|
||||||
|
// 83. 'clusters.form.setDefault': 'Set as Default',
|
||||||
|
// 84. 'clusters.form.setDefault.tips': 'Default for deployment'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ export default {
|
|||||||
'clusters.table.ip.internal': 'Internal',
|
'clusters.table.ip.internal': 'Internal',
|
||||||
'clusters.table.ip.external': 'External',
|
'clusters.table.ip.external': 'External',
|
||||||
'clusters.form.serverUrl.tips':
|
'clusters.form.serverUrl.tips':
|
||||||
'Specify the server URL accessible from your cloud provider.'
|
'Specify the server URL accessible from your cloud provider.',
|
||||||
|
'clusters.form.setDefault': 'Set as Default',
|
||||||
|
'clusters.form.setDefault.tips': 'Default for deployment.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -130,4 +132,6 @@ export default {
|
|||||||
// 11. 'clusters.table.ip.external': 'External',
|
// 11. 'clusters.table.ip.external': 'External',
|
||||||
// 12. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.'
|
// 12. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.'
|
||||||
// 13. 'clusters.addworker.specifyWorkerIP': 'Specify Worker IP <span class="text-tertiary">{type}</span>',
|
// 13. 'clusters.addworker.specifyWorkerIP': 'Specify Worker IP <span class="text-tertiary">{type}</span>',
|
||||||
|
// 14. 'clusters.form.setDefault': 'Set as Default',
|
||||||
|
// 15. 'clusters.form.setDefault.tips': 'Default for deployment'
|
||||||
// ================================================================
|
// ================================================================
|
||||||
|
|||||||
@@ -106,5 +106,7 @@ export default {
|
|||||||
'clusters.addworker.dataVolume.tips': '为 GPUStack 指定数据存储路径。',
|
'clusters.addworker.dataVolume.tips': '为 GPUStack 指定数据存储路径。',
|
||||||
'clusters.table.ip.internal': '内',
|
'clusters.table.ip.internal': '内',
|
||||||
'clusters.table.ip.external': '外',
|
'clusters.table.ip.external': '外',
|
||||||
'clusters.form.serverUrl.tips': '指定可从您的云服务提供商访问的服务器地址。'
|
'clusters.form.serverUrl.tips': '指定可从您的云服务提供商访问的服务器地址。',
|
||||||
|
'clusters.form.setDefault': '设为默认',
|
||||||
|
'clusters.form.setDefault.tips': '部署时的默认集群。'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { ListItem } from '../config/types';
|
|||||||
|
|
||||||
interface ColumnsHookProps {
|
interface ColumnsHookProps {
|
||||||
handleSelect: (val: string, record: ListItem) => void;
|
handleSelect: (val: string, record: ListItem) => void;
|
||||||
sortOrder: string;
|
sortOrder: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionList: Global.ActionItem[] = [
|
const actionList: Global.ActionItem[] = [
|
||||||
|
|||||||
@@ -198,3 +198,9 @@ export async function querySystemConfig() {
|
|||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setDefaultCluster(params: { id: number }) {
|
||||||
|
return request(`${CLUSTERS_API}/${params.id}/set-default`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,13 @@ import _ from 'lodash';
|
|||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { PageContainerInner } from '../_components/page-box';
|
import { PageContainerInner } from '../_components/page-box';
|
||||||
import { createCluster, queryClusterToken, queryCredentialList } from './apis';
|
import {
|
||||||
|
createCluster,
|
||||||
|
queryClusterList,
|
||||||
|
queryClusterToken,
|
||||||
|
queryCredentialList,
|
||||||
|
setDefaultCluster
|
||||||
|
} from './apis';
|
||||||
import {
|
import {
|
||||||
DockerStepsFromCluster,
|
DockerStepsFromCluster,
|
||||||
K8sStepsFromCluter
|
K8sStepsFromCluter
|
||||||
@@ -248,6 +254,20 @@ const ClusterCreate = () => {
|
|||||||
return step?.showButtons ? step?.showButtons(extraData.provider) : {};
|
return step?.showButtons ? step?.showButtons(extraData.provider) : {};
|
||||||
}, [currentStep, steps, extraData.provider]);
|
}, [currentStep, steps, extraData.provider]);
|
||||||
|
|
||||||
|
const checkDefaultCluster = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryClusterList({ page: 1, perPage: 10 });
|
||||||
|
if (res.items.length === 1) {
|
||||||
|
const cluster = res.items[0];
|
||||||
|
if (!cluster.is_default) {
|
||||||
|
await setDefaultCluster({
|
||||||
|
id: cluster.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
const submit = async (values: ClusterFormData) => {
|
const submit = async (values: ClusterFormData) => {
|
||||||
const data = {
|
const data = {
|
||||||
...extraData,
|
...extraData,
|
||||||
@@ -259,6 +279,7 @@ const ClusterCreate = () => {
|
|||||||
...info,
|
...info,
|
||||||
cluster_id: res.id
|
cluster_id: res.id
|
||||||
});
|
});
|
||||||
|
checkDefaultCluster();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
queryClusterList,
|
queryClusterList,
|
||||||
queryCredentialList,
|
queryCredentialList,
|
||||||
queryWorkerPools,
|
queryWorkerPools,
|
||||||
|
setDefaultCluster,
|
||||||
updateCluster,
|
updateCluster,
|
||||||
WORKER_POOLS_API
|
WORKER_POOLS_API
|
||||||
} from './apis';
|
} from './apis';
|
||||||
@@ -192,6 +193,10 @@ const Clusters: React.FC = () => {
|
|||||||
} else if (val === 'register_cluster') {
|
} else if (val === 'register_cluster') {
|
||||||
handleAddWorker(row);
|
handleAddWorker(row);
|
||||||
setStepList(K8sStepsFromCluter);
|
setStepList(K8sStepsFromCluter);
|
||||||
|
} else if (val === 'isDefault') {
|
||||||
|
setDefaultCluster({ id: row.id }).then(() => {
|
||||||
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,11 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
credentialList={credentialList}
|
credentialList={credentialList}
|
||||||
></CloudProvider>
|
></CloudProvider>
|
||||||
)}
|
)}
|
||||||
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
<Form.Item<FormData>
|
||||||
|
name="description"
|
||||||
|
rules={[{ required: false }]}
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
>
|
||||||
<SealTextArea
|
<SealTextArea
|
||||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||||
label={intl.formatMessage({ id: 'common.table.description' })}
|
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||||
|
|||||||
@@ -112,6 +112,11 @@ export const clusterActionList = [
|
|||||||
locale: true,
|
locale: true,
|
||||||
icon: icons.Catalog1
|
icon: icons.Catalog1
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'isDefault',
|
||||||
|
label: 'clusters.form.setDefault',
|
||||||
|
icon: icons.StarOutlined
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
label: 'common.button.delete',
|
label: 'common.button.delete',
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export interface NodePoolListItem extends NodePoolFormData {
|
|||||||
export interface ClusterListItem {
|
export interface ClusterListItem {
|
||||||
name: string;
|
name: string;
|
||||||
display_name: string;
|
display_name: string;
|
||||||
|
is_default: boolean;
|
||||||
description: string;
|
description: string;
|
||||||
worker_config: Record<string, any>;
|
worker_config: Record<string, any>;
|
||||||
provider: ProviderType;
|
provider: ProviderType;
|
||||||
@@ -72,6 +73,7 @@ export interface ClusterFormData {
|
|||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
provider: ProviderType;
|
provider: ProviderType;
|
||||||
|
is_default?: boolean;
|
||||||
credential_id: number;
|
credential_id: number;
|
||||||
zone: string;
|
zone: string;
|
||||||
region: string;
|
region: string;
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import DropdownButtons from '@/components/drop-down-buttons';
|
|||||||
import { SealColumnProps } from '@/components/seal-table/types';
|
import { SealColumnProps } from '@/components/seal-table/types';
|
||||||
import StatusTag from '@/components/status-tag';
|
import StatusTag from '@/components/status-tag';
|
||||||
import { tableSorter } from '@/config/settings';
|
import { tableSorter } from '@/config/settings';
|
||||||
|
import { StarFilled } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Tooltip } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
@@ -37,7 +39,20 @@ const useClusterColumns = (
|
|||||||
sorter: tableSorter(1),
|
sorter: tableSorter(1),
|
||||||
span: 3,
|
span: 3,
|
||||||
render: (text: string, record: ClusterListItem) => (
|
render: (text: string, record: ClusterListItem) => (
|
||||||
<AutoTooltip ghost>{text}</AutoTooltip>
|
<>
|
||||||
|
<AutoTooltip ghost>{text}</AutoTooltip>
|
||||||
|
{record.is_default && (
|
||||||
|
<Tooltip
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'clusters.form.setDefault.tips'
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<StarFilled
|
||||||
|
style={{ color: 'var(--ant-gold-4)', marginLeft: 4 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -191,6 +191,10 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initClusterId = (): number => {
|
const initClusterId = (): number => {
|
||||||
|
const defaultCluster = clusterList?.find((item) => item.is_default);
|
||||||
|
if (defaultCluster) {
|
||||||
|
return defaultCluster.value;
|
||||||
|
}
|
||||||
const cluster_id =
|
const cluster_id =
|
||||||
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
|
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
|
||||||
?.value || clusterList?.[0]?.value;
|
?.value || clusterList?.[0]?.value;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ type AddModalProps = {
|
|||||||
deploymentType?: 'modelList' | 'modelFiles';
|
deploymentType?: 'modelList' | 'modelFiles';
|
||||||
clusterList: Global.BaseOption<
|
clusterList: Global.BaseOption<
|
||||||
number,
|
number,
|
||||||
{ provider: string; state: string | number }
|
{ provider: string; state: string | number; is_default: boolean }
|
||||||
>[];
|
>[];
|
||||||
onOk: (values: FormData) => void;
|
onOk: (values: FormData) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
@@ -441,6 +441,12 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
if (initialValues?.cluster_id) {
|
if (initialValues?.cluster_id) {
|
||||||
return initialValues.cluster_id;
|
return initialValues.cluster_id;
|
||||||
}
|
}
|
||||||
|
// Find default cluster
|
||||||
|
const defaultCluster = clusterList?.find((item) => item.is_default);
|
||||||
|
if (defaultCluster) {
|
||||||
|
return defaultCluster.value;
|
||||||
|
}
|
||||||
|
|
||||||
const cluster_id =
|
const cluster_id =
|
||||||
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
|
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
|
||||||
?.value || clusterList?.[0]?.value;
|
?.value || clusterList?.[0]?.value;
|
||||||
|
|||||||
@@ -217,7 +217,8 @@ export const useGenerateWorkerOptions = () => {
|
|||||||
label: item.name,
|
label: item.name,
|
||||||
value: item.id,
|
value: item.id,
|
||||||
provider: item.provider as string,
|
provider: item.provider as string,
|
||||||
state: item.state
|
state: item.state,
|
||||||
|
is_default: item.is_default
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -235,7 +236,10 @@ export default function useFormInitialValues() {
|
|||||||
const [, setWorkerListAtom] = useAtom(workerListAtom);
|
const [, setWorkerListAtom] = useAtom(workerListAtom);
|
||||||
|
|
||||||
const [clusterList, setClusterList] = useState<
|
const [clusterList, setClusterList] = useState<
|
||||||
Global.BaseOption<number, { provider: string; state: string }>[]
|
Global.BaseOption<
|
||||||
|
number,
|
||||||
|
{ provider: string; state: string; is_default: boolean }
|
||||||
|
>[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
const [workerList, setWorkerList] = useState<WorkerListItem[]>([]);
|
const [workerList, setWorkerList] = useState<WorkerListItem[]>([]);
|
||||||
@@ -249,7 +253,8 @@ export default function useFormInitialValues() {
|
|||||||
label: item.name,
|
label: item.name,
|
||||||
value: item.id,
|
value: item.id,
|
||||||
provider: item.provider as string,
|
provider: item.provider as string,
|
||||||
state: item.state
|
state: item.state,
|
||||||
|
is_default: item.is_default
|
||||||
}));
|
}));
|
||||||
setClusterList(list);
|
setClusterList(list);
|
||||||
setClusterListAtom(list);
|
setClusterListAtom(list);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { initialPasswordAtom } from '@/atoms/user';
|
import { initialPasswordAtom } from '@/atoms/user';
|
||||||
|
import { clearStorageUserSettings } from '@/atoms/utils';
|
||||||
import {
|
import {
|
||||||
CRYPT_TEXT,
|
CRYPT_TEXT,
|
||||||
REMEMBER_ME_KEY,
|
REMEMBER_ME_KEY,
|
||||||
@@ -81,7 +82,7 @@ export const useLocalAuth = ({
|
|||||||
if (userInfo?.require_password_change) {
|
if (userInfo?.require_password_change) {
|
||||||
setInitialPassword(encryptPassword(values.password));
|
setInitialPassword(encryptPassword(values.password));
|
||||||
}
|
}
|
||||||
|
clearStorageUserSettings();
|
||||||
onSuccess?.(userInfo);
|
onSuccess?.(userInfo);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
onError?.(error);
|
onError?.(error);
|
||||||
|
|||||||
Reference in New Issue
Block a user