Compare commits
1
Commits
v1.0.0-rc2
...
83b68923d9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83b68923d9 |
@@ -662,6 +662,10 @@ A port entry's `hostnames` lists every name that resolves to its address,
|
|||||||
domains included. A port entry in the older format, with one `hostname` instead
|
domains included. A port entry in the older format, with one `hostname` instead
|
||||||
of the `hostnames` list, loads as a list of that one name.
|
of the `hostnames` list, loads as a list of that one name.
|
||||||
|
|
||||||
|
A state file that leaves out any of `domains`, `hostnames`, `ports` and
|
||||||
|
`certificates`, or holds `null` for one, loads with that section empty. A file
|
||||||
|
holding `{}` loads as empty state.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Entrypoints
|
## Entrypoints
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ trial run of the finished image: https://git.eeqj.de/sneak/dnswatcher/issues/149
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-13: a state file holding `{}`, or leaving out a section, loads with
|
||||||
|
that section empty; the first check no longer crashes on it (closes #267).
|
||||||
- 2026-10-13: a stop before the first scan completes sends no startup
|
- 2026-10-13: a stop before the first scan completes sends no startup
|
||||||
notification; a scan that ran to the end still sends it (closes #271).
|
notification; a scan that ran to the end still sends it (closes #271).
|
||||||
- 2026-10-13: a nameserver leaving a domain's delegation sends only NS Change;
|
- 2026-10-13: a nameserver leaving a domain's delegation sends only NS Change;
|
||||||
|
|||||||
@@ -216,6 +216,24 @@ func (s *State) Load() error {
|
|||||||
return fmt.Errorf("parsing state file: %w", err)
|
return fmt.Errorf("parsing state file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A section the file leaves out, or holds as null, loads empty: the
|
||||||
|
// setters write into these maps, and writing into a nil map panics.
|
||||||
|
if snapshot.Domains == nil {
|
||||||
|
snapshot.Domains = make(map[string]*DomainState)
|
||||||
|
}
|
||||||
|
|
||||||
|
if snapshot.Hostnames == nil {
|
||||||
|
snapshot.Hostnames = make(map[string]*HostnameState)
|
||||||
|
}
|
||||||
|
|
||||||
|
if snapshot.Ports == nil {
|
||||||
|
snapshot.Ports = make(map[string]*PortState)
|
||||||
|
}
|
||||||
|
|
||||||
|
if snapshot.Certificates == nil {
|
||||||
|
snapshot.Certificates = make(map[string]*CertificateState)
|
||||||
|
}
|
||||||
|
|
||||||
// A state file saved before each record value was stored once can
|
// A state file saved before each record value was stored once can
|
||||||
// hold a hostname's CNAME once for every record type asked for.
|
// hold a hostname's CNAME once for every record type asked for.
|
||||||
// Each value is kept once, so the first check does not see a
|
// Each value is kept once, so the first check does not see a
|
||||||
|
|||||||
@@ -695,6 +695,58 @@ func TestLoadMissingFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestLoadFileWithoutSections loads a state file holding {}, and files
|
||||||
|
// that each leave out one section. Every section loads as an empty map.
|
||||||
|
// TestFirstCheckAfterLoadingFileWithoutSections in the watcher tests
|
||||||
|
// runs the first check after such a load.
|
||||||
|
func TestLoadFileWithoutSections(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
file string
|
||||||
|
}{
|
||||||
|
{"no sections", `{}`},
|
||||||
|
{"no domains", `{"hostnames": {}, "ports": {}, "certificates": {}}`},
|
||||||
|
{"no hostnames", `{"domains": {}, "ports": {}, "certificates": {}}`},
|
||||||
|
{"no ports", `{"domains": {}, "hostnames": {}, "certificates": {}}`},
|
||||||
|
{"no certificates", `{"domains": {}, "hostnames": {}, "ports": {}}`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
err := os.WriteFile(
|
||||||
|
filepath.Join(dir, "state.json"), []byte(tt.file), 0o600,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("writing state file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := state.NewForTestWithDataDir(dir)
|
||||||
|
|
||||||
|
err = s.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := state.Snapshot{
|
||||||
|
Domains: map[string]*state.DomainState{},
|
||||||
|
Hostnames: map[string]*state.HostnameState{},
|
||||||
|
Ports: map[string]*state.PortState{},
|
||||||
|
Certificates: map[string]*state.CertificateState{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := s.GetSnapshot(); !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("loaded %#v, want %#v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestLoadCorruptFile verifies that a corrupt state file returns an error.
|
// TestLoadCorruptFile verifies that a corrupt state file returns an error.
|
||||||
func TestLoadCorruptFile(t *testing.T) {
|
func TestLoadCorruptFile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|||||||
@@ -477,6 +477,54 @@ func TestDomainPortAndTLSChecks(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFirstCheckAfterLoadingFileWithoutSections loads a state file
|
||||||
|
// holding {}, and one that leaves out the certificates section, and runs
|
||||||
|
// the first check. It saves domain, hostname, port and certificate
|
||||||
|
// entries; a section loaded as nil made it panic.
|
||||||
|
func TestFirstCheckAfterLoadingFileWithoutSections(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
file string
|
||||||
|
}{
|
||||||
|
{"no sections", `{}`},
|
||||||
|
{"no certificates", `{"domains": {}, "hostnames": {}, "ports": {}}`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cfg := defaultTestConfig(t)
|
||||||
|
cfg.Domains = []string{testSmallDomain}
|
||||||
|
|
||||||
|
_, deps := runChecks(t, cfg, func(deps *testDeps) {
|
||||||
|
err := os.WriteFile(cfg.StatePath(), []byte(tt.file), 0o600)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("writing the state file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = deps.state.Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("loading the state file: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
snap := deps.state.GetSnapshot()
|
||||||
|
if len(snap.Domains) == 0 || len(snap.Hostnames) == 0 ||
|
||||||
|
len(snap.Ports) == 0 || len(snap.Certificates) == 0 {
|
||||||
|
t.Errorf(
|
||||||
|
"saved %d domain, %d hostname, %d port and %d "+
|
||||||
|
"certificate entries, want at least one of each",
|
||||||
|
len(snap.Domains), len(snap.Hostnames),
|
||||||
|
len(snap.Ports), len(snap.Certificates),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNSChangeDetection(t *testing.T) {
|
func TestNSChangeDetection(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user