From 25daec55167da673911524e3f60955f971a5e41b Mon Sep 17 00:00:00 2001 From: TYuan0816 Date: Thu, 9 May 2024 23:40:46 +0800 Subject: [PATCH] refactor: sbi server --- cmd/main.go | 19 +++- internal/logger/logger.go | 2 + internal/sbi/server.go | 123 +++++++++++++++++++++++++ pkg/app/app.go | 18 ++++ pkg/factory/config.go | 54 +++++++++++ pkg/service/init.go | 189 ++++++++++++++++++++++---------------- 6 files changed, 325 insertions(+), 80 deletions(-) create mode 100644 internal/sbi/server.go create mode 100644 pkg/app/app.go diff --git a/cmd/main.go b/cmd/main.go index 8f0bdb4..acbfef8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -9,9 +9,12 @@ package main import ( + "context" "os" + "os/signal" "path/filepath" "runtime/debug" + "syscall" "github.com/urfave/cli" @@ -60,19 +63,31 @@ func action(cliCtx *cli.Context) error { logger.MainLog.Infoln("AUSF version: ", version.GetVersion()) + ctx, cancel := context.WithCancel(context.Background()) + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) + + go func() { + <-sigCh // Wait for interrupt signal to gracefully shutdown + cancel() // Notify each goroutine and wait them stopped + }() + cfg, err := factory.ReadConfig(cliCtx.String("config")) if err != nil { + sigCh <- nil return err } factory.AusfConfig = cfg - ausf, err := service.NewApp(cfg) + ausf, err := service.NewApp(ctx, cfg, tlsKeyLogPath) if err != nil { + sigCh <- nil return err } AUSF = ausf - ausf.Start(tlsKeyLogPath) + ausf.Start() + AUSF.WaitRoutineStopped() return nil } diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 90993e0..76327e9 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -13,6 +13,7 @@ var ( InitLog *logrus.Entry CfgLog *logrus.Entry CtxLog *logrus.Entry + SBILog *logrus.Entry GinLog *logrus.Entry ConsumerLog *logrus.Entry UeAuthLog *logrus.Entry @@ -33,6 +34,7 @@ func init() { InitLog = NfLog.WithField(logger_util.FieldCategory, "Init") CfgLog = NfLog.WithField(logger_util.FieldCategory, "CFG") CtxLog = NfLog.WithField(logger_util.FieldCategory, "CTX") + SBILog = NfLog.WithField(logger_util.FieldCategory, "SBI") GinLog = NfLog.WithField(logger_util.FieldCategory, "GIN") ConsumerLog = NfLog.WithField(logger_util.FieldCategory, "Consumer") UeAuthLog = NfLog.WithField(logger_util.FieldCategory, "UeAuth") diff --git a/internal/sbi/server.go b/internal/sbi/server.go new file mode 100644 index 0000000..5498f43 --- /dev/null +++ b/internal/sbi/server.go @@ -0,0 +1,123 @@ +package sbi + +import ( + "context" + "fmt" + "log" + "net/http" + "runtime/debug" + "sync" + "time" + + "github.com/free5gc/ausf/pkg/app" + "github.com/free5gc/ausf/pkg/factory" + + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/ausf/internal/sbi/consumer" + "github.com/free5gc/ausf/internal/sbi/processor" + "github.com/free5gc/ausf/internal/util" + "github.com/free5gc/openapi/models" + "github.com/free5gc/util/httpwrapper" + logger_util "github.com/free5gc/util/logger" + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" +) + +type ServerAusf interface { + app.App + + Consumer() *consumer.Consumer + Processor() *processor.Processor +} + +type Server struct { + ServerAusf + + httpServer *http.Server + router *gin.Engine +} + +func NewServer(ausf ServerAusf, tlsKeyLogPath string) (*Server, error) { + s := &Server{ + ServerAusf: ausf, + router: logger_util.NewGinWithLogrus(logger.GinLog), + } + + routes := s.getUeAuthenticationRoutes() + group := s.router.Group(factory.AusfAuthResUriPrefix) + routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NAUSF_AUTH) + group.Use(func(c *gin.Context) { + routerAuthorizationCheck.Check(c, ausf_context.GetSelf()) + }) + applyRoutes(group, routes) + + cfg := s.Config() + bindAddr := cfg.GetSbiBindingAddr() + logger.SBILog.Infof("Binding addr: [%s]", bindAddr) + var err error + if s.httpServer, err = httpwrapper.NewHttp2Server(bindAddr, tlsKeyLogPath, s.router); err != nil { + logger.InitLog.Errorf("Initialize HTTP server failed: %v", err) + return nil, err + } + s.httpServer.ErrorLog = log.New(logger.SBILog.WriterLevel(logrus.ErrorLevel), "HTTP2: ", 0) + + return s, nil +} + +func (s *Server) Run(traceCtx context.Context, wg *sync.WaitGroup) error { + var err error + _, s.Context().NfId, err = s.Consumer().RegisterNFInstance(context.Background()) + if err != nil { + logger.InitLog.Errorf("AUSF register to NRF Error[%s]", err.Error()) + } + + wg.Add(1) + go s.startServer(wg) + + return nil +} + +func (s *Server) Stop() { + const defaultShutdownTimeout time.Duration = 2 * time.Second + + if s.httpServer != nil { + logger.SBILog.Infof("Stop SBI server (listen on %s)", s.httpServer.Addr) + toCtx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout) + defer cancel() + if err := s.httpServer.Shutdown(toCtx); err != nil { + logger.SBILog.Errorf("Could not close SBI server: %#v", err) + } + } +} + +func (s *Server) startServer(wg *sync.WaitGroup) { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.SBILog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + s.Terminate() + } + wg.Done() + }() + + logger.SBILog.Infof("Start SBI server (listen on %s)", s.httpServer.Addr) + + var err error + cfg := s.Config() + scheme := cfg.GetSbiScheme() + if scheme == "http" { + err = s.httpServer.ListenAndServe() + } else if scheme == "https" { + err = s.httpServer.ListenAndServeTLS( + cfg.GetCertPemPath(), + cfg.GetCertKeyPath()) + } else { + err = fmt.Errorf("No support this scheme[%s]", scheme) + } + + if err != nil && err != http.ErrServerClosed { + logger.SBILog.Errorf("SBI server error: %v", err) + } + logger.SBILog.Warnf("SBI server (listen on %s) stopped", s.httpServer.Addr) +} diff --git a/pkg/app/app.go b/pkg/app/app.go new file mode 100644 index 0000000..ef2cbe9 --- /dev/null +++ b/pkg/app/app.go @@ -0,0 +1,18 @@ +package app + +import ( + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/pkg/factory" +) + +type App interface { + SetLogEnable(enable bool) + SetLogLevel(level string) + SetReportCaller(reportCaller bool) + + Start() + Terminate() + + Context() *ausf_context.AUSFContext + Config() *factory.Config +} diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 983da78..e61b852 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -7,6 +7,7 @@ package factory import ( "errors" "fmt" + "os" "strconv" "sync" @@ -235,3 +236,56 @@ func (c *Config) GetLogReportCaller() bool { } return c.Logger.ReportCaller } + +func (c *Config) GetSbiBindingAddr() string { + c.RLock() + defer c.RUnlock() + return c.GetSbiBindingIP() + ":" + strconv.Itoa(c.GetSbiPort()) +} + +func (c *Config) GetSbiBindingIP() string { + c.RLock() + defer c.RUnlock() + bindIP := "0.0.0.0" + if c.Configuration == nil || c.Configuration.Sbi == nil { + return bindIP + } + if c.Configuration.Sbi.BindingIPv4 != "" { + if bindIP = os.Getenv(c.Configuration.Sbi.BindingIPv4); bindIP != "" { + logger.CfgLog.Infof("Parsing ServerIPv4 [%s] from ENV Variable", bindIP) + } else { + bindIP = c.Configuration.Sbi.BindingIPv4 + } + } + return bindIP +} + +func (c *Config) GetSbiPort() int { + c.RLock() + defer c.RUnlock() + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Port != 0 { + return c.Configuration.Sbi.Port + } + return AusfSbiDefaultPort +} + +func (c *Config) GetSbiScheme() string { + c.RLock() + defer c.RUnlock() + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Scheme != "" { + return c.Configuration.Sbi.Scheme + } + return AusfSbiDefaultScheme +} + +func (c *Config) GetCertPemPath() string { + c.RLock() + defer c.RUnlock() + return c.Configuration.Sbi.Tls.Pem +} + +func (c *Config) GetCertKeyPath() string { + c.RLock() + defer c.RUnlock() + return c.Configuration.Sbi.Tls.Key +} diff --git a/pkg/service/init.go b/pkg/service/init.go index ac478e8..4e59f92 100644 --- a/pkg/service/init.go +++ b/pkg/service/init.go @@ -1,41 +1,99 @@ package service import ( - "fmt" + "context" "io" "os" - "os/signal" "runtime/debug" - "syscall" + "sync" "github.com/sirupsen/logrus" ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/ausf/internal/sbi" "github.com/free5gc/ausf/internal/sbi/consumer" - "github.com/free5gc/ausf/internal/sbi/ueauthentication" + "github.com/free5gc/ausf/internal/sbi/processor" + "github.com/free5gc/ausf/pkg/app" "github.com/free5gc/ausf/pkg/factory" - "github.com/free5gc/util/httpwrapper" - logger_util "github.com/free5gc/util/logger" ) +var AUSF *AusfApp + +var _ app.App = &AusfApp{} + type AusfApp struct { - cfg *factory.Config + app.App + consumer.ConsumerAusf + processor.ProcessorAusf + sbi.ServerAusf + ausfCtx *ausf_context.AUSFContext + cfg *factory.Config + + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + + sbiServer *sbi.Server + consumer *consumer.Consumer + processor *processor.Processor } -func NewApp(cfg *factory.Config) (*AusfApp, error) { - ausf := &AusfApp{cfg: cfg} +func NewApp(ctx context.Context, cfg *factory.Config, tlsKeyLogPath string) (*AusfApp, error) { + ausf := &AusfApp{ + cfg: cfg, + wg: sync.WaitGroup{}, + } ausf.SetLogEnable(cfg.GetLogEnable()) ausf.SetLogLevel(cfg.GetLogLevel()) ausf.SetReportCaller(cfg.GetLogReportCaller()) - ausf_context.Init() + + processor, err_p := processor.NewProcessor(ausf) + if err_p != nil { + return ausf, err_p + } + ausf.processor = processor + + consumer, err := consumer.NewConsumer(ausf) + if err != nil { + return ausf, err + } + ausf.consumer = consumer + + ausf.ctx, ausf.cancel = context.WithCancel(ctx) ausf.ausfCtx = ausf_context.GetSelf() + + if ausf.sbiServer, err = sbi.NewServer(ausf, tlsKeyLogPath); err != nil { + return nil, err + } + AUSF = ausf + return ausf, nil } -func (a *AusfApp) SetLogEnable(enable bool) { +func (a *AusfApp) CancelContext() context.Context { + return a.ctx +} + +func (a *AusfApp) Consumer() *consumer.Consumer { + return a.consumer +} + +func (a *AusfApp) Processor() *processor.Processor { + return a.processor +} + +func (a *AusfApp) Context() *ausf_context.AUSFContext { + return a.ausfCtx +} + +func (a *AusfApp) Config() *factory.Config { + return a.cfg +} + +func (c *AusfApp) SetLogEnable(enable bool) { logger.MainLog.Infof("Log enable is set to [%v]", enable) if enable && logger.Log.Out == os.Stderr { return @@ -43,15 +101,16 @@ func (a *AusfApp) SetLogEnable(enable bool) { return } - a.cfg.SetLogEnable(enable) + c.Config().SetLogEnable(enable) if enable { logger.Log.SetOutput(os.Stderr) } else { logger.Log.SetOutput(io.Discard) + } } -func (a *AusfApp) SetLogLevel(level string) { +func (c *AusfApp) SetLogLevel(level string) { lvl, err := logrus.ParseLevel(level) if err != nil { logger.MainLog.Warnf("Log level [%s] is invalid", level) @@ -63,95 +122,69 @@ func (a *AusfApp) SetLogLevel(level string) { return } - a.cfg.SetLogLevel(level) + c.Config().SetLogLevel(level) logger.Log.SetLevel(lvl) } -func (a *AusfApp) SetReportCaller(reportCaller bool) { +func (c *AusfApp) SetReportCaller(reportCaller bool) { logger.MainLog.Infof("Report Caller is set to [%v]", reportCaller) if reportCaller == logger.Log.ReportCaller { return } - a.cfg.SetLogReportCaller(reportCaller) + c.Config().SetLogReportCaller(reportCaller) logger.Log.SetReportCaller(reportCaller) } -func (a *AusfApp) Start(tlsKeyLogPath string) { +// tlsKeyLogPath have to remove after all NFs are refactor +func (a *AusfApp) Start() { logger.InitLog.Infoln("Server started") - router := logger_util.NewGinWithLogrus(logger.GinLog) - ueauthentication.AddService(router) + a.wg.Add(1) + go a.listenShutdownEvent() - pemPath := factory.AusfDefaultCertPemPath - keyPath := factory.AusfDefaultPrivateKeyPath - sbi := factory.AusfConfig.Configuration.Sbi - if sbi.Tls != nil { - pemPath = sbi.Tls.Pem - keyPath = sbi.Tls.Key - } - - self := a.ausfCtx - // Register to NRF - profile, err := consumer.BuildNFInstance(self) - if err != nil { - logger.InitLog.Error("Build AUSF Profile Error") - } - _, self.NfId, err = consumer.SendRegisterNFInstance(self.NrfUri, self.NfId, profile) - if err != nil { - logger.InitLog.Errorf("AUSF register to NRF Error[%s]", err.Error()) + if err := a.sbiServer.Run(context.Background(), &a.wg); err != nil { + logger.MainLog.Fatalf("Run SBI server failed: %+v", err) } +} - addr := fmt.Sprintf("%s:%d", self.BindingIPv4, self.SBIPort) - - signalChannel := make(chan os.Signal, 1) - signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - <-signalChannel - a.Terminate() - os.Exit(0) +func (a *AusfApp) listenShutdownEvent() { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.MainLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + a.wg.Done() }() - server, err := httpwrapper.NewHttp2Server(addr, tlsKeyLogPath, router) - if server == nil { - logger.InitLog.Errorf("Initialize HTTP server failed: %+v", err) - return - } - - if err != nil { - logger.InitLog.Warnf("Initialize HTTP server: +%v", err) - } - - serverScheme := factory.AusfConfig.Configuration.Sbi.Scheme - if serverScheme == "http" { - err = server.ListenAndServe() - } else if serverScheme == "https" { - err = server.ListenAndServeTLS(pemPath, keyPath) - } - - if err != nil { - logger.InitLog.Fatalf("HTTP server setup failed: %+v", err) - } + <-a.ctx.Done() + a.Terminate() } -func (a *AusfApp) Terminate() { - logger.InitLog.Infof("Terminating AUSF...") +func (c *AusfApp) Terminate() { + logger.MainLog.Infof("Terminating AUSF...") + c.cancel() + c.CallServerStop() + // deregister with NRF - problemDetails, err := consumer.SendDeregisterNFInstance() + problemDetails, err := c.Consumer().SendDeregisterNFInstance() if problemDetails != nil { - logger.InitLog.Errorf("Deregister NF instance Failed Problem[%+v]", problemDetails) + logger.MainLog.Errorf("Deregister NF instance Failed Problem[%+v]", problemDetails) } else if err != nil { - logger.InitLog.Errorf("Deregister NF instance Error[%+v]", err) + logger.MainLog.Errorf("Deregister NF instance Error[%+v]", err) } else { - logger.InitLog.Infof("Deregister from NRF successfully") + logger.MainLog.Infof("Deregister from NRF successfully") + } + logger.MainLog.Infof("AUSF SBI Server terminated") +} + +func (a *AusfApp) CallServerStop() { + if a.sbiServer != nil { + a.sbiServer.Stop() } +} - logger.InitLog.Infof("AUSF terminated") +func (a *AusfApp) WaitRoutineStopped() { + a.wg.Wait() + logger.MainLog.Infof("AUSF App is terminated") }