From 8161605f1b76d5978cf89deeb2b82cd6121c1c88 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Fri, 3 Mar 2023 19:07:57 -0800 Subject: [PATCH 01/15] snapshot_group: Fix typo --- internal/restic/snapshot_group.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/restic/snapshot_group.go b/internal/restic/snapshot_group.go index c3f3307f6..9efae2ff6 100644 --- a/internal/restic/snapshot_group.go +++ b/internal/restic/snapshot_group.go @@ -68,7 +68,7 @@ type SnapshotGroupKey struct { } // GroupSnapshots takes a list of snapshots and a grouping criteria and creates -// a group list of snapshots. +// a grouped list of snapshots. func GroupSnapshots(snapshots Snapshots, groupBy SnapshotGroupByOptions) (map[string]Snapshots, bool, error) { // group by hostname and dirs snapshotGroups := make(map[string]Snapshots) From 6aca7dac21e08e86c30da7af3b490bf704455906 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Fri, 3 Mar 2023 19:10:11 -0800 Subject: [PATCH 02/15] forget: Verify forget opts --- cmd/restic/cmd_forget.go | 34 +++++++++++++++++++++++- cmd/restic/cmd_forget_test.go | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 cmd/restic/cmd_forget_test.go diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index fbe4c1c8a..caa205a45 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -99,8 +99,40 @@ func init() { addPruneOptions(cmdForget) } +func verifyForgetOptions(opts *ForgetOptions) error { + var negValFound = false + + if opts.Last < -1 || opts.Hourly < -1 || opts.Daily < -1 || opts.Weekly < -1 || + opts.Monthly < -1 || opts.Yearly < -1 { + negValFound = true + } + + if !negValFound { + // durations := [6]restic.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily, + // opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} + for _, d := range [6]restic.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily, + opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} { + if d.Hours < -1 || d.Days < -1 || d.Months < -1 || d.Years < -1 { + negValFound = true + break + } + } + } + + if negValFound { + return errors.Fatal("negative values other than -1 are not allowed for --keep-* options") + } + + return nil +} + func runForget(ctx context.Context, opts ForgetOptions, gopts GlobalOptions, args []string) error { - err := verifyPruneOptions(&pruneOptions) + err := verifyForgetOptions(&opts) + if err != nil { + return err + } + + err = verifyPruneOptions(&pruneOptions) if err != nil { return err } diff --git a/cmd/restic/cmd_forget_test.go b/cmd/restic/cmd_forget_test.go new file mode 100644 index 000000000..778df4549 --- /dev/null +++ b/cmd/restic/cmd_forget_test.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/restic/restic/internal/restic" + rtest "github.com/restic/restic/internal/test" +) + +func TestPreventNegativeForgetOptionValues(t *testing.T) { + invalidForgetOpts := []ForgetOptions{ + {Last: -2}, + {Hourly: -2}, + {Daily: -2}, + {Weekly: -2}, + {Monthly: -2}, + {Yearly: -2}, + {Within: restic.Duration{Hours: -2}}, + {Within: restic.Duration{Days: -2}}, + {Within: restic.Duration{Months: -2}}, + {Within: restic.Duration{Years: -2}}, + {WithinHourly: restic.Duration{Hours: -2}}, + {WithinHourly: restic.Duration{Days: -2}}, + {WithinHourly: restic.Duration{Months: -2}}, + {WithinHourly: restic.Duration{Years: -2}}, + {WithinDaily: restic.Duration{Hours: -2}}, + {WithinDaily: restic.Duration{Days: -2}}, + {WithinDaily: restic.Duration{Months: -2}}, + {WithinDaily: restic.Duration{Years: -2}}, + {WithinWeekly: restic.Duration{Hours: -2}}, + {WithinWeekly: restic.Duration{Days: -2}}, + {WithinWeekly: restic.Duration{Months: -2}}, + {WithinWeekly: restic.Duration{Years: -2}}, + {WithinMonthly: restic.Duration{Hours: -2}}, + {WithinMonthly: restic.Duration{Days: -2}}, + {WithinMonthly: restic.Duration{Months: -2}}, + {WithinMonthly: restic.Duration{Years: -2}}, + {WithinYearly: restic.Duration{Hours: -2}}, + {WithinYearly: restic.Duration{Days: -2}}, + {WithinYearly: restic.Duration{Months: -2}}, + {WithinYearly: restic.Duration{Years: -2}}, + } + + for _, opts := range invalidForgetOpts { + err := verifyForgetOptions(&opts) + rtest.Assert(t, err != nil, fmt.Sprintf("should have returned error for %+v", opts)) + rtest.Equals(t, "Fatal: negative values other than -1 are not allowed for --keep-* options", err.Error()) + } +} From b77b0749fa44f8d60a4b7344802e2843a40dd72a Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sat, 4 Mar 2023 19:01:37 -0800 Subject: [PATCH 03/15] forget: Treat -1 as forever for all "last n" opts --- cmd/restic/cmd_forget.go | 2 - internal/restic/snapshot_policy.go | 5 +- internal/restic/snapshot_policy_test.go | 6 + .../restic/testdata/policy_keep_snapshots_36 | 1782 ++++++++++++++++ .../restic/testdata/policy_keep_snapshots_37 | 1872 +++++++++++++++++ 5 files changed, 3664 insertions(+), 3 deletions(-) create mode 100644 internal/restic/testdata/policy_keep_snapshots_36 create mode 100644 internal/restic/testdata/policy_keep_snapshots_37 diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index caa205a45..2d9341fac 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -108,8 +108,6 @@ func verifyForgetOptions(opts *ForgetOptions) error { } if !negValFound { - // durations := [6]restic.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily, - // opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} for _, d := range [6]restic.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily, opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} { if d.Hours < -1 || d.Days < -1 || d.Months < -1 || d.Years < -1 { diff --git a/internal/restic/snapshot_policy.go b/internal/restic/snapshot_policy.go index 3271140aa..d63cf45e3 100644 --- a/internal/restic/snapshot_policy.go +++ b/internal/restic/snapshot_policy.go @@ -260,13 +260,16 @@ func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots, reason // Now update the other buckets and see if they have some counts left. for i, b := range buckets { - if b.Count > 0 { + if b.Count <= -1 || b.Count > 0 { val := b.bucker(cur.Time, nr) if val != b.Last { debug.Log("keep %v %v, bucker %v, val %v\n", cur.Time, cur.id.Str(), i, val) keepSnap = true buckets[i].Last = val buckets[i].Count-- + if buckets[i].Count < -1 { + buckets[i].Count = -1 + } keepSnapReasons = append(keepSnapReasons, b.reason) } } diff --git a/internal/restic/snapshot_policy_test.go b/internal/restic/snapshot_policy_test.go index 918ea4ec7..6f921f08e 100644 --- a/internal/restic/snapshot_policy_test.go +++ b/internal/restic/snapshot_policy_test.go @@ -239,6 +239,12 @@ func TestApplyPolicy(t *testing.T) { WithinWeekly: parseDuration("1m"), WithinMonthly: parseDuration("1y"), WithinYearly: parseDuration("9999y")}, + {Last: -1}, // keep all + {Last: -1, Hourly: -1}, // keep all (Last overrides Hourly) + // {Hourly: -1}, // keep all hourlies + // {Daily: -1}, // keep all dailies + // {Daily: -1, Weekly: 4}, // keep all dailies and 4 weeklies + // {Daily: 3, Weekly: -1}, // keep 3 dailies and all weeklies } for i, p := range tests { diff --git a/internal/restic/testdata/policy_keep_snapshots_36 b/internal/restic/testdata/policy_keep_snapshots_36 new file mode 100644 index 000000000..75a3a5b46 --- /dev/null +++ b/internal/restic/testdata/policy_keep_snapshots_36 @@ -0,0 +1,1782 @@ +{ + "keep": [ + { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-12T21:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-08T20:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-07T10:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-06T08:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-05T09:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T16:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:30:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:28:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:24:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T11:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T10:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-03T07:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T07:08:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T01:03:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T01:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-13T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": [ + "path1", + "path2" + ], + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2015-10-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-15T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2014-11-13T10:20:30.1Z", + "tree": null, + "paths": null, + "tags": [ + "bar" + ] + }, + { + "time": "2014-11-13T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-12T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-20T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-11T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-09T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-06T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-05T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-02T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-01T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + } + ], + "reasons": [ + { + "snapshot": { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-12T21:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-08T20:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-07T10:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-06T08:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-05T09:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-04T16:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:30:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:28:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:24:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-04T11:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-04T10:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-03T07:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-01T07:08:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-01T01:03:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2016-01-01T01:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-13T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-11-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": [ + "path1", + "path2" + ], + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-10-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2015-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-15T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-13T10:20:30.1Z", + "tree": null, + "paths": null, + "tags": [ + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-13T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-12T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-11-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-20T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-11T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-09T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-06T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-05T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-02T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-10-01T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + }, + { + "snapshot": { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1} + } + ] +} \ No newline at end of file diff --git a/internal/restic/testdata/policy_keep_snapshots_37 b/internal/restic/testdata/policy_keep_snapshots_37 new file mode 100644 index 000000000..f6ffa40ea --- /dev/null +++ b/internal/restic/testdata/policy_keep_snapshots_37 @@ -0,0 +1,1872 @@ +{ + "keep": [ + { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-12T21:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-08T20:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-07T10:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-06T08:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-05T09:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T16:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:30:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:28:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:24:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T11:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T10:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-03T07:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T07:08:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T01:03:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T01:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-13T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": [ + "path1", + "path2" + ], + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2015-10-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-15T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2014-11-13T10:20:30.1Z", + "tree": null, + "paths": null, + "tags": [ + "bar" + ] + }, + { + "time": "2014-11-13T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-12T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-20T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-11T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-09T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-06T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-05T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-02T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-01T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + } + ], + "reasons": [ + { + "snapshot": { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-12T21:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-08T20:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-07T10:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-06T08:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-05T09:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T16:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:30:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:28:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:24:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T11:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T10:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-03T07:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-01T07:08:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-01T01:03:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-01T01:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-13T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": [ + "path1", + "path2" + ], + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-15T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-13T10:20:30.1Z", + "tree": null, + "paths": null, + "tags": [ + "bar" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-13T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-12T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-20T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-11T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-09T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-06T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-05T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-02T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-01T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-13T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "last snapshot", + "hourly snapshot" + ], + "counters": {"Last": -1, "Hourly": -1} + } + ] +} \ No newline at end of file From 32e6a438be153aeb3d99f758646faedff8783c3e Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sat, 4 Mar 2023 22:13:34 -0800 Subject: [PATCH 04/15] forget: Add test for "keep all hourly snapshots" --- internal/restic/snapshot_policy_test.go | 6 +- .../restic/testdata/policy_keep_snapshots_38 | 1538 +++++++++++++++++ 2 files changed, 1540 insertions(+), 4 deletions(-) create mode 100644 internal/restic/testdata/policy_keep_snapshots_38 diff --git a/internal/restic/snapshot_policy_test.go b/internal/restic/snapshot_policy_test.go index 6f921f08e..92e53848c 100644 --- a/internal/restic/snapshot_policy_test.go +++ b/internal/restic/snapshot_policy_test.go @@ -241,10 +241,8 @@ func TestApplyPolicy(t *testing.T) { WithinYearly: parseDuration("9999y")}, {Last: -1}, // keep all {Last: -1, Hourly: -1}, // keep all (Last overrides Hourly) - // {Hourly: -1}, // keep all hourlies - // {Daily: -1}, // keep all dailies - // {Daily: -1, Weekly: 4}, // keep all dailies and 4 weeklies - // {Daily: 3, Weekly: -1}, // keep 3 dailies and all weeklies + {Hourly: -1}, // keep all hourlies + // {Daily: 3, Weekly: 2, Monthly: -1, Yearly: -1}, } for i, p := range tests { diff --git a/internal/restic/testdata/policy_keep_snapshots_38 b/internal/restic/testdata/policy_keep_snapshots_38 new file mode 100644 index 000000000..6bfdd57f1 --- /dev/null +++ b/internal/restic/testdata/policy_keep_snapshots_38 @@ -0,0 +1,1538 @@ +{ + "keep": [ + { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-08T20:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-07T10:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-06T08:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-05T09:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T16:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T12:30:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T11:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-04T10:23:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-03T07:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T07:08:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-01T01:03:03Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-15T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + { + "time": "2014-11-13T10:20:30.1Z", + "tree": null, + "paths": null, + "tags": [ + "bar" + ] + }, + { + "time": "2014-11-12T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-11-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-20T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-11T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-09T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-06T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-05T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-02T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-10-01T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + } + ], + "reasons": [ + { + "snapshot": { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-08T20:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-07T10:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-06T08:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-05T09:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T16:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T12:30:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T11:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-04T10:23:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-03T07:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-01T07:08:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2016-01-01T01:03:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-11-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-10-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2015-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-15T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo", + "bar" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-13T10:20:30.1Z", + "tree": null, + "paths": null, + "tags": [ + "bar" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-12T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-11-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-20T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-11T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-10T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-09T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-08T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-06T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-05T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-02T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-10-01T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-11T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-09T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-06T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-05T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-02T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-09-01T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-21T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-20T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-18T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-15T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-13T10:20:30.1Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-12T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-10T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + }, + { + "snapshot": { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "hourly snapshot" + ], + "counters": {"Hourly": -1} + } + ] +} From ba183c44c3812882586083550d86ce782343be93 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sun, 5 Mar 2023 13:56:16 -0800 Subject: [PATCH 05/15] forget: Add test with regular and "forever" opts --- internal/restic/snapshot_policy_test.go | 10 +- .../restic/testdata/policy_keep_snapshots_39 | 194 ++++++++++++++++++ 2 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 internal/restic/testdata/policy_keep_snapshots_39 diff --git a/internal/restic/snapshot_policy_test.go b/internal/restic/snapshot_policy_test.go index 92e53848c..97a01ce03 100644 --- a/internal/restic/snapshot_policy_test.go +++ b/internal/restic/snapshot_policy_test.go @@ -242,7 +242,7 @@ func TestApplyPolicy(t *testing.T) { {Last: -1}, // keep all {Last: -1, Hourly: -1}, // keep all (Last overrides Hourly) {Hourly: -1}, // keep all hourlies - // {Daily: 3, Weekly: 2, Monthly: -1, Yearly: -1}, + {Daily: 3, Weekly: 2, Monthly: -1, Yearly: -1}, } for i, p := range tests { @@ -255,9 +255,11 @@ func TestApplyPolicy(t *testing.T) { len(keep)+len(remove), len(testExpireSnapshots)) } - if p.Sum() > 0 && len(keep) > p.Sum() { - t.Errorf("not enough snapshots removed: policy allows %v snapshots to remain, but ended up with %v", - p.Sum(), len(keep)) + if p.Last >= 0 && p.Hourly >= 0 && p.Daily >= 0 && p.Weekly >= 0 && p.Monthly >= 0 && p.Yearly >= 0 { + if p.Sum() > 0 && len(keep) > p.Sum() { + t.Errorf("not enough snapshots removed: policy allows %v snapshots to remain, but ended up with %v", + p.Sum(), len(keep)) + } } if len(keep) != len(reasons) { diff --git a/internal/restic/testdata/policy_keep_snapshots_39 b/internal/restic/testdata/policy_keep_snapshots_39 new file mode 100644 index 000000000..a8e6ca827 --- /dev/null +++ b/internal/restic/testdata/policy_keep_snapshots_39 @@ -0,0 +1,194 @@ +{ + "keep": [ + { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + } + ], + "reasons": [ + { + "snapshot": { + "time": "2016-01-18T12:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "daily snapshot", + "weekly snapshot", + "monthly snapshot", + "yearly snapshot" + ], + "counters": {"Daily": 2, "Weekly": 1, "Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2016-01-12T21:08:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "daily snapshot", + "weekly snapshot" + ], + "counters": {"Daily": 1, "Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2016-01-09T21:02:03Z", + "tree": null, + "paths": null + }, + "matches": [ + "daily snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2015-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot", + "yearly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2015-10-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2015-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2015-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2014-11-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot", + "yearly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2014-10-22T10:20:30Z", + "tree": null, + "paths": null, + "tags": [ + "foo" + ] + }, + "matches": [ + "monthly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2014-09-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2014-08-22T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} + } + ] +} \ No newline at end of file From 667536cea4074a81d787ba4ff6c711f6a22a060b Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sun, 5 Mar 2023 14:18:08 -0800 Subject: [PATCH 06/15] forget: Allow neg. values in "--keep-within*" opts --- cmd/restic/cmd_forget.go | 10 ---------- cmd/restic/cmd_forget_test.go | 25 ------------------------- 2 files changed, 35 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index 2d9341fac..82313c6ef 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -107,16 +107,6 @@ func verifyForgetOptions(opts *ForgetOptions) error { negValFound = true } - if !negValFound { - for _, d := range [6]restic.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily, - opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} { - if d.Hours < -1 || d.Days < -1 || d.Months < -1 || d.Years < -1 { - negValFound = true - break - } - } - } - if negValFound { return errors.Fatal("negative values other than -1 are not allowed for --keep-* options") } diff --git a/cmd/restic/cmd_forget_test.go b/cmd/restic/cmd_forget_test.go index 778df4549..c45d72815 100644 --- a/cmd/restic/cmd_forget_test.go +++ b/cmd/restic/cmd_forget_test.go @@ -4,7 +4,6 @@ import ( "fmt" "testing" - "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) @@ -16,30 +15,6 @@ func TestPreventNegativeForgetOptionValues(t *testing.T) { {Weekly: -2}, {Monthly: -2}, {Yearly: -2}, - {Within: restic.Duration{Hours: -2}}, - {Within: restic.Duration{Days: -2}}, - {Within: restic.Duration{Months: -2}}, - {Within: restic.Duration{Years: -2}}, - {WithinHourly: restic.Duration{Hours: -2}}, - {WithinHourly: restic.Duration{Days: -2}}, - {WithinHourly: restic.Duration{Months: -2}}, - {WithinHourly: restic.Duration{Years: -2}}, - {WithinDaily: restic.Duration{Hours: -2}}, - {WithinDaily: restic.Duration{Days: -2}}, - {WithinDaily: restic.Duration{Months: -2}}, - {WithinDaily: restic.Duration{Years: -2}}, - {WithinWeekly: restic.Duration{Hours: -2}}, - {WithinWeekly: restic.Duration{Days: -2}}, - {WithinWeekly: restic.Duration{Months: -2}}, - {WithinWeekly: restic.Duration{Years: -2}}, - {WithinMonthly: restic.Duration{Hours: -2}}, - {WithinMonthly: restic.Duration{Days: -2}}, - {WithinMonthly: restic.Duration{Months: -2}}, - {WithinMonthly: restic.Duration{Years: -2}}, - {WithinYearly: restic.Duration{Hours: -2}}, - {WithinYearly: restic.Duration{Days: -2}}, - {WithinYearly: restic.Duration{Months: -2}}, - {WithinYearly: restic.Duration{Years: -2}}, } for _, opts := range invalidForgetOpts { From b7f03d01b8eed1c0dab111aa83ee9bfd1dd8fbeb Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Tue, 14 Mar 2023 19:16:24 -0700 Subject: [PATCH 07/15] Add helper function for Duration parsing Tests in cmd_forget_test.go need the same convenience function that was implemented in snapshot_policy_test.go. Function parseDuration(...) was moved to testing.go and renamed to ParseDurationOrPanic(...). --- internal/restic/testing.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/internal/restic/testing.go b/internal/restic/testing.go index ebafdf651..9c21b81d1 100644 --- a/internal/restic/testing.go +++ b/internal/restic/testing.go @@ -212,3 +212,14 @@ func TestParseHandle(s string, t BlobType) BlobHandle { func TestSetSnapshotID(t testing.TB, sn *Snapshot, id ID) { sn.id = &id } + +// Convenience function that parses a duration from a string or panics if string is invalid. +// The format is `6y5m234d37h`. +func ParseDurationOrPanic(s string) Duration { + d, err := ParseDuration(s) + if err != nil { + panic(err) + } + + return d +} From 84ede6ad7a4c8cc7791145b952e5f48d3c13266a Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Tue, 14 Mar 2023 19:20:03 -0700 Subject: [PATCH 08/15] forget: Prevent neg. values in --keep-within* opts --- cmd/restic/cmd_forget.go | 11 +++--- cmd/restic/cmd_forget_test.go | 66 ++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index 82313c6ef..ac3601584 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -100,15 +100,16 @@ func init() { } func verifyForgetOptions(opts *ForgetOptions) error { - var negValFound = false - if opts.Last < -1 || opts.Hourly < -1 || opts.Daily < -1 || opts.Weekly < -1 || opts.Monthly < -1 || opts.Yearly < -1 { - negValFound = true + return errors.Fatal("negative values other than -1 are not allowed for --keep-*") } - if negValFound { - return errors.Fatal("negative values other than -1 are not allowed for --keep-* options") + for _, d := range []restic.Duration{opts.Within, opts.WithinHourly, opts.WithinDaily, + opts.WithinMonthly, opts.WithinWeekly, opts.WithinYearly} { + if d.Hours < 0 || d.Days < 0 || d.Months < 0 || d.Years < 0 { + return errors.Fatal("durations containing negative values are not allowed for --keep-within*") + } } return nil diff --git a/cmd/restic/cmd_forget_test.go b/cmd/restic/cmd_forget_test.go index c45d72815..9fd5c7bb0 100644 --- a/cmd/restic/cmd_forget_test.go +++ b/cmd/restic/cmd_forget_test.go @@ -1,25 +1,65 @@ package main import ( - "fmt" "testing" + "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) -func TestPreventNegativeForgetOptionValues(t *testing.T) { - invalidForgetOpts := []ForgetOptions{ - {Last: -2}, - {Hourly: -2}, - {Daily: -2}, - {Weekly: -2}, - {Monthly: -2}, - {Yearly: -2}, +func TestForgetOptionValues(t *testing.T) { + const negValErrorMsg = "Fatal: negative values other than -1 are not allowed for --keep-*" + const negDurationValErrorMsg = "Fatal: durations containing negative values are not allowed for --keep-within*" + testCases := []struct { + input ForgetOptions + expectsError bool + errorMsg string + }{ + {ForgetOptions{Last: 1}, false, ""}, + {ForgetOptions{Hourly: 1}, false, ""}, + {ForgetOptions{Daily: 1}, false, ""}, + {ForgetOptions{Weekly: 1}, false, ""}, + {ForgetOptions{Monthly: 1}, false, ""}, + {ForgetOptions{Yearly: 1}, false, ""}, + {ForgetOptions{Last: 0}, false, ""}, + {ForgetOptions{Hourly: 0}, false, ""}, + {ForgetOptions{Daily: 0}, false, ""}, + {ForgetOptions{Weekly: 0}, false, ""}, + {ForgetOptions{Monthly: 0}, false, ""}, + {ForgetOptions{Yearly: 0}, false, ""}, + {ForgetOptions{Last: -1}, false, ""}, + {ForgetOptions{Hourly: -1}, false, ""}, + {ForgetOptions{Daily: -1}, false, ""}, + {ForgetOptions{Weekly: -1}, false, ""}, + {ForgetOptions{Monthly: -1}, false, ""}, + {ForgetOptions{Yearly: -1}, false, ""}, + {ForgetOptions{Last: -2}, true, negValErrorMsg}, + {ForgetOptions{Hourly: -2}, true, negValErrorMsg}, + {ForgetOptions{Daily: -2}, true, negValErrorMsg}, + {ForgetOptions{Weekly: -2}, true, negValErrorMsg}, + {ForgetOptions{Monthly: -2}, true, negValErrorMsg}, + {ForgetOptions{Yearly: -2}, true, negValErrorMsg}, + {ForgetOptions{Within: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""}, + {ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""}, + {ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""}, + {ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d3h")}, false, ""}, + {ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("2y4m6d8h")}, false, ""}, + {ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y4m6d8h")}, false, ""}, + {ForgetOptions{Within: restic.ParseDurationOrPanic("-1y2m3d3h")}, true, negDurationValErrorMsg}, + {ForgetOptions{WithinHourly: restic.ParseDurationOrPanic("1y-2m3d3h")}, true, negDurationValErrorMsg}, + {ForgetOptions{WithinDaily: restic.ParseDurationOrPanic("1y2m-3d3h")}, true, negDurationValErrorMsg}, + {ForgetOptions{WithinWeekly: restic.ParseDurationOrPanic("1y2m3d-3h")}, true, negDurationValErrorMsg}, + {ForgetOptions{WithinMonthly: restic.ParseDurationOrPanic("-2y4m6d8h")}, true, negDurationValErrorMsg}, + {ForgetOptions{WithinYearly: restic.ParseDurationOrPanic("2y-4m6d8h")}, true, negDurationValErrorMsg}, } - for _, opts := range invalidForgetOpts { - err := verifyForgetOptions(&opts) - rtest.Assert(t, err != nil, fmt.Sprintf("should have returned error for %+v", opts)) - rtest.Equals(t, "Fatal: negative values other than -1 are not allowed for --keep-* options", err.Error()) + for _, testCase := range testCases { + err := verifyForgetOptions(&testCase.input) + if testCase.expectsError { + rtest.Assert(t, err != nil, "should have returned error for input %+v", testCase.input) + rtest.Equals(t, testCase.errorMsg, err.Error()) + } else { + rtest.Assert(t, err == nil, "expected no error for input %+v", testCase.input) + } } } From 1a584cb16e4df2782996bbfa5b9f4493485ffb46 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Tue, 14 Mar 2023 19:29:08 -0700 Subject: [PATCH 09/15] Refactor policy sum calculation & duration parsing - Convert policy sum calculation to function and move to tests. - Remove parseDuration(...) and use ParseDurationOrPanic(...) instead. --- internal/restic/snapshot_policy.go | 16 ++----- internal/restic/snapshot_policy_test.go | 61 ++++++++++++------------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/internal/restic/snapshot_policy.go b/internal/restic/snapshot_policy.go index d63cf45e3..a9ec4a9f7 100644 --- a/internal/restic/snapshot_policy.go +++ b/internal/restic/snapshot_policy.go @@ -100,13 +100,7 @@ func (e ExpirePolicy) String() (s string) { return s } -// Sum returns the maximum number of snapshots to be kept according to this -// policy. -func (e ExpirePolicy) Sum() int { - return e.Last + e.Hourly + e.Daily + e.Weekly + e.Monthly + e.Yearly -} - -// Empty returns true iff no policy has been configured (all values zero). +// Empty returns true if no policy has been configured (all values zero). func (e ExpirePolicy) Empty() bool { if len(e.Tags) != 0 { return false @@ -260,15 +254,15 @@ func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots, reason // Now update the other buckets and see if they have some counts left. for i, b := range buckets { - if b.Count <= -1 || b.Count > 0 { + // -1 means "keep all" + if b.Count > 0 || b.Count == -1 { val := b.bucker(cur.Time, nr) if val != b.Last { debug.Log("keep %v %v, bucker %v, val %v\n", cur.Time, cur.id.Str(), i, val) keepSnap = true buckets[i].Last = val - buckets[i].Count-- - if buckets[i].Count < -1 { - buckets[i].Count = -1 + if buckets[i].Count > 0 { + buckets[i].Count-- } keepSnapReasons = append(keepSnapReasons, b.reason) } diff --git a/internal/restic/snapshot_policy_test.go b/internal/restic/snapshot_policy_test.go index 97a01ce03..75f0f18f4 100644 --- a/internal/restic/snapshot_policy_test.go +++ b/internal/restic/snapshot_policy_test.go @@ -22,13 +22,14 @@ func parseTimeUTC(s string) time.Time { return t.UTC() } -func parseDuration(s string) restic.Duration { - d, err := restic.ParseDuration(s) - if err != nil { - panic(err) +// Returns the maximum number of snapshots to be kept according to this policy. +// If any of the counts is -1 it will return 0. +func policySum(e *restic.ExpirePolicy) int { + if e.Last == -1 || e.Hourly == -1 || e.Daily == -1 || e.Weekly == -1 || e.Monthly == -1 || e.Yearly == -1 { + return 0 } - return d + return e.Last + e.Hourly + e.Daily + e.Weekly + e.Monthly + e.Yearly } func TestExpireSnapshotOps(t *testing.T) { @@ -46,7 +47,7 @@ func TestExpireSnapshotOps(t *testing.T) { if isEmpty != d.expectEmpty { t.Errorf("empty test %v: wrong result, want:\n %#v\ngot:\n %#v", i, d.expectEmpty, isEmpty) } - hasSum := d.p.Sum() + hasSum := policySum(d.p) if hasSum != d.expectSum { t.Errorf("sum test %v: wrong result, want:\n %#v\ngot:\n %#v", i, d.expectSum, hasSum) } @@ -219,26 +220,26 @@ func TestApplyPolicy(t *testing.T) { {Tags: []restic.TagList{{"foo"}}}, {Tags: []restic.TagList{{"foo", "bar"}}}, {Tags: []restic.TagList{{"foo"}, {"bar"}}}, - {Within: parseDuration("1d")}, - {Within: parseDuration("2d")}, - {Within: parseDuration("7d")}, - {Within: parseDuration("1m")}, - {Within: parseDuration("1m14d")}, - {Within: parseDuration("1y1d1m")}, - {Within: parseDuration("13d23h")}, - {Within: parseDuration("2m2h")}, - {Within: parseDuration("1y2m3d3h")}, - {WithinHourly: parseDuration("1y2m3d3h")}, - {WithinDaily: parseDuration("1y2m3d3h")}, - {WithinWeekly: parseDuration("1y2m3d3h")}, - {WithinMonthly: parseDuration("1y2m3d3h")}, - {WithinYearly: parseDuration("1y2m3d3h")}, - {Within: parseDuration("1h"), - WithinHourly: parseDuration("1d"), - WithinDaily: parseDuration("7d"), - WithinWeekly: parseDuration("1m"), - WithinMonthly: parseDuration("1y"), - WithinYearly: parseDuration("9999y")}, + {Within: restic.ParseDurationOrPanic("1d")}, + {Within: restic.ParseDurationOrPanic("2d")}, + {Within: restic.ParseDurationOrPanic("7d")}, + {Within: restic.ParseDurationOrPanic("1m")}, + {Within: restic.ParseDurationOrPanic("1m14d")}, + {Within: restic.ParseDurationOrPanic("1y1d1m")}, + {Within: restic.ParseDurationOrPanic("13d23h")}, + {Within: restic.ParseDurationOrPanic("2m2h")}, + {Within: restic.ParseDurationOrPanic("1y2m3d3h")}, + {WithinHourly: restic.ParseDurationOrPanic("1y2m3d3h")}, + {WithinDaily: restic.ParseDurationOrPanic("1y2m3d3h")}, + {WithinWeekly: restic.ParseDurationOrPanic("1y2m3d3h")}, + {WithinMonthly: restic.ParseDurationOrPanic("1y2m3d3h")}, + {WithinYearly: restic.ParseDurationOrPanic("1y2m3d3h")}, + {Within: restic.ParseDurationOrPanic("1h"), + WithinHourly: restic.ParseDurationOrPanic("1d"), + WithinDaily: restic.ParseDurationOrPanic("7d"), + WithinWeekly: restic.ParseDurationOrPanic("1m"), + WithinMonthly: restic.ParseDurationOrPanic("1y"), + WithinYearly: restic.ParseDurationOrPanic("9999y")}, {Last: -1}, // keep all {Last: -1, Hourly: -1}, // keep all (Last overrides Hourly) {Hourly: -1}, // keep all hourlies @@ -255,11 +256,9 @@ func TestApplyPolicy(t *testing.T) { len(keep)+len(remove), len(testExpireSnapshots)) } - if p.Last >= 0 && p.Hourly >= 0 && p.Daily >= 0 && p.Weekly >= 0 && p.Monthly >= 0 && p.Yearly >= 0 { - if p.Sum() > 0 && len(keep) > p.Sum() { - t.Errorf("not enough snapshots removed: policy allows %v snapshots to remain, but ended up with %v", - p.Sum(), len(keep)) - } + if policySum(&p) > 0 && len(keep) > policySum(&p) { + t.Errorf("not enough snapshots removed: policy allows %v snapshots to remain, but ended up with %v", + policySum(&p), len(keep)) } if len(keep) != len(reasons) { From 5069c9edd95d5a13b71d682c8214962802df80e7 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Wed, 15 Mar 2023 15:07:51 -0700 Subject: [PATCH 10/15] Represent -1 as "all" in ExpirePolicy's Stringer --- internal/restic/snapshot_policy.go | 33 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/internal/restic/snapshot_policy.go b/internal/restic/snapshot_policy.go index a9ec4a9f7..228e4c88a 100644 --- a/internal/restic/snapshot_policy.go +++ b/internal/restic/snapshot_policy.go @@ -31,23 +31,22 @@ func (e ExpirePolicy) String() (s string) { var keeps []string var keepw []string - if e.Last > 0 { - keeps = append(keeps, fmt.Sprintf("%d latest", e.Last)) - } - if e.Hourly > 0 { - keeps = append(keeps, fmt.Sprintf("%d hourly", e.Hourly)) - } - if e.Daily > 0 { - keeps = append(keeps, fmt.Sprintf("%d daily", e.Daily)) - } - if e.Weekly > 0 { - keeps = append(keeps, fmt.Sprintf("%d weekly", e.Weekly)) - } - if e.Monthly > 0 { - keeps = append(keeps, fmt.Sprintf("%d monthly", e.Monthly)) - } - if e.Yearly > 0 { - keeps = append(keeps, fmt.Sprintf("%d yearly", e.Yearly)) + for _, opt := range []struct { + count int + descr string + }{ + {e.Last, "latest"}, + {e.Hourly, "hourly"}, + {e.Daily, "daily"}, + {e.Weekly, "weekly"}, + {e.Monthly, "monthly"}, + {e.Yearly, "yearly"}, + } { + if opt.count > 0 { + keeps = append(keeps, fmt.Sprintf("%d %s", opt.count, opt.descr)) + } else if opt.count == -1 { + keeps = append(keeps, fmt.Sprintf("all %s", opt.descr)) + } } if !e.WithinHourly.Zero() { From 9f9e91eb0d402f55234e8fa564e7903ad679c727 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sun, 9 Apr 2023 11:47:10 -0700 Subject: [PATCH 11/15] Fix comment to comply with linter --- internal/restic/testing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/restic/testing.go b/internal/restic/testing.go index 9c21b81d1..dda9eff44 100644 --- a/internal/restic/testing.go +++ b/internal/restic/testing.go @@ -213,7 +213,7 @@ func TestSetSnapshotID(t testing.TB, sn *Snapshot, id ID) { sn.id = &id } -// Convenience function that parses a duration from a string or panics if string is invalid. +// ParseDurationOrPanic parses a duration from a string or panics if string is invalid. // The format is `6y5m234d37h`. func ParseDurationOrPanic(s string) Duration { d, err := ParseDuration(s) From 309cf0586ad6f36a7bd99e331cacfe9fbbea2a65 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sun, 9 Apr 2023 12:05:15 -0700 Subject: [PATCH 12/15] Add changelog entry --- changelog/unreleased/issue-2565 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changelog/unreleased/issue-2565 diff --git a/changelog/unreleased/issue-2565 b/changelog/unreleased/issue-2565 new file mode 100644 index 000000000..4150dcda4 --- /dev/null +++ b/changelog/unreleased/issue-2565 @@ -0,0 +1,8 @@ +Bugfix: Restic forget --keep-* options now interpret "-1" as "forever" + +Restic would forget snapshots that should have been kept when "-1" was +used as a value for --keep-* options. It now interprets "-1" as forever, +e.g. an option like --keep-monthly -1 will keep all monthly snapshots. + +https://github.com/restic/restic/issues/2565 +https://github.com/restic/restic/pull/4234 From 403b7ca2be321d7bb0092e6b42e5709b95b4eb03 Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sun, 9 Apr 2023 12:57:37 -0700 Subject: [PATCH 13/15] forget: Update documentation --- doc/060_forget.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/060_forget.rst b/doc/060_forget.rst index b960ddb14..2353ef6a0 100644 --- a/doc/060_forget.rst +++ b/doc/060_forget.rst @@ -205,6 +205,7 @@ The ``forget`` command accepts the following policy options: natural time boundaries and *not* relative to when you run ``forget``. Weeks are Monday 00:00 to Sunday 23:59, days 00:00 to 23:59, hours :00 to :59, etc. They also only count hours/days/weeks/etc which have one or more snapshots. + A value of ``-1`` will be interpreted as "forever", i.e. "keep all". .. note:: All duration related options (``--keep-{within,-*}``) ignore snapshots with a timestamp in the future (relative to when the ``forget`` command is From ce51d2f3c07b2ebb831e29d8a3a2c7764e1ae93f Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Sun, 9 Apr 2023 12:59:15 -0700 Subject: [PATCH 14/15] forget: Update usage text --- cmd/restic/cmd_forget.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index ac3601584..cc1b4f6d8 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -67,12 +67,12 @@ func init() { cmdRoot.AddCommand(cmdForget) f := cmdForget.Flags() - f.IntVarP(&forgetOptions.Last, "keep-last", "l", 0, "keep the last `n` snapshots") - f.IntVarP(&forgetOptions.Hourly, "keep-hourly", "H", 0, "keep the last `n` hourly snapshots") - f.IntVarP(&forgetOptions.Daily, "keep-daily", "d", 0, "keep the last `n` daily snapshots") - f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots") - f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots") - f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots") + f.IntVarP(&forgetOptions.Last, "keep-last", "l", 0, "keep the last `n` snapshots (use \"-1\" for n to keep all snapshots)") + f.IntVarP(&forgetOptions.Hourly, "keep-hourly", "H", 0, "keep the last `n` hourly snapshots (use \"-1\" for n to keep all hourly snapshots)") + f.IntVarP(&forgetOptions.Daily, "keep-daily", "d", 0, "keep the last `n` daily snapshots (use \"-1\" for n to keep all daily snapshots)") + f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots (use \"-1\" for n to keep all weekly snapshots)") + f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots (use \"-1\" for n to keep all monthly snapshots)") + f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots (use \"-1\" for n to keep all yearly snapshots)") f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") f.VarP(&forgetOptions.WithinHourly, "keep-within-hourly", "", "keep hourly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") f.VarP(&forgetOptions.WithinDaily, "keep-within-daily", "", "keep daily snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") From 6d6c04abef9929e1ce04f33a379386c4de3b819b Mon Sep 17 00:00:00 2001 From: Torben Giesselmann Date: Fri, 14 Apr 2023 10:05:23 -0700 Subject: [PATCH 15/15] forget: Simplify usage text --- cmd/restic/cmd_forget.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index cc1b4f6d8..3154a050e 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -67,12 +67,12 @@ func init() { cmdRoot.AddCommand(cmdForget) f := cmdForget.Flags() - f.IntVarP(&forgetOptions.Last, "keep-last", "l", 0, "keep the last `n` snapshots (use \"-1\" for n to keep all snapshots)") - f.IntVarP(&forgetOptions.Hourly, "keep-hourly", "H", 0, "keep the last `n` hourly snapshots (use \"-1\" for n to keep all hourly snapshots)") - f.IntVarP(&forgetOptions.Daily, "keep-daily", "d", 0, "keep the last `n` daily snapshots (use \"-1\" for n to keep all daily snapshots)") - f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots (use \"-1\" for n to keep all weekly snapshots)") - f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots (use \"-1\" for n to keep all monthly snapshots)") - f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots (use \"-1\" for n to keep all yearly snapshots)") + f.IntVarP(&forgetOptions.Last, "keep-last", "l", 0, "keep the last `n` snapshots (use '-1' to keep all snapshots)") + f.IntVarP(&forgetOptions.Hourly, "keep-hourly", "H", 0, "keep the last `n` hourly snapshots (use '-1' to keep all hourly snapshots)") + f.IntVarP(&forgetOptions.Daily, "keep-daily", "d", 0, "keep the last `n` daily snapshots (use '-1' to keep all daily snapshots)") + f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots (use '-1' to keep all weekly snapshots)") + f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots (use '-1' to keep all monthly snapshots)") + f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots (use '-1' to keep all yearly snapshots)") f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") f.VarP(&forgetOptions.WithinHourly, "keep-within-hourly", "", "keep hourly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") f.VarP(&forgetOptions.WithinDaily, "keep-within-daily", "", "keep daily snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot")