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:
co-authored by
Claude Opus 5.5
parent
c43ff17627
commit
54f9626121
@@ -43,7 +43,7 @@ func TestLoad_Defaults(t *testing.T) {
|
||||
want any
|
||||
}{
|
||||
{"addr", cfg.Addr, ":5555"},
|
||||
{"admin_addr", cfg.AdminAddr, ":9095"},
|
||||
{"admin_addr", cfg.AdminAddr, "127.0.0.1:9095"},
|
||||
{"auth_token", cfg.AuthToken, "t"},
|
||||
{"queue_size", cfg.QueueSize, 10000},
|
||||
{"workers", cfg.Workers, 8},
|
||||
@@ -62,9 +62,10 @@ func TestLoad_Defaults(t *testing.T) {
|
||||
{"redis.pool_size", cfg.Redis.PoolSize, 32},
|
||||
{"redis.timeout_ms", cfg.Redis.TimeoutMS, 500},
|
||||
{"redis.sentinel_mode", cfg.Redis.SentinelMode(), false},
|
||||
{"notify.timeout_seconds", cfg.Notify.TimeoutSeconds, 5},
|
||||
{"notify.timeout_seconds", cfg.Notify.TimeoutSeconds, 15},
|
||||
{"notify.warn_percent", cfg.Notify.WarnPercent, int64(80)},
|
||||
{"redis timeout", cfg.Redis.Timeout(), 500 * time.Millisecond},
|
||||
{"notify timeout", cfg.Notify.Timeout(), 5 * time.Second},
|
||||
{"notify timeout", cfg.Notify.Timeout(), 15 * time.Second},
|
||||
{"handler timeout", cfg.HandlerTimeoutDuration(), 4 * time.Second},
|
||||
}
|
||||
for _, c := range checks {
|
||||
@@ -86,9 +87,17 @@ func TestLoad_Defaults(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// The shipped example must always load, validate and produce no warnings.
|
||||
// The shipped example must load and validate once its placeholders are filled
|
||||
// in, produce no warnings, and be refused while they are still there.
|
||||
func TestLoad_Example(t *testing.T) {
|
||||
cfg, err := Load("../../config.toml.example")
|
||||
if _, err := Load("../../config.toml.example"); err == nil || !strings.Contains(err.Error(), "placeholder") {
|
||||
t.Fatalf("the unedited example must be refused, got %v", err)
|
||||
}
|
||||
raw, err := os.ReadFile("../../config.toml.example")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := Load(writeConfig(t, strings.ReplaceAll(string(raw), "\"…\"", "\"filled-in\"")))
|
||||
if err != nil {
|
||||
t.Fatalf("Load(config.toml.example): %v", err)
|
||||
}
|
||||
@@ -164,7 +173,7 @@ func TestLoad_AllowedCIDRs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoad_SentinelMode(t *testing.T) {
|
||||
cfg := mustLoad(t, top+"[redis]\nmaster_name = \"mymaster\"\nsentinels = [\"10.0.0.1:26379\", \"10.0.0.2:26379\"]\n")
|
||||
cfg := mustLoad(t, top+"[redis]\npassword = \"s\"\nmaster_name = \"mymaster\"\nsentinels = [\"10.0.0.1:26379\", \"10.0.0.2:26379\"]\n")
|
||||
if !cfg.Redis.SentinelMode() || len(cfg.Redis.ReadAddrs) != 0 || cfg.Redis.WriteAddr != "" {
|
||||
t.Errorf("sentinel mode config = %+v", cfg.Redis)
|
||||
}
|
||||
@@ -202,6 +211,14 @@ func TestValidate_Errors(t *testing.T) {
|
||||
{"addr", top + "addr = \"no-port\"\n" + redisStatic, "addr"},
|
||||
{"admin_addr", top + "admin_addr = \"no-port\"\n" + redisStatic, "admin_addr"},
|
||||
{"notify timeout", minimal + "[notify]\ntimeout_seconds = 0\n", "timeout_seconds"},
|
||||
{"sentinel needs password", top + "[redis]\nmaster_name = \"m\"\nsentinels = [\"10.0.0.1:26379\"]\n", "redis.password"},
|
||||
{"placeholder token", "domains_cdb = \"/tmp/x\"\nauth_token = \"…\"\n" + redisStatic, "auth_token"},
|
||||
{"placeholder redis password", top + redisStatic + "password = \"…\"\n", "redis.password"},
|
||||
{"placeholder notify password", minimal + "[notify]\npassword = \"…\"\n", "notify.password"},
|
||||
{"warn percent high", minimal + "[notify]\nwarn_percent = 100\n", "warn_percent"},
|
||||
{"warn percent negative", minimal + "[notify]\nwarn_percent = -1\n", "warn_percent"},
|
||||
{"warn url scheme", minimal + "[notify]\nwarn_org_url = \"ftp://x/w\"\n", "warn_org_url"},
|
||||
{"warn url same as breach", minimal + "[notify]\norg_url = \"https://x/api/limits?level=$LEVEL\"\nwarn_org_url = \"https://x/api/limits?level=$LEVEL&percent=$PERCENT\"\n", "warn_org_url"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -286,3 +303,33 @@ func TestDiscover(t *testing.T) {
|
||||
t.Errorf("Discover(\"\") = %q, %v; want %q", got, err, found)
|
||||
}
|
||||
}
|
||||
|
||||
// An explicit opt-out allows a passwordless Sentinel setup (development).
|
||||
func TestLoad_SentinelPasswordOptional(t *testing.T) {
|
||||
cfg := mustLoad(t, top+"[redis]\npassword_optional = true\nmaster_name = \"m\"\nsentinels = [\"10.0.0.1:26379\"]\n")
|
||||
if !cfg.Redis.SentinelMode() {
|
||||
t.Errorf("config = %+v", cfg.Redis)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWarnings_PlainHTTPWithBasicAuth(t *testing.T) {
|
||||
const q = "?level=$LEVEL&id=$ID&flow=$FLOW&period=$PERIOD&by=$ADDRESS&count=$COUNT"
|
||||
cfg := mustLoad(t, minimal+"[notify]\nuser = \"u\"\norg_url = \"http://x/l"+q+"\"\n")
|
||||
w := strings.Join(cfg.Warnings(), "\n")
|
||||
if !strings.Contains(w, "org_url") || !strings.Contains(w, "plain http") {
|
||||
t.Errorf("Warnings() = %q, want a plain-http warning for org_url", w)
|
||||
}
|
||||
cfg = mustLoad(t, minimal+"[notify]\nuser = \"u\"\norg_url = \"https://x/l"+q+"\"\n")
|
||||
if w := cfg.Warnings(); len(w) != 0 {
|
||||
t.Errorf("https should not warn: %v", w)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEffectiveWarnPercent(t *testing.T) {
|
||||
if got := (NotifyConfig{WarnPercent: 80}).EffectiveWarnPercent(); got != 0 {
|
||||
t.Errorf("no warning URLs: %d, want 0", got)
|
||||
}
|
||||
if got := (NotifyConfig{WarnPercent: 80, WarnOrgURL: "https://x/w"}).EffectiveWarnPercent(); got != 80 {
|
||||
t.Errorf("with a warning URL: %d, want 80", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user