From cc7d79b1dc0ee8fb7908bd18512943572a29d476 Mon Sep 17 00:00:00 2001 From: garethgeorge Date: Sat, 18 May 2024 19:28:19 -0700 Subject: [PATCH] feat: keep a rolling backup of the last 10 config versions --- internal/config/jsonstore.go | 50 +++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/internal/config/jsonstore.go b/internal/config/jsonstore.go index 4f622c98..2cbfe6ef 100644 --- a/internal/config/jsonstore.go +++ b/internal/config/jsonstore.go @@ -7,12 +7,17 @@ import ( "os" "path/filepath" "sync" + "time" v1 "github.com/garethgeorge/backrest/gen/go/v1" "github.com/natefinch/atomic" "google.golang.org/protobuf/encoding/protojson" ) +var ( + configKeepVersions = 10 +) + type JsonFileStore struct { Path string mu sync.Mutex @@ -49,17 +54,22 @@ func (f *JsonFileStore) Update(config *v1.Config) error { Multiline: true, }.Marshal(config) if err != nil { - return fmt.Errorf("failed to marshal config: %w", err) + return fmt.Errorf("marshal config: %w", err) } err = os.MkdirAll(filepath.Dir(f.Path), 0755) if err != nil { - return fmt.Errorf("failed to create config directory: %w", err) + return fmt.Errorf("create config directory: %w", err) + } + + // backup the old config file + if err := f.makeBackup(); err != nil { + return fmt.Errorf("backup config file: %w", err) } err = atomic.WriteFile(f.Path, bytes.NewReader(data)) if err != nil { - return fmt.Errorf("failed to write config file: %w", err) + return fmt.Errorf("write config file: %w", err) } // only the user running backrest should be able to read the config. @@ -69,3 +79,37 @@ func (f *JsonFileStore) Update(config *v1.Config) error { return nil } + +func (f *JsonFileStore) makeBackup() error { + curConfig, err := os.ReadFile(f.Path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return err + } + + // backup the current config file + backupName := fmt.Sprintf("%s.bak.%s", f.Path, time.Now().Format("2006-01-02-15-04-05")) + if err := atomic.WriteFile(backupName, bytes.NewBuffer(curConfig)); err != nil { + return err + } + if err := os.Chmod(backupName, 0600); err != nil { + return err + } + + // only keep the last 10 versions + files, err := filepath.Glob(f.Path + ".bak.*") + if err != nil { + return err + } + if len(files) > configKeepVersions { + for _, file := range files[:len(files)-configKeepVersions] { + if err := os.Remove(file); err != nil { + return err + } + } + } + + return nil +}