Merge branch v2.9 into master
This commit is contained in:
commit
281fa25844
17 changed files with 267 additions and 95 deletions
|
@ -22,22 +22,23 @@ builds:
|
|||
- openbsd
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- '386'
|
||||
- arm
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
goarm:
|
||||
- 7
|
||||
- 6
|
||||
- 5
|
||||
- '7'
|
||||
- '6'
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
goarch: '386'
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
- goos: openbsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
- goos: freebsd
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
|
|
26
CHANGELOG.md
26
CHANGELOG.md
|
@ -1,3 +1,29 @@
|
|||
## [v2.9.4](https://github.com/traefik/traefik/tree/v2.9.4) (2022-10-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.1...v2.9.4)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.9.0 ([#9413](https://github.com/traefik/traefik/pull/9413) by [tony-defa](https://github.com/tony-defa))
|
||||
- **[kv,redis]** Fix Redis configuration type ([#9435](https://github.com/traefik/traefik/pull/9435) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware,metrics]** Handle capture on redefined http.responseWriters ([#9440](https://github.com/traefik/traefik/pull/9440) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[middleware,k8s]** Remove raw cert escape in PassTLSClientCert middleware ([#9412](https://github.com/traefik/traefik/pull/9412) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[plugins]** Update Yaegi to v0.14.3 ([#9468](https://github.com/traefik/traefik/pull/9468) by [ldez](https://github.com/ldez))
|
||||
- Remove side effect on default transport tests ([#9460](https://github.com/traefik/traefik/pull/9460) by [sdelicata](https://github.com/sdelicata))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Fix links to gateway API guides ([#9445](https://github.com/traefik/traefik/pull/9445) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Simplify dashboard rule example ([#9454](https://github.com/traefik/traefik/pull/9454) by [sosoba](https://github.com/sosoba))
|
||||
- Add v2.9 to release page ([#9438](https://github.com/traefik/traefik/pull/9438) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.9.3](https://github.com/traefik/traefik/tree/v2.9.3) (2022-10-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.1...v2.9.3)
|
||||
|
||||
Release canceled.
|
||||
|
||||
## [v2.9.2](https://github.com/traefik/traefik/tree/v2.9.2) (2022-10-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.1...v2.9.2)
|
||||
|
||||
Release canceled.
|
||||
|
||||
## [v2.9.1](https://github.com/traefik/traefik/tree/v2.9.1) (2022-10-03)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.0-rc1...v2.9.1)
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -189,7 +189,7 @@ generate-genconf:
|
|||
.PHONY: release-packages
|
||||
release-packages: generate-webui build-dev-image
|
||||
rm -rf dist
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish --timeout="90m"
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish -p 4 --timeout="90m"
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
|
|
|
@ -32,7 +32,6 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/metrics"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/capture"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/acme"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/aggregator"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/hub"
|
||||
|
@ -287,9 +286,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
tracer := setupTracing(staticConfiguration.Tracing)
|
||||
captureMiddleware := setupCapture(staticConfiguration)
|
||||
|
||||
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer, captureMiddleware)
|
||||
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer)
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder, metricsRegistry)
|
||||
|
||||
// Watcher
|
||||
|
@ -638,13 +636,6 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
|
|||
return tracer
|
||||
}
|
||||
|
||||
func setupCapture(staticConfiguration *static.Configuration) *capture.Handler {
|
||||
if staticConfiguration.AccessLog == nil && staticConfiguration.Metrics == nil {
|
||||
return nil
|
||||
}
|
||||
return &capture.Handler{}
|
||||
}
|
||||
|
||||
func configureLogging(staticConfiguration *static.Configuration) {
|
||||
// configure default log flags
|
||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||
|
|
2
go.mod
2
go.mod
|
@ -63,7 +63,7 @@ require (
|
|||
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
|
||||
github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2
|
||||
github.com/traefik/paerser v0.1.9
|
||||
github.com/traefik/yaegi v0.14.2
|
||||
github.com/traefik/yaegi v0.14.3
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.2.0+incompatible
|
||||
github.com/unrolled/render v1.0.2
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1997,8 +1997,8 @@ github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305 h1:y/1cL5AL2oRcfz
|
|||
github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305/go.mod h1:gXOLibKqQTRAVuVZ9gX7G9Ykky8ll8yb4slxsEMoY0c=
|
||||
github.com/traefik/paerser v0.1.9 h1:x5hZafOt/yogLvr6upoSOYIAn2nh2GsnLb236MOzd4I=
|
||||
github.com/traefik/paerser v0.1.9/go.mod h1:Dk3Bfz6Zyj13/S8pJyRdx/FNvXlsVRVbtp0UK4ZSiA0=
|
||||
github.com/traefik/yaegi v0.14.2 h1:9t9xepIfar6BrYdwJHGc+XRKo6qFoJCl6Z46N3hUtUw=
|
||||
github.com/traefik/yaegi v0.14.2/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0=
|
||||
github.com/traefik/yaegi v0.14.3 h1:LqA0k8DKwvRMc+msfQjNusphHJc+r6WC5tZU5TmUFOM=
|
||||
github.com/traefik/yaegi v0.14.3/go.mod h1:AVRxhaI2G+nUsaM1zyktzwXn69G3t/AuTDrCiTds9p0=
|
||||
github.com/transip/gotransip/v6 v6.17.0 h1:2RCyqYqz5+Ej8z96EyE4sf6tQrrfEBaFDO0LliSl6+8=
|
||||
github.com/transip/gotransip/v6 v6.17.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.webA]
|
||||
address = ":8001"
|
||||
[entryPoints.webB]
|
||||
address = ":8002"
|
||||
[entryPoints.webC]
|
||||
address = ":8003"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[metrics]
|
||||
[metrics.prometheus]
|
||||
buckets = "0.1,0.3,1.2,5.0"
|
||||
|
||||
[providers]
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[http.routers]
|
||||
|
||||
[http.routers.router-without]
|
||||
entrypoints = ["webA"]
|
||||
service = "service-without"
|
||||
rule = "PathPrefix(`/without`)"
|
||||
|
||||
[http.routers.router-req]
|
||||
entrypoints = ["webB"]
|
||||
service = "service-req"
|
||||
rule = "PathPrefix(`/with-req`)"
|
||||
middlewares = ["buffer-req"]
|
||||
|
||||
[http.routers.router-resp]
|
||||
entrypoints = ["webC"]
|
||||
service = "service-resp"
|
||||
rule = "PathPrefix(`/with-resp`)"
|
||||
middlewares = ["buffer-resp"]
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.buffer-req.buffering]
|
||||
maxRequestBodyBytes = 10
|
||||
|
||||
[http.middlewares.buffer-resp.buffering]
|
||||
maxResponseBodyBytes = 10
|
||||
|
||||
[http.services]
|
||||
[http.services.service-without.loadBalancer]
|
||||
[[http.services.service-without.loadBalancer.servers]]
|
||||
url = "http://{{ .IP }}"
|
||||
|
||||
[http.services.service-req.loadBalancer]
|
||||
[[http.services.service-req.loadBalancer.servers]]
|
||||
url = "http://{{ .IP }}"
|
||||
|
||||
[http.services.service-resp.loadBalancer]
|
||||
[[http.services.service-resp.loadBalancer.servers]]
|
||||
url = "http://{{ .IP }}"
|
|
@ -308,7 +308,7 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntryPoint(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix(`/whoami`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -369,6 +369,84 @@ func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService(c *check.C) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestMetricsWithBufferingMiddleware checks that the buffering middleware
|
||||
// (which introduces its own response writer in the chain), does not interfere with
|
||||
// the capture middleware on which the metrics mechanism relies.
|
||||
func (s *SimpleSuite) TestMetricsWithBufferingMiddleware(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
|
||||
s.composeUp(c)
|
||||
defer s.composeDown(c)
|
||||
|
||||
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("MORE THAN TEN BYTES IN RESPONSE"))
|
||||
}))
|
||||
|
||||
server.Start()
|
||||
defer server.Close()
|
||||
|
||||
file := s.adaptFile(c, "fixtures/simple_metrics_with_buffer_middleware.toml", struct{ IP string }{IP: strings.TrimPrefix(server.URL, "http://")})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, output := s.traefikCmd(withConfigFile(file))
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix(`/without`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8001/without", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8002/with-req", strings.NewReader("MORE THAN TEN BYTES IN REQUEST"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// The request should fail because the body is too large.
|
||||
err = try.Request(req, 1*time.Second, try.StatusCodeIs(http.StatusRequestEntityTooLarge))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// The request should fail because the response exceeds the configured limit.
|
||||
err = try.GetRequest("http://127.0.0.1:8003/with-resp", 1*time.Second, try.StatusCodeIs(http.StatusInternalServerError))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/metrics", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
response, err := http.DefaultClient.Do(request)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
body, err := io.ReadAll(response.Body)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// For allowed requests and responses, the entrypoint and service metrics have the same status code.
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_bytes_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_responses_bytes_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 31")
|
||||
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_responses_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 31")
|
||||
|
||||
// For forbidden requests, the entrypoints have metrics, the services don't.
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_bytes_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_responses_bytes_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 24")
|
||||
|
||||
// For disallowed responses, the entrypoint and service metrics don't have the same status code.
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_bytes_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_responses_bytes_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 21")
|
||||
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_responses_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 31")
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
|
||||
|
|
|
@ -227,6 +227,15 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
|||
core[ClientHost] = forwardedFor
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
capt, err := capture.FromContext(ctx)
|
||||
if err != nil {
|
||||
log.FromContext(log.With(ctx, log.Str(log.MiddlewareType, "AccessLogs"))).
|
||||
WithError(err).
|
||||
Errorf("Could not get Capture")
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, reqWithDataTable)
|
||||
|
||||
if _, ok := core[ClientUsername]; !ok {
|
||||
|
@ -237,13 +246,6 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
|||
headers: rw.Header().Clone(),
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
capt, err := capture.FromContext(ctx)
|
||||
if err != nil {
|
||||
log.FromContext(log.With(ctx, log.Str(log.MiddlewareType, "AccessLogs"))).Errorf("Could not get Capture: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
logDataTable.DownstreamResponse.status = capt.StatusCode()
|
||||
logDataTable.DownstreamResponse.size = capt.ResponseSize()
|
||||
logDataTable.Request.size = capt.RequestSize()
|
||||
|
|
|
@ -57,7 +57,7 @@ func TestLogRotation(t *testing.T) {
|
|||
})
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(WrapHandler(logHandler))
|
||||
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
@ -210,7 +210,7 @@ func TestLoggerHeaderFields(t *testing.T) {
|
|||
}
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(WrapHandler(logger))
|
||||
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
@ -784,7 +784,7 @@ func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS bool) {
|
|||
}
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(WrapHandler(logger))
|
||||
handler, err := chain.Then(http.HandlerFunc(logWriterTestHandlerFunc))
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
// For another middleware to get those attributes of a request/response, this middleware
|
||||
// should be added before in the middleware chain.
|
||||
//
|
||||
// handler, _ := NewHandler()
|
||||
// chain := alice.New().
|
||||
// Append(WrapHandler(handler)).
|
||||
// Append(capture.Wrap).
|
||||
// Append(myOtherMiddleware).
|
||||
// then(...)
|
||||
//
|
||||
|
@ -33,7 +32,6 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
)
|
||||
|
||||
|
@ -41,62 +39,67 @@ type key string
|
|||
|
||||
const capturedData key = "capturedData"
|
||||
|
||||
// Handler will store each request data to its context.
|
||||
type Handler struct{}
|
||||
|
||||
// WrapHandler wraps capture handler into an Alice Constructor.
|
||||
func WrapHandler(handler *Handler) alice.Constructor {
|
||||
return func(next http.Handler) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
handler.ServeHTTP(rw, req, next)
|
||||
}), nil
|
||||
}
|
||||
// Wrap returns a new handler that inserts a Capture into the given handler.
|
||||
// It satisfies the alice.Constructor type.
|
||||
func Wrap(handler http.Handler) (http.Handler, error) {
|
||||
c := Capture{}
|
||||
return c.Reset(handler), nil
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) {
|
||||
c := Capture{}
|
||||
if req.Body != nil {
|
||||
readCounter := &readCounter{source: req.Body}
|
||||
c.rr = readCounter
|
||||
req.Body = readCounter
|
||||
// FromContext returns the Capture value found in ctx, or an empty Capture otherwise.
|
||||
func FromContext(ctx context.Context) (Capture, error) {
|
||||
c := ctx.Value(capturedData)
|
||||
if c == nil {
|
||||
return Capture{}, errors.New("value not found in context")
|
||||
}
|
||||
responseWriter := newResponseWriter(rw)
|
||||
c.rw = responseWriter
|
||||
ctx := context.WithValue(req.Context(), capturedData, &c)
|
||||
next.ServeHTTP(responseWriter, req.WithContext(ctx))
|
||||
capt, ok := c.(*Capture)
|
||||
if !ok {
|
||||
return Capture{}, errors.New("value stored in context is not a *Capture")
|
||||
}
|
||||
return *capt, nil
|
||||
}
|
||||
|
||||
// Capture is the object populated by the capture middleware,
|
||||
// allowing to gather information about the request and response.
|
||||
// holding probes that allow to gather information about the request and response.
|
||||
type Capture struct {
|
||||
rr *readCounter
|
||||
rw responseWriter
|
||||
}
|
||||
|
||||
// FromContext returns the Capture value found in ctx, or an empty Capture otherwise.
|
||||
func FromContext(ctx context.Context) (*Capture, error) {
|
||||
c := ctx.Value(capturedData)
|
||||
if c == nil {
|
||||
return nil, errors.New("value not found")
|
||||
}
|
||||
capt, ok := c.(*Capture)
|
||||
if !ok {
|
||||
return nil, errors.New("value stored in Context is not a *Capture")
|
||||
}
|
||||
return capt, nil
|
||||
// NeedsReset returns whether the given http.ResponseWriter is the capture's probe.
|
||||
func (c *Capture) NeedsReset(rw http.ResponseWriter) bool {
|
||||
return c.rw != rw
|
||||
}
|
||||
|
||||
func (c Capture) ResponseSize() int64 {
|
||||
// Reset returns a new handler that renews the Capture's probes, and inserts
|
||||
// them when deferring to next.
|
||||
func (c *Capture) Reset(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
ctx := context.WithValue(req.Context(), capturedData, c)
|
||||
newReq := req.WithContext(ctx)
|
||||
|
||||
if newReq.Body != nil {
|
||||
readCounter := &readCounter{source: newReq.Body}
|
||||
c.rr = readCounter
|
||||
newReq.Body = readCounter
|
||||
}
|
||||
c.rw = newResponseWriter(rw)
|
||||
|
||||
next.ServeHTTP(c.rw, newReq)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Capture) ResponseSize() int64 {
|
||||
return c.rw.Size()
|
||||
}
|
||||
|
||||
func (c Capture) StatusCode() int {
|
||||
func (c *Capture) StatusCode() int {
|
||||
return c.rw.Status()
|
||||
}
|
||||
|
||||
// RequestSize returns the size of the request's body if it applies,
|
||||
// zero otherwise.
|
||||
func (c Capture) RequestSize() int64 {
|
||||
func (c *Capture) RequestSize() int64 {
|
||||
if c.rr == nil {
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -38,9 +38,8 @@ func TestCapture(t *testing.T) {
|
|||
assert.Equal(t, "bar", string(all))
|
||||
})
|
||||
|
||||
wrapped := WrapHandler(&Handler{})
|
||||
chain := alice.New()
|
||||
chain = chain.Append(wrapped)
|
||||
chain = chain.Append(Wrap)
|
||||
chain = chain.Append(wrapMiddleware)
|
||||
handlers, err := chain.Then(handler)
|
||||
require.NoError(t, err)
|
||||
|
@ -142,8 +141,7 @@ func BenchmarkCapture(b *testing.B) {
|
|||
|
||||
chain := alice.New()
|
||||
if test.capture || test.body {
|
||||
captureWrapped := WrapHandler(&Handler{})
|
||||
chain = chain.Append(captureWrapped)
|
||||
chain = chain.Append(Wrap)
|
||||
}
|
||||
handlers, err := chain.Then(next)
|
||||
require.NoError(b, err)
|
||||
|
|
|
@ -24,6 +24,7 @@ const (
|
|||
protoWebsocket = "websocket"
|
||||
typeName = "Metrics"
|
||||
nameEntrypoint = "metrics-entrypoint"
|
||||
nameRouter = "metrics-router"
|
||||
nameService = "metrics-service"
|
||||
)
|
||||
|
||||
|
@ -56,7 +57,7 @@ func NewEntryPointMiddleware(ctx context.Context, next http.Handler, registry me
|
|||
|
||||
// NewRouterMiddleware creates a new metrics middleware for a Router.
|
||||
func NewRouterMiddleware(ctx context.Context, next http.Handler, registry metrics.Registry, routerName string, serviceName string) http.Handler {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, nameEntrypoint, typeName)).Debug("Creating middleware")
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, nameRouter, typeName)).Debug("Creating middleware")
|
||||
|
||||
return &metricsMiddleware{
|
||||
next: next,
|
||||
|
@ -125,17 +126,25 @@ func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
|||
m.reqsTLSCounter.With(tlsLabels...).Add(1)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
m.next.ServeHTTP(rw, req)
|
||||
|
||||
ctx := req.Context()
|
||||
|
||||
capt, err := capture.FromContext(ctx)
|
||||
if err != nil {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, nameEntrypoint, typeName)).Errorf("Could not get Capture: %w", err)
|
||||
for i := 0; i < len(m.baseLabels); i += 2 {
|
||||
ctx = log.With(ctx, log.Str(m.baseLabels[i], m.baseLabels[i+1]))
|
||||
}
|
||||
log.FromContext(ctx).WithError(err).Errorf("Could not get Capture")
|
||||
return
|
||||
}
|
||||
|
||||
next := m.next
|
||||
if capt.NeedsReset(rw) {
|
||||
next = capt.Reset(m.next)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
next.ServeHTTP(rw, req)
|
||||
|
||||
labels = append(labels, "code", strconv.Itoa(capt.StatusCode()))
|
||||
m.reqDurationHistogram.With(labels...).ObserveFromStart(start)
|
||||
m.reqsCounter.With(labels...).Add(1)
|
||||
|
|
|
@ -18,16 +18,14 @@ type ChainBuilder struct {
|
|||
metricsRegistry metrics.Registry
|
||||
accessLoggerMiddleware *accesslog.Handler
|
||||
tracer *tracing.Tracing
|
||||
captureMiddleware *capture.Handler
|
||||
}
|
||||
|
||||
// NewChainBuilder Creates a new ChainBuilder.
|
||||
func NewChainBuilder(metricsRegistry metrics.Registry, accessLoggerMiddleware *accesslog.Handler, tracer *tracing.Tracing, captureMiddleware *capture.Handler) *ChainBuilder {
|
||||
func NewChainBuilder(metricsRegistry metrics.Registry, accessLoggerMiddleware *accesslog.Handler, tracer *tracing.Tracing) *ChainBuilder {
|
||||
return &ChainBuilder{
|
||||
metricsRegistry: metricsRegistry,
|
||||
accessLoggerMiddleware: accessLoggerMiddleware,
|
||||
tracer: tracer,
|
||||
captureMiddleware: captureMiddleware,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,8 +33,8 @@ func NewChainBuilder(metricsRegistry metrics.Registry, accessLoggerMiddleware *a
|
|||
func (c *ChainBuilder) Build(ctx context.Context, entryPointName string) alice.Chain {
|
||||
chain := alice.New()
|
||||
|
||||
if c.captureMiddleware != nil {
|
||||
chain = chain.Append(capture.WrapHandler(c.captureMiddleware))
|
||||
if c.accessLoggerMiddleware != nil || c.metricsRegistry != nil && (c.metricsRegistry.IsEpEnabled() || c.metricsRegistry.IsRouterEnabled() || c.metricsRegistry.IsSvcEnabled()) {
|
||||
chain = chain.Append(capture.Wrap)
|
||||
}
|
||||
|
||||
if c.accessLoggerMiddleware != nil {
|
||||
|
|
|
@ -316,7 +316,7 @@ func TestRouterManager_Get(t *testing.T) {
|
|||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
|
||||
|
@ -422,7 +422,7 @@ func TestAccessLog(t *testing.T) {
|
|||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
|
||||
|
@ -439,7 +439,7 @@ func TestAccessLog(t *testing.T) {
|
|||
reqHost := requestdecorator.New(nil)
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(accesslog.WrapHandler(accesslogger))
|
||||
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
reqHost.ServeHTTP(w, req, handlers["web"].ServeHTTP)
|
||||
|
@ -717,7 +717,7 @@ func TestRuntimeConfiguration(t *testing.T) {
|
|||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
|
||||
|
@ -792,7 +792,7 @@ func TestProviderOnMiddlewares(t *testing.T) {
|
|||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
|
||||
|
@ -860,7 +860,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ func TestReuseService(t *testing.T) {
|
|||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
|
||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
||||
|
||||
|
@ -189,7 +189,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
|
||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)}))
|
||||
|
||||
|
@ -232,7 +232,7 @@ func TestInternalServices(t *testing.T) {
|
|||
|
||||
voidRegistry := metrics.NewVoidRegistry()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(voidRegistry, nil, nil, nil), nil, voidRegistry)
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(voidRegistry, nil, nil), nil, voidRegistry)
|
||||
|
||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
|||
OutputType = "file"
|
||||
FileName = "traefik_changelog.md"
|
||||
|
||||
# example new bugfix v2.9.1
|
||||
# example new bugfix v2.9.2
|
||||
CurrentRef = "v2.9"
|
||||
PreviousRef = "v2.9.0"
|
||||
PreviousRef = "v2.9.1"
|
||||
BaseBranch = "v2.9"
|
||||
FutureCurrentRefName = "v2.9.0"
|
||||
FutureCurrentRefName = "v2.9.2"
|
||||
|
||||
ThresholdPreviousRef = 10
|
||||
ThresholdCurrentRef = 10
|
||||
|
|
Loading…
Reference in a new issue