refactor(monitor): encapsulate engine state, add graceful shutdown and tests
Replace all monitor package-level mutable state with Engine struct. All state (liveState, logStore, histories, tokenIndex, HTTP clients) is now encapsulated in Engine, created via NewEngine(store). Key changes: - Engine struct holds all monitor state with proper mutex protection - Engine.Start(ctx) and monitorRoutine respect context cancellation for graceful shutdown — no more leaked goroutines - cluster.runFollowerLoop also respects context for clean exit - Token index (map[string]int) for O(1) push heartbeat lookup, replacing O(n) linear scan through LiveState - UpdateSiteConfig preserves 8 runtime fields instead of copying 17 config fields individually - triggerAlert goroutines get 30s timeout context - All consumers (TUI, server, cluster, main) receive *Engine via constructor/parameter — no package-level state access - main.go creates context.WithCancel, passes to engine and cluster First test suite: 12 tests across store and alert packages - Store: CRUD for sites/alerts/users, push token generation, import/export round-trip, check history persistence - Alert: Discord/Slack/Webhook payload format, HTTP 4xx error propagation, Ntfy headers, unknown provider returns nil
This commit is contained in:
+19
-12
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go-upkeep/internal/cluster"
|
||||
@@ -68,9 +69,6 @@ func main() {
|
||||
if v := os.Getenv("UPKEEP_CLUSTER_SECRET"); v != "" {
|
||||
clusterKey = v
|
||||
}
|
||||
if os.Getenv("UPKEEP_INSECURE_SKIP_VERIFY") == "true" {
|
||||
monitor.SetInsecureSkipVerify(true)
|
||||
}
|
||||
|
||||
port := flag.Int("port", portVal, "SSH Port")
|
||||
flagDBType := flag.String("db-type", dbType, "Database type")
|
||||
@@ -115,26 +113,34 @@ func main() {
|
||||
fmt.Printf("Imported %d monitors and %d alerts from Uptime Kuma v%s\n", len(backup.Sites), len(backup.Alerts), kb.Version)
|
||||
}
|
||||
|
||||
monitor.InitHistoryFromStore(s)
|
||||
monitor.StartEngine(s)
|
||||
eng := monitor.NewEngine(s)
|
||||
if os.Getenv("UPKEEP_INSECURE_SKIP_VERIFY") == "true" {
|
||||
eng.SetInsecureSkipVerify(true)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
eng.InitHistory()
|
||||
eng.Start(ctx)
|
||||
|
||||
server.Start(server.ServerConfig{
|
||||
Port: httpPort,
|
||||
EnableStatus: enableStatus,
|
||||
Title: statusTitle,
|
||||
ClusterKey: clusterKey,
|
||||
}, s)
|
||||
}, s, eng)
|
||||
|
||||
cluster.Start(cluster.Config{
|
||||
cluster.Start(ctx, cluster.Config{
|
||||
Mode: clusterMode,
|
||||
PeerURL: clusterPeer,
|
||||
SharedKey: clusterKey,
|
||||
})
|
||||
}, eng)
|
||||
|
||||
startSSHServer(*port, s)
|
||||
startSSHServer(*port, s, eng)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||
p := tea.NewProgram(tui.InitialModel(true, s), tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||
p := tea.NewProgram(tui.InitialModel(true, s, eng), tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||
if _, err := p.Run(); err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
}
|
||||
@@ -145,9 +151,10 @@ func main() {
|
||||
<-done
|
||||
fmt.Println("Shutting down...")
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
|
||||
func startSSHServer(port int, db store.Store) {
|
||||
func startSSHServer(port int, db store.Store, eng *monitor.Engine) {
|
||||
s, err := wish.NewServer(
|
||||
wish.WithAddress(fmt.Sprintf(":%d", port)),
|
||||
wish.WithHostKeyPath(".ssh/id_ed25519"),
|
||||
@@ -156,7 +163,7 @@ func startSSHServer(port int, db store.Store) {
|
||||
}),
|
||||
wish.WithMiddleware(
|
||||
bm.Middleware(func(s ssh.Session) (tea.Model, []tea.ProgramOption) {
|
||||
return tui.InitialModel(false, db), []tea.ProgramOption{tea.WithAltScreen(), tea.WithMouseCellMotion()}
|
||||
return tui.InitialModel(false, db, eng), []tea.ProgramOption{tea.WithAltScreen(), tea.WithMouseCellMotion()}
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user