Add 80% limit warning, stop repeat notifications, fix review findings

Notifications:
- Warn once when a counter reaches warn_percent of its limit, through
  separate warn_*_url endpoints.
- Write ntf/ markers so a lost Redis reply cannot re-send a breach.
- Retry only when the request never left or the web app answered 429/5xx.

Fixes:
- Retry transactions Redis rejected; treat pool timeouts as definite.
- Install the signal handler before startup work.
- Stop logging the login suffix; redact notify URL credentials.
- Reject corrupt or empty CDBs, truncated hash tables included.
- Require a Sentinel password, refuse example placeholders, and default
  admin_addr to localhost.
- Normalise /counters domains, count on the arrival day, log rejections,
  hide internals in errors and exit 1 on a failed drain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dimitrije Dimitrijevic
2026-11-09 09:01:56 +01:00
co-authored by Claude Opus 5.5
parent c43ff17627
commit 54f9626121
29 changed files with 1673 additions and 245 deletions
+35
View File
@@ -57,6 +57,8 @@ func (f *fakeAPI) last() *http.Request {
return f.requests[len(f.requests)-1]
}
func (f *fakeAPI) rawQuery() string { return f.last().URL.RawQuery }
func writeConfig(t *testing.T, body string) string {
t.Helper()
p := filepath.Join(t.TempDir(), "config.toml")
@@ -333,3 +335,36 @@ func TestRun_Version(t *testing.T) {
t.Errorf("exit %d output %q", code, out)
}
}
// Keys live 31 days from their last increment, so a date exactly 31 days back
// can still hold data.
func TestDateWarning_Boundary(t *testing.T) {
f := &fakeAPI{body: sampleResponse()}
s := httptest.NewServer(f)
defer s.Close()
cfg := writeConfig(t, "auth_token = \"t\"\n")
exact := now.UTC().AddDate(0, 0, -31).Format("2006-01-02")
if _, _, errOut := runCLI(t, noEnv, "--config", cfg, "--url", s.URL, "--address", "me@example.com", "--date", exact); strings.Contains(errOut, "will read as 0") {
t.Errorf("31 days back must not warn: %q", errOut)
}
older := now.UTC().AddDate(0, 0, -32).Format("2006-01-02")
if _, _, errOut := runCLI(t, noEnv, "--config", cfg, "--url", s.URL, "--address", "me@example.com", "--date", older); !strings.Contains(errOut, "will read as 0") {
t.Errorf("32 days back must warn: %q", errOut)
}
}
func TestDomainFlag_Normalised(t *testing.T) {
f := &fakeAPI{body: sampleResponse()}
s := httptest.NewServer(f)
defer s.Close()
cfg := writeConfig(t, "auth_token = \"t\"\n")
if code, _, errOut := runCLI(t, noEnv, "--config", cfg, "--url", s.URL, "--domain", "Bücher.de"); code != 0 {
t.Fatalf("exit %d: %s", code, errOut)
}
if !strings.Contains(f.rawQuery(), "domain=xn--bcher-kva.de") {
t.Errorf("query = %q", f.rawQuery())
}
if code, _, _ := runCLI(t, noEnv, "--config", cfg, "--url", s.URL, "--domain", "a..b"); code == 0 {
t.Error("an invalid domain must exit non-zero")
}
}