mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-04 13:52:26 +08:00
chore: update backup
This commit is contained in:
@@ -35,11 +35,11 @@ func init() {
|
||||
logrus.Infof("Run mode:%s", mode)
|
||||
|
||||
// 加载配置文件
|
||||
dir, err := config.WalkWorkDir()
|
||||
etc, err := config.WorkEtcPath()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
path := filepath.Join(dir, "etc", "app.yml")
|
||||
path := filepath.Join(etc, "app.yml")
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
|
||||
7
cmd/backup/handler/timer/db/db.go
Normal file
7
cmd/backup/handler/timer/db/db.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package db
|
||||
|
||||
// Storage 备份恢复器
|
||||
type Storage interface {
|
||||
Backup(name string) (string, error)
|
||||
Restore(path string) error
|
||||
}
|
||||
72
cmd/backup/handler/timer/db/mgodb.go
Normal file
72
cmd/backup/handler/timer/db/mgodb.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/cmd/backup/config"
|
||||
"github.com/eiblog/eiblog/pkg/connector/db"
|
||||
)
|
||||
|
||||
// MongoStorage 备份恢复器
|
||||
type MongoStorage struct{}
|
||||
|
||||
// Backup 备份
|
||||
func (r MongoStorage) Backup(name string) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
// dump
|
||||
u, err := url.Parse(config.Conf.Database.Source)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
arg := fmt.Sprintf("mongodump -h %s -d eiblog -o /tmp", u.Host)
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// tar
|
||||
arg = fmt.Sprintf("tar czf /tmp/%s -C /tmp eiblog", name)
|
||||
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "/tmp/" + name, nil
|
||||
}
|
||||
|
||||
// Restore 恢复
|
||||
func (r MongoStorage) Restore(path string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
// drop database
|
||||
mdb, err := db.NewMDB(config.Conf.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mdb.Drop(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// unarchive
|
||||
arg := fmt.Sprintf("tar xzf %s -C /tmp", path)
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// restore
|
||||
u, err := url.Parse(config.Conf.Database.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arg = fmt.Sprintf("mongorestore -h %s -d eiblog /tmp/eiblog", u.Host)
|
||||
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
return cmd.Run()
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package qiniu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/cmd/backup/config"
|
||||
"github.com/eiblog/eiblog/cmd/backup/handler/internal"
|
||||
"github.com/eiblog/eiblog/pkg/connector/db"
|
||||
"github.com/eiblog/eiblog/pkg/third/qiniu"
|
||||
)
|
||||
|
||||
// BackupRestorer qiniu backup restorer
|
||||
type BackupRestorer struct{}
|
||||
|
||||
// Backup implements timer.BackupRestorer
|
||||
func (s BackupRestorer) Backup(now time.Time) error {
|
||||
switch config.Conf.Database.Driver {
|
||||
case "mongodb":
|
||||
return backupFromMongoDB(now)
|
||||
default:
|
||||
return errors.New("unsupported source backup to qiniu: " +
|
||||
config.Conf.Database.Driver)
|
||||
}
|
||||
}
|
||||
|
||||
// Restore implements timer.BackupRestorer
|
||||
func (s BackupRestorer) Restore() error {
|
||||
switch config.Conf.Database.Driver {
|
||||
case "mongodb":
|
||||
return restoreToMongoDB()
|
||||
default:
|
||||
return errors.New("unsupported source restore from qiniu: " +
|
||||
config.Conf.Database.Driver)
|
||||
}
|
||||
}
|
||||
|
||||
func backupFromMongoDB(now time.Time) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
|
||||
defer cancel()
|
||||
|
||||
// dump
|
||||
u, err := url.Parse(config.Conf.Database.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arg := fmt.Sprintf("mongodump -h %s -d eiblog -o /tmp", u.Host)
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// tar
|
||||
name := fmt.Sprintf("eiblog-%s.tar.gz", now.Format("2006-01-02"))
|
||||
arg = fmt.Sprintf("tar czf /tmp/%s -C /tmp eiblog", name)
|
||||
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// upload file
|
||||
f, err := os.Open("/tmp/" + name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uploadParams := qiniu.UploadParams{
|
||||
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
|
||||
Size: s.Size(),
|
||||
Data: f,
|
||||
NoCompletePath: true,
|
||||
}
|
||||
_, err = internal.QiniuClient.Upload(uploadParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// after days delete
|
||||
deleteParams := qiniu.DeleteParams{
|
||||
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
|
||||
Days: config.Conf.Validity,
|
||||
NoCompletePath: true,
|
||||
}
|
||||
return internal.QiniuClient.Delete(deleteParams)
|
||||
}
|
||||
|
||||
func restoreToMongoDB() error {
|
||||
// backup file
|
||||
params := qiniu.ContentParams{
|
||||
Prefix: "blog/",
|
||||
}
|
||||
raw, err := internal.QiniuClient.Content(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile("/tmp/eiblog.tar.gz", os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = f.Write(raw)
|
||||
defer f.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
|
||||
defer cancel()
|
||||
// drop database
|
||||
mdb, err := db.NewMDB(config.Conf.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mdb.Drop(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// unarchive
|
||||
arg := "tar xzf /tmp/eiblog.tar.gz -C /tmp"
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// restore
|
||||
u, err := url.Parse(config.Conf.Database.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arg = fmt.Sprintf("mongorestore -h %s -d eiblog /tmp/eiblog", u.Host)
|
||||
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
|
||||
return cmd.Run()
|
||||
}
|
||||
@@ -2,49 +2,71 @@ package timer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/cmd/backup/config"
|
||||
"github.com/eiblog/eiblog/cmd/backup/handler/timer/qiniu"
|
||||
"github.com/eiblog/eiblog/cmd/backup/handler/timer/db"
|
||||
"github.com/eiblog/eiblog/cmd/backup/handler/timer/to"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// BackupRestorer 备份恢复器
|
||||
type BackupRestorer interface {
|
||||
Backup(now time.Time) error
|
||||
Restore() error
|
||||
}
|
||||
|
||||
// Start to backup with ticker
|
||||
func Start(restore bool) (err error) {
|
||||
var storage BackupRestorer
|
||||
var (
|
||||
storage db.Storage
|
||||
backupTo to.BackupRestorer
|
||||
)
|
||||
|
||||
// backup instance
|
||||
// backup from
|
||||
switch config.Conf.Database.Driver {
|
||||
case "mongodb":
|
||||
storage = db.MongoStorage{}
|
||||
|
||||
default:
|
||||
return errors.New("timer: unknown backup from driver: " +
|
||||
config.Conf.Database.Driver)
|
||||
}
|
||||
|
||||
// backup to
|
||||
switch config.Conf.BackupTo {
|
||||
case "qiniu":
|
||||
storage = qiniu.BackupRestorer{}
|
||||
backupTo = to.QiniuBackupRestorer{}
|
||||
|
||||
default:
|
||||
return errors.New("timer: unknown backup to driver: " +
|
||||
config.Conf.BackupTo)
|
||||
}
|
||||
|
||||
// restore
|
||||
if restore {
|
||||
err = storage.Restore()
|
||||
path, err := backupTo.Download()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = storage.Restore(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Info("timer: Restore success")
|
||||
}
|
||||
// parse duration
|
||||
|
||||
// backup
|
||||
interval, err := ParseDuration(config.Conf.Interval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t := time.NewTicker(interval)
|
||||
for now := range t.C {
|
||||
err = storage.Backup(now)
|
||||
name := fmt.Sprintf("eiblog-%s.tar.gz", now.Format("2006-01-02"))
|
||||
path, err := storage.Backup(name)
|
||||
if err != nil {
|
||||
logrus.Error("timer: Start.Backup: ", now.Format(time.RFC3339), err)
|
||||
continue
|
||||
}
|
||||
err = backupTo.Upload(path)
|
||||
if err != nil {
|
||||
logrus.Error("timer: Start.Backup: ", now.Format(time.RFC3339), err)
|
||||
}
|
||||
|
||||
66
cmd/backup/handler/timer/to/qiniu.go
Normal file
66
cmd/backup/handler/timer/to/qiniu.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package to
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/eiblog/eiblog/cmd/backup/config"
|
||||
"github.com/eiblog/eiblog/cmd/backup/handler/internal"
|
||||
"github.com/eiblog/eiblog/pkg/third/qiniu"
|
||||
)
|
||||
|
||||
// QiniuBackupRestorer qiniu backup restorer
|
||||
type QiniuBackupRestorer struct{}
|
||||
|
||||
// Upload implements timer.BackupRestorer
|
||||
func (s QiniuBackupRestorer) Upload(path string) error {
|
||||
name := filepath.Base(path)
|
||||
|
||||
// upload file
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uploadParams := qiniu.UploadParams{
|
||||
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
|
||||
Size: fi.Size(),
|
||||
Data: f,
|
||||
NoCompletePath: true,
|
||||
}
|
||||
_, err = internal.QiniuClient.Upload(uploadParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// after days delete
|
||||
deleteParams := qiniu.DeleteParams{
|
||||
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
|
||||
Days: config.Conf.Validity,
|
||||
NoCompletePath: true,
|
||||
}
|
||||
return internal.QiniuClient.Delete(deleteParams)
|
||||
}
|
||||
|
||||
// Download implements timer.BackupRestorer
|
||||
func (s QiniuBackupRestorer) Download() (string, error) {
|
||||
// backup file
|
||||
params := qiniu.ContentParams{
|
||||
Prefix: "blog/",
|
||||
}
|
||||
raw, err := internal.QiniuClient.Content(params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
path := filepath.Join("/tmp", "eiblog.tar.gz")
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, _ = f.Write(raw)
|
||||
defer f.Close()
|
||||
return path, nil
|
||||
}
|
||||
7
cmd/backup/handler/timer/to/to.go
Normal file
7
cmd/backup/handler/timer/to/to.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package to
|
||||
|
||||
// BackupRestorer 备份存储接口
|
||||
type BackupRestorer interface {
|
||||
Upload(path string) error
|
||||
Download() (path string, err error)
|
||||
}
|
||||
Reference in New Issue
Block a user