Support time ranges expressed in hours in snapshot retention policies
Make restic forget --keep-within accept time ranges measured in hours and choose accordingly which snapshots to keep and which to forget. Add relative tests.
This commit is contained in:
parent
7486bfea5b
commit
71891b340c
6 changed files with 1661 additions and 2 deletions
|
@ -59,7 +59,7 @@ func init() {
|
||||||
f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly 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.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.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots")
|
||||||
f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are older than `duration` (eg. 1y5m7d) relative to the latest snapshot")
|
f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot")
|
||||||
|
|
||||||
f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
|
f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
|
||||||
f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
|
f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
|
||||||
|
|
|
@ -196,7 +196,7 @@ func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots, reason
|
||||||
|
|
||||||
// If the timestamp of the snapshot is within the range, then keep it.
|
// If the timestamp of the snapshot is within the range, then keep it.
|
||||||
if !p.Within.Zero() {
|
if !p.Within.Zero() {
|
||||||
t := latest.AddDate(-p.Within.Years, -p.Within.Months, -p.Within.Days)
|
t := latest.AddDate(-p.Within.Years, -p.Within.Months, -p.Within.Days).Add(time.Hour * time.Duration(-p.Within.Hours))
|
||||||
if cur.Time.After(t) {
|
if cur.Time.After(t) {
|
||||||
keepSnap = true
|
keepSnap = true
|
||||||
keepSnapReasons = append(keepSnapReasons, fmt.Sprintf("within %v", p.Within))
|
keepSnapReasons = append(keepSnapReasons, fmt.Sprintf("within %v", p.Within))
|
||||||
|
|
|
@ -225,6 +225,9 @@ func TestApplyPolicy(t *testing.T) {
|
||||||
{Within: parseDuration("1m")},
|
{Within: parseDuration("1m")},
|
||||||
{Within: parseDuration("1m14d")},
|
{Within: parseDuration("1m14d")},
|
||||||
{Within: parseDuration("1y1d1m")},
|
{Within: parseDuration("1y1d1m")},
|
||||||
|
{Within: parseDuration("13d23h")},
|
||||||
|
{Within: parseDuration("2m2h")},
|
||||||
|
{Within: parseDuration("1y2m3d3h")},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, p := range tests {
|
for i, p := range tests {
|
||||||
|
|
150
internal/restic/testdata/policy_keep_snapshots_27
vendored
Normal file
150
internal/restic/testdata/policy_keep_snapshots_27
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reasons": [
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-18T12:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-12T21:08:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-12T21:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-09T21:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-08T20:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-07T10:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-06T08:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-05T09:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T16:23:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 13d23h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
374
internal/restic/testdata/policy_keep_snapshots_28
vendored
Normal file
374
internal/restic/testdata/policy_keep_snapshots_28
vendored
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reasons": [
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-18T12:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-12T21:08:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-12T21:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-09T21:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-08T20:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-07T10:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-06T08:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-05T09:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T16:23:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T12:30:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T12:28:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T12:24:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T12:23:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T11:23:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-04T10:23:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-03T07:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-01T07:08:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-01T01:03:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2016-01-01T01:02:03Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2015-11-22T10:20:30Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2015-11-21T10:20:30Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2015-11-20T10:20:30Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"snapshot": {
|
||||||
|
"time": "2015-11-18T10:20:30Z",
|
||||||
|
"tree": null,
|
||||||
|
"paths": null
|
||||||
|
},
|
||||||
|
"matches": [
|
||||||
|
"within 2m2h"
|
||||||
|
],
|
||||||
|
"counters": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1132
internal/restic/testdata/policy_keep_snapshots_29
vendored
Normal file
1132
internal/restic/testdata/policy_keep_snapshots_29
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue