detached 2cd6dfb

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
16901690
16911691 registrySrv, registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log)
16921692 proxySrv, proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log)
1693+ var tlsSrv *http.Server
16931694 var tlsReady <-chan struct{}
16941695 if cfg.Server.TLSEnabled {
16951696 if cfg.Server.TLSCertFile == "" || cfg.Server.TLSKeyFile == "" {
16961697 return fmt.Errorf("server.tls_enabled=true requires both server.tls_cert_file and server.tls_key_file")
16971698 }
1698- tlsReady = startTLSServer(
1699+ tlsSrv, tlsReady = startTLSServer(
16991700 fmt.Sprintf(":%d", cfg.Server.TLSPort),
17001701 proxyHandler,
17011702 "proxy-tls",
@@ -1740,7 +1741,7 @@ func runServers(ctx context.Context, v *viper.Viper, cfg Config, registryHandler
17401741 syncAndAutoStart(ctx, svc, log)
17411742
17421743 waitForShutdown(ctx, errChan, reloadChan, deployChan, eventBus, log)
1743- gracefulShutdown(registrySrv, proxySrv, containerSvc, log)
1744+ gracefulShutdown(registrySrv, proxySrv, tlsSrv, containerSvc, log)
17441745 return nil
17451746 }
17461747
@@ -1940,17 +1941,19 @@ func waitForShutdown(ctx context.Context, errChan <-chan error, reloadChan, depl
19401941
19411942 // gracefulShutdown stops HTTP servers with a 30s timeout, then shuts down
19421943 // 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) {
19441945 log.Info().Msg("shutting down Gordon...")
19451946
19461947 shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
19471948 defer shutdownCancel()
19481949
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+ }
19541957 }
19551958
19561959 containerSvc.StopMonitor()
@@ -2000,9 +2003,20 @@ func startServer(addr string, handler http.Handler, name string, errChan chan<-
20002003 }
20012004
20022005 // 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{}) {
20042008 ready := make(chan struct{})
20052009
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+
20062020 go func() {
20072021 log.Info().
20082022 Str("address", addr).
@@ -2010,16 +2024,6 @@ func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile s
20102024 Str("key_file", keyFile).
20112025 Msgf("%s server starting", name)
20122026
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-
20232027 ln, err := net.Listen("tcp", addr)
20242028 if err != nil {
20252029 errChan <- fmt.Errorf("%s server error: %w", name, err)
@@ -2032,7 +2036,7 @@ func startTLSServer(addr string, handler http.Handler, name, certFile, keyFile s
20322036 }
20332037 }()
20342038
2035- return ready
2039+ return server, ready
20362040 }
20372041
20382042 // 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 nil1745 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 down1942 // 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 ready2039+ 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.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close