mirror of
https://github.com/zrepl/zrepl.git
synced 2025-04-01 03:27:21 +02:00
start pruning reimplementation in cmd/pruning subpackage
This commit is contained in:
parent
b4ea5f56b2
commit
ecd9db4ac6
17
cmd/pruning/keep_helpers.go
Normal file
17
cmd/pruning/keep_helpers.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package pruning
|
||||||
|
|
||||||
|
func filterSnapList(snaps []Snapshot, predicate func(Snapshot) bool) []Snapshot {
|
||||||
|
r := make([]Snapshot, 0, len(snaps))
|
||||||
|
for i := range snaps {
|
||||||
|
if predicate(snaps[i]) {
|
||||||
|
r = append(r, snaps[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func shallowCopySnapList(snaps []Snapshot) []Snapshot {
|
||||||
|
c := make([]Snapshot, len(snaps))
|
||||||
|
copy(c, snaps)
|
||||||
|
return c
|
||||||
|
}
|
22
cmd/pruning/keep_helpers_test.go
Normal file
22
cmd/pruning/keep_helpers_test.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package pruning
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShallowCopySnapList(t *testing.T) {
|
||||||
|
|
||||||
|
l1 := []Snapshot{
|
||||||
|
stubSnap{name: "foo"},
|
||||||
|
stubSnap{name: "bar"},
|
||||||
|
}
|
||||||
|
l2 := shallowCopySnapList(l1)
|
||||||
|
|
||||||
|
assert.Equal(t, l1, l2)
|
||||||
|
|
||||||
|
l1[0] = stubSnap{name: "baz"}
|
||||||
|
assert.Equal(t, "baz", l1[0].Name())
|
||||||
|
assert.Equal(t, "foo", l2[0].Name())
|
||||||
|
|
||||||
|
}
|
32
cmd/pruning/keep_last_n.go
Normal file
32
cmd/pruning/keep_last_n.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package pruning
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeepLastN struct {
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeepLastN(n int) (*KeepLastN, error) {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil, errors.Errorf("must specify positive number as 'keep last count', got %d", n)
|
||||||
|
}
|
||||||
|
return &KeepLastN{n}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeepLastN) KeepRule(snaps []Snapshot) []Snapshot {
|
||||||
|
|
||||||
|
if k.n > len(snaps) {
|
||||||
|
return snaps
|
||||||
|
}
|
||||||
|
|
||||||
|
res := shallowCopySnapList(snaps)
|
||||||
|
|
||||||
|
sort.Slice(res, func(i, j int) bool {
|
||||||
|
return res[i].Date().After(res[j].Date())
|
||||||
|
})
|
||||||
|
|
||||||
|
return res[:k.n]
|
||||||
|
}
|
33
cmd/pruning/keep_prefix.go
Normal file
33
cmd/pruning/keep_prefix.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package pruning
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeepRegex struct {
|
||||||
|
expr *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ KeepRule = &KeepRegex{}
|
||||||
|
|
||||||
|
func NewKeepRegex(expr string) (*KeepRegex, error) {
|
||||||
|
re, err := regexp.Compile(expr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &KeepRegex{re}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustKeepRegex(expr string) *KeepRegex {
|
||||||
|
k, err := NewKeepRegex(expr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KeepRegex) KeepRule(snaps []Snapshot) []Snapshot {
|
||||||
|
return filterSnapList(snaps, func(s Snapshot) bool {
|
||||||
|
return k.expr.FindStringIndex(s.Name()) == nil
|
||||||
|
})
|
||||||
|
}
|
39
cmd/pruning/pruning.go
Normal file
39
cmd/pruning/pruning.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package pruning
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeepRule interface {
|
||||||
|
KeepRule(snaps []Snapshot) []Snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
type Snapshot interface {
|
||||||
|
Name() string
|
||||||
|
Replicated() bool
|
||||||
|
Date() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func PruneSnapshots(snaps []Snapshot, keepRules []KeepRule) []Snapshot {
|
||||||
|
|
||||||
|
if len(keepRules) == 0 {
|
||||||
|
return snaps
|
||||||
|
}
|
||||||
|
|
||||||
|
remCount := make(map[Snapshot]int, len(snaps))
|
||||||
|
for _, r := range keepRules {
|
||||||
|
ruleRems := r.KeepRule(snaps)
|
||||||
|
for _, ruleRem := range ruleRems {
|
||||||
|
remCount[ruleRem]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove := make([]Snapshot, 0, len(snaps))
|
||||||
|
for snap, rc := range remCount {
|
||||||
|
if rc == len(keepRules) {
|
||||||
|
remove = append(remove, snap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return remove
|
||||||
|
}
|
88
cmd/pruning/pruning_test.go
Normal file
88
cmd/pruning/pruning_test.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package pruning
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stubSnap struct {
|
||||||
|
name string
|
||||||
|
replicated bool
|
||||||
|
date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s stubSnap) Name() string { return s.name }
|
||||||
|
|
||||||
|
func (s stubSnap) Replicated() bool { return s.replicated }
|
||||||
|
|
||||||
|
func (s stubSnap) Date() time.Time { return s.date }
|
||||||
|
|
||||||
|
func TestPruneSnapshots(t *testing.T) {
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
inputs []Snapshot
|
||||||
|
rules []KeepRule
|
||||||
|
exp, eff []Snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs := map[string][]Snapshot{
|
||||||
|
"s1": []Snapshot{
|
||||||
|
stubSnap{name: "foo_123"},
|
||||||
|
stubSnap{name: "foo_456"},
|
||||||
|
stubSnap{name: "bar_123"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tcs := map[string]testCase{
|
||||||
|
"simple": {
|
||||||
|
inputs: inputs["s1"],
|
||||||
|
rules: []KeepRule{
|
||||||
|
MustKeepRegex("foo_"),
|
||||||
|
},
|
||||||
|
exp: []Snapshot{
|
||||||
|
stubSnap{name: "bar_123"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"multipleRules": {
|
||||||
|
inputs: inputs["s1"],
|
||||||
|
rules: []KeepRule{
|
||||||
|
MustKeepRegex("foo_"),
|
||||||
|
MustKeepRegex("bar_"),
|
||||||
|
},
|
||||||
|
exp: []Snapshot{},
|
||||||
|
},
|
||||||
|
"onlyThoseRemovedByAllAreRemoved": {
|
||||||
|
inputs: inputs["s1"],
|
||||||
|
rules: []KeepRule{
|
||||||
|
MustKeepRegex("notInS1"), // would remove all
|
||||||
|
MustKeepRegex("bar_"), // would remove all but bar_, i.e. foo_.*
|
||||||
|
},
|
||||||
|
exp: []Snapshot{
|
||||||
|
stubSnap{name: "foo_123"},
|
||||||
|
stubSnap{name: "foo_456"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"noRulesKeepsAll": {
|
||||||
|
inputs: inputs["s1"],
|
||||||
|
rules: []KeepRule{},
|
||||||
|
exp: inputs["s1"],
|
||||||
|
},
|
||||||
|
"noSnaps": {
|
||||||
|
inputs: []Snapshot{},
|
||||||
|
rules: []KeepRule{
|
||||||
|
MustKeepRegex("foo_"),
|
||||||
|
},
|
||||||
|
exp: []Snapshot{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range tcs {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
tc := tcs[name]
|
||||||
|
tc.eff = PruneSnapshots(tc.inputs, tc.rules)
|
||||||
|
assert.Equal(t, tc.exp, tc.eff)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user