package main import ( _ "apiserver/docs" "apiserver/backend" "apiserver/backend/auth" "apiserver/middleware" "apiserver/router" "apiserver/store" "apiserver/utils" logger "apiserver/utils" "context" "flag" "fmt" "io" "os" "apiserver/controllers" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // GitCommit and BuildTime are injected at link time via // `-ldflags "-X main.GitCommit=... -X main.BuildTime=..."` (see // src/tools/go_build and src/frontend/Makefile's build-server target). The // "unknown" defaults matter: a bare `go build`/`go run` that bypasses that // ldflags injection must still report "unknown" rather than an empty // string, matching this feature's unknown-fallback requirement (see // xce_version_field() in src/lib/utils.c for the equivalent C-side // convention). var ( GitCommit = "unknown" BuildTime = "unknown" ) func main() { gin.SetMode(gin.ReleaseMode) gin.DefaultWriter = io.Discard gin.DefaultErrorWriter = io.Discard topDir := flag.String("d", "", "Top directory (default: current directory)") versionFlag := flag.Bool("version", false, "Print version information and exit") flag.Usage = func() { fmt.Fprintln(os.Stderr, ` Usage: apiserver -d Top directory (default: current directory) `) } flag.Parse() // Checked before utils.InitLogger() runs so -version has no logging // side effects: it must only print and exit, matching controller's/ // scheduler's/worker's own --version contract (Requirement 2.1). if *versionFlag { fmt.Printf("xce-apiserver %s built %s\n", GitCommit, BuildTime) return } utils.Global_TopDir = utils.PromptForTopDir(topDir) if err := utils.InitLogger(); err != nil { os.Exit(1) } logger.Info("********** Initialize Logger done!", zap.String("gitCommit", GitCommit), zap.String("buildTime", BuildTime)) if err := utils.InitConf(); err != nil { logger.Error("Failed to initialize configuration files", zap.Error(err)) os.Exit(1) } logger.Info("********** Initialize configuration files done!") // Initialize in-memory stores store.InitStores() logger.Info("********** Initialize in-memory stores done!") // Connect to the XCE controller backend for live data. // StartNotificationListener must be called before RegisterToBackend so the // handler is ready before any push events can arrive. if utils.BackendYamlConfig != nil && utils.BackendYamlConfig.Address != "" { handler := &backend.DefaultHandler{} if err := backend.StartNotificationListener(handler); err != nil { logger.Error("Failed to start notification listener", zap.Error(err)) os.Exit(1) } bcfg := &backend.BackendConfig{ Address: utils.BackendYamlConfig.Address, Port: utils.BackendYamlConfig.Port, From: utils.BackendYamlConfig.From, Key: utils.BackendYamlConfig.Key, Peers: utils.BackendYamlConfig.Peers, } if err := backend.RegisterToBackend(bcfg); err != nil { logger.Error("Failed to connect to backend", zap.Error(err)) os.Exit(1) } logger.Info("********** Backend connection registered!") backend.StartHeartbeat(context.Background()) } else { logger.Info("********** Backend not configured — running in stub mode") } // Load RBAC config rbacPath := utils.Global_TopDir + "/config/rbac.yaml" if err := controllers.LoadRBACConfig(rbacPath); err != nil { logger.Error("Failed to load RBAC config", zap.Error(err)) os.Exit(1) } logger.Info("********** Load RBAC config done!") // Initialize auth provider authConfigPath := utils.Global_TopDir + "/config/auth.yaml" if err := auth.InitAuthProvider(authConfigPath); err != nil { logger.Error("Failed to initialize auth provider", zap.Error(err)) os.Exit(1) } logger.Info("********** Initialize auth provider done! (" + auth.GlobalProvider.Name() + ")") // Pre-populate user-role store from auth provider so permissions work // immediately after restart without requiring users to re-login. if seeder, ok := auth.GlobalProvider.(auth.UserRoleSeeder); ok { for _, username := range seeder.AllUsers() { for _, role := range auth.GlobalProvider.GetUserRoles(username) { store.AddRoleForUser(username, role) } } logger.Info("********** Pre-populated user-role store from auth provider done!") } r := router.Router() if err := middleware.StartServer(r); err != nil { logger.Error("Server failed to start:", zap.Error(err)) os.Exit(1) } }