A trim schedule of weekly or monthly with no trim-day or trim-time is one refused value naming it; a keep count of 0 is that kind's none: the existing scheduled ZFS snapshots are kept and a warning names them. Builds: design 42 zfs-owns-zfs §20, §21, §22, A-33, A-35
99 lines
6.2 KiB
Go
99 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Design 42 zfs-owns-zfs §11: each value of a fixed form, not managed until set and not empty; a value of
|
|
// another form is refused before anything is written or run (§31, one table per value).
|
|
|
|
func TestEachValueHasItsFormAndABlankOneIsNotManaged(t *testing.T) {
|
|
bad := func(key, val string) string { return `{"pool":"tank","` + key + `":` + val + `}` }
|
|
refused := []struct{ name, conf, why string }{
|
|
{"pool with a space", `{"pool":"ta nk"}`, "pool's name"},
|
|
{"pool with a newline", `{"pool":"tank\nx"}`, "pool's name"},
|
|
{"pool not a string", `{"pool":3}`, "not a string"},
|
|
{"arc-max below 64", bad("arc-max", "63"), "below 64"},
|
|
{"arc-max at memory", bad("arc-max", "16000"), "not below the node's memory"},
|
|
{"arc-max fraction", bad("arc-max", "100.5"), "whole number"},
|
|
{"arc-max string", bad("arc-max", `"4096"`), "whole number"},
|
|
{"arc-min above arc-max", `{"arc-max":1000,"arc-min":2000}`, "above arc-max"},
|
|
{"module option of arc", bad("module-options", `["zfs_arc_max=1"]`), "arc-max and arc-min"},
|
|
{"module option unknown", bad("module-options", `["zfs_foo=1"]`), "not a parameter"},
|
|
{"module option not digits", bad("module-options", `["zfs_txg_timeout=5;x"]`), "not name=digits"},
|
|
{"module option twice", bad("module-options", `["zfs_txg_timeout=5","zfs_txg_timeout=6"]`), "twice"},
|
|
{"hostid short", bad("hostid", `"abc"`), "eight hexadecimal"},
|
|
{"hostid zero", bad("hostid", `"00000000"`), "all zero"},
|
|
{"hostid backtick", bad("hostid", "\"0123456`\""), "eight hexadecimal"},
|
|
{"zed value", bad("zed", `"yes"`), "on, off"},
|
|
{"zed handler", bad("zed-handlers", `["notify"]`), "not all-syslog"},
|
|
{"trim schedule", bad("trim-schedule", `"daily"`), "none, weekly, monthly"},
|
|
{"trim day", bad("trim-day", `"Monday"`), "Mon"},
|
|
{"trim time", bad("trim-time", `"24:00"`), "HH:MM"},
|
|
{"trim schedule without day or time", `{"trim-schedule":"weekly","trim-time":"03:00"}`, "needs trim-day"},
|
|
{"trim schedule without time", `{"trim-schedule":"monthly","trim-day":"Sun"}`, "needs trim-time"},
|
|
{"cache device with a slash", bad("cache-devices", `["a/b"]`), "under /dev/disk/by-id"},
|
|
{"cache device with a dollar", bad("cache-devices", `["a$b"]`), "under /dev/disk/by-id"},
|
|
{"cache device with a quote", bad("cache-devices", `["a\"b"]`), "under /dev/disk/by-id"},
|
|
{"log device with a semicolon", bad("log-devices", `["a;b"]`), "under /dev/disk/by-id"},
|
|
{"log mirror of one", `{"log-mirror":true,"log-devices":["a"]}`, "exactly two"},
|
|
{"log mirror not a boolean", bad("log-mirror", `"yes"`), "true or false"},
|
|
{"pool property", bad("pool-properties", `{"ashift":"12"}`), "not a pool property"},
|
|
{"pool property value", bad("pool-properties", `{"failmode":"loud"}`), "failmode=loud"},
|
|
{"dataset of another pool", bad("dataset-properties", `{"other/x":{"atime":"off"}}`), "not the pool's name"},
|
|
{"dataset property sharenfs", bad("dataset-properties", `{"tank":{"sharenfs":"on"}}`), "not a dataset property"},
|
|
{"dataset property sharesmb", bad("dataset-properties", `{"tank":{"sharesmb":"on"}}`), "not a dataset property"},
|
|
{"dataset recordsize", bad("dataset-properties", `{"tank/a":{"recordsize":"100K"}}`), "power of two"},
|
|
{"dataset compression", bad("dataset-properties", `{"tank/a":{"compression":"zstd-20"}}`), "compression"},
|
|
{"dataset quota", bad("dataset-properties", `{"tank/a":{"quota":"5X"}}`), "quota"},
|
|
{"dataset name with a snapshot mark", bad("dataset-properties", `{"tank/a@x":{"atime":"on"}}`), "dataset's name"},
|
|
{"snapshots above the bound", bad("snapshots", `{"hourly":169}`), "0 to 168"},
|
|
{"snapshots unknown kind", bad("snapshots", `{"yearly":1}`), "not none, hourly"},
|
|
{"snapshots none with counts", bad("snapshots", `{"none":true,"daily":1}`), "none is given"},
|
|
{"scrub cadence", bad("scrub-cadence", `"daily"`), "weekly, monthly"},
|
|
{"not an object", `[1]`, "not a JSON object"},
|
|
}
|
|
for _, c := range refused {
|
|
cfg, ref := ParseConfig([]byte(c.conf), 16000)
|
|
if cfg != nil || len(ref) == 0 || !strings.Contains(ref[0].Reason, c.why) {
|
|
t.Errorf("%s: %+v %+v, want a refusal saying %q", c.name, cfg, ref, c.why)
|
|
}
|
|
}
|
|
// Without the node's memory arc-max cannot be checked.
|
|
if _, ref := ParseConfig([]byte(`{"arc-max":1000}`), 0); len(ref) != 1 || ref[0].Key != "arc-max" {
|
|
t.Errorf("arc-max with no memory read: %v", ref)
|
|
}
|
|
// Absent, empty and null are not managed: no refusal, nothing set.
|
|
for _, conf := range []string{`{}`, `{"pool":"tank","cache-devices":[],"log-devices":[],"module-options":[],"zed-handlers":[],
|
|
"pool-properties":{},"dataset-properties":{},"snapshots":{},"hostid":"","zed":"","trim-schedule":"","arc-max":null}`} {
|
|
c, ref := ParseConfig([]byte(conf), 16000)
|
|
if len(ref) != 0 || c.ArcMax != nil || c.HostID != "" || c.Zed != "" || c.TrimSchedule != "" || c.Snapshots != nil ||
|
|
len(c.CacheDevices)+len(c.LogDevices)+len(c.ModuleOptions)+len(c.ZedHandlers)+len(c.PoolProperties)+len(c.DatasetProperties) != 0 {
|
|
t.Errorf("%s: %+v %v", conf, c, ref)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAWholeConfigIsReadValueByValue(t *testing.T) {
|
|
c, ref := ParseConfig([]byte(`{"pool":"tank","scrub-cadence":"weekly","scrub-day":"Mon","scrub-time":"00:00",
|
|
"arc-max":8192,"arc-min":1024,"module-options":["zfs_txg_timeout=10"],"hostid":"0A1B2C3D","zed":"on",
|
|
"zed-handlers":["all-syslog"],"trim-schedule":"weekly","trim-day":"Sun","trim-time":"03:00",
|
|
"cache-devices":["nvme-a","nvme-b"],"log-devices":["nvme-c","nvme-d"],"log-mirror":true,
|
|
"pool-properties":{"autotrim":"on","failmode":"default"},
|
|
"dataset-properties":{"tank":{"compression":"zstd-3"},"tank/media":{"recordsize":"1M","quota":"5T"}},
|
|
"snapshots":{"hourly":24,"daily":7}}`), 16000)
|
|
if len(ref) != 0 {
|
|
t.Fatal(ref)
|
|
}
|
|
if c.Pool != "tank" || *c.ArcMax != 8192 || *c.ArcMin != 1024 || c.HostID != "0a1b2c3d" || c.Zed != "on" ||
|
|
c.ModuleOptions[0] != (ModuleOption{"zfs_txg_timeout", "10"}) || len(c.CacheDevices) != 2 || !*c.LogMirror ||
|
|
c.PoolProperties["autotrim"] != "on" || c.DatasetProperties["tank/media"]["quota"] != "5T" ||
|
|
c.Snapshots.Of("hourly") != 24 || c.Snapshots.Of("monthly") != 0 || c.Snapshots.None {
|
|
t.Errorf("%+v", c)
|
|
}
|
|
if c, _ := ParseConfig([]byte(`{"snapshots":{"none":true}}`), 0); !c.Snapshots.None {
|
|
t.Errorf("none: %+v", c.Snapshots)
|
|
}
|
|
}
|