diff options
-rw-r--r-- | app.go | 102 | ||||
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | go.sum | 5 | ||||
-rw-r--r-- | helpers.go | 11 | ||||
-rw-r--r-- | internal/errortracking/errortracking.go | 10 | ||||
-rw-r--r-- | main.go | 18 |
6 files changed, 87 insertions, 62 deletions
@@ -7,9 +7,7 @@ import ( "fmt" "net" "net/http" - "os" "os/signal" - "sync" "syscall" "time" @@ -21,6 +19,7 @@ import ( "gitlab.com/gitlab-org/labkit/log" labmetrics "gitlab.com/gitlab-org/labkit/metrics" "gitlab.com/gitlab-org/labkit/monitoring" + "golang.org/x/sync/errgroup" "gitlab.com/gitlab-org/gitlab-pages/internal/acme" "gitlab.com/gitlab-org/gitlab-pages/internal/artifact" @@ -247,9 +246,7 @@ func (a *theApp) buildHandlerPipeline() (http.Handler, error) { } // nolint: gocyclo // ignore this -func (a *theApp) Run() { - var wg sync.WaitGroup - +func (a *theApp) Run() error { var limiter *netutil.Limiter if a.config.General.MaxConns > 0 { limiter = netutil.NewLimiterWithMetrics( @@ -264,18 +261,23 @@ func (a *theApp) Run() { // instead of making two nearly identical pipelines commonHandlerPipeline, err := a.buildHandlerPipeline() if err != nil { - log.WithError(err).Fatal("Unable to configure pipeline") + return fmt.Errorf("unable to configure pipeline: %w", err) } proxyHandler := ghandlers.ProxyHeaders(commonHandlerPipeline) httpHandler := a.httpInitialMiddleware(commonHandlerPipeline) + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) + defer stop() + + eg, ctx := errgroup.WithContext(ctx) var servers []*http.Server // Listen for HTTP for _, addr := range a.config.ListenHTTPStrings.Split() { s := a.listen( + eg, addr, httpHandler, errortracking.WithField("listener", request.SchemeHTTP), @@ -288,10 +290,11 @@ func (a *theApp) Run() { for _, addr := range a.config.ListenHTTPSStrings.Split() { tlsConfig, err := a.getTLSConfig() if err != nil { - log.WithError(err).Fatal("Unable to retrieve tls config") + return fmt.Errorf("unable to retrieve tls config: %w", err) } s := a.listen( + eg, addr, httpHandler, errortracking.WithField("listener", request.SchemeHTTPS), @@ -304,6 +307,7 @@ func (a *theApp) Run() { // Listen for HTTP proxy requests for _, addr := range a.config.ListenProxyStrings.Split() { s := a.listen( + eg, addr, proxyHandler, errortracking.WithField("listener", "http proxy"), @@ -316,10 +320,11 @@ func (a *theApp) Run() { for _, addr := range a.config.ListenHTTPSProxyv2Strings.Split() { tlsConfig, err := a.getTLSConfig() if err != nil { - log.WithError(err).Fatal("Unable to retrieve tls config") + return fmt.Errorf("unable to retrieve tls config: %w", err) } s := a.listen( + eg, addr, httpHandler, errortracking.WithField("listener", "https proxy"), @@ -332,13 +337,11 @@ func (a *theApp) Run() { // Serve metrics for Prometheus if a.config.General.MetricsAddress != "" { - a.listenMetrics(&wg, a.config.General.MetricsAddress) + s := a.listenMetrics(eg, a.config.General.MetricsAddress) + servers = append(servers, s) } - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) - - <-sigChan + <-ctx.Done() var result *multierror.Error @@ -352,57 +355,79 @@ func (a *theApp) Run() { cancel() } + if err := eg.Wait(); err != nil { + result = multierror.Append(result, err) + } + if result.ErrorOrNil() != nil { - capturingFatal(result) + errortracking.CaptureErrWithStackTrace(result.ErrorOrNil()) + return result.ErrorOrNil() } + + return nil } -func (a *theApp) listen(addr string, h http.Handler, errTrackingOpt errortracking.CaptureOption, opts ...option) *http.Server { +func (a *theApp) listen(eg *errgroup.Group, addr string, h http.Handler, errTrackingOpt errortracking.CaptureOption, opts ...option) *http.Server { server := &http.Server{} - go func() { + eg.Go(func() error { if err := a.listenAndServe(server, addr, h, opts...); err != nil && !errors.Is(err, http.ErrServerClosed) { - capturingFatal(err, errTrackingOpt) + errortracking.CaptureErrWithStackTrace(err, errTrackingOpt) + return err } - }() + + return nil + }) return server } -func (a *theApp) listenMetrics(wg *sync.WaitGroup, addr string) { - wg.Add(1) - go func() { - defer wg.Done() - +func (a *theApp) listenMetrics(eg *errgroup.Group, addr string) *http.Server { + server := &http.Server{} + eg.Go(func() error { l, err := net.Listen("tcp", addr) if err != nil { - capturingFatal(fmt.Errorf("failed to listen on addr %s: %w", addr, err), errortracking.WithField("listener", "metrics")) + errortracking.CaptureErrWithStackTrace(err, errortracking.WithField("listener", "metrics")) + return fmt.Errorf("failed to listen on addr %s: %w", addr, err) } monitoringOpts := []monitoring.Option{ monitoring.WithBuildInformation(VERSION, ""), monitoring.WithListener(l), + monitoring.WithServer(server), } err = monitoring.Start(monitoringOpts...) - if err != nil { - capturingFatal(err, errortracking.WithField("listener", "metrics")) + if err != nil && !errors.Is(err, http.ErrServerClosed) { + errortracking.CaptureErrWithStackTrace(err, errortracking.WithField("listener", "metrics")) + return err } - }() + + return nil + }) + + return server } -func runApp(config *cfg.Config) { +func runApp(config *cfg.Config) error { source, err := gitlab.New(&config.GitLab) if err != nil { - log.WithError(err).Fatal("could not create domains config source") + return fmt.Errorf("could not create domains config source: %w", err) } a := theApp{config: config, source: source} + err = logging.ConfigureLogging(a.config.Log.Format, a.config.Log.Verbose) + if err != nil { + return fmt.Errorf("failed to initialize logging: %w", err) + } + if config.ArtifactsServer.URL != "" { a.Artifact = artifact.New(config.ArtifactsServer.URL, config.ArtifactsServer.TimeoutSeconds, config.General.Domain) } - a.setAuth(config) + if err := a.setAuth(config); err != nil { + return err + } a.Handlers = handlers.New(a.Auth, a.Artifact) @@ -416,7 +441,7 @@ func runApp(config *cfg.Config) { if len(config.General.CustomHeaders) != 0 { customHeaders, err := customheaders.ParseHeaderString(config.General.CustomHeaders) if err != nil { - log.WithError(err).Fatal("Unable to parse header string") + return fmt.Errorf("unable to parse header string: %w", err) } a.CustomHeaders = customHeaders } @@ -428,28 +453,25 @@ func runApp(config *cfg.Config) { // TODO: reconfigure all VFS' // https://gitlab.com/gitlab-org/gitlab-pages/-/issues/512 if err := zip.Instance().Reconfigure(config); err != nil { - fatal(err, "failed to reconfigure zip VFS") + return fmt.Errorf("failed to reconfigure zip VFS: %w", err) } - a.Run() + return a.Run() } -func (a *theApp) setAuth(config *cfg.Config) { +func (a *theApp) setAuth(config *cfg.Config) error { if config.Authentication.ClientID == "" { - return + return nil } var err error a.Auth, err = auth.New(config.General.Domain, config.Authentication.Secret, config.Authentication.ClientID, config.Authentication.ClientSecret, config.Authentication.RedirectURI, config.GitLab.InternalServer, config.GitLab.PublicServer, config.Authentication.Scope, config.Authentication.Timeout) if err != nil { - log.WithError(err).Fatal("could not initialize auth package") + return fmt.Errorf("could not initialize auth package: %w", err) } -} -// fatal will log a fatal error and exit. -func fatal(err error, message string) { - log.WithError(err).Fatal(message) + return nil } // handlePanicMiddleware logs and captures the recover() information from any panic @@ -26,9 +26,10 @@ require ( gitlab.com/feistel/go-contentencoding v1.0.0 gitlab.com/gitlab-org/go-mimedb v1.45.0 gitlab.com/gitlab-org/golang-archive-zip v0.1.1 - gitlab.com/gitlab-org/labkit v1.14.0 + gitlab.com/gitlab-org/labkit v1.14.1-0.20220513075046-c39e3ddcade2 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/net v0.0.0-20211008194852-3b03d305991f + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 ) @@ -460,8 +460,8 @@ gitlab.com/gitlab-org/go-mimedb v1.45.0 h1:PO8dx6HEWzPYU6MQTYnCbpQEJzhJLW/Bh43+2 gitlab.com/gitlab-org/go-mimedb v1.45.0/go.mod h1:wa9y/zOSFKmTXLyBs4clz2FNVhZQmmEQM9TxslPAjZ0= gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE= gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE= -gitlab.com/gitlab-org/labkit v1.14.0 h1:LSrvHgybidPyH8fHnsy1GBghrLR4kFObFrtZwUfCgAI= -gitlab.com/gitlab-org/labkit v1.14.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc= +gitlab.com/gitlab-org/labkit v1.14.1-0.20220513075046-c39e3ddcade2 h1:gTAxadIvetz0ljXPpLnE4Vv7vYDM91YXg2eZ9DOZWQ4= +gitlab.com/gitlab-org/labkit v1.14.1-0.20220513075046-c39e3ddcade2/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -597,6 +597,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/helpers.go b/helpers.go deleted file mode 100644 index 00d6d55b..00000000 --- a/helpers.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "gitlab.com/gitlab-org/labkit/errortracking" -) - -func capturingFatal(err error, fields ...errortracking.CaptureOption) { - fields = append(fields, errortracking.WithStackTrace()) - errortracking.Capture(err, fields...) - fatal(err, "capturing fatal") -} diff --git a/internal/errortracking/errortracking.go b/internal/errortracking/errortracking.go index d82674cc..8ac0fa4b 100644 --- a/internal/errortracking/errortracking.go +++ b/internal/errortracking/errortracking.go @@ -25,3 +25,13 @@ func CaptureErrWithReqAndStackTrace(err error, r *http.Request, fields ...errort errortracking.Capture(err, opts...) } + +// CaptureErrWithStackTrace calls labkit's errortracking function and attaches the stack trace and any additional fields +func CaptureErrWithStackTrace(err error, fields ...errortracking.CaptureOption) { + opts := append( + fields, + errortracking.WithStackTrace(), + ) + + errortracking.Capture(err, opts...) +} @@ -31,9 +31,9 @@ func initErrorReporting(sentryDSN, sentryEnvironment string) error { errortracking.WithSentryEnvironment(sentryEnvironment)) } -func appMain() { +func appMain() error { if err := validateargs.NotAllowed(os.Args[1:]); err != nil { - log.WithError(err).Fatal("Using invalid arguments, use -config=gitlab-pages-config file instead") + return fmt.Errorf("using invalid arguments, use -config=gitlab-pages-config file instead: %w", err) } if err := validateargs.Deprecated(os.Args[1:]); err != nil { @@ -42,13 +42,13 @@ func appMain() { config, err := cfg.LoadConfig() if err != nil { - log.WithError(err).Fatal("Failed to load config") + return fmt.Errorf("failed to load config: %w", err) } printVersion(config.General.ShowVersion, VERSION) if err := cfg.Validate(config); err != nil { - log.WithError(err).Fatal("invalid config settings") + return fmt.Errorf("invalid config settings: %w", err) } if config.Sentry.DSN != "" { @@ -60,7 +60,7 @@ func appMain() { err = logging.ConfigureLogging(config.Log.Format, config.Log.Verbose) if err != nil { - log.WithError(err).Fatal("Failed to initialize logging") + return fmt.Errorf("failed to initialize logging: %w", err) } cfg.LogConfig(config) @@ -72,11 +72,11 @@ func appMain() { log.Info("URL: https://gitlab.com/gitlab-org/gitlab-pages") if err := os.Chdir(config.General.RootDir); err != nil { - fatal(err, "could not change directory into pagesRoot") + return fmt.Errorf("could not change directory into pagesRoot: %w", err) } fips.Check() - runApp(config) + return runApp(config) } func printVersion(showVersion bool, version string) { @@ -93,5 +93,7 @@ func main() { metrics.MustRegister() - appMain() + if err := appMain(); err != nil { + log.WithError(err).Fatal(err) + } } |