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  }