Merge branch v2.4 into master
This commit is contained in:
commit
0509b6fdb9
29 changed files with 317 additions and 17 deletions
|
@ -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]
|
||||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,10 @@
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,10 @@
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
|
@ -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 {
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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`)",
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue