Merge v2.11 into v3.6

This commit is contained in:
mmatur
2026-01-26 17:20:11 +01:00
22 changed files with 307 additions and 156 deletions
+12 -12
View File
@@ -413,13 +413,13 @@ func TestPrometheusMetricRemoval(t *testing.T) {
th.WithRouter("foo@providerName", th.WithServiceName("bar")),
th.WithRouter("router2", th.WithServiceName("bar@providerName")),
),
th.WithLoadBalancerServices(
th.WithService("bar@providerName", th.WithServers(
th.WithServices(
th.WithService("bar@providerName", th.WithServiceServersLoadBalancer(th.WithServers(
th.WithServer("http://localhost:9000"),
th.WithServer("http://localhost:9999"),
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")))),
),
),
}
@@ -429,8 +429,8 @@ func TestPrometheusMetricRemoval(t *testing.T) {
th.WithRouters(
th.WithRouter("foo@providerName", th.WithServiceName("bar")),
),
th.WithLoadBalancerServices(
th.WithService("bar@providerName", th.WithServers(th.WithServer("http://localhost:9000"))),
th.WithServices(
th.WithService("bar@providerName", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9000")))),
),
),
}
@@ -500,8 +500,8 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) {
conf1 := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithLoadBalancerServices(
th.WithService("service1", th.WithServers(th.WithServer("http://localhost:9000"))),
th.WithServices(
th.WithService("service1", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9000")))),
),
),
}
@@ -512,8 +512,8 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) {
conf3 := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithLoadBalancerServices(
th.WithService("service1", th.WithServers(th.WithServer("http://localhost:9001"))),
th.WithServices(
th.WithService("service1", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9001")))),
),
),
}
@@ -539,8 +539,8 @@ func TestPrometheusRemovedMetricsReset(t *testing.T) {
conf1 := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithLoadBalancerServices(th.WithService("service",
th.WithServers(th.WithServer("http://localhost:9000"))),
th.WithServices(
th.WithService("service", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer("http://localhost:9000")))),
),
),
}
+67 -25
View File
@@ -87,7 +87,7 @@ func TestNewConfigurationWatcher(t *testing.T) {
th.WithServiceName("scv"),
th.WithObservability())),
th.WithMiddlewares(),
th.WithLoadBalancerServices(),
th.WithServices(),
),
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
@@ -126,7 +126,9 @@ func TestWaitForRequiredProvider(t *testing.T) {
config := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
}
@@ -170,14 +172,18 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
config := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
}
expectedConfig := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"), th.WithObservability())),
th.WithLoadBalancerServices(th.WithService("bar@mock")),
th.WithServices(
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
),
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
@@ -202,7 +208,9 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
expectedConfig3 := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"), th.WithObservability())),
th.WithLoadBalancerServices(th.WithService("bar-config3@mock")),
th.WithServices(
th.WithService("bar-config3@mock", th.WithServiceServersLoadBalancer()),
),
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
@@ -227,14 +235,18 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
config2 := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("baz", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("toto")),
th.WithServices(
th.WithService("toto", th.WithServiceServersLoadBalancer()),
),
),
}
config3 := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
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{}, "")
@@ -318,7 +330,9 @@ func TestListenProvidersThrottleProviderConfigReload(t *testing.T) {
Configuration: &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo"+strconv.Itoa(i), th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
},
})
@@ -378,7 +392,9 @@ func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
Configuration: &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
},
}
@@ -410,14 +426,18 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
configuration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
}
transientConfiguration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bad")),
th.WithServices(
th.WithService("bad", th.WithServiceServersLoadBalancer()),
),
),
}
@@ -449,7 +469,9 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
expected := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"), th.WithObservability())),
th.WithLoadBalancerServices(th.WithService("bar@mock")),
th.WithServices(
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
),
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
@@ -480,14 +502,18 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
configuration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
}
transientConfiguration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bad")),
th.WithServices(
th.WithService("bad", th.WithServiceServersLoadBalancer()),
),
),
}
@@ -540,7 +566,9 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
expected := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo@mock", th.WithEntryPoints("ep"), th.WithObservability())),
th.WithLoadBalancerServices(th.WithService("bar@mock")),
th.WithServices(
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
),
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
@@ -581,7 +609,9 @@ func TestApplyConfigUnderStress(t *testing.T) {
case watcher.allProvidersConfigs <- dynamic.Message{ProviderName: "mock", Configuration: &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo"+strconv.Itoa(i), th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
}}:
}
@@ -616,28 +646,36 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
configuration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
}
transientConfiguration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("bad", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bad")),
th.WithServices(
th.WithService("bad", th.WithServiceServersLoadBalancer()),
),
),
}
transientConfiguration2 := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("bad2", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bad2")),
th.WithServices(
th.WithService("bad2", th.WithServiceServersLoadBalancer()),
),
),
}
finalConfiguration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("final", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("final")),
th.WithServices(
th.WithService("final", th.WithServiceServersLoadBalancer()),
),
),
}
@@ -676,7 +714,9 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
expected := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("final@mock", th.WithEntryPoints("ep"), th.WithObservability())),
th.WithLoadBalancerServices(th.WithService("final@mock")),
th.WithServices(
th.WithService("final@mock", th.WithServiceServersLoadBalancer()),
),
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
@@ -709,7 +749,9 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
configuration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo", th.WithEntryPoints("ep"))),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
),
}
@@ -742,9 +784,9 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
th.WithRouter("foo@mock", th.WithEntryPoints("ep"), th.WithObservability()),
th.WithRouter("foo@mock2", th.WithEntryPoints("ep"), th.WithObservability()),
),
th.WithLoadBalancerServices(
th.WithService("bar@mock"),
th.WithService("bar@mock2"),
th.WithServices(
th.WithService("bar@mock", th.WithServiceServersLoadBalancer()),
th.WithService("bar@mock2", th.WithServiceServersLoadBalancer()),
),
th.WithMiddlewares(),
),
+2 -20
View File
@@ -6,8 +6,6 @@ import (
"fmt"
"net/http"
"reflect"
"slices"
"strings"
"github.com/containous/alice"
"github.com/rs/zerolog/log"
@@ -38,12 +36,7 @@ import (
"github.com/traefik/traefik/v3/pkg/middlewares/stripprefix"
"github.com/traefik/traefik/v3/pkg/middlewares/stripprefixregex"
"github.com/traefik/traefik/v3/pkg/server/provider"
)
type middlewareStackType int
const (
middlewareStackKey middlewareStackType = iota
"github.com/traefik/traefik/v3/pkg/server/recursion"
)
// Builder the middleware builder.
@@ -75,7 +68,7 @@ func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.C
}
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)
return nil, err
}
@@ -98,17 +91,6 @@ func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.C
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.
func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (alice.Constructor, error) {
config := b.configs[middlewareName]
+6 -5
View File
@@ -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",
@@ -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"},
configuration: map[string]*dynamic.Middleware{
"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",
@@ -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: "--",
@@ -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"),
},
}
+26
View File
@@ -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
}
+66 -23
View File
@@ -46,8 +46,8 @@ func TestReuseService(t *testing.T) {
th.WithMiddlewares(th.WithMiddleware("basicauth",
th.WithBasicAuth(&dynamic.BasicAuth{Users: []string{"foo:bar"}}),
)),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithServers(th.WithServer(testServer.URL))),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer(testServer.URL)))),
),
)
@@ -98,8 +98,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithServers(th.WithServer(testServerURL))),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithServers(th.WithServer(testServerURL)))),
),
)
},
@@ -121,7 +121,9 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer()),
),
)
},
expectedStatusCode: http.StatusServiceUnavailable,
@@ -135,27 +137,13 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithSticky("test")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithSticky("test"))),
),
)
},
expectedStatusCode: http.StatusServiceUnavailable,
},
{
desc: "Empty Backend LB",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
return th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar")),
)
},
expectedStatusCode: http.StatusServiceUnavailable,
},
{
desc: "Empty Backend LB Sticky",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
@@ -165,8 +153,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithSticky("test")),
th.WithServices(
th.WithService("bar", th.WithServiceServersLoadBalancer(th.WithSticky("test"))),
),
)
},
@@ -256,6 +244,61 @@ func TestInternalServices(t *testing.T) {
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")))),
),
)
transportManager := service.NewTransportManager(nil)
transportManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
managerFactory := service.NewManagerFactory(staticConfig, nil, nil, transportManager, nil, nil)
tlsManager := tls.NewManager(nil)
dialerManager := tcp.NewDialerManager(nil)
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
factory, err := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager)
require.NoError(t, err)
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, "building HTTP service \"foo\": building HTTP service \"bar\": 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")
}
type proxyBuilderMock struct{}
func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _ bool, _ time.Duration) (http.Handler, error) {
+8 -1
View File
@@ -27,6 +27,7 @@ import (
"github.com/traefik/traefik/v3/pkg/server/cookie"
"github.com/traefik/traefik/v3/pkg/server/middleware"
"github.com/traefik/traefik/v3/pkg/server/provider"
"github.com/traefik/traefik/v3/pkg/server/recursion"
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/failover"
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/hrw"
"github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/leasttime"
@@ -122,6 +123,12 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string) (http.H
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
switch {
@@ -319,7 +326,7 @@ func (m *Manager) getServiceHandler(ctx context.Context, service dynamic.WRRServ
svcHandler, err := m.BuildHTTP(ctx, service.Name)
if err != nil {
return nil, fmt.Errorf("building HTTP service: %w", err)
return nil, fmt.Errorf("building HTTP service %q: %w", service.Name, err)
}
if service.Headers != nil {
+61 -11
View File
@@ -66,32 +66,75 @@ func WithObservability() func(*dynamic.Router) {
}
}
// WithLoadBalancerServices is a helper to create a configuration.
func WithLoadBalancerServices(opts ...func(service *dynamic.ServersLoadBalancer) string) func(*dynamic.HTTPConfiguration) {
// WithServices is a helper to create a configuration.
func WithServices(opts ...func(service *dynamic.Service) string) func(*dynamic.HTTPConfiguration) {
return func(c *dynamic.HTTPConfiguration) {
c.Services = make(map[string]*dynamic.Service)
for _, opt := range opts {
b := &dynamic.ServersLoadBalancer{}
b.SetDefaults()
b := &dynamic.Service{}
name := opt(b)
c.Services[name] = &dynamic.Service{
LoadBalancer: b,
}
c.Services[name] = b
}
}
}
// WithService is a helper to create a configuration.
func WithService(name string, opts ...func(*dynamic.ServersLoadBalancer)) func(*dynamic.ServersLoadBalancer) string {
return func(r *dynamic.ServersLoadBalancer) string {
func WithService(name string, opts ...func(*dynamic.Service)) func(*dynamic.Service) string {
return func(s *dynamic.Service) string {
for _, opt := range opts {
opt(r)
opt(s)
}
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.
func WithMiddlewares(opts ...func(*dynamic.Middleware) string) func(*dynamic.HTTPConfiguration) {
return func(c *dynamic.HTTPConfiguration) {
@@ -121,6 +164,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.
func WithEntryPoints(eps ...string) func(*dynamic.Router) {
return func(f *dynamic.Router) {