refactor(store): propagate context.Context through all Store methods
Every Store interface method (except Close) now takes context.Context as first parameter. All 54 db.Query/Exec/QueryRow calls in SQLStore replaced with their *Context variants. DB operations now respect cancellation and deadlines. Context sources by caller: - Engine dbWriter/poll/pruner: engine ctx from Start() - HTTP handlers: r.Context() - config.Apply/Export: caller-provided ctx - TUI/main.go init: context.Background() RunCheck and all sub-checks (HTTP/ping/port/DNS) accept parent ctx. HTTP checks now inherit shutdown cancellation instead of rooting in context.Background(). dbWrite.exec takes ctx so the writer goroutine can cancel stuck DB operations. DeleteSite/ImportData use BeginTx(ctx) instead of Begin().
This commit is contained in:
+10
-10
@@ -35,7 +35,7 @@ type CheckResult struct {
|
||||
ErrorReason string
|
||||
}
|
||||
|
||||
func RunCheck(site models.Site, strict, insecure *http.Client, globalInsecure bool, allowPrivate ...bool) CheckResult {
|
||||
func RunCheck(ctx context.Context, site models.Site, strict, insecure *http.Client, globalInsecure bool, allowPrivate ...bool) CheckResult {
|
||||
private := len(allowPrivate) > 0 && allowPrivate[0]
|
||||
|
||||
if site.Type != "http" && site.Type != "dns" && !private {
|
||||
@@ -56,26 +56,26 @@ func RunCheck(site models.Site, strict, insecure *http.Client, globalInsecure bo
|
||||
|
||||
switch site.Type {
|
||||
case "http":
|
||||
return runHTTPCheck(site, strict, insecure, globalInsecure)
|
||||
return runHTTPCheck(ctx, site, strict, insecure, globalInsecure)
|
||||
case "ping":
|
||||
return runPingCheck(site)
|
||||
return runPingCheck(ctx, site)
|
||||
case "port":
|
||||
return runPortCheck(site)
|
||||
return runPortCheck(ctx, site)
|
||||
case "dns":
|
||||
return runDNSCheck(site)
|
||||
return runDNSCheck(ctx, site)
|
||||
default:
|
||||
return CheckResult{SiteID: site.ID, Status: "DOWN", ErrorReason: "unsupported monitor type: " + site.Type}
|
||||
}
|
||||
}
|
||||
|
||||
func runHTTPCheck(site models.Site, strict, insecure *http.Client, globalInsecure bool) CheckResult {
|
||||
func runHTTPCheck(ctx context.Context, site models.Site, strict, insecure *http.Client, globalInsecure bool) CheckResult {
|
||||
method := site.Method
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
}
|
||||
|
||||
timeout := siteTimeout(site)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, site.URL, nil)
|
||||
@@ -128,7 +128,7 @@ func runHTTPCheck(site models.Site, strict, insecure *http.Client, globalInsecur
|
||||
return result
|
||||
}
|
||||
|
||||
func runPingCheck(site models.Site) CheckResult {
|
||||
func runPingCheck(_ context.Context, site models.Site) CheckResult {
|
||||
host := site.Hostname
|
||||
if host == "" {
|
||||
host = site.URL
|
||||
@@ -157,7 +157,7 @@ func runPingCheck(site models.Site) CheckResult {
|
||||
return CheckResult{SiteID: site.ID, Status: "UP", LatencyNs: stats.AvgRtt.Nanoseconds()}
|
||||
}
|
||||
|
||||
func runPortCheck(site models.Site) CheckResult {
|
||||
func runPortCheck(_ context.Context, site models.Site) CheckResult {
|
||||
host := site.Hostname
|
||||
if host == "" {
|
||||
host = site.URL
|
||||
@@ -176,7 +176,7 @@ func runPortCheck(site models.Site) CheckResult {
|
||||
return CheckResult{SiteID: site.ID, Status: "UP", LatencyNs: latency.Nanoseconds()}
|
||||
}
|
||||
|
||||
func runDNSCheck(site models.Site) CheckResult {
|
||||
func runDNSCheck(_ context.Context, site models.Site) CheckResult {
|
||||
host := site.Hostname
|
||||
if host == "" {
|
||||
host = site.URL
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -19,7 +20,7 @@ func TestRunCheck_HTTP_Success(t *testing.T) {
|
||||
defer srv.Close()
|
||||
|
||||
site := models.Site{ID: 1, Type: "http", URL: srv.URL}
|
||||
result := RunCheck(site, http.DefaultClient, http.DefaultClient, false)
|
||||
result := RunCheck(context.Background(), site, http.DefaultClient, http.DefaultClient, false)
|
||||
|
||||
if result.Status != "UP" {
|
||||
t.Errorf("expected UP, got %s", result.Status)
|
||||
@@ -39,7 +40,7 @@ func TestRunCheck_HTTP_ServerError(t *testing.T) {
|
||||
defer srv.Close()
|
||||
|
||||
site := models.Site{ID: 1, Type: "http", URL: srv.URL}
|
||||
result := RunCheck(site, http.DefaultClient, http.DefaultClient, false)
|
||||
result := RunCheck(context.Background(), site, http.DefaultClient, http.DefaultClient, false)
|
||||
|
||||
if result.Status != "DOWN" {
|
||||
t.Errorf("expected DOWN, got %s", result.Status)
|
||||
@@ -60,7 +61,7 @@ func TestRunCheck_HTTP_CustomAcceptedCodes(t *testing.T) {
|
||||
}}
|
||||
|
||||
site := models.Site{ID: 1, Type: "http", URL: srv.URL, AcceptedCodes: "200-399"}
|
||||
result := RunCheck(site, client, client, false)
|
||||
result := RunCheck(context.Background(), site, client, client, false)
|
||||
|
||||
if result.Status != "UP" {
|
||||
t.Errorf("expected UP with accepted 200-399, got %s", result.Status)
|
||||
@@ -76,7 +77,7 @@ func TestRunCheck_HTTP_MethodRespected(t *testing.T) {
|
||||
defer srv.Close()
|
||||
|
||||
site := models.Site{ID: 1, Type: "http", URL: srv.URL, Method: "HEAD"}
|
||||
RunCheck(site, http.DefaultClient, http.DefaultClient, false)
|
||||
RunCheck(context.Background(), site, http.DefaultClient, http.DefaultClient, false)
|
||||
|
||||
if receivedMethod != "HEAD" {
|
||||
t.Errorf("expected HEAD, got %s", receivedMethod)
|
||||
@@ -91,7 +92,7 @@ func TestRunCheck_HTTP_Timeout(t *testing.T) {
|
||||
defer srv.Close()
|
||||
|
||||
site := models.Site{ID: 1, Type: "http", URL: srv.URL, Timeout: 1}
|
||||
result := RunCheck(site, http.DefaultClient, http.DefaultClient, false)
|
||||
result := RunCheck(context.Background(), site, http.DefaultClient, http.DefaultClient, false)
|
||||
|
||||
if result.Status != "DOWN" {
|
||||
t.Errorf("expected DOWN on timeout, got %s", result.Status)
|
||||
@@ -109,7 +110,7 @@ func TestRunCheck_HTTP_SSLFields(t *testing.T) {
|
||||
}
|
||||
|
||||
site := models.Site{ID: 1, Type: "http", URL: srv.URL, CheckSSL: true, IgnoreTLS: true}
|
||||
result := RunCheck(site, http.DefaultClient, insecureClient, false)
|
||||
result := RunCheck(context.Background(), site, http.DefaultClient, insecureClient, false)
|
||||
|
||||
if result.Status != "UP" {
|
||||
t.Errorf("expected UP, got %s", result.Status)
|
||||
@@ -133,7 +134,7 @@ func TestRunCheck_Port_Open(t *testing.T) {
|
||||
port, _ := strconv.Atoi(portStr)
|
||||
|
||||
site := models.Site{ID: 1, Type: "port", Hostname: "127.0.0.1", Port: port, Timeout: 2}
|
||||
result := RunCheck(site, nil, nil, false, true)
|
||||
result := RunCheck(context.Background(), site, nil, nil, false, true)
|
||||
|
||||
if result.Status != "UP" {
|
||||
t.Errorf("expected UP, got %s", result.Status)
|
||||
@@ -153,7 +154,7 @@ func TestRunCheck_Port_Closed(t *testing.T) {
|
||||
ln.Close()
|
||||
|
||||
site := models.Site{ID: 1, Type: "port", Hostname: "127.0.0.1", Port: port, Timeout: 1}
|
||||
result := RunCheck(site, nil, nil, false, true)
|
||||
result := RunCheck(context.Background(), site, nil, nil, false, true)
|
||||
|
||||
if result.Status != "DOWN" {
|
||||
t.Errorf("expected DOWN, got %s", result.Status)
|
||||
@@ -171,7 +172,7 @@ func TestRunCheck_Port_BlocksPrivateByDefault(t *testing.T) {
|
||||
port, _ := strconv.Atoi(portStr)
|
||||
|
||||
site := models.Site{ID: 1, Type: "port", Hostname: "127.0.0.1", Port: port, Timeout: 2}
|
||||
result := RunCheck(site, nil, nil, false)
|
||||
result := RunCheck(context.Background(), site, nil, nil, false)
|
||||
|
||||
if result.Status != "DOWN" {
|
||||
t.Errorf("expected DOWN when private targets blocked, got %s", result.Status)
|
||||
@@ -180,7 +181,7 @@ func TestRunCheck_Port_BlocksPrivateByDefault(t *testing.T) {
|
||||
|
||||
func TestRunCheck_UnknownType(t *testing.T) {
|
||||
site := models.Site{ID: 1, Type: "invalid"}
|
||||
result := RunCheck(site, nil, nil, false)
|
||||
result := RunCheck(context.Background(), site, nil, nil, false)
|
||||
|
||||
if result.Status != "DOWN" {
|
||||
t.Errorf("expected DOWN for unknown type, got %s", result.Status)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
|
||||
"gitea.lerkolabs.com/lerkolabs/uptop/internal/store"
|
||||
)
|
||||
@@ -10,14 +12,14 @@ import (
|
||||
// serializing all writes through one connection and surfacing errors instead of
|
||||
// discarding them. desc names the write for diagnostics on drop/failure.
|
||||
type dbWrite interface {
|
||||
exec(s store.Store) error
|
||||
exec(ctx context.Context, s store.Store) error
|
||||
desc() string
|
||||
}
|
||||
|
||||
type writeLog struct{ message string }
|
||||
|
||||
func (w writeLog) exec(s store.Store) error { return s.SaveLog(w.message) }
|
||||
func (w writeLog) desc() string { return "log" }
|
||||
func (w writeLog) exec(ctx context.Context, s store.Store) error { return s.SaveLog(ctx, w.message) }
|
||||
func (w writeLog) desc() string { return "log" }
|
||||
|
||||
type writeCheck struct {
|
||||
siteID int
|
||||
@@ -25,8 +27,10 @@ type writeCheck struct {
|
||||
isUp bool
|
||||
}
|
||||
|
||||
func (w writeCheck) exec(s store.Store) error { return s.SaveCheck(w.siteID, w.latencyNs, w.isUp) }
|
||||
func (w writeCheck) desc() string { return "check" }
|
||||
func (w writeCheck) exec(ctx context.Context, s store.Store) error {
|
||||
return s.SaveCheck(ctx, w.siteID, w.latencyNs, w.isUp)
|
||||
}
|
||||
func (w writeCheck) desc() string { return "check" }
|
||||
|
||||
type writeStateChange struct {
|
||||
siteID int
|
||||
@@ -35,15 +39,17 @@ type writeStateChange struct {
|
||||
reason string
|
||||
}
|
||||
|
||||
func (w writeStateChange) exec(s store.Store) error {
|
||||
return s.SaveStateChange(w.siteID, w.fromStatus, w.toStatus, w.reason)
|
||||
func (w writeStateChange) exec(ctx context.Context, s store.Store) error {
|
||||
return s.SaveStateChange(ctx, w.siteID, w.fromStatus, w.toStatus, w.reason)
|
||||
}
|
||||
func (w writeStateChange) desc() string { return "state-change" }
|
||||
|
||||
type writeAlertHealth struct{ rec models.AlertHealthRecord }
|
||||
|
||||
func (w writeAlertHealth) exec(s store.Store) error { return s.SaveAlertHealth(w.rec) }
|
||||
func (w writeAlertHealth) desc() string { return "alert-health" }
|
||||
func (w writeAlertHealth) exec(ctx context.Context, s store.Store) error {
|
||||
return s.SaveAlertHealth(ctx, w.rec)
|
||||
}
|
||||
func (w writeAlertHealth) desc() string { return "alert-health" }
|
||||
|
||||
type writeProbeCheck struct {
|
||||
siteID int
|
||||
@@ -52,7 +58,7 @@ type writeProbeCheck struct {
|
||||
isUp bool
|
||||
}
|
||||
|
||||
func (w writeProbeCheck) exec(s store.Store) error {
|
||||
return s.SaveCheckFromNode(w.siteID, w.nodeID, w.latencyNs, w.isUp)
|
||||
func (w writeProbeCheck) exec(ctx context.Context, s store.Store) error {
|
||||
return s.SaveCheckFromNode(ctx, w.siteID, w.nodeID, w.latencyNs, w.isUp)
|
||||
}
|
||||
func (w writeProbeCheck) desc() string { return "probe-check" }
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package monitor
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const maxHistoryLen = 60
|
||||
|
||||
@@ -12,7 +15,7 @@ type SiteHistory struct {
|
||||
}
|
||||
|
||||
func (e *Engine) InitHistory() {
|
||||
all, err := e.db.LoadAllHistory(maxHistoryLen)
|
||||
all, err := e.db.LoadAllHistory(context.Background(), maxHistoryLen)
|
||||
if err != nil {
|
||||
e.AddLog("Failed to load check history: " + err.Error())
|
||||
return
|
||||
|
||||
+30
-30
@@ -185,16 +185,16 @@ func (e *Engine) dbWriter(ctx context.Context) {
|
||||
|
||||
pruneTicker := time.NewTicker(dbPruneInterval)
|
||||
defer pruneTicker.Stop()
|
||||
e.prune()
|
||||
e.prune(ctx)
|
||||
|
||||
for {
|
||||
select {
|
||||
case w := <-e.dbWrites:
|
||||
if err := w.exec(e.db); err != nil {
|
||||
if err := w.exec(ctx, e.db); err != nil {
|
||||
e.appendLog(fmt.Sprintf("db %s write failed: %v", w.desc(), err))
|
||||
}
|
||||
case <-pruneTicker.C:
|
||||
e.prune()
|
||||
e.prune(ctx)
|
||||
case <-ctx.Done():
|
||||
e.drainWrites()
|
||||
return
|
||||
@@ -207,7 +207,7 @@ func (e *Engine) drainWrites() {
|
||||
for {
|
||||
select {
|
||||
case w := <-e.dbWrites:
|
||||
if err := w.exec(e.db); err != nil {
|
||||
if err := w.exec(context.Background(), e.db); err != nil {
|
||||
e.appendLog(fmt.Sprintf("db %s write failed (drain): %v", w.desc(), err))
|
||||
}
|
||||
default:
|
||||
@@ -216,14 +216,14 @@ func (e *Engine) drainWrites() {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) prune() {
|
||||
if err := e.db.PruneLogs(); err != nil {
|
||||
func (e *Engine) prune(ctx context.Context) {
|
||||
if err := e.db.PruneLogs(ctx); err != nil {
|
||||
e.appendLog(fmt.Sprintf("log prune failed: %v", err))
|
||||
}
|
||||
if err := e.db.PruneCheckHistory(); err != nil {
|
||||
if err := e.db.PruneCheckHistory(ctx); err != nil {
|
||||
e.appendLog(fmt.Sprintf("check-history prune failed: %v", err))
|
||||
}
|
||||
if err := e.db.PruneStateChanges(); err != nil {
|
||||
if err := e.db.PruneStateChanges(ctx); err != nil {
|
||||
e.appendLog(fmt.Sprintf("state-change prune failed: %v", err))
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func (e *Engine) Stop() {
|
||||
}
|
||||
|
||||
func (e *Engine) InitLogs() {
|
||||
logs, err := e.db.LoadLogs(maxLogEntries)
|
||||
logs, err := e.db.LoadLogs(context.Background(), maxLogEntries)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -257,7 +257,7 @@ func (e *Engine) InitLogs() {
|
||||
// InitAlertHealth restores persisted alert send health so the dashboard shows real
|
||||
// "last sent" / health state on startup instead of resetting every channel to "never".
|
||||
func (e *Engine) InitAlertHealth() {
|
||||
records, err := e.db.LoadAlertHealth()
|
||||
records, err := e.db.LoadAlertHealth(context.Background())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -416,9 +416,9 @@ func (e *Engine) Start(ctx context.Context) {
|
||||
default:
|
||||
}
|
||||
|
||||
e.refreshMaintenanceCache()
|
||||
e.refreshMaintenanceCache(ctx)
|
||||
|
||||
sites, err := e.db.GetSites()
|
||||
sites, err := e.db.GetSites(ctx)
|
||||
if err != nil {
|
||||
e.AddLog(fmt.Sprintf("Failed to load sites: %v", err))
|
||||
select {
|
||||
@@ -475,20 +475,20 @@ func (e *Engine) maintenancePruner(ctx context.Context) {
|
||||
ticker := time.NewTicker(maintPruneInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
e.pruneMaintenanceWindows()
|
||||
e.pruneMaintenanceWindows(ctx)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
e.pruneMaintenanceWindows()
|
||||
e.pruneMaintenanceWindows(ctx)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) pruneMaintenanceWindows() {
|
||||
pruned, err := e.db.PruneExpiredMaintenanceWindows(e.maintRetention)
|
||||
func (e *Engine) pruneMaintenanceWindows(ctx context.Context) {
|
||||
pruned, err := e.db.PruneExpiredMaintenanceWindows(ctx, e.maintRetention)
|
||||
if err != nil {
|
||||
e.AddLog(fmt.Sprintf("Maintenance prune error: %v", err))
|
||||
return
|
||||
@@ -588,7 +588,7 @@ func (e *Engine) monitorRoutine(ctx context.Context, id int) {
|
||||
return
|
||||
}
|
||||
|
||||
e.checkByID(id)
|
||||
e.checkByID(ctx, id)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -634,7 +634,7 @@ func (e *Engine) monitorRoutine(ctx context.Context, id int) {
|
||||
return
|
||||
case <-recheckCh:
|
||||
}
|
||||
e.checkByID(id)
|
||||
e.checkByID(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,7 +657,7 @@ func (e *Engine) applyState(id int, mutate func(s *models.Site)) (models.Site, b
|
||||
return cur, true
|
||||
}
|
||||
|
||||
func (e *Engine) checkByID(id int) {
|
||||
func (e *Engine) checkByID(ctx context.Context, id int) {
|
||||
if !e.IsActive() {
|
||||
return
|
||||
}
|
||||
@@ -671,11 +671,11 @@ func (e *Engine) checkByID(id int) {
|
||||
|
||||
switch site.Type {
|
||||
case "push":
|
||||
e.checkPush(site)
|
||||
e.checkPush(ctx, site)
|
||||
case "group":
|
||||
e.checkGroup(site)
|
||||
e.checkGroup(ctx, site)
|
||||
default:
|
||||
result := RunCheck(site, e.strictClient, e.insecureClient, e.insecureSkipVerify, e.allowPrivateTargets)
|
||||
result := RunCheck(ctx, site, e.strictClient, e.insecureClient, e.insecureSkipVerify, e.allowPrivateTargets)
|
||||
updatedSite := site
|
||||
updatedSite.HasSSL = result.HasSSL
|
||||
updatedSite.CertExpiry = result.CertExpiry
|
||||
@@ -685,7 +685,7 @@ func (e *Engine) checkByID(id int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) checkPush(site models.Site) {
|
||||
func (e *Engine) checkPush(_ context.Context, site models.Site) {
|
||||
if site.Status == "PENDING" {
|
||||
return
|
||||
}
|
||||
@@ -875,7 +875,7 @@ func (e *Engine) handleStatusChange(snap models.Site, rawStatus string, code int
|
||||
}
|
||||
|
||||
func (e *Engine) triggerAlert(alertID int, title, message string) {
|
||||
cfg, err := e.db.GetAlert(alertID)
|
||||
cfg, err := e.db.GetAlert(context.Background(), alertID)
|
||||
if err != nil {
|
||||
e.AddLog(fmt.Sprintf("Failed to load alert config %d: %v", alertID, err))
|
||||
return
|
||||
@@ -928,7 +928,7 @@ func (e *Engine) GetAlertHealth(alertID int) AlertHealth {
|
||||
}
|
||||
|
||||
func (e *Engine) TestAlert(alertID int) error {
|
||||
cfg, err := e.db.GetAlert(alertID)
|
||||
cfg, err := e.db.GetAlert(context.Background(), alertID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load alert: %w", err)
|
||||
}
|
||||
@@ -954,8 +954,8 @@ func (e *Engine) isInMaintenance(monitorID int) bool {
|
||||
return e.maintCache[monitorID]
|
||||
}
|
||||
|
||||
func (e *Engine) refreshMaintenanceCache() {
|
||||
windows, err := e.db.GetActiveMaintenanceWindows()
|
||||
func (e *Engine) refreshMaintenanceCache(ctx context.Context) {
|
||||
windows, err := e.db.GetActiveMaintenanceWindows(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -994,7 +994,7 @@ func (e *Engine) GetDisplayStatus(site models.Site) string {
|
||||
return site.Status
|
||||
}
|
||||
|
||||
func (e *Engine) checkGroup(site models.Site) {
|
||||
func (e *Engine) checkGroup(_ context.Context, site models.Site) {
|
||||
e.mu.RLock()
|
||||
status := "UP"
|
||||
hasChildren := false
|
||||
@@ -1095,7 +1095,7 @@ func (e *Engine) GetProbeResults(siteID int) map[string]NodeResult {
|
||||
}
|
||||
|
||||
func (e *Engine) GetStateChanges(siteID int, limit int) []models.StateChange {
|
||||
changes, err := e.db.GetStateChanges(siteID, limit)
|
||||
changes, err := e.db.GetStateChanges(context.Background(), siteID, limit)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -1103,7 +1103,7 @@ func (e *Engine) GetStateChanges(siteID int, limit int) []models.StateChange {
|
||||
}
|
||||
|
||||
func (e *Engine) GetStateChangesSince(siteID int, since time.Time) []models.StateChange {
|
||||
changes, err := e.db.GetStateChangesSince(siteID, since)
|
||||
changes, err := e.db.GetStateChangesSince(context.Background(), siteID, since)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,37 +38,43 @@ func newMockStore() *mockStore {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockStore) Init() error { return nil }
|
||||
func (m *mockStore) GetSites() ([]models.Site, error) { return m.sites, nil }
|
||||
func (m *mockStore) AddSite(models.Site) error { return nil }
|
||||
func (m *mockStore) UpdateSite(models.Site) error { return nil }
|
||||
func (m *mockStore) UpdateSitePaused(int, bool) error { return nil }
|
||||
func (m *mockStore) DeleteSite(int) error { return nil }
|
||||
func (m *mockStore) AddAlert(string, string, map[string]string) error { return nil }
|
||||
func (m *mockStore) UpdateAlert(int, string, string, map[string]string) error { return nil }
|
||||
func (m *mockStore) DeleteAlert(int) error { return nil }
|
||||
func (m *mockStore) GetAllUsers() ([]models.User, error) { return nil, nil }
|
||||
func (m *mockStore) AddUser(string, string, string) error { return nil }
|
||||
func (m *mockStore) UpdateUser(int, string, string, string) error { return nil }
|
||||
func (m *mockStore) DeleteUser(int) error { return nil }
|
||||
func (m *mockStore) ExportData() (models.Backup, error) { return models.Backup{}, nil }
|
||||
func (m *mockStore) ImportData(models.Backup) error { return nil }
|
||||
func (m *mockStore) GetSiteByName(string) (models.Site, error) { return models.Site{}, nil }
|
||||
func (m *mockStore) AddSiteReturningID(models.Site) (int, error) { return 0, nil }
|
||||
func (m *mockStore) AddAlertReturningID(string, string, map[string]string) (int, error) {
|
||||
func (m *mockStore) Init(context.Context) error { return nil }
|
||||
func (m *mockStore) GetSites(context.Context) ([]models.Site, error) { return m.sites, nil }
|
||||
func (m *mockStore) AddSite(context.Context, models.Site) error { return nil }
|
||||
func (m *mockStore) UpdateSite(context.Context, models.Site) error { return nil }
|
||||
func (m *mockStore) UpdateSitePaused(context.Context, int, bool) error { return nil }
|
||||
func (m *mockStore) DeleteSite(context.Context, int) error { return nil }
|
||||
func (m *mockStore) AddAlert(context.Context, string, string, map[string]string) error { return nil }
|
||||
func (m *mockStore) UpdateAlert(context.Context, int, string, string, map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockStore) DeleteAlert(context.Context, int) error { return nil }
|
||||
func (m *mockStore) GetAllUsers(context.Context) ([]models.User, error) { return nil, nil }
|
||||
func (m *mockStore) AddUser(context.Context, string, string, string) error { return nil }
|
||||
func (m *mockStore) UpdateUser(context.Context, int, string, string, string) error { return nil }
|
||||
func (m *mockStore) DeleteUser(context.Context, int) error { return nil }
|
||||
func (m *mockStore) ExportData(context.Context) (models.Backup, error) { return models.Backup{}, nil }
|
||||
func (m *mockStore) ImportData(context.Context, models.Backup) error { return nil }
|
||||
func (m *mockStore) GetSiteByName(context.Context, string) (models.Site, error) {
|
||||
return models.Site{}, nil
|
||||
}
|
||||
func (m *mockStore) AddSiteReturningID(context.Context, models.Site) (int, error) { return 0, nil }
|
||||
func (m *mockStore) AddAlertReturningID(context.Context, string, string, map[string]string) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (m *mockStore) SaveCheckFromNode(int, string, int64, bool) error { return nil }
|
||||
func (m *mockStore) RegisterNode(models.ProbeNode) error { return nil }
|
||||
func (m *mockStore) GetNode(string) (models.ProbeNode, error) { return models.ProbeNode{}, nil }
|
||||
func (m *mockStore) GetAllNodes() ([]models.ProbeNode, error) { return nil, nil }
|
||||
func (m *mockStore) UpdateNodeLastSeen(string) error { return nil }
|
||||
func (m *mockStore) DeleteNode(string) error { return nil }
|
||||
func (m *mockStore) LoadAlertHealth() (map[int]models.AlertHealthRecord, error) {
|
||||
func (m *mockStore) SaveCheckFromNode(context.Context, int, string, int64, bool) error { return nil }
|
||||
func (m *mockStore) RegisterNode(context.Context, models.ProbeNode) error { return nil }
|
||||
func (m *mockStore) GetNode(context.Context, string) (models.ProbeNode, error) {
|
||||
return models.ProbeNode{}, nil
|
||||
}
|
||||
func (m *mockStore) GetAllNodes(context.Context) ([]models.ProbeNode, error) { return nil, nil }
|
||||
func (m *mockStore) UpdateNodeLastSeen(context.Context, string) error { return nil }
|
||||
func (m *mockStore) DeleteNode(context.Context, string) error { return nil }
|
||||
func (m *mockStore) LoadAlertHealth(context.Context) (map[int]models.AlertHealthRecord, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockStore) SaveAlertHealth(models.AlertHealthRecord) error { return nil }
|
||||
func (m *mockStore) GetActiveMaintenanceWindows() ([]models.MaintenanceWindow, error) {
|
||||
func (m *mockStore) SaveAlertHealth(context.Context, models.AlertHealthRecord) error { return nil }
|
||||
func (m *mockStore) GetActiveMaintenanceWindows(context.Context) ([]models.MaintenanceWindow, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
var windows []models.MaintenanceWindow
|
||||
@@ -77,23 +83,27 @@ func (m *mockStore) GetActiveMaintenanceWindows() ([]models.MaintenanceWindow, e
|
||||
}
|
||||
return windows, nil
|
||||
}
|
||||
func (m *mockStore) GetAllMaintenanceWindows(int) ([]models.MaintenanceWindow, error) {
|
||||
func (m *mockStore) GetAllMaintenanceWindows(context.Context, int) ([]models.MaintenanceWindow, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockStore) AddMaintenanceWindow(models.MaintenanceWindow) error { return nil }
|
||||
func (m *mockStore) EndMaintenanceWindow(int) error { return nil }
|
||||
func (m *mockStore) DeleteMaintenanceWindow(int) error { return nil }
|
||||
func (m *mockStore) PruneExpiredMaintenanceWindows(time.Duration) (int64, error) { return 0, nil }
|
||||
func (m *mockStore) GetPreference(string) (string, error) { return "", nil }
|
||||
func (m *mockStore) SetPreference(string, string) error { return nil }
|
||||
func (m *mockStore) SaveStateChange(int, string, string, string) error { return nil }
|
||||
func (m *mockStore) GetStateChanges(int, int) ([]models.StateChange, error) { return nil, nil }
|
||||
func (m *mockStore) GetStateChangesSince(int, time.Time) ([]models.StateChange, error) {
|
||||
func (m *mockStore) AddMaintenanceWindow(context.Context, models.MaintenanceWindow) error { return nil }
|
||||
func (m *mockStore) EndMaintenanceWindow(context.Context, int) error { return nil }
|
||||
func (m *mockStore) DeleteMaintenanceWindow(context.Context, int) error { return nil }
|
||||
func (m *mockStore) PruneExpiredMaintenanceWindows(context.Context, time.Duration) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (m *mockStore) GetPreference(context.Context, string) (string, error) { return "", nil }
|
||||
func (m *mockStore) SetPreference(context.Context, string, string) error { return nil }
|
||||
func (m *mockStore) SaveStateChange(context.Context, int, string, string, string) error { return nil }
|
||||
func (m *mockStore) GetStateChanges(context.Context, int, int) ([]models.StateChange, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockStore) GetStateChangesSince(context.Context, int, time.Time) ([]models.StateChange, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockStore) Close() error { return nil }
|
||||
|
||||
func (m *mockStore) GetAllAlerts() ([]models.AlertConfig, error) {
|
||||
func (m *mockStore) GetAllAlerts(context.Context) ([]models.AlertConfig, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
var result []models.AlertConfig
|
||||
@@ -103,7 +113,7 @@ func (m *mockStore) GetAllAlerts() ([]models.AlertConfig, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *mockStore) GetAlert(id int) (models.AlertConfig, error) {
|
||||
func (m *mockStore) GetAlert(_ context.Context, id int) (models.AlertConfig, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.getAlertCalls = append(m.getAlertCalls, id)
|
||||
@@ -113,7 +123,7 @@ func (m *mockStore) GetAlert(id int) (models.AlertConfig, error) {
|
||||
return models.AlertConfig{}, fmt.Errorf("alert %d not found", id)
|
||||
}
|
||||
|
||||
func (m *mockStore) GetAlertByName(name string) (models.AlertConfig, error) {
|
||||
func (m *mockStore) GetAlertByName(_ context.Context, name string) (models.AlertConfig, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for _, a := range m.alerts {
|
||||
@@ -124,37 +134,37 @@ func (m *mockStore) GetAlertByName(name string) (models.AlertConfig, error) {
|
||||
return models.AlertConfig{}, fmt.Errorf("alert %q not found", name)
|
||||
}
|
||||
|
||||
func (m *mockStore) IsMonitorInMaintenance(id int) (bool, error) {
|
||||
func (m *mockStore) IsMonitorInMaintenance(_ context.Context, id int) (bool, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.maintenance[id], nil
|
||||
}
|
||||
|
||||
func (m *mockStore) SaveCheck(siteID int, latencyNs int64, isUp bool) error {
|
||||
func (m *mockStore) SaveCheck(_ context.Context, siteID int, latencyNs int64, isUp bool) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.savedChecks = append(m.savedChecks, savedCheck{siteID, latencyNs, isUp})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockStore) SaveLog(msg string) error {
|
||||
func (m *mockStore) SaveLog(_ context.Context, msg string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.savedLogs = append(m.savedLogs, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockStore) LoadLogs(limit int) ([]string, error) {
|
||||
func (m *mockStore) LoadLogs(_ context.Context, limit int) ([]string, error) {
|
||||
return m.logs, nil
|
||||
}
|
||||
|
||||
func (m *mockStore) LoadAllHistory(limit int) (map[int][]models.CheckRecord, error) {
|
||||
func (m *mockStore) LoadAllHistory(_ context.Context, limit int) (map[int][]models.CheckRecord, error) {
|
||||
return m.history, nil
|
||||
}
|
||||
|
||||
func (m *mockStore) PruneLogs() error { return nil }
|
||||
func (m *mockStore) PruneCheckHistory() error { return nil }
|
||||
func (m *mockStore) PruneStateChanges() error { return nil }
|
||||
func (m *mockStore) PruneLogs(context.Context) error { return nil }
|
||||
func (m *mockStore) PruneCheckHistory(context.Context) error { return nil }
|
||||
func (m *mockStore) PruneStateChanges(context.Context) error { return nil }
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
@@ -336,7 +346,7 @@ func TestHandleStatusChange_AlertSuppressedMaintenance(t *testing.T) {
|
||||
e := newTestEngine(ms)
|
||||
site := models.Site{ID: 1, Name: "test", Status: "UP", MaxRetries: 0, AlertID: 1}
|
||||
injectSite(e, site)
|
||||
e.refreshMaintenanceCache()
|
||||
e.refreshMaintenanceCache(context.Background())
|
||||
|
||||
e.handleStatusChange(site, "DOWN", 0, 0, "test error")
|
||||
|
||||
@@ -368,7 +378,7 @@ func TestHandleStatusChange_RecoverySuppressedMaintenance(t *testing.T) {
|
||||
e := newTestEngine(ms)
|
||||
site := models.Site{ID: 1, Name: "test", Status: "DOWN", AlertID: 1}
|
||||
injectSite(e, site)
|
||||
e.refreshMaintenanceCache()
|
||||
e.refreshMaintenanceCache(context.Background())
|
||||
|
||||
e.handleStatusChange(site, "UP", 200, 0, "")
|
||||
|
||||
@@ -456,7 +466,7 @@ func TestHandleStatusChange_SSLWarningSuppressedMaint(t *testing.T) {
|
||||
CertExpiry: time.Now().Add(15 * 24 * time.Hour),
|
||||
}
|
||||
injectSite(e, site)
|
||||
e.refreshMaintenanceCache()
|
||||
e.refreshMaintenanceCache(context.Background())
|
||||
|
||||
e.handleStatusChange(site, "UP", 200, 0, "")
|
||||
|
||||
@@ -563,7 +573,7 @@ func TestCheckPush_DeadlineMissed(t *testing.T) {
|
||||
}
|
||||
injectSite(e, site)
|
||||
|
||||
e.checkPush(site)
|
||||
e.checkPush(context.Background(), site)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "DOWN" {
|
||||
@@ -581,7 +591,7 @@ func TestCheckPush_OverdueBecomesLate(t *testing.T) {
|
||||
}
|
||||
injectSite(e, site)
|
||||
|
||||
e.checkPush(site)
|
||||
e.checkPush(context.Background(), site)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "LATE" {
|
||||
@@ -601,7 +611,7 @@ func TestCheckPush_OverdueBecomesStale(t *testing.T) {
|
||||
}
|
||||
injectSite(e, site)
|
||||
|
||||
e.checkPush(site)
|
||||
e.checkPush(context.Background(), site)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "STALE" {
|
||||
@@ -618,7 +628,7 @@ func TestCheckPush_WithinDeadline(t *testing.T) {
|
||||
}
|
||||
injectSite(e, site)
|
||||
|
||||
e.checkPush(site)
|
||||
e.checkPush(context.Background(), site)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "UP" {
|
||||
@@ -635,7 +645,7 @@ func TestCheckPush_PendingStaysPending(t *testing.T) {
|
||||
}
|
||||
injectSite(e, site)
|
||||
|
||||
e.checkPush(site)
|
||||
e.checkPush(context.Background(), site)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "PENDING" {
|
||||
@@ -655,7 +665,7 @@ func TestCheckGroup_AllChildrenUp(t *testing.T) {
|
||||
injectSite(e, child1)
|
||||
injectSite(e, child2)
|
||||
|
||||
e.checkGroup(group)
|
||||
e.checkGroup(context.Background(), group)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "UP" {
|
||||
@@ -673,7 +683,7 @@ func TestCheckGroup_OneChildDown(t *testing.T) {
|
||||
injectSite(e, child1)
|
||||
injectSite(e, child2)
|
||||
|
||||
e.checkGroup(group)
|
||||
e.checkGroup(context.Background(), group)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "DOWN" {
|
||||
@@ -691,7 +701,7 @@ func TestCheckGroup_PausedChildIgnored(t *testing.T) {
|
||||
injectSite(e, child1)
|
||||
injectSite(e, child2)
|
||||
|
||||
e.checkGroup(group)
|
||||
e.checkGroup(context.Background(), group)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "UP" {
|
||||
@@ -709,9 +719,9 @@ func TestCheckGroup_MaintenanceChildIgnored(t *testing.T) {
|
||||
injectSite(e, group)
|
||||
injectSite(e, child1)
|
||||
injectSite(e, child2)
|
||||
e.refreshMaintenanceCache()
|
||||
e.refreshMaintenanceCache(context.Background())
|
||||
|
||||
e.checkGroup(group)
|
||||
e.checkGroup(context.Background(), group)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "UP" {
|
||||
@@ -725,7 +735,7 @@ func TestCheckGroup_NoChildren(t *testing.T) {
|
||||
group := models.Site{ID: 1, Name: "group", Type: "group", Status: "UP"}
|
||||
injectSite(e, group)
|
||||
|
||||
e.checkGroup(group)
|
||||
e.checkGroup(context.Background(), group)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Status != "PENDING" {
|
||||
@@ -1241,7 +1251,7 @@ func TestCheckGroup_AllPausedNoAutoFreeze(t *testing.T) {
|
||||
injectSite(e, child1)
|
||||
injectSite(e, child2)
|
||||
|
||||
e.checkGroup(group)
|
||||
e.checkGroup(context.Background(), group)
|
||||
|
||||
s, _ := getSite(e, 1)
|
||||
if s.Paused {
|
||||
@@ -1361,7 +1371,7 @@ func TestIsInMaintenance_UsesCache(t *testing.T) {
|
||||
child := models.Site{ID: 20, Name: "child", Type: "http", ParentID: 10, Status: "UP"}
|
||||
injectSite(e, group)
|
||||
injectSite(e, child)
|
||||
e.refreshMaintenanceCache()
|
||||
e.refreshMaintenanceCache(context.Background())
|
||||
|
||||
if !e.isInMaintenance(10) {
|
||||
t.Error("group should be in maintenance (direct)")
|
||||
@@ -1381,7 +1391,7 @@ func TestIsInMaintenance_GlobalMaintenance(t *testing.T) {
|
||||
e := newTestEngine(ms)
|
||||
site := models.Site{ID: 1, Name: "test", Type: "http", Status: "UP"}
|
||||
injectSite(e, site)
|
||||
e.refreshMaintenanceCache()
|
||||
e.refreshMaintenanceCache(context.Background())
|
||||
|
||||
if !e.isInMaintenance(1) {
|
||||
t.Error("all monitors should be in maintenance during global window")
|
||||
|
||||
Reference in New Issue
Block a user