mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-11-07 02:07:40 +08:00
fix: Add error ignore functionality to cronjob backups (#9396)
This commit is contained in:
parent
fc81801c4c
commit
3fade48f8f
23 changed files with 102 additions and 8 deletions
|
|
@ -45,6 +45,7 @@ type CronjobOperate struct {
|
|||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
RetryTimes int `json:"retryTimes" validate:"number,min=0"`
|
||||
Timeout uint `json:"timeout" validate:"number,min=1"`
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
Secret string `json:"secret"`
|
||||
|
||||
AlertCount uint `json:"alertCount"`
|
||||
|
|
@ -105,6 +106,7 @@ type CronjobInfo struct {
|
|||
RetainCopies int `json:"retainCopies"`
|
||||
RetryTimes int `json:"retryTimes"`
|
||||
Timeout uint `json:"timeout"`
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
SnapshotRule SnapshotRule `json:"snapshotRule"`
|
||||
|
||||
SourceAccounts []string `json:"sourceAccounts"`
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ type Cronjob struct {
|
|||
DownloadAccountID uint `json:"downloadAccountID"`
|
||||
RetryTimes uint `json:"retryTimes"`
|
||||
Timeout uint `json:"timeout"`
|
||||
IgnoreErr bool `json:"ignoreErr"`
|
||||
RetainCopies uint64 `json:"retainCopies"`
|
||||
|
||||
Status string `json:"status"`
|
||||
|
|
|
|||
|
|
@ -439,6 +439,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
|
|||
upMap["retain_copies"] = req.RetainCopies
|
||||
upMap["retry_times"] = req.RetryTimes
|
||||
upMap["timeout"] = req.Timeout
|
||||
upMap["ignore_err"] = req.IgnoreErr
|
||||
upMap["secret"] = req.Secret
|
||||
err = cronjobRepo.Update(id, upMap)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
|||
return err
|
||||
}
|
||||
for _, app := range apps {
|
||||
retry := 0
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
|
|
@ -59,11 +60,22 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
|||
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
|
||||
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if err := doAppBackup(&app, task, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
|
|
@ -87,6 +99,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
|||
return err
|
||||
}
|
||||
for _, web := range webs {
|
||||
retry := 0
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(web.Alias, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
record.From = "cronjob"
|
||||
|
|
@ -99,12 +112,23 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
|||
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
|
||||
if err := doWebsiteBackup(&web, taskItem, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
|
|
@ -128,6 +152,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
|||
return err
|
||||
}
|
||||
for _, dbInfo := range dbs {
|
||||
retry := 0
|
||||
itemName := fmt.Sprintf("%s[%s] - %s", dbInfo.Database, dbInfo.DBType, dbInfo.Name)
|
||||
taskItem.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
|
||||
var record model.BackupRecord
|
||||
|
|
@ -142,17 +167,34 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
|||
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
|
||||
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
|
||||
if err := doMysqlBackup(dbInfo, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName); err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
} else {
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
|
||||
retry++
|
||||
return err
|
||||
}
|
||||
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
|
||||
return nil
|
||||
}
|
||||
record.FileDir = path.Dir(downloadPath)
|
||||
if err := backupRepo.CreateRecord(&record); err != nil {
|
||||
|
|
@ -280,6 +322,7 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
|
|||
AppData: itemData.AppData,
|
||||
PanelData: itemData.PanelData,
|
||||
BackupData: itemData.BackupData,
|
||||
WithDockerConf: true,
|
||||
WithMonitorData: true,
|
||||
WithLoginLog: true,
|
||||
WithOperationLog: true,
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ SystemLog: 'System Log'
|
|||
CutWebsiteLog: 'Rotate Website Log'
|
||||
FileOrDir: 'Directory / File'
|
||||
UploadFile: 'Uploading backup file {{ .file }} to {{ .backup }}'
|
||||
IgnoreBackupErr: 'Backup failed, error: {{ .detail }}, ignoring this error...'
|
||||
IgnoreUploadErr: 'Upload failed, error: {{ .detail }}, ignoring this error...'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: 'The current user does not exist, please modify and try again!'
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ SystemLog: 'システムログ'
|
|||
CutWebsiteLog: 'ウェブサイトログのローテーション'
|
||||
FileOrDir: 'ディレクトリ / ファイル'
|
||||
UploadFile: 'バックアップファイル {{ .file }} を {{ .backup }} にアップロード中'
|
||||
IgnoreBackupErr: 'バックアップ失敗、エラー:{{ .detail }}、このエラーを無視します...'
|
||||
IgnoreUploadErr: 'アップロード失敗、エラー:{{ .detail }}、このエラーを無視します...'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: '現在のユーザーは存在しません。変更してもう一度お試しください。'
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ SystemLog: '시스템 로그'
|
|||
CutWebsiteLog: '웹사이트 로그 회전'
|
||||
FileOrDir: '디렉터리 / 파일'
|
||||
UploadFile: '백업 파일 {{ .file }} 을(를) {{ .backup }}(으)로 업로드 중'
|
||||
IgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
IgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
|
||||
#도구상자
|
||||
ErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요!'
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ SystemLog: 'Log Sistem'
|
|||
CutWebsiteLog: 'Putar Log Laman Web'
|
||||
FileOrDir: 'Direktori / Fail'
|
||||
UploadFile: 'Muat naik fail sandaran {{ .file }} ke {{ .backup }}'
|
||||
IgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
IgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'
|
||||
|
||||
#kotak alat
|
||||
ErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi!'
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ SystemLog: 'Log do Sistema'
|
|||
CutWebsiteLog: 'Rotacionar Log do Website'
|
||||
FileOrDir: 'Diretório / Arquivo'
|
||||
UploadFile: 'Enviando arquivo de backup {{ .file }} para {{ .backup }}'
|
||||
IgnoreBackupErr: 'Backup falhou, erro: {{ .detail }}, ignorando este erro...'
|
||||
IgnoreUploadErr: 'Upload falhou, erro: {{ .detail }}, ignorando este erro...'
|
||||
|
||||
#caixa de ferramentas
|
||||
ErrNotExistUser: 'O usuário atual não existe, modifique e tente novamente!'
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ SystemLog: 'Системный лог'
|
|||
CutWebsiteLog: 'Ротация логов сайта'
|
||||
FileOrDir: 'Каталог / Файл'
|
||||
UploadFile: 'Загрузка файла резервной копии {{ .file }} в {{ .backup }}'
|
||||
IgnoreBackupErr: 'Ошибка резервного копирования: {{ .detail }}, игнорируем эту ошибку...'
|
||||
IgnoreUploadErr: 'Ошибка загрузки: {{ .detail }}, игнорируем эту ошибку...'
|
||||
|
||||
#ящик для инструментов
|
||||
ErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку!'
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ SystemLog: '系統日誌'
|
|||
CutWebsiteLog: '切割網站日誌'
|
||||
FileOrDir: '目錄 / 檔案'
|
||||
UploadFile: '上傳備份文件 {{ .file }} 到 {{ .backup }}'
|
||||
IgnoreBackupErr: '備份失敗,錯誤:{{ .detail }},忽略本次錯誤...'
|
||||
IgnoreUploadErr: '上傳失敗,錯誤:{{ .detail }},忽略本次錯誤...'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: '目前使用者不存在,請修改後重試!'
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ SystemLog: "系统日志"
|
|||
CutWebsiteLog: "切割网站日志"
|
||||
FileOrDir: "目录 / 文件"
|
||||
UploadFile: "上传备份文件 {{ .file }} 到 {{ .backup }}"
|
||||
IgnoreBackupErr: "备份失败,错误:{{ .detail }},忽略本次错误..."
|
||||
IgnoreUploadErr: "上传失败,错误:{{ .detail }},忽略本次错误..."
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ var UpdateRuntime = &gormigrate.Migration{
|
|||
}
|
||||
|
||||
var AddSnapshotRule = &gormigrate.Migration{
|
||||
ID: "20250627-add-snapshot-rule",
|
||||
ID: "20250703-add-snapshot-rule",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.Cronjob{},
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export namespace Cronjob {
|
|||
sourceAccountItems: Array<number>;
|
||||
|
||||
retainCopies: number;
|
||||
ignoreErr: boolean;
|
||||
retryTimes: number;
|
||||
timeout: number;
|
||||
timeoutItem: number;
|
||||
|
|
@ -91,6 +92,7 @@ export namespace Cronjob {
|
|||
retainCopies: number;
|
||||
retryTimes: number;
|
||||
timeout: number;
|
||||
ignoreErr: boolean;
|
||||
secret: string;
|
||||
|
||||
alertCount: number;
|
||||
|
|
|
|||
|
|
@ -1035,6 +1035,8 @@ const message = {
|
|||
retainCopies: 'Retain records',
|
||||
retryTimes: 'Retry Attempts',
|
||||
timeout: 'Timeout',
|
||||
ignoreErr: 'Ignore errors',
|
||||
ignoreErrHelper: 'Ignore errors during backup to ensure all backup tasks complete',
|
||||
retryTimesHelper: '0 means no retry after failure',
|
||||
retainCopiesHelper: 'Number of copies to retain for execution records and logs',
|
||||
retainCopiesHelper1: 'Number of copies to retain for backup files',
|
||||
|
|
|
|||
|
|
@ -1004,6 +1004,8 @@ const message = {
|
|||
retainCopies: '記録を保持します',
|
||||
retryTimes: 'リトライ回数',
|
||||
timeout: 'タイムアウト',
|
||||
ignoreErr: 'エラーを無視',
|
||||
ignoreErrHelper: 'バックアップ中のエラーを無視し、全てのバックアップタスクを確実に実行します',
|
||||
retryTimesHelper: '0は失敗後リトライしないことを意味します',
|
||||
retainCopiesHelper: '実行記録とログのために保持するコピーの数',
|
||||
retainCopiesHelper1: 'バックアップファイル用に保持するコピーの数',
|
||||
|
|
|
|||
|
|
@ -991,6 +991,8 @@ const message = {
|
|||
retainCopies: '기록 보관',
|
||||
retryTimes: '재시도 횟수',
|
||||
timeout: '타임아웃',
|
||||
ignoreErr: '오류 무시',
|
||||
ignoreErrHelper: '백업 과정에서 발생하는 오류를 무시하여 모든 백업 작업이 실행되도록 합니다',
|
||||
retryTimesHelper: '0은 실패 후 재시도 안 함을 의미합니다',
|
||||
retainCopiesHelper: '실행 기록과 로그에 대해 보관할 복사본 수',
|
||||
retainCopiesHelper1: '백업 파일에 대해 보관할 복사본 수',
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,8 @@ const message = {
|
|||
retainCopies: 'Simpan salinan',
|
||||
retryTimes: 'Bilangan Cubaan Semula',
|
||||
timeout: 'Masa Tamat',
|
||||
ignoreErr: 'Abaikan ralat',
|
||||
ignoreErrHelper: 'Abaikan ralat semasa sandaran untuk memastikan semua tugas sandaran dilaksanakan',
|
||||
retryTimesHelper: '0 bermaksud tiada cubaan semula selepas gagal',
|
||||
retainCopiesHelper: 'Bilangan salinan untuk menyimpan rekod pelaksanaan dan log',
|
||||
retainCopiesHelper1: 'Bilangan salinan untuk menyimpan fail sandaran',
|
||||
|
|
|
|||
|
|
@ -1021,6 +1021,8 @@ const message = {
|
|||
retainCopies: 'Manter cópias',
|
||||
retryTimes: 'Tentativas de Repetição',
|
||||
timeout: 'Tempo Limite',
|
||||
ignoreErr: 'Ignorar erros',
|
||||
ignoreErrHelper: 'Ignorar erros durante o backup para garantir a execução de todas as tarefas de backup',
|
||||
retryTimesHelper: '0 significa não repetir após falha',
|
||||
retainCopiesHelper: 'Número de cópias a serem mantidas para registros de execução e logs',
|
||||
retainCopiesHelper1: 'Número de cópias a serem mantidas para arquivos de backup',
|
||||
|
|
|
|||
|
|
@ -1017,6 +1017,9 @@ const message = {
|
|||
retainCopies: 'Сохранять записи',
|
||||
retryTimes: 'Количество повторов',
|
||||
timeout: 'Таймаут',
|
||||
ignoreErr: 'Игнорировать ошибки',
|
||||
ignoreErrHelper:
|
||||
'Игнорировать ошибки во время резервного копирования для выполнения всех задач резервного копирования',
|
||||
retryTimesHelper: '0 означает отсутствие повторов после сбоя',
|
||||
retainCopiesHelper: 'Количество копий для сохранения записей выполнения и логов',
|
||||
retainCopiesHelper1: 'Количество копий для сохранения файлов резервных копий',
|
||||
|
|
|
|||
|
|
@ -983,6 +983,8 @@ const message = {
|
|||
retainCopies: '保留份數',
|
||||
retryTimes: '失敗重試次數',
|
||||
timeout: '逾時時間',
|
||||
ignoreErr: '忽略錯誤',
|
||||
ignoreErrHelper: '忽略備份過程中出現的錯誤,保證所有備份任務執行',
|
||||
retryTimesHelper: '為0表示失敗後不重試',
|
||||
retainCopiesHelper: '執行記錄及日誌保留份数',
|
||||
retainCopiesHelper1: '備份文件保留份数',
|
||||
|
|
|
|||
|
|
@ -982,6 +982,8 @@ const message = {
|
|||
retainCopies: '保留份数',
|
||||
retryTimes: '失败重试次数',
|
||||
timeout: '超时时间',
|
||||
ignoreErr: '忽略错误',
|
||||
ignoreErrHelper: '忽略备份过程中出现的错误,保证所有备份任务执行',
|
||||
retryTimesHelper: '为 0 表示失败后不重试',
|
||||
retainCopiesHelper: '执行记录及日志保留份数',
|
||||
retainCopiesHelper1: '备份文件保留份数',
|
||||
|
|
|
|||
|
|
@ -644,6 +644,12 @@
|
|||
</div>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<LayoutCol :span="20" v-if="hasIgnore()">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.ignoreErr" :label="$t('cronjob.ignoreErr')" />
|
||||
<span class="input-help">{{ $t('cronjob.ignoreErrHelper') }}</span>
|
||||
</el-form-item>
|
||||
</LayoutCol>
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('cronjob.timeout')" prop="timeoutItem">
|
||||
<el-input type="number" class="selectClass" v-model.number="form.timeoutItem">
|
||||
|
|
@ -776,6 +782,7 @@ const form = reactive<Cronjob.CronjobInfo>({
|
|||
dbNameList: [],
|
||||
|
||||
retainCopies: 7,
|
||||
ignoreErr: false,
|
||||
retryTimes: 3,
|
||||
timeout: 3600,
|
||||
timeoutItem: 3600,
|
||||
|
|
@ -863,6 +870,7 @@ const search = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
form.ignoreErr = res.data.ignoreErr;
|
||||
form.retainCopies = res.data.retainCopies;
|
||||
form.retryTimes = res.data.retryTimes;
|
||||
form.timeout = res.data.timeout;
|
||||
|
|
@ -1287,6 +1295,10 @@ function hasExclusionRules() {
|
|||
);
|
||||
}
|
||||
|
||||
function hasIgnore() {
|
||||
return form.type === 'app' || form.type === 'website' || form.type === 'database';
|
||||
}
|
||||
|
||||
function hasScript() {
|
||||
return form.type === 'shell';
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue