From 15add6d6c89297d2ea72c81cca5561915447942c Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Mon, 3 Jul 2023 17:37:00 +0800 Subject: [PATCH] feat: redis and dyminic dbversion (#3449) * get version by cr * feat: redis --- .../dbprovider/public/locales/en/common.json | 3 +- .../dbprovider/public/locales/zh/common.json | 4 +- .../providers/dbprovider/src/api/platform.ts | 2 + .../src/components/RangeInput/index.tsx | 1 + .../dbprovider/src/components/Tip/index.tsx | 5 +- .../providers/dbprovider/src/constants/db.ts | 38 +++-- .../providers/dbprovider/src/pages/_app.tsx | 5 +- .../src/pages/api/getSecretByName.ts | 4 + .../src/pages/api/platform/getVersion.ts | 64 ++++++++ .../db/detail/components/AppBaseInfo.tsx | 3 +- .../dbprovider/src/pages/db/detail/index.tsx | 15 +- .../src/pages/db/edit/components/Form.tsx | 42 +++++- .../src/pages/db/edit/components/PriceBox.tsx | 54 ++++--- .../dbprovider/src/pages/db/edit/index.tsx | 12 +- .../src/services/backend/kubernetes.ts | 35 +++-- .../providers/dbprovider/src/store/static.ts | 26 +++- .../providers/dbprovider/src/types/db.d.ts | 2 +- .../dbprovider/src/utils/json2Yaml.ts | 137 +++++++++++++++++- 18 files changed, 392 insertions(+), 60 deletions(-) create mode 100644 frontend/providers/dbprovider/src/pages/api/platform/getVersion.ts diff --git a/frontend/providers/dbprovider/public/locales/en/common.json b/frontend/providers/dbprovider/public/locales/en/common.json index 2dd936daaf5..0aaba51a81a 100644 --- a/frontend/providers/dbprovider/public/locales/en/common.json +++ b/frontend/providers/dbprovider/public/locales/en/common.json @@ -91,5 +91,6 @@ "Confirm delete the backup": "Confirm delete the backup", "Basic Info": "Basic", "Restore Backup Tip": "Restoring the backup will create a new database, and you will need to provide the name of the new data, which cannot be the same as the current database.", - "Total Price": "Total" + "Total Price": "Total", + "Redis HA": "HA" } diff --git a/frontend/providers/dbprovider/public/locales/zh/common.json b/frontend/providers/dbprovider/public/locales/zh/common.json index 67e6266c2a8..610293bc0b0 100644 --- a/frontend/providers/dbprovider/public/locales/zh/common.json +++ b/frontend/providers/dbprovider/public/locales/zh/common.json @@ -121,5 +121,7 @@ "Backup Database": "备份数据库", "Manual Backup": "手动备份", "Auto Backup": "自动备份", - "Containers": "容器" + "Containers": "容器", + "Redis does not support backup at this time": "Redis 暂时不支持备份", + "The multi-replica Redis includes High Availability (HA) nodes. Please note, the anticipated price already encompasses the cost for the HA nodes.": "Redis 多副本包含 HA 节点,请悉知,预估价格已包含 HA 节点费用" } diff --git a/frontend/providers/dbprovider/src/api/platform.ts b/frontend/providers/dbprovider/src/api/platform.ts index b9253336bd9..5f9d0d4c330 100644 --- a/frontend/providers/dbprovider/src/api/platform.ts +++ b/frontend/providers/dbprovider/src/api/platform.ts @@ -1,4 +1,6 @@ import { GET, POST, DELETE } from '@/services/request'; import type { Response as resourcePriceResponse } from '@/pages/api/platform/resourcePrice'; +import type { Response as DBVersionMapType } from '@/pages/api/platform/getVersion'; export const getResourcePrice = () => GET('/api/platform/resourcePrice'); +export const getDBVersionMap = () => GET('/api/platform/getVersion'); diff --git a/frontend/providers/dbprovider/src/components/RangeInput/index.tsx b/frontend/providers/dbprovider/src/components/RangeInput/index.tsx index bd77f303955..50511271bb5 100644 --- a/frontend/providers/dbprovider/src/components/RangeInput/index.tsx +++ b/frontend/providers/dbprovider/src/components/RangeInput/index.tsx @@ -46,6 +46,7 @@ const RangeInput = ({ return ( { + const { t } = useTranslation(); + const sizeMap = useMemo(() => { switch (size) { case 'sm': @@ -55,7 +58,7 @@ const Tip = ({ size = 'md', text, icon, theme, ...props }: Props) => { {icon} ) : null} - {text} + {t(text)} ); }; diff --git a/frontend/providers/dbprovider/src/constants/db.ts b/frontend/providers/dbprovider/src/constants/db.ts index fb582acc37a..0b909b0a8cd 100644 --- a/frontend/providers/dbprovider/src/constants/db.ts +++ b/frontend/providers/dbprovider/src/constants/db.ts @@ -1,4 +1,4 @@ -import { DBEditType, DBDetailType, PodDetailType, BackupStatusMapType } from '@/types/db'; +import { DBEditType, DBDetailType, PodDetailType } from '@/types/db'; import { CpuSlideMarkList, MemorySlideMarkList } from './editApp'; export const crLabelKey = 'sealos-db-provider-cr'; @@ -6,7 +6,8 @@ export const crLabelKey = 'sealos-db-provider-cr'; export enum DBTypeEnum { 'postgresql' = 'postgresql', 'mongodb' = 'mongodb', - 'mysql' = 'apecloud-mysql' + 'mysql' = 'apecloud-mysql', + 'redis' = 'redis' } export enum DBStatusEnum { @@ -149,27 +150,25 @@ export const minReplicasKey = 'deploy.cloud.sealos.io/minReplicas'; export const DBTypeList = [ { id: DBTypeEnum.postgresql, label: 'postgres' }, { id: DBTypeEnum.mongodb, label: 'mongo' }, - { id: DBTypeEnum.mysql, label: 'mysql' } + { id: DBTypeEnum.mysql, label: 'mysql' }, + { id: DBTypeEnum.redis, label: 'redis' } ]; -export const DBVersionMap = { - [DBTypeEnum.postgresql]: [{ id: 'postgresql-14.8.0', label: 'postgresql-14.8.0' }], - [DBTypeEnum.mongodb]: [{ id: 'mongodb-5.0.14', label: 'mongodb-5.0.14' }], - [DBTypeEnum.mysql]: [{ id: 'ac-mysql-8.0.30', label: 'ac-mysql-8.0.30' }] -}; export const DBComponentNameMap = { [DBTypeEnum.postgresql]: 'postgresql', [DBTypeEnum.mongodb]: 'mongo', - [DBTypeEnum.mysql]: 'mysql' + [DBTypeEnum.mysql]: 'mysql', + [DBTypeEnum.redis]: 'redis' }; export const DBBackupPolicyNameMap = { [DBTypeEnum.postgresql]: 'postgresql', [DBTypeEnum.mongodb]: 'mongodb', - [DBTypeEnum.mysql]: 'mysql' + [DBTypeEnum.mysql]: 'mysql', + [DBTypeEnum.redis]: 'redis' }; export const defaultDBEditValue: DBEditType = { dbType: DBTypeEnum.postgresql, - dbVersion: DBVersionMap[DBTypeEnum.postgresql][0].id, + dbVersion: '', dbName: 'dbname', replicas: 1, cpu: CpuSlideMarkList[1].value, @@ -195,3 +194,20 @@ export const defaultPod: PodDetailType = { cpu: 1, memory: 1 }; + +export const RedisHAConfig = (ha = true) => { + if (ha) { + return { + cpu: 200, + memory: 200, + storage: 1, + replicas: 3 + }; + } + return { + cpu: 100, + memory: 100, + storage: 0, + replicas: 1 + }; +}; diff --git a/frontend/providers/dbprovider/src/pages/_app.tsx b/frontend/providers/dbprovider/src/pages/_app.tsx index b9563b72dd7..addf80ea198 100644 --- a/frontend/providers/dbprovider/src/pages/_app.tsx +++ b/frontend/providers/dbprovider/src/pages/_app.tsx @@ -15,7 +15,7 @@ import { useLoading } from '@/hooks/useLoading'; import { useRouter } from 'next/router'; import { appWithTranslation, useTranslation } from 'next-i18next'; import { getLangStore, setLangStore } from '@/utils/cookieUtils'; -import { getUserPrice } from '@/store/static'; +import { getUserPrice, getDBVersion } from '@/store/static'; import 'nprogress/nprogress.css'; import 'react-day-picker/dist/style.css'; @@ -60,6 +60,7 @@ function App({ Component, pageProps, domain }: AppProps & { domain: string }) { } catch (err) { console.log('App is not running in desktop'); if (!process.env.NEXT_PUBLIC_MOCK_USER) { + localStorage.removeItem('session'); openConfirm(() => { window.open(`https://${domain}`, '_self'); })(); @@ -99,6 +100,8 @@ function App({ Component, pageProps, domain }: AppProps & { domain: string }) { }; getUserPrice(); + getDBVersion(); + (async () => { try { const lang = await sealosApp.getLanguage(); diff --git a/frontend/providers/dbprovider/src/pages/api/getSecretByName.ts b/frontend/providers/dbprovider/src/pages/api/getSecretByName.ts index 00e92739660..a70507e3cac 100644 --- a/frontend/providers/dbprovider/src/pages/api/getSecretByName.ts +++ b/frontend/providers/dbprovider/src/pages/api/getSecretByName.ts @@ -38,6 +38,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< [DBTypeEnum.mysql]: { passwordKey: 'password', connectKey: 'mysql' + }, + [DBTypeEnum.redis]: { + passwordKey: 'password', + connectKey: 'redis' } }; // get secret diff --git a/frontend/providers/dbprovider/src/pages/api/platform/getVersion.ts b/frontend/providers/dbprovider/src/pages/api/platform/getVersion.ts new file mode 100644 index 00000000000..bfdcec427cb --- /dev/null +++ b/frontend/providers/dbprovider/src/pages/api/platform/getVersion.ts @@ -0,0 +1,64 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { K8sApi } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; +import { DBTypeEnum } from '@/constants/db'; +import * as k8s from '@kubernetes/client-node'; + +export type Response = Record< + `${DBTypeEnum}`, + { + id: string; + label: string; + }[] +>; + +const MOCK = { + [DBTypeEnum.postgresql]: [{ id: 'postgresql-14.8.0', label: 'postgresql-14.8.0' }], + [DBTypeEnum.mongodb]: [{ id: 'mongodb-5.0.14', label: 'mongodb-5.0.14' }], + [DBTypeEnum.mysql]: [{ id: 'ac-mysql-8.0.30', label: 'ac-mysql-8.0.30' }], + [DBTypeEnum.redis]: [{ id: 'redis-7.0.6', label: 'redis-7.0.6' }] +}; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const DBVersionMap: Response = { + [DBTypeEnum.postgresql]: [], + [DBTypeEnum.mongodb]: [], + [DBTypeEnum.mysql]: [], + [DBTypeEnum.redis]: [] + }; + + // source price + const kc = K8sApi(); + const k8sCustomObjects = kc.makeApiClient(k8s.CustomObjectsApi); + + const { body } = (await k8sCustomObjects.listClusterCustomObject( + 'apps.kubeblocks.io', + 'v1alpha1', + 'clusterversions' + )) as any; + + body.items.forEach((item: any) => { + const db = item?.spec?.clusterDefinitionRef as `${DBTypeEnum}`; + if ( + DBVersionMap[db] && + item?.metadata?.name && + !DBVersionMap[db].find((db) => db.id === item.metadata.name) + ) { + DBVersionMap[db].push({ + id: item.metadata.name, + label: item.metadata.name + }); + } + }); + + jsonRes(res, { + data: DBVersionMap + }); + } catch (error) { + console.log(error); + jsonRes(res, { + data: MOCK + }); + } +} 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 0478f7df888..2d14278bc10 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/components/AppBaseInfo.tsx @@ -60,7 +60,8 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => { const commandMap = { [DBTypeEnum.postgresql]: `psql '${secret.connection}'`, [DBTypeEnum.mongodb]: `mongosh '${secret.connection}'`, - [DBTypeEnum.mysql]: `mysql -h ${secret.host} -P ${secret.port} -u ${secret.username} -p${secret.password}` + [DBTypeEnum.mysql]: `mysql -h ${secret.host} -P ${secret.port} -u ${secret.username} -p${secret.password}`, + [DBTypeEnum.redis]: `redis-cli -h ${secret.host} -p ${secret.port}` }; const defaultCommand = commandMap[db.dbType]; diff --git a/frontend/providers/dbprovider/src/pages/db/detail/index.tsx b/frontend/providers/dbprovider/src/pages/db/detail/index.tsx index 68af6cda0af..28299552016 100644 --- a/frontend/providers/dbprovider/src/pages/db/detail/index.tsx +++ b/frontend/providers/dbprovider/src/pages/db/detail/index.tsx @@ -12,6 +12,7 @@ import AppBaseInfo from './components/AppBaseInfo'; import Pods from './components/Pods'; import BackupTable, { type ComponentRef } from './components/BackupTable'; import { useTranslation } from 'next-i18next'; +import { DBTypeEnum } from '@/constants/db'; enum TabEnum { pod = 'pod', @@ -110,7 +111,19 @@ const AppDetail = ({ dbName, listType }: { dbName: string; listType: `${TabEnum} {listType === 'pod' && {dbPods.length} Items} {listType === 'backup' && !BackupTableRef.current?.backupProcessing && ( - diff --git a/frontend/providers/dbprovider/src/pages/db/edit/components/Form.tsx b/frontend/providers/dbprovider/src/pages/db/edit/components/Form.tsx index a8a5aabd14f..19fc27a70a8 100644 --- a/frontend/providers/dbprovider/src/pages/db/edit/components/Form.tsx +++ b/frontend/providers/dbprovider/src/pages/db/edit/components/Form.tsx @@ -11,7 +11,8 @@ import { NumberInputStepper, NumberIncrementStepper, NumberDecrementStepper, - Tooltip + Tooltip, + Switch } from '@chakra-ui/react'; import { UseFormReturn } from 'react-hook-form'; import { useRouter } from 'next/router'; @@ -23,13 +24,16 @@ import type { DBEditType } from '@/types/db'; import { CpuSlideMarkList, MemorySlideMarkList } from '@/constants/editApp'; import Tabs from '@/components/Tabs'; import MySelect from '@/components/Select'; -import { DBTypeList, DBVersionMap } from '@/constants/db'; +import { DBTypeEnum, DBTypeList, RedisHAConfig } from '@/constants/db'; +import { DBVersionMap } from '@/store/static'; import { useTranslation } from 'next-i18next'; import PriceBox from './PriceBox'; import { INSTALL_ACCOUNT } from '@/store/static'; +import Tip from '@/components/Tip'; import { obj2Query } from '@/api/tools'; import { throttle } from 'lodash'; +import { InfoOutlineIcon } from '@chakra-ui/icons'; const Form = ({ formHook, @@ -196,10 +200,25 @@ const Form = ({ {INSTALL_ACCOUNT && ( { + const config = RedisHAConfig(getValues('replicas') > 1); + return [ + { + ...config, + replicas: [config.replicas, config.replicas] + } + ]; + })() + : []) + ]} /> )} @@ -292,6 +311,7 @@ const Form = ({ + {getValues('dbType') === DBTypeEnum.redis && getValues('replicas') > 1 && ( + } + text="The multi-replica Redis includes High Availability (HA) nodes. Please note, the anticipated price already encompasses the cost for the HA nodes." + size="sm" + /> + )} + { + components = [] +}: { + components: { + cpu: number; + memory: number; + storage: number; + replicas: number[]; + }[]; +}) => { const { t } = useTranslation(); - const priceList = useMemo(() => { - const cpuP = +((SOURCE_PRICE.cpu * cpu * 24) / 1000).toFixed(2); - const memoryP = +((SOURCE_PRICE.memory * memory * 24) / 1024).toFixed(2); - const storageP = +(SOURCE_PRICE.storage * storage * 24).toFixed(2); - const totalP = +(cpuP + memoryP + storageP).toFixed(2); + let cp = [0, 0]; + let mp = [0, 0]; + let sp = [0, 0]; + let tp = [0, 0]; + components.forEach(({ cpu, memory, storage, replicas }) => { + const cpuP = (SOURCE_PRICE.cpu * cpu * 24) / 1000; + const memoryP = (SOURCE_PRICE.memory * memory * 24) / 1024; + const storageP = SOURCE_PRICE.storage * storage * 24; + const totalP = cpuP + memoryP + storageP; + + replicas.forEach((item, i) => { + cp[i] += cpuP * item; + mp[i] += memoryP * item; + sp[i] += storageP * item; + tp[i] += totalP * item; + }); + }); - const podScale = (val: number) => { - const min = (val * pods[0]).toFixed(2); - const max = (val * pods[1]).toFixed(2); - return pods[0] === pods[1] ? `¥${min}` : `¥${min} ~ ${max}`; + const podScale = (val: number[]) => { + return val[0] === val[1] + ? `¥${val[0].toFixed(2)}` + : `¥${val[0].toFixed(2)} ~ ${val[1].toFixed(2)}`; }; return [ { label: 'CPU', color: '#33BABB', - value: podScale(cpuP) + value: podScale(cp) }, - { label: 'Memory', color: '#36ADEF', value: podScale(memoryP) }, - { label: 'Storage', color: '#8172D8', value: podScale(storageP) }, - { label: 'Total Price', color: '#485058', value: podScale(totalP) } + { label: 'Memory', color: '#36ADEF', value: podScale(mp) }, + { label: 'Storage', color: '#8172D8', value: podScale(sp) }, + { label: 'Total Price', color: '#485058', value: podScale(tp) } ]; - }, [cpu, memory, pods, storage]); + }, [components]); return ( diff --git a/frontend/providers/dbprovider/src/pages/db/edit/index.tsx b/frontend/providers/dbprovider/src/pages/db/edit/index.tsx index 77a3ed49932..01110b45c55 100644 --- a/frontend/providers/dbprovider/src/pages/db/edit/index.tsx +++ b/frontend/providers/dbprovider/src/pages/db/edit/index.tsx @@ -19,11 +19,17 @@ import { useGlobalStore } from '@/store/global'; import { serviceSideProps } from '@/utils/i18n'; import { useTranslation } from 'next-i18next'; import { adaptDBForm } from '@/utils/adapt'; +import { DBVersionMap } from '@/store/static'; import Header from './components/Header'; import Form from './components/Form'; import Yaml from './components/Yaml'; const ErrorModal = dynamic(() => import('./components/ErrorModal')); +const defaultEdit = { + ...defaultDBEditValue, + dbVersion: DBVersionMap.postgresql[0]?.id +}; + const EditApp = ({ dbName, tabType }: { dbName?: string; tabType?: 'form' | 'yaml' }) => { const { t } = useTranslation(); const router = useRouter(); @@ -52,7 +58,7 @@ const EditApp = ({ dbName, tabType }: { dbName?: string; tabType?: 'form' | 'yam // form const formHook = useForm({ - defaultValues: defaultDBEditValue + defaultValues: defaultEdit }); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -131,11 +137,11 @@ const EditApp = ({ dbName, tabType }: { dbName?: string; tabType?: 'form' | 'yam setYamlList([ { filename: 'cluster.yaml', - value: json2CreateCluster(defaultDBEditValue) + value: json2CreateCluster(defaultEdit) }, { filename: 'account.yaml', - value: json2Account(defaultDBEditValue) + value: json2Account(defaultEdit) } ]); return null; diff --git a/frontend/providers/dbprovider/src/services/backend/kubernetes.ts b/frontend/providers/dbprovider/src/services/backend/kubernetes.ts index 93732a9127d..feb7a1a75d5 100644 --- a/frontend/providers/dbprovider/src/services/backend/kubernetes.ts +++ b/frontend/providers/dbprovider/src/services/backend/kubernetes.ts @@ -17,29 +17,40 @@ export function CheckIsInCluster(): [boolean, string] { } /* init api */ -export function K8sApi(config: string): k8s.KubeConfig { +export function K8sApi(config = ''): k8s.KubeConfig { const kc = new k8s.KubeConfig(); - kc.loadFromString(config); + config ? kc.loadFromString(config) : kc.loadFromCluster(); const cluster = kc.getCurrentCluster(); - if (cluster) { - const [inCluster, hosts] = CheckIsInCluster(); - - const server: k8s.Cluster = { - name: cluster.name, - caData: cluster.caData, - caFile: cluster.caFile, - server: inCluster && hosts ? hosts : 'https://apiserver.cluster.local:6443', - skipTLSVerify: cluster.skipTLSVerify - }; + if (cluster !== null) { + let server: k8s.Cluster; + const [inCluster, hosts] = CheckIsInCluster(); + if (inCluster && hosts !== '') { + server = { + name: cluster.name, + caData: cluster.caData, + caFile: cluster.caFile, + server: hosts, + skipTLSVerify: cluster.skipTLSVerify + }; + } else { + server = { + name: cluster.name, + caData: cluster.caData, + caFile: cluster.caFile, + server: 'https://apiserver.cluster.local:6443', + skipTLSVerify: cluster.skipTLSVerify + }; + } kc.clusters.forEach((item, i) => { if (item.name === cluster.name) { kc.clusters[i] = server; } }); } + return kc; } diff --git a/frontend/providers/dbprovider/src/store/static.ts b/frontend/providers/dbprovider/src/store/static.ts index f039530a495..710c301fa95 100644 --- a/frontend/providers/dbprovider/src/store/static.ts +++ b/frontend/providers/dbprovider/src/store/static.ts @@ -1,5 +1,7 @@ -import { getResourcePrice } from '@/api/platform'; +import { getResourcePrice, getDBVersionMap } from '@/api/platform'; import type { Response as resourcePriceResponse } from '@/pages/api/platform/resourcePrice'; +import { DBTypeEnum } from '@/constants/db'; +import type { Response as DBVersionMapType } from '@/pages/api/platform/getVersion'; export let SOURCE_PRICE: resourcePriceResponse = { cpu: 0.067, @@ -9,6 +11,14 @@ export let SOURCE_PRICE: resourcePriceResponse = { export let INSTALL_ACCOUNT = false; let retryGetPrice = 3; +let retryVersion = 3; + +export let DBVersionMap: DBVersionMapType = { + [DBTypeEnum.postgresql]: [], + [DBTypeEnum.mongodb]: [], + [DBTypeEnum.mysql]: [], + [DBTypeEnum.redis]: [] +}; export const getUserPrice = async () => { try { @@ -24,3 +34,17 @@ export const getUserPrice = async () => { } } }; + +export const getDBVersion = async () => { + try { + const res = await getDBVersionMap(); + DBVersionMap = res; + } catch (err) { + retryVersion--; + if (retryVersion >= 0) { + setTimeout(() => { + getDBVersion(); + }, 1000); + } + } +}; diff --git a/frontend/providers/dbprovider/src/types/db.d.ts b/frontend/providers/dbprovider/src/types/db.d.ts index 887e229b496..f7a7fbb440b 100644 --- a/frontend/providers/dbprovider/src/types/db.d.ts +++ b/frontend/providers/dbprovider/src/types/db.d.ts @@ -51,7 +51,7 @@ export interface DBEditType { dbVersion: string; dbName: string; replicas: number; - cpu: number; // + cpu: number; memory: number; storage: number; } diff --git a/frontend/providers/dbprovider/src/utils/json2Yaml.ts b/frontend/providers/dbprovider/src/utils/json2Yaml.ts index 7ba66a33ca3..5f14f938dfc 100644 --- a/frontend/providers/dbprovider/src/utils/json2Yaml.ts +++ b/frontend/providers/dbprovider/src/utils/json2Yaml.ts @@ -1,7 +1,7 @@ import yaml from 'js-yaml'; import type { DBEditType, DBType } from '@/types/db'; import { str2Num } from '@/utils/tools'; -import { DBTypeEnum, DBComponentNameMap } from '@/constants/db'; +import { DBTypeEnum, DBComponentNameMap, RedisHAConfig } from '@/constants/db'; import { crLabelKey } from '@/constants/db'; import { getUserNamespace } from './user'; import dayjs from 'dayjs'; @@ -35,6 +35,9 @@ export const json2CreateCluster = (data: DBEditType, backupName?: string) => { }, name: data.dbName }; + + const redisHA = RedisHAConfig(data.replicas > 1); + const map = { [DBTypeEnum.postgresql]: [ { @@ -170,6 +173,87 @@ export const json2CreateCluster = (data: DBEditType, backupName?: string) => { tolerations: [] } } + ], + [DBTypeEnum.redis]: [ + { + apiVersion: 'apps.kubeblocks.io/v1alpha1', + kind: 'Cluster', + metadata, + spec: { + affinity: { + nodeLabels: {}, + podAntiAffinity: 'Preferred', + tenancy: 'SharedNode', + topologyKeys: [] + }, + clusterDefinitionRef: 'redis', + clusterVersionRef: data.dbVersion, + componentSpecs: [ + { + componentDefRef: 'redis', + monitor: true, + name: 'redis', + replicas: data.replicas, + resources, + serviceAccountName: data.dbName, + switchPolicy: { + type: 'Noop' + }, + volumeClaimTemplates: [ + { + name: 'data', + spec: { + accessModes: ['ReadWriteOnce'], + resources: { + requests: { + storage: `${data.storage}Gi` + } + }, + storageClassName: 'openebs-backup' + } + } + ] + }, + { + componentDefRef: 'redis-sentinel', + monitor: true, + name: 'redis-sentinel', + replicas: redisHA.replicas, + resources: { + limits: { + cpu: `${redisHA.cpu}m`, + memory: `${redisHA.memory}Mi` + }, + requests: { + cpu: `${redisHA.cpu}m`, + memory: `${redisHA.memory}Mi` + } + }, + serviceAccountName: data.dbName, + ...(redisHA.storage > 0 + ? { + volumeClaimTemplates: [ + { + name: 'data', + spec: { + accessModes: ['ReadWriteOnce'], + resources: { + requests: { + storage: `${redisHA.storage}Gi` + } + }, + storageClassName: 'openebs-backup' + } + } + ] + } + : {}) + } + ], + terminationPolicy: 'Delete', + tolerations: [] + } + } ] }; @@ -350,6 +434,57 @@ export const json2Account = (data: DBEditType) => { } ] } + ], + [DBTypeEnum.redis]: [ + { + apiVersion: 'v1', + kind: 'ServiceAccount', + metadata: { + labels: { + ...commonLabels + }, + name: data.dbName + } + }, + { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'Role', + metadata: { + labels: { + ...commonLabels + }, + name: data.dbName + }, + rules: [ + { + apiGroups: [''], + resources: ['events'], + verbs: ['create'] + } + ] + }, + { + apiVersion: 'rbac.authorization.k8s.io/v1', + kind: 'RoleBinding', + metadata: { + labels: { + ...commonLabels + }, + name: data.dbName + }, + roleRef: { + apiGroup: 'rbac.authorization.k8s.io', + kind: 'Role', + name: data.dbName + }, + subjects: [ + { + kind: 'ServiceAccount', + name: data.dbName, + namespace: getUserNamespace() + } + ] + } ] }; return map[data.dbType].map((item) => yaml.dump(item)).join('\n---\n');