aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/mirum-server/main.go')
-rw-r--r--cmd/mirum-server/main.go253+0 −253
1 files changed, 0 insertions, 253 deletions
diff --git a/cmd/mirum-server/main.go b/cmd/mirum-server/main.go
deleted file mode 100644
--- a/cmd/mirum-server/main.go
+++ /dev/null
@@ -1,253 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-package main
-
-import (
- "context"
- "errors"
- "fmt"
- "log/slog"
- "net"
- "net/http"
- "os"
-
- "dimidiumlabs/mirum/cmd/mirum-server/apipb/apipbconnect"
- "dimidiumlabs/mirum/internal/config"
- "dimidiumlabs/mirum/internal/forges"
- "dimidiumlabs/mirum/internal/protocol/wirepb"
- "dimidiumlabs/mirum/internal/supervisor"
-
- "github.com/spf13/cobra"
-)
-
-func hardenServer(s *http.Server) *http.Server {
- s.IdleTimeout = config.HTTPIdleTimeout
- s.MaxHeaderBytes = config.HTTPMaxHeaderBytes
- s.ReadHeaderTimeout = config.HTTPReadHeaderTimeout
- return s
-}
-
-func main() {
- var socketPath string
-
- root := &cobra.Command{Use: "mirum-server", Short: "Mirum CI server"}
- root.PersistentFlags().StringVar(&socketPath, "socket", "", "admin socket path (default from config or /run/mirum-server/admin.sock)")
- root.AddGroup(&cobra.Group{ID: "main", Title: "Commands:"})
-
- daemonCmd := &cobra.Command{
- Use: "daemon",
- Short: "Start the server",
- GroupID: "main",
- SilenceUsage: true,
- SilenceErrors: true,
- RunE: func(cmd *cobra.Command, args []string) error {
- configFile, _ := cmd.Flags().GetString("config")
- return daemon(configFile, socketPath)
- },
- }
- daemonCmd.Flags().String("config", "", "path to config file")
- _ = daemonCmd.MarkFlagRequired("config")
- root.AddCommand(daemonCmd)
-
- // Console subcommands are generated from api.proto via reflection.
- buildConsoleCLI(root, func() apipbconnect.ConsoleClient { return consoleClient(socketPath) })
- for _, c := range root.Commands() {
- if c.GroupID == "" {
- c.GroupID = "main"
- }
- }
-
- if err := root.Execute(); err != nil {
- os.Exit(1)
- }
-}
-
-func daemon(configFile, socketFlag string) error {
- cfg, err := getConfig(configFile)
- if err != nil {
- slog.Error("config parsing failed", "err", err)
- return err
- }
-
- if socketFlag != "" {
- cfg.AdminSocket = socketFlag
- }
-
- slog.Info("config loaded", "configfile", configFile)
-
- sup := supervisor.Detect()
- ctx, cancel := context.WithCancel(sup.WaitForStop(context.Background()))
- defer cancel()
-
- db, err := DatabaseOpen(ctx, cfg.DatabaseUri)
- if err != nil {
- slog.Error("couldn't open database", "err", err)
- return err
- }
-
- srv := &server{
- db: db,
- cfg: cfg,
- forge: &forges.GitHub{Secret: cfg.WebhookSecret, Token: cfg.GitHubToken},
- queue: make(chan *wirepb.Task, config.TaskQueueCapacity),
- }
- defer srv.Close()
-
- if err := db.Migrate(ctx); err != nil {
- slog.Error("migration failed", "err", err)
- return err
- }
-
- slog.Info("database ready")
-
- go srv.PurgeSessions(ctx)
-
- consolePath, consoleHandler := NewConsoleHandler(srv)
-
- webSrv := hardenServer(NewWebServer(ctx, srv, consolePath, consoleHandler))
- grpcSrv := hardenServer(NewGrpcServer(ctx, srv))
-
- adminMux := http.NewServeMux()
- adminMux.Handle(consolePath, consoleHandler)
- adminSrv := hardenServer(&http.Server{
- Handler: adminMux,
- ConnContext: func(ctx context.Context, _ net.Conn) context.Context {
- return context.WithValue(ctx, actorKey{}, OperatorActor())
- },
- BaseContext: func(_ net.Listener) context.Context {
- return ctx
- },
- })
-
- grpcLn, webLn, adminLn, err := listeners(cfg, sup)
- if err != nil {
- slog.Error("listeners failed", "err", err)
- return err
- }
-
- slog.Info("listening", "grpc", grpcLn.Addr(), "web", webLn.Addr(), "admin", cfg.AdminSocket)
-
- errs := make(chan error, 3)
- serve := func(name string, fn func() error) {
- go func() {
- err := fn()
- if errors.Is(err, http.ErrServerClosed) {
- err = nil
- }
- if err != nil {
- err = fmt.Errorf("%s server: %w", name, err)
- }
- errs <- err
- }()
- }
- serve("web", func() error {
- if webSrv.TLSConfig != nil {
- return webSrv.ServeTLS(webLn, "", "")
- }
- return webSrv.Serve(webLn)
- })
- serve("grpc", func() error { return grpcSrv.ServeTLS(grpcLn, "", "") })
- serve("admin", func() error { return adminSrv.Serve(adminLn) })
-
- sup.Ready()
- go sup.StartWatchdog(ctx)
-
- var runErr error
- select {
- case <-ctx.Done():
- slog.Info("shutting down")
- case err := <-errs:
- runErr = err
- // Propagate the crash to all handler contexts so Poll and
- // other long-lived RPCs exit via ctx.Done(); Shutdown below
- // then completes without waiting on them.
- cancel()
- if err != nil {
- slog.Error("server exited, shutting down peers", "err", err)
- } else {
- slog.Warn("server exited unexpectedly, shutting down peers")
- }
- }
-
- sup.Stopping()
-
- shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), config.HTTPShutdownTimeout)
- defer shutdownCancel()
-
- shutdown := func(name string, s *http.Server) {
- if err := s.Shutdown(shutdownCtx); err != nil {
- slog.Error("server shutdown", "name", name, "err", err)
- }
- }
-
- shutdown("web", webSrv)
- shutdown("grpc", grpcSrv)
- shutdown("admin", adminSrv)
-
- return runErr
-}
-
-// listeners returns gRPC, web, and admin listeners.
-// With systemd socket activation it expects two named fds: "grpc" and "web".
-// Without socket activation it falls back to configured addresses.
-func listeners(cfg *appConfig, sup supervisor.Supervisor) (grpcLn, webLn, adminLn net.Listener, err error) {
- named, err := sup.ActivationListeners()
- if err != nil {
- return nil, nil, nil, fmt.Errorf("socket activation: %w", err)
- }
-
- if lns := named["grpc"]; len(lns) > 0 {
- grpcLn = lns[0]
- } else if grpcLn, err = net.Listen("tcp", cfg.GrpcAddr); err != nil {
- return nil, nil, nil, err
- }
- defer func() {
- if err != nil && grpcLn != nil {
- _ = grpcLn.Close()
- }
- }()
-
- if lns := named["web"]; len(lns) > 0 {
- webLn = lns[0]
- } else if webLn, err = net.Listen("tcp", cfg.WebAddr); err != nil {
- return nil, nil, nil, err
- }
- defer func() {
- if err != nil && webLn != nil {
- _ = webLn.Close()
- }
- }()
-
- _ = os.Remove(cfg.AdminSocket)
- if adminLn, err = net.Listen("unix", cfg.AdminSocket); err != nil {
- return nil, nil, nil, err
- }
- defer func() {
- if err != nil && adminLn != nil {
- _ = adminLn.Close()
- }
- }()
-
- if err = os.Chmod(cfg.AdminSocket, 0o660); err != nil {
- return nil, nil, nil, fmt.Errorf("chmod admin socket: %w", err)
- }
-
- return grpcLn, webLn, adminLn, nil
-}
-
-func consoleClient(socketPath string) apipbconnect.ConsoleClient {
- if socketPath == "" {
- socketPath = "/run/mirum-server/admin.sock"
- }
- return apipbconnect.NewConsoleClient(
- &http.Client{
- Transport: &http.Transport{
- DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
- return net.Dial("unix", socketPath)
- },
- },
- },
- "http://localhost.unix",
- )
-}