package main import ( "context" "log" "net/http" "os" "os/signal" "strings" "syscall" "time" "evobgp/internal/birdfmt" "evobgp/internal/config" "evobgp/internal/dbcli" "evobgp/internal/httpapi" "evobgp/internal/observability" "evobgp/internal/platform" "evobgp/internal/version" ) func main() { if len(os.Args) > 1 && os.Args[1] == "db" { os.Exit(dbcli.Run(os.Args[2:])) } cfg := config.Load() seedDemo := os.Getenv("EVOBGP_SEED_DEMO") != "0" opts := httpapi.Options{ APIKeys: os.Getenv("EVOBGP_API_KEYS"), DatabaseURL: cfg.DatabaseURL, InsecureDev: os.Getenv("EVOBGP_DEV_INSECURE") == "1", SeedDemo: seedDemo, BundleSeedHex: strings.TrimSpace(os.Getenv("EVOBGP_BUNDLE_SEED_HEX")), CORSAllowedOrigins: strings.TrimSpace(os.Getenv("EVOBGP_CORS_ORIGINS")), } srv, err := httpapi.New(opts) if err != nil { log.Fatal(err) } defer srv.Close() observability.SetBuildInfo(version.Version, version.GitSHA) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() srv.StartBackground(ctx) startBirdMetricsPoller(ctx) httpSrv := &http.Server{ Addr: cfg.HTTPAddr, Handler: srv.Handler(), ReadHeaderTimeout: 10 * time.Second, ReadTimeout: 60 * time.Second, WriteTimeout: 120 * time.Second, IdleTimeout: 120 * time.Second, } go func() { svc := platform.ServiceName("evobgp-api") log.Printf("%s listening on %s", svc, cfg.HTTPAddr) log.Printf("bundle signing public key (base64, set on evobgp-node): %s", srv.BundlePublicKeyBase64()) if opts.SeedDemo { tid, mCDN, mIP, rev, sp := srv.Store().DemoIDs() log.Printf("demo tenant=%s module_cdn=%s module_ip_ranges=%s revision=%s speaker=%s", tid, mCDN, mIP, rev, sp) log.Printf("example: EVOBGP_API_KEYS=op|%s|operator,node|%s|node", tid, tid) log.Printf("demo auth: Authorization: Bearer dev (operator, demo tenant only)") } if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatal(err) } }() <-ctx.Done() shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() if err := httpSrv.Shutdown(shutdownCtx); err != nil { log.Printf("HTTP shutdown: %v", err) } log.Printf("%s stopped", platform.ServiceName("evobgp-api")) } func startBirdMetricsPoller(ctx context.Context) { sock := strings.TrimSpace(os.Getenv("EVOBGP_BIRDC_SOCKET")) if sock == "" { return } interval := 30 * time.Second if d, err := time.ParseDuration(strings.TrimSpace(os.Getenv("EVOBGP_BIRDC_INTERVAL"))); err == nil && d > 0 { interval = d } bin := strings.TrimSpace(os.Getenv("EVOBGP_BIRDC_BIN")) observability.StartBirdProtocolsPoller(ctx, sock, bin, interval, func(ctx context.Context, socket, birdcBin string) (string, error) { return birdfmt.ShowProtocols(ctx, socket, birdcBin) }, birdfmt.CountEstablishedBGPSessions, birdfmt.ParseBGPProtocolStates, ) log.Printf("birdc protocols poller enabled (socket=%s interval=%s)", sock, interval) }