2022-09-29 08:15:59 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
2022-12-21 07:54:34 +00:00
|
|
|
"context"
|
2022-09-29 08:15:59 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2022-10-17 08:32:31 +00:00
|
|
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/global"
|
|
|
|
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
|
2022-09-29 08:15:59 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/robfig/cron/v3"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
|
|
|
|
var (
|
|
|
|
message []byte
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
record := cronjobRepo.StartRecords(cronjob.ID, "")
|
2022-10-28 10:46:14 +00:00
|
|
|
record.FromLocal = cronjob.KeepLocal
|
2022-09-29 08:15:59 +00:00
|
|
|
switch cronjob.Type {
|
|
|
|
case "shell":
|
|
|
|
cmd := exec.Command(cronjob.Script)
|
2022-10-28 10:46:14 +00:00
|
|
|
stdout, errExec := cmd.CombinedOutput()
|
|
|
|
if errExec != nil {
|
|
|
|
err = errors.New(string(stdout))
|
|
|
|
}
|
2022-12-14 11:34:42 +00:00
|
|
|
message = stdout
|
2022-09-29 08:15:59 +00:00
|
|
|
case "website":
|
2022-10-28 10:46:14 +00:00
|
|
|
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
2022-09-29 08:15:59 +00:00
|
|
|
case "database":
|
2022-10-28 10:46:14 +00:00
|
|
|
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
2022-09-29 08:15:59 +00:00
|
|
|
case "directory":
|
|
|
|
if len(cronjob.SourceDir) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2022-10-28 10:46:14 +00:00
|
|
|
record.File, err = u.HandleBackup(cronjob, record.StartTime)
|
2022-09-29 08:15:59 +00:00
|
|
|
case "curl":
|
|
|
|
if len(cronjob.URL) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
tr := &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
|
|
}
|
|
|
|
client := &http.Client{Timeout: 1 * time.Second, Transport: tr}
|
|
|
|
request, _ := http.NewRequest("GET", cronjob.URL, nil)
|
|
|
|
response, err := client.Do(request)
|
|
|
|
if err != nil {
|
|
|
|
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
message, _ = ioutil.ReadAll(response.Body)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
|
|
|
|
return
|
|
|
|
}
|
2022-12-14 11:34:42 +00:00
|
|
|
if len(message) != 0 {
|
|
|
|
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
|
|
|
if err != nil {
|
|
|
|
global.LOG.Errorf("save file %s failed, err: %v", record.Records, err)
|
|
|
|
}
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
|
|
|
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
|
|
|
}
|
|
|
|
|
2022-10-28 10:46:14 +00:00
|
|
|
func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
|
|
|
|
var (
|
|
|
|
baseDir string
|
|
|
|
backupDir string
|
|
|
|
fileName string
|
|
|
|
)
|
2022-09-29 08:15:59 +00:00
|
|
|
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
|
|
|
|
if err != nil {
|
2022-10-28 10:46:14 +00:00
|
|
|
return "", err
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
2022-12-28 10:35:53 +00:00
|
|
|
|
|
|
|
global.LOG.Infof("start to backup %s %s to %s", cronjob.Type, cronjob.Name, backup.Type)
|
2022-10-28 10:46:14 +00:00
|
|
|
if cronjob.KeepLocal || cronjob.Type != "LOCAL" {
|
2022-11-09 07:08:38 +00:00
|
|
|
localDir, err := loadLocalDir()
|
2022-10-28 10:46:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
2022-10-28 10:46:14 +00:00
|
|
|
baseDir = localDir
|
|
|
|
} else {
|
|
|
|
baseDir = constant.TmpDir
|
|
|
|
}
|
|
|
|
|
2022-11-30 07:47:11 +00:00
|
|
|
switch cronjob.Type {
|
|
|
|
case "database":
|
2022-12-21 04:21:27 +00:00
|
|
|
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
|
2022-11-29 09:39:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2022-11-30 07:47:11 +00:00
|
|
|
fileName = fmt.Sprintf("db_%s_%s.sql.gz", cronjob.DBName, startTime.Format("20060102150405"))
|
2022-11-18 08:14:23 +00:00
|
|
|
backupDir = fmt.Sprintf("database/mysql/%s/%s", app.Name, cronjob.DBName)
|
2022-11-30 07:47:11 +00:00
|
|
|
if err = backupMysql(backup.Type, baseDir, backupDir, app.Name, cronjob.DBName, fileName); err != nil {
|
2022-10-28 10:46:14 +00:00
|
|
|
return "", err
|
|
|
|
}
|
2022-11-30 07:47:11 +00:00
|
|
|
case "website":
|
|
|
|
fileName = fmt.Sprintf("website_%s_%s", cronjob.Website, startTime.Format("20060102150405"))
|
|
|
|
backupDir = fmt.Sprintf("website/%s", cronjob.Website)
|
|
|
|
if err := handleWebsiteBackup(backup.Type, baseDir, backupDir, cronjob.Website, fileName); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
fileName = fileName + ".tar.gz"
|
|
|
|
default:
|
2022-10-28 10:46:14 +00:00
|
|
|
fileName = fmt.Sprintf("%s.tar.gz", startTime.Format("20060102150405"))
|
|
|
|
backupDir = fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name)
|
2022-12-28 10:35:53 +00:00
|
|
|
global.LOG.Infof("handle tar %s to %s", backupDir, fileName)
|
2022-10-28 10:46:14 +00:00
|
|
|
if err := handleTar(cronjob.SourceDir, baseDir+"/"+backupDir, fileName, cronjob.ExclusionRules); err != nil {
|
|
|
|
return "", err
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-28 10:46:14 +00:00
|
|
|
|
|
|
|
if backup.Type == "LOCAL" {
|
|
|
|
u.HandleRmExpired(backup.Type, baseDir, backupDir, cronjob, nil)
|
|
|
|
return baseDir + "/" + backupDir + "/" + fileName, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
cloudFile := baseDir + "/" + backupDir + "/" + fileName
|
|
|
|
if !cronjob.KeepLocal {
|
|
|
|
cloudFile = backupDir + "/" + fileName
|
|
|
|
}
|
2022-09-29 08:15:59 +00:00
|
|
|
client, err := NewIBackupService().NewClient(&backup)
|
|
|
|
if err != nil {
|
2022-10-28 10:46:14 +00:00
|
|
|
return cloudFile, err
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
2022-10-28 10:46:14 +00:00
|
|
|
if _, err = client.Upload(baseDir+"/"+backupDir+"/"+fileName, backupDir+"/"+fileName); err != nil {
|
|
|
|
return cloudFile, err
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
2022-10-28 10:46:14 +00:00
|
|
|
u.HandleRmExpired(backup.Type, baseDir, backupDir, cronjob, client)
|
|
|
|
return cloudFile, nil
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *CronjobService) HandleDelete(id uint) error {
|
|
|
|
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id))
|
|
|
|
if cronjob.ID == 0 {
|
|
|
|
return errors.New("find cronjob in db failed")
|
|
|
|
}
|
|
|
|
commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name)
|
|
|
|
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
2022-12-28 10:35:53 +00:00
|
|
|
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
|
2022-09-29 08:15:59 +00:00
|
|
|
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(id)))
|
|
|
|
|
2023-01-29 08:38:34 +00:00
|
|
|
dir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronjob.Type, cronjob.Name)
|
2022-12-14 11:34:42 +00:00
|
|
|
if _, err := os.Stat(dir); err == nil {
|
|
|
|
if err := os.RemoveAll(dir); err != nil {
|
2023-01-29 08:38:34 +00:00
|
|
|
global.LOG.Errorf("rm file %s/task/%s failed, err: %v", constant.DataDir, commonDir, err)
|
2022-12-14 11:34:42 +00:00
|
|
|
}
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-28 10:46:14 +00:00
|
|
|
func (u *CronjobService) HandleRmExpired(backType, baseDir, backupDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
|
2022-12-28 10:35:53 +00:00
|
|
|
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
|
2022-09-29 08:15:59 +00:00
|
|
|
if backType != "LOCAL" {
|
2022-10-28 10:46:14 +00:00
|
|
|
currentObjs, err := backClient.ListObjects(backupDir + "/")
|
2022-09-29 08:15:59 +00:00
|
|
|
if err != nil {
|
2022-10-28 10:46:14 +00:00
|
|
|
global.LOG.Errorf("list bucket object %s failed, err: %v", backupDir, err)
|
2022-09-29 08:15:59 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
for i := 0; i < len(currentObjs)-int(cronjob.RetainCopies); i++ {
|
|
|
|
_, _ = backClient.Delete(currentObjs[i].(string))
|
|
|
|
}
|
2022-10-28 10:46:14 +00:00
|
|
|
if !cronjob.KeepLocal {
|
|
|
|
return
|
|
|
|
}
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
2022-10-28 10:46:14 +00:00
|
|
|
files, err := ioutil.ReadDir(baseDir + "/" + backupDir)
|
2022-09-29 08:15:59 +00:00
|
|
|
if err != nil {
|
2022-10-28 10:46:14 +00:00
|
|
|
global.LOG.Errorf("read dir %s failed, err: %v", baseDir+"/"+backupDir, err)
|
2022-09-29 08:15:59 +00:00
|
|
|
return
|
|
|
|
}
|
2022-11-08 11:03:38 +00:00
|
|
|
if len(files) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if cronjob.Type == "database" {
|
|
|
|
dbCopies := uint64(0)
|
|
|
|
for i := len(files) - 1; i >= 0; i-- {
|
|
|
|
if strings.HasPrefix(files[i].Name(), "db_") {
|
|
|
|
dbCopies++
|
|
|
|
if dbCopies > cronjob.RetainCopies {
|
|
|
|
_ = os.Remove(baseDir + "/" + backupDir + "/" + files[i].Name())
|
2022-12-21 07:54:34 +00:00
|
|
|
_ = backupRepo.DeleteRecord(context.Background(), backupRepo.WithByFileName(files[i].Name()))
|
2022-11-08 11:03:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for i := 0; i < len(files)-int(cronjob.RetainCopies); i++ {
|
|
|
|
_ = os.Remove(baseDir + "/" + backupDir + "/" + files[i].Name())
|
|
|
|
}
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
|
|
|
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)))
|
|
|
|
if len(records) > int(cronjob.RetainCopies) {
|
|
|
|
for i := int(cronjob.RetainCopies); i < len(records); i++ {
|
|
|
|
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(records[i].ID)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-28 10:46:14 +00:00
|
|
|
func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
2022-09-29 08:15:59 +00:00
|
|
|
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
|
|
|
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
2022-10-28 10:46:14 +00:00
|
|
|
return err
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
exStr := []string{}
|
|
|
|
exStr = append(exStr, "zcvf")
|
2022-10-28 10:46:14 +00:00
|
|
|
exStr = append(exStr, targetDir+"/"+name)
|
2022-09-29 08:15:59 +00:00
|
|
|
excludes := strings.Split(exclusionRules, ";")
|
|
|
|
for _, exclude := range excludes {
|
|
|
|
if len(exclude) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
exStr = append(exStr, "--exclude")
|
|
|
|
exStr = append(exStr, exclude)
|
|
|
|
}
|
|
|
|
if len(strings.Split(sourceDir, "/")) > 3 {
|
|
|
|
exStr = append(exStr, "-C")
|
|
|
|
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
|
|
|
|
aheadDir := strings.ReplaceAll(sourceDir, itemDir, "")
|
|
|
|
exStr = append(exStr, aheadDir)
|
|
|
|
exStr = append(exStr, itemDir)
|
|
|
|
} else {
|
|
|
|
exStr = append(exStr, sourceDir)
|
|
|
|
}
|
|
|
|
cmd := exec.Command("tar", exStr...)
|
2022-10-28 10:46:14 +00:00
|
|
|
stdout, err := cmd.CombinedOutput()
|
2023-01-09 14:55:10 +00:00
|
|
|
fmt.Println(string(stdout))
|
2022-10-28 10:46:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.New(string(stdout))
|
|
|
|
}
|
|
|
|
return nil
|
2022-09-29 08:15:59 +00:00
|
|
|
}
|
2022-11-04 11:02:15 +00:00
|
|
|
|
|
|
|
func handleUnTar(sourceFile, targetDir string) error {
|
|
|
|
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
|
|
|
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := exec.Command("tar", "zxvfC", sourceFile, targetDir)
|
|
|
|
stdout, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
return errors.New(string(stdout))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|