重构代码
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package rotatemanage
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
MaxBackups int
|
||||
MaxAge time.Duration
|
||||
Compress bool
|
||||
Pattern string
|
||||
}
|
||||
|
||||
type backupFileMeta struct {
|
||||
path string
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func Apply(archivePath string, currentPath string, options Options) error {
|
||||
if archivePath == "" || currentPath == "" {
|
||||
return nil
|
||||
}
|
||||
if options.Compress {
|
||||
if _, err := gzipBackupFile(archivePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
backups, err := listManagedBackups(currentPath, options.Pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cleanupManagedBackups(backups, options)
|
||||
}
|
||||
|
||||
func listManagedBackups(currentPath string, pattern string) ([]backupFileMeta, error) {
|
||||
dir := filepath.Dir(currentPath)
|
||||
base := filepath.Base(currentPath)
|
||||
stem := strings.TrimSuffix(base, filepath.Ext(base))
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backups := make([]backupFileMeta, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if name == base {
|
||||
continue
|
||||
}
|
||||
matched, err := IsManagedBackupName(name, base, stem, pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backups = append(backups, backupFileMeta{
|
||||
path: filepath.Join(dir, name),
|
||||
modTime: info.ModTime(),
|
||||
})
|
||||
}
|
||||
return backups, nil
|
||||
}
|
||||
|
||||
func IsManagedBackupName(name string, base string, stem string, pattern string) (bool, error) {
|
||||
if pattern != "" {
|
||||
return filepath.Match(pattern, name)
|
||||
}
|
||||
prefixes := []string{
|
||||
base + ".",
|
||||
base + "_",
|
||||
base + "-",
|
||||
}
|
||||
if stem != "" && stem != base {
|
||||
prefixes = append(prefixes,
|
||||
stem+".",
|
||||
stem+"_",
|
||||
stem+"-",
|
||||
)
|
||||
}
|
||||
for _, prefix := range prefixes {
|
||||
if !strings.HasPrefix(name, prefix) {
|
||||
continue
|
||||
}
|
||||
if isLikelyManagedBackupSuffix(strings.TrimPrefix(name, prefix)) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isLikelyManagedBackupSuffix(suffix string) bool {
|
||||
suffix = strings.TrimSpace(strings.ToLower(suffix))
|
||||
if suffix == "" {
|
||||
return false
|
||||
}
|
||||
suffix = strings.TrimSuffix(suffix, ".gz")
|
||||
suffix = strings.TrimSuffix(suffix, ".zip")
|
||||
if suffix == "" {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(suffix, "bak") {
|
||||
return true
|
||||
}
|
||||
for _, ch := range suffix {
|
||||
if ch >= '0' && ch <= '9' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cleanupManagedBackups(backups []backupFileMeta, options Options) error {
|
||||
var firstErr error
|
||||
now := time.Now()
|
||||
kept := make([]backupFileMeta, 0, len(backups))
|
||||
for _, item := range backups {
|
||||
if options.MaxAge > 0 && now.Sub(item.modTime) > options.MaxAge {
|
||||
if err := os.Remove(item.path); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
kept = append(kept, item)
|
||||
}
|
||||
if options.MaxBackups > 0 && len(kept) > options.MaxBackups {
|
||||
sort.Slice(kept, func(i, j int) bool {
|
||||
return kept[i].modTime.After(kept[j].modTime)
|
||||
})
|
||||
for _, item := range kept[options.MaxBackups:] {
|
||||
if err := os.Remove(item.path); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
return firstErr
|
||||
}
|
||||
|
||||
func gzipBackupFile(path string) (string, error) {
|
||||
if strings.HasSuffix(path, ".gz") {
|
||||
return path, nil
|
||||
}
|
||||
source, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
destination := path + ".gz"
|
||||
temp := destination + ".tmp"
|
||||
target, err := os.Create(temp)
|
||||
if err != nil {
|
||||
_ = source.Close()
|
||||
return "", err
|
||||
}
|
||||
|
||||
gzWriter := gzip.NewWriter(target)
|
||||
if _, err = io.Copy(gzWriter, source); err != nil {
|
||||
_ = source.Close()
|
||||
_ = gzWriter.Close()
|
||||
_ = target.Close()
|
||||
_ = os.Remove(temp)
|
||||
return "", err
|
||||
}
|
||||
if err = gzWriter.Close(); err != nil {
|
||||
_ = source.Close()
|
||||
_ = target.Close()
|
||||
_ = os.Remove(temp)
|
||||
return "", err
|
||||
}
|
||||
if err = target.Close(); err != nil {
|
||||
_ = source.Close()
|
||||
_ = os.Remove(temp)
|
||||
return "", err
|
||||
}
|
||||
if err = source.Close(); err != nil {
|
||||
_ = os.Remove(temp)
|
||||
return "", err
|
||||
}
|
||||
if err = os.Rename(temp, destination); err != nil {
|
||||
_ = os.Remove(temp)
|
||||
return "", err
|
||||
}
|
||||
if err = os.Remove(path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return destination, nil
|
||||
}
|
||||
Reference in New Issue
Block a user