mirror of
https://github.com/traefik/traefik
synced 2026-02-03 11:10:33 +00:00
Avoid recursion with services
This commit is contained in:
@@ -451,13 +451,13 @@ func TestPrometheusMetricRemoval(t *testing.T) {
|
|||||||
th.WithRouter("foo@providerName", th.WithServiceName("bar")),
|
th.WithRouter("foo@providerName", th.WithServiceName("bar")),
|
||||||
th.WithRouter("router2", th.WithServiceName("bar@providerName")),
|
th.WithRouter("router2", th.WithServiceName("bar@providerName")),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(
|
th.WithServices(
|
||||||
th.WithService("bar@providerName", th.WithServers(
|
th.WithService("bar@providerName", th.WithServiceServersLoadBalancer(th.WithServers(
|
||||||
th.WithServer("http://localhost:9000"),
|
th.WithServer("http://localhost:9000"),
|
||||||
th.WithServer("http://localhost:9999"),
|
th.WithServer("http://localhost:9999"),
|
||||||
th.WithServer("http://localhost:9998"),
|
th.WithServer("http://localhost:9998"),
|
||||||
)),
|
))),
|
||||||
th.WithService("service1", th.WithServers(th.WithServer("http://localhost:9000"))),
|
th.WithService("service1", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9000")))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -467,8 +467,8 @@ func TestPrometheusMetricRemoval(t *testing.T) {
|
|||||||
th.WithRouters(
|
th.WithRouters(
|
||||||
th.WithRouter("foo@providerName", th.WithServiceName("bar")),
|
th.WithRouter("foo@providerName", th.WithServiceName("bar")),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(
|
th.WithServices(
|
||||||
th.WithService("bar@providerName", th.WithServers(th.WithServer("http://localhost:9000"))),
|
th.WithService("bar@providerName", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9000")))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -538,8 +538,8 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) {
|
|||||||
|
|
||||||
conf1 := dynamic.Configuration{
|
conf1 := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithLoadBalancerServices(
|
th.WithServices(
|
||||||
th.WithService("service1", th.WithServers(th.WithServer("http://localhost:9000"))),
|
th.WithService("service1", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9000")))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -550,8 +550,8 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) {
|
|||||||
|
|
||||||
conf3 := dynamic.Configuration{
|
conf3 := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithLoadBalancerServices(
|
th.WithServices(
|
||||||
th.WithService("service1", th.WithServers(th.WithServer("http://localhost:9001"))),
|
th.WithService("service1", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9001")))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -577,8 +577,8 @@ func TestPrometheusRemovedMetricsReset(t *testing.T) {
|
|||||||
|
|
||||||
conf1 := dynamic.Configuration{
|
conf1 := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithLoadBalancerServices(th.WithService("service",
|
th.WithServices(
|
||||||
th.WithServers(th.WithServer("http://localhost:9000"))),
|
th.WithService("service", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9000")))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func TestNewConfigurationWatcher(t *testing.T) {
|
|||||||
th.WithEntryPoints("e"),
|
th.WithEntryPoints("e"),
|
||||||
th.WithServiceName("scv"))),
|
th.WithServiceName("scv"))),
|
||||||
th.WithMiddlewares(),
|
th.WithMiddlewares(),
|
||||||
th.WithLoadBalancerServices(),
|
th.WithServices(),
|
||||||
),
|
),
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
Routers: map[string]*dynamic.TCPRouter{},
|
Routers: map[string]*dynamic.TCPRouter{},
|
||||||
@@ -123,7 +123,9 @@ func TestWaitForRequiredProvider(t *testing.T) {
|
|||||||
config := &dynamic.Configuration{
|
config := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,14 +169,18 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
|
|||||||
config := &dynamic.Configuration{
|
config := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedConfig := dynamic.Configuration{
|
expectedConfig := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar@mock")),
|
th.WithServices(
|
||||||
|
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
th.WithMiddlewares(),
|
th.WithMiddlewares(),
|
||||||
),
|
),
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
@@ -197,7 +203,9 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
|
|||||||
expectedConfig3 := dynamic.Configuration{
|
expectedConfig3 := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar-config3@mock")),
|
th.WithServices(
|
||||||
|
th.WithService("bar-config3@mock", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
th.WithMiddlewares(),
|
th.WithMiddlewares(),
|
||||||
),
|
),
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
@@ -220,14 +228,18 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
|
|||||||
config2 := &dynamic.Configuration{
|
config2 := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("baz", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("baz", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("toto")),
|
th.WithServices(
|
||||||
|
th.WithService("toto", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
config3 := &dynamic.Configuration{
|
config3 := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar-config3")),
|
th.WithServices(
|
||||||
|
th.WithService("bar-config3", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
watcher := NewConfigurationWatcher(routinesPool, &mockProvider{}, []string{}, "")
|
watcher := NewConfigurationWatcher(routinesPool, &mockProvider{}, []string{}, "")
|
||||||
@@ -311,7 +323,9 @@ func TestListenProvidersThrottleProviderConfigReload(t *testing.T) {
|
|||||||
Configuration: &dynamic.Configuration{
|
Configuration: &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo"+strconv.Itoa(i), th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo"+strconv.Itoa(i), th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -371,7 +385,9 @@ func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
|
|||||||
Configuration: &dynamic.Configuration{
|
Configuration: &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -403,14 +419,18 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
|||||||
configuration := &dynamic.Configuration{
|
configuration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
transientConfiguration := &dynamic.Configuration{
|
transientConfiguration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bad")),
|
th.WithServices(
|
||||||
|
th.WithService("bad", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,7 +462,9 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
|||||||
expected := dynamic.Configuration{
|
expected := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar@mock")),
|
th.WithServices(
|
||||||
|
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
th.WithMiddlewares(),
|
th.WithMiddlewares(),
|
||||||
),
|
),
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
@@ -471,14 +493,18 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
|
|||||||
configuration := &dynamic.Configuration{
|
configuration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
transientConfiguration := &dynamic.Configuration{
|
transientConfiguration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bad")),
|
th.WithServices(
|
||||||
|
th.WithService("bad", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +557,9 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
|
|||||||
expected := dynamic.Configuration{
|
expected := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar@mock")),
|
th.WithServices(
|
||||||
|
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
th.WithMiddlewares(),
|
th.WithMiddlewares(),
|
||||||
),
|
),
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
@@ -570,7 +598,9 @@ func TestApplyConfigUnderStress(t *testing.T) {
|
|||||||
case watcher.allProvidersConfigs <- dynamic.Message{ProviderName: "mock", Configuration: &dynamic.Configuration{
|
case watcher.allProvidersConfigs <- dynamic.Message{ProviderName: "mock", Configuration: &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo"+strconv.Itoa(i), th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo"+strconv.Itoa(i), th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}}:
|
}}:
|
||||||
}
|
}
|
||||||
@@ -605,28 +635,36 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
|
|||||||
configuration := &dynamic.Configuration{
|
configuration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
transientConfiguration := &dynamic.Configuration{
|
transientConfiguration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bad")),
|
th.WithServices(
|
||||||
|
th.WithService("bad", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
transientConfiguration2 := &dynamic.Configuration{
|
transientConfiguration2 := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("bad2", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("bad2", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bad2")),
|
th.WithServices(
|
||||||
|
th.WithService("bad2", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
finalConfiguration := &dynamic.Configuration{
|
finalConfiguration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("final", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("final", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("final")),
|
th.WithServices(
|
||||||
|
th.WithService("final", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,7 +703,9 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
|
|||||||
expected := dynamic.Configuration{
|
expected := dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("final@mock", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("final@mock", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("final@mock")),
|
th.WithServices(
|
||||||
|
th.WithService("final@mock", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
th.WithMiddlewares(),
|
th.WithMiddlewares(),
|
||||||
),
|
),
|
||||||
TCP: &dynamic.TCPConfiguration{
|
TCP: &dynamic.TCPConfiguration{
|
||||||
@@ -696,7 +736,9 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
|||||||
configuration := &dynamic.Configuration{
|
configuration := &dynamic.Configuration{
|
||||||
HTTP: th.BuildConfiguration(
|
HTTP: th.BuildConfiguration(
|
||||||
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,9 +771,9 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
|||||||
th.WithRouter("foo@mock", th.WithEntryPoints("ep")),
|
th.WithRouter("foo@mock", th.WithEntryPoints("ep")),
|
||||||
th.WithRouter("foo@mock2", th.WithEntryPoints("ep")),
|
th.WithRouter("foo@mock2", th.WithEntryPoints("ep")),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(
|
th.WithServices(
|
||||||
th.WithService("bar@mock"),
|
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
|
||||||
th.WithService("bar@mock2"),
|
th.WithService("bar@mock2", th.WithServiceServersLoadBalancer()),
|
||||||
),
|
),
|
||||||
th.WithMiddlewares(),
|
th.WithMiddlewares(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containous/alice"
|
"github.com/containous/alice"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||||
@@ -33,12 +31,7 @@ import (
|
|||||||
"github.com/traefik/traefik/v2/pkg/middlewares/stripprefixregex"
|
"github.com/traefik/traefik/v2/pkg/middlewares/stripprefixregex"
|
||||||
"github.com/traefik/traefik/v2/pkg/middlewares/tracing"
|
"github.com/traefik/traefik/v2/pkg/middlewares/tracing"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/provider"
|
"github.com/traefik/traefik/v2/pkg/server/provider"
|
||||||
)
|
"github.com/traefik/traefik/v2/pkg/server/recursion"
|
||||||
|
|
||||||
type middlewareStackType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
middlewareStackKey middlewareStackType = iota
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Builder the middleware builder.
|
// Builder the middleware builder.
|
||||||
@@ -70,7 +63,7 @@ func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if constructorContext, err = checkRecursion(constructorContext, middlewareName); err != nil {
|
if constructorContext, err = recursion.CheckRecursion(constructorContext, "middleware", middlewareName); err != nil {
|
||||||
b.configs[middlewareName].AddError(err, true)
|
b.configs[middlewareName].AddError(err, true)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -93,17 +86,6 @@ func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.C
|
|||||||
return &chain
|
return &chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRecursion(ctx context.Context, middlewareName string) (context.Context, error) {
|
|
||||||
currentStack, ok := ctx.Value(middlewareStackKey).([]string)
|
|
||||||
if !ok {
|
|
||||||
currentStack = []string{}
|
|
||||||
}
|
|
||||||
if slices.Contains(currentStack, middlewareName) {
|
|
||||||
return ctx, fmt.Errorf("could not instantiate middleware %s: recursion detected in %s", middlewareName, strings.Join(append(currentStack, middlewareName), "->"))
|
|
||||||
}
|
|
||||||
return context.WithValue(ctx, middlewareStackKey, append(currentStack, middlewareName)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// it is the responsibility of the caller to make sure that b.configs[middlewareName].Middleware exists.
|
// it is the responsibility of the caller to make sure that b.configs[middlewareName].Middleware exists.
|
||||||
func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (alice.Constructor, error) {
|
func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (alice.Constructor, error) {
|
||||||
config := b.configs[middlewareName]
|
config := b.configs[middlewareName]
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: errors.New("could not instantiate middleware m1: recursion detected in m1->m2->m3->m1"),
|
expectedError: errors.New("could not instantiate middleware m1: recursion detected in middleware:m1->middleware:m2->middleware:m3->middleware:m1"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Detects recursion in Middleware chain",
|
desc: "Detects recursion in Middleware chain",
|
||||||
@@ -197,9 +197,10 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: errors.New("could not instantiate middleware m1@provider: recursion detected in m1@provider->m2@provider2->m3@provider->m1@provider"),
|
expectedError: errors.New("could not instantiate middleware m1@provider: recursion detected in middleware:m1@provider->middleware:m2@provider2->middleware:m3@provider->middleware:m1@provider"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
desc: "Detects recursion in Middleware chain",
|
||||||
buildChain: []string{"ok", "m0"},
|
buildChain: []string{"ok", "m0"},
|
||||||
configuration: map[string]*dynamic.Middleware{
|
configuration: map[string]*dynamic.Middleware{
|
||||||
"ok": {
|
"ok": {
|
||||||
@@ -211,7 +212,7 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: errors.New("could not instantiate middleware m0: recursion detected in m0->m0"),
|
expectedError: errors.New("could not instantiate middleware m0: recursion detected in middleware:m0->middleware:m0"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Detects MiddlewareChain that references a Chain that references a Chain with a missing middleware",
|
desc: "Detects MiddlewareChain that references a Chain that references a Chain with a missing middleware",
|
||||||
@@ -238,7 +239,7 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: errors.New("could not instantiate middleware m2: recursion detected in m0->m1->m2->m3->m2"),
|
expectedError: errors.New("could not instantiate middleware m2: recursion detected in middleware:m0->middleware:m1->middleware:m2->middleware:m3->middleware:m2"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "--",
|
desc: "--",
|
||||||
@@ -250,7 +251,7 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: errors.New("could not instantiate middleware m0: recursion detected in m0->m0"),
|
expectedError: errors.New("could not instantiate middleware m0: recursion detected in middleware:m0->middleware:m0"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package recursion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stackType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
stackKey stackType = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckRecursion(ctx context.Context, itemType, itemName string) (context.Context, error) {
|
||||||
|
currentStack, ok := ctx.Value(stackKey).([]string)
|
||||||
|
if !ok {
|
||||||
|
currentStack = []string{}
|
||||||
|
}
|
||||||
|
name := itemType + ":" + itemName
|
||||||
|
if slices.Contains(currentStack, name) {
|
||||||
|
return ctx, fmt.Errorf("could not instantiate %s %s: recursion detected in %s", itemType, itemName, strings.Join(append(currentStack, name), "->"))
|
||||||
|
}
|
||||||
|
return context.WithValue(ctx, stackKey, append(currentStack, name)), nil
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
@@ -43,8 +44,8 @@ func TestReuseService(t *testing.T) {
|
|||||||
th.WithMiddlewares(th.WithMiddleware("basicauth",
|
th.WithMiddlewares(th.WithMiddleware("basicauth",
|
||||||
th.WithBasicAuth(&dynamic.BasicAuth{Users: []string{"foo:bar"}}),
|
th.WithBasicAuth(&dynamic.BasicAuth{Users: []string{"foo:bar"}}),
|
||||||
)),
|
)),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar",
|
th.WithServices(
|
||||||
th.WithServers(th.WithServer(testServer.URL))),
|
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer(testServer.URL)))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -91,8 +92,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||||||
th.WithServiceName("bar"),
|
th.WithServiceName("bar"),
|
||||||
th.WithRule(routeRule)),
|
th.WithRule(routeRule)),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar",
|
th.WithServices(
|
||||||
th.WithServers(th.WithServer(testServerURL))),
|
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer(testServerURL)))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -114,7 +115,9 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||||||
th.WithServiceName("bar"),
|
th.WithServiceName("bar"),
|
||||||
th.WithRule(routeRule)),
|
th.WithRule(routeRule)),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusServiceUnavailable,
|
expectedStatusCode: http.StatusServiceUnavailable,
|
||||||
@@ -128,8 +131,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||||||
th.WithServiceName("bar"),
|
th.WithServiceName("bar"),
|
||||||
th.WithRule(routeRule)),
|
th.WithRule(routeRule)),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar",
|
th.WithServices(
|
||||||
th.WithSticky("test")),
|
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithSticky("test"))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -144,7 +147,9 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||||||
th.WithServiceName("bar"),
|
th.WithServiceName("bar"),
|
||||||
th.WithRule(routeRule)),
|
th.WithRule(routeRule)),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar")),
|
th.WithServices(
|
||||||
|
th.WithService("bar", th.WithServiceServersLoadBalancer()),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusServiceUnavailable,
|
expectedStatusCode: http.StatusServiceUnavailable,
|
||||||
@@ -158,8 +163,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||||||
th.WithServiceName("bar"),
|
th.WithServiceName("bar"),
|
||||||
th.WithRule(routeRule)),
|
th.WithRule(routeRule)),
|
||||||
),
|
),
|
||||||
th.WithLoadBalancerServices(th.WithService("bar",
|
th.WithServices(
|
||||||
th.WithSticky("test")),
|
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithSticky("test"))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -241,3 +246,56 @@ func TestInternalServices(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, http.StatusOK, responseRecorderOk.Result().StatusCode, "status code")
|
assert.Equal(t, http.StatusOK, responseRecorderOk.Result().StatusCode, "status code")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRecursionService(t *testing.T) {
|
||||||
|
testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}))
|
||||||
|
defer testServer.Close()
|
||||||
|
|
||||||
|
staticConfig := static.Configuration{
|
||||||
|
EntryPoints: map[string]*static.EntryPoint{
|
||||||
|
"web": {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicConfigs := th.BuildConfiguration(
|
||||||
|
th.WithRouters(
|
||||||
|
th.WithRouter("foo@provider1",
|
||||||
|
th.WithEntryPoints("web"),
|
||||||
|
th.WithServiceName("bar"),
|
||||||
|
th.WithRule("Path(`/ok`)")),
|
||||||
|
),
|
||||||
|
th.WithMiddlewares(th.WithMiddleware("customerror",
|
||||||
|
th.WithErrorPage(&dynamic.ErrorPage{Service: "bar"}),
|
||||||
|
)),
|
||||||
|
th.WithServices(
|
||||||
|
th.WithService("bar@provider1", th.WithServiceWRR(th.WithWRRServices(th.WithWRRService("foo")))),
|
||||||
|
th.WithService("foo@provider1", th.WithServiceWRR(th.WithWRRServices(th.WithWRRService("bar")))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
roundTripperManager := service.NewRoundTripperManager()
|
||||||
|
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||||
|
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||||
|
tlsManager := tls.NewManager()
|
||||||
|
|
||||||
|
voidRegistry := metrics.NewVoidRegistry()
|
||||||
|
|
||||||
|
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(voidRegistry, nil, nil), nil, voidRegistry)
|
||||||
|
|
||||||
|
rtConf := runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs})
|
||||||
|
entryPointsHandlers, _ := factory.CreateRouters(rtConf)
|
||||||
|
|
||||||
|
// Test that the /ok path returns a status 404.
|
||||||
|
responseRecorderOk := &httptest.ResponseRecorder{}
|
||||||
|
requestOk := httptest.NewRequest(http.MethodGet, testServer.URL+"/ok", nil)
|
||||||
|
entryPointsHandlers["web"].GetHTTPHandler().ServeHTTP(responseRecorderOk, requestOk)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, responseRecorderOk.Result().StatusCode, "status code")
|
||||||
|
|
||||||
|
require.NotNil(t, rtConf.Routers["foo@provider1"])
|
||||||
|
assert.Contains(t, rtConf.Routers["foo@provider1"].Err, "could not instantiate service bar@provider1: recursion detected in service:bar@provider1->service:foo@provider1->service:bar@provider1")
|
||||||
|
require.NotNil(t, rtConf.Services["bar@provider1"])
|
||||||
|
assert.Contains(t, rtConf.Services["bar@provider1"].Err, "could not instantiate service bar@provider1: recursion detected in service:bar@provider1->service:foo@provider1->service:bar@provider1")
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/traefik/traefik/v2/pkg/safe"
|
"github.com/traefik/traefik/v2/pkg/safe"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/cookie"
|
"github.com/traefik/traefik/v2/pkg/server/cookie"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/provider"
|
"github.com/traefik/traefik/v2/pkg/server/provider"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/server/recursion"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/failover"
|
"github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/failover"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/mirror"
|
"github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/mirror"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/wrr"
|
"github.com/traefik/traefik/v2/pkg/server/service/loadbalancer/wrr"
|
||||||
@@ -116,6 +117,12 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string) (http.H
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errRecursion error
|
||||||
|
if ctx, errRecursion = recursion.CheckRecursion(ctx, "service", serviceName); errRecursion != nil {
|
||||||
|
conf.AddError(errRecursion, true)
|
||||||
|
return nil, errRecursion
|
||||||
|
}
|
||||||
|
|
||||||
var lb http.Handler
|
var lb http.Handler
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -53,30 +53,75 @@ func WithServiceName(serviceName string) func(*dynamic.Router) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithLoadBalancerServices is a helper to create a configuration.
|
// WithServices is a helper to create a configuration.
|
||||||
func WithLoadBalancerServices(opts ...func(service *dynamic.ServersLoadBalancer) string) func(*dynamic.HTTPConfiguration) {
|
func WithServices(opts ...func(service *dynamic.Service) string) func(*dynamic.HTTPConfiguration) {
|
||||||
return func(c *dynamic.HTTPConfiguration) {
|
return func(c *dynamic.HTTPConfiguration) {
|
||||||
c.Services = make(map[string]*dynamic.Service)
|
c.Services = make(map[string]*dynamic.Service)
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
b := &dynamic.ServersLoadBalancer{}
|
b := &dynamic.Service{}
|
||||||
name := opt(b)
|
name := opt(b)
|
||||||
c.Services[name] = &dynamic.Service{
|
c.Services[name] = b
|
||||||
LoadBalancer: b,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithService is a helper to create a configuration.
|
// WithService is a helper to create a configuration.
|
||||||
func WithService(name string, opts ...func(*dynamic.ServersLoadBalancer)) func(*dynamic.ServersLoadBalancer) string {
|
func WithService(name string, opts ...func(*dynamic.Service)) func(*dynamic.Service) string {
|
||||||
return func(r *dynamic.ServersLoadBalancer) string {
|
return func(s *dynamic.Service) string {
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(r)
|
opt(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithServiceServersLoadBalancer(opts ...func(*dynamic.ServersLoadBalancer)) func(*dynamic.Service) {
|
||||||
|
return func(s *dynamic.Service) {
|
||||||
|
b := &dynamic.ServersLoadBalancer{}
|
||||||
|
b.SetDefaults()
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.LoadBalancer = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithServiceWRR(opts ...func(*dynamic.WeightedRoundRobin)) func(*dynamic.Service) {
|
||||||
|
return func(s *dynamic.Service) {
|
||||||
|
b := &dynamic.WeightedRoundRobin{}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Weighted = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithWRRServices is a helper to create a configuration.
|
||||||
|
func WithWRRServices(opts ...func(*dynamic.WRRService)) func(*dynamic.WeightedRoundRobin) {
|
||||||
|
return func(b *dynamic.WeightedRoundRobin) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
service := dynamic.WRRService{}
|
||||||
|
opt(&service)
|
||||||
|
b.Services = append(b.Services, service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithWRRService is a helper to create a configuration.
|
||||||
|
func WithWRRService(name string, opts ...func(*dynamic.WRRService)) func(*dynamic.WRRService) {
|
||||||
|
return func(s *dynamic.WRRService) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(s)
|
||||||
|
}
|
||||||
|
s.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithMiddlewares is a helper to create a configuration.
|
// WithMiddlewares is a helper to create a configuration.
|
||||||
func WithMiddlewares(opts ...func(*dynamic.Middleware) string) func(*dynamic.HTTPConfiguration) {
|
func WithMiddlewares(opts ...func(*dynamic.Middleware) string) func(*dynamic.HTTPConfiguration) {
|
||||||
return func(c *dynamic.HTTPConfiguration) {
|
return func(c *dynamic.HTTPConfiguration) {
|
||||||
@@ -106,6 +151,13 @@ func WithBasicAuth(auth *dynamic.BasicAuth) func(*dynamic.Middleware) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithErrorPage is a helper to create a configuration.
|
||||||
|
func WithErrorPage(errorPage *dynamic.ErrorPage) func(*dynamic.Middleware) {
|
||||||
|
return func(r *dynamic.Middleware) {
|
||||||
|
r.Errors = errorPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithEntryPoints is a helper to create a configuration.
|
// WithEntryPoints is a helper to create a configuration.
|
||||||
func WithEntryPoints(eps ...string) func(*dynamic.Router) {
|
func WithEntryPoints(eps ...string) func(*dynamic.Router) {
|
||||||
return func(f *dynamic.Router) {
|
return func(f *dynamic.Router) {
|
||||||
|
|||||||
Reference in New Issue
Block a user