feat: wire access log config and middleware into application bootstrap
- commit
- 2147872cf3d4622aef644133c9d54e80c2eba291
- author
- brice · · 2026-04-03 15:21 UTC
- parent
- aa3dd1d
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +101 -17
M internal/app/run.go +87 -3
| @@ -25,6 +25,7 @@ import ( | |||
| 25 | 25 | "github.com/spf13/viper" | |
| 26 | 26 | ||
| 27 | 27 | // Adapters - Output | |
| 28 | + | "github.com/bnema/gordon/internal/adapters/out/accesslog" | |
| 28 | 29 | "github.com/bnema/gordon/internal/adapters/out/docker" | |
| 29 | 30 | "github.com/bnema/gordon/internal/adapters/out/domainsecrets" | |
| 30 | 31 | "github.com/bnema/gordon/internal/adapters/out/envloader" | |
| @@ -119,6 +120,17 @@ type Config struct { | |||
| 119 | 120 | MaxBackups int `mapstructure:"max_backups"` | |
| 120 | 121 | MaxAge int `mapstructure:"max_age"` | |
| 121 | 122 | } `mapstructure:"container_logs"` | |
| 123 | + | AccessLog struct { | |
| 124 | + | Enabled bool `mapstructure:"enabled"` | |
| 125 | + | Format string `mapstructure:"format"` | |
| 126 | + | Output string `mapstructure:"output"` | |
| 127 | + | FilePath string `mapstructure:"file_path"` | |
| 128 | + | MaxSize int `mapstructure:"max_size"` | |
| 129 | + | MaxBackups int `mapstructure:"max_backups"` | |
| 130 | + | MaxAge int `mapstructure:"max_age"` | |
| 131 | + | ExcludeHealthChecks bool `mapstructure:"exclude_health_checks"` | |
| 132 | + | SyslogIdentifier string `mapstructure:"syslog_identifier"` | |
| 133 | + | } `mapstructure:"access_log"` | |
| 122 | 134 | } `mapstructure:"logging"` | |
| 123 | 135 | ||
| 124 | 136 | Env struct { | |
| @@ -342,6 +354,43 @@ func initLogger(cfg Config) (zerowrap.Logger, func(), error) { | |||
| 342 | 354 | return log, cleanup, nil | |
| 343 | 355 | } | |
| 344 | 356 | ||
| 357 | + | // initAccessLog creates an access log writer when access logging is enabled. | |
| 358 | + | // Returns nil, nil when disabled — callers must treat nil writer as "disabled". | |
| 359 | + | func initAccessLog(cfg Config, log zerowrap.Logger) (*accesslog.Writer, error) { | |
| 360 | + | if !cfg.Logging.AccessLog.Enabled { | |
| 361 | + | return nil, nil | |
| 362 | + | } | |
| 363 | + | ||
| 364 | + | filePath := cfg.Logging.AccessLog.FilePath | |
| 365 | + | if filePath == "" && cfg.Logging.AccessLog.Output == "file" { | |
| 366 | + | dataDir := cfg.Server.DataDir | |
| 367 | + | if dataDir == "" { | |
| 368 | + | dataDir = DefaultDataDir() | |
| 369 | + | } | |
| 370 | + | filePath = filepath.Join(dataDir, "logs", "access.log") | |
| 371 | + | } | |
| 372 | + | ||
| 373 | + | writer, err := accesslog.New(accesslog.Config{ | |
| 374 | + | Format: cfg.Logging.AccessLog.Format, | |
| 375 | + | Output: cfg.Logging.AccessLog.Output, | |
| 376 | + | FilePath: filePath, | |
| 377 | + | MaxSize: cfg.Logging.AccessLog.MaxSize, | |
| 378 | + | MaxBackups: cfg.Logging.AccessLog.MaxBackups, | |
| 379 | + | MaxAge: cfg.Logging.AccessLog.MaxAge, | |
| 380 | + | SyslogIdentifier: cfg.Logging.AccessLog.SyslogIdentifier, | |
| 381 | + | }) | |
| 382 | + | if err != nil { | |
| 383 | + | return nil, fmt.Errorf("failed to initialize access log: %w", err) | |
| 384 | + | } | |
| 385 | + | ||
| 386 | + | log.Info(). | |
| 387 | + | Str("format", cfg.Logging.AccessLog.Format). | |
| 388 | + | Str("output", cfg.Logging.AccessLog.Output). | |
| 389 | + | Msg("access log enabled") | |
| 390 | + | ||
| 391 | + | return writer, nil | |
| 392 | + | } | |
| 393 | + | ||
| 345 | 394 | // serviceInit holds the shared context for service initialization helpers. | |
| 346 | 395 | type serviceInit struct { | |
| 347 | 396 | ctx context.Context | |
| @@ -1611,7 +1660,7 @@ func loopbackOnly(next http.Handler, log zerowrap.Logger) http.Handler { | |||
| 1611 | 1660 | ||
| 1612 | 1661 | // createHTTPHandlers creates HTTP handlers with middleware. | |
| 1613 | 1662 | // Returns three handlers: registry, HTTP proxy (with CIDR + onboarding), and HTTPS proxy. | |
| 1614 | - | func createHTTPHandlers(svc *services, cfg Config, log zerowrap.Logger) (http.Handler, http.Handler, http.Handler) { | |
| 1663 | + | func createHTTPHandlers(svc *services, cfg Config, log zerowrap.Logger, accessWriter middleware.AccessLogWriter) (http.Handler, http.Handler, http.Handler) { | |
| 1615 | 1664 | // Parse trusted proxies once for all middleware chains. | |
| 1616 | 1665 | // This ensures consistent IP extraction across logging, rate limiting, and auth. | |
| 1617 | 1666 | trustedNets := middleware.ParseTrustedProxies(cfg.API.RateLimit.TrustedProxies) | |
| @@ -1703,7 +1752,18 @@ func createHTTPHandlers(svc *services, cfg Config, log zerowrap.Logger) (http.Ha | |||
| 1703 | 1752 | ||
| 1704 | 1753 | httpsHandler := otelhttp.NewHandler(middleware.Chain(httpsProxyMiddlewares...)(httpsMux), "gordon.proxy.tls") | |
| 1705 | 1754 | ||
| 1706 | - | return registryMux, proxyMux, httpsHandler | |
| 1755 | + | // Wrap top-level handlers with access logging outside all gates | |
| 1756 | + | // (loopbackOnly, denyAllHandler, CIDR allowlist) so every request — | |
| 1757 | + | // including rejected probes — produces exactly one access-log line. | |
| 1758 | + | var registryOut, proxyOut, httpsOut http.Handler = registryMux, proxyMux, httpsHandler | |
| 1759 | + | if accessWriter != nil { | |
| 1760 | + | excludeHC := cfg.Logging.AccessLog.ExcludeHealthChecks | |
| 1761 | + | registryOut = middleware.AccessLogger(accessWriter, excludeHC, log, trustedNets)(registryOut) | |
| 1762 | + | proxyOut = middleware.AccessLogger(accessWriter, excludeHC, log, trustedNets)(proxyOut) | |
| 1763 | + | httpsOut = middleware.AccessLogger(accessWriter, excludeHC, log, trustedNets)(httpsOut) | |
| 1764 | + | } | |
| 1765 | + | ||
| 1766 | + | return registryOut, proxyOut, httpsOut | |
| 1707 | 1767 | } | |
| 1708 | 1768 | ||
| 1709 | 1769 | // directHTTPOnboardingGate returns an http.Handler that splits HTTP traffic | |
| @@ -1984,6 +2044,21 @@ func registerAdminRoutes(registryMux *http.ServeMux, svc *services, cfg Config, | |||
| 1984 | 2044 | // The deferred signal.Stop calls ensure signal handlers are properly | |
| 1985 | 2045 | // cleaned up before program exit, preventing signal handler leaks. | |
| 1986 | 2046 | func runServers(ctx context.Context, v *viper.Viper, cfg Config, svc *services, cleanupHandlers func(), log zerowrap.Logger) error { | |
| 2047 | + | // Initialize access log writer. Kept here (not in Run) to keep Run's cyclomatic | |
| 2048 | + | // complexity within the project limit of 15. | |
| 2049 | + | accessWriterConcrete, err := initAccessLog(cfg, log) | |
| 2050 | + | if err != nil { | |
| 2051 | + | return err | |
| 2052 | + | } | |
| 2053 | + | if accessWriterConcrete != nil { | |
| 2054 | + | defer accessWriterConcrete.Close() | |
| 2055 | + | } | |
| 2056 | + | // Convert to interface only when non-nil to avoid the Go nil-interface pitfall | |
| 2057 | + | // where a typed nil pointer becomes a non-nil interface value. | |
| 2058 | + | var accessWriter middleware.AccessLogWriter | |
| 2059 | + | if accessWriterConcrete != nil { | |
| 2060 | + | accessWriter = accessWriterConcrete | |
| 2061 | + | } | |
| 1987 | 2062 | ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) | |
| 1988 | 2063 | defer cancel() | |
| 1989 | 2064 | ||
| @@ -2001,7 +2076,7 @@ func runServers(ctx context.Context, v *viper.Viper, cfg Config, svc *services, | |||
| 2001 | 2076 | ||
| 2002 | 2077 | errChan := make(chan error, 3) | |
| 2003 | 2078 | ||
| 2004 | - | registryHandler, httpProxyHandler, httpsProxyHandler := createHTTPHandlers(svc, cfg, log) | |
| 2079 | + | registryHandler, httpProxyHandler, httpsProxyHandler := createHTTPHandlers(svc, cfg, log, accessWriter) | |
| 2005 | 2080 | ||
| 2006 | 2081 | registryAddr := net.JoinHostPort(cfg.Server.RegistryListenAddr, strconv.Itoa(cfg.Server.RegistryPort)) | |
| 2007 | 2082 | registrySrv, registryReady := startServer(registryAddr, registryHandler, "registry", nil, errChan, log) | |
| @@ -2702,6 +2777,15 @@ func loadConfig(v *viper.Viper, configPath string) error { | |||
| 2702 | 2777 | v.SetDefault("logging.container_logs.max_size", 100) | |
| 2703 | 2778 | v.SetDefault("logging.container_logs.max_backups", 3) | |
| 2704 | 2779 | v.SetDefault("logging.container_logs.max_age", 28) | |
| 2780 | + | v.SetDefault("logging.access_log.enabled", false) | |
| 2781 | + | v.SetDefault("logging.access_log.format", "json") | |
| 2782 | + | v.SetDefault("logging.access_log.output", "stdout") | |
| 2783 | + | v.SetDefault("logging.access_log.file_path", "") | |
| 2784 | + | v.SetDefault("logging.access_log.max_size", 100) | |
| 2785 | + | v.SetDefault("logging.access_log.max_backups", 3) | |
| 2786 | + | v.SetDefault("logging.access_log.max_age", 28) | |
| 2787 | + | v.SetDefault("logging.access_log.exclude_health_checks", true) | |
| 2788 | + | v.SetDefault("logging.access_log.syslog_identifier", "gordon-access") | |
| 2705 | 2789 | v.SetDefault("env.dir", "") // defaults to {data_dir}/env when empty | |
| 2706 | 2790 | v.SetDefault("auth.enabled", true) | |
| 2707 | 2791 | // Note: auth.type defaults to "token" (the only supported mode) | |
| @@ -25,6 +25,7 @@ import ( | |||||
| 25 | "github.com/spf13/viper" | 25 | "github.com/spf13/viper" | ||
| 26 | 26 | ||||
| 27 | // Adapters - Output | 27 | // Adapters - Output | ||
| 28 | + | "github.com/bnema/gordon/internal/adapters/out/accesslog" | |||
| 28 | "github.com/bnema/gordon/internal/adapters/out/docker" | 29 | "github.com/bnema/gordon/internal/adapters/out/docker" | ||
| 29 | "github.com/bnema/gordon/internal/adapters/out/domainsecrets" | 30 | "github.com/bnema/gordon/internal/adapters/out/domainsecrets" | ||
| 30 | "github.com/bnema/gordon/internal/adapters/out/envloader" | 31 | "github.com/bnema/gordon/internal/adapters/out/envloader" | ||
| @@ -119,6 +120,17 @@ type Config struct { | |||||
| 119 | MaxBackups int `mapstructure:"max_backups"` | 120 | MaxBackups int `mapstructure:"max_backups"` | ||
| 120 | MaxAge int `mapstructure:"max_age"` | 121 | MaxAge int `mapstructure:"max_age"` | ||
| 121 | } `mapstructure:"container_logs"` | 122 | } `mapstructure:"container_logs"` | ||
| 123 | + | AccessLog struct { | |||
| 124 | + | Enabled bool `mapstructure:"enabled"` | |||
| 125 | + | Format string `mapstructure:"format"` | |||
| 126 | + | Output string `mapstructure:"output"` | |||
| 127 | + | FilePath string `mapstructure:"file_path"` | |||
| 128 | + | MaxSize int `mapstructure:"max_size"` | |||
| 129 | + | MaxBackups int `mapstructure:"max_backups"` | |||
| 130 | + | MaxAge int `mapstructure:"max_age"` | |||
| 131 | + | ExcludeHealthChecks bool `mapstructure:"exclude_health_checks"` | |||
| 132 | + | SyslogIdentifier string `mapstructure:"syslog_identifier"` | |||
| 133 | + | } `mapstructure:"access_log"` | |||
| 122 | } `mapstructure:"logging"` | 134 | } `mapstructure:"logging"` | ||
| 123 | 135 | ||||
| 124 | Env struct { | 136 | Env struct { | ||
| @@ -342,6 +354,43 @@ func initLogger(cfg Config) (zerowrap.Logger, func(), error) { | |||||
| 342 | return log, cleanup, nil | 354 | return log, cleanup, nil | ||
| 343 | } | 355 | } | ||
| 344 | 356 | ||||
| 357 | + | // initAccessLog creates an access log writer when access logging is enabled. | |||
| 358 | + | // Returns nil, nil when disabled — callers must treat nil writer as "disabled". | |||
| 359 | + | func initAccessLog(cfg Config, log zerowrap.Logger) (*accesslog.Writer, error) { | |||
| 360 | + | if !cfg.Logging.AccessLog.Enabled { | |||
| 361 | + | return nil, nil | |||
| 362 | + | } | |||
| 363 | + | ||||
| 364 | + | filePath := cfg.Logging.AccessLog.FilePath | |||
| 365 | + | if filePath == "" && cfg.Logging.AccessLog.Output == "file" { | |||
| 366 | + | dataDir := cfg.Server.DataDir | |||
| 367 | + | if dataDir == "" { | |||
| 368 | + | dataDir = DefaultDataDir() | |||
| 369 | + | } | |||
| 370 | + | filePath = filepath.Join(dataDir, "logs", "access.log") | |||
| 371 | + | } | |||
| 372 | + | ||||
| 373 | + | writer, err := accesslog.New(accesslog.Config{ | |||
| 374 | + | Format: cfg.Logging.AccessLog.Format, | |||
| 375 | + | Output: cfg.Logging.AccessLog.Output, | |||
| 376 | + | FilePath: filePath, | |||
| 377 | + | MaxSize: cfg.Logging.AccessLog.MaxSize, | |||
| 378 | + | MaxBackups: cfg.Logging.AccessLog.MaxBackups, | |||
| 379 | + | MaxAge: cfg.Logging.AccessLog.MaxAge, | |||
| 380 | + | SyslogIdentifier: cfg.Logging.AccessLog.SyslogIdentifier, | |||
| 381 | + | }) | |||
| 382 | + | if err != nil { | |||
| 383 | + | return nil, fmt.Errorf("failed to initialize access log: %w", err) | |||
| 384 | + | } | |||
| 385 | + | ||||
| 386 | + | log.Info(). | |||
| 387 | + | Str("format", cfg.Logging.AccessLog.Format). | |||
| 388 | + | Str("output", cfg.Logging.AccessLog.Output). | |||
| 389 | + | Msg("access log enabled") | |||
| 390 | + | ||||
| 391 | + | return writer, nil | |||
| 392 | + | } | |||
| 393 | + | ||||
| 345 | // serviceInit holds the shared context for service initialization helpers. | 394 | // serviceInit holds the shared context for service initialization helpers. | ||
| 346 | type serviceInit struct { | 395 | type serviceInit struct { | ||
| 347 | ctx context.Context | 396 | ctx context.Context | ||
| @@ -1611,7 +1660,7 @@ func loopbackOnly(next http.Handler, log zerowrap.Logger) http.Handler { | |||||
| 1611 | 1660 | ||||
| 1612 | // createHTTPHandlers creates HTTP handlers with middleware. | 1661 | // createHTTPHandlers creates HTTP handlers with middleware. | ||
| 1613 | // Returns three handlers: registry, HTTP proxy (with CIDR + onboarding), and HTTPS proxy. | 1662 | // Returns three handlers: registry, HTTP proxy (with CIDR + onboarding), and HTTPS proxy. | ||
| 1614 | - | func createHTTPHandlers(svc *services, cfg Config, log zerowrap.Logger) (http.Handler, http.Handler, http.Handler) { | 1663 | + | func createHTTPHandlers(svc *services, cfg Config, log zerowrap.Logger, accessWriter middleware.AccessLogWriter) (http.Handler, http.Handler, http.Handler) { |
| 1615 | // Parse trusted proxies once for all middleware chains. | 1664 | // Parse trusted proxies once for all middleware chains. | ||
| 1616 | // This ensures consistent IP extraction across logging, rate limiting, and auth. | 1665 | // This ensures consistent IP extraction across logging, rate limiting, and auth. | ||
| 1617 | trustedNets := middleware.ParseTrustedProxies(cfg.API.RateLimit.TrustedProxies) | 1666 | trustedNets := middleware.ParseTrustedProxies(cfg.API.RateLimit.TrustedProxies) | ||
| @@ -1703,7 +1752,18 @@ func createHTTPHandlers(svc *services, cfg Config, log zerowrap.Logger) (http.Ha | |||||
| 1703 | 1752 | ||||
| 1704 | httpsHandler := otelhttp.NewHandler(middleware.Chain(httpsProxyMiddlewares...)(httpsMux), "gordon.proxy.tls") | 1753 | httpsHandler := otelhttp.NewHandler(middleware.Chain(httpsProxyMiddlewares...)(httpsMux), "gordon.proxy.tls") | ||
| 1705 | 1754 | ||||
| 1706 | - | return registryMux, proxyMux, httpsHandler | 1755 | + | // Wrap top-level handlers with access logging outside all gates |
| 1756 | + | // (loopbackOnly, denyAllHandler, CIDR allowlist) so every request — | |||
| 1757 | + | // including rejected probes — produces exactly one access-log line. | |||
| 1758 | + | var registryOut, proxyOut, httpsOut http.Handler = registryMux, proxyMux, httpsHandler | |||
| 1759 | + | if accessWriter != nil { | |||
| 1760 | + | excludeHC := cfg.Logging.AccessLog.ExcludeHealthChecks | |||
| 1761 | + | registryOut = middleware.AccessLogger(accessWriter, excludeHC, log, trustedNets)(registryOut) | |||
| 1762 | + | proxyOut = middleware.AccessLogger(accessWriter, excludeHC, log, trustedNets)(proxyOut) | |||
| 1763 | + | httpsOut = middleware.AccessLogger(accessWriter, excludeHC, log, trustedNets)(httpsOut) | |||
| 1764 | + | } | |||
| 1765 | + | ||||
| 1766 | + | return registryOut, proxyOut, httpsOut | |||
| 1707 | } | 1767 | } | ||
| 1708 | 1768 | ||||
| 1709 | // directHTTPOnboardingGate returns an http.Handler that splits HTTP traffic | 1769 | // directHTTPOnboardingGate returns an http.Handler that splits HTTP traffic | ||
| @@ -1984,6 +2044,21 @@ func registerAdminRoutes(registryMux *http.ServeMux, svc *services, cfg Config, | |||||
| 1984 | // The deferred signal.Stop calls ensure signal handlers are properly | 2044 | // The deferred signal.Stop calls ensure signal handlers are properly | ||
| 1985 | // cleaned up before program exit, preventing signal handler leaks. | 2045 | // cleaned up before program exit, preventing signal handler leaks. | ||
| 1986 | func runServers(ctx context.Context, v *viper.Viper, cfg Config, svc *services, cleanupHandlers func(), log zerowrap.Logger) error { | 2046 | func runServers(ctx context.Context, v *viper.Viper, cfg Config, svc *services, cleanupHandlers func(), log zerowrap.Logger) error { | ||
| 2047 | + | // Initialize access log writer. Kept here (not in Run) to keep Run's cyclomatic | |||
| 2048 | + | // complexity within the project limit of 15. | |||
| 2049 | + | accessWriterConcrete, err := initAccessLog(cfg, log) | |||
| 2050 | + | if err != nil { | |||
| 2051 | + | return err | |||
| 2052 | + | } | |||
| 2053 | + | if accessWriterConcrete != nil { | |||
| 2054 | + | defer accessWriterConcrete.Close() | |||
| 2055 | + | } | |||
| 2056 | + | // Convert to interface only when non-nil to avoid the Go nil-interface pitfall | |||
| 2057 | + | // where a typed nil pointer becomes a non-nil interface value. | |||
| 2058 | + | var accessWriter middleware.AccessLogWriter | |||
| 2059 | + | if accessWriterConcrete != nil { | |||
| 2060 | + | accessWriter = accessWriterConcrete | |||
| 2061 | + | } | |||
| 1987 | ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) | 2062 | ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) | ||
| 1988 | defer cancel() | 2063 | defer cancel() | ||
| 1989 | 2064 | ||||
| @@ -2001,7 +2076,7 @@ func runServers(ctx context.Context, v *viper.Viper, cfg Config, svc *services, | |||||
| 2001 | 2076 | ||||
| 2002 | errChan := make(chan error, 3) | 2077 | errChan := make(chan error, 3) | ||
| 2003 | 2078 | ||||
| 2004 | - | registryHandler, httpProxyHandler, httpsProxyHandler := createHTTPHandlers(svc, cfg, log) | 2079 | + | registryHandler, httpProxyHandler, httpsProxyHandler := createHTTPHandlers(svc, cfg, log, accessWriter) |
| 2005 | 2080 | ||||
| 2006 | registryAddr := net.JoinHostPort(cfg.Server.RegistryListenAddr, strconv.Itoa(cfg.Server.RegistryPort)) | 2081 | registryAddr := net.JoinHostPort(cfg.Server.RegistryListenAddr, strconv.Itoa(cfg.Server.RegistryPort)) | ||
| 2007 | registrySrv, registryReady := startServer(registryAddr, registryHandler, "registry", nil, errChan, log) | 2082 | registrySrv, registryReady := startServer(registryAddr, registryHandler, "registry", nil, errChan, log) | ||
| @@ -2702,6 +2777,15 @@ func loadConfig(v *viper.Viper, configPath string) error { | |||||
| 2702 | v.SetDefault("logging.container_logs.max_size", 100) | 2777 | v.SetDefault("logging.container_logs.max_size", 100) | ||
| 2703 | v.SetDefault("logging.container_logs.max_backups", 3) | 2778 | v.SetDefault("logging.container_logs.max_backups", 3) | ||
| 2704 | v.SetDefault("logging.container_logs.max_age", 28) | 2779 | v.SetDefault("logging.container_logs.max_age", 28) | ||
| 2780 | + | v.SetDefault("logging.access_log.enabled", false) | |||
| 2781 | + | v.SetDefault("logging.access_log.format", "json") | |||
| 2782 | + | v.SetDefault("logging.access_log.output", "stdout") | |||
| 2783 | + | v.SetDefault("logging.access_log.file_path", "") | |||
| 2784 | + | v.SetDefault("logging.access_log.max_size", 100) | |||
| 2785 | + | v.SetDefault("logging.access_log.max_backups", 3) | |||
| 2786 | + | v.SetDefault("logging.access_log.max_age", 28) | |||
| 2787 | + | v.SetDefault("logging.access_log.exclude_health_checks", true) | |||
| 2788 | + | v.SetDefault("logging.access_log.syslog_identifier", "gordon-access") | |||
| 2705 | v.SetDefault("env.dir", "") // defaults to {data_dir}/env when empty | 2789 | v.SetDefault("env.dir", "") // defaults to {data_dir}/env when empty | ||
| 2706 | v.SetDefault("auth.enabled", true) | 2790 | v.SetDefault("auth.enabled", true) | ||
| 2707 | // Note: auth.type defaults to "token" (the only supported mode) | 2791 | // Note: auth.type defaults to "token" (the only supported mode) | ||
M internal/app/run_http_handlers_test.go +14 -14
| @@ -42,7 +42,7 @@ func TestCreateHTTPHandlers_LocalMode_DisablesAdminRoutes(t *testing.T) { | |||
| 42 | 42 | cfg.Auth.Enabled = false | |
| 43 | 43 | ||
| 44 | 44 | svc := &services{adminHandler: &adminhttp.Handler{}} | |
| 45 | - | registryHandler, _, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 45 | + | registryHandler, _, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 46 | 46 | ||
| 47 | 47 | req := httptest.NewRequest(http.MethodGet, "/admin/status", nil) | |
| 48 | 48 | req.RemoteAddr = "192.0.2.10:12345" | |
| @@ -61,7 +61,7 @@ func TestCreateHTTPHandlers_LocalMode_RestrictsRegistryToLoopback(t *testing.T) | |||
| 61 | 61 | cfg := Config{} | |
| 62 | 62 | cfg.Auth.Enabled = false | |
| 63 | 63 | ||
| 64 | - | registryHandler, _, _ := createHTTPHandlers(&services{}, cfg, zerowrap.Default()) | |
| 64 | + | registryHandler, _, _ := createHTTPHandlers(&services{}, cfg, zerowrap.Default(), nil) | |
| 65 | 65 | ||
| 66 | 66 | req := httptest.NewRequest(http.MethodGet, "/v2/test/manifests/latest", nil) | |
| 67 | 67 | req.RemoteAddr = "192.0.2.20:12345" | |
| @@ -103,7 +103,7 @@ func TestCreateHTTPHandlers_DirectHTTPRoot_ServesOnboarding(t *testing.T) { | |||
| 103 | 103 | cfg := newOnboardingConfig() | |
| 104 | 104 | svc := &services{caAdapter: ca} | |
| 105 | 105 | ||
| 106 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 106 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 107 | 107 | ||
| 108 | 108 | req := httptest.NewRequest(http.MethodGet, "/", nil) | |
| 109 | 109 | req.RemoteAddr = directAddr | |
| @@ -122,7 +122,7 @@ func TestCreateHTTPHandlers_DirectHTTPCertPath_ServesCACert(t *testing.T) { | |||
| 122 | 122 | cfg := newOnboardingConfig() | |
| 123 | 123 | svc := &services{caAdapter: ca} | |
| 124 | 124 | ||
| 125 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 125 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 126 | 126 | ||
| 127 | 127 | req := httptest.NewRequest(http.MethodGet, "/ca.crt", nil) | |
| 128 | 128 | req.RemoteAddr = directAddr | |
| @@ -141,7 +141,7 @@ func TestCreateHTTPHandlers_DirectHTTPUnknownPath_ReturnsForbidden(t *testing.T) | |||
| 141 | 141 | cfg := newOnboardingConfig() | |
| 142 | 142 | svc := &services{caAdapter: ca} | |
| 143 | 143 | ||
| 144 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 144 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 145 | 145 | ||
| 146 | 146 | req := httptest.NewRequest(http.MethodGet, "/web", nil) | |
| 147 | 147 | req.RemoteAddr = directAddr | |
| @@ -159,7 +159,7 @@ func TestCreateHTTPHandlers_TrustedProxyHTTP_DoesNotServeOnboarding(t *testing.T | |||
| 159 | 159 | cfg := newOnboardingConfig() | |
| 160 | 160 | svc := &services{caAdapter: ca} | |
| 161 | 161 | ||
| 162 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 162 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 163 | 163 | ||
| 164 | 164 | req := httptest.NewRequest(http.MethodGet, "/", nil) | |
| 165 | 165 | req.RemoteAddr = trustedProxyAddr | |
| @@ -179,7 +179,7 @@ func TestCreateHTTPHandlers_TrustedProxyCACertPath_DoesNotServeOnboardingResourc | |||
| 179 | 179 | cfg := newOnboardingConfig() | |
| 180 | 180 | svc := &services{caAdapter: ca} | |
| 181 | 181 | ||
| 182 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 182 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 183 | 183 | ||
| 184 | 184 | req := httptest.NewRequest(http.MethodGet, "/ca.crt", nil) | |
| 185 | 185 | req.RemoteAddr = trustedProxyAddr | |
| @@ -199,7 +199,7 @@ func TestCreateHTTPHandlers_ForceHTTPSRedirect_DoesNotBypassDirectOnboarding(t * | |||
| 199 | 199 | cfg.Server.ForceHTTPSRedirect = true | |
| 200 | 200 | svc := &services{caAdapter: ca} | |
| 201 | 201 | ||
| 202 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 202 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 203 | 203 | ||
| 204 | 204 | req := httptest.NewRequest(http.MethodGet, "/", nil) | |
| 205 | 205 | req.RemoteAddr = directAddr | |
| @@ -218,7 +218,7 @@ func TestCreateHTTPHandlers_HTTPSOnboarding_RemainsAvailable(t *testing.T) { | |||
| 218 | 218 | cfg := newOnboardingConfig() | |
| 219 | 219 | svc := &services{caAdapter: ca} | |
| 220 | 220 | ||
| 221 | - | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 221 | + | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 222 | 222 | ||
| 223 | 223 | req := httptest.NewRequest(http.MethodGet, "/ca", nil) | |
| 224 | 224 | req.RemoteAddr = directAddr | |
| @@ -236,7 +236,7 @@ func TestCreateHTTPHandlers_HTTPSOnboarding_HEADRoutesRemainAvailable(t *testing | |||
| 236 | 236 | cfg := newOnboardingConfig() | |
| 237 | 237 | svc := &services{caAdapter: ca} | |
| 238 | 238 | ||
| 239 | - | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 239 | + | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 240 | 240 | ||
| 241 | 241 | // Use httptest.NewServer so Go's net/http server strips the body for HEAD. | |
| 242 | 242 | // httptest.NewRecorder does not apply server-level HEAD body stripping. | |
| @@ -262,7 +262,7 @@ func TestCreateHTTPHandlers_DirectHTTPOnboarding_HEADRoutesRemainAvailable(t *te | |||
| 262 | 262 | cfg := newOnboardingConfig() | |
| 263 | 263 | svc := &services{caAdapter: ca} | |
| 264 | 264 | ||
| 265 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 265 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 266 | 266 | ||
| 267 | 267 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 268 | 268 | r.RemoteAddr = directAddr | |
| @@ -297,7 +297,7 @@ func TestCreateHTTPHandlers_DirectHTTPForbiddenHEAD_ReturnsForbidden(t *testing. | |||
| 297 | 297 | cfg := newOnboardingConfig() | |
| 298 | 298 | svc := &services{caAdapter: ca} | |
| 299 | 299 | ||
| 300 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 300 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 301 | 301 | ||
| 302 | 302 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 303 | 303 | r.RemoteAddr = directAddr | |
| @@ -323,7 +323,7 @@ func TestCreateHTTPHandlers_DirectHTTPACMEChallenge_Returns404(t *testing.T) { | |||
| 323 | 323 | cfg := newOnboardingConfig() | |
| 324 | 324 | svc := &services{caAdapter: ca} | |
| 325 | 325 | ||
| 326 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 326 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 327 | 327 | ||
| 328 | 328 | req := httptest.NewRequest(http.MethodGet, "/.well-known/acme-challenge/test-token", nil) | |
| 329 | 329 | req.RemoteAddr = directAddr | |
| @@ -341,7 +341,7 @@ func TestCreateHTTPHandlers_TLSDisabled_DoesNotServeHTTPOnboarding(t *testing.T) | |||
| 341 | 341 | cfg.Server.TLSPort = 0 // TLS disabled | |
| 342 | 342 | svc := &services{caAdapter: ca} | |
| 343 | 343 | ||
| 344 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | |
| 344 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) | |
| 345 | 345 | ||
| 346 | 346 | req := httptest.NewRequest(http.MethodGet, "/", nil) | |
| 347 | 347 | req.RemoteAddr = directAddr | |
| @@ -42,7 +42,7 @@ func TestCreateHTTPHandlers_LocalMode_DisablesAdminRoutes(t *testing.T) { | |||||
| 42 | cfg.Auth.Enabled = false | 42 | cfg.Auth.Enabled = false | ||
| 43 | 43 | ||||
| 44 | svc := &services{adminHandler: &adminhttp.Handler{}} | 44 | svc := &services{adminHandler: &adminhttp.Handler{}} | ||
| 45 | - | registryHandler, _, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 45 | + | registryHandler, _, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 46 | 46 | ||||
| 47 | req := httptest.NewRequest(http.MethodGet, "/admin/status", nil) | 47 | req := httptest.NewRequest(http.MethodGet, "/admin/status", nil) | ||
| 48 | req.RemoteAddr = "192.0.2.10:12345" | 48 | req.RemoteAddr = "192.0.2.10:12345" | ||
| @@ -61,7 +61,7 @@ func TestCreateHTTPHandlers_LocalMode_RestrictsRegistryToLoopback(t *testing.T) | |||||
| 61 | cfg := Config{} | 61 | cfg := Config{} | ||
| 62 | cfg.Auth.Enabled = false | 62 | cfg.Auth.Enabled = false | ||
| 63 | 63 | ||||
| 64 | - | registryHandler, _, _ := createHTTPHandlers(&services{}, cfg, zerowrap.Default()) | 64 | + | registryHandler, _, _ := createHTTPHandlers(&services{}, cfg, zerowrap.Default(), nil) |
| 65 | 65 | ||||
| 66 | req := httptest.NewRequest(http.MethodGet, "/v2/test/manifests/latest", nil) | 66 | req := httptest.NewRequest(http.MethodGet, "/v2/test/manifests/latest", nil) | ||
| 67 | req.RemoteAddr = "192.0.2.20:12345" | 67 | req.RemoteAddr = "192.0.2.20:12345" | ||
| @@ -103,7 +103,7 @@ func TestCreateHTTPHandlers_DirectHTTPRoot_ServesOnboarding(t *testing.T) { | |||||
| 103 | cfg := newOnboardingConfig() | 103 | cfg := newOnboardingConfig() | ||
| 104 | svc := &services{caAdapter: ca} | 104 | svc := &services{caAdapter: ca} | ||
| 105 | 105 | ||||
| 106 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 106 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 107 | 107 | ||||
| 108 | req := httptest.NewRequest(http.MethodGet, "/", nil) | 108 | req := httptest.NewRequest(http.MethodGet, "/", nil) | ||
| 109 | req.RemoteAddr = directAddr | 109 | req.RemoteAddr = directAddr | ||
| @@ -122,7 +122,7 @@ func TestCreateHTTPHandlers_DirectHTTPCertPath_ServesCACert(t *testing.T) { | |||||
| 122 | cfg := newOnboardingConfig() | 122 | cfg := newOnboardingConfig() | ||
| 123 | svc := &services{caAdapter: ca} | 123 | svc := &services{caAdapter: ca} | ||
| 124 | 124 | ||||
| 125 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 125 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 126 | 126 | ||||
| 127 | req := httptest.NewRequest(http.MethodGet, "/ca.crt", nil) | 127 | req := httptest.NewRequest(http.MethodGet, "/ca.crt", nil) | ||
| 128 | req.RemoteAddr = directAddr | 128 | req.RemoteAddr = directAddr | ||
| @@ -141,7 +141,7 @@ func TestCreateHTTPHandlers_DirectHTTPUnknownPath_ReturnsForbidden(t *testing.T) | |||||
| 141 | cfg := newOnboardingConfig() | 141 | cfg := newOnboardingConfig() | ||
| 142 | svc := &services{caAdapter: ca} | 142 | svc := &services{caAdapter: ca} | ||
| 143 | 143 | ||||
| 144 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 144 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 145 | 145 | ||||
| 146 | req := httptest.NewRequest(http.MethodGet, "/web", nil) | 146 | req := httptest.NewRequest(http.MethodGet, "/web", nil) | ||
| 147 | req.RemoteAddr = directAddr | 147 | req.RemoteAddr = directAddr | ||
| @@ -159,7 +159,7 @@ func TestCreateHTTPHandlers_TrustedProxyHTTP_DoesNotServeOnboarding(t *testing.T | |||||
| 159 | cfg := newOnboardingConfig() | 159 | cfg := newOnboardingConfig() | ||
| 160 | svc := &services{caAdapter: ca} | 160 | svc := &services{caAdapter: ca} | ||
| 161 | 161 | ||||
| 162 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 162 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 163 | 163 | ||||
| 164 | req := httptest.NewRequest(http.MethodGet, "/", nil) | 164 | req := httptest.NewRequest(http.MethodGet, "/", nil) | ||
| 165 | req.RemoteAddr = trustedProxyAddr | 165 | req.RemoteAddr = trustedProxyAddr | ||
| @@ -179,7 +179,7 @@ func TestCreateHTTPHandlers_TrustedProxyCACertPath_DoesNotServeOnboardingResourc | |||||
| 179 | cfg := newOnboardingConfig() | 179 | cfg := newOnboardingConfig() | ||
| 180 | svc := &services{caAdapter: ca} | 180 | svc := &services{caAdapter: ca} | ||
| 181 | 181 | ||||
| 182 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 182 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 183 | 183 | ||||
| 184 | req := httptest.NewRequest(http.MethodGet, "/ca.crt", nil) | 184 | req := httptest.NewRequest(http.MethodGet, "/ca.crt", nil) | ||
| 185 | req.RemoteAddr = trustedProxyAddr | 185 | req.RemoteAddr = trustedProxyAddr | ||
| @@ -199,7 +199,7 @@ func TestCreateHTTPHandlers_ForceHTTPSRedirect_DoesNotBypassDirectOnboarding(t * | |||||
| 199 | cfg.Server.ForceHTTPSRedirect = true | 199 | cfg.Server.ForceHTTPSRedirect = true | ||
| 200 | svc := &services{caAdapter: ca} | 200 | svc := &services{caAdapter: ca} | ||
| 201 | 201 | ||||
| 202 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 202 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 203 | 203 | ||||
| 204 | req := httptest.NewRequest(http.MethodGet, "/", nil) | 204 | req := httptest.NewRequest(http.MethodGet, "/", nil) | ||
| 205 | req.RemoteAddr = directAddr | 205 | req.RemoteAddr = directAddr | ||
| @@ -218,7 +218,7 @@ func TestCreateHTTPHandlers_HTTPSOnboarding_RemainsAvailable(t *testing.T) { | |||||
| 218 | cfg := newOnboardingConfig() | 218 | cfg := newOnboardingConfig() | ||
| 219 | svc := &services{caAdapter: ca} | 219 | svc := &services{caAdapter: ca} | ||
| 220 | 220 | ||||
| 221 | - | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 221 | + | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 222 | 222 | ||||
| 223 | req := httptest.NewRequest(http.MethodGet, "/ca", nil) | 223 | req := httptest.NewRequest(http.MethodGet, "/ca", nil) | ||
| 224 | req.RemoteAddr = directAddr | 224 | req.RemoteAddr = directAddr | ||
| @@ -236,7 +236,7 @@ func TestCreateHTTPHandlers_HTTPSOnboarding_HEADRoutesRemainAvailable(t *testing | |||||
| 236 | cfg := newOnboardingConfig() | 236 | cfg := newOnboardingConfig() | ||
| 237 | svc := &services{caAdapter: ca} | 237 | svc := &services{caAdapter: ca} | ||
| 238 | 238 | ||||
| 239 | - | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 239 | + | _, _, httpsHandler := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 240 | 240 | ||||
| 241 | // Use httptest.NewServer so Go's net/http server strips the body for HEAD. | 241 | // Use httptest.NewServer so Go's net/http server strips the body for HEAD. | ||
| 242 | // httptest.NewRecorder does not apply server-level HEAD body stripping. | 242 | // httptest.NewRecorder does not apply server-level HEAD body stripping. | ||
| @@ -262,7 +262,7 @@ func TestCreateHTTPHandlers_DirectHTTPOnboarding_HEADRoutesRemainAvailable(t *te | |||||
| 262 | cfg := newOnboardingConfig() | 262 | cfg := newOnboardingConfig() | ||
| 263 | svc := &services{caAdapter: ca} | 263 | svc := &services{caAdapter: ca} | ||
| 264 | 264 | ||||
| 265 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 265 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 266 | 266 | ||||
| 267 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 267 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| 268 | r.RemoteAddr = directAddr | 268 | r.RemoteAddr = directAddr | ||
| @@ -297,7 +297,7 @@ func TestCreateHTTPHandlers_DirectHTTPForbiddenHEAD_ReturnsForbidden(t *testing. | |||||
| 297 | cfg := newOnboardingConfig() | 297 | cfg := newOnboardingConfig() | ||
| 298 | svc := &services{caAdapter: ca} | 298 | svc := &services{caAdapter: ca} | ||
| 299 | 299 | ||||
| 300 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 300 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 301 | 301 | ||||
| 302 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 302 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| 303 | r.RemoteAddr = directAddr | 303 | r.RemoteAddr = directAddr | ||
| @@ -323,7 +323,7 @@ func TestCreateHTTPHandlers_DirectHTTPACMEChallenge_Returns404(t *testing.T) { | |||||
| 323 | cfg := newOnboardingConfig() | 323 | cfg := newOnboardingConfig() | ||
| 324 | svc := &services{caAdapter: ca} | 324 | svc := &services{caAdapter: ca} | ||
| 325 | 325 | ||||
| 326 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 326 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 327 | 327 | ||||
| 328 | req := httptest.NewRequest(http.MethodGet, "/.well-known/acme-challenge/test-token", nil) | 328 | req := httptest.NewRequest(http.MethodGet, "/.well-known/acme-challenge/test-token", nil) | ||
| 329 | req.RemoteAddr = directAddr | 329 | req.RemoteAddr = directAddr | ||
| @@ -341,7 +341,7 @@ func TestCreateHTTPHandlers_TLSDisabled_DoesNotServeHTTPOnboarding(t *testing.T) | |||||
| 341 | cfg.Server.TLSPort = 0 // TLS disabled | 341 | cfg.Server.TLSPort = 0 // TLS disabled | ||
| 342 | svc := &services{caAdapter: ca} | 342 | svc := &services{caAdapter: ca} | ||
| 343 | 343 | ||||
| 344 | - | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default()) | 344 | + | _, httpHandler, _ := createHTTPHandlers(svc, cfg, zerowrap.Default(), nil) |
| 345 | 345 | ||||
| 346 | req := httptest.NewRequest(http.MethodGet, "/", nil) | 346 | req := httptest.NewRequest(http.MethodGet, "/", nil) | ||
| 347 | req.RemoteAddr = directAddr | 347 | req.RemoteAddr = directAddr | ||