Merge branch v2.4 into master

This commit is contained in:
Michael 2021-01-06 18:59:45 +01:00
commit 0509b6fdb9
No known key found for this signature in database
GPG key ID: 71EDE16780F920E8
29 changed files with 317 additions and 17 deletions

View file

@ -57,7 +57,10 @@
"nlreturn", # Not relevant "nlreturn", # Not relevant
"wrapcheck", # Too strict "wrapcheck", # Too strict
"tparallel", # Not relevant "tparallel", # Not relevant
"paralleltest", # Not relevant
"exhaustivestruct", # Not relevant "exhaustivestruct", # Not relevant
"makezero", # not relevant
"forbidigo", # not relevant
] ]
[issues] [issues]

View file

@ -1,3 +1,15 @@
## [v2.3.6](https://github.com/traefik/traefik/tree/v2.3.6) (2020-12-17)
[All Commits](https://github.com/traefik/traefik/compare/v2.3.5...v2.3.6)
**Bug fixes:**
- **[logs]** Update Logrus to v1.7.0 ([#7663](https://github.com/traefik/traefik/pull/7663) by [jspdown](https://github.com/jspdown))
- **[plugins]** Update Yaegi to v0.9.8 ([#7659](https://github.com/traefik/traefik/pull/7659) by [ldez](https://github.com/ldez))
- **[rules]** Disable router when a rule has an error ([#7680](https://github.com/traefik/traefik/pull/7680) by [ldez](https://github.com/ldez))
**Documentation:**
- **[logs]** Add configuration example for access log filePath ([#7655](https://github.com/traefik/traefik/pull/7655) by [wernerfred](https://github.com/wernerfred))
- **[middleware]** Add missing quotes in errorpages k8s example yaml ([#7675](https://github.com/traefik/traefik/pull/7675) by [icelynjennings](https://github.com/icelynjennings))
## [v2.4.0-rc1](https://github.com/traefik/traefik/tree/v2.4.0-rc1) (2020-12-16) ## [v2.4.0-rc1](https://github.com/traefik/traefik/tree/v2.4.0-rc1) (2020-12-16)
[All Commits](https://github.com/traefik/traefik/compare/v2.3.0-rc1...v2.4.0-rc1) [All Commits](https://github.com/traefik/traefik/compare/v2.3.0-rc1...v2.4.0-rc1)

View file

@ -19,7 +19,7 @@ RUN mkdir -p /usr/local/bin \
&& chmod +x /usr/local/bin/go-bindata && chmod +x /usr/local/bin/go-bindata
# Download golangci-lint binary to bin folder in $GOPATH # Download golangci-lint binary to bin folder in $GOPATH
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.32.2 RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.34.0
# Download misspell binary to bin folder in $GOPATH # Download misspell binary to bin folder in $GOPATH
RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4 RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4

View file

@ -285,7 +285,7 @@ http:
### `authRequestHeaders` ### `authRequestHeaders`
The `authRequestHeaders` option is the list of the headers to copy from the request to the authentication server. The `authRequestHeaders` option is the list of the headers to copy from the request to the authentication server.
It allows to prevent passing headers that have not to be passed to the authentication server. It allows filtering headers that should not be passed to the authentication server.
If not set or empty then all request headers will be passed. If not set or empty then all request headers will be passed.
```yaml tab="Docker" ```yaml tab="Docker"

View file

@ -188,7 +188,7 @@ While in Swarm Mode, Traefik uses labels found on services, not on individual co
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the Therefore, if you use a compose file with Swarm Mode, labels should be defined in the
[`deploy`](https://docs.docker.com/compose/compose-file/compose-file-v3/#labels-1) part of your service. [`deploy`](https://docs.docker.com/compose/compose-file/compose-file-v3/#labels-1) part of your service.
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file)). This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/compose-file-v3/)).
### Port Detection ### Port Detection

View file

@ -131,13 +131,14 @@ Below is the list of the currently supported providers in Traefik.
| [Docker](./docker.md) | Orchestrator | Label | | [Docker](./docker.md) | Orchestrator | Label |
| [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource or Ingress | | [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource or Ingress |
| [Consul Catalog](./consul-catalog.md) | Orchestrator | Label | | [Consul Catalog](./consul-catalog.md) | Orchestrator | Label |
| [ECS](./ecs.md) | Orchestrator | Label |
| [Marathon](./marathon.md) | Orchestrator | Label | | [Marathon](./marathon.md) | Orchestrator | Label |
| [Rancher](./rancher.md) | Orchestrator | Label | | [Rancher](./rancher.md) | Orchestrator | Label |
| [File](./file.md) | Manual | TOML/YAML format | | [File](./file.md) | Manual | TOML/YAML format |
| [Consul](./consul.md) | KV | KV | | [Consul](./consul.md) | KV | KV |
| [Etcd](./etcd.md) | KV | KV | | [Etcd](./etcd.md) | KV | KV |
| [Redis](./redis.md) | KV | KV |
| [ZooKeeper](./zookeeper.md) | KV | KV | | [ZooKeeper](./zookeeper.md) | KV | KV |
| [Redis](./redis.md) | KV | KV |
| [HTTP](./http.md) | Manual | JSON format | | [HTTP](./http.md) | Manual | JSON format |
!!! info "More Providers" !!! info "More Providers"

View file

@ -300,6 +300,8 @@ func findPilotMetric(name string, metrics []PilotMetric) *PilotMetric {
} }
func buildPilotCounterAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) { func buildPilotCounterAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) {
t.Helper()
return func(metric *PilotMetric) { return func(metric *PilotMetric) {
for _, value := range metric.Observations { for _, value := range metric.Observations {
if cv := value.(float64); cv != expectedValue { if cv := value.(float64); cv != expectedValue {
@ -311,6 +313,8 @@ func buildPilotCounterAssert(t *testing.T, metricName string, expectedValue floa
} }
func buildPilotGreaterThanCounterAssert(t *testing.T, metricName string, expectedMinValue float64) func(metric *PilotMetric) { func buildPilotGreaterThanCounterAssert(t *testing.T, metricName string, expectedMinValue float64) func(metric *PilotMetric) {
t.Helper()
return func(metric *PilotMetric) { return func(metric *PilotMetric) {
for _, value := range metric.Observations { for _, value := range metric.Observations {
if cv := value.(float64); cv < expectedMinValue { if cv := value.(float64); cv < expectedMinValue {
@ -322,6 +326,8 @@ func buildPilotGreaterThanCounterAssert(t *testing.T, metricName string, expecte
} }
func buildPilotHistogramAssert(t *testing.T, metricName string, expectedSampleCount float64) func(metric *PilotMetric) { func buildPilotHistogramAssert(t *testing.T, metricName string, expectedSampleCount float64) func(metric *PilotMetric) {
t.Helper()
return func(metric *PilotMetric) { return func(metric *PilotMetric) {
for _, value := range metric.Observations { for _, value := range metric.Observations {
if pho := value.(*pilotHistogramObservation); pho.Count != expectedSampleCount { if pho := value.(*pilotHistogramObservation); pho.Count != expectedSampleCount {
@ -333,6 +339,8 @@ func buildPilotHistogramAssert(t *testing.T, metricName string, expectedSampleCo
} }
func buildPilotGaugeAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) { func buildPilotGaugeAssert(t *testing.T, metricName string, expectedValue float64) func(metric *PilotMetric) {
t.Helper()
return func(metric *PilotMetric) { return func(metric *PilotMetric) {
for _, value := range metric.Observations { for _, value := range metric.Observations {
if gv := value.(float64); gv != expectedValue { if gv := value.(float64); gv != expectedValue {
@ -344,6 +352,8 @@ func buildPilotGaugeAssert(t *testing.T, metricName string, expectedValue float6
} }
func buildPilotTimestampAssert(t *testing.T, metricName string) func(metric *PilotMetric) { func buildPilotTimestampAssert(t *testing.T, metricName string) func(metric *PilotMetric) {
t.Helper()
return func(metric *PilotMetric) { return func(metric *PilotMetric) {
for _, value := range metric.Observations { for _, value := range metric.Observations {
if ts := time.Unix(int64(value.(float64)), 0); time.Since(ts) > time.Minute { if ts := time.Unix(int64(value.(float64)), 0); time.Since(ts) > time.Minute {

View file

@ -486,6 +486,8 @@ func assertCounterValue(t *testing.T, want float64, family *dto.MetricFamily, la
} }
func buildCounterAssert(t *testing.T, metricName string, expectedValue int) func(family *dto.MetricFamily) { func buildCounterAssert(t *testing.T, metricName string, expectedValue int) func(family *dto.MetricFamily) {
t.Helper()
return func(family *dto.MetricFamily) { return func(family *dto.MetricFamily) {
if cv := int(family.Metric[0].Counter.GetValue()); cv != expectedValue { if cv := int(family.Metric[0].Counter.GetValue()); cv != expectedValue {
t.Errorf("metric %s has value %d, want %d", metricName, cv, expectedValue) t.Errorf("metric %s has value %d, want %d", metricName, cv, expectedValue)
@ -494,6 +496,8 @@ func buildCounterAssert(t *testing.T, metricName string, expectedValue int) func
} }
func buildGreaterThanCounterAssert(t *testing.T, metricName string, expectedMinValue int) func(family *dto.MetricFamily) { func buildGreaterThanCounterAssert(t *testing.T, metricName string, expectedMinValue int) func(family *dto.MetricFamily) {
t.Helper()
return func(family *dto.MetricFamily) { return func(family *dto.MetricFamily) {
if cv := int(family.Metric[0].Counter.GetValue()); cv < expectedMinValue { if cv := int(family.Metric[0].Counter.GetValue()); cv < expectedMinValue {
t.Errorf("metric %s has value %d, want at least %d", metricName, cv, expectedMinValue) t.Errorf("metric %s has value %d, want at least %d", metricName, cv, expectedMinValue)
@ -502,6 +506,8 @@ func buildGreaterThanCounterAssert(t *testing.T, metricName string, expectedMinV
} }
func buildHistogramAssert(t *testing.T, metricName string, expectedSampleCount int) func(family *dto.MetricFamily) { func buildHistogramAssert(t *testing.T, metricName string, expectedSampleCount int) func(family *dto.MetricFamily) {
t.Helper()
return func(family *dto.MetricFamily) { return func(family *dto.MetricFamily) {
if sc := int(family.Metric[0].Histogram.GetSampleCount()); sc != expectedSampleCount { if sc := int(family.Metric[0].Histogram.GetSampleCount()); sc != expectedSampleCount {
t.Errorf("metric %s has sample count value %d, want %d", metricName, sc, expectedSampleCount) t.Errorf("metric %s has sample count value %d, want %d", metricName, sc, expectedSampleCount)
@ -510,6 +516,8 @@ func buildHistogramAssert(t *testing.T, metricName string, expectedSampleCount i
} }
func buildGaugeAssert(t *testing.T, metricName string, expectedValue int) func(family *dto.MetricFamily) { func buildGaugeAssert(t *testing.T, metricName string, expectedValue int) func(family *dto.MetricFamily) {
t.Helper()
return func(family *dto.MetricFamily) { return func(family *dto.MetricFamily) {
if gv := int(family.Metric[0].Gauge.GetValue()); gv != expectedValue { if gv := int(family.Metric[0].Gauge.GetValue()); gv != expectedValue {
t.Errorf("metric %s has value %d, want %d", metricName, gv, expectedValue) t.Errorf("metric %s has value %d, want %d", metricName, gv, expectedValue)
@ -518,6 +526,8 @@ func buildGaugeAssert(t *testing.T, metricName string, expectedValue int) func(f
} }
func buildTimestampAssert(t *testing.T, metricName string) func(family *dto.MetricFamily) { func buildTimestampAssert(t *testing.T, metricName string) func(family *dto.MetricFamily) {
t.Helper()
return func(family *dto.MetricFamily) { return func(family *dto.MetricFamily) {
if ts := time.Unix(int64(family.Metric[0].Gauge.GetValue()), 0); time.Since(ts) > time.Minute { if ts := time.Unix(int64(family.Metric[0].Gauge.GetValue()), 0); time.Since(ts) > time.Minute {
t.Errorf("metric %s has wrong timestamp %v", metricName, ts) t.Errorf("metric %s has wrong timestamp %v", metricName, ts)

View file

@ -683,6 +683,8 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
} }
func assertValidLogData(t *testing.T, expected string, logData []byte) { func assertValidLogData(t *testing.T, expected string, logData []byte) {
t.Helper()
if len(expected) == 0 { if len(expected) == 0 {
assert.Zero(t, len(logData)) assert.Zero(t, len(logData))
t.Log(string(logData)) t.Log(string(logData))
@ -716,6 +718,8 @@ func assertValidLogData(t *testing.T, expected string, logData []byte) {
} }
func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) { func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) {
t.Helper()
file, err := ioutil.TempFile("", "testlogger") file, err := ioutil.TempFile("", "testlogger")
require.NoError(t, err, "failed to create temp file") require.NoError(t, err, "failed to create temp file")
@ -731,6 +735,8 @@ func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) {
} }
func createTempDir(t *testing.T, prefix string) string { func createTempDir(t *testing.T, prefix string) string {
t.Helper()
tmpDir, err := ioutil.TempDir("", prefix) tmpDir, err := ioutil.TempDir("", prefix)
require.NoError(t, err, "failed to create temp dir") require.NoError(t, err, "failed to create temp dir")
@ -740,6 +746,8 @@ func createTempDir(t *testing.T, prefix string) string {
} }
func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS bool) { func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS bool) {
t.Helper()
logger, err := NewHandler(config) logger, err := NewHandler(config)
require.NoError(t, err) require.NoError(t, err)
defer logger.Close() defer logger.Close()
@ -771,10 +779,14 @@ func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS bool) {
} }
func doLoggingTLS(t *testing.T, config *types.AccessLog) { func doLoggingTLS(t *testing.T, config *types.AccessLog) {
t.Helper()
doLoggingTLSOpt(t, config, true) doLoggingTLSOpt(t, config, true)
} }
func doLogging(t *testing.T, config *types.AccessLog) { func doLogging(t *testing.T, config *types.AccessLog) {
t.Helper()
doLoggingTLSOpt(t, config, false) doLoggingTLSOpt(t, config, false)
} }

View file

@ -76,8 +76,7 @@ func TestErrorWhenEmptyConfig(t *testing.T) {
func TestProvideWithoutWatch(t *testing.T) { func TestProvideWithoutWatch(t *testing.T) {
for _, test := range getTestCases() { for _, test := range getTestCases() {
t.Run(test.desc+" without watch", func(t *testing.T) { t.Run(test.desc+" without watch", func(t *testing.T) {
provider, clean := createProvider(t, test, false) provider := createProvider(t, test, false)
defer clean()
configChan := make(chan dynamic.Message) configChan := make(chan dynamic.Message)
provider.DebugLogGeneratedTemplate = true provider.DebugLogGeneratedTemplate = true
@ -109,8 +108,7 @@ func TestProvideWithoutWatch(t *testing.T) {
func TestProvideWithWatch(t *testing.T) { func TestProvideWithWatch(t *testing.T) {
for _, test := range getTestCases() { for _, test := range getTestCases() {
t.Run(test.desc+" with watch", func(t *testing.T) { t.Run(test.desc+" with watch", func(t *testing.T) {
provider, clean := createProvider(t, test, true) provider := createProvider(t, test, true)
defer clean()
configChan := make(chan dynamic.Message) configChan := make(chan dynamic.Message)
go func() { go func() {
@ -244,7 +242,9 @@ func getTestCases() []ProvideTestCase {
} }
} }
func createProvider(t *testing.T, test ProvideTestCase, watch bool) (*Provider, func()) { func createProvider(t *testing.T, test ProvideTestCase, watch bool) *Provider {
t.Helper()
tempDir := createTempDir(t, "testdir") tempDir := createTempDir(t, "testdir")
provider := &Provider{} provider := &Provider{}
@ -276,9 +276,11 @@ func createProvider(t *testing.T, test ProvideTestCase, watch bool) (*Provider,
provider.Filename = file.Name() provider.Filename = file.Name()
} }
return provider, func() { t.Cleanup(func() {
os.RemoveAll(tempDir) os.RemoveAll(tempDir)
} })
return provider
} }
// createTempDir Helper. // createTempDir Helper.

View file

@ -0,0 +1,15 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080

View file

@ -0,0 +1,23 @@
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
name: ""
namespace: testing
spec:
rules:
- host: "*.bar"
http:
paths:
- path: /bar
backend:
serviceName: service1
servicePort: 80
- host: "bar"
http:
paths:
- path: /bar
backend:
serviceName: service1
servicePort: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIp: 10.0.0.1

View file

@ -0,0 +1,15 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080

View file

@ -0,0 +1,19 @@
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
name: ""
namespace: testing
spec:
rules:
- http:
paths:
- path: /foo/bar
backend:
serviceName: service1
servicePort: 80
- path: /foo-bar
backend:
serviceName: service1
servicePort: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIp: 10.0.0.1

View file

@ -2,6 +2,7 @@ package ingress
import ( import (
"context" "context"
"crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"math" "math"
@ -252,6 +253,8 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
conf.HTTP.Services["default-backend"] = service conf.HTTP.Services["default-backend"] = service
} }
routers := map[string][]*dynamic.Router{}
for _, rule := range ingress.Spec.Rules { for _, rule := range ingress.Spec.Rules {
if err := p.updateIngressStatus(ingress, client); err != nil { if err := p.updateIngressStatus(ingress, client); err != nil {
log.FromContext(ctx).Errorf("Error while updating ingress status: %v", err) log.FromContext(ctx).Errorf("Error while updating ingress status: %v", err)
@ -275,8 +278,26 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
conf.HTTP.Services[serviceName] = service conf.HTTP.Services[serviceName] = service
routerKey := strings.TrimPrefix(provider.Normalize(ingress.Name+"-"+ingress.Namespace+"-"+rule.Host+pa.Path), "-") routerKey := strings.TrimPrefix(provider.Normalize(ingress.Name+"-"+ingress.Namespace+"-"+rule.Host+pa.Path), "-")
routers[routerKey] = append(routers[routerKey], loadRouter(rule, pa, rtConfig, serviceName))
}
}
conf.HTTP.Routers[routerKey] = loadRouter(rule, pa, rtConfig, serviceName) for routerKey, conflictingRouters := range routers {
if len(conflictingRouters) == 1 {
conf.HTTP.Routers[routerKey] = conflictingRouters[0]
continue
}
log.FromContext(ctx).Debugf("Multiple routers are defined with the same key %q, generating hashes to avoid conflicts", routerKey)
for _, router := range conflictingRouters {
key, err := makeRouterKeyWithHash(routerKey, router.Rule)
if err != nil {
log.FromContext(ctx).Error(err)
continue
}
conf.HTTP.Routers[key] = router
} }
} }
} }
@ -542,6 +563,17 @@ func getProtocol(portSpec corev1.ServicePort, portName string, svcConfig *Servic
return protocol return protocol
} }
func makeRouterKeyWithHash(key, rule string) (string, error) {
h := sha256.New()
if _, err := h.Write([]byte(rule)); err != nil {
return "", err
}
dupKey := fmt.Sprintf("%s-%.10x", key, h.Sum(nil))
return dupKey, nil
}
func loadRouter(rule networkingv1beta1.IngressRule, pa networkingv1beta1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router { func loadRouter(rule networkingv1beta1.IngressRule, pa networkingv1beta1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router {
var rules []string var rules []string
if len(rule.Host) > 0 { if len(rule.Host) > 0 {

View file

@ -169,6 +169,74 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
}, },
}, },
}, },
{
desc: "Ingress with conflicting routers on host",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar-bar-3be6cfd7daba66cf2fdd": {
Rule: "HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.bar`) && PathPrefix(`/bar`)",
Service: "testing-service1-80",
},
"testing-bar-bar-636bf36c00fedaab3d44": {
Rule: "Host(`bar`) && PathPrefix(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
{
URL: "http://10.21.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "Ingress with conflicting routers on path",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-foo-bar-d0b30949e54d6a7515ca": {
Rule: "PathPrefix(`/foo/bar`)",
Service: "testing-service1-80",
},
"testing-foo-bar-dcd54bae39a6d7557f48": {
Rule: "PathPrefix(`/foo-bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
{
URL: "http://10.21.0.1:8080",
},
},
},
},
},
},
},
},
{ {
desc: "Ingress one rule with two paths", desc: "Ingress one rule with two paths",
expected: &dynamic.Configuration{ expected: &dynamic.Configuration{

View file

@ -59,7 +59,14 @@ func (r *Router) AddRoute(rule string, priority int, handler http.Handler) error
} }
route := r.NewRoute().Handler(handler).Priority(priority) route := r.NewRoute().Handler(handler).Priority(priority)
return addRuleOnRoute(route, buildTree())
err = addRuleOnRoute(route, buildTree())
if err != nil {
route.BuildOnly()
return err
}
return nil
} }
type tree struct { type tree struct {

View file

@ -29,6 +29,16 @@ func Test_addRoute(t *testing.T) {
rule: "rulewithnotmatcher", rule: "rulewithnotmatcher",
expectedError: true, expectedError: true,
}, },
{
desc: "Host empty",
rule: "Host(``)",
expectedError: true,
},
{
desc: "PathPrefix empty",
rule: "PathPrefix(``)",
expectedError: true,
},
{ {
desc: "PathPrefix", desc: "PathPrefix",
rule: "PathPrefix(`/foo`)", rule: "PathPrefix(`/foo`)",

View file

@ -62,6 +62,29 @@ func TestRouterManager_Get(t *testing.T) {
entryPoints: []string{"web"}, entryPoints: []string{"web"},
expected: expectedResult{StatusCode: http.StatusOK}, expected: expectedResult{StatusCode: http.StatusOK},
}, },
{
desc: "empty host",
routersConfig: map[string]*dynamic.Router{
"foo": {
EntryPoints: []string{"web"},
Service: "foo-service",
Rule: "Host(``)",
},
},
serviceConfig: map[string]*dynamic.Service{
"foo-service": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: server.URL,
},
},
},
},
},
entryPoints: []string{"web"},
expected: expectedResult{StatusCode: http.StatusNotFound},
},
{ {
desc: "no load balancer", desc: "no load balancer",
routersConfig: map[string]*dynamic.Router{ routersConfig: map[string]*dynamic.Router{

View file

@ -28,6 +28,7 @@ func TestShutdownHijacked(t *testing.T) {
err = resp.Write(conn) err = resp.Write(conn)
require.NoError(t, err) require.NoError(t, err)
})) }))
testShutdown(t, router) testShutdown(t, router)
} }
@ -37,6 +38,7 @@ func TestShutdownHTTP(t *testing.T) {
rw.WriteHeader(http.StatusOK) rw.WriteHeader(http.StatusOK)
time.Sleep(time.Second) time.Sleep(time.Second)
})) }))
testShutdown(t, router) testShutdown(t, router)
} }
@ -61,6 +63,8 @@ func TestShutdownTCP(t *testing.T) {
} }
func testShutdown(t *testing.T, router *tcp.Router) { func testShutdown(t *testing.T, router *tcp.Router) {
t.Helper()
epConfig := &static.EntryPointsTransport{} epConfig := &static.EntryPointsTransport{}
epConfig.SetDefaults() epConfig.SetDefaults()

View file

@ -103,6 +103,8 @@ func TestShutdownUDPConn(t *testing.T) {
// It fatals if the read blocks longer than timeout, which is useful to detect // It fatals if the read blocks longer than timeout, which is useful to detect
// regressions that would make a test wait forever. // regressions that would make a test wait forever.
func requireEcho(t *testing.T, data string, conn io.ReadWriter, timeout time.Duration) { func requireEcho(t *testing.T, data string, conn io.ReadWriter, timeout time.Duration) {
t.Helper()
_, err := conn.Write([]byte(data)) _, err := conn.Write([]byte(data))
require.NoError(t, err) require.NoError(t, err)

View file

@ -696,12 +696,16 @@ func (w *websocketRequest) open() (*websocket.Conn, net.Conn, error) {
} }
func parseURI(t *testing.T, uri string) *url.URL { func parseURI(t *testing.T, uri string) *url.URL {
t.Helper()
out, err := url.ParseRequestURI(uri) out, err := url.ParseRequestURI(uri)
require.NoError(t, err) require.NoError(t, err)
return out return out
} }
func createProxyWithForwarder(t *testing.T, proxy http.Handler, url string) *httptest.Server { func createProxyWithForwarder(t *testing.T, proxy http.Handler, url string) *httptest.Server {
t.Helper()
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
path := req.URL.Path // keep the original path path := req.URL.Path // keep the original path
// Set new backend URL // Set new backend URL

View file

@ -17,6 +17,8 @@ import (
) )
func fakeRedis(t *testing.T, listener net.Listener) { func fakeRedis(t *testing.T, listener net.Listener) {
t.Helper()
for { for {
conn, err := listener.Accept() conn, err := listener.Accept()
fmt.Println("Accept on server") fmt.Println("Accept on server")

View file

@ -171,6 +171,8 @@ func TestTimeoutWithoutRead(t *testing.T) {
} }
func testTimeout(t *testing.T, withRead bool) { func testTimeout(t *testing.T, withRead bool) {
t.Helper()
addr, err := net.ResolveUDPAddr("udp", ":0") addr, err := net.ResolveUDPAddr("udp", ":0")
require.NoError(t, err) require.NoError(t, err)
@ -312,6 +314,8 @@ func TestShutdown(t *testing.T) {
// It fatals if the read blocks longer than timeout, // It fatals if the read blocks longer than timeout,
// which is useful to detect regressions that would make a test wait forever. // which is useful to detect regressions that would make a test wait forever.
func requireEcho(t *testing.T, data string, conn io.ReadWriter, timeout time.Duration) { func requireEcho(t *testing.T, data string, conn io.ReadWriter, timeout time.Duration) {
t.Helper()
_, err := conn.Write([]byte(data)) _, err := conn.Write([]byte(data))
require.NoError(t, err) require.NoError(t, err)

View file

@ -41,6 +41,8 @@ func TestUDPProxy(t *testing.T) {
} }
func newServer(t *testing.T, addr string, handler Handler) { func newServer(t *testing.T, addr string, handler Handler) {
t.Helper()
addrL, err := net.ResolveUDPAddr("udp", addr) addrL, err := net.ResolveUDPAddr("udp", addr)
require.NoError(t, err) require.NoError(t, err)

View file

@ -6,9 +6,9 @@ FileName = "traefik_changelog.md"
# example new bugfix v2.3.5 # example new bugfix v2.3.5
CurrentRef = "v2.3" CurrentRef = "v2.3"
PreviousRef = "v2.3.4" PreviousRef = "v2.3.5"
BaseBranch = "v2.3" BaseBranch = "v2.3"
FutureCurrentRefName = "v2.3.5" FutureCurrentRefName = "v2.3.6"
ThresholdPreviousRef = 10 ThresholdPreviousRef = 10
ThresholdCurrentRef = 10 ThresholdCurrentRef = 10

View file

@ -330,7 +330,7 @@
v-for="(val, key) in exData(middleware).customRequestHeaders" :key="key" v-for="(val, key) in exData(middleware).customRequestHeaders" :key="key"
dense dense
class="app-chip app-chip-green"> class="app-chip app-chip-green">
{{ val }} {{ key }}: {{ val }}
</q-chip> </q-chip>
</div> </div>
</div> </div>
@ -344,7 +344,7 @@
v-for="(val, key) in exData(middleware).customResponseHeaders" :key="key" v-for="(val, key) in exData(middleware).customResponseHeaders" :key="key"
dense dense
class="app-chip app-chip-green"> class="app-chip app-chip-green">
{{ val }} {{ key }}: {{ val }}
</q-chip> </q-chip>
</div> </div>
</div> </div>