Files
mesh-catalog/modules/zfs/cmd/zfs-tools/apply_properties_test.go
T
2026-11-13 00:27:31 +01:00

101 lines
4.2 KiB
Go

package main
import (
"context"
"strings"
"testing"
)
// §16, §31 convergence table: a property at its value runs nothing; a set one is set and read back and
// said; `default` is ZFS's default for a pool property and an inherit for a dataset's; a read-back that
// differs is a failure.
func TestPropertiesConvergeFromWhatZFSSays(t *testing.T) {
cfg := func(j string) *Config {
c, ref := ParseConfig([]byte(j), 16000)
if len(ref) != 0 {
t.Fatal(ref)
}
return c
}
type step struct{ cmd, out string }
cases := []struct {
name string
conf string
reads []step // read answers
writes []string
after map[string]string // a read that changes once the write ran
events int
fails bool
}{
{"pool property at its value", `{"pool":"tank","pool-properties":{"autotrim":"on"}}`,
[]step{{"zpool get -Hp -o value autotrim -- tank", "on\n"}}, nil, nil, 0, false},
{"pool property set", `{"pool":"tank","pool-properties":{"autotrim":"on"}}`,
[]step{{"zpool get -Hp -o value autotrim -- tank", "off\n"}}, []string{"zpool set autotrim=on -- tank"},
map[string]string{"zpool get -Hp -o value autotrim -- tank": "on\n"}, 1, false},
{"pool property to ZFS's default", `{"pool":"tank","pool-properties":{"failmode":"default"}}`,
[]step{{"zpool get -Hp -o value failmode -- tank", "continue\n"}}, []string{"zpool set failmode=wait -- tank"},
map[string]string{"zpool get -Hp -o value failmode -- tank": "wait\n"}, 1, false},
{"pool property read back differs", `{"pool":"tank","pool-properties":{"autotrim":"on"}}`,
[]step{{"zpool get -Hp -o value autotrim -- tank", "off\n"}}, []string{"zpool set autotrim=on -- tank"}, nil, 0, true},
{"dataset size at its value in bytes", `{"pool":"tank","dataset-properties":{"tank/a":{"recordsize":"1M"}}}`,
[]step{{"zfs get -Hp -o value,source recordsize -- tank/a", "1048576\tlocal\n"}}, nil, nil, 0, false},
{"dataset compression set", `{"pool":"tank","dataset-properties":{"tank":{"compression":"zstd-3"}}}`,
[]step{{"zfs get -Hp -o value,source compression -- tank", "lz4\tlocal\n"}}, []string{"zfs set compression=zstd-3 -- tank"},
map[string]string{"zfs get -Hp -o value,source compression -- tank": "zstd-3\tlocal\n"}, 1, false},
{"dataset default of an inherited one", `{"pool":"tank","dataset-properties":{"tank/a":{"atime":"default"}}}`,
[]step{{"zfs get -Hp -o value,source atime -- tank/a", "on\tinherited from tank\n"}}, nil, nil, 0, false},
{"dataset default of a local one", `{"pool":"tank","dataset-properties":{"tank/a":{"atime":"default"}}}`,
[]step{{"zfs get -Hp -o value,source atime -- tank/a", "off\tlocal\n"}}, []string{"zfs inherit atime -- tank/a"},
map[string]string{"zfs get -Hp -o value,source atime -- tank/a": "on\tdefault\n"}, 1, false},
{"quota none at zero", `{"pool":"tank","dataset-properties":{"tank/a":{"quota":"none"}}}`,
[]step{{"zfs get -Hp -o value,source quota -- tank/a", "0\tdefault\n"}}, nil, nil, 0, false},
}
for _, c := range cases {
f := newFake(t)
f.uid = 0
for _, r := range c.reads {
f.on(r.cmd, r.out)
}
wrote := map[string]bool{}
for _, w := range c.writes {
w := w
f.on(w, "")
f.after[w] = func(f *fake) {
wrote[w] = true
for k, v := range c.after {
f.on(k, v)
}
}
}
m := f.machine()
m.States = memStates{}
err := m.ApplyProperties(context.Background(), cfg(c.conf))
if (err != nil) != c.fails {
t.Errorf("%s: error %v", c.name, err)
}
for _, w := range c.writes {
if !wrote[w] {
t.Errorf("%s: %q not run; ran %v", c.name, w, f.calls)
}
}
for _, call := range f.calls {
if (strings.Contains(call, " set ") || strings.Contains(call, " inherit ")) && !contains(c.writes, call) {
t.Errorf("%s: ran %q", c.name, call)
}
}
if len(f.emitted) != c.events {
t.Errorf("%s: %d events, want %d: %v", c.name, len(f.emitted), c.events, f.emitted)
}
}
}
// §10: a dataset's sharenfs and sharesmb are never converged, never run: the parse refuses them, so the
// apply has no Config to run.
func TestSharenfsAndSharesmbAreNeverASetProperty(t *testing.T) {
for _, p := range []string{"sharenfs", "sharesmb"} {
if c, ref := ParseConfig([]byte(`{"pool":"tank","dataset-properties":{"tank":{"`+p+`":"on"}}}`), 16000); c != nil || len(ref) == 0 {
t.Errorf("%s accepted", p)
}
}
}