zrepl/pruning/keep_grid.go

134 lines
3.3 KiB
Go
Raw Normal View History

2018-09-24 17:30:03 +02:00
package pruning
import (
"fmt"
"regexp"
"sort"
"time"
2019-03-22 19:41:12 +01:00
"github.com/pkg/errors"
"github.com/zrepl/zrepl/config"
"github.com/zrepl/zrepl/pruning/retentiongrid"
2018-09-24 17:30:03 +02:00
)
// KeepGrid fits snapshots that match a given regex into a retentiongrid.Grid,
// uses the most recent snapshot among those that match the regex as 'now',
// and deletes all snapshots that do not fit the grid specification.
type KeepGrid struct {
retentionGrid *retentiongrid.Grid
2019-03-22 19:41:12 +01:00
re *regexp.Regexp
2018-09-24 17:30:03 +02:00
}
func NewKeepGrid(in *config.PruneGrid) (p *KeepGrid, err error) {
if in.Regex == "" {
return nil, fmt.Errorf("Regex must not be empty")
}
re, err := regexp.Compile(in.Regex)
if err != nil {
return nil, errors.Wrap(err, "Regex is invalid")
}
return newKeepGrid(re, in.Grid)
}
func MustNewKeepGrid(regex, gridspec string) *KeepGrid {
ris, err := config.ParseRetentionIntervalSpec(gridspec)
if err != nil {
panic(err)
}
re := regexp.MustCompile(regex)
grid, err := newKeepGrid(re, ris)
if err != nil {
panic(err)
}
return grid
}
func newKeepGrid(re *regexp.Regexp, configIntervals []config.RetentionInterval) (*KeepGrid, error) {
if re == nil {
panic("re must not be nil")
}
if len(configIntervals) == 0 {
return nil, errors.New("retention grid must specify at least one interval")
}
intervals := make([]retentiongrid.Interval, len(configIntervals))
for i := range configIntervals {
intervals[i] = &configIntervals[i]
}
2018-09-24 17:30:03 +02:00
// Assert intervals are of increasing length (not necessarily required, but indicates config mistake)
lastDuration := time.Duration(0)
for i := range intervals {
2018-09-24 17:30:03 +02:00
if intervals[i].Length() < lastDuration {
2018-09-24 17:30:03 +02:00
// If all intervals before were keep=all, this is ok
allPrevKeepCountAll := true
for j := i - 1; allPrevKeepCountAll && j >= 0; j-- {
allPrevKeepCountAll = intervals[j].KeepCount() == config.RetentionGridKeepCountAll
2018-09-24 17:30:03 +02:00
}
if allPrevKeepCountAll {
goto isMonotonicIncrease
}
return nil, errors.New("retention grid interval length must be monotonically increasing")
2018-09-24 17:30:03 +02:00
}
isMonotonicIncrease:
lastDuration = intervals[i].Length()
2018-09-24 17:30:03 +02:00
}
return &KeepGrid{
retentionGrid: retentiongrid.NewGrid(intervals),
re: re,
2018-09-24 17:30:03 +02:00
}, nil
}
type retentionGridAdaptor struct {
Snapshot
}
func (a retentionGridAdaptor) LessThan(b retentiongrid.Entry) bool {
return a.Date().Before(b.Date())
}
// Prune filters snapshots with the retention grid.
func (p *KeepGrid) KeepRule(snaps []Snapshot) (destroyList []Snapshot) {
matching, notMatching := partitionSnapList(snaps, func(snapshot Snapshot) bool {
2018-09-24 17:30:03 +02:00
return p.re.MatchString(snapshot.Name())
})
// snaps that don't match the regex are not kept by this rule
destroyList = append(destroyList, notMatching...)
if len(matching) == 0 {
return destroyList
2018-09-24 17:30:03 +02:00
}
// Build adaptors for retention grid
adaptors := make([]retentiongrid.Entry, 0)
for i := range matching {
adaptors = append(adaptors, retentionGridAdaptor{matching[i]})
2018-09-24 17:30:03 +02:00
}
// determine 'now' edge
sort.SliceStable(adaptors, func(i, j int) bool {
return adaptors[i].LessThan(adaptors[j])
})
now := adaptors[len(adaptors)-1].Date()
// Evaluate retention grid
_, removea := p.retentionGrid.FitEntries(now, adaptors)
// Revert adaptors
for i := range removea {
destroyList = append(destroyList, removea[i].(retentionGridAdaptor).Snapshot)
2018-09-24 17:30:03 +02:00
}
return destroyList
}