diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 51ebc4e83b..24ded12bd1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,7 +10,7 @@ on: - 'script/gcg/**' env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' CGO_ENABLED: 0 jobs: diff --git a/.github/workflows/experimental.yaml b/.github/workflows/experimental.yaml index c46aa07d0a..b74c1bedbb 100644 --- a/.github/workflows/experimental.yaml +++ b/.github/workflows/experimental.yaml @@ -7,7 +7,7 @@ on: - v* env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' CGO_ENABLED: 0 jobs: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 207f236918..4db77ae52b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,7 +6,7 @@ on: - 'v*.*.*' env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' CGO_ENABLED: 0 VERSION: ${{ github.ref_name }} TRAEFIKER_EMAIL: "traefiker@traefik.io" @@ -130,8 +130,8 @@ jobs: --exclude .idea \ --exclude .github \ --exclude dist . - + chown -R "$(id -u)":"$(id -g)" dist/ gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION} --latest=false - + ./script/deploy.sh diff --git a/.github/workflows/test-gateway-api-conformance.yaml b/.github/workflows/test-gateway-api-conformance.yaml index cfe2da412a..a27b7d23ff 100644 --- a/.github/workflows/test-gateway-api-conformance.yaml +++ b/.github/workflows/test-gateway-api-conformance.yaml @@ -12,7 +12,7 @@ on: - 'integration/integration_test.go' env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' CGO_ENABLED: 0 jobs: diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index 39e802e719..df4fff710f 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -10,7 +10,7 @@ on: - 'script/gcg/**' env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' CGO_ENABLED: 0 jobs: diff --git a/.github/workflows/test-knative-conformance.yaml b/.github/workflows/test-knative-conformance.yaml index 2ca5584a24..7fa8e19935 100644 --- a/.github/workflows/test-knative-conformance.yaml +++ b/.github/workflows/test-knative-conformance.yaml @@ -12,7 +12,7 @@ on: - 'integration/integration_test.go' env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' CGO_ENABLED: 0 jobs: diff --git a/.github/workflows/test-unit.yaml b/.github/workflows/test-unit.yaml index bdb6003ccc..1dfcf006a1 100644 --- a/.github/workflows/test-unit.yaml +++ b/.github/workflows/test-unit.yaml @@ -10,7 +10,7 @@ on: - 'script/gcg/**' env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' jobs: generate-packages: diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 1c245f4d57..3db0ad181d 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -6,7 +6,7 @@ on: - '*' env: - GO_VERSION: '1.24' + GO_VERSION: '1.25' GOLANGCI_LINT_VERSION: v2.8.0 MISSPELL_VERSION: v0.7.0 diff --git a/docs/content/reference/install-configuration/observability/tracing.md b/docs/content/reference/install-configuration/observability/tracing.md index c3748e5f10..88301dbfb3 100644 --- a/docs/content/reference/install-configuration/observability/tracing.md +++ b/docs/content/reference/install-configuration/observability/tracing.md @@ -41,7 +41,7 @@ tracing: {} | `tracing.addInternals` | Enables tracing for internal resources (e.g.: `ping@internal`). | false | No | | `tracing.serviceName` | Defines the service name resource attribute. | "traefik" | No | | `tracing.resourceAttributes` | Defines additional resource attributes to be sent to the collector. See [resourceAttributes](#resourceattributes) for details. | [] | No | -| `tracing.sampleRate` | The proportion of requests to trace, specified between 0.0 and 1.0. | 1.0 | No | +| `tracing.sampleRate` | The proportion of requests to trace, specified between 0.0 and 1.0.
Since Traefik supports parent-based sampling ratios, root spans (i.e., spans initiated by Traefik) are sampled according to this rate, while child spans inherit the sampling decision of their parent (i.e., the tracing context from incoming requests). See [sampleRate](#samplerate) for details. | 1.0 | No | | `tracing.capturedRequestHeaders` | Defines the list of request headers to add as attributes.
It applies to client and server kind spans. | [] | No | | `tracing.capturedResponseHeaders` | Defines the list of response headers to add as attributes.
It applies to client and server kind spans. | [] | False | | `tracing.safeQueryParams` | By default, all query parameters are redacted.
Defines the list of query parameters to not redact. | [] | No | @@ -61,6 +61,17 @@ tracing: {} | `tracing.otlp.grpc.tls.key` | This instructs the exporter to send the tracing to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | ""null/false "" | No | | `tracing.otlp.grpc.tls.insecureskipverify` | If `insecureSkipVerify` is `true`, the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | +## sampleRate + +The `sampleRate` option controls trace sampling using a `ParentBased(TraceIDRatioBased)` strategy. + +!!! info "Sampling Strategy Behavior" + + - **Root spans** (trace originating at Traefik): Sampled according to the configured `sampleRate` using trace ID ratio-based sampling. + - **Child spans** (requests with existing trace context): Inherit the sampling decision from the parent span, regardless of the local `sampleRate`. + + This ensures consistent sampling decisions across distributed traces: once a trace is sampled, all spans in that trace are sampled, providing complete end-to-end visibility. + ## resourceAttributes The `resourceAttributes` option allows setting the resource attributes sent along the traces. diff --git a/docs/content/reference/routing-configuration/http/middlewares/errorpages.md b/docs/content/reference/routing-configuration/http/middlewares/errorpages.md index 2855a5cbb4..7e17d480b6 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/errorpages.md +++ b/docs/content/reference/routing-configuration/http/middlewares/errorpages.md @@ -19,8 +19,8 @@ http: - "503" - "505-599" statusRewrites: - "418": "404" - "502-504": "500" + "418": 404 + "502-504": 500 service: error-handler-service query: "/{status}.html" diff --git a/docs/content/reference/routing-configuration/http/tls/tls-certificates.md b/docs/content/reference/routing-configuration/http/tls/tls-certificates.md index cb2f1a4cef..48cee82023 100644 --- a/docs/content/reference/routing-configuration/http/tls/tls-certificates.md +++ b/docs/content/reference/routing-configuration/http/tls/tls-certificates.md @@ -41,6 +41,20 @@ tls: It is the only available method to configure the certificates (as well as the options and the stores). However, in [Kubernetes](../../../install-configuration/providers/kubernetes/kubernetes-crd.md), the certificates can and must be provided by [secrets](https://kubernetes.io/docs/concepts/configuration/secret/). +#### Certificate selection (SNI) + +Traefik selects the certificate to present during the TLS handshake, based on the Server Name Indication (SNI) sent by the client. + +However, HTTP router rules (e.g., `Host()`) are evaluated after TLS has been established, so they do not influence certificate selection. + +##### Strict SNI Checking + +By default, if the client does not send SNI, or if no certificate matches the requested server name, +Traefik falls back to the [default certificate](#default-certificate) from the TLS store (if configured). + +To reject connections without SNI (or with an unknown server name) instead of falling back to the default certificate, +enable `sniStrict` in [TLS Options](./tls-options.md#strict-sni-checking). + ## Certificates Stores In Traefik, certificates are grouped together in certificates stores. @@ -82,6 +96,12 @@ tls: The `stores` list will actually be ignored and automatically set to `["default"]`. +!!! tip "Per provider examples" + + - [Docker: Enable TLS](../../../../expose/docker/basic.md#enable-tls) + - [Swarm: Enable TLS](../../../../expose/swarm/basic.md#enable-tls) + - [Kubernetes: Enable TLS](../../../../expose/kubernetes/basic.md#enable-tls) + ### Default Certificate Traefik can use a default certificate for connections without a SNI, or without a matching domain. diff --git a/docs/content/secure/secure-api-access-with-jwt.md b/docs/content/secure/secure-api-access-with-jwt.md index 4c9b8eb658..acc6fe7358 100644 --- a/docs/content/secure/secure-api-access-with-jwt.md +++ b/docs/content/secure/secure-api-access-with-jwt.md @@ -9,7 +9,7 @@ description: 'Traefik Hub API Gateway - Learn how to configure the JWT Authentic This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). JSON Web Token (JWT) (defined in the [RFC 7519](https://tools.ietf.org/html/rfc7519)) allows -Traefik Hub API Gateway to secure the API access using a token signed using either a private signing secret or a plublic/private key. +Traefik Hub API Gateway to secure the API access using a token signed using either a private signing secret or a public/private key. Traefik Hub API Gateway provides many kinds of sources to perform the token validation: diff --git a/go.mod b/go.mod index 5c0935e899..b017d66db2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/traefik/traefik/v3 -go 1.24.0 +go 1.25.0 require ( github.com/BurntSushi/toml v1.6.0 @@ -217,7 +217,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.2 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.1 // indirect @@ -392,7 +392,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect + golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect golang.org/x/oauth2 v0.34.0 // indirect golang.org/x/term v0.39.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 00b764cced..57d3e30a68 100644 --- a/go.sum +++ b/go.sum @@ -464,8 +464,9 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA= github.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= @@ -1524,8 +1525,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 30db7c3cce..14670336ec 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -398,8 +398,7 @@ func (l *ServersLoadBalancer) Merge(other *ServersLoadBalancer) bool { // SetDefaults Default values for a ServersLoadBalancer. func (l *ServersLoadBalancer) SetDefaults() { - defaultPassHostHeader := DefaultPassHostHeader - l.PassHostHeader = &defaultPassHostHeader + l.PassHostHeader = ptr.To(DefaultPassHostHeader) l.Strategy = BalancerStrategyWRR l.ResponseForwarding = &ResponseForwarding{} @@ -473,8 +472,7 @@ type ServerHealthCheck struct { // SetDefaults Default values for a HealthCheck. func (h *ServerHealthCheck) SetDefaults() { - fr := true - h.FollowRedirects = &fr + h.FollowRedirects = ptr.To(true) h.Mode = "http" h.Interval = DefaultHealthCheckInterval h.Timeout = DefaultHealthCheckTimeout diff --git a/pkg/config/static/entrypoints.go b/pkg/config/static/entrypoints.go index 7c6b642a06..6f6ac1fc34 100644 --- a/pkg/config/static/entrypoints.go +++ b/pkg/config/static/entrypoints.go @@ -9,6 +9,7 @@ import ( ptypes "github.com/traefik/paerser/types" otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/types" + "k8s.io/utils/ptr" ) // EntryPoint holds the entry point configuration. @@ -76,8 +77,7 @@ type HTTPConfig struct { // SetDefaults sets the default values. func (c *HTTPConfig) SetDefaults() { - sanitizePath := true - c.SanitizePath = &sanitizePath + c.SanitizePath = ptr.To(true) c.MaxHeaderBytes = http.DefaultMaxHeaderBytes } @@ -201,9 +201,8 @@ type ObservabilityConfig struct { // SetDefaults sets the default values. func (o *ObservabilityConfig) SetDefaults() { - defaultValue := true - o.AccessLogs = &defaultValue - o.Metrics = &defaultValue - o.Tracing = &defaultValue + o.AccessLogs = ptr.To(true) + o.Metrics = ptr.To(true) + o.Tracing = ptr.To(true) o.TraceVerbosity = otypes.MinimalVerbosity } diff --git a/pkg/healthcheck/healthcheck_tcp_test.go b/pkg/healthcheck/healthcheck_tcp_test.go index 4f80fd0176..bb151cf933 100644 --- a/pkg/healthcheck/healthcheck_tcp_test.go +++ b/pkg/healthcheck/healthcheck_tcp_test.go @@ -571,12 +571,9 @@ func TestServiceTCPHealthChecker_differentIntervals(t *testing.T) { hc := NewServiceTCPHealthChecker(ctx, config, lb, serviceInfo, targets, "test-service") wg := sync.WaitGroup{} - wg.Add(1) - - go func() { + wg.Go(func() { hc.Launch(ctx) - wg.Done() - }() + }) // Let it run for 2 seconds to see the different check frequencies select { diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go index 077a01349f..cba259ce6e 100644 --- a/pkg/healthcheck/healthcheck_test.go +++ b/pkg/healthcheck/healthcheck_test.go @@ -439,12 +439,9 @@ func TestServiceHealthChecker_Launch(t *testing.T) { hc := NewServiceHealthChecker(ctx, &MetricsMock{gauge}, config, lb, serviceInfo, http.DefaultTransport, map[string]*url.URL{"test": targetURL}, "foobar") wg := sync.WaitGroup{} - wg.Add(1) - - go func() { + wg.Go(func() { hc.Launch(ctx) - wg.Done() - }() + }) // Wait for expected health check events using channel synchronization. for i := range expectedEvents { @@ -508,12 +505,10 @@ func TestDifferentIntervals(t *testing.T) { hc := NewServiceHealthChecker(ctx, &MetricsMock{gauge}, config, lb, serviceInfo, http.DefaultTransport, map[string]*url.URL{"healthy": healthyURL, "unhealthy": unhealthyURL}, "foobar") wg := sync.WaitGroup{} - wg.Add(1) - - go func() { + wg.Go(func() { hc.Launch(ctx) wg.Done() - }() + }) select { case <-time.After(2 * time.Second): diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index 312ce648a0..a9461828c3 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -159,13 +159,11 @@ func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error) } if config.BufferingSize > 0 { - logHandler.wg.Add(1) - go func() { - defer logHandler.wg.Done() + logHandler.wg.Go(func() { for handlerParams := range logHandler.logHandlerChan { logHandler.logTheRoundTrip(handlerParams.ctx, handlerParams.logDataTable) } - }() + }) } return logHandler, nil diff --git a/pkg/middlewares/forwardedheaders/forwarded_header.go b/pkg/middlewares/forwardedheaders/forwarded_header.go index ba0e1355bc..861624596c 100644 --- a/pkg/middlewares/forwardedheaders/forwarded_header.go +++ b/pkg/middlewares/forwardedheaders/forwarded_header.go @@ -98,16 +98,14 @@ func isWebsocketRequest(req *http.Request) bool { containsHeader := func(name, value string) bool { h := unsafeHeader(req.Header).Get(name) for { - pos := strings.Index(h, ",") - if pos == -1 { - return strings.EqualFold(value, strings.TrimSpace(h)) - } - - if strings.EqualFold(value, strings.TrimSpace(h[:pos])) { + before, after, found := strings.Cut(h, ",") + if strings.EqualFold(value, strings.TrimSpace(before)) { return true } - - h = h[pos+1:] + if !found { + return false + } + h = after } } diff --git a/pkg/observability/types/tracing.go b/pkg/observability/types/tracing.go index 9f525f9c2b..c9db2de766 100644 --- a/pkg/observability/types/tracing.go +++ b/pkg/observability/types/tracing.go @@ -97,7 +97,7 @@ func (c *OTelTracing) Setup(ctx context.Context, serviceName string, sampleRate // span processor to aggregate spans before export. bsp := sdktrace.NewBatchSpanProcessor(exporter) tracerProvider := sdktrace.NewTracerProvider( - sdktrace.WithSampler(sdktrace.TraceIDRatioBased(sampleRate)), + sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(sampleRate))), sdktrace.WithResource(res), sdktrace.WithSpanProcessor(bsp), ) diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go index 705bab6b1c..fe82cc7dff 100644 --- a/pkg/server/server_entrypoint_tcp.go +++ b/pkg/server/server_entrypoint_tcp.go @@ -147,16 +147,12 @@ func (eps TCPEntryPoints) Stop() { var wg sync.WaitGroup for epn, ep := range eps { - wg.Add(1) - - go func(entryPointName string, entryPoint *TCPEntryPoint) { - defer wg.Done() - - logger := log.With().Str(logs.EntryPointName, entryPointName).Logger() - entryPoint.Shutdown(logger.WithContext(context.Background())) + wg.Go(func() { + logger := log.With().Str(logs.EntryPointName, epn).Logger() + ep.Shutdown(logger.WithContext(context.Background())) logger.Debug().Msg("Entrypoint closed") - }(epn, ep) + }) } wg.Wait() @@ -313,7 +309,6 @@ func (e *TCPEntryPoint) Shutdown(ctx context.Context) { var wg sync.WaitGroup shutdownServer := func(server stoppable) { - defer wg.Done() err := server.Shutdown(ctx) if err == nil { return @@ -334,24 +329,19 @@ func (e *TCPEntryPoint) Shutdown(ctx context.Context) { } if e.httpServer.Server != nil { - wg.Add(1) - go shutdownServer(e.httpServer.Server) + wg.Go(func() { shutdownServer(e.httpServer.Server) }) } if e.httpsServer.Server != nil { - wg.Add(1) - go shutdownServer(e.httpsServer.Server) + wg.Go(func() { shutdownServer(e.httpsServer.Server) }) if e.http3Server != nil { - wg.Add(1) - go shutdownServer(e.http3Server) + wg.Go(func() { shutdownServer(e.http3Server) }) } } if e.tracker != nil { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { err := e.tracker.Shutdown(ctx) if err == nil { return @@ -360,7 +350,7 @@ func (e *TCPEntryPoint) Shutdown(ctx context.Context) { logger.Debug().Err(err).Msg("Server failed to shutdown before deadline") } e.tracker.Close() - }() + }) } wg.Wait() diff --git a/pkg/server/server_entrypoint_udp.go b/pkg/server/server_entrypoint_udp.go index 2d72504960..323d5ac2c8 100644 --- a/pkg/server/server_entrypoint_udp.go +++ b/pkg/server/server_entrypoint_udp.go @@ -50,16 +50,14 @@ func (eps UDPEntryPoints) Stop() { var wg sync.WaitGroup for epn, ep := range eps { - wg.Add(1) - - go func(entryPointName string, entryPoint *UDPEntryPoint) { + wg.Go(func() { defer wg.Done() - logger := log.With().Str(logs.EntryPointName, entryPointName).Logger() - entryPoint.Shutdown(logger.WithContext(context.Background())) + logger := log.With().Str(logs.EntryPointName, epn).Logger() + ep.Shutdown(logger.WithContext(context.Background())) logger.Debug().Msg("Entry point closed") - }(epn, ep) + }) } wg.Wait() diff --git a/pkg/server/service/loadbalancer/leasttime/leasttime_test.go b/pkg/server/service/loadbalancer/leasttime/leasttime_test.go index 7c199eefc9..ede621426a 100644 --- a/pkg/server/service/loadbalancer/leasttime/leasttime_test.go +++ b/pkg/server/service/loadbalancer/leasttime/leasttime_test.go @@ -592,9 +592,7 @@ func TestConcurrentInflightTracking(t *testing.T) { numRequests := 50 for range numRequests { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { handler.inflightCount.Add(1) defer handler.inflightCount.Add(-1) @@ -608,7 +606,7 @@ func TestConcurrentInflightTracking(t *testing.T) { } time.Sleep(1 * time.Millisecond) - }() + }) } wg.Wait() @@ -661,12 +659,10 @@ func TestConcurrentRequestsRespectInflight(t *testing.T) { inflightRequests := 5 for range inflightRequests { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { recorder := httptest.NewRecorder() balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) - }() + }) } // Wait for goroutines to start and increment inflight counters. @@ -687,9 +683,7 @@ func TestConcurrentRequestsRespectInflight(t *testing.T) { // Launch new requests in background so they don't block. var newWg sync.WaitGroup for range newRequests { - newWg.Add(1) - go func() { - defer newWg.Done() + newWg.Go(func() { rec := httptest.NewRecorder() balancer.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil)) server := rec.Header().Get("server") @@ -698,7 +692,7 @@ func TestConcurrentRequestsRespectInflight(t *testing.T) { save[server]++ saveMu.Unlock() } - }() + }) } // Wait for new requests to start and see the inflight counts.