fix(app): include TLS server in graceful shutdown to drain active HTTPS connections
- commit
- 2cd6dfbfdcfb5b764063f6a98af40106cf54c342
- author
- bnema · · 2026-02-25 01:32 UTC
- parent
- eb1babf
- signature
- unsigned
1 file · +24 -20
M internal/app/run.go +24 -20
| @@ -1690,12 +1690,13 @@ func runServers(ctx context.Context, v *viper.Viper, cfg Config, registryHandler | |||
| 1690 | 1690 | ||
| 1691 | 1691 | registrySrv, registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log) | |
| 1692 | 1692 | proxySrv, proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log) | |
| 1693 | + | var tlsSrv *http.Server | |
| 1693 | 1694 | var tlsReady <-chan struct{} | |
| 1694 | 1695 | if cfg.Server.TLSEnabled { | |
| 1695 | 1696 | if cfg.Server.TLSCertFile == "" || cfg.Server.TLSKeyFile == "" { | |
| 1696 | 1697 | return fmt.Errorf("server.tls_enabled=true requires both server.tls_cert_file and server.tls_key_file") | |
| 1697 | 1698 | } | |
| 1698 | - | tlsReady = startTLSServer( | |
| 1699 | + | tlsSrv, tlsReady = startTLSServer( | |
| 1699 | 1700 | fmt.Sprintf(":%d", cfg.Server.TLSPort), | |
| 1700 | 1701 | proxyHandler, | |
| 1701 | 1702 | "proxy-tls", | |
| @@ -1740,7 +1741,7 @@ func runServers(ctx context.Context, v *viper.Viper, cfg Config, registryHandler | |||
| 1740 | 1741 | syncAndAutoStart(ctx, svc, log) | |
| 1741 | 1742 | ||
| 1742 | 1743 | waitForShutdown(ctx, errChan, reloadChan, deployChan, eventBus, log) | |
| 1743 | - | gracefulShutdown(registrySrv, proxySrv, containerSvc, log) | |
| 1744 | + | gracefulShutdown(registrySrv, proxySrv, tlsSrv, containerSvc, log) | |
| 1744 | 1745 | return nil | |
| 1745 | 1746 | } | |
| 1746 | 1747 | ||
| @@ -1940,17 +1941,19 @@ func waitForShutdown(ctx context.Context, errChan <-chan error, reloadChan, depl | |||
| 1940 | 1941 | ||
| 1941 | 1942 | // gracefulShutdown stops HTTP servers with a 30s timeout, then shuts down | |
| 1942 | 1943 | // the container service and cleans up runtime files. | |
| 1943 | - | func gracefulShutdown(registrySrv, proxySrv *http.Server, containerSvc *container.Service, log zerowrap.Logger) { | |
| 1944 | + | func gracefulShutdown(registrySrv, proxySrv, tlsSrv *http.Server, containerSvc *container.Service, log zerowrap.Logger) { | |
| 1944 | 1945 | log.Info().Msg("shutting down Gordon...") | |
| 1945 | 1946 | ||
| 1946 | 1947 | shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) | |
| 1947 | 1948 | defer shutdownCancel() | |
| 1948 | 1949 | ||
| 1949 | - | if err := registrySrv.Shutdown(shutdownCtx); err != nil { | |
| 1950 | - | log.Warn().Err(err).Msg("registry server shutdown error") | |
| 1951 | - | } | |
| 1952 | - | if err := proxySrv.Shutdown(shutdownCtx); err != nil { | |
| 1953 | - | log.Warn().Err(err).Msg("proxy server shutdown error") | |
| 1950 | + | for _, srv := range []*http.Server{registrySrv, proxySrv, tlsSrv} { | |
| 1951 | + | if srv == nil { | |
| 1952 | + | continue | |
| 1953 | + | } | |
| 1954 | + | if err := srv.Shutdown(shutdownCtx); err != nil { | |
| 1955 | + | log.Warn().Err(err).Str("addr", srv.Addr).Msg("server shutdown error") | |
| 1956 | + | } | |
| 1954 | 1957 | } | |
| 1955 | 1958 | ||
| 1956 | 1959 | containerSvc.StopMonitor() | |
| @@ -2000,9 +2003,20 @@ func startServer(addr string, handler http.Handler, name string, errChan chan<- | |||
| 2000 | 2003 | } | |
| 2001 | 2004 | ||
| 2002 | 2005 | // startTLSServer starts an HTTPS server with the provided certificate and key. | |
| 2003 | - | func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile string, errChan chan<- error, log zerowrap.Logger) <-chan struct{} { | |
| 2006 | + | // It returns the server instance (for graceful shutdown) and a channel that closes once the port is bound. | |
| 2007 | + | func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile string, errChan chan<- error, log zerowrap.Logger) (*http.Server, <-chan struct{}) { | |
| 2004 | 2008 | ready := make(chan struct{}) | |
| 2005 | 2009 | ||
| 2010 | + | server := &http.Server{ | |
| 2011 | + | Addr: addr, | |
| 2012 | + | Handler: handler, | |
| 2013 | + | ReadHeaderTimeout: 10 * time.Second, | |
| 2014 | + | ReadTimeout: 5 * time.Minute, | |
| 2015 | + | WriteTimeout: 5 * time.Minute, | |
| 2016 | + | IdleTimeout: 120 * time.Second, | |
| 2017 | + | MaxHeaderBytes: 1 << 20, | |
| 2018 | + | } | |
| 2019 | + | ||
| 2006 | 2020 | go func() { | |
| 2007 | 2021 | log.Info(). | |
| 2008 | 2022 | Str("address", addr). | |
| @@ -2010,16 +2024,6 @@ func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile s | |||
| 2010 | 2024 | Str("key_file", keyFile). | |
| 2011 | 2025 | Msgf("%s server starting", name) | |
| 2012 | 2026 | ||
| 2013 | - | server := &http.Server{ | |
| 2014 | - | Addr: addr, | |
| 2015 | - | Handler: handler, | |
| 2016 | - | ReadHeaderTimeout: 10 * time.Second, | |
| 2017 | - | ReadTimeout: 5 * time.Minute, | |
| 2018 | - | WriteTimeout: 5 * time.Minute, | |
| 2019 | - | IdleTimeout: 120 * time.Second, | |
| 2020 | - | MaxHeaderBytes: 1 << 20, | |
| 2021 | - | } | |
| 2022 | - | ||
| 2023 | 2027 | ln, err := net.Listen("tcp", addr) | |
| 2024 | 2028 | if err != nil { | |
| 2025 | 2029 | errChan <- fmt.Errorf("%s server error: %w", name, err) | |
| @@ -2032,7 +2036,7 @@ func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile s | |||
| 2032 | 2036 | } | |
| 2033 | 2037 | }() | |
| 2034 | 2038 | ||
| 2035 | - | return ready | |
| 2039 | + | return server, ready | |
| 2036 | 2040 | } | |
| 2037 | 2041 | ||
| 2038 | 2042 | // SendReloadSignal sends SIGUSR1 to the running Gordon process. | |
| @@ -1690,12 +1690,13 @@ func runServers(ctx context.Context, v *viper.Viper, cfg Config, registryHandler | |||||
| 1690 | 1690 | ||||
| 1691 | registrySrv, registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log) | 1691 | registrySrv, registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log) | ||
| 1692 | proxySrv, proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log) | 1692 | proxySrv, proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log) | ||
| 1693 | + | var tlsSrv *http.Server | |||
| 1693 | var tlsReady <-chan struct{} | 1694 | var tlsReady <-chan struct{} | ||
| 1694 | if cfg.Server.TLSEnabled { | 1695 | if cfg.Server.TLSEnabled { | ||
| 1695 | if cfg.Server.TLSCertFile == "" || cfg.Server.TLSKeyFile == "" { | 1696 | if cfg.Server.TLSCertFile == "" || cfg.Server.TLSKeyFile == "" { | ||
| 1696 | return fmt.Errorf("server.tls_enabled=true requires both server.tls_cert_file and server.tls_key_file") | 1697 | return fmt.Errorf("server.tls_enabled=true requires both server.tls_cert_file and server.tls_key_file") | ||
| 1697 | } | 1698 | } | ||
| 1698 | - | tlsReady = startTLSServer( | 1699 | + | tlsSrv, tlsReady = startTLSServer( |
| 1699 | fmt.Sprintf(":%d", cfg.Server.TLSPort), | 1700 | fmt.Sprintf(":%d", cfg.Server.TLSPort), | ||
| 1700 | proxyHandler, | 1701 | proxyHandler, | ||
| 1701 | "proxy-tls", | 1702 | "proxy-tls", | ||
| @@ -1740,7 +1741,7 @@ func runServers(ctx context.Context, v *viper.Viper, cfg Config, registryHandler | |||||
| 1740 | syncAndAutoStart(ctx, svc, log) | 1741 | syncAndAutoStart(ctx, svc, log) | ||
| 1741 | 1742 | ||||
| 1742 | waitForShutdown(ctx, errChan, reloadChan, deployChan, eventBus, log) | 1743 | waitForShutdown(ctx, errChan, reloadChan, deployChan, eventBus, log) | ||
| 1743 | - | gracefulShutdown(registrySrv, proxySrv, containerSvc, log) | 1744 | + | gracefulShutdown(registrySrv, proxySrv, tlsSrv, containerSvc, log) |
| 1744 | return nil | 1745 | return nil | ||
| 1745 | } | 1746 | } | ||
| 1746 | 1747 | ||||
| @@ -1940,17 +1941,19 @@ func waitForShutdown(ctx context.Context, errChan <-chan error, reloadChan, depl | |||||
| 1940 | 1941 | ||||
| 1941 | // gracefulShutdown stops HTTP servers with a 30s timeout, then shuts down | 1942 | // gracefulShutdown stops HTTP servers with a 30s timeout, then shuts down | ||
| 1942 | // the container service and cleans up runtime files. | 1943 | // the container service and cleans up runtime files. | ||
| 1943 | - | func gracefulShutdown(registrySrv, proxySrv *http.Server, containerSvc *container.Service, log zerowrap.Logger) { | 1944 | + | func gracefulShutdown(registrySrv, proxySrv, tlsSrv *http.Server, containerSvc *container.Service, log zerowrap.Logger) { |
| 1944 | log.Info().Msg("shutting down Gordon...") | 1945 | log.Info().Msg("shutting down Gordon...") | ||
| 1945 | 1946 | ||||
| 1946 | shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) | 1947 | shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
| 1947 | defer shutdownCancel() | 1948 | defer shutdownCancel() | ||
| 1948 | 1949 | ||||
| 1949 | - | if err := registrySrv.Shutdown(shutdownCtx); err != nil { | 1950 | + | for _, srv := range []*http.Server{registrySrv, proxySrv, tlsSrv} { |
| 1950 | - | log.Warn().Err(err).Msg("registry server shutdown error") | 1951 | + | if srv == nil { |
| 1951 | - | } | 1952 | + | continue |
| 1952 | - | if err := proxySrv.Shutdown(shutdownCtx); err != nil { | 1953 | + | } |
| 1953 | - | log.Warn().Err(err).Msg("proxy server shutdown error") | 1954 | + | if err := srv.Shutdown(shutdownCtx); err != nil { |
| 1955 | + | log.Warn().Err(err).Str("addr", srv.Addr).Msg("server shutdown error") | |||
| 1956 | + | } | |||
| 1954 | } | 1957 | } | ||
| 1955 | 1958 | ||||
| 1956 | containerSvc.StopMonitor() | 1959 | containerSvc.StopMonitor() | ||
| @@ -2000,9 +2003,20 @@ func startServer(addr string, handler http.Handler, name string, errChan chan<- | |||||
| 2000 | } | 2003 | } | ||
| 2001 | 2004 | ||||
| 2002 | // startTLSServer starts an HTTPS server with the provided certificate and key. | 2005 | // startTLSServer starts an HTTPS server with the provided certificate and key. | ||
| 2003 | - | func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile string, errChan chan<- error, log zerowrap.Logger) <-chan struct{} { | 2006 | + | // It returns the server instance (for graceful shutdown) and a channel that closes once the port is bound. |
| 2007 | + | func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile string, errChan chan<- error, log zerowrap.Logger) (*http.Server, <-chan struct{}) { | |||
| 2004 | ready := make(chan struct{}) | 2008 | ready := make(chan struct{}) | ||
| 2005 | 2009 | ||||
| 2010 | + | server := &http.Server{ | |||
| 2011 | + | Addr: addr, | |||
| 2012 | + | Handler: handler, | |||
| 2013 | + | ReadHeaderTimeout: 10 * time.Second, | |||
| 2014 | + | ReadTimeout: 5 * time.Minute, | |||
| 2015 | + | WriteTimeout: 5 * time.Minute, | |||
| 2016 | + | IdleTimeout: 120 * time.Second, | |||
| 2017 | + | MaxHeaderBytes: 1 << 20, | |||
| 2018 | + | } | |||
| 2019 | + | ||||
| 2006 | go func() { | 2020 | go func() { | ||
| 2007 | log.Info(). | 2021 | log.Info(). | ||
| 2008 | Str("address", addr). | 2022 | Str("address", addr). | ||
| @@ -2010,16 +2024,6 @@ func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile s | |||||
| 2010 | Str("key_file", keyFile). | 2024 | Str("key_file", keyFile). | ||
| 2011 | Msgf("%s server starting", name) | 2025 | Msgf("%s server starting", name) | ||
| 2012 | 2026 | ||||
| 2013 | - | server := &http.Server{ | |||
| 2014 | - | Addr: addr, | |||
| 2015 | - | Handler: handler, | |||
| 2016 | - | ReadHeaderTimeout: 10 * time.Second, | |||
| 2017 | - | ReadTimeout: 5 * time.Minute, | |||
| 2018 | - | WriteTimeout: 5 * time.Minute, | |||
| 2019 | - | IdleTimeout: 120 * time.Second, | |||
| 2020 | - | MaxHeaderBytes: 1 << 20, | |||
| 2021 | - | } | |||
| 2022 | - | ||||
| 2023 | ln, err := net.Listen("tcp", addr) | 2027 | ln, err := net.Listen("tcp", addr) | ||
| 2024 | if err != nil { | 2028 | if err != nil { | ||
| 2025 | errChan <- fmt.Errorf("%s server error: %w", name, err) | 2029 | errChan <- fmt.Errorf("%s server error: %w", name, err) | ||
| @@ -2032,7 +2036,7 @@ func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile s | |||||
| 2032 | } | 2036 | } | ||
| 2033 | }() | 2037 | }() | ||
| 2034 | 2038 | ||||
| 2035 | - | return ready | 2039 | + | return server, ready |
| 2036 | } | 2040 | } | ||
| 2037 | 2041 | ||||
| 2038 | // SendReloadSignal sends SIGUSR1 to the running Gordon process. | 2042 | // SendReloadSignal sends SIGUSR1 to the running Gordon process. | ||