mirror of
https://github.com/restic/restic.git
synced 2025-03-16 00:00:05 +01:00
Add snapshot filtering --older-than and --newer-than
This would solve issue #5136
This commit is contained in:
parent
da47967316
commit
146587416f
6 changed files with 104 additions and 2 deletions
cmd/restic
internal/restic
|
@ -17,6 +17,8 @@ func initMultiSnapshotFilter(flags *pflag.FlagSet, filt *restic.SnapshotFilter,
|
|||
}
|
||||
flags.StringArrayVarP(&filt.Hosts, "host", hostShorthand, nil, "only consider snapshots for this `host` (can be specified multiple times) (default: $RESTIC_HOST)")
|
||||
flags.Var(&filt.Tags, "tag", "only consider snapshots including `tag[,tag,...]` (can be specified multiple times)")
|
||||
flags.Var(&filt.OlderThan, "older-than", "only consider snapshots made more than `duration` time ago")
|
||||
flags.Var(&filt.NewerThan, "newer-than", "only consider snapshots made less than `duration` time ago")
|
||||
flags.StringArrayVar(&filt.Paths, "path", nil, "only consider snapshots including this (absolute) `path` (can be specified multiple times, snapshots must include all specified paths)")
|
||||
|
||||
// set default based on env if set
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/restic/restic/internal/errors"
|
||||
|
@ -126,6 +127,18 @@ func (d *Duration) Set(s string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Subtract duration from t to return past point in time. If t is not given, use time.Now().
|
||||
func (d Duration) PastTime(t ...time.Time) time.Time {
|
||||
var reftime time.Time
|
||||
if t == nil {
|
||||
reftime = time.Now()
|
||||
} else {
|
||||
reftime = t[0]
|
||||
}
|
||||
|
||||
return reftime.AddDate(-d.Years, -d.Months, -d.Days).Add(-time.Duration(d.Hours) * time.Hour)
|
||||
}
|
||||
|
||||
// Type returns the type of Duration, usable within github.com/spf13/pflag and
|
||||
// in help texts.
|
||||
func (d Duration) Type() string {
|
||||
|
|
|
@ -2,6 +2,7 @@ package restic
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
@ -104,3 +105,18 @@ func TestParseDuration(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPastTime(t *testing.T) {
|
||||
t.Run("", func(t *testing.T) {
|
||||
d, err := ParseDuration("1y2m3d4h")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reftime, err := time.Parse(time.DateTime, "1999-12-30 15:16:17")
|
||||
expected := "1998-10-27 11:16:17"
|
||||
result := d.PastTime(reftime).Format(time.DateTime)
|
||||
if result != expected {
|
||||
t.Errorf("unexpected return of PastTime, wanted %q, got %q", expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -248,6 +248,22 @@ func (sn *Snapshot) HasHostname(hostnames []string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// HasTimeBetween returns false if either
|
||||
// - start is given and Time is before start, or
|
||||
// - stop is given and Time is after stop
|
||||
// Otherwise return true
|
||||
// start is the value closest in time, ie. the shortest duration value.
|
||||
func (sn *Snapshot) HasTimeBetween(start Duration, stop Duration) bool {
|
||||
if !start.Zero() && sn.Time.After(start.PastTime()) {
|
||||
return false
|
||||
}
|
||||
if !stop.Zero() && sn.Time.Before(stop.PastTime()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Snapshots is a list of snapshots.
|
||||
type Snapshots []*Snapshot
|
||||
|
||||
|
|
|
@ -20,8 +20,12 @@ type SnapshotFilter struct {
|
|||
Hosts []string
|
||||
Tags TagLists
|
||||
Paths []string
|
||||
// Match snapshots from before this timestamp. Zero for no limit.
|
||||
// Match snapshots from before this timestamp. Zero for no limit. Only used to find parent.
|
||||
TimestampLimit time.Time
|
||||
// Match snapshots from before this timestamp. Zero for no limit.
|
||||
NewerThan Duration
|
||||
// Match snapshots from after this timestamp. Zero for no limit.
|
||||
OlderThan Duration
|
||||
}
|
||||
|
||||
func (f *SnapshotFilter) Empty() bool {
|
||||
|
@ -29,7 +33,7 @@ func (f *SnapshotFilter) Empty() bool {
|
|||
}
|
||||
|
||||
func (f *SnapshotFilter) matches(sn *Snapshot) bool {
|
||||
return sn.HasHostname(f.Hosts) && sn.HasTagList(f.Tags) && sn.HasPaths(f.Paths)
|
||||
return sn.HasHostname(f.Hosts) && sn.HasTagList(f.Tags) && sn.HasPaths(f.Paths) && sn.HasTimeBetween(f.OlderThan, f.NewerThan)
|
||||
}
|
||||
|
||||
// findLatest finds the latest snapshot with optional target/directory,
|
||||
|
|
|
@ -3,6 +3,7 @@ package restic_test
|
|||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
@ -89,3 +90,53 @@ func TestFindAllSubpathError(t *testing.T) {
|
|||
}))
|
||||
test.Assert(t, count == 2, "unexpected number of subfolder errors: %v, wanted %v", count, 2)
|
||||
}
|
||||
|
||||
func TestFindAllNewerThan(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
now := time.Now()
|
||||
|
||||
oneDay := time.Duration(24) * time.Hour
|
||||
restic.TestCreateSnapshot(t, repo, now.Add(-14*oneDay), 1)
|
||||
desiredSnapshot := restic.TestCreateSnapshot(t, repo, now.Add(-4*oneDay), 1)
|
||||
|
||||
var found restic.Snapshot
|
||||
count := 0
|
||||
test.OK(t, (&restic.SnapshotFilter{
|
||||
NewerThan: restic.Duration{Days: 5},
|
||||
}).FindAll(context.TODO(), repo, repo, nil,
|
||||
func(id string, sn *restic.Snapshot, err error) error {
|
||||
if err == nil {
|
||||
found = *sn
|
||||
count++
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
test.Assert(t, count == 1, "unexpected number of snapshots: %v, wanted %v", count, 1)
|
||||
test.Assert(t, desiredSnapshot.ID().Equal(*found.ID()), "unexpected snapshot found: %s, wanted %s", desiredSnapshot, found)
|
||||
}
|
||||
|
||||
func TestFindAllWithin(t *testing.T) {
|
||||
repo := repository.TestRepository(t)
|
||||
now := time.Now()
|
||||
|
||||
oneDay := time.Duration(24) * time.Hour
|
||||
restic.TestCreateSnapshot(t, repo, now.Add(-14*oneDay), 1)
|
||||
restic.TestCreateSnapshot(t, repo, now.Add(-1*oneDay), 1)
|
||||
desiredSnapshot := restic.TestCreateSnapshot(t, repo, now.Add(-4*oneDay), 1)
|
||||
|
||||
var found restic.Snapshot
|
||||
count := 0
|
||||
test.OK(t, (&restic.SnapshotFilter{
|
||||
NewerThan: restic.Duration{Days: 5},
|
||||
OlderThan: restic.Duration{Days: 2},
|
||||
}).FindAll(context.TODO(), repo, repo, nil,
|
||||
func(id string, sn *restic.Snapshot, err error) error {
|
||||
if err == nil {
|
||||
found = *sn
|
||||
count++
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
test.Assert(t, count == 1, "unexpected number of snapshots: %v, wanted %v", count, 1)
|
||||
test.Assert(t, desiredSnapshot.ID().Equal(*found.ID()), "unexpected snapshot found: %s, wanted %s", desiredSnapshot, found)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue