github.com/mckael/restic@v0.8.3/internal/restic/snapshot_policy.go (about) 1 package restic 2 3 import ( 4 "reflect" 5 "sort" 6 "time" 7 ) 8 9 // ExpirePolicy configures which snapshots should be automatically removed. 10 type ExpirePolicy struct { 11 Last int // keep the last n snapshots 12 Hourly int // keep the last n hourly snapshots 13 Daily int // keep the last n daily snapshots 14 Weekly int // keep the last n weekly snapshots 15 Monthly int // keep the last n monthly snapshots 16 Yearly int // keep the last n yearly snapshots 17 Tags []TagList // keep all snapshots that include at least one of the tag lists. 18 } 19 20 // Sum returns the maximum number of snapshots to be kept according to this 21 // policy. 22 func (e ExpirePolicy) Sum() int { 23 return e.Last + e.Hourly + e.Daily + e.Weekly + e.Monthly + e.Yearly 24 } 25 26 // Empty returns true iff no policy has been configured (all values zero). 27 func (e ExpirePolicy) Empty() bool { 28 if len(e.Tags) != 0 { 29 return false 30 } 31 32 empty := ExpirePolicy{Tags: e.Tags} 33 return reflect.DeepEqual(e, empty) 34 } 35 36 // ymdh returns an integer in the form YYYYMMDDHH. 37 func ymdh(d time.Time, _ int) int { 38 return d.Year()*1000000 + int(d.Month())*10000 + d.Day()*100 + d.Hour() 39 } 40 41 // ymd returns an integer in the form YYYYMMDD. 42 func ymd(d time.Time, _ int) int { 43 return d.Year()*10000 + int(d.Month())*100 + d.Day() 44 } 45 46 // yw returns an integer in the form YYYYWW, where WW is the week number. 47 func yw(d time.Time, _ int) int { 48 year, week := d.ISOWeek() 49 return year*100 + week 50 } 51 52 // ym returns an integer in the form YYYYMM. 53 func ym(d time.Time, _ int) int { 54 return d.Year()*100 + int(d.Month()) 55 } 56 57 // y returns the year of d. 58 func y(d time.Time, _ int) int { 59 return d.Year() 60 } 61 62 // always returns a unique number for d. 63 func always(d time.Time, nr int) int { 64 return nr 65 } 66 67 // ApplyPolicy returns the snapshots from list that are to be kept and removed 68 // according to the policy p. list is sorted in the process. 69 func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots) { 70 sort.Sort(list) 71 72 if p.Empty() { 73 return list, remove 74 } 75 76 if len(list) == 0 { 77 return list, remove 78 } 79 80 var buckets = [6]struct { 81 Count int 82 bucker func(d time.Time, nr int) int 83 Last int 84 }{ 85 {p.Last, always, -1}, 86 {p.Hourly, ymdh, -1}, 87 {p.Daily, ymd, -1}, 88 {p.Weekly, yw, -1}, 89 {p.Monthly, ym, -1}, 90 {p.Yearly, y, -1}, 91 } 92 93 for nr, cur := range list { 94 var keepSnap bool 95 96 // Tags are handled specially as they are not counted. 97 for _, l := range p.Tags { 98 if cur.HasTags(l) { 99 keepSnap = true 100 } 101 } 102 103 // Now update the other buckets and see if they have some counts left. 104 for i, b := range buckets { 105 if b.Count > 0 { 106 val := b.bucker(cur.Time, nr) 107 if val != b.Last { 108 keepSnap = true 109 buckets[i].Last = val 110 buckets[i].Count-- 111 } 112 } 113 } 114 115 if keepSnap { 116 keep = append(keep, cur) 117 } else { 118 remove = append(remove, cur) 119 } 120 } 121 122 return keep, remove 123 }