Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:db automatic database backup #4377

Merged
merged 1 commit into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 42 additions & 4 deletions frontend/providers/dbprovider/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
"Submit Error": "Submit Error",
"Export": "Export",
"Deploy": "Deploy",
"Edit Environment Variables": "Edit Environment Variables",
"DataBase Deployment": "DataBase Deployment",
"Creating": "Creating",
"Failed": "Failed",
"Update DataBase": "Update DataBase",
Expand Down Expand Up @@ -93,7 +91,6 @@
"Basic": "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",
"Redis HA": "HA",
"Monitor List": "Monitor List",
"No Data Available": "No Data Available",
"Resources": "Resources",
Expand Down Expand Up @@ -220,5 +217,46 @@
"stepOne": "# Set 'binlog_format' configuration to 'row'",
"stepTwo": "# Set 'binlog_row_image' configuration to 'full'"
}
}
},
"Auto": "Auto",
"Auto Backup": "Auto Backup",
"Backup Database": "Backup Database",
"Save": "Save",
"SaveTime": "SaveTime",
"Pod": "Pod",
"Monday": "Monday",
"Day": "Day",
"CronExpression": "Cron Expression",
"Confirm Restart": "Confirm Restart",
"Database Name": "Database Name",
"Delete Failed": "Delete Failed",
"Delete successful": "Delete successful",
"Friday": "Friday",
"Manual": "Manual",
"Delete Backup": "Delete Backup",
"Anticipated Price": "Anticipated Price",
"Restarting": "Restarting",
"Week": "Week",
"Start Minute": "Start Minute",
"Start Hour": "Start Hour",
"Start Backup": "Start Backup",
"Backup Name cannot empty": "Backup Name cannot empty",
"Manual Backup": "Manual Backup",
"Set auto backup successful": "Set auto backup successful",
"Saturday": "Saturday",
"Starting": "Starting",
"Sunday": "Sunday",
"Restore Success": "Restore Success",
"Restore Backup": "Restore Backup",
"Restore Database": "Restore Database",
"Pausing": "Pausing",
"Perday": "Perday",
"The backup task has been created successfully !": "The backup task has benn created successfully !",
"Thursday": "Thursday",
"Tuesday": "Tuesday",
"Updating": "Updating",
"Wednesday": "Wednesday",
"Failed to turn off automatic backup": "Failed to turn off automatic backup",
"Automatic backup is turned off": "Automatic backup is turned off",
"Are you sure you want to turn off automatic backup": "Are you sure you want to turn off automatic backup?"
}
7 changes: 6 additions & 1 deletion frontend/providers/dbprovider/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,5 +255,10 @@
"stepOne": "# 设置 'binlog_format' 配置为 'row'",
"stepTwo": "# 设置 'binlog_row_image' 配置为'full'"
}
}
},
"Start Hour": "小时",
"Backup Name cannot empty": "备份名称不能为空",
"Failed to turn off automatic backup": "关闭自动备份失败",
"Automatic backup is turned off": "已关闭自动备份",
"Are you sure you want to turn off automatic backup": "确定关闭自动备份吗"
}
25 changes: 24 additions & 1 deletion frontend/providers/dbprovider/src/api/backup.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
import { GET, POST, DELETE } from '@/services/request';
import type { Props as CreateBackupPros } from '@/pages/api/backup/create';
import { adaptBackup } from '@/utils/adapt';
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.
*
* @param data - Object containing information about the database, including dbName and dbType.
* @returns {Promise<AutoBackupFormType>} - A promise resolving to the auto-backup configuration form.
*/
export const getBackupPolicy = (data: { dbName: string; dbType: string }) =>
GET<AutoBackupFormType>(`/api/backup/policy`, data);

export const createBackup = (data: CreateBackupPros) => POST('/api/backup/create', data);

export const getBackupList = (dbName: string) =>
GET('/api/backup/getBackupList', { dbName }).then((res) => res.map(adaptBackup));

export const deleteBackup = (backupName: string) =>
DELETE(`/api/backup/delBackup?backupName=${backupName}`);

export const updateBackupPolicy = (data: UpdatePolicyProps) =>
POST<AutoBackupFormType>(`/api/backup/updatePolicy`, data);

/**
* Retrieves backup policy by cluster.
* @param data An object containing dbName and dbType properties.
* @returns A Promise that resolves to the adapted backup policy data upon successful execution.
*/
export const getBackupPolicyByCluster = (data: { dbName: string; dbType: string }) =>
GET(`/api/getDBByName?name=${data.dbName}`).then(adaptBackupByCluster);
2 changes: 0 additions & 2 deletions frontend/providers/dbprovider/src/pages/api/backup/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,3 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
}

export const getBackupPolicy = ({}: Props) => {};
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export type Props = {
export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
const { dbName, dbType, patch } = req.body as Props;

console.log(dbName, dbType, patch);

if (!dbName || !dbType || !patch) {
jsonRes(res, {
code: 500,
Expand All @@ -23,30 +25,30 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
return;
}

const group = 'dataprotection.kubeblocks.io';
const group = 'apps.kubeblocks.io';
const version = 'v1alpha1';
const plural = 'backuppolicies';
const plural = 'clusters';

try {
const { k8sCustomObjects, namespace } = await getK8s({
kubeconfig: await authSession(req)
});

// get backup backupolicies.dataprotection.kubeblocks.io
await k8sCustomObjects.patchNamespacedCustomObject(
const result = await k8sCustomObjects.patchNamespacedCustomObject(
group,
version,
namespace,
plural,
`${dbName}-${DBBackupPolicyNameMap[dbType]}-backup-policy`,
dbName,
patch,
undefined,
undefined,
undefined,
{ headers: { 'Content-type': PatchUtils.PATCH_FORMAT_JSON_PATCH } }
);

jsonRes(res);
jsonRes(res, { data: result?.body });
} catch (err: any) {
jsonRes(res, {
code: 500,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ const BackupModal = ({
content: t('Manual Backup Tip'),
confirmText: 'Start Backup'
});

const { openConfirm: CloseAutoBackup, ConfirmChild: AutoBackupConfirmChild } = useConfirm({
title: 'Prompt',
content: t('Are you sure you want to turn off automatic backup'),
confirmText: 'Confirm'
});

const [refresh, setRefresh] = useState(false);
const [currentNav, setCurrentNav] = useState<`${NavEnum}`>(NavEnum.manual);
const {
Expand Down Expand Up @@ -162,12 +169,16 @@ const BackupModal = ({
})();

const patch = [
{ op: 'replace', path: '/spec/retention/ttl', value: `${data.saveTime}${data.saveType}` },
{ op: 'replace', path: '/spec/schedule/datafile/enable', value: data.start },
{
op: 'replace',
path: '/spec/schedule/datafile/cronExpression',
value: convertCronTime(cron, -8)
path: '/spec/backup',
value: {
enabled: data.start,
cronExpression: convertCronTime(cron, -8),
method: 'backupTool',
pitrEnabled: false,
retentionPeriod: `${data.saveTime}${data.saveType}`
}
}
];

Expand All @@ -193,6 +204,38 @@ const BackupModal = ({
}
});

const { mutate: onclickCloseAutoBackup } = useMutation({
mutationFn: async () => {
const patch = [
{
op: 'replace',
path: '/spec/backup/enabled',
value: false
}
];

return updateBackupPolicy({
dbName,
dbType,
patch
});
},
onSuccess() {
toast({
status: 'success',
title: t('Automatic backup is turned off')
});
refetchPolicy();
onClose();
},
onError(err) {
toast({
status: 'error',
title: t('Failed to turn off automatic backup')
});
}
});

return (
<>
<Modal isOpen onClose={onClose} isCentered>
Expand All @@ -209,6 +252,10 @@ const BackupModal = ({
variant={'deepLight'}
isChecked={getAutoValues('start')}
onChange={(e) => {
if (defaultVal.start) {
CloseAutoBackup(onclickCloseAutoBackup)();
return;
}
setAutoValue('start', e.target.checked);
setRefresh((state) => !state);
}}
Expand Down Expand Up @@ -389,6 +436,7 @@ const BackupModal = ({
</ModalContent>
</Modal>
<ConfirmChild />
<AutoBackupConfirmChild />
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { useConfirm } from '@/hooks/useConfirm';
import dayjs from 'dayjs';
import { BackupStatusEnum, backupTypeMap } from '@/constants/backup';
import { useTranslation } from 'next-i18next';
import { deleteBackup, getBackupPolicy } from '@/api/backup';
import { deleteBackup, getBackupPolicy, getBackupPolicyByCluster } from '@/api/backup';
import { getErrText } from '@/utils/tools';
import { getBackupList } from '@/api/backup';
import MyIcon from '@/components/Icon';
Expand Down Expand Up @@ -190,20 +190,10 @@ const BackupTable = ({ db }: { db?: DBDetailType }, ref: ForwardedRef<ComponentR
}));

const { data, refetch: refetchPolicy } = useQuery(['initpolicy', db.dbName, db.dbType], () =>
db.dbName && db.dbType
? getBackupPolicy({
dbName: db.dbName,
dbType: db.dbType
})
: {
start: false,
hour: '18',
minute: '00',
week: [],
type: 'day',
saveTime: 7,
saveType: 'day'
}
getBackupPolicyByCluster({
dbName: db.dbName,
dbType: db.dbType
})
);

return (
Expand Down
7 changes: 7 additions & 0 deletions frontend/providers/dbprovider/src/types/cluster.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ export interface KubeBlockClusterSpec {
};
}[];
}[];
backup: {
enabled: boolean;
cronExpression: string;
method: string;
pitrEnabled: boolean;
retentionPeriod: string;
};
}
export interface KubeBlockClusterStatus {
clusterDefGeneration: number;
Expand Down
31 changes: 31 additions & 0 deletions frontend/providers/dbprovider/src/utils/adapt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,37 @@ export const adaptDBDetail = (db: KbPgClusterType): DBDetailType => {
};
};

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'
};
return backup;
};

export const adaptDBForm = (db: DBDetailType): DBEditType => {
const keys: Record<keyof DBEditType, any> = {
dbType: 1,
Expand Down
4 changes: 2 additions & 2 deletions frontend/providers/template/src/pages/api/updateRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
const gitOperationPromise = !fs.existsSync(targetPath)
? execAsync(`git clone ${repoHttpUrl} ${targetPath} --depth=1`)
: execAsync(`cd ${targetPath} && git pull --depth=1`);
: execAsync(`cd ${targetPath} && git pull --depth=1 --rebase`);

await Promise.race([gitOperationPromise, timeoutPromise]);
} catch (error) {
console.log('git operation timed out');
console.log('git operation timed out: \n', error);
}

if (!fs.existsSync(targetPath)) {
Expand Down