From fd9542c83c19e99471b1327c2a6f9c159a0600c7 Mon Sep 17 00:00:00 2001
From: jingyang <72259332+zjy365@users.noreply.github.com>
Date: Mon, 9 Dec 2024 11:22:06 +0800
Subject: [PATCH] feat: support backup selection during database creation
(#5252)
* fix db backup cron
* feat: support backup selection during database creation
* update terminationPolicy
* update
* update
* update SelectTimeList
* update style
* update
* support PVC_STORAGE_MAX
---
.../providers/applaunchpad/data/config.yaml | 1 +
.../src/pages/api/platform/getInitData.ts | 5 +-
.../pages/app/edit/components/StoreModal.tsx | 11 +-
.../applaunchpad/src/store/static.ts | 2 +
.../applaunchpad/src/types/index.d.ts | 1 +
.../dbprovider/.vscode/settings.json | 1 -
.../dbprovider/public/locales/en/common.json | 15 +-
.../dbprovider/public/locales/zh/common.json | 12 +-
.../providers/dbprovider/src/api/backup.ts | 14 --
.../components/Icon/icons/backupSettings.svg | 4 +
.../dbprovider/src/components/Icon/index.tsx | 1 +
.../providers/dbprovider/src/constants/db.ts | 38 ++-
.../dbprovider/src/constants/theme.ts | 33 ++-
.../dbprovider/src/pages/api/backup/policy.ts | 52 -----
.../src/pages/api/backup/updatePolicy.ts | 147 +++++++-----
.../dbprovider/src/pages/api/createDB.ts | 85 ++++++-
.../db/detail/components/BackupModal.tsx | 38 +--
.../dbprovider/src/pages/db/detail/index.tsx | 5 +-
.../src/pages/db/edit/components/Form.tsx | 219 +++++++++++++++++-
.../dbprovider/src/pages/db/edit/index.tsx | 5 +-
.../dbprovider/src/pages/db/migrate/index.tsx | 3 +-
.../dbprovider/src/types/backup.d.ts | 4 +-
.../dbprovider/src/types/cluster.d.ts | 15 +-
.../providers/dbprovider/src/types/db.d.ts | 4 +
.../dbprovider/src/types/migrate.d.ts | 2 +
.../providers/dbprovider/src/utils/adapt.ts | 97 ++++----
.../dbprovider/src/utils/json2Yaml.ts | 11 +-
27 files changed, 593 insertions(+), 232 deletions(-)
create mode 100644 frontend/providers/dbprovider/src/components/Icon/icons/backupSettings.svg
delete mode 100644 frontend/providers/dbprovider/src/pages/api/backup/policy.ts
diff --git a/frontend/providers/applaunchpad/data/config.yaml b/frontend/providers/applaunchpad/data/config.yaml
index f7ab8b485c6..4ee76742b03 100644
--- a/frontend/providers/applaunchpad/data/config.yaml
+++ b/frontend/providers/applaunchpad/data/config.yaml
@@ -9,6 +9,7 @@ common:
guideEnabled: false
apiEnabled: false
launchpad:
+ pvcStorageMax: 100
eventAnalyze:
enabled: false
fastGPTKey: ""
diff --git a/frontend/providers/applaunchpad/src/pages/api/platform/getInitData.ts b/frontend/providers/applaunchpad/src/pages/api/platform/getInitData.ts
index 0b899ff4348..4c81fecda00 100644
--- a/frontend/providers/applaunchpad/src/pages/api/platform/getInitData.ts
+++ b/frontend/providers/applaunchpad/src/pages/api/platform/getInitData.ts
@@ -17,6 +17,7 @@ export type Response = {
fileMangerConfig: FileMangerType;
SEALOS_USER_DOMAINS: { name: string; secretName: string }[];
DESKTOP_DOMAIN: string;
+ PVC_STORAGE_MAX: number;
};
export const defaultAppConfig: AppConfigType = {
@@ -38,6 +39,7 @@ export const defaultAppConfig: AppConfigType = {
},
launchpad: {
currencySymbol: Coin.shellCoin,
+ pvcStorageMax: 20,
eventAnalyze: {
enabled: false,
fastGPTKey: ''
@@ -93,7 +95,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
fileMangerConfig: global.AppConfig.launchpad.fileManger,
CURRENCY: global.AppConfig.launchpad.currencySymbol || Coin.shellCoin,
SEALOS_USER_DOMAINS: global.AppConfig.cloud.userDomains || [],
- DESKTOP_DOMAIN: global.AppConfig.cloud.desktopDomain
+ DESKTOP_DOMAIN: global.AppConfig.cloud.desktopDomain,
+ PVC_STORAGE_MAX: global.AppConfig.launchpad.pvcStorageMax || 20
}
});
} catch (error) {
diff --git a/frontend/providers/applaunchpad/src/pages/app/edit/components/StoreModal.tsx b/frontend/providers/applaunchpad/src/pages/app/edit/components/StoreModal.tsx
index 9a26406738f..98ac957f8c0 100644
--- a/frontend/providers/applaunchpad/src/pages/app/edit/components/StoreModal.tsx
+++ b/frontend/providers/applaunchpad/src/pages/app/edit/components/StoreModal.tsx
@@ -22,6 +22,7 @@ import MyFormControl from '@/components/FormControl';
import { useTranslation } from 'next-i18next';
import { pathToNameFormat } from '@/utils/tools';
import { MyTooltip } from '@sealos/ui';
+import { PVC_STORAGE_MAX } from '@/store/static';
export type StoreType = {
id?: string;
@@ -82,8 +83,8 @@ const StoreModal = ({
{t('capacity')}
-
-
+
+
diff --git a/frontend/providers/applaunchpad/src/store/static.ts b/frontend/providers/applaunchpad/src/store/static.ts
index 10c8bd2a79e..a5019dfb09c 100644
--- a/frontend/providers/applaunchpad/src/store/static.ts
+++ b/frontend/providers/applaunchpad/src/store/static.ts
@@ -9,6 +9,7 @@ export let SHOW_EVENT_ANALYZE = false;
export let CURRENCY = Coin.shellCoin;
export let UPLOAD_LIMIT = 50;
export let DOWNLOAD_LIMIT = 100;
+export let PVC_STORAGE_MAX = 20;
export const loadInitData = async () => {
try {
@@ -21,6 +22,7 @@ export const loadInitData = async () => {
UPLOAD_LIMIT = res.fileMangerConfig.uploadLimit;
DOWNLOAD_LIMIT = res.fileMangerConfig.downloadLimit;
DESKTOP_DOMAIN = res.DESKTOP_DOMAIN;
+ PVC_STORAGE_MAX = res.PVC_STORAGE_MAX;
return {
SEALOS_DOMAIN,
diff --git a/frontend/providers/applaunchpad/src/types/index.d.ts b/frontend/providers/applaunchpad/src/types/index.d.ts
index 2d3df3c88b5..51cbc29e43c 100644
--- a/frontend/providers/applaunchpad/src/types/index.d.ts
+++ b/frontend/providers/applaunchpad/src/types/index.d.ts
@@ -41,6 +41,7 @@ export type AppConfigType = {
};
launchpad: {
currencySymbol: Coin;
+ pvcStorageMax: number;
eventAnalyze: {
enabled: boolean;
fastGPTKey?: string;
diff --git a/frontend/providers/dbprovider/.vscode/settings.json b/frontend/providers/dbprovider/.vscode/settings.json
index be18ba21857..bc175c84cd6 100644
--- a/frontend/providers/dbprovider/.vscode/settings.json
+++ b/frontend/providers/dbprovider/.vscode/settings.json
@@ -17,7 +17,6 @@
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key",
"i18n-ally.translate.engines": [
- "deepl",
"google",
]
}
\ No newline at end of file
diff --git a/frontend/providers/dbprovider/public/locales/en/common.json b/frontend/providers/dbprovider/public/locales/en/common.json
index 93c6f82c577..373942dc3d8 100644
--- a/frontend/providers/dbprovider/public/locales/en/common.json
+++ b/frontend/providers/dbprovider/public/locales/en/common.json
@@ -40,7 +40,7 @@
"Running": "Running",
"Saturday": "Sat",
"Save": "Save",
- "SaveTime": "Retention Period",
+ "SaveTime": "Retention",
"Start": "Start",
"Starting": "Starting",
"Success": "succeeded",
@@ -80,6 +80,7 @@
"backup_name_cannot_empty": "Must provide backup name",
"backup_processing": "Saving Backup",
"backup_running": "Saving Backup",
+ "backup_settings": "Backup",
"backup_success_tip": "The backup task has been successfully created",
"backup_time": "Backup Timestamp",
"balance": "balance",
@@ -117,7 +118,7 @@
"copy_success": "Copy succeeded",
"covering_risks": "Coverage Risks",
"cpu": "CPU",
- "create_db": "Create New Database",
+ "create_db": "Create Database",
"creation_time": "Creation Time",
"current_connections": "Current Connections",
"data_migration_config": "Data Migration Settings",
@@ -129,6 +130,7 @@
"database_name": "Database Name",
"database_name_cannot_empty": "Database name cannot be empty",
"database_name_empty": "Application name is required",
+ "database_name_max_length": "Database name length cannot exceed {{length}} characters",
"database_name_regex": "Start with a letter, only allow lowercase letters, numbers and -",
"database_name_regex_error": "Database name can only contain lowercase letters, numbers, -, and must start with a letter.",
"database_password_empty": "Please enter the database password",
@@ -201,6 +203,7 @@
"guide_terminal_button": "Convenient terminal connection method improves data processing efficiency",
"have_error": "Failed",
"hits_ratio": "Hits Ratio",
+ "hour": "hour",
"import_through_file": "Import via File",
"important_tips_for_migrating": "Tip: Create a new database in sink DB if source_database and sink_database have overlapping data, to avoid conflicts",
"innodb_buffer_pool": "InnoDB Buffer Pool",
@@ -289,6 +292,7 @@
"start_hour": "Hour",
"start_minute": "Minute",
"start_success": "Database started. Please wait...",
+ "start_time": "Start Time",
"status": "Status",
"storage": "Storage",
"storage_cannot_empty": "Please specify storage size",
@@ -300,6 +304,8 @@
"successfully_closed_external_network_access": "Internet access disabled",
"table_locks": "Table-level locks",
"terminated_logs": "Terminated logs",
+ "termination_policy": "Delete Policy",
+ "termination_policy_tip": "Whether to keep a backup when deleting the database",
"total_price": "total_price",
"total_price_tip": "The estimated cost does not include port fees and traffic fees, and is subject to actual usage.",
"turn_on": "Enable",
@@ -317,5 +323,8 @@
"within_5_minutes": "Within 5 minutes",
"yaml_file": "YAML",
"you_have_successfully_deployed_database": "You have successfully deployed and created a database!",
- "database_name_max_length": "Database name length cannot exceed {{length}} characters"
+ "delete_backup_with_db": "Keep Backups",
+ "delete_backup_with_db_tip": "Delete the databases but leave the backups as they are",
+ "wipeout_backup_with_db": "Discard Backups",
+ "wipeout_backup_with_db_tip": "Delete the databases and the backups"
}
\ 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 8c5fe2582b8..7b6056a88cc 100644
--- a/frontend/providers/dbprovider/public/locales/zh/common.json
+++ b/frontend/providers/dbprovider/public/locales/zh/common.json
@@ -80,6 +80,7 @@
"backup_name_cannot_empty": "备份名称不能为空",
"backup_processing": "备份中",
"backup_running": "备份中",
+ "backup_settings": "备份设置",
"backup_success_tip": "备份任务已经成功创建",
"backup_time": "备份时间",
"balance": "余额",
@@ -129,6 +130,7 @@
"database_name": "新数据库名",
"database_name_cannot_empty": "数据库名称不能为空",
"database_name_empty": "应用名称不能为空",
+ "database_name_max_length": "数据库名长度不能超过 {{length}} 个字符",
"database_name_regex": "字母开头,仅能包含小写字母、数字和 -",
"database_name_regex_error": "数据库名只能包含小写字母、数字和 -,并且字母开头。",
"database_password_empty": "缺少数据库密码",
@@ -201,6 +203,7 @@
"guide_terminal_button": "便捷的终端连接方式,提升数据处理效率",
"have_error": "出现异常",
"hits_ratio": "命中率",
+ "hour": "时",
"import_through_file": "文件导入",
"important_tips_for_migrating": "如果 source 数据库中 source_database 和 sink 数据库中 sink_database 的数据库有重叠,应该在sink 数据库中新建 database,以免出现数据重叠",
"innodb_buffer_pool": "InnoDB 缓冲池",
@@ -246,6 +249,7 @@
"migration_successful": "迁移成功",
"migration_task_created_successfully": "迁移任务创建成功",
"min_replicas": "实例数最小为: ",
+ "minute": "分",
"monitor_list": "实时监控",
"multi_replica_redis_tip": "Redis 多副本包含 HA 节点,请悉知,预估价格已包含 HA 节点费用",
"name": "名字",
@@ -289,6 +293,7 @@
"start_hour": "小时",
"start_minute": "分钟",
"start_success": "数据库启动成功,请等待",
+ "start_time": "开始时间",
"status": "状态",
"storage": "磁盘",
"storage_cannot_empty": "容量不能为空",
@@ -300,6 +305,8 @@
"successfully_closed_external_network_access": "已关闭外网访问",
"table_locks": "表锁",
"terminated_logs": "中断前",
+ "termination_policy": "删除策略",
+ "termination_policy_tip": "删除数据库时是否保留备份",
"total_price": "总价",
"total_price_tip": "预估费用不包括端口费用和流量费用,以实际使用为准",
"turn_on": "开启",
@@ -317,5 +324,8 @@
"within_5_minutes": "五分钟内",
"yaml_file": "YAML 文件",
"you_have_successfully_deployed_database": "您已成功部署创建一个数据库!",
- "database_name_max_length": "数据库名长度不能超过 {{length}} 个字符"
+ "delete_backup_with_db": "保留备份",
+ "delete_backup_with_db_tip": "在删除数据库时,保留其备份",
+ "wipeout_backup_with_db": "随数据库删除",
+ "wipeout_backup_with_db_tip": "在删除数据库时,删除其备份"
}
\ 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 02c0baa180e..70b8f14d151 100644
--- a/frontend/providers/dbprovider/src/api/backup.ts
+++ b/frontend/providers/dbprovider/src/api/backup.ts
@@ -4,20 +4,6 @@ import { adaptBackup, adaptBackupByCluster, adaptDBDetail } from '@/utils/adapt'
import { AutoBackupFormType } from '@/types/backup';
import type { Props as UpdatePolicyProps } from '@/pages/api/backup/updatePolicy';
-/**
- * Deprecated API: This endpoint is no longer supported.
- *
- * The new method for obtaining backup policies is by querying the 'cluster spec backup' endpoint
- * 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.
- */
-export const getBackupPolicy = (data: { dbName: string; dbType: string }) =>
- GET(`/api/backup/policy`, data);
-
export const createBackup = (data: CreateBackupPros) => POST('/api/backup/create', data);
export const getBackupList = (dbName: string) =>
diff --git a/frontend/providers/dbprovider/src/components/Icon/icons/backupSettings.svg b/frontend/providers/dbprovider/src/components/Icon/icons/backupSettings.svg
new file mode 100644
index 00000000000..1c399a73cc4
--- /dev/null
+++ b/frontend/providers/dbprovider/src/components/Icon/icons/backupSettings.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/frontend/providers/dbprovider/src/components/Icon/index.tsx b/frontend/providers/dbprovider/src/components/Icon/index.tsx
index 3e03b06897d..b4b35735a65 100644
--- a/frontend/providers/dbprovider/src/components/Icon/index.tsx
+++ b/frontend/providers/dbprovider/src/components/Icon/index.tsx
@@ -53,6 +53,7 @@ const map = {
import: require('./icons/import.svg').default,
file: require('./icons/file.svg').default,
config: require('./icons/config.svg').default,
+ backupSettings: require('./icons/backupSettings.svg').default,
monitor: require('./icons/monitor.svg').default,
arrowDown: require('./icons/arrowDown.svg').default,
docs: require('./icons/docs.svg').default
diff --git a/frontend/providers/dbprovider/src/constants/db.ts b/frontend/providers/dbprovider/src/constants/db.ts
index 2806376806b..aa1a527c74b 100644
--- a/frontend/providers/dbprovider/src/constants/db.ts
+++ b/frontend/providers/dbprovider/src/constants/db.ts
@@ -7,6 +7,7 @@ import {
DBSourceType
} from '@/types/db';
import { CpuSlideMarkList, MemorySlideMarkList } from './editApp';
+import { I18nCommonKey } from '@/types/i18next';
export const crLabelKey = 'sealos-db-provider-cr';
export const CloudMigraionLabel = 'sealos-db-provider-cr-migrate';
@@ -270,7 +271,17 @@ export const defaultDBEditValue: DBEditType = {
cpu: CpuSlideMarkList[1].value,
memory: MemorySlideMarkList[1].value,
storage: 3,
- labels: {}
+ labels: {},
+ autoBackup: {
+ start: true,
+ type: 'day',
+ week: [],
+ hour: '23',
+ minute: '00',
+ saveTime: 7,
+ saveType: 'd'
+ },
+ terminationPolicy: 'Delete'
};
export const defaultDBDetail: DBDetailType = {
@@ -428,3 +439,28 @@ export const DBSourceConfigs: Array<{
{ key: templateDeployKey, type: 'app_store' },
{ key: sealafDeployKey, type: 'sealaf' }
];
+
+export const SelectTimeList = new Array(60).fill(0).map((item, i) => {
+ const val = i < 10 ? `0${i}` : `${i}`;
+ return {
+ id: val,
+ label: val
+ };
+});
+
+export const WeekSelectList: { label: I18nCommonKey; id: string }[] = [
+ { label: 'Monday', id: '1' },
+ { label: 'Tuesday', id: '2' },
+ { label: 'Wednesday', id: '3' },
+ { label: 'Thursday', id: '4' },
+ { label: 'Friday', id: '5' },
+ { label: 'Saturday', id: '6' },
+ { label: 'Sunday', id: '0' }
+];
+
+export const BackupSupportedDBTypeList: DBType[] = [
+ 'postgresql',
+ 'mongodb',
+ 'apecloud-mysql',
+ 'redis'
+];
diff --git a/frontend/providers/dbprovider/src/constants/theme.ts b/frontend/providers/dbprovider/src/constants/theme.ts
index 71bb2020bcf..b8638768891 100644
--- a/frontend/providers/dbprovider/src/constants/theme.ts
+++ b/frontend/providers/dbprovider/src/constants/theme.ts
@@ -1,5 +1,35 @@
import { extendTheme } from '@chakra-ui/react';
import { theme as SealosTheme } from '@sealos/ui';
+import { checkboxAnatomy } from '@chakra-ui/anatomy';
+import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
+
+const { definePartsStyle, defineMultiStyleConfig } = createMultiStyleConfigHelpers(
+ checkboxAnatomy.keys
+);
+
+const checkbox = defineMultiStyleConfig({
+ baseStyle: {
+ control: {
+ borderWidth: '1px',
+ borderRadius: '4px',
+ _checked: {
+ bg: '#F0FBFF',
+ borderColor: '#219BF4',
+ boxShadow: ' 0px 0px 0px 2.4px rgba(33, 155, 244, 0.15)',
+ _hover: {
+ bg: '#F0FBFF',
+ borderColor: '#219BF4'
+ }
+ },
+ _hover: {
+ bg: 'transparent'
+ }
+ },
+ icon: {
+ color: '#219BF4'
+ }
+ }
+});
export const theme = extendTheme(SealosTheme, {
styles: {
@@ -13,5 +43,6 @@ export const theme = extendTheme(SealosTheme, {
minWidth: '700px'
}
}
- }
+ },
+ components: { Checkbox: checkbox }
});
diff --git a/frontend/providers/dbprovider/src/pages/api/backup/policy.ts b/frontend/providers/dbprovider/src/pages/api/backup/policy.ts
deleted file mode 100644
index e87b1b4c5ff..00000000000
--- a/frontend/providers/dbprovider/src/pages/api/backup/policy.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import type { NextApiRequest, NextApiResponse } from 'next';
-import { ApiResp } from '@/services/kubernet';
-import { authSession } from '@/services/backend/auth';
-import { getK8s } from '@/services/backend/kubernetes';
-import { jsonRes } from '@/services/backend/response';
-import { adaptPolicy } from '@/utils/adapt';
-import { DBBackupPolicyNameMap, DBTypeEnum } from '@/constants/db';
-
-export type Props = {
- dbName: string;
- dbType: `${DBTypeEnum}`;
-};
-
-export default async function handler(req: NextApiRequest, res: NextApiResponse) {
- const { dbName, dbType } = req.query as Props;
-
- if (!dbName || !dbType) {
- jsonRes(res, {
- code: 500,
- error: 'params error'
- });
- return;
- }
-
- const group = 'dataprotection.kubeblocks.io';
- const version = 'v1alpha1';
- const plural = 'backuppolicies';
-
- try {
- const { k8sCustomObjects, namespace } = await getK8s({
- kubeconfig: await authSession(req)
- });
-
- // get backup backupolicies.dataprotection.kubeblocks.io
- const { body } = (await k8sCustomObjects.getNamespacedCustomObject(
- group,
- version,
- namespace,
- plural,
- `${dbName}-${DBBackupPolicyNameMap[dbType]}-backup-policy`
- )) as { body: any };
-
- jsonRes(res, {
- data: adaptPolicy(body)
- });
- } catch (err: any) {
- jsonRes(res, {
- code: 500,
- error: err
- });
- }
-}
diff --git a/frontend/providers/dbprovider/src/pages/api/backup/updatePolicy.ts b/frontend/providers/dbprovider/src/pages/api/backup/updatePolicy.ts
index 739845706af..33087a3da7f 100644
--- a/frontend/providers/dbprovider/src/pages/api/backup/updatePolicy.ts
+++ b/frontend/providers/dbprovider/src/pages/api/backup/updatePolicy.ts
@@ -1,10 +1,11 @@
import { BACKUP_REPO_DEFAULT_KEY } from '@/constants/backup';
import { DBTypeEnum } from '@/constants/db';
import { authSession } from '@/services/backend/auth';
-import { K8sApi, K8sApiDefault, getK8s } from '@/services/backend/kubernetes';
+import { K8sApiDefault, getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';
import { ApiResp } from '@/services/kubernet';
import { BackupRepoCRItemType } from '@/types/backup';
+import { KbPgClusterType } from '@/types/cluster';
import * as k8s from '@kubernetes/client-node';
import { PatchUtils } from '@kubernetes/client-node';
import type { NextApiRequest, NextApiResponse } from 'next';
@@ -12,13 +13,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
export type Props = {
dbName: string;
dbType: `${DBTypeEnum}`;
- autoBackup?: {
- enabled: boolean;
- cronExpression: string;
- method: string;
- retentionPeriod: string;
- repoName: string;
- };
+ autoBackup?: KbPgClusterType['spec']['backup'];
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -31,66 +26,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
- const group = 'apps.kubeblocks.io';
- const version = 'v1alpha1';
- const plural = 'clusters';
-
try {
const { k8sCustomObjects, namespace } = await getK8s({
kubeconfig: await authSession(req)
});
- // Get cluster backup repository
- const kc = K8sApiDefault();
- const backupRepos = (await kc
- .makeApiClient(k8s.CustomObjectsApi)
- .listClusterCustomObject('dataprotection.kubeblocks.io', 'v1alpha1', 'backuprepos')) as {
- body: {
- items: BackupRepoCRItemType[];
- };
- };
-
- const defaultRepoItem = backupRepos?.body?.items?.find(
- (item) => item.metadata.annotations[BACKUP_REPO_DEFAULT_KEY] === 'true'
- );
-
- const backupRepoName = defaultRepoItem?.metadata?.name;
-
- if (!backupRepoName) {
- throw new Error('Missing backup repository');
- }
- const patch = autoBackup
- ? [
- {
- op: 'replace',
- path: '/spec/backup',
- value: {
- ...autoBackup,
- repoName: backupRepoName
- }
- }
- ]
- : [
- {
- op: 'replace',
- path: '/spec/backup/enabled',
- value: false
- }
- ];
-
- // get backup backupolicies.dataprotection.kubeblocks.io
- const result = await k8sCustomObjects.patchNamespacedCustomObject(
- group,
- version,
- namespace,
- plural,
+ const result = await updateBackupPolicyApi({
dbName,
- patch,
- undefined,
- undefined,
- undefined,
- { headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_PATCH } }
- );
+ dbType,
+ autoBackup,
+ k8sCustomObjects,
+ namespace
+ });
jsonRes(res, { data: result?.body });
} catch (err: any) {
@@ -100,3 +47,79 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
}
+
+export type UpdateBackupPolicyParams = {
+ dbName: string;
+ dbType: `${DBTypeEnum}`;
+ autoBackup?: KbPgClusterType['spec']['backup'];
+ k8sCustomObjects: k8s.CustomObjectsApi;
+ namespace: string;
+};
+
+export async function updateBackupPolicyApi({
+ dbName,
+ dbType,
+ autoBackup,
+ k8sCustomObjects,
+ namespace
+}: UpdateBackupPolicyParams) {
+ const group = 'apps.kubeblocks.io';
+ const version = 'v1alpha1';
+ const plural = 'clusters';
+
+ // Get cluster backup repository
+ const kc = K8sApiDefault();
+ const backupRepos = (await kc
+ .makeApiClient(k8s.CustomObjectsApi)
+ .listClusterCustomObject('dataprotection.kubeblocks.io', 'v1alpha1', 'backuprepos')) as {
+ body: {
+ items: BackupRepoCRItemType[];
+ };
+ };
+
+ const defaultRepoItem = backupRepos?.body?.items?.find(
+ (item) => item.metadata.annotations[BACKUP_REPO_DEFAULT_KEY] === 'true'
+ );
+
+ const backupRepoName = defaultRepoItem?.metadata?.name;
+
+ if (!backupRepoName) {
+ throw new Error('Missing backup repository');
+ }
+
+ const patch = autoBackup
+ ? [
+ {
+ op: 'replace',
+ path: '/spec/backup',
+ value: {
+ ...autoBackup,
+ repoName: backupRepoName
+ }
+ }
+ ]
+ : [
+ {
+ op: 'replace',
+ path: '/spec/backup/enabled',
+ value: false
+ }
+ ];
+
+ console.log('backup patch', patch);
+
+ const result = await k8sCustomObjects.patchNamespacedCustomObject(
+ group,
+ version,
+ namespace,
+ plural,
+ dbName,
+ patch,
+ undefined,
+ undefined,
+ undefined,
+ { headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_PATCH } }
+ );
+
+ return result;
+}
diff --git a/frontend/providers/dbprovider/src/pages/api/createDB.ts b/frontend/providers/dbprovider/src/pages/api/createDB.ts
index 76126e612c9..7b94e2832ac 100644
--- a/frontend/providers/dbprovider/src/pages/api/createDB.ts
+++ b/frontend/providers/dbprovider/src/pages/api/createDB.ts
@@ -6,6 +6,10 @@ import { KbPgClusterType } from '@/types/cluster';
import { BackupItemType, DBEditType } from '@/types/db';
import { json2Account, json2ClusterOps, json2CreateCluster } from '@/utils/json2Yaml';
import type { NextApiRequest, NextApiResponse } from 'next';
+import { updateBackupPolicyApi } from './backup/updatePolicy';
+import { BackupSupportedDBTypeList } from '@/constants/db';
+import { convertBackupFormToSpec } from '@/utils/adapt';
+import { CustomObjectsApi, PatchUtils } from '@kubernetes/client-node';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -39,7 +43,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
'Gi',
''
)
- )
+ ),
+ terminationPolicy: body.spec.terminationPolicy
};
const opsRequests = [];
@@ -77,6 +82,30 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
+ if (BackupSupportedDBTypeList.includes(dbForm.dbType) && dbForm?.autoBackup) {
+ const autoBackup = convertBackupFormToSpec({
+ autoBackup: dbForm?.autoBackup,
+ dbType: dbForm.dbType
+ });
+
+ await updateBackupPolicyApi({
+ dbName: dbForm.dbName,
+ dbType: dbForm.dbType,
+ autoBackup,
+ k8sCustomObjects,
+ namespace
+ });
+
+ if (currentConfig.terminationPolicy !== dbForm.terminationPolicy) {
+ await updateTerminationPolicyApi({
+ dbName: dbForm.dbName,
+ terminationPolicy: dbForm.terminationPolicy,
+ k8sCustomObjects,
+ namespace
+ });
+ }
+ }
+
return jsonRes(res, {
data: 'success update db'
});
@@ -101,6 +130,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
await applyYamlList([updateAccountYaml], 'replace');
+ if (BackupSupportedDBTypeList.includes(dbForm.dbType) && dbForm?.autoBackup) {
+ const autoBackup = convertBackupFormToSpec({
+ autoBackup: dbForm?.autoBackup,
+ dbType: dbForm.dbType
+ });
+
+ await updateBackupPolicyApi({
+ dbName: dbForm.dbName,
+ dbType: dbForm.dbType,
+ autoBackup,
+ k8sCustomObjects,
+ namespace
+ });
+ }
+
jsonRes(res, {
data: 'success create db'
});
@@ -111,3 +155,42 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
}
+
+export async function updateTerminationPolicyApi({
+ dbName,
+ terminationPolicy,
+ k8sCustomObjects,
+ namespace
+}: {
+ dbName: string;
+ terminationPolicy: string;
+ k8sCustomObjects: CustomObjectsApi;
+ namespace: string;
+}) {
+ const group = 'apps.kubeblocks.io';
+ const version = 'v1alpha1';
+ const plural = 'clusters';
+
+ const patch = [
+ {
+ op: 'replace',
+ path: '/spec/terminationPolicy',
+ value: terminationPolicy
+ }
+ ];
+
+ const result = await k8sCustomObjects.patchNamespacedCustomObject(
+ group,
+ version,
+ namespace,
+ plural,
+ dbName,
+ patch,
+ undefined,
+ undefined,
+ undefined,
+ { headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_PATCH } }
+ );
+
+ return result;
+}
diff --git a/frontend/providers/dbprovider/src/pages/db/detail/components/BackupModal.tsx b/frontend/providers/dbprovider/src/pages/db/detail/components/BackupModal.tsx
index c4275efaab1..1174a2df80a 100644
--- a/frontend/providers/dbprovider/src/pages/db/detail/components/BackupModal.tsx
+++ b/frontend/providers/dbprovider/src/pages/db/detail/components/BackupModal.tsx
@@ -1,6 +1,6 @@
import { createBackup, updateBackupPolicy } from '@/api/backup';
import Tip from '@/components/Tip';
-import { DBBackupMethodNameMap, DBTypeEnum } from '@/constants/db';
+import { DBBackupMethodNameMap, DBTypeEnum, SelectTimeList, WeekSelectList } from '@/constants/db';
import { useConfirm } from '@/hooks/useConfirm';
import type { AutoBackupFormType, AutoBackupType } from '@/types/backup';
import { I18nCommonKey } from '@/types/i18next';
@@ -83,31 +83,6 @@ const BackupModal = ({
defaultValues: defaultVal
});
- const selectTimeList = useRef<
- {
- id: string;
- label: string;
- }[]
- >(
- (() =>
- new Array(60).fill(0).map((item, i) => {
- const val = i < 10 ? `0${i}` : `${i}`;
- return {
- id: val,
- label: val
- };
- }))()
- );
- const weekSelectList: MutableRefObject<{ label: I18nCommonKey; id: string }[]> = useRef([
- { label: 'Monday', id: '1' },
- { label: 'Tuesday', id: '2' },
- { label: 'Wednesday', id: '3' },
- { label: 'Thursday', id: '4' },
- { label: 'Friday', id: '5' },
- { label: 'Saturday', id: '6' },
- { label: 'Sunday', id: '0' }
- ]);
-
const navStyle = useCallback(
(nav: `${NavEnum}`) => ({
p: '8px',
@@ -331,7 +306,7 @@ const BackupModal = ({
{getAutoValues('type') === 'week' && (
- {weekSelectList.current.map((item) => (
+ {WeekSelectList.map((item) => (
({ value: i.id, label: i.label }))}
+ list={SelectTimeList.slice(0, 24).map((i) => ({
+ value: i.id,
+ label: i.label
+ }))}
// icon={}
onchange={(val: any) => {
setAutoValue('hour', val);
@@ -376,7 +352,7 @@ const BackupModal = ({
({
+ list={SelectTimeList.map((i) => ({
value: i.id,
label: i.label
}))}
diff --git a/frontend/providers/dbprovider/src/pages/db/detail/index.tsx b/frontend/providers/dbprovider/src/pages/db/detail/index.tsx
index ff545e2f854..c882761518c 100644
--- a/frontend/providers/dbprovider/src/pages/db/detail/index.tsx
+++ b/frontend/providers/dbprovider/src/pages/db/detail/index.tsx
@@ -22,6 +22,7 @@ import ReconfigureTable from './components/Reconfigure/index';
import useDetailDriver from '@/hooks/useDetailDriver';
import ErrorLog from '@/pages/db/detail/components/ErrorLog';
import MyIcon from '@/components/Icon';
+import { BackupSupportedDBTypeList } from '@/constants/db';
enum TabEnum {
pod = 'pod',
@@ -52,9 +53,7 @@ const AppDetail = ({
const { listNav } = useMemo(() => {
const PublicNetMigration = ['postgresql', 'apecloud-mysql', 'mongodb'].includes(dbType);
const MigrateSupported = ['postgresql', 'mongodb', 'apecloud-mysql'].includes(dbType);
- const BackupSupported =
- ['postgresql', 'mongodb', 'apecloud-mysql', 'redis'].includes(dbType) &&
- SystemEnv.BACKUP_ENABLED;
+ const BackupSupported = BackupSupportedDBTypeList.includes(dbType) && SystemEnv.BACKUP_ENABLED;
const listNavValue = [
{
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 83baeeb9cd9..b2bc5d6e8ff 100644
--- a/frontend/providers/dbprovider/src/pages/db/edit/components/Form.tsx
+++ b/frontend/providers/dbprovider/src/pages/db/edit/components/Form.tsx
@@ -3,11 +3,19 @@ import MyIcon from '@/components/Icon';
import PriceBox from '@/components/PriceBox';
import QuotaBox from '@/components/QuotaBox';
import Tip from '@/components/Tip';
-import { DBTypeEnum, DBTypeList, RedisHAConfig } from '@/constants/db';
+import {
+ BackupSupportedDBTypeList,
+ DBTypeEnum,
+ DBTypeList,
+ RedisHAConfig,
+ SelectTimeList,
+ WeekSelectList
+} from '@/constants/db';
import { CpuSlideMarkList, MemorySlideMarkList } from '@/constants/editApp';
import useEnvStore from '@/store/env';
import { DBVersionMap, INSTALL_ACCOUNT } from '@/store/static';
import type { QueryType } from '@/types';
+import { AutoBackupType } from '@/types/backup';
import type { DBEditType } from '@/types/db';
import { I18nCommonKey } from '@/types/i18next';
import { InfoOutlineIcon } from '@chakra-ui/icons';
@@ -15,6 +23,8 @@ import {
Box,
Button,
Center,
+ Checkbox,
+ Collapse,
Flex,
FormControl,
Grid,
@@ -25,6 +35,7 @@ import {
NumberInput,
NumberInputField,
NumberInputStepper,
+ Switch,
Text,
useDisclosure,
useTheme
@@ -33,7 +44,7 @@ import { MySelect, MySlider, MyTooltip, RangeInput, Tabs } from '@sealos/ui';
import { throttle } from 'lodash';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
-import { useEffect, useMemo, useState } from 'react';
+import { MutableRefObject, useEffect, useMemo, useRef, useState } from 'react';
import { UseFormReturn } from 'react-hook-form';
const Form = ({
@@ -64,6 +75,11 @@ const Form = ({
id: 'baseInfo',
label: 'basic',
icon: 'formInfo'
+ },
+ {
+ id: 'backupSettings',
+ label: 'backup_settings',
+ icon: 'backupSettings'
}
];
@@ -194,7 +210,7 @@ const Form = ({
name={item.icon as any}
w={'20px'}
h={'20px'}
- color={activeNav === item.id ? 'grayModern.600' : 'myGray.400'}
+ color={activeNav === item.id ? 'grayModern.900' : 'grayModern.500'}
/>
{t(item.label)}
@@ -504,6 +520,203 @@ const Form = ({
+ {BackupSupportedDBTypeList.includes(getValues('dbType')) && (
+
+
+
+ {t('backup_settings')}
+ {
+ setValue('autoBackup.start', e.target.checked);
+ }}
+ />
+
+
+
+
+ {t('CronExpression')}
+ {
+ setValue('autoBackup.type', e as AutoBackupType);
+ }}
+ />
+
+ {getValues('autoBackup.type') === 'week' && (
+
+
+ {WeekSelectList.map((item) => (
+
+ {
+ const val = e.target.checked;
+ const checkedList = [...getValues('autoBackup.week')];
+ const index = checkedList.findIndex((week) => week === item.id);
+ if (val && index === -1) {
+ setValue('autoBackup.week', checkedList.concat(item.id));
+ } else if (!val && index > -1) {
+ checkedList.splice(index, 1);
+ setValue('autoBackup.week', checkedList);
+ }
+ }}
+ >
+ {t(item.label)}
+
+
+ ))}
+
+ )}
+
+ {t('start_time')}
+ {getValues('autoBackup.type') !== 'hour' && (
+
+ ({
+ value: i.id,
+ label: i.label
+ }))}
+ onchange={(val: any) => {
+ setValue('autoBackup.hour', val);
+ }}
+ />
+
+ {t('hour')}
+
+
+ )}
+
+
+ ({
+ value: i.id,
+ label: i.label
+ }))}
+ onchange={(val: any) => {
+ setValue('autoBackup.minute', val);
+ }}
+ />
+
+ {t('minute')}
+
+
+
+
+
+ {t('SaveTime')}
+
+ {
+ setValue('autoBackup.saveType', val);
+ }}
+ />
+
+
+ {t('termination_policy')}
+ {/* {
+ setValue('terminationPolicy', e.target.checked ? 'Delete' : 'WipeOut');
+ }}
+ /> */}
+
+ {['Delete', 'WipeOut'].map((item) => {
+ const isChecked = getValues('terminationPolicy') === item;
+
+ return (
+ {
+ setValue(
+ 'terminationPolicy',
+ getValues('terminationPolicy') === 'Delete' ? 'WipeOut' : 'Delete'
+ );
+ }}
+ cursor={'pointer'}
+ >
+
+ {isChecked && (
+
+ )}
+
+
+
+ {t(`${item.toLowerCase()}_backup_with_db` as I18nCommonKey)}
+
+
+ {t(`${item.toLowerCase()}_backup_with_db_tip` as I18nCommonKey)}
+
+
+
+ );
+ })}
+
+
+
+
+
+ )}
>
diff --git a/frontend/providers/dbprovider/src/pages/db/edit/index.tsx b/frontend/providers/dbprovider/src/pages/db/edit/index.tsx
index f8d72d7324e..aa9e182131f 100644
--- a/frontend/providers/dbprovider/src/pages/db/edit/index.tsx
+++ b/frontend/providers/dbprovider/src/pages/db/edit/index.tsx
@@ -1,5 +1,5 @@
import { adapterMongoHaConfig, applyYamlList, createDB } from '@/api/db';
-import { defaultDBEditValue } from '@/constants/db';
+import { BackupSupportedDBTypeList, defaultDBEditValue } from '@/constants/db';
import { editModeMap } from '@/constants/editApp';
import { useConfirm } from '@/hooks/useConfirm';
import { useLoading } from '@/hooks/useLoading';
@@ -9,7 +9,7 @@ import { DBVersionMap } from '@/store/static';
import { useUserStore } from '@/store/user';
import type { YamlItemType } from '@/types';
import type { DBEditType } from '@/types/db';
-import { adaptDBForm } from '@/utils/adapt';
+import { adaptDBForm, convertBackupFormToSpec } from '@/utils/adapt';
import { serviceSideProps } from '@/utils/i18n';
import { json2Account, json2CreateCluster, limitRangeYaml } from '@/utils/json2Yaml';
import { Box, Flex } from '@chakra-ui/react';
@@ -25,6 +25,7 @@ import Form from './components/Form';
import Header from './components/Header';
import Yaml from './components/Yaml';
import useDriver from '@/hooks/useDriver';
+import { updateBackupPolicy } from '@/api/backup';
const ErrorModal = dynamic(() => import('@/components/ErrorModal'));
diff --git a/frontend/providers/dbprovider/src/pages/db/migrate/index.tsx b/frontend/providers/dbprovider/src/pages/db/migrate/index.tsx
index 62381c5d251..216f1ba3f65 100644
--- a/frontend/providers/dbprovider/src/pages/db/migrate/index.tsx
+++ b/frontend/providers/dbprovider/src/pages/db/migrate/index.tsx
@@ -39,7 +39,8 @@ const defaultEdit: MigrateForm = {
sourceDatabase: '',
sourceDatabaseTable: ['All'],
isChecked: false,
- continued: false
+ continued: false,
+ terminationPolicy: 'Delete'
};
const EditApp = ({
diff --git a/frontend/providers/dbprovider/src/types/backup.d.ts b/frontend/providers/dbprovider/src/types/backup.d.ts
index 7468cf44e40..a0b03bdf044 100644
--- a/frontend/providers/dbprovider/src/types/backup.d.ts
+++ b/frontend/providers/dbprovider/src/types/backup.d.ts
@@ -59,8 +59,8 @@ export interface BackupCRItemType {
export type AutoBackupType = 'day' | 'hour' | 'week';
export type AutoBackupFormType = {
- start: boolean;
- type: AutoType;
+ start: boolean; // enable auto backup
+ type: AutoBackupType;
week: string[];
hour: string;
minute: string;
diff --git a/frontend/providers/dbprovider/src/types/cluster.d.ts b/frontend/providers/dbprovider/src/types/cluster.d.ts
index c75d11ec549..3ca22bead0d 100644
--- a/frontend/providers/dbprovider/src/types/cluster.d.ts
+++ b/frontend/providers/dbprovider/src/types/cluster.d.ts
@@ -20,10 +20,18 @@ export type KbPgClusterType = {
status?: KubeBlockClusterStatus;
};
+/**
+ * DoNotTerminate: The database deletion cannot be performed.
+ * Halt: deletes only database resources, but retains database disks.
+ * Delete: deletes only the database resource, but keeps the database backup.
+ * WipeOut: deletes all contents of the database.
+ */
+export type KubeBlockClusterTerminationPolicy = 'Delete' | 'WipeOut';
+
export interface KubeBlockClusterSpec {
clusterDefinitionRef: `${DBTypeEnum}`;
clusterVersionRef: string;
- terminationPolicy: string;
+ terminationPolicy: KubeBlockClusterTerminationPolicy;
componentSpecs: {
componentDefRef: `${DBTypeEnum}`;
name: `${DBTypeEnum}`;
@@ -54,7 +62,7 @@ export interface KubeBlockClusterSpec {
enabled: boolean;
cronExpression: string;
method: string;
- pitrEnabled: boolean;
+ pitrEnabled?: boolean;
repoName: string;
retentionPeriod: string;
};
@@ -89,6 +97,9 @@ export type KbPodType = {
};
};
+/**
+ * @deprecated
+ */
export type KubeBlockBackupPolicyType = {
metadata: {
name: string;
diff --git a/frontend/providers/dbprovider/src/types/db.d.ts b/frontend/providers/dbprovider/src/types/db.d.ts
index 054612e8636..48fe96769ff 100644
--- a/frontend/providers/dbprovider/src/types/db.d.ts
+++ b/frontend/providers/dbprovider/src/types/db.d.ts
@@ -13,6 +13,8 @@ import type {
V1ContainerStatus
} from '@kubernetes/client-node';
import { I18nCommonKey } from './i18next';
+import { AutoBackupFormType } from './backup';
+import { KubeBlockClusterTerminationPolicy } from './cluster';
export type DBType = `${DBTypeEnum}`;
@@ -68,6 +70,8 @@ export interface DBEditType {
memory: number;
storage: number;
labels: { [key: string]: string };
+ terminationPolicy: KubeBlockClusterTerminationPolicy;
+ autoBackup?: AutoBackupFormType;
}
export type DBSourceType = 'app_store' | 'sealaf';
diff --git a/frontend/providers/dbprovider/src/types/migrate.d.ts b/frontend/providers/dbprovider/src/types/migrate.d.ts
index 8a1fab267c0..6e343407868 100644
--- a/frontend/providers/dbprovider/src/types/migrate.d.ts
+++ b/frontend/providers/dbprovider/src/types/migrate.d.ts
@@ -1,3 +1,4 @@
+import { KubeBlockClusterTerminationPolicy } from './cluster';
import { DBType } from './db';
export enum InternetMigrationTemplate {
@@ -113,6 +114,7 @@ export type MigrateForm = {
isChecked: boolean;
continued: boolean;
remark?: string;
+ terminationPolicy: KubeBlockClusterTerminationPolicy;
};
export interface MigrateItemType {
diff --git a/frontend/providers/dbprovider/src/utils/adapt.ts b/frontend/providers/dbprovider/src/utils/adapt.ts
index d8429ec9b10..d43342f0d3a 100644
--- a/frontend/providers/dbprovider/src/utils/adapt.ts
+++ b/frontend/providers/dbprovider/src/utils/adapt.ts
@@ -1,17 +1,14 @@
import { BACKUP_REMARK_LABEL_KEY, BackupTypeEnum, backupStatusMap } from '@/constants/backup';
import {
+ DBBackupMethodNameMap,
DBPreviousConfigKey,
DBReconfigStatusMap,
DBSourceConfigs,
MigrationRemark,
dbStatusMap
} from '@/constants/db';
-import type { AutoBackupFormType, BackupCRItemType } from '@/types/backup';
-import type {
- KbPgClusterType,
- KubeBlockBackupPolicyType,
- KubeBlockOpsRequestType
-} from '@/types/cluster';
+import type { AutoBackupFormType, AutoBackupType, BackupCRItemType } from '@/types/backup';
+import type { KbPgClusterType, KubeBlockOpsRequestType } from '@/types/cluster';
import type {
DBDetailType,
DBEditType,
@@ -106,41 +103,57 @@ export const adaptDBDetail = (db: KbPgClusterType): DBDetailType => {
conditions: db?.status?.conditions || [],
isDiskSpaceOverflow: false,
labels: db.metadata.labels || {},
- source: getDBSource(db)
+ source: getDBSource(db),
+ autoBackup: adaptBackupByCluster(db),
+ terminationPolicy: db.spec?.terminationPolicy || 'Delete'
};
};
export const adaptBackupByCluster = (db: KbPgClusterType): AutoBackupFormType => {
- const backup = db.spec.backup
- ? adaptPolicy({
- metadata: {
- name: db.metadata.name,
- uid: db.metadata.uid
- },
- spec: {
- retention: {
- ttl: db.spec.backup.retentionPeriod
- },
- schedule: {
- datafile: {
- cronExpression: db.spec.backup.cronExpression,
- enable: db.spec.backup.enabled
- }
- }
- }
- })
- : {
- start: false,
- hour: '18',
- minute: '00',
- week: [],
- type: 'day',
- saveTime: 7,
- saveType: 'd'
- };
+ const backup =
+ db.spec?.backup && db.spec?.backup?.cronExpression
+ ? adaptPolicy(db.spec.backup)
+ : {
+ start: false,
+ hour: '18',
+ minute: '00',
+ week: [],
+ type: 'day' as AutoBackupType,
+ saveTime: 7,
+ saveType: 'd'
+ };
return backup;
};
+export const convertBackupFormToSpec = (data: {
+ autoBackup?: AutoBackupFormType;
+ dbType: DBType;
+}): KbPgClusterType['spec']['backup'] => {
+ const cron = (() => {
+ if (data.autoBackup?.type === 'week') {
+ if (!data.autoBackup?.week?.length) {
+ throw new Error('Week is empty');
+ }
+ return `${data.autoBackup.minute} ${data.autoBackup.hour} * * ${data.autoBackup.week.join(
+ ','
+ )}`;
+ }
+ if (data.autoBackup?.type === 'day') {
+ return `${data.autoBackup.minute} ${data.autoBackup.hour} * * *`;
+ }
+ return `${data.autoBackup?.minute} * * * *`;
+ })();
+
+ return {
+ enabled: data.autoBackup?.start ?? false,
+ cronExpression: convertCronTime(cron, -8),
+ method: DBBackupMethodNameMap[data.dbType],
+ retentionPeriod: `${data.autoBackup?.saveTime}${data.autoBackup?.saveType}`,
+ repoName: '',
+ pitrEnabled: false
+ };
+};
+
export const adaptDBForm = (db: DBDetailType): DBEditType => {
const keys: Record = {
dbType: 1,
@@ -150,7 +163,9 @@ export const adaptDBForm = (db: DBDetailType): DBEditType => {
memory: 1,
replicas: 1,
storage: 1,
- labels: 1
+ labels: 1,
+ autoBackup: 1,
+ terminationPolicy: 1
};
const form: any = {};
@@ -220,7 +235,7 @@ export const adaptBackup = (backup: BackupCRItemType): BackupItemType => {
};
};
-export const adaptPolicy = (policy: KubeBlockBackupPolicyType): AutoBackupFormType => {
+export const adaptPolicy = (policy: KbPgClusterType['spec']['backup']): AutoBackupFormType => {
function parseDate(str: string) {
const regex = /(\d+)([a-zA-Z]+)/;
const matches = str.match(regex);
@@ -234,6 +249,7 @@ export const adaptPolicy = (policy: KubeBlockBackupPolicyType): AutoBackupFormTy
return { number: 7, unit: 'd' };
}
+
function parseCron(str: string) {
const cronFields = convertCronTime(str, 8).split(' ');
const minuteField = cronFields[0];
@@ -249,7 +265,6 @@ export const adaptPolicy = (policy: KubeBlockBackupPolicyType): AutoBackupFormTy
type: 'week'
};
}
- console.log(minuteField, hourField, weekField);
// every day
if (hourField !== '*') {
@@ -279,12 +294,12 @@ export const adaptPolicy = (policy: KubeBlockBackupPolicyType): AutoBackupFormTy
};
}
- const { number: saveTime, unit: saveType } = parseDate(policy.spec.retention.ttl);
- const { hour, minute, week, type } = parseCron(policy.spec.schedule.datafile.cronExpression);
+ const { number: saveTime, unit: saveType } = parseDate(policy.retentionPeriod);
+ const { hour, minute, week, type } = parseCron(policy?.cronExpression ?? '0 0 * * *');
return {
- start: policy.spec.schedule.datafile.enable,
- type,
+ start: policy.enabled,
+ type: type as AutoBackupType,
week,
hour,
minute,
diff --git a/frontend/providers/dbprovider/src/utils/json2Yaml.ts b/frontend/providers/dbprovider/src/utils/json2Yaml.ts
index 88fb8333efd..6c581cd1491 100644
--- a/frontend/providers/dbprovider/src/utils/json2Yaml.ts
+++ b/frontend/providers/dbprovider/src/utils/json2Yaml.ts
@@ -11,13 +11,14 @@ import {
} from '@/constants/db';
import { StorageClassName } from '@/store/env';
import type { BackupItemType, DBDetailType, DBEditType, DBType } from '@/types/db';
-import { DumpForm, MigrateForm } from '@/types/migrate';
+import { MigrateForm } from '@/types/migrate';
import { encodeToHex, formatTime, str2Num } from '@/utils/tools';
import dayjs from 'dayjs';
import yaml from 'js-yaml';
import { getUserNamespace } from './user';
import { V1StatefulSet } from '@kubernetes/client-node';
import { customAlphabet } from 'nanoid';
+
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5);
/**
@@ -110,7 +111,7 @@ export const json2CreateCluster = (data: DBEditType, backupInfo?: BackupItemType
]
}
],
- terminationPolicy,
+ terminationPolicy: data.terminationPolicy,
tolerations: []
}
}
@@ -153,7 +154,7 @@ export const json2CreateCluster = (data: DBEditType, backupInfo?: BackupItemType
]
}
],
- terminationPolicy,
+ terminationPolicy: data.terminationPolicy,
tolerations: []
}
}
@@ -199,7 +200,7 @@ export const json2CreateCluster = (data: DBEditType, backupInfo?: BackupItemType
]
}
],
- terminationPolicy,
+ terminationPolicy: data.terminationPolicy,
tolerations: []
}
}
@@ -280,7 +281,7 @@ export const json2CreateCluster = (data: DBEditType, backupInfo?: BackupItemType
: {})
}
],
- terminationPolicy,
+ terminationPolicy: data.terminationPolicy,
tolerations: []
}
}