zrepl/pruning/keep_grid.go

117 lines
2.9 KiB
Go
Raw Permalink Normal View History

2018-09-24 17:30:03 +02:00
package pruning
import (
"fmt"
"regexp"
"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
}
// 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
}
// Evaluate retention grid
entrySlice := make([]retentiongrid.Entry, 0)
for i := range matching {
entrySlice = append(entrySlice, matching[i])
2018-09-24 17:30:03 +02:00
}
_, gridDestroyList := p.retentionGrid.FitEntries(entrySlice)
2018-09-24 17:30:03 +02:00
// Revert adaptors
for i := range gridDestroyList {
destroyList = append(destroyList, gridDestroyList[i].(Snapshot))
2018-09-24 17:30:03 +02:00
}
return destroyList
}