package main import ( "context" "database/sql" "log" "net/http" "os" "os/signal" "sync" "syscall" "deployment-manager/internal/api" "deployment-manager/internal/config" "deployment-manager/internal/db" "deployment-manager/internal/events" "deployment-manager/internal/reconciler" "deployment-manager/internal/worker" _ "github.com/mattn/go-sqlite3" ) func main() { cfg := config.Load() ctx, cancel := context.WithCancel(context.Background()) defer cancel() // ---- graceful shutdown ---- sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) go func() { <-sig log.Println("shutdown signal received") cancel() }() // ---- DB ---- database, err := sql.Open("sqlite3", cfg.DBPath) if err != nil { log.Fatal(err) } defer database.Close() if err := db.Migrate(database); err != nil { log.Fatal(err) } // ---- channels ---- jobQueue := make(chan int64, 100) // ---- event bus ---- eventBus := events.NewBus() // Subscribe to events for workers eventChan := eventBus.Subscribe(ctx) // ---- worker pool ---- var wg sync.WaitGroup for i := 0; i < cfg.MaxWorkers; i++ { wg.Add(1) go func(id int) { defer wg.Done() w := worker.NewWorker(id, database, eventChan, jobQueue, cfg.Kubeconfig, cfg.Namespace) w.Run(ctx) }(i) } // ---- reconciler ---- reconciler := reconciler.NewReconciler(database, jobQueue, cfg.ReconcileTick) go reconciler.Run(ctx) // ---- HTTP (API + SSE) ---- httpServer := api.NewHTTPServer(database, eventBus) port := ":" + cfg.HTTPPort go func() { log.Printf("HTTP server listening on %s", port) if err := httpServer.Start(port); err != nil && err != http.ErrServerClosed { log.Fatal(err) } }() <-ctx.Done() log.Println("shutting down...") httpServer.Shutdown(context.Background()) close(jobQueue) wg.Wait() log.Println("bye") }