From 8128d6ca2687b904eb8399bab74841d0bddfef63 Mon Sep 17 00:00:00 2001 From: sosoba Date: Tue, 18 Oct 2022 15:38:12 +0200 Subject: [PATCH 01/13] Simplify dashboard rule example --- docs/content/operations/dashboard.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index af1ce1058..c838f0afd 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -93,12 +93,12 @@ rule = "Host(`traefik.example.com`)" ```bash tab="Path Prefix Rule" # The dashboard can be accessed on http://example.com/dashboard/ or http://traefik.example.com/dashboard/ -rule = "PathPrefix(`/api`) || PathPrefix(`/dashboard`)" +rule = "PathPrefix(`/api`, `/dashboard`)" ``` ```bash tab="Combination of Rules" # The dashboard can be accessed on http://traefik.example.com/dashboard/ -rule = "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" +rule = "Host(`traefik.example.com`) && PathPrefix(`/api`, `/dashboard`)" ``` ??? example "Dashboard Dynamic Configuration Examples" From be3b798dd6bdc44c5e468ac17810807d5cbbad29 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 21 Oct 2022 16:08:05 +0200 Subject: [PATCH 02/13] chore: update actions/cache to v3 --- .github/workflows/build.yaml | 2 +- .github/workflows/test-unit.yaml | 2 +- .github/workflows/validate.yaml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fd6f61dc0..110edf6cb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -56,7 +56,7 @@ jobs: fetch-depth: 0 - name: Cache Go modules - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/go/pkg/mod diff --git a/.github/workflows/test-unit.yaml b/.github/workflows/test-unit.yaml index cbc2eb57d..c819cd1aa 100644 --- a/.github/workflows/test-unit.yaml +++ b/.github/workflows/test-unit.yaml @@ -31,7 +31,7 @@ jobs: fetch-depth: 0 - name: Cache Go modules - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/go/pkg/mod diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 9cb143d8b..beb87eec1 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -33,7 +33,7 @@ jobs: fetch-depth: 0 - name: Cache Go modules - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/go/pkg/mod @@ -73,7 +73,7 @@ jobs: fetch-depth: 0 - name: Cache Go modules - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/go/pkg/mod From 93212125e3f95893d4aaa726275344d329fcaf69 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Sun, 23 Oct 2022 14:16:05 +0200 Subject: [PATCH 03/13] chore: bump github.com/BurntSushi/toml to v1.2.1 --- .golangci.yml | 1 - go.mod | 5 +---- go.sum | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 8267a2b94..87e90c964 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -75,7 +75,6 @@ linters-settings: - github.com/mailgun/minheap - github.com/mailgun/multibuf - github.com/jaguilar/vt100 - - github.com/BurntSushi/toml linters: enable-all: true diff --git a/go.mod b/go.mod index a153b1af6..94e122e67 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/traefik/traefik/v2 go 1.19 require ( - github.com/BurntSushi/toml v1.2.0 + github.com/BurntSushi/toml v1.2.1 github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61 github.com/Masterminds/sprig/v3 v3.2.2 github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000 @@ -354,9 +354,6 @@ replace ( // https://github.com/docker/compose/blob/e44222664abd07ce1d1fe6796d84d93cbc7468c3/go.mod#L131 replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305 -// https://github.com/BurntSushi/toml/issues/360 -replace github.com/BurntSushi/toml v1.2.0 => github.com/BurntSushi/toml v1.1.0 - // ambiguous import: found package github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http in multiple modules // tencentcloud uses monorepo with multimodule but the go.mod files are incomplete. exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible diff --git a/go.sum b/go.sum index c644b5edc..568b63f0b 100644 --- a/go.sum +++ b/go.sum @@ -112,8 +112,8 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 h1:3nVO1nQyh64IUY6BPZUpMYMZ738Pu+LsMt3E0eqqIYw= github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583/go.mod h1:EP9f4GqaDJyP1F5jTNMtzdIpw3JpNs3rMSJOnYywCiw= From ccbbd0d76686e2f8b008852d89c48c50ec5b58bb Mon Sep 17 00:00:00 2001 From: Simon Delicata Date: Mon, 24 Oct 2022 10:52:04 +0200 Subject: [PATCH 04/13] Remove side effect on default transport tests --- pkg/server/service/proxy_websocket_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/server/service/proxy_websocket_test.go b/pkg/server/service/proxy_websocket_test.go index 8b58cf94a..84e6a1aef 100644 --- a/pkg/server/service/proxy_websocket_test.go +++ b/pkg/server/service/proxy_websocket_test.go @@ -593,9 +593,11 @@ func TestWebSocketTransferTLSConfig(t *testing.T) { require.NoError(t, err) assert.Equal(t, "ok", resp) - http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + // Don't alter default transport to prevent side effects on other tests. + defaultTransport := http.DefaultTransport.(*http.Transport).Clone() + defaultTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - forwarderWithTLSConfigFromDefaultTransport, err := buildProxy(Bool(true), nil, http.DefaultTransport, nil) + forwarderWithTLSConfigFromDefaultTransport, err := buildProxy(Bool(true), nil, defaultTransport, nil) require.NoError(t, err) proxyWithTLSConfigFromDefaultTransport := createProxyWithForwarder(t, forwarderWithTLSConfigFromDefaultTransport, srv.URL) From 1b9873cae990cf446584f08299fd91037d3e2117 Mon Sep 17 00:00:00 2001 From: Wambugu Date: Wed, 26 Oct 2022 18:16:05 +0300 Subject: [PATCH 05/13] Renaming IPWhiteList to IPAllowList --- .../assets/img/middleware/ipwhitelist.png | Bin 59469 -> 0 bytes docs/content/middlewares/http/chain.md | 16 ++-- .../http/{ipwhitelist.md => ipallowlist.md} | 90 +++++++++--------- docs/content/middlewares/http/overview.md | 2 +- .../tcp/{ipwhitelist.md => ipallowlist.md} | 26 ++--- docs/content/middlewares/tcp/overview.md | 46 ++++----- docs/content/migration/v2-to-v3.md | 18 ++++ docs/content/operations/dashboard.md | 2 +- docs/content/providers/file.md | 2 +- .../dynamic-configuration/docker-labels.yml | 8 +- .../reference/dynamic-configuration/file.toml | 6 +- .../reference/dynamic-configuration/file.yaml | 4 +- .../kubernetes-crd-definition-v1.yml | 16 ++-- .../kubernetes-crd-resource.yml | 2 +- .../reference/dynamic-configuration/kv-ref.md | 14 +-- .../marathon-labels.json | 8 +- .../traefik.containo.us_middlewares.yaml | 12 +-- .../traefik.containo.us_middlewaretcps.yaml | 4 +- .../routing/providers/kubernetes-crd.md | 8 +- docs/content/routing/routers/index.md | 4 +- docs/mkdocs.yml | 5 +- integration/access_log_test.go | 8 +- integration/fixtures/k8s/01-traefik-crd.yml | 16 ++-- ...e_whitelist.toml => simple_allowlist.toml} | 0 .../{ip-whitelist.toml => ipallowlist.toml} | 8 +- integration/resources/compose/access_log.yml | 10 +- integration/resources/compose/allowlist.yml | 41 ++++++++ integration/resources/compose/whitelist.yml | 41 -------- integration/simple_test.go | 26 ++--- integration/tcp_test.go | 6 +- pkg/api/handler_overview_test.go | 12 +-- pkg/api/handler_tcp_test.go | 68 ++++++------- ...st.json => tcpmiddleware-ipallowlist.json} | 8 +- .../tcpmiddlewares-filtered-search.json | 14 +-- .../tcpmiddlewares-filtered-status.json | 14 +-- pkg/api/testdata/tcpmiddlewares-page2.json | 8 +- pkg/api/testdata/tcpmiddlewares.json | 20 ++-- pkg/config/dynamic/fixtures/sample.toml | 6 +- pkg/config/dynamic/middlewares.go | 10 +- pkg/config/dynamic/tcp_middlewares.go | 6 +- pkg/config/dynamic/zz_generated.deepcopy.go | 72 +++++++------- pkg/config/label/label_test.go | 24 ++--- pkg/ip/checker_test.go | 10 +- .../ip_allowlist.go} | 38 ++++---- .../ip_allowlist_test.go} | 26 ++--- .../ip_allowlist.go} | 32 +++---- .../ip_allowlist_test.go} | 28 +++--- pkg/provider/consulcatalog/config_test.go | 4 +- pkg/provider/docker/config_test.go | 4 +- pkg/provider/ecs/config_test.go | 4 +- .../crd/fixtures/tcp/with_middleware.yml | 12 +-- .../tcp/with_middleware_crossprovider.yml | 16 ++-- .../tcp/with_middleware_multiple_hyphens.yml | 2 +- .../with_middleware_with_cross_namespace.yml | 12 +-- pkg/provider/kubernetes/crd/kubernetes.go | 4 +- .../kubernetes/crd/kubernetes_test.go | 44 ++++----- .../crd/traefik/v1alpha1/middleware.go | 2 +- .../crd/traefik/v1alpha1/middlewaretcp.go | 4 +- .../traefik/v1alpha1/zz_generated.deepcopy.go | 12 +-- pkg/provider/kv/kv_test.go | 12 +-- pkg/provider/marathon/config_test.go | 6 +- pkg/provider/nomad/config_test.go | 4 +- pkg/provider/rancher/config_test.go | 4 +- pkg/redactor/redactor_config_test.go | 2 +- .../testdata/anonymized-dynamic-config.json | 4 +- .../testdata/secured-dynamic-config.json | 4 +- pkg/server/middleware/middlewares.go | 8 +- pkg/server/middleware/tcp/middlewares.go | 8 +- .../components/_commons/PanelMiddlewares.vue | 12 +-- 69 files changed, 523 insertions(+), 506 deletions(-) delete mode 100644 docs/content/assets/img/middleware/ipwhitelist.png rename docs/content/middlewares/http/{ipwhitelist.md => ipallowlist.md} (68%) rename docs/content/middlewares/tcp/{ipwhitelist.md => ipallowlist.md} (66%) create mode 100644 docs/content/migration/v2-to-v3.md rename integration/fixtures/{simple_whitelist.toml => simple_allowlist.toml} (100%) rename integration/fixtures/tcp/{ip-whitelist.toml => ipallowlist.toml} (83%) create mode 100644 integration/resources/compose/allowlist.yml delete mode 100644 integration/resources/compose/whitelist.yml rename pkg/api/testdata/{tcpmiddleware-ipwhitelist.json => tcpmiddleware-ipallowlist.json} (64%) rename pkg/middlewares/{ipwhitelist/ip_whitelist.go => ipallowlist/ip_allowlist.go} (59%) rename pkg/middlewares/{ipwhitelist/ip_whitelist_test.go => ipallowlist/ip_allowlist_test.go} (73%) rename pkg/middlewares/tcp/{ipwhitelist/ip_whitelist.go => ipallowlist/ip_allowlist.go} (54%) rename pkg/middlewares/tcp/{ipwhitelist/ip_whitelist_test.go => ipallowlist/ip_allowlist_test.go} (76%) diff --git a/docs/content/assets/img/middleware/ipwhitelist.png b/docs/content/assets/img/middleware/ipwhitelist.png deleted file mode 100644 index 8c6b0c97a5a68d4818584110ccd8f38a19f1ba44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59469 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

d$No7{;b{hl3DysLb*ryK8|o)4}D60_~i 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 - - + +

Source Range
@@ -689,8 +689,8 @@
- - + +
IP Strategy
@@ -1076,8 +1076,8 @@ - - + +
Source Range
From 7a6bfd333666ab8efe147d0e0b871b49a9ba51d0 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 26 Oct 2022 17:42:07 +0200 Subject: [PATCH 06/13] chore: change TCP middleware package --- pkg/middlewares/tcp/inflightconn/inflight_conn.go | 2 +- pkg/middlewares/tcp/inflightconn/inflight_conn_test.go | 2 +- pkg/middlewares/tcp/ipallowlist/ip_allowlist.go | 2 +- pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go | 2 +- pkg/server/middleware/tcp/middlewares.go | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/middlewares/tcp/inflightconn/inflight_conn.go b/pkg/middlewares/tcp/inflightconn/inflight_conn.go index dbb602432..ddbb4bdab 100644 --- a/pkg/middlewares/tcp/inflightconn/inflight_conn.go +++ b/pkg/middlewares/tcp/inflightconn/inflight_conn.go @@ -1,4 +1,4 @@ -package tcpinflightconn +package inflightconn import ( "context" diff --git a/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go b/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go index 666df4317..138e92b41 100644 --- a/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go +++ b/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go @@ -1,4 +1,4 @@ -package tcpinflightconn +package inflightconn import ( "context" diff --git a/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go b/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go index 7e8dafdc4..dd5d14d43 100644 --- a/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go +++ b/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go @@ -1,4 +1,4 @@ -package tcpipallowlist +package ipallowlist import ( "context" diff --git a/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go b/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go index 75bef050d..4f3b59e15 100644 --- a/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go +++ b/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go @@ -1,4 +1,4 @@ -package tcpipallowlist +package ipallowlist import ( "context" diff --git a/pkg/server/middleware/tcp/middlewares.go b/pkg/server/middleware/tcp/middlewares.go index 739ed3b5b..ad6320a46 100644 --- a/pkg/server/middleware/tcp/middlewares.go +++ b/pkg/server/middleware/tcp/middlewares.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/traefik/traefik/v2/pkg/config/runtime" - inflightconn "github.com/traefik/traefik/v2/pkg/middlewares/tcp/inflightconn" - ipallowlist "github.com/traefik/traefik/v2/pkg/middlewares/tcp/ipallowlist" + "github.com/traefik/traefik/v2/pkg/middlewares/tcp/inflightconn" + "github.com/traefik/traefik/v2/pkg/middlewares/tcp/ipallowlist" "github.com/traefik/traefik/v2/pkg/server/provider" "github.com/traefik/traefik/v2/pkg/tcp" ) From 7582da96506689b9516adae046902f78926c2e61 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 26 Oct 2022 18:22:05 +0200 Subject: [PATCH 07/13] Update Yaegi to v0.14.3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 94e122e67..15344fbe1 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/stretchr/testify v1.8.0 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 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 diff --git a/go.sum b/go.sum index 568b63f0b..edfe2cc70 100644 --- a/go.sum +++ b/go.sum @@ -1956,8 +1956,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= From a041a6b198daa63750cf02b33114e7a902d19589 Mon Sep 17 00:00:00 2001 From: Romain Date: Thu, 27 Oct 2022 16:08:06 +0200 Subject: [PATCH 08/13] Handle capture on redefined http.responseWriters Co-authored-by: Mathieu Lonjaret --- cmd/traefik/traefik.go | 11 +-- ...simple_metrics_with_buffer_middleware.toml | 66 +++++++++++++++ integration/simple_test.go | 80 ++++++++++++++++++- pkg/middlewares/accesslog/logger.go | 16 ++-- pkg/middlewares/accesslog/logger_test.go | 6 +- pkg/middlewares/capture/capture.go | 79 +++++++++--------- pkg/middlewares/capture/capture_test.go | 6 +- pkg/middlewares/metrics/metrics.go | 21 +++-- pkg/server/middleware/chainbuilder.go | 8 +- pkg/server/router/router_test.go | 12 +-- pkg/server/routerfactory_test.go | 6 +- 11 files changed, 228 insertions(+), 83 deletions(-) create mode 100644 integration/fixtures/simple_metrics_with_buffer_middleware.toml diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index f54477375..125025251 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -31,7 +31,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" @@ -260,9 +259,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 @@ -565,13 +563,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) diff --git a/integration/fixtures/simple_metrics_with_buffer_middleware.toml b/integration/fixtures/simple_metrics_with_buffer_middleware.toml new file mode 100644 index 000000000..2781c1981 --- /dev/null +++ b/integration/fixtures/simple_metrics_with_buffer_middleware.toml @@ -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 }}" diff --git a/integration/simple_test.go b/integration/simple_test.go index c939d8f0a..28e5291fd 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -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") diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index 841728582..cf729c6de 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -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() diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index b7b3b565e..80bda1530 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -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) diff --git a/pkg/middlewares/capture/capture.go b/pkg/middlewares/capture/capture.go index 6ee0ed399..845c2e983 100644 --- a/pkg/middlewares/capture/capture.go +++ b/pkg/middlewares/capture/capture.go @@ -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 } diff --git a/pkg/middlewares/capture/capture_test.go b/pkg/middlewares/capture/capture_test.go index f6718ad6e..9d8f91fcf 100644 --- a/pkg/middlewares/capture/capture_test.go +++ b/pkg/middlewares/capture/capture_test.go @@ -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) diff --git a/pkg/middlewares/metrics/metrics.go b/pkg/middlewares/metrics/metrics.go index 08d0185cc..48ff699c7 100644 --- a/pkg/middlewares/metrics/metrics.go +++ b/pkg/middlewares/metrics/metrics.go @@ -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) diff --git a/pkg/server/middleware/chainbuilder.go b/pkg/server/middleware/chainbuilder.go index b4e1223a8..c5c833606 100644 --- a/pkg/server/middleware/chainbuilder.go +++ b/pkg/server/middleware/chainbuilder.go @@ -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 { diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 867da0a9b..26e8a68a3 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -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()) diff --git a/pkg/server/routerfactory_test.go b/pkg/server/routerfactory_test.go index 0c6697e38..fdf4efc69 100644 --- a/pkg/server/routerfactory_test.go +++ b/pkg/server/routerfactory_test.go @@ -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})) From 15f747209130b9db440aea90f815a2aa85e68b6d Mon Sep 17 00:00:00 2001 From: Kevin Pollet Date: Thu, 27 Oct 2022 16:53:16 +0200 Subject: [PATCH 09/13] Prepare release v2.9.2 --- CHANGELOG.md | 16 ++++++++++++++++ script/gcg/traefik-bugfix.toml | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c14767d8..f4baf1d31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## [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) + +**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.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) diff --git a/script/gcg/traefik-bugfix.toml b/script/gcg/traefik-bugfix.toml index 46502fc32..228224a30 100644 --- a/script/gcg/traefik-bugfix.toml +++ b/script/gcg/traefik-bugfix.toml @@ -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 From bd3eaf4f5e6a313443db775f7a9cc14bc7cb35a7 Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Thu, 27 Oct 2022 17:34:06 +0200 Subject: [PATCH 10/13] Add GrpcWeb middleware Co-authored-by: Kevin Pollet --- docs/content/middlewares/http/grpcweb.md | 77 +++++++++++++++++++ .../dynamic-configuration/docker-labels.yml | 1 + .../reference/dynamic-configuration/file.toml | 3 + .../reference/dynamic-configuration/file.yaml | 5 ++ .../kubernetes-crd-definition-v1.yml | 11 +++ .../reference/dynamic-configuration/kv-ref.md | 2 + .../marathon-labels.json | 1 + .../traefik.containo.us_middlewares.yaml | 11 +++ docs/mkdocs.yml | 1 + go.mod | 6 +- go.sum | 42 +++++++++- integration/fixtures/k8s/01-traefik-crd.yml | 11 +++ pkg/config/dynamic/middlewares.go | 11 +++ pkg/config/dynamic/zz_generated.deepcopy.go | 26 +++++++ pkg/middlewares/grpcweb/grpcweb.go | 27 +++++++ pkg/provider/kubernetes/crd/kubernetes.go | 1 + .../crd/traefik/v1alpha1/middleware.go | 1 + .../traefik/v1alpha1/zz_generated.deepcopy.go | 5 ++ pkg/server/middleware/middlewares.go | 11 +++ 19 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 docs/content/middlewares/http/grpcweb.md create mode 100644 pkg/middlewares/grpcweb/grpcweb.go diff --git a/docs/content/middlewares/http/grpcweb.md b/docs/content/middlewares/http/grpcweb.md new file mode 100644 index 000000000..d6d2c9a6b --- /dev/null +++ b/docs/content/middlewares/http/grpcweb.md @@ -0,0 +1,77 @@ +--- +title: "Traefik GrpcWeb Documentation" +description: "In Traefik Proxy's HTTP middleware, GrpcWeb converts a gRPC Web requests to HTTP/2 gRPC requests. Read the technical documentation." +--- + +# GrpcWeb + +Converting gRPC Web requests to HTTP/2 gRPC requests. +{: .subtitle } + +The GrpcWeb middleware converts gRPC Web requests to HTTP/2 gRPC requests before forwarding them to the backends. + +!!! tip + + Please note, that Traefik needs to communicate using gRPC with the backends (h2c or HTTP/2 over TLS). + Check out the [gRPC](../../user-guides/grpc.md) user guide for more details. + +## Configuration Examples + +```yaml tab="Docker" +labels: + - "traefik.http.middlewares.test-grpcweb.grpcweb.allowOrigins=*" +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.containo.us/v1alpha1 +kind: Middleware +metadata: + name: test-grpcweb +spec: + grpcWeb: + allowOrigins: + - "*" +``` + +```yaml tab="Consul Catalog" +- "traefik.http.middlewares.test-grpcweb.grpcWeb.allowOrigins=*" +``` + +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.test-grpcweb.grpcweb.alloworigins": "*" +} +``` + +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.test-grpcweb.grpcweb.alloworigins=*" +``` + +```yaml tab="File (YAML)" +http: + middlewares: + test-grpcweb: + grpcWeb: + allowOrigins: + - "*" +``` + +```toml tab="File (TOML)" +[http.middlewares] + [http.middlewares.test-grpcweb.grpcWeb] + allowOrigins = ["*"] +``` + +## Configuration Options + +### `allowOrigins` + +The `allowOrigins` contains the list of allowed origins. +A wildcard origin `*` can also be configured to match all requests. + +More information including how to use the settings can be found at: + +- [Mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) +- [w3](https://fetch.spec.whatwg.org/#http-access-control-allow-origin) +- [IETF](https://tools.ietf.org/html/rfc6454#section-7.1) diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 6d79e63fe..3c91d6f7d 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -121,6 +121,7 @@ - "traefik.http.middlewares.middleware21.stripprefix.forceslash=true" - "traefik.http.middlewares.middleware21.stripprefix.prefixes=foobar, foobar" - "traefik.http.middlewares.middleware22.stripprefixregex.regex=foobar, foobar" +- "traefik.http.middlewares.middleware23.grpcweb.alloworigins=foobar, foobar" - "traefik.http.routers.router0.entrypoints=foobar, foobar" - "traefik.http.routers.router0.middlewares=foobar, foobar" - "traefik.http.routers.router0.priority=42" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 6c540e5f6..649431ffc 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -285,6 +285,9 @@ [http.middlewares.Middleware22] [http.middlewares.Middleware22.stripPrefixRegex] regex = ["foobar", "foobar"] + [http.middlewares.Middleware23] + [http.middlewares.Middleware23.grpcWeb] + allowOrigins = ["foobar", "foobar"] [http.serversTransports] [http.serversTransports.ServersTransport0] serverName = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index c86e4d167..7b153bb28 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -324,6 +324,11 @@ http: regex: - foobar - foobar + Middleware23: + grpcWeb: + allowOrigins: + - foobar + - foobar serversTransports: ServersTransport0: serverName: foobar diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index d54fa537d..d7ca0f5da 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -967,6 +967,17 @@ spec: forward) all X-Forwarded-* headers.' type: boolean type: object + grpcWeb: + description: GrpcWeb holds the gRPC web middleware configuration. + This middleware converts a gRPC web request to an HTTP/2 gRPC request. + properties: + allowOrigins: + description: AllowOrigins is a list of allowable origins. Can + also be a wildcard origin "*". + items: + type: string + type: array + type: object headers: description: 'Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. More diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index e65b2ec66..5457dfafa 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -140,6 +140,8 @@ | `traefik/http/middlewares/Middleware21/stripPrefix/prefixes/1` | `foobar` | | `traefik/http/middlewares/Middleware22/stripPrefixRegex/regex/0` | `foobar` | | `traefik/http/middlewares/Middleware22/stripPrefixRegex/regex/1` | `foobar` | +| `traefik/http/middlewares/Middleware23/grpcWeb/allowOrigins/0` | `foobar` | +| `traefik/http/middlewares/Middleware23/grpcWeb/allowOrigins/1` | `foobar` | | `traefik/http/routers/Router0/entryPoints/0` | `foobar` | | `traefik/http/routers/Router0/entryPoints/1` | `foobar` | | `traefik/http/routers/Router0/middlewares/0` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json index fdcc74cb3..fbadce690 100644 --- a/docs/content/reference/dynamic-configuration/marathon-labels.json +++ b/docs/content/reference/dynamic-configuration/marathon-labels.json @@ -121,6 +121,7 @@ "traefik.http.middlewares.middleware21.stripprefix.forceslash": "true", "traefik.http.middlewares.middleware21.stripprefix.prefixes": "foobar, foobar", "traefik.http.middlewares.middleware22.stripprefixregex.regex": "foobar, foobar", +"traefik.http.middlewares.middleware23.grpcweb.alloworigins": "foobar, foobar", "traefik.http.routers.router0.entrypoints": "foobar, foobar", "traefik.http.routers.router0.middlewares": "foobar, foobar", "traefik.http.routers.router0.priority": "42", diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml index 85e80aae3..1fe2e7f63 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml @@ -390,6 +390,17 @@ spec: forward) all X-Forwarded-* headers.' type: boolean type: object + grpcWeb: + description: GrpcWeb holds the gRPC web middleware configuration. + This middleware converts a gRPC web request to an HTTP/2 gRPC request. + properties: + allowOrigins: + description: AllowOrigins is a list of allowable origins. Can + also be a wildcard origin "*". + items: + type: string + type: array + type: object headers: description: 'Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. More diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 2fb3d20be..8090daa7a 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -125,6 +125,7 @@ nav: - 'DigestAuth': 'middlewares/http/digestauth.md' - 'Errors': 'middlewares/http/errorpages.md' - 'ForwardAuth': 'middlewares/http/forwardauth.md' + - 'GrpcWeb': 'middlewares/http/grpcweb.md' - 'Headers': 'middlewares/http/headers.md' - 'IpAllowList': 'middlewares/http/ipallowlist.md' - 'InFlightReq': 'middlewares/http/inflightreq.md' diff --git a/go.mod b/go.mod index 66072bcc9..c03db4e05 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f + github.com/improbable-eng/grpc-web v0.15.0 github.com/influxdata/influxdb-client-go/v2 v2.7.0 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d github.com/instana/go-sensor v1.38.3 @@ -142,6 +143,7 @@ require ( github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/cpu/goacmedns v0.1.1 // indirect github.com/deepmap/oapi-codegen v1.9.1 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dimchansky/utfbom v1.1.1 // indirect @@ -189,7 +191,7 @@ require ( github.com/gophercloud/gophercloud v1.0.0 // indirect github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/hashicorp/consul/sdk v0.10.0 // indirect github.com/hashicorp/cronexpr v1.1.1 // indirect @@ -280,6 +282,7 @@ require ( github.com/pquerna/otp v1.3.0 // indirect github.com/prometheus/common v0.35.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rs/cors v1.7.0 // indirect github.com/sacloud/api-client-go v0.2.1 // indirect github.com/sacloud/go-http v0.1.2 // indirect github.com/sacloud/iaas-api-go v1.3.2 // indirect @@ -343,6 +346,7 @@ require ( howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect k8s.io/klog/v2 v2.10.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect + nhooyr.io/websocket v1.8.6 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index 1c9a71bdd..8bf297467 100644 --- a/go.sum +++ b/go.sum @@ -522,6 +522,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xb github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -681,8 +683,11 @@ github.com/getsentry/raven-go v0.0.0-20180121060056-563b81fc02b7/go.mod h1:KungG github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -700,6 +705,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea h1:CnEQOUv4ilElSwFB9g/lVmz206oLE4aNZDYngIY1Gvg= github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -735,11 +741,15 @@ github.com/go-pg/pg/v10 v10.0.0/go.mod h1:XHU1AkQW534GFuUdSiQ46+Xw6Ah+9+b8DlT4Yw github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v7 v7.1.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v8 v8.0.0/go.mod h1:isLoQT/NFSP7V67lyvM9GmdvLdyZ7pEhsXvvyQtnQTo= @@ -788,6 +798,12 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gocql/gocql v0.0.0-20220224095938-0eacd3183625/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -962,6 +978,7 @@ github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -973,8 +990,9 @@ github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:Fecb github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -1128,6 +1146,8 @@ github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb-client-go/v2 v2.7.0 h1:QgP5mlBE9sGnzplpnf96pr+p7uqlIlL4W2GAP3n+XZg= @@ -1278,7 +1298,9 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/kisom/goutils v1.1.0/go.mod h1:+UBTfd78habUYWFbNWTJNG+jNG/i/lGURakr4A/yNRw= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= @@ -1328,6 +1350,7 @@ github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= @@ -1522,7 +1545,9 @@ github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= @@ -1730,6 +1755,7 @@ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.35.0 h1:Eyr+Pw2VymWejHqCugNaQXkAi6KayVNxaHeu6khmFBE= @@ -1748,6 +1774,7 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= @@ -1769,6 +1796,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= @@ -1982,9 +2011,11 @@ github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/ github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/unrolled/render v1.0.2 h1:dGS3EmChQP3yOi1YeFNO/Dx+MbWZhdvhQJTXochM5bs= github.com/unrolled/render v1.0.2/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= @@ -2208,6 +2239,7 @@ 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-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20200901203048-c4f52b2c50aa/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925 h1:5XVKs2rlCg8EFyRcvO8/XFwYxh1oKJO1Q3X5vttIf9c= golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925/go.mod h1:1phAWC201xIgDyaFpmDeZkgf70Q4Pd/CNqfRtVPtxNw= @@ -2280,6 +2312,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -2307,6 +2340,7 @@ golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -2420,6 +2454,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2470,6 +2505,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2676,6 +2712,7 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -2695,6 +2732,7 @@ google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -2939,6 +2977,8 @@ launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80 mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA= mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index d54fa537d..d7ca0f5da 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -967,6 +967,17 @@ spec: forward) all X-Forwarded-* headers.' type: boolean type: object + grpcWeb: + description: GrpcWeb holds the gRPC web middleware configuration. + This middleware converts a gRPC web request to an HTTP/2 gRPC request. + properties: + allowOrigins: + description: AllowOrigins is a list of allowable origins. Can + also be a wildcard origin "*". + items: + type: string + type: array + type: object headers: description: 'Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. More diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index 25eab40fa..ea482a9eb 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -34,12 +34,23 @@ type Middleware struct { PassTLSClientCert *PassTLSClientCert `json:"passTLSClientCert,omitempty" toml:"passTLSClientCert,omitempty" yaml:"passTLSClientCert,omitempty" export:"true"` Retry *Retry `json:"retry,omitempty" toml:"retry,omitempty" yaml:"retry,omitempty" export:"true"` ContentType *ContentType `json:"contentType,omitempty" toml:"contentType,omitempty" yaml:"contentType,omitempty" export:"true"` + GrpcWeb *GrpcWeb `json:"grpcWeb,omitempty" toml:"grpcWeb,omitempty" yaml:"grpcWeb,omitempty" export:"true"` Plugin map[string]PluginConf `json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty" export:"true"` } // +k8s:deepcopy-gen=true +// GrpcWeb holds the gRPC web middleware configuration. +// This middleware converts a gRPC web request to an HTTP/2 gRPC request. +type GrpcWeb struct { + // AllowOrigins is a list of allowable origins. + // Can also be a wildcard origin "*". + AllowOrigins []string `json:"allowOrigins,omitempty" toml:"allowOrigins,omitempty" yaml:"allowOrigins,omitempty"` +} + +// +k8s:deepcopy-gen=true + // ContentType holds the content-type middleware configuration. // This middleware exists to enable the correct behavior until at least the default one can be changed in a future version. type ContentType struct { diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index 241088ea0..1abb4727e 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -353,6 +353,27 @@ func (in *ForwardingTimeouts) DeepCopy() *ForwardingTimeouts { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GrpcWeb) DeepCopyInto(out *GrpcWeb) { + *out = *in + if in.AllowOrigins != nil { + in, out := &in.AllowOrigins, &out.AllowOrigins + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrpcWeb. +func (in *GrpcWeb) DeepCopy() *GrpcWeb { + if in == nil { + return nil + } + out := new(GrpcWeb) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPConfiguration) DeepCopyInto(out *HTTPConfiguration) { *out = *in @@ -734,6 +755,11 @@ func (in *Middleware) DeepCopyInto(out *Middleware) { *out = new(ContentType) **out = **in } + if in.GrpcWeb != nil { + in, out := &in.GrpcWeb, &out.GrpcWeb + *out = new(GrpcWeb) + (*in).DeepCopyInto(*out) + } if in.Plugin != nil { in, out := &in.Plugin, &out.Plugin *out = make(map[string]PluginConf, len(*in)) diff --git a/pkg/middlewares/grpcweb/grpcweb.go b/pkg/middlewares/grpcweb/grpcweb.go new file mode 100644 index 000000000..b5dffe6f0 --- /dev/null +++ b/pkg/middlewares/grpcweb/grpcweb.go @@ -0,0 +1,27 @@ +package grpcweb + +import ( + "context" + "net/http" + + "github.com/improbable-eng/grpc-web/go/grpcweb" + "github.com/traefik/traefik/v2/pkg/config/dynamic" + "github.com/traefik/traefik/v2/pkg/log" + "github.com/traefik/traefik/v2/pkg/middlewares" +) + +const typeName = "grpc-web" + +// New builds a new gRPC web request converter. +func New(ctx context.Context, next http.Handler, config dynamic.GrpcWeb, name string) http.Handler { + log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") + + return grpcweb.WrapHandler(next, grpcweb.WithCorsForRegisteredEndpointsOnly(false), grpcweb.WithOriginFunc(func(origin string) bool { + for _, originCfg := range config.AllowOrigins { + if originCfg == "*" || originCfg == origin { + return true + } + } + return false + })) +} diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index bc0368179..31366ba7c 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -279,6 +279,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) PassTLSClientCert: middleware.Spec.PassTLSClientCert, Retry: retry, ContentType: middleware.Spec.ContentType, + GrpcWeb: middleware.Spec.GrpcWeb, Plugin: plugin, } } diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go index 5483b2bdd..bde544b85 100644 --- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go +++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go @@ -48,6 +48,7 @@ type MiddlewareSpec struct { PassTLSClientCert *dynamic.PassTLSClientCert `json:"passTLSClientCert,omitempty"` Retry *Retry `json:"retry,omitempty"` ContentType *dynamic.ContentType `json:"contentType,omitempty"` + GrpcWeb *dynamic.GrpcWeb `json:"grpcWeb,omitempty"` // Plugin defines the middleware plugin configuration. // More info: https://doc.traefik.io/traefik/plugins/ Plugin map[string]apiextensionv1.JSON `json:"plugin,omitempty"` diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go index 46d3510cd..45ffd09fd 100644 --- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go @@ -769,6 +769,11 @@ func (in *MiddlewareSpec) DeepCopyInto(out *MiddlewareSpec) { *out = new(dynamic.ContentType) **out = **in } + if in.GrpcWeb != nil { + in, out := &in.GrpcWeb, &out.GrpcWeb + *out = new(dynamic.GrpcWeb) + (*in).DeepCopyInto(*out) + } if in.Plugin != nil { in, out := &in.Plugin, &out.Plugin *out = make(map[string]v1.JSON, len(*in)) diff --git a/pkg/server/middleware/middlewares.go b/pkg/server/middleware/middlewares.go index de1571005..18c1f25ba 100644 --- a/pkg/server/middleware/middlewares.go +++ b/pkg/server/middleware/middlewares.go @@ -17,6 +17,7 @@ import ( "github.com/traefik/traefik/v2/pkg/middlewares/circuitbreaker" "github.com/traefik/traefik/v2/pkg/middlewares/compress" "github.com/traefik/traefik/v2/pkg/middlewares/customerrors" + "github.com/traefik/traefik/v2/pkg/middlewares/grpcweb" "github.com/traefik/traefik/v2/pkg/middlewares/headers" "github.com/traefik/traefik/v2/pkg/middlewares/inflightreq" "github.com/traefik/traefik/v2/pkg/middlewares/ipallowlist" @@ -219,6 +220,16 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) ( } } + // GrpcWeb + if config.GrpcWeb != nil { + if middleware != nil { + return nil, badConf + } + middleware = func(next http.Handler) (http.Handler, error) { + return grpcweb.New(ctx, next, *config.GrpcWeb, middlewareName), nil + } + } + // Headers if config.Headers != nil { if middleware != nil { From 725804840371c77cd2648d2f3661c4039200bb7b Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Thu, 27 Oct 2022 17:50:48 +0200 Subject: [PATCH 11/13] Prepare release v2.9.3 --- .goreleaser.yml | 11 ++++++----- CHANGELOG.md | 9 +++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 27bc16cd2..9194b4a1e 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index f4baf1d31..2b0fa7934 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -## [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) +## [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) **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)) @@ -14,6 +14,11 @@ - 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.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) From 454f552691015d5f01041f61ae0cca0cff5d5d5d Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Thu, 27 Oct 2022 20:40:05 +0200 Subject: [PATCH 12/13] Prepare release v2.9.4 --- CHANGELOG.md | 9 +++++++-- Makefile | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b0fa7934..03cf18a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -## [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) +## [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)) @@ -14,6 +14,11 @@ - 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) diff --git a/Makefile b/Makefile index c04dc40e7..0c957b003 100644 --- a/Makefile +++ b/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 \ From d131ef57dafdbe0f3f5b4d7b18bc4711c1cb5000 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 3 Nov 2022 16:30:08 +0100 Subject: [PATCH 13/13] chore: update nhooyr.io/websocket --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index c13df8bc5..ffff93cbf 100644 --- a/go.mod +++ b/go.mod @@ -346,7 +346,7 @@ require ( howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect k8s.io/klog/v2 v2.10.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect - nhooyr.io/websocket v1.8.6 // indirect + nhooyr.io/websocket v1.8.7 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index 8573beef9..38e01b047 100644 --- a/go.sum +++ b/go.sum @@ -2977,8 +2977,9 @@ launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80 mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA= mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E= -nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=