Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64930a00fc |
+20
-11
@@ -222,7 +222,7 @@ func (f *Files) Watch(ctx context.Context) {
|
||||
case event := <-watcher.Events:
|
||||
switch name := filepath.Base(event.Name); name {
|
||||
case bansJSON, clientsJSON, lookupsJSON:
|
||||
f.takeInEdit(name)
|
||||
f.fileChanged(name)
|
||||
}
|
||||
case err = <-watcher.Errors:
|
||||
f.params.ProcessLog.Warn("watching the state files failed",
|
||||
@@ -239,10 +239,11 @@ func (f *Files) logFailure(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// takeInEdit takes in an edit of the state file name and logs it, if the
|
||||
// file has changed since smallwebwaf last read or wrote it and parses. A
|
||||
// file that cannot be read or does not parse is left for its next write.
|
||||
func (f *Files) takeInEdit(name string) {
|
||||
// fileChanged takes in what the state file name holds, as Watch sees it
|
||||
// change, if that is an edit made since smallwebwaf last read or wrote
|
||||
// the file. A file that cannot be read or does not parse is left for its
|
||||
// next write.
|
||||
func (f *Files) fileChanged(name string) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
@@ -251,9 +252,17 @@ func (f *Files) takeInEdit(name string) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = f.takeIn(name, data)
|
||||
_ = f.takeInEdit(name, data)
|
||||
}
|
||||
|
||||
// takeInEdit takes in data, an edit of the state file name, as takeIn
|
||||
// does, and counts and logs it. Every edit taken in while smallwebwaf
|
||||
// runs, by Watch or by a write, is taken in here. An edit that does not
|
||||
// parse is neither counted nor logged, and takeIn's error returned.
|
||||
func (f *Files) takeInEdit(name string, data []byte) error {
|
||||
_, err := f.takeIn(name, data)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Counted before it is logged, so that the count is there once the
|
||||
@@ -261,6 +270,8 @@ func (f *Files) takeInEdit(name string) {
|
||||
f.params.Metrics.StateFileEditTakenIn(name)
|
||||
f.params.ProcessLog.Info("took in an edit of a state file",
|
||||
"file", filepath.Join(f.params.Dir, name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// read takes in the state file name at start, and returns how many
|
||||
@@ -357,10 +368,8 @@ func (f *Files) writeFile(name string) error {
|
||||
|
||||
data, changed, err := f.readChanged(name)
|
||||
if err == nil && changed {
|
||||
_, err = f.takeIn(name, data)
|
||||
if err == nil {
|
||||
f.params.Metrics.StateFileEditTakenIn(name)
|
||||
} else {
|
||||
err = f.takeInEdit(name, data)
|
||||
if err != nil {
|
||||
err = f.setAside(name, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -774,6 +775,53 @@ func TestEditsTakenInAreCountedInTheMetrics(t *testing.T) {
|
||||
`smallwebwaf_state_file_edits_taken_in_total{file="bans.json"}`, 2)
|
||||
}
|
||||
|
||||
func TestEditTakenInByAWriteIsLoggedAsWatchLogsIt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
params := newParams(dir)
|
||||
lines := logInto(¶ms)
|
||||
files := load(t, params)
|
||||
|
||||
// An edit taken in by Watch, which is then stopped.
|
||||
ctx, stop := context.WithCancel(t.Context())
|
||||
stopped := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
files.Watch(ctx)
|
||||
close(stopped)
|
||||
}()
|
||||
|
||||
lines.waitFor(t, watching)
|
||||
edit(t, dir, bansJSON, `{"version": 1, "bans": []}`)
|
||||
byWatch := lines.waitFor(t, tookIn)
|
||||
|
||||
stop()
|
||||
<-stopped
|
||||
|
||||
// An edit taken in by the write of its file. Nothing logs after the
|
||||
// write, so the log is closed, and a write that does not log the edit
|
||||
// fails the test at once instead of waiting for the line.
|
||||
edit(t, dir, bansJSON, permanentBansJSON)
|
||||
|
||||
err := files.WriteAll()
|
||||
if err != nil {
|
||||
t.Fatalf("write: %v", err)
|
||||
}
|
||||
|
||||
close(lines)
|
||||
|
||||
byWrite := lines.waitFor(t, tookIn)
|
||||
|
||||
// The two lines differ only in their time.
|
||||
delete(byWatch, "time")
|
||||
delete(byWrite, "time")
|
||||
|
||||
if !maps.Equal(byWrite, byWatch) {
|
||||
t.Errorf("the write logged %v, where Watch logged %v", byWrite, byWatch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditsSetAsideAreCountedInTheMetrics(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -957,8 +1005,9 @@ func (l processLog) Write(line []byte) (int, error) {
|
||||
}
|
||||
|
||||
// waitFor returns the next line of the process log whose message is msg,
|
||||
// passing over the lines before it. It waits as long as that takes, so
|
||||
// that a slow test process cannot fail the test.
|
||||
// passing over the lines before it, or nil if the log is closed first. It
|
||||
// waits as long as that takes, so that a slow test process cannot fail
|
||||
// the test.
|
||||
func (l processLog) waitFor(t *testing.T, msg string) map[string]any {
|
||||
t.Helper()
|
||||
|
||||
@@ -975,7 +1024,7 @@ func (l processLog) waitFor(t *testing.T, msg string) map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
return nil // never reached: nothing closes the log
|
||||
return nil
|
||||
}
|
||||
|
||||
// wantTakenIn waits for the next edit taken in, and checks that it is of
|
||||
|
||||
Reference in New Issue
Block a user