From cfdf4c92f79f1ddcbec0b49b7137ee06ed9691a1 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Tue, 13 Sep 2016 20:37:11 +0200 Subject: [PATCH] Add `--keep-tag` to `forget` command --- src/cmds/restic/cmd_forget.go | 9 +- src/restic/snapshot_filter.go | 33 +++++-- src/restic/snapshot_filter_test.go | 29 +++--- src/restic/testdata/expired_snapshots_0 | 70 +++++++++++--- src/restic/testdata/expired_snapshots_15 | 5 +- src/restic/testdata/expired_snapshots_18 | 114 +++++++++++++++++++++++ src/restic/testdata/expired_snapshots_3 | 70 +++++++++++--- src/restic/testdata/expired_snapshots_4 | 70 +++++++++++--- 8 files changed, 333 insertions(+), 67 deletions(-) create mode 100644 src/restic/testdata/expired_snapshots_18 diff --git a/src/cmds/restic/cmd_forget.go b/src/cmds/restic/cmd_forget.go index 1a2e98098..350b02597 100644 --- a/src/cmds/restic/cmd_forget.go +++ b/src/cmds/restic/cmd_forget.go @@ -9,13 +9,15 @@ import ( // CmdForget implements the 'forget' command. type CmdForget struct { - Last int `short:"l" long:"keep-last" description:"keep the last n snapshots"` + Last int `short:"l" long:"keep-last" description:"keep the last n snapshots"` Hourly int `short:"H" long:"keep-hourly" description:"keep the last n hourly snapshots"` - Daily int `short:"d" long:"keep-daily" description:"keep the last n daily snapshots"` + Daily int `short:"d" long:"keep-daily" description:"keep the last n daily snapshots"` Weekly int `short:"w" long:"keep-weekly" description:"keep the last n weekly snapshots"` - Monthly int `short:"m" long:"keep-monthly" description:"keep the last n monthly snapshots"` + Monthly int `short:"m" long:"keep-monthly"description:"keep the last n monthly snapshots"` Yearly int `short:"y" long:"keep-yearly" description:"keep the last n yearly snapshots"` + KeepTags []string `long:"keep-tag" description:"alwaps keep snapshots with this tag (can be specified multiple times)"` + Hostname string `long:"hostname" description:"only forget snapshots for the given hostname"` Tags []string `long:"tag" description:"only forget snapshots with the tag (can be specified multiple times)"` @@ -111,6 +113,7 @@ func (cmd CmdForget) Execute(args []string) error { Weekly: cmd.Weekly, Monthly: cmd.Monthly, Yearly: cmd.Yearly, + Tags: cmd.KeepTags, } if policy.Empty() { diff --git a/src/restic/snapshot_filter.go b/src/restic/snapshot_filter.go index 6a49d6121..7d5b2e59a 100644 --- a/src/restic/snapshot_filter.go +++ b/src/restic/snapshot_filter.go @@ -61,12 +61,13 @@ func FilterSnapshots(s Snapshots, f SnapshotFilter) (result Snapshots) { // ExpirePolicy configures which snapshots should be automatically removed. type ExpirePolicy struct { - Last int // keep the last n snapshots - Hourly int // keep the last n hourly snapshots - Daily int // keep the last n daily snapshots - Weekly int // keep the last n weekly snapshots - Monthly int // keep the last n monthly snapshots - Yearly int // keep the last n yearly snapshots + Last int // keep the last n snapshots + Hourly int // keep the last n hourly snapshots + Daily int // keep the last n daily snapshots + Weekly int // keep the last n weekly snapshots + Monthly int // keep the last n monthly snapshots + Yearly int // keep the last n yearly snapshots + Tags []string // keep all snapshots with these tags } // Sum returns the maximum number of snapshots to be kept according to this @@ -77,8 +78,12 @@ func (e ExpirePolicy) Sum() int { // Empty returns true iff no policy has been configured (all values zero). func (e ExpirePolicy) Empty() bool { + if len(e.Tags) != 0 { + return false + } + empty := ExpirePolicy{} - return e == empty + return reflect.DeepEqual(e, empty) } // filter is used to split a list of snapshots into those to keep and those to @@ -166,6 +171,19 @@ func (f *filter) apply(fn func(time.Time) int, max int) { } } +// keepTags marks the snapshots which have all tags as to be kept. +func (f *filter) keepTags(tags []string) { + if len(tags) == 0 { + return + } + + for _, sn := range f.Unprocessed { + if sn.HasTags(tags) { + f.Keep = append(f.Keep, sn) + } + } +} + // keepLast marks the last n snapshots as to be kept. func (f *filter) keepLast(n int) { if n > len(f.Unprocessed) { @@ -200,6 +218,7 @@ func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots) { Keep: Snapshots{}, } + f.keepTags(p.Tags) f.keepLast(p.Last) f.apply(ymdh, p.Hourly) f.apply(ymd, p.Daily) diff --git a/src/restic/snapshot_filter_test.go b/src/restic/snapshot_filter_test.go index 000544977..beca742a2 100644 --- a/src/restic/snapshot_filter_test.go +++ b/src/restic/snapshot_filter_test.go @@ -112,20 +112,20 @@ var testExpireSnapshots = restic.Snapshots{ {Time: parseTimeUTC("2014-08-20 10:20:30")}, {Time: parseTimeUTC("2014-08-21 10:20:30")}, {Time: parseTimeUTC("2014-08-22 10:20:30")}, - {Time: parseTimeUTC("2014-10-01 10:20:30")}, - {Time: parseTimeUTC("2014-10-02 10:20:30")}, - {Time: parseTimeUTC("2014-10-05 10:20:30")}, - {Time: parseTimeUTC("2014-10-06 10:20:30")}, - {Time: parseTimeUTC("2014-10-08 10:20:30")}, - {Time: parseTimeUTC("2014-10-09 10:20:30")}, - {Time: parseTimeUTC("2014-10-10 10:20:30")}, - {Time: parseTimeUTC("2014-10-11 10:20:30")}, - {Time: parseTimeUTC("2014-10-20 10:20:30")}, - {Time: parseTimeUTC("2014-10-22 10:20:30")}, - {Time: parseTimeUTC("2014-11-08 10:20:30")}, - {Time: parseTimeUTC("2014-11-10 10:20:30")}, - {Time: parseTimeUTC("2014-11-12 10:20:30")}, - {Time: parseTimeUTC("2014-11-13 10:20:30")}, + {Time: parseTimeUTC("2014-10-01 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-02 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-05 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-06 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-08 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-09 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-10 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-11 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-20 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-10-22 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-11-08 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-11-10 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-11-12 10:20:30"), Tags: []string{"foo"}}, + {Time: parseTimeUTC("2014-11-13 10:20:30"), Tags: []string{"foo"}}, {Time: parseTimeUTC("2014-11-13 10:20:30")}, {Time: parseTimeUTC("2014-11-15 10:20:30")}, {Time: parseTimeUTC("2014-11-18 10:20:30")}, @@ -212,6 +212,7 @@ var expireTests = []restic.ExpirePolicy{ {Daily: 2, Weekly: 2, Monthly: 6}, {Yearly: 10}, {Daily: 7, Weekly: 2, Monthly: 3, Yearly: 10}, + {Tags: []string{"foo"}}, } func TestApplyPolicy(t *testing.T) { diff --git a/src/restic/testdata/expired_snapshots_0 b/src/restic/testdata/expired_snapshots_0 index d70bdbaa1..261d70719 100644 --- a/src/restic/testdata/expired_snapshots_0 +++ b/src/restic/testdata/expired_snapshots_0 @@ -327,72 +327,114 @@ { "time": "2014-11-13T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-12T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-10T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-08T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-22T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-20T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-11T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-10T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-09T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-08T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-06T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-05T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-02T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-01T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-09-22T10:20:30Z", diff --git a/src/restic/testdata/expired_snapshots_15 b/src/restic/testdata/expired_snapshots_15 index 58d52ae4a..973e951ef 100644 --- a/src/restic/testdata/expired_snapshots_15 +++ b/src/restic/testdata/expired_snapshots_15 @@ -47,6 +47,9 @@ { "time": "2014-10-22T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] } ] \ No newline at end of file diff --git a/src/restic/testdata/expired_snapshots_18 b/src/restic/testdata/expired_snapshots_18 new file mode 100644 index 000000000..5898b0950 --- /dev/null +++ b/src/restic/testdata/expired_snapshots_18 @@ -0,0 +1,114 @@ +[ + { + "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" + ] + } +] \ No newline at end of file diff --git a/src/restic/testdata/expired_snapshots_3 b/src/restic/testdata/expired_snapshots_3 index d70bdbaa1..261d70719 100644 --- a/src/restic/testdata/expired_snapshots_3 +++ b/src/restic/testdata/expired_snapshots_3 @@ -327,72 +327,114 @@ { "time": "2014-11-13T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-12T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-10T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-08T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-22T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-20T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-11T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-10T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-09T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-08T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-06T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-05T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-02T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-01T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-09-22T10:20:30Z", diff --git a/src/restic/testdata/expired_snapshots_4 b/src/restic/testdata/expired_snapshots_4 index d70bdbaa1..261d70719 100644 --- a/src/restic/testdata/expired_snapshots_4 +++ b/src/restic/testdata/expired_snapshots_4 @@ -327,72 +327,114 @@ { "time": "2014-11-13T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-12T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-10T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-11-08T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-22T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-20T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-11T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-10T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-09T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-08T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-06T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-05T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-02T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-10-01T10:20:30Z", "tree": null, - "paths": null + "paths": null, + "tags": [ + "foo" + ] }, { "time": "2014-09-22T10:20:30Z",