Files
homelab-frontend/cmd/gateway/main.go
T
Admin Bot c237b633e9
CI / Vet, test, build (push) Successful in 3m52s
CI / Build and push image (push) Successful in 1m42s
fix: route klog to stderr so k8s client errors are visible
2026-08-26 14:55:06 -07:00

125 lines
3.5 KiB
Go

package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/config"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/proxy"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/server"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/serviceadapter"
"forgejo.riotpiao.com/rock/homelab-frontend/internal/temporal"
)
func main() {
// Load configuration
cfg, err := config.Load()
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
os.Exit(1)
}
// Determine if auth is enabled by checking if any route requires it
authEnabled := false
for _, route := range cfg.Routes {
if route.Upstream.AuthRequired {
authEnabled = true
break
}
}
// Create the reverse proxy handler that routes requests based on configuration
upstreamHandler := proxy.New(cfg)
// Create the Temporal workflow handler
temporalHostPort := os.Getenv("TEMPORAL_HOST_PORT")
if temporalHostPort == "" {
temporalHostPort = "localhost:7233"
}
log.Printf("Temporal server: %s", temporalHostPort)
temporalHandler := temporal.NewHandler(temporalHostPort)
// Create server with health checker
srv := server.New(cfg.ListenAddr, cfg.ShutdownTimeout, nil)
// Initialize health checker with config validity and auth status
healthChecker := server.NewHealthChecker(true, authEnabled)
srv.SetHealthChecker(healthChecker)
// Route klog (used by k8s client-go) to stderr so reflector errors are visible
klog.InitFlags(nil)
klog.SetOutput(os.Stderr)
// Create ServiceAdapter registry and dispatcher (phase 8)
registry := serviceadapter.NewRegistry(nil)
dispatcher := serviceadapter.NewDispatcher(registry)
// Start ServiceAdapter informer to watch CRDs (8.1 integration)
var informerMgr *serviceadapter.InformerManager
kubeConfig, err := rest.InClusterConfig()
if err != nil {
log.Printf("ServiceAdapter informer disabled: %v", err)
} else {
dynamicClient, err := dynamic.NewForConfig(kubeConfig)
if err != nil {
log.Printf("ServiceAdapter informer: failed to create k8s client: %v", err)
} else {
mgr, err := serviceadapter.NewInformerManager(dynamicClient, registry, "api")
if err != nil {
log.Printf("ServiceAdapter informer: failed to create manager: %v", err)
} else {
if err := mgr.Start(context.Background()); err != nil {
log.Printf("ServiceAdapter informer: failed to start: %v", err)
} else {
informerMgr = mgr
log.Printf("ServiceAdapter informer started")
}
}
}
}
// Create router that handles health endpoints, X-Service (ServiceAdapter) routing,
// temporal endpoints, and passes others to upstream handler
router := server.NewRouter(healthChecker, dispatcher, temporalHandler, upstreamHandler)
srv.SetHandler(router)
// Set up signal handling
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
// Start server in a goroutine
var serverErr error
go func() {
log.Printf("gateway listening on %s", srv.Addr())
serverErr = srv.ListenAndServe()
if serverErr != nil && serverErr != http.ErrServerClosed {
log.Printf("server error: %v", serverErr)
}
}()
// Wait for shutdown signal
sig := <-sigChan
log.Printf("received signal: %v", sig)
if informerMgr != nil {
informerMgr.Stop()
}
if err := srv.Shutdown(context.Background()); err != nil {
fmt.Fprintf(os.Stderr, "shutdown error: %v\n", err)
os.Exit(1)
}
log.Printf("gateway shutdown complete")
os.Exit(0)
}