2017-07-06 13:03:44 +02:00
|
|
|
package cmd
|
2017-04-26 20:21:18 +02:00
|
|
|
|
|
|
|
import (
|
2017-04-26 20:25:53 +02:00
|
|
|
"testing"
|
2017-06-22 19:04:48 +02:00
|
|
|
"time"
|
2017-08-05 21:15:37 +02:00
|
|
|
|
2017-09-13 22:55:10 +02:00
|
|
|
"github.com/kr/pretty"
|
2017-08-05 21:15:37 +02:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/zrepl/zrepl/util"
|
|
|
|
"github.com/zrepl/zrepl/zfs"
|
2017-04-26 20:21:18 +02:00
|
|
|
)
|
|
|
|
|
2017-09-10 16:13:05 +02:00
|
|
|
func TestSampleConfigsAreParsedWithoutErrors(t *testing.T) {
|
|
|
|
|
2017-09-11 15:45:10 +02:00
|
|
|
paths := []string{
|
2017-09-10 16:13:05 +02:00
|
|
|
"./sampleconf/localbackup/host1.yml",
|
|
|
|
"./sampleconf/pullbackup/backuphost.yml",
|
|
|
|
"./sampleconf/pullbackup/productionhost.yml",
|
2017-09-11 15:45:10 +02:00
|
|
|
"./sampleconf/random/debugging.yml",
|
2018-04-14 11:24:47 +02:00
|
|
|
"./sampleconf/random/logging_and_monitoring.yml",
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, p := range paths {
|
|
|
|
|
2017-09-24 14:34:50 +02:00
|
|
|
c, err := ParseConfig(p)
|
2017-09-10 16:13:05 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error parsing %s:\n%+v", p, err)
|
|
|
|
}
|
2017-09-24 14:34:50 +02:00
|
|
|
|
|
|
|
t.Logf("file: %s", p)
|
|
|
|
t.Log(pretty.Sprint(c))
|
|
|
|
|
2017-09-10 16:13:05 +02:00
|
|
|
}
|
|
|
|
|
2017-04-26 20:21:18 +02:00
|
|
|
}
|
2017-06-22 19:04:48 +02:00
|
|
|
|
|
|
|
func TestParseRetentionGridStringParsing(t *testing.T) {
|
|
|
|
|
2017-07-09 00:38:16 +02:00
|
|
|
intervals, err := parseRetentionGridIntervalsString("2x10m(keep=2) | 1x1h | 3x1w")
|
2017-06-22 19:04:48 +02:00
|
|
|
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Len(t, intervals, 6)
|
|
|
|
proto := util.RetentionInterval{
|
|
|
|
KeepCount: 2,
|
|
|
|
Length: 10 * time.Minute,
|
|
|
|
}
|
|
|
|
assert.EqualValues(t, proto, intervals[0])
|
|
|
|
assert.EqualValues(t, proto, intervals[1])
|
|
|
|
|
|
|
|
proto.KeepCount = 1
|
|
|
|
proto.Length = 1 * time.Hour
|
|
|
|
assert.EqualValues(t, proto, intervals[2])
|
|
|
|
|
|
|
|
proto.Length = 7 * 24 * time.Hour
|
|
|
|
assert.EqualValues(t, proto, intervals[3])
|
|
|
|
assert.EqualValues(t, proto, intervals[4])
|
|
|
|
assert.EqualValues(t, proto, intervals[5])
|
|
|
|
|
|
|
|
intervals, err = parseRetentionGridIntervalsString("|")
|
|
|
|
assert.Error(t, err)
|
2017-07-09 00:38:16 +02:00
|
|
|
intervals, err = parseRetentionGridIntervalsString("2x10m")
|
2017-06-22 19:04:48 +02:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2017-07-09 00:38:16 +02:00
|
|
|
intervals, err = parseRetentionGridIntervalsString("1x10m(keep=all)")
|
2017-06-22 19:04:48 +02:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Len(t, intervals, 1)
|
|
|
|
assert.EqualValues(t, util.RetentionGridKeepCountAll, intervals[0].KeepCount)
|
|
|
|
|
|
|
|
}
|
2017-08-05 21:15:37 +02:00
|
|
|
|
|
|
|
func TestDatasetMapFilter(t *testing.T) {
|
|
|
|
|
|
|
|
expectMapping := func(m map[string]string, from, to string) {
|
|
|
|
dmf, err := parseDatasetMapFilter(m, false)
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("expect test map to be valid: %s", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
fromPath, err := zfs.NewDatasetPath(from)
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("expect test from path to be valid: %s", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2017-09-16 20:30:29 +02:00
|
|
|
|
|
|
|
res, err := dmf.Map(fromPath)
|
|
|
|
if to == "" {
|
|
|
|
assert.Nil(t, res)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
t.Logf("%s => NOT MAPPED", fromPath.ToString())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.Nil(t, err)
|
2017-08-05 21:15:37 +02:00
|
|
|
toPath, err := zfs.NewDatasetPath(to)
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("expect test to path to be valid: %s", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
assert.True(t, res.Equal(toPath))
|
|
|
|
}
|
|
|
|
|
|
|
|
expectFilter := func(m map[string]string, path string, pass bool) {
|
|
|
|
dmf, err := parseDatasetMapFilter(m, true)
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("expect test filter to be valid: %s", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
p, err := zfs.NewDatasetPath(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Logf("expect test path to be valid: %s", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
res, err := dmf.Filter(p)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, pass, res)
|
|
|
|
}
|
|
|
|
|
|
|
|
map1 := map[string]string{
|
2017-09-16 20:30:29 +02:00
|
|
|
"a/b/c<": "root1",
|
|
|
|
"a/b<": "root2",
|
|
|
|
"<": "root3/b/c",
|
|
|
|
"b": "!",
|
|
|
|
"a/b/c/d/e<": "!",
|
|
|
|
"q<": "root4/1/2",
|
2017-08-05 21:15:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
expectMapping(map1, "a/b/c", "root1")
|
|
|
|
expectMapping(map1, "a/b/c/d", "root1/d")
|
2017-09-16 20:30:29 +02:00
|
|
|
expectMapping(map1, "a/b/c/d/e", "")
|
2017-08-05 21:15:37 +02:00
|
|
|
expectMapping(map1, "a/b/e", "root2/e")
|
|
|
|
expectMapping(map1, "a/b", "root2")
|
2017-10-05 19:58:43 +02:00
|
|
|
expectMapping(map1, "x", "root3/b/c/x")
|
|
|
|
expectMapping(map1, "x/y", "root3/b/c/x/y")
|
2017-08-05 21:15:37 +02:00
|
|
|
expectMapping(map1, "q", "root4/1/2")
|
2017-09-16 20:30:29 +02:00
|
|
|
expectMapping(map1, "b", "")
|
2017-08-05 21:15:37 +02:00
|
|
|
expectMapping(map1, "q/r", "root4/1/2/r")
|
|
|
|
|
2018-06-20 20:20:37 +02:00
|
|
|
map2 := map[string]string{ // identity mapping
|
2018-08-25 21:30:25 +02:00
|
|
|
"<": "",
|
2018-06-20 20:20:37 +02:00
|
|
|
}
|
|
|
|
expectMapping(map2, "foo/bar", "foo/bar")
|
|
|
|
|
|
|
|
map3 := map[string]string{ // subtree to local mapping, need that for Invert()
|
|
|
|
"foo/bar<": "",
|
|
|
|
}
|
|
|
|
{
|
|
|
|
m, _ := parseDatasetMapFilter(map3, false)
|
|
|
|
p, _ := zfs.NewDatasetPath("foo/bar")
|
|
|
|
tp, err := m.Map(p)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.True(t, tp.Empty())
|
|
|
|
|
|
|
|
expectMapping(map3, "foo/bar/x", "x")
|
|
|
|
expectMapping(map3, "x", "")
|
|
|
|
}
|
|
|
|
|
2017-08-05 21:15:37 +02:00
|
|
|
filter1 := map[string]string{
|
2017-09-16 20:30:29 +02:00
|
|
|
"<": "!",
|
2017-08-05 21:15:37 +02:00
|
|
|
"a<": "ok",
|
2017-09-16 20:30:29 +02:00
|
|
|
"a/b<": "!",
|
2017-08-05 21:15:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
expectFilter(filter1, "b", false)
|
|
|
|
expectFilter(filter1, "a", true)
|
|
|
|
expectFilter(filter1, "a/d", true)
|
|
|
|
expectFilter(filter1, "a/b", false)
|
|
|
|
expectFilter(filter1, "a/b/c", false)
|
|
|
|
|
|
|
|
filter2 := map[string]string{}
|
|
|
|
expectFilter(filter2, "foo", false) // default to omit
|
|
|
|
|
|
|
|
}
|
2017-09-13 22:55:10 +02:00
|
|
|
|
2017-09-16 20:30:29 +02:00
|
|
|
func TestDatasetMapFilter_AsFilter(t *testing.T) {
|
|
|
|
|
|
|
|
mapspec := map[string]string{
|
|
|
|
"a/b/c<": "root1",
|
|
|
|
"a/b<": "root2",
|
|
|
|
"<": "root3/b/c",
|
|
|
|
"b": "!",
|
|
|
|
"a/b/c/d/e<": "!",
|
|
|
|
"q<": "root4/1/2",
|
|
|
|
}
|
|
|
|
|
|
|
|
m, err := parseDatasetMapFilter(mapspec, false)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
|
|
|
|
f := m.AsFilter()
|
|
|
|
|
|
|
|
t.Logf("Mapping:\n%s\nFilter:\n%s", pretty.Sprint(m), pretty.Sprint(f))
|
|
|
|
|
|
|
|
tf := func(f zfs.DatasetFilter, path string, pass bool) {
|
|
|
|
p, err := zfs.NewDatasetPath(path)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
r, err := f.Filter(p)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, pass, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
tf(f, "a/b/c", true)
|
|
|
|
tf(f, "a/b", true)
|
|
|
|
tf(f, "b", false)
|
|
|
|
tf(f, "a/b/c/d/e", false)
|
|
|
|
tf(f, "a/b/c/d/e/f", false)
|
|
|
|
tf(f, "a", true)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-09-13 22:55:10 +02:00
|
|
|
func TestDatasetMapFilter_InvertedFilter(t *testing.T) {
|
|
|
|
mapspec := map[string]string{
|
|
|
|
"a/b": "1/2",
|
|
|
|
"a/b/c<": "3",
|
|
|
|
"a/b/c/d<": "1/2/a",
|
2017-09-16 20:30:29 +02:00
|
|
|
"a/b/d": "!",
|
2017-09-13 22:55:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m, err := parseDatasetMapFilter(mapspec, false)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
|
|
|
|
inv, err := m.InvertedFilter()
|
|
|
|
assert.Nil(t, err)
|
|
|
|
|
|
|
|
t.Log(pretty.Sprint(inv))
|
|
|
|
|
|
|
|
expectMapping := func(m *DatasetMapFilter, ps string, expRes bool) {
|
|
|
|
p, err := zfs.NewDatasetPath(ps)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
r, err := m.Filter(p)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, expRes, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
expectMapping(inv, "4", false)
|
|
|
|
expectMapping(inv, "3", true)
|
|
|
|
expectMapping(inv, "3/x", true)
|
|
|
|
expectMapping(inv, "1", false)
|
|
|
|
expectMapping(inv, "1/2", true)
|
|
|
|
expectMapping(inv, "1/2/3", false)
|
|
|
|
expectMapping(inv, "1/2/a/b", true)
|
|
|
|
|
|
|
|
}
|
2018-06-20 20:20:37 +02:00
|
|
|
|
|
|
|
func TestDatasetMapFilter_Invert(t *testing.T) {
|
|
|
|
|
|
|
|
mapspec := map[string]string{
|
|
|
|
"<": "foo/bar",
|
|
|
|
}
|
|
|
|
|
|
|
|
m, err := parseDatasetMapFilter(mapspec, false)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2018-08-25 13:02:38 +02:00
|
|
|
invI, err := m.Invert()
|
2018-06-20 20:20:37 +02:00
|
|
|
assert.NoError(t, err)
|
2018-08-25 13:02:38 +02:00
|
|
|
inv, ok := invI.(*DatasetMapFilter)
|
|
|
|
assert.True(t, ok)
|
2018-06-20 20:20:37 +02:00
|
|
|
|
|
|
|
expectMapping := func(m *DatasetMapFilter, input, expect string, expErr bool, expEmpty bool) {
|
|
|
|
p, err := zfs.NewDatasetPath(input)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
r, err := m.Map(p)
|
|
|
|
if expErr {
|
|
|
|
assert.Nil(t, r)
|
|
|
|
assert.Error(t, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if expEmpty {
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.True(t, r.Empty())
|
|
|
|
} else if expect == "" {
|
|
|
|
assert.Nil(t, r)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
} else {
|
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.NotNil(t, r)
|
|
|
|
assert.Equal(t, expect, r.ToString())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expectMapping(inv, "x", "", false, false)
|
|
|
|
expectMapping(inv, "foo/bar", "", false, true)
|
|
|
|
expectMapping(inv, "foo/bar/bee", "bee", false, false)
|
|
|
|
|
2018-08-25 21:30:25 +02:00
|
|
|
}
|