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
"wrapcheck", # Too strict
"tparallel", # Not relevant
"paralleltest", # Not relevant
"exhaustivestruct", # Not relevant
"makezero", # not relevant
"forbidigo", # not relevant
]
[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)
[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
# 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
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`
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.
```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
[`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

View file

@ -131,13 +131,14 @@ Below is the list of the currently supported providers in Traefik.
| [Docker](./docker.md) | Orchestrator | Label |
| [Kubernetes](./kubernetes-crd.md) | Orchestrator | Custom Resource or Ingress |
| [Consul Catalog](./consul-catalog.md) | Orchestrator | Label |
| [ECS](./ecs.md) | Orchestrator | Label |
| [Marathon](./marathon.md) | Orchestrator | Label |
| [Rancher](./rancher.md) | Orchestrator | Label |
| [File](./file.md) | Manual | TOML/YAML format |
| [Consul](./consul.md) | KV | KV |
| [Etcd](./etcd.md) | KV | KV |
| [Redis](./redis.md) | KV | KV |
| [ZooKeeper](./zookeeper.md) | KV | KV |
| [Redis](./redis.md) | KV | KV |
| [HTTP](./http.md) | Manual | JSON format |
!!! 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) {
t.Helper()
return func(metric *PilotMetric) {
for _, value := range metric.Observations {
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) {
t.Helper()
return func(metric *PilotMetric) {
for _, value := range metric.Observations {
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) {
t.Helper()
return func(metric *PilotMetric) {
for _, value := range metric.Observations {
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) {
t.Helper()
return func(metric *PilotMetric) {
for _, value := range metric.Observations {
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) {
t.Helper()
return func(metric *PilotMetric) {
for _, value := range metric.Observations {
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) {
t.Helper()
return func(family *dto.MetricFamily) {
if cv := int(family.Metric[0].Counter.GetValue()); 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) {
t.Helper()
return func(family *dto.MetricFamily) {
if cv := int(family.Metric[0].Counter.GetValue()); 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) {
t.Helper()
return func(family *dto.MetricFamily) {
if sc := int(family.Metric[0].Histogram.GetSampleCount()); 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) {
t.Helper()
return func(family *dto.MetricFamily) {
if gv := int(family.Metric[0].Gauge.GetValue()); 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) {
t.Helper()
return func(family *dto.MetricFamily) {
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)

View file

@ -683,6 +683,8 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
}
func assertValidLogData(t *testing.T, expected string, logData []byte) {
t.Helper()
if len(expected) == 0 {
assert.Zero(t, len(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()) {
t.Helper()
file, err := ioutil.TempFile("", "testlogger")
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 {
t.Helper()
tmpDir, err := ioutil.TempDir("", prefix)
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) {
t.Helper()
logger, err := NewHandler(config)
require.NoError(t, err)
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) {
t.Helper()
doLoggingTLSOpt(t, config, true)
}
func doLogging(t *testing.T, config *types.AccessLog) {
t.Helper()
doLoggingTLSOpt(t, config, false)
}

View file

@ -76,8 +76,7 @@ func TestErrorWhenEmptyConfig(t *testing.T) {
func TestProvideWithoutWatch(t *testing.T) {
for _, test := range getTestCases() {
t.Run(test.desc+" without watch", func(t *testing.T) {
provider, clean := createProvider(t, test, false)
defer clean()
provider := createProvider(t, test, false)
configChan := make(chan dynamic.Message)
provider.DebugLogGeneratedTemplate = true
@ -109,8 +108,7 @@ func TestProvideWithoutWatch(t *testing.T) {
func TestProvideWithWatch(t *testing.T) {
for _, test := range getTestCases() {
t.Run(test.desc+" with watch", func(t *testing.T) {
provider, clean := createProvider(t, test, true)
defer clean()
provider := createProvider(t, test, true)
configChan := make(chan dynamic.Message)
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")
provider := &Provider{}
@ -276,9 +276,11 @@ func createProvider(t *testing.T, test ProvideTestCase, watch bool) (*Provider,
provider.Filename = file.Name()
}
return provider, func() {
t.Cleanup(func() {
os.RemoveAll(tempDir)
}
})
return provider
}
// 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 (
"context"
"crypto/sha256"
"errors"
"fmt"
"math"
@ -252,6 +253,8 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
conf.HTTP.Services["default-backend"] = service
}
routers := map[string][]*dynamic.Router{}
for _, rule := range ingress.Spec.Rules {
if err := p.updateIngressStatus(ingress, client); err != nil {
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
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
}
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 {
var rules []string
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",
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)
return addRuleOnRoute(route, buildTree())
err = addRuleOnRoute(route, buildTree())
if err != nil {
route.BuildOnly()
return err
}
return nil
}
type tree struct {

View file

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

View file

@ -62,6 +62,29 @@ func TestRouterManager_Get(t *testing.T) {
entryPoints: []string{"web"},
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",
routersConfig: map[string]*dynamic.Router{

View file

@ -28,6 +28,7 @@ func TestShutdownHijacked(t *testing.T) {
err = resp.Write(conn)
require.NoError(t, err)
}))
testShutdown(t, router)
}
@ -37,6 +38,7 @@ func TestShutdownHTTP(t *testing.T) {
rw.WriteHeader(http.StatusOK)
time.Sleep(time.Second)
}))
testShutdown(t, router)
}
@ -61,6 +63,8 @@ func TestShutdownTCP(t *testing.T) {
}
func testShutdown(t *testing.T, router *tcp.Router) {
t.Helper()
epConfig := &static.EntryPointsTransport{}
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
// regressions that would make a test wait forever.
func requireEcho(t *testing.T, data string, conn io.ReadWriter, timeout time.Duration) {
t.Helper()
_, err := conn.Write([]byte(data))
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 {
t.Helper()
out, err := url.ParseRequestURI(uri)
require.NoError(t, err)
return out
}
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) {
path := req.URL.Path // keep the original path
// Set new backend URL

View file

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

View file

@ -171,6 +171,8 @@ func TestTimeoutWithoutRead(t *testing.T) {
}
func testTimeout(t *testing.T, withRead bool) {
t.Helper()
addr, err := net.ResolveUDPAddr("udp", ":0")
require.NoError(t, err)
@ -312,6 +314,8 @@ func TestShutdown(t *testing.T) {
// It fatals if the read blocks longer than timeout,
// 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) {
t.Helper()
_, err := conn.Write([]byte(data))
require.NoError(t, err)

View file

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

View file

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

View file

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