diff --git a/frontend/providers/applaunchpad/src/api/app.ts b/frontend/providers/applaunchpad/src/api/app.ts index adc06655ee2..b18c98e2d7c 100644 --- a/frontend/providers/applaunchpad/src/api/app.ts +++ b/frontend/providers/applaunchpad/src/api/app.ts @@ -7,7 +7,7 @@ import { adaptMetrics, adaptEvents } from '@/utils/adapt'; -import type { AppPatchPropsType } from '@/types/app'; +import type { AppPatchPropsType, PodDetailType } from '@/types/app'; import { MonitorDataResult, MonitorQueryKey } from '@/types/monitor'; export const postDeployApp = (yamlList: string[]) => POST('/api/applyApp', { yamlList }); @@ -27,7 +27,7 @@ export const getAppByName = (name: string) => GET(`/api/getAppByAppName?appName=${name}`).then(adaptAppDetail); export const getAppPodsByAppName = (name: string) => - GET('/api/getAppPodsByAppName', { name }).then((item) => item.map(adaptPod)); + GET('/api/getAppPodsByAppName', { name }); export const getPodsMetrics = (podsName: string[]) => POST('/api/getPodsMetrics', { podsName }).then((item) => diff --git a/frontend/providers/applaunchpad/src/pages/api/getAppPodsByAppName.ts b/frontend/providers/applaunchpad/src/pages/api/getAppPodsByAppName.ts index ee0ccf45a34..f398b7a24f1 100644 --- a/frontend/providers/applaunchpad/src/pages/api/getAppPodsByAppName.ts +++ b/frontend/providers/applaunchpad/src/pages/api/getAppPodsByAppName.ts @@ -3,6 +3,7 @@ import { ApiResp } from '@/services/kubernet'; import { authSession } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; +import { adaptPod } from '@/utils/adapt'; // get App Metrics By DeployName. compute average value export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -30,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< ); jsonRes(res, { - data: pods + data: pods.map((item) => adaptPod(item)) }); } catch (err: any) { // console.log(err, 'get metrics error') diff --git a/frontend/providers/applaunchpad/src/pages/app/edit/index.tsx b/frontend/providers/applaunchpad/src/pages/app/edit/index.tsx index 5607697e0bd..1ec0a53ded7 100644 --- a/frontend/providers/applaunchpad/src/pages/app/edit/index.tsx +++ b/frontend/providers/applaunchpad/src/pages/app/edit/index.tsx @@ -201,8 +201,7 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) => t, applySuccess, userSourcePrice?.gpu, - refetchPrice, - isGuided + refetchPrice ] ); @@ -322,7 +321,7 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) => formHook.setValue('networks', completeNetworks); } } catch (error) {} - }, []); + }, [router.query]); return ( <> diff --git a/frontend/providers/dbprovider/public/locales/en/common.json b/frontend/providers/dbprovider/public/locales/en/common.json index 9f051b919e5..93c6f82c577 100644 --- a/frontend/providers/dbprovider/public/locales/en/common.json +++ b/frontend/providers/dbprovider/public/locales/en/common.json @@ -75,7 +75,7 @@ "backup_database": "Backup Database", "backup_deleting": "Purging Backup", "backup_failed": "Backup Failed", - "backup_list": "Backup History", + "backup_list": "Data Backups", "backup_name": "Backup Name", "backup_name_cannot_empty": "Must provide backup name", "backup_processing": "Saving Backup", @@ -316,5 +316,6 @@ "within_1_hour": "Within 1 hour", "within_5_minutes": "Within 5 minutes", "yaml_file": "YAML", - "you_have_successfully_deployed_database": "You have successfully deployed and created a database!" + "you_have_successfully_deployed_database": "You have successfully deployed and created a database!", + "database_name_max_length": "Database name length cannot exceed {{length}} characters" } \ No newline at end of file diff --git a/frontend/providers/dbprovider/public/locales/zh/common.json b/frontend/providers/dbprovider/public/locales/zh/common.json index b34eb47203b..8c5fe2582b8 100644 --- a/frontend/providers/dbprovider/public/locales/zh/common.json +++ b/frontend/providers/dbprovider/public/locales/zh/common.json @@ -75,7 +75,7 @@ "backup_database": "备份数据库", "backup_deleting": "删除中", "backup_failed": "备份失败", - "backup_list": "备份历史", + "backup_list": "数据备份", "backup_name": "备份名", "backup_name_cannot_empty": "备份名称不能为空", "backup_processing": "备份中", @@ -316,5 +316,6 @@ "within_1_hour": "一小时内", "within_5_minutes": "五分钟内", "yaml_file": "YAML 文件", - "you_have_successfully_deployed_database": "您已成功部署创建一个数据库!" -} + "you_have_successfully_deployed_database": "您已成功部署创建一个数据库!", + "database_name_max_length": "数据库名长度不能超过 {{length}} 个字符" +} \ No newline at end of file diff --git a/frontend/providers/dbprovider/src/api/backup.ts b/frontend/providers/dbprovider/src/api/backup.ts index f4bfaa496a4..02c0baa180e 100644 --- a/frontend/providers/dbprovider/src/api/backup.ts +++ b/frontend/providers/dbprovider/src/api/backup.ts @@ -11,7 +11,7 @@ import type { Props as UpdatePolicyProps } from '@/pages/api/backup/updatePolicy * for the specific database in the cluster. * * To update the auto-backup policy, use the PATCH operation on the 'cluster spec backup' resource. - * + * @deprecated * @param data - Object containing information about the database, including dbName and dbType. * @returns {Promise} - A promise resolving to the auto-backup configuration form. */ diff --git a/frontend/providers/dbprovider/src/api/db.ts b/frontend/providers/dbprovider/src/api/db.ts index 58d416b18ea..1ff7eb6d159 100644 --- a/frontend/providers/dbprovider/src/api/db.ts +++ b/frontend/providers/dbprovider/src/api/db.ts @@ -43,7 +43,7 @@ export const applyYamlList = (yamlList: string[], type: 'create' | 'replace' | ' POST('/api/applyYamlList', { yamlList, type }); export const getPodsByDBName = (name: string): Promise => - GET('/api/pod/getPodsByDBName', { name }).then((res) => res.map(adaptPod)); + GET('/api/pod/getPodsByDBName', { name }); export const getPodLogs = (data: { dbName: string; @@ -66,21 +66,11 @@ export const restartDB = (data: { dbName: string; dbType: DBType }) => { return applyYamlList([yaml], 'update'); }; -export const pauseDBByName = (data: { dbName: string; dbType: DBType }) => { - const yaml = json2StartOrStop({ - ...data, - type: 'Stop' - }); - return applyYamlList([yaml], 'update'); -}; +export const pauseDBByName = (data: { dbName: string; dbType: DBType }) => + POST('/api/pauseDBByName', data); -export const startDBByName = (data: { dbName: string; dbType: DBType }) => { - const yaml = json2StartOrStop({ - ...data, - type: 'Start' - }); - return applyYamlList([yaml], 'update'); -}; +export const startDBByName = (data: { dbName: string; dbType: DBType }) => + POST('/api/startDBByName', data); export const getDBServiceByName = (name: string) => GET(`/api/getServiceByName?name=${name}`); diff --git a/frontend/providers/dbprovider/src/pages/api/createDB.ts b/frontend/providers/dbprovider/src/pages/api/createDB.ts index 3daf8e84620..76126e612c9 100644 --- a/frontend/providers/dbprovider/src/pages/api/createDB.ts +++ b/frontend/providers/dbprovider/src/pages/api/createDB.ts @@ -4,7 +4,7 @@ import { jsonRes } from '@/services/backend/response'; import { ApiResp } from '@/services/kubernet'; import { KbPgClusterType } from '@/types/cluster'; import { BackupItemType, DBEditType } from '@/types/db'; -import { json2Account, json2CreateCluster } from '@/utils/json2Yaml'; +import { json2Account, json2ClusterOps, json2CreateCluster } from '@/utils/json2Yaml'; import type { NextApiRequest, NextApiResponse } from 'next'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -20,8 +20,63 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< }); if (isEdit) { - const cluster = json2CreateCluster(dbForm); - await applyYamlList([cluster], 'replace'); + const { body } = (await k8sCustomObjects.getNamespacedCustomObject( + 'apps.kubeblocks.io', + 'v1alpha1', + namespace, + 'clusters', + dbForm.dbName + )) as { + body: KbPgClusterType; + }; + + const currentConfig = { + cpu: parseInt(body.spec.componentSpecs[0].resources.limits.cpu.replace('m', '')), + memory: parseInt(body.spec.componentSpecs[0].resources.limits.memory.replace('Mi', '')), + replicas: body.spec.componentSpecs[0].replicas, + storage: parseInt( + body.spec.componentSpecs[0].volumeClaimTemplates[0].spec.resources.requests.storage.replace( + 'Gi', + '' + ) + ) + }; + + const opsRequests = []; + + if (currentConfig.cpu !== dbForm.cpu || currentConfig.memory !== dbForm.memory) { + const verticalScalingYaml = json2ClusterOps(dbForm, 'VerticalScaling'); + opsRequests.push(verticalScalingYaml); + } + + if (currentConfig.replicas !== dbForm.replicas) { + const horizontalScalingYaml = json2ClusterOps(dbForm, 'HorizontalScaling'); + opsRequests.push(horizontalScalingYaml); + } + + if (dbForm.storage > currentConfig.storage) { + const volumeExpansionYaml = json2ClusterOps(dbForm, 'VolumeExpansion'); + opsRequests.push(volumeExpansionYaml); + } + + console.log('DB Edit Operation:', { + dbName: dbForm.dbName, + changes: { + cpu: currentConfig.cpu !== dbForm.cpu, + memory: currentConfig.memory !== dbForm.memory, + replicas: currentConfig.replicas !== dbForm.replicas, + storage: dbForm.storage > currentConfig.storage + }, + opsCount: opsRequests.length + }); + + if (opsRequests.length > 0) { + await applyYamlList(opsRequests, 'create'); + return jsonRes(res, { + data: `Successfully submitted ${opsRequests.length} change requests` + }); + } + return jsonRes(res, { data: 'success update db' }); diff --git a/frontend/providers/dbprovider/src/pages/api/getStatefulSetByName.ts b/frontend/providers/dbprovider/src/pages/api/getStatefulSetByName.ts index d9834ea0e08..3538c0c3b7f 100644 --- a/frontend/providers/dbprovider/src/pages/api/getStatefulSetByName.ts +++ b/frontend/providers/dbprovider/src/pages/api/getStatefulSetByName.ts @@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< key: 'redis' }, [DBTypeEnum.kafka]: { - key: 'kafka' + key: 'kafka-broker' }, [DBTypeEnum.qdrant]: { key: 'qdrant' diff --git a/frontend/providers/dbprovider/src/pages/api/pauseDBByName.ts b/frontend/providers/dbprovider/src/pages/api/pauseDBByName.ts new file mode 100644 index 00000000000..602e14de0f3 --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/api/pauseDBByName.ts @@ -0,0 +1,73 @@ +import { authSession } from '@/services/backend/auth'; +import { getK8s } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; +import { ApiResp } from '@/services/kubernet'; +import { KbPgClusterType } from '@/types/cluster'; +import { json2StartOrStop } from '@/utils/json2Yaml'; +import { PatchUtils } from '@kubernetes/client-node'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { dbName } = req.body as { + dbName: string; + }; + + if (!dbName) { + return jsonRes(res, { + code: 400, + error: 'params error' + }); + } + + const { applyYamlList, k8sCustomObjects, namespace } = await getK8s({ + kubeconfig: await authSession(req) + }); + + const { yaml, yamlObj } = json2StartOrStop({ + dbName, + type: 'Stop' + }); + + const { body } = (await k8sCustomObjects.getNamespacedCustomObject( + 'apps.kubeblocks.io', + 'v1alpha1', + namespace, + 'clusters', + dbName + )) as { + body: KbPgClusterType; + }; + + if (body.spec.backup?.enabled === true) { + const patch = [ + { + op: 'replace', + path: '/spec/backup/enabled', + value: false + } + ]; + await k8sCustomObjects.patchNamespacedCustomObject( + 'apps.kubeblocks.io', + 'v1alpha1', + namespace, + 'clusters', + dbName, + patch, + undefined, + undefined, + undefined, + { headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_PATCH } } + ); + } + + await applyYamlList([yaml], 'update'); + + jsonRes(res, { data: 'pause success' }); + } catch (err: any) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/frontend/providers/dbprovider/src/pages/api/pod/getPodsByDBName.ts b/frontend/providers/dbprovider/src/pages/api/pod/getPodsByDBName.ts index a5f34939155..ae6bf7b4c2c 100644 --- a/frontend/providers/dbprovider/src/pages/api/pod/getPodsByDBName.ts +++ b/frontend/providers/dbprovider/src/pages/api/pod/getPodsByDBName.ts @@ -4,6 +4,7 @@ import { authSession } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; import { jsonRes } from '@/services/backend/response'; import { KBBackupNameLabel } from '@/constants/db'; +import { adaptPod } from '@/utils/adapt'; // get App Metrics By DeployName. compute average value export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -31,7 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< ); jsonRes(res, { - data: pods + data: pods.map((pod) => adaptPod(pod)) }); } catch (err: any) { // console.log(err, 'get metrics error') diff --git a/frontend/providers/dbprovider/src/pages/api/startDBByName.ts b/frontend/providers/dbprovider/src/pages/api/startDBByName.ts new file mode 100644 index 00000000000..ec8859284eb --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/api/startDBByName.ts @@ -0,0 +1,75 @@ +import { authSession } from '@/services/backend/auth'; +import { getK8s } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; +import { ApiResp } from '@/services/kubernet'; +import { KbPgClusterType } from '@/types/cluster'; +import { json2StartOrStop } from '@/utils/json2Yaml'; +import { PatchUtils } from '@kubernetes/client-node'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { dbName } = req.body as { + dbName: string; + }; + + if (!dbName) { + return jsonRes(res, { + code: 400, + error: 'params error' + }); + } + + const { applyYamlList, k8sCustomObjects, namespace } = await getK8s({ + kubeconfig: await authSession(req) + }); + + const { yaml, yamlObj } = json2StartOrStop({ + dbName, + type: 'Start' + }); + + const { body } = (await k8sCustomObjects.getNamespacedCustomObject( + 'apps.kubeblocks.io', + 'v1alpha1', + namespace, + 'clusters', + dbName + )) as { + body: KbPgClusterType; + }; + + console.log(yamlObj, body.spec.backup, body.spec.backup?.enabled === false, 'yaml'); + + if (body.spec.backup?.enabled === false) { + const patch = [ + { + op: 'replace', + path: '/spec/backup/enabled', + value: true + } + ]; + + await k8sCustomObjects.patchNamespacedCustomObject( + 'apps.kubeblocks.io', + 'v1alpha1', + namespace, + 'clusters', + dbName, + patch, + undefined, + undefined, + undefined, + { headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_PATCH } } + ); + } + await applyYamlList([yaml], 'update'); + + jsonRes(res, { data: 'start success' }); + } catch (err: any) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx index 5830a302438..e15951ec28a 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx @@ -50,7 +50,7 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { ); }, [db.dbType]); - const { data: dbStatefulSet } = useQuery( + const { data: dbStatefulSet, refetch: refetchDBStatefulSet } = useQuery( ['getDBStatefulSetByName', db.dbName, db.dbType], () => getDBStatefulSetByName(db.dbName, db.dbType), { @@ -59,7 +59,7 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { } ); - const { data: secret } = useQuery( + const { data: secret, refetch: refetchSecret } = useQuery( ['getDBSecret', db.dbName, db.dbType], () => (db.dbName ? getDBSecret({ dbName: db.dbName, dbType: db.dbType }) : null), { @@ -67,7 +67,7 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { } ); - const { data: service, refetch } = useQuery( + const { data: service, refetch: refetchService } = useQuery( ['getDBService', db.dbName, db.dbType], () => (db.dbName ? getDBServiceByName(`${db.dbName}-export`) : null), { @@ -94,6 +94,10 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { connection += '/?directConnection=true'; } + if (db?.dbType === 'kafka' || db?.dbType === 'milvus') { + connection = host + ':' + port; + } + return { host, port, @@ -168,6 +172,12 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { }); }, [db.dbType, secret]); + const refetchAll = () => { + refetchDBStatefulSet(); + refetchSecret(); + refetchService(); + }; + const openNetWorkService = async () => { try { console.log('openNetWorkService', dbStatefulSet, db); @@ -181,7 +191,7 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { await applyYamlList([yaml], 'create'); onClose(); setIsChecked(true); - refetch(); + refetchAll(); toast({ title: t('Success'), status: 'success' @@ -324,37 +334,37 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { > + {['milvus', 'kafka'].indexOf(db.dbType) === -1 && ( - <> -
onclickConnectDB()} - _hover={{ - color: 'brightBlue.600' - }} - > - - {t('direct_connection')} -
-
- {t('external_network')} - (isChecked ? closeNetWorkService() : onOpen())} - /> -
- +
onclickConnectDB()} + _hover={{ + color: 'brightBlue.600' + }} + > + + {t('direct_connection')} +
)} + +
+ {t('external_network')} + (isChecked ? closeNetWorkService() : onOpen())} + /> +
{['milvus', 'kafka'].indexOf(db.dbType) === -1 && ( { {t('billing_standards')}
- {SOURCE_PRICE.nodeports} + {SOURCE_PRICE.nodeports.toFixed(3)} diff --git a/frontend/providers/dbprovider/src/pages/db/edit/index.tsx b/frontend/providers/dbprovider/src/pages/db/edit/index.tsx index 464f64096d6..f8d72d7324e 100644 --- a/frontend/providers/dbprovider/src/pages/db/edit/index.tsx +++ b/frontend/providers/dbprovider/src/pages/db/edit/index.tsx @@ -20,7 +20,7 @@ import { useTranslation } from 'next-i18next'; import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useForm } from 'react-hook-form'; +import { FieldErrors, useForm } from 'react-hook-form'; import Form from './components/Form'; import Header from './components/Header'; import Yaml from './components/Yaml'; @@ -142,7 +142,7 @@ const EditApp = ({ dbName, tabType }: { dbName?: string; tabType?: 'form' | 'yam setIsLoading(false); }; - const submitError = useCallback(() => { + const submitError = (err: FieldErrors) => { // deep search message const deepSearch = (obj: any): string => { if (!obj || typeof obj !== 'object') return t('submit_error'); @@ -152,13 +152,13 @@ const EditApp = ({ dbName, tabType }: { dbName?: string; tabType?: 'form' | 'yam return deepSearch(Object.values(obj)[0]); }; toast({ - title: deepSearch(formHook.formState.errors), + title: deepSearch(err), status: 'error', position: 'top', duration: 3000, isClosable: true }); - }, [formHook.formState.errors, t, toast]); + }; useQuery( ['init'], @@ -213,7 +213,10 @@ const EditApp = ({ dbName, tabType }: { dbName?: string; tabType?: 'form' | 'yam yamlList={yamlList} applyBtnText={applyBtnText} applyCb={() => - formHook.handleSubmit((data) => openConfirm(() => submitSuccess(data))(), submitError)() + formHook.handleSubmit( + (data) => openConfirm(() => submitSuccess(data))(), + (err) => submitError(err) + )() } /> diff --git a/frontend/providers/dbprovider/src/utils/json2Yaml.ts b/frontend/providers/dbprovider/src/utils/json2Yaml.ts index 553fff74a68..88fb8333efd 100644 --- a/frontend/providers/dbprovider/src/utils/json2Yaml.ts +++ b/frontend/providers/dbprovider/src/utils/json2Yaml.ts @@ -854,11 +854,13 @@ export const json2Upgrade = ({ dbName, dbVersion }: DBEditType) => { }; export const json2StartOrStop = ({ dbName, type }: { dbName: string; type: 'Start' | 'Stop' }) => { + const nameType = type.toLocaleLowerCase(); + const template = { apiVersion: 'apps.kubeblocks.io/v1alpha1', kind: 'OpsRequest', metadata: { - name: `ops-stop-${dayjs().format('YYYYMMDDHHmmss')}`, + name: `ops-${nameType}-${dayjs().format('YYYYMMDDHHmmss')}`, labels: { [crLabelKey]: dbName } @@ -868,7 +870,10 @@ export const json2StartOrStop = ({ dbName, type }: { dbName: string; type: 'Star type } }; - return yaml.dump(template); + return { + yaml: yaml.dump(template), + yamlObj: template + }; }; export const json2Restart = ({ dbName, dbType }: { dbName: string; dbType: DBType }) => { @@ -1037,11 +1042,11 @@ export const json2NetworkService = ({ mongodb: 27017, 'apecloud-mysql': 3306, redis: 6379, - kafka: '', + kafka: 9092, qdrant: '', nebula: '', weaviate: '', - milvus: '' + milvus: 19530 }; const labelMap = { postgresql: { @@ -1056,11 +1061,15 @@ export const json2NetworkService = ({ redis: { 'kubeblocks.io/role': 'primary' }, - kafka: {}, + kafka: { + 'apps.kubeblocks.io/component-name': 'kafka-broker' + }, qdrant: {}, nebula: {}, weaviate: {}, - milvus: {} + milvus: { + 'apps.kubeblocks.io/component-name': 'milvus' + } }; const template = { @@ -1070,7 +1079,9 @@ export const json2NetworkService = ({ name: `${dbDetail.dbName}-export`, labels: { 'app.kubernetes.io/instance': dbDetail.dbName, - 'apps.kubeblocks.io/component-name': dbDetail.dbType + 'app.kubernetes.io/managed-by': 'kubeblocks', + 'apps.kubeblocks.io/component-name': dbDetail.dbType, + ...labelMap[dbDetail.dbType] }, ownerReferences: [ { @@ -1094,6 +1105,7 @@ export const json2NetworkService = ({ ], selector: { 'app.kubernetes.io/instance': dbDetail.dbName, + 'app.kubernetes.io/managed-by': 'kubeblocks', ...labelMap[dbDetail.dbType] }, type: 'NodePort' @@ -1162,3 +1174,80 @@ export const json2Reconfigure = ( return yaml.dump(template); }; + +export const json2ClusterOps = ( + data: DBEditType, + type: 'VerticalScaling' | 'HorizontalScaling' | 'VolumeExpansion' +) => { + const componentName = + data.dbType === 'apecloud-mysql' ? 'mysql' : data.dbType === 'kafka' ? 'broker' : data.dbType; + + const getOpsName = () => { + const timeStr = dayjs().format('YYYYMMDDHHmmss'); + return `ops-${type.toLowerCase()}-${timeStr}`; + }; + + const baseTemplate = { + apiVersion: 'apps.kubeblocks.io/v1alpha1', + kind: 'OpsRequest', + metadata: { + name: getOpsName(), + labels: { + [crLabelKey]: data.dbName + } + }, + spec: { + clusterRef: data.dbName, + type: type + } + }; + + const opsConfig = { + VerticalScaling: { + verticalScaling: [ + { + componentName, + requests: { + cpu: `${Math.floor(str2Num(data.cpu) * 0.1)}m`, + memory: `${Math.floor(str2Num(data.memory) * 0.1)}Mi` + }, + limits: { + cpu: `${str2Num(Math.floor(data.cpu))}m`, + memory: `${str2Num(data.memory)}Mi` + } + } + ] + }, + HorizontalScaling: { + horizontalScaling: [ + { + componentName, + replicas: data.replicas + } + ] + }, + VolumeExpansion: { + volumeExpansion: [ + { + componentName, + volumeClaimTemplates: [ + { + name: 'data', + storage: `${data.storage}Gi` + } + ] + } + ] + } + }; + + const template = { + ...baseTemplate, + spec: { + ...baseTemplate.spec, + ...opsConfig[type] + } + }; + + return yaml.dump(template); +};