package main import ( "context" "database/sql" "log" "net/http" "os" "os/signal" "sync" "syscall" "time" "deployment-manager/internal/api" "deployment-manager/internal/db" "deployment-manager/internal/events" "deployment-manager/internal/reconciler" "deployment-manager/internal/worker" _ "github.com/mattn/go-sqlite3" ) const ( dbPath = "./manager.db" maxWorkers = 2 reconcileTick = 2 * time.Second ) func main() { 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", 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 < maxWorkers; i++ { wg.Add(1) go func(id int) { defer wg.Done() w := worker.NewWorker(id, database, eventChan, jobQueue) w.Run(ctx) }(i) } // ---- reconciler ---- reconciler := reconciler.NewReconciler(database, jobQueue, reconcileTick) go reconciler.Run(ctx) // ---- HTTP (API + SSE) ---- httpServer := api.NewHTTPServer(database, eventBus) go func() { log.Println("HTTP server listening on :8080") if err := httpServer.Start(":8080"); 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") }