-
-
Notifications
You must be signed in to change notification settings - Fork 372
/
backup.go
98 lines (89 loc) · 2.82 KB
/
backup.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package migrator
import (
"compress/gzip"
"context"
"fmt"
"os"
"sort"
"strings"
"time"
log "github.com/go-pkgz/lgr"
)
// AutoBackup struct handles daily backups params for siteID
type AutoBackup struct {
Exporter Exporter
BackupLocation string
SiteID string
KeepMax int
Duration time.Duration
}
// Do runs daily export to local files, keeps up to keepMax backups for given siteID
func (ab AutoBackup) Do(ctx context.Context) {
log.Printf("[INFO] activate auto-backup for %s under %s, duration %s", ab.SiteID, ab.BackupLocation, ab.Duration)
tick := time.NewTicker(ab.Duration)
defer tick.Stop()
log.Printf("[DEBUG] first backup for %s at %s", ab.SiteID, time.Now().Add(ab.Duration))
for {
select {
case <-tick.C:
if _, err := ab.makeBackup(); err != nil {
log.Printf("[WARN] auto-backup for %s failed, %s", ab.SiteID, err)
continue
}
ab.removeOldBackupFiles()
log.Printf("[DEBUG] next backup for %s at %s", ab.SiteID, time.Now().Add(ab.Duration))
case <-ctx.Done():
log.Printf("[WARN] terminated autobackup for %s", ab.SiteID)
return
}
}
}
func (ab AutoBackup) makeBackup() (string, error) {
log.Printf("[DEBUG] make backup for %s", ab.SiteID)
backupFile := fmt.Sprintf("%s/backup-%s-%s.gz", ab.BackupLocation, ab.SiteID, time.Now().Format("20060102"))
fh, err := os.Create(backupFile) //nolint:gosec // harmless
if err != nil {
return "", fmt.Errorf("can't create backup file %s: %w", backupFile, err)
}
gz := gzip.NewWriter(fh)
if _, err = ab.Exporter.Export(gz, ab.SiteID); err != nil {
return "", fmt.Errorf("export failed for %s: %w", ab.SiteID, err)
}
if err = gz.Close(); err != nil {
return "", fmt.Errorf("can't close gz for %s: %w", backupFile, err)
}
if err = fh.Close(); err != nil {
return "", fmt.Errorf("can't close file handler for %s: %w", backupFile, err)
}
log.Printf("[DEBUG] created backup file %s", backupFile)
return backupFile, nil
}
func (ab AutoBackup) removeOldBackupFiles() {
files, err := os.ReadDir(ab.BackupLocation)
if err != nil {
log.Printf("[WARN] can't read files in backup directory %s, %s", ab.BackupLocation, err)
return
}
backFiles := []os.FileInfo{}
for _, file := range files {
info, e := file.Info()
if e != nil {
log.Printf("[WARN] can't read info for directory %s, %s", file.Name(), e)
return
}
if strings.HasPrefix(file.Name(), "backup-"+ab.SiteID) {
backFiles = append(backFiles, info)
}
}
sort.Slice(backFiles, func(i, j int) bool { return backFiles[i].Name() < backFiles[j].Name() })
if len(backFiles) > ab.KeepMax {
for i := 0; i < len(backFiles)-ab.KeepMax; i++ {
fpath := ab.BackupLocation + "/" + backFiles[i].Name()
if e := os.Remove(fpath); e != nil {
log.Printf("[WARN] can't delete %s, %s", fpath, err)
continue
}
log.Printf("[DEBUG] removed %s", fpath)
}
}
}