From 0b0766c70c95e0da12db5df6e1e3085ca610a351 Mon Sep 17 00:00:00 2001 From: Robert Cunius Jr Date: Tue, 3 May 2022 17:37:43 -0400 Subject: [PATCH] add more flexible snapshot match by regular expression --- backup/backup.go | 25 ++++++++++++++++++++++++- backup/backup_test.go | 13 +++++++++++++ cmd/send.go | 6 ++++++ files/jobinfo.go | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/backup/backup.go b/backup/backup.go index 29245d8..036cb64 100644 --- a/backup/backup.go +++ b/backup/backup.go @@ -31,6 +31,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "sort" "strings" "sync" @@ -54,6 +55,22 @@ var ( manifestmutex sync.Mutex ) +type snapshotFilter struct { + prefix string + regexpMatch *regexp.Regexp +} + +func newSnapshotFilter(prefix string, match string) *snapshotFilter { + filter := &snapshotFilter{ + prefix: prefix, + } + if match != "" { + filter.regexpMatch = regexp.MustCompile(match) + } + log.AppLogger.Debugf("Filtering snapshots with prefix = %s, regex matcher = %v", filter.prefix, filter.regexpMatch) + return filter +} + // ProcessSmartOptions will compute the snapshots to use // nolint:funlen,gocyclo // Difficult to break this up func ProcessSmartOptions(ctx context.Context, jobInfo *files.JobInfo) error { @@ -61,11 +78,12 @@ func ProcessSmartOptions(ctx context.Context, jobInfo *files.JobInfo) error { if err != nil { return err } + filter := newSnapshotFilter(jobInfo.SnapshotPrefix, jobInfo.SnapshotRegexp) // Base Snapshots cannot be a bookmark for i := range snapshots { log.AppLogger.Debugf("Considering snapshot %s", snapshots[i].Name) if !snapshots[i].Bookmark { - if jobInfo.SnapshotPrefix == "" || strings.HasPrefix(snapshots[i].Name, jobInfo.SnapshotPrefix) { + if includeSnapshot(&snapshots[i], filter) { log.AppLogger.Debugf("Matched snapshot: %s", snapshots[i].Name) jobInfo.BaseSnapshot = snapshots[i] break @@ -163,6 +181,11 @@ func ProcessSmartOptions(ctx context.Context, jobInfo *files.JobInfo) error { return nil } +func includeSnapshot(snapshot *files.SnapshotInfo, filter *snapshotFilter) bool { + return (filter.prefix == "" || strings.HasPrefix(snapshot.Name, filter.prefix)) && + (filter.regexpMatch == nil || filter.regexpMatch.MatchString(snapshot.Name)) +} + // Will list all backups found in the target destination func getBackupsForTarget(ctx context.Context, volume, target string, jobInfo *files.JobInfo) ([]*files.JobInfo, error) { // Prepare the backend client diff --git a/backup/backup_test.go b/backup/backup_test.go index 3ddc4c3..9cc738a 100644 --- a/backup/backup_test.go +++ b/backup/backup_test.go @@ -65,6 +65,19 @@ type errTestFunc func(error) bool func nilErrTest(e error) bool { return e == nil } +func TestIncludeSnapshot(t *testing.T) { + filter := newSnapshotFilter("", "^weekly.*") + + snapInfo := &files.SnapshotInfo{Name: "hourly123"} + if includeSnapshot(snapInfo, filter) { + t.Errorf("%s incorrectly included", snapInfo.Name) + } + snapInfo.Name = "weekly456" + if !includeSnapshot(snapInfo, filter) { + t.Errorf("%s incorrectly excluded", snapInfo.Name) + } +} + func TestRetryUploadChainer(t *testing.T) { _, goodVol, badVol, err := prepareTestVols() if err != nil { diff --git a/cmd/send.go b/cmd/send.go index bba5cdd..95b75bf 100644 --- a/cmd/send.go +++ b/cmd/send.go @@ -117,6 +117,12 @@ func init() { "", "Only consider snapshots starting with the given snapshot prefix", ) + sendCmd.Flags().StringVar( + &jobInfo.SnapshotRegexp, + "snapshotRegexp", + "", + "Only consider snapshots matching given regex", + ) sendCmd.Flags().DurationVar( &jobInfo.FullIfOlderThan, "fullIfOlderThan", diff --git a/files/jobinfo.go b/files/jobinfo.go index f424a0c..ab0e204 100644 --- a/files/jobinfo.go +++ b/files/jobinfo.go @@ -45,6 +45,7 @@ type JobInfo struct { BaseSnapshot SnapshotInfo IncrementalSnapshot SnapshotInfo SnapshotPrefix string + SnapshotRegexp string Compressor string CompressionLevel int Separator string