API: expose runtime representation
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com> Co-authored-by: Jean-Baptiste Doumenjou <jb.doumenjou@gmail.com>
This commit is contained in:
parent
5cd9396dae
commit
f6df556eb0
50 changed files with 2250 additions and 1158 deletions
|
@ -39,7 +39,7 @@ const (
|
|||
|
||||
// Builder the middleware builder
|
||||
type Builder struct {
|
||||
configs map[string]*config.Middleware
|
||||
configs map[string]*config.MiddlewareInfo
|
||||
serviceBuilder serviceBuilder
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ type serviceBuilder interface {
|
|||
}
|
||||
|
||||
// NewBuilder creates a new Builder
|
||||
func NewBuilder(configs map[string]*config.Middleware, serviceBuilder serviceBuilder) *Builder {
|
||||
func NewBuilder(configs map[string]*config.MiddlewareInfo, serviceBuilder serviceBuilder) *Builder {
|
||||
return &Builder{configs: configs, serviceBuilder: serviceBuilder}
|
||||
}
|
||||
|
||||
|
@ -60,20 +60,29 @@ func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.C
|
|||
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
constructorContext := internal.AddProviderInContext(ctx, middlewareName)
|
||||
if _, ok := b.configs[middlewareName]; !ok {
|
||||
if midInf, ok := b.configs[middlewareName]; !ok || midInf.Middleware == nil {
|
||||
return nil, fmt.Errorf("middleware %q does not exist", middlewareName)
|
||||
}
|
||||
|
||||
var err error
|
||||
if constructorContext, err = checkRecursion(constructorContext, middlewareName); err != nil {
|
||||
b.configs[middlewareName].Err = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
constructor, err := b.buildConstructor(constructorContext, middlewareName, *b.configs[middlewareName])
|
||||
constructor, err := b.buildConstructor(constructorContext, middlewareName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error during instanciation of %s: %v", middlewareName, err)
|
||||
b.configs[middlewareName].Err = err
|
||||
return nil, err
|
||||
}
|
||||
return constructor(next)
|
||||
|
||||
handler, err := constructor(next)
|
||||
if err != nil {
|
||||
b.configs[middlewareName].Err = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return handler, nil
|
||||
})
|
||||
}
|
||||
return &chain
|
||||
|
@ -90,7 +99,9 @@ func checkRecursion(ctx context.Context, middlewareName string) (context.Context
|
|||
return context.WithValue(ctx, middlewareStackKey, append(currentStack, middlewareName)), nil
|
||||
}
|
||||
|
||||
func (b *Builder) buildConstructor(ctx context.Context, middlewareName string, config config.Middleware) (alice.Constructor, error) {
|
||||
// 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]
|
||||
var middleware alice.Constructor
|
||||
badConf := errors.New("cannot create middleware: multi-types middleware not supported, consider declaring two different pieces of middleware instead")
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
func TestBuilder_BuildChainNilConfig(t *testing.T) {
|
||||
testConfig := map[string]*config.Middleware{
|
||||
testConfig := map[string]*config.MiddlewareInfo{
|
||||
"empty": {},
|
||||
}
|
||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||
|
@ -25,7 +25,7 @@ func TestBuilder_BuildChainNilConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBuilder_BuildChainNonExistentChain(t *testing.T) {
|
||||
testConfig := map[string]*config.Middleware{
|
||||
testConfig := map[string]*config.MiddlewareInfo{
|
||||
"foobar": {},
|
||||
}
|
||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||
|
@ -264,7 +264,12 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
|||
ctx = internal.AddProviderInContext(ctx, test.contextProvider+".foobar")
|
||||
}
|
||||
|
||||
builder := NewBuilder(test.configuration, nil)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Middlewares: test.configuration,
|
||||
},
|
||||
})
|
||||
builder := NewBuilder(rtConf.Middlewares, nil)
|
||||
|
||||
result := builder.BuildChain(ctx, test.buildChain)
|
||||
|
||||
|
@ -310,7 +315,12 @@ func TestBuilder_buildConstructor(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Middlewares: testConfig,
|
||||
},
|
||||
})
|
||||
middlewaresBuilder := NewBuilder(rtConf.Middlewares, nil)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -344,7 +354,8 @@ func TestBuilder_buildConstructor(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
constructor, err := middlewaresBuilder.buildConstructor(context.Background(), test.middlewareID, *testConfig[test.middlewareID])
|
||||
constructor, err := middlewaresBuilder.buildConstructor(context.Background(), test.middlewareID)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
middleware, err2 := constructor(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
|
||||
|
|
|
@ -6,10 +6,10 @@ import (
|
|||
"github.com/containous/alice"
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/pkg/api"
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/metrics"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -19,7 +19,8 @@ type chainBuilder interface {
|
|||
}
|
||||
|
||||
// NewRouteAppenderAggregator Creates a new RouteAppenderAggregator
|
||||
func NewRouteAppenderAggregator(ctx context.Context, chainBuilder chainBuilder, conf static.Configuration, entryPointName string, currentConfiguration *safe.Safe) *RouteAppenderAggregator {
|
||||
func NewRouteAppenderAggregator(ctx context.Context, chainBuilder chainBuilder, conf static.Configuration,
|
||||
entryPointName string, runtimeConfiguration *config.RuntimeConfiguration) *RouteAppenderAggregator {
|
||||
aggregator := &RouteAppenderAggregator{}
|
||||
|
||||
if conf.Providers != nil && conf.Providers.Rest != nil {
|
||||
|
@ -29,17 +30,9 @@ func NewRouteAppenderAggregator(ctx context.Context, chainBuilder chainBuilder,
|
|||
if conf.API != nil && conf.API.EntryPoint == entryPointName {
|
||||
chain := chainBuilder.BuildChain(ctx, conf.API.Middlewares)
|
||||
aggregator.AddAppender(&WithMiddleware{
|
||||
appender: api.Handler{
|
||||
EntryPoint: conf.API.EntryPoint,
|
||||
Dashboard: conf.API.Dashboard,
|
||||
Statistics: conf.API.Statistics,
|
||||
DashboardAssets: conf.API.DashboardAssets,
|
||||
CurrentConfigurations: currentConfiguration,
|
||||
Debug: conf.Global.Debug,
|
||||
},
|
||||
appender: api.New(conf, runtimeConfiguration),
|
||||
routerMiddlewares: chain,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
if conf.Ping != nil && conf.Ping.EntryPoint == entryPointName {
|
||||
|
|
|
@ -62,7 +62,7 @@ func TestNewRouteAppenderAggregator(t *testing.T) {
|
|||
"/wrong": http.StatusBadGateway,
|
||||
"/ping": http.StatusOK,
|
||||
// "/.well-known/acme-challenge/token": http.StatusNotFound, // FIXME
|
||||
"/api/providers": http.StatusUnauthorized,
|
||||
"/api/rawdata": http.StatusUnauthorized,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -3,9 +3,9 @@ package router
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/provider/acme"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/containous/traefik/pkg/server/middleware"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
)
|
||||
|
@ -27,8 +27,8 @@ type RouteAppenderFactory struct {
|
|||
}
|
||||
|
||||
// NewAppender Creates a new RouteAppender
|
||||
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, currentConfiguration *safe.Safe) types.RouteAppender {
|
||||
aggregator := NewRouteAppenderAggregator(ctx, middlewaresBuilder, r.staticConfiguration, r.entryPointName, currentConfiguration)
|
||||
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, runtimeConfiguration *config.RuntimeConfiguration) types.RouteAppender {
|
||||
aggregator := NewRouteAppenderAggregator(ctx, middlewaresBuilder, r.staticConfiguration, r.entryPointName, runtimeConfiguration)
|
||||
|
||||
if r.acmeProvider != nil && r.acmeProvider.HTTPChallenge != nil && r.acmeProvider.HTTPChallenge.EntryPoint == r.entryPointName {
|
||||
aggregator.AddAppender(r.acmeProvider)
|
||||
|
|
|
@ -23,7 +23,7 @@ const (
|
|||
)
|
||||
|
||||
// NewManager Creates a new Manager
|
||||
func NewManager(routers map[string]*config.Router,
|
||||
func NewManager(routers map[string]*config.RouterInfo,
|
||||
serviceManager *service.Manager, middlewaresBuilder *middleware.Builder, modifierBuilder *responsemodifiers.Builder,
|
||||
) *Manager {
|
||||
return &Manager{
|
||||
|
@ -38,7 +38,7 @@ func NewManager(routers map[string]*config.Router,
|
|||
// Manager A route/router manager
|
||||
type Manager struct {
|
||||
routerHandlers map[string]http.Handler
|
||||
configs map[string]*config.Router
|
||||
configs map[string]*config.RouterInfo
|
||||
serviceManager *service.Manager
|
||||
middlewaresBuilder *middleware.Builder
|
||||
modifierBuilder *responsemodifiers.Builder
|
||||
|
@ -75,8 +75,17 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
|
|||
return entryPointHandlers
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.Router {
|
||||
entryPointsRouters := make(map[string]map[string]*config.Router)
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.RouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*config.RouterInfo)
|
||||
|
||||
for rtName, rt := range m.configs {
|
||||
if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
|
||||
|
@ -95,7 +104,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls
|
|||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.Router)
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.RouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
|
@ -105,7 +114,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls
|
|||
return entryPointsRouters
|
||||
}
|
||||
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.Router) (http.Handler, error) {
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.RouterInfo) (http.Handler, error) {
|
||||
router, err := rules.NewRouter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -117,12 +126,14 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
|
||||
handler, err := m.buildRouterHandler(ctxRouter, routerName)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
@ -166,7 +177,7 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string) (ht
|
|||
return m.routerHandlers[routerName], nil
|
||||
}
|
||||
|
||||
func (m *Manager) buildHTTPHandler(ctx context.Context, router *config.Router, routerName string) (http.Handler, error) {
|
||||
func (m *Manager) buildHTTPHandler(ctx context.Context, router *config.RouterInfo, routerName string) (http.Handler, error) {
|
||||
qualifiedNames := make([]string, len(router.Middlewares))
|
||||
for i, name := range router.Middlewares {
|
||||
qualifiedNames[i] = internal.GetQualifiedName(ctx, name)
|
||||
|
@ -186,12 +197,3 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *config.Router, r
|
|||
|
||||
return alice.New().Extend(*mHandler).Append(tHandler).Then(sHandler)
|
||||
}
|
||||
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -314,11 +314,17 @@ func TestRouterManager_Get(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
serviceManager := service.NewManager(test.serviceConfig, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(test.middlewaresConfig, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(test.middlewaresConfig)
|
||||
|
||||
routerManager := NewManager(test.routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: test.serviceConfig,
|
||||
Routers: test.routersConfig,
|
||||
Middlewares: test.middlewaresConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
|
@ -413,11 +419,17 @@ func TestAccessLog(t *testing.T) {
|
|||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
|
||||
serviceManager := service.NewManager(test.serviceConfig, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(test.middlewaresConfig, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(test.middlewaresConfig)
|
||||
|
||||
routerManager := NewManager(test.routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: test.serviceConfig,
|
||||
Routers: test.routersConfig,
|
||||
Middlewares: test.middlewaresConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
|
@ -443,6 +455,310 @@ func TestAccessLog(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRuntimeConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serviceConfig map[string]*config.Service
|
||||
routerConfig map[string]*config.Router
|
||||
middlewareConfig map[string]*config.Middleware
|
||||
expectedError int
|
||||
}{
|
||||
{
|
||||
desc: "No error",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1:8085",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
URL: "http://127.0.0.1:8086",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
HealthCheck: &config.HealthCheck{
|
||||
Interval: "500ms",
|
||||
Path: "/health",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 0,
|
||||
},
|
||||
{
|
||||
desc: "One router with wrong rule",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "All router with wrong rule",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
{
|
||||
desc: "Router with unknown service",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "wrong-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "Router with broken service",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
{
|
||||
desc: "Router with middleware",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*config.Middleware{
|
||||
"auth": {
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
"addPrefixTest": {
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest"},
|
||||
},
|
||||
"test": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Router with unknown middleware",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*config.Middleware{
|
||||
"auth": {
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"unknown"},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Router with broken middleware",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*config.Middleware{
|
||||
"auth": {
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth"},
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
entryPoints := []string{"web"}
|
||||
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: test.serviceConfig,
|
||||
Routers: test.routerConfig,
|
||||
Middlewares: test.middlewareConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(map[string]*config.MiddlewareInfo{})
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
// even though rtConf was passed by argument to the manager builders above,
|
||||
// it's ok to use it as the result we check, because everything worth checking
|
||||
// can be accessed by pointers in it.
|
||||
var allErrors int
|
||||
for _, v := range rtConf.Services {
|
||||
if v.Err != nil {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
for _, v := range rtConf.Routers {
|
||||
if v.Err != "" {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
for _, v := range rtConf.Middlewares {
|
||||
if v.Err != nil {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, test.expectedError, allErrors)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type staticTransport struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
@ -480,11 +796,17 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
}
|
||||
entryPoints := []string{"web"}
|
||||
|
||||
serviceManager := service.NewManager(serviceConfig, &staticTransport{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(map[string]*config.Middleware{}, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(map[string]*config.Middleware{})
|
||||
|
||||
routerManager := NewManager(routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: serviceConfig,
|
||||
Routers: routersConfig,
|
||||
Middlewares: map[string]*config.Middleware{},
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
|
@ -498,6 +820,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkService(b *testing.B) {
|
||||
res := &http.Response{
|
||||
StatusCode: 200,
|
||||
|
@ -518,7 +841,12 @@ func BenchmarkService(b *testing.B) {
|
|||
},
|
||||
}
|
||||
|
||||
serviceManager := service.NewManager(serviceConfig, &staticTransport{res})
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: serviceConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res})
|
||||
w := httptest.NewRecorder()
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package tcp
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
|
@ -14,14 +15,14 @@ import (
|
|||
)
|
||||
|
||||
// NewManager Creates a new Manager
|
||||
func NewManager(routers map[string]*config.TCPRouter,
|
||||
func NewManager(conf *config.RuntimeConfiguration,
|
||||
serviceManager *tcpservice.Manager,
|
||||
httpHandlers map[string]http.Handler,
|
||||
httpsHandlers map[string]http.Handler,
|
||||
tlsConfig *tls.Config,
|
||||
) *Manager {
|
||||
return &Manager{
|
||||
configs: routers,
|
||||
configs: conf.TCPRouters,
|
||||
serviceManager: serviceManager,
|
||||
httpHandlers: httpHandlers,
|
||||
httpsHandlers: httpsHandlers,
|
||||
|
@ -31,7 +32,7 @@ func NewManager(routers map[string]*config.TCPRouter,
|
|||
|
||||
// Manager is a route/router manager
|
||||
type Manager struct {
|
||||
configs map[string]*config.TCPRouter
|
||||
configs map[string]*config.TCPRouterInfo
|
||||
serviceManager *tcpservice.Manager
|
||||
httpHandlers map[string]http.Handler
|
||||
httpsHandlers map[string]http.Handler
|
||||
|
@ -60,7 +61,7 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
|
|||
return entryPointHandlers
|
||||
}
|
||||
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.TCPRouter, handlerHTTP http.Handler, handlerHTTPS http.Handler) (*tcp.Router, error) {
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.TCPRouterInfo, handlerHTTP http.Handler, handlerHTTPS http.Handler) (*tcp.Router, error) {
|
||||
router := &tcp.Router{}
|
||||
router.HTTPHandler(handlerHTTP)
|
||||
router.HTTPSHandler(handlerHTTPS, m.tlsConfig)
|
||||
|
@ -71,18 +72,21 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
|
||||
handler, err := m.serviceManager.BuildTCP(ctxRouter, routerConfig.Service)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
domains, err := rules.ParseHostSNI(routerConfig.Rule)
|
||||
if err != nil {
|
||||
logger.Debugf("Unknown rule %s", routerConfig.Rule)
|
||||
routerErr := fmt.Errorf("unknown rule %s", routerConfig.Rule)
|
||||
routerConfig.Err = routerErr.Error()
|
||||
logger.Debug(routerErr)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
logger.Debugf("Add route %s on TCP", domain)
|
||||
logger.Debugf("Adding route %s on TCP", domain)
|
||||
switch {
|
||||
case routerConfig.TLS != nil:
|
||||
if routerConfig.TLS.Passthrough {
|
||||
|
@ -101,8 +105,17 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
return router, nil
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map[string]map[string]*config.TCPRouter {
|
||||
entryPointsRouters := make(map[string]map[string]*config.TCPRouter)
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map[string]map[string]*config.TCPRouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*config.TCPRouterInfo)
|
||||
|
||||
for rtName, rt := range m.configs {
|
||||
eps := rt.EntryPoints
|
||||
|
@ -118,7 +131,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map
|
|||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.TCPRouter)
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.TCPRouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
|
@ -127,12 +140,3 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map
|
|||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
223
pkg/server/router/tcp/router_test.go
Normal file
223
pkg/server/router/tcp/router_test.go
Normal file
|
@ -0,0 +1,223 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/server/service/tcp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRuntimeConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serviceConfig map[string]*config.TCPServiceInfo
|
||||
routerConfig map[string]*config.TCPRouterInfo
|
||||
expectedError int
|
||||
}{
|
||||
{
|
||||
desc: "No error",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Port: "8085",
|
||||
Address: "127.0.0.1:8085",
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.1:8086",
|
||||
Port: "8086",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 0,
|
||||
},
|
||||
{
|
||||
desc: "One router with wrong rule",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "All router with wrong rule",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
{
|
||||
desc: "Router with unknown service",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "wrong-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "Router with broken service",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
entryPoints := []string{"web"}
|
||||
|
||||
conf := &config.RuntimeConfiguration{
|
||||
TCPServices: test.serviceConfig,
|
||||
TCPRouters: test.routerConfig,
|
||||
}
|
||||
serviceManager := tcp.NewManager(conf)
|
||||
routerManager := NewManager(conf, serviceManager,
|
||||
nil, nil, nil)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints)
|
||||
|
||||
// even though conf was passed by argument to the manager builders above,
|
||||
// it's ok to use it as the result we check, because everything worth checking
|
||||
// can be accessed by pointers in it.
|
||||
var allErrors int
|
||||
for _, v := range conf.TCPServices {
|
||||
if v.Err != nil {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
for _, v := range conf.TCPRouters {
|
||||
if v.Err != "" {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, test.expectedError, allErrors)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -50,7 +50,7 @@ type Server struct {
|
|||
|
||||
// RouteAppenderFactory the route appender factory interface
|
||||
type RouteAppenderFactory interface {
|
||||
NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, currentConfigurations *safe.Safe) types.RouteAppender
|
||||
NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, runtimeConfiguration *config.RuntimeConfiguration) types.RouteAppender
|
||||
}
|
||||
|
||||
func setupTracing(conf *static.Tracing) tracing.TrackingBackend {
|
||||
|
|
|
@ -69,47 +69,46 @@ func (s *Server) loadConfigurationTCP(configurations config.Configurations) map[
|
|||
|
||||
s.tlsManager.UpdateConfigs(conf.TLSStores, conf.TLSOptions, conf.TLS)
|
||||
|
||||
handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, *conf.HTTP, entryPoints)
|
||||
|
||||
routersTCP := s.createTCPRouters(ctx, conf.TCP, entryPoints, handlersNonTLS, handlersTLS, s.tlsManager.Get("default", "default"))
|
||||
rtConf := config.NewRuntimeConfig(conf)
|
||||
handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, rtConf, entryPoints)
|
||||
routersTCP := s.createTCPRouters(ctx, rtConf, entryPoints, handlersNonTLS, handlersTLS, s.tlsManager.Get("default", "default"))
|
||||
rtConf.PopulateUsedBy()
|
||||
|
||||
return routersTCP
|
||||
}
|
||||
|
||||
func (s *Server) createTCPRouters(ctx context.Context, configuration *config.TCPConfiguration, entryPoints []string, handlers map[string]http.Handler, handlersTLS map[string]http.Handler, tlsConfig *tls.Config) map[string]*tcpCore.Router {
|
||||
// the given configuration must not be nil. its fields will get mutated.
|
||||
func (s *Server) createTCPRouters(ctx context.Context, configuration *config.RuntimeConfiguration, entryPoints []string, handlers map[string]http.Handler, handlersTLS map[string]http.Handler, tlsConfig *tls.Config) map[string]*tcpCore.Router {
|
||||
if configuration == nil {
|
||||
return make(map[string]*tcpCore.Router)
|
||||
}
|
||||
|
||||
serviceManager := tcp.NewManager(configuration.Services)
|
||||
routerManager := routertcp.NewManager(configuration.Routers, serviceManager, handlers, handlersTLS, tlsConfig)
|
||||
serviceManager := tcp.NewManager(configuration)
|
||||
routerManager := routertcp.NewManager(configuration, serviceManager, handlers, handlersTLS, tlsConfig)
|
||||
|
||||
return routerManager.BuildHandlers(ctx, entryPoints)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) createHTTPHandlers(ctx context.Context, configuration config.HTTPConfiguration, entryPoints []string) (map[string]http.Handler, map[string]http.Handler) {
|
||||
// createHTTPHandlers returns, for the given configuration and entryPoints, the HTTP handlers for non-TLS connections, and for the TLS ones. the given configuration must not be nil. its fields will get mutated.
|
||||
func (s *Server) createHTTPHandlers(ctx context.Context, configuration *config.RuntimeConfiguration, entryPoints []string) (map[string]http.Handler, map[string]http.Handler) {
|
||||
serviceManager := service.NewManager(configuration.Services, s.defaultRoundTripper)
|
||||
middlewaresBuilder := middleware.NewBuilder(configuration.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(configuration.Middlewares)
|
||||
|
||||
routerManager := router.NewManager(configuration.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlersNonTLS := routerManager.BuildHandlers(ctx, entryPoints, false)
|
||||
handlersTLS := routerManager.BuildHandlers(ctx, entryPoints, true)
|
||||
|
||||
routerHandlers := make(map[string]http.Handler)
|
||||
|
||||
for _, entryPointName := range entryPoints {
|
||||
internalMuxRouter := mux.NewRouter().
|
||||
SkipClean(true)
|
||||
internalMuxRouter := mux.NewRouter().SkipClean(true)
|
||||
|
||||
ctx = log.With(ctx, log.Str(log.EntryPointName, entryPointName))
|
||||
|
||||
factory := s.entryPointsTCP[entryPointName].RouteAppenderFactory
|
||||
if factory != nil {
|
||||
// FIXME remove currentConfigurations
|
||||
appender := factory.NewAppender(ctx, middlewaresBuilder, &s.currentConfigurations)
|
||||
appender := factory.NewAppender(ctx, middlewaresBuilder, configuration)
|
||||
appender.Append(internalMuxRouter)
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ func TestReuseService(t *testing.T) {
|
|||
|
||||
srv := NewServer(staticConfig, nil, entryPoints, nil)
|
||||
|
||||
entrypointsHandlers, _ := srv.createHTTPHandlers(context.Background(), *dynamicConfigs, []string{"http"})
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{HTTP: dynamicConfigs})
|
||||
entrypointsHandlers, _ := srv.createHTTPHandlers(context.Background(), rtConf, []string{"http"})
|
||||
|
||||
// Test that the /ok path returns a status 200.
|
||||
responseRecorderOk := &httptest.ResponseRecorder{}
|
||||
|
|
|
@ -258,7 +258,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||
}
|
||||
|
||||
srv := NewServer(globalConfig, nil, entryPointsConfig, nil)
|
||||
entryPoints, _ := srv.createHTTPHandlers(context.Background(), *test.config(testServer.URL), []string{"http"})
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{HTTP: test.config(testServer.URL)})
|
||||
entryPoints, _ := srv.createHTTPHandlers(context.Background(), rtConf, []string{"http"})
|
||||
|
||||
responseRecorder := &httptest.ResponseRecorder{}
|
||||
request := httptest.NewRequest(http.MethodGet, testServer.URL+requestPath, nil)
|
||||
|
|
|
@ -26,7 +26,7 @@ const (
|
|||
)
|
||||
|
||||
// NewManager creates a new Manager
|
||||
func NewManager(configs map[string]*config.Service, defaultRoundTripper http.RoundTripper) *Manager {
|
||||
func NewManager(configs map[string]*config.ServiceInfo, defaultRoundTripper http.RoundTripper) *Manager {
|
||||
return &Manager{
|
||||
bufferPool: newBufferPool(),
|
||||
defaultRoundTripper: defaultRoundTripper,
|
||||
|
@ -40,7 +40,7 @@ type Manager struct {
|
|||
bufferPool httputil.BufferPool
|
||||
defaultRoundTripper http.RoundTripper
|
||||
balancers map[string][]healthcheck.BalancerHandler
|
||||
configs map[string]*config.Service
|
||||
configs map[string]*config.ServiceInfo
|
||||
}
|
||||
|
||||
// BuildHTTP Creates a http.Handler for a service configuration.
|
||||
|
@ -50,15 +50,25 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
|||
serviceName = internal.GetQualifiedName(ctx, serviceName)
|
||||
ctx = internal.AddProviderInContext(ctx, serviceName)
|
||||
|
||||
if conf, ok := m.configs[serviceName]; ok {
|
||||
// TODO Should handle multiple service types
|
||||
// FIXME Check if the service is declared multiple times with different types
|
||||
if conf.LoadBalancer != nil {
|
||||
return m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
|
||||
}
|
||||
return nil, fmt.Errorf("the service %q doesn't have any load balancer", serviceName)
|
||||
conf, ok := m.configs[serviceName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("the service %q does not exist", serviceName)
|
||||
}
|
||||
return nil, fmt.Errorf("the service %q does not exits", serviceName)
|
||||
|
||||
// TODO Should handle multiple service types
|
||||
// FIXME Check if the service is declared multiple times with different types
|
||||
if conf.LoadBalancer == nil {
|
||||
conf.Err = fmt.Errorf("the service %q doesn't have any load balancer", serviceName)
|
||||
return nil, conf.Err
|
||||
}
|
||||
|
||||
lb, err := m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
|
||||
if err != nil {
|
||||
conf.Err = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lb, nil
|
||||
}
|
||||
|
||||
func (m *Manager) getLoadBalancerServiceHandler(
|
||||
|
@ -158,7 +168,7 @@ func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler
|
|||
}
|
||||
|
||||
if timeout >= interval {
|
||||
logger.Warnf("Health check timeout for backend '%s' should be lower than the health check interval. Interval set to timeout + 1 second (%s).", backend)
|
||||
logger.Warnf("Health check timeout for backend '%s' should be lower than the health check interval. Interval set to timeout + 1 second (%s).", backend, interval)
|
||||
}
|
||||
|
||||
return &healthcheck.Options{
|
||||
|
@ -229,7 +239,8 @@ func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, servi
|
|||
}
|
||||
}
|
||||
|
||||
if err := m.upsertServers(ctx, lb, service.Servers); err != nil {
|
||||
lbsu := healthcheck.NewLBStatusUpdater(lb, m.configs[serviceName])
|
||||
if err := m.upsertServers(ctx, lbsu, service.Servers); err != nil {
|
||||
return nil, fmt.Errorf("error configuring load balancer for service %s: %v", serviceName, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -275,33 +275,39 @@ func TestManager_Build(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
serviceName string
|
||||
configs map[string]*config.Service
|
||||
configs map[string]*config.ServiceInfo
|
||||
providerName string
|
||||
}{
|
||||
{
|
||||
desc: "Simple service name",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.Service{
|
||||
configs: map[string]*config.ServiceInfo{
|
||||
"serviceName": {
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider",
|
||||
serviceName: "provider-1.serviceName",
|
||||
configs: map[string]*config.Service{
|
||||
configs: map[string]*config.ServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider in context",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.Service{
|
||||
configs: map[string]*config.ServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
|
|
|
@ -13,13 +13,13 @@ import (
|
|||
|
||||
// Manager is the TCPHandlers factory
|
||||
type Manager struct {
|
||||
configs map[string]*config.TCPService
|
||||
configs map[string]*config.TCPServiceInfo
|
||||
}
|
||||
|
||||
// NewManager creates a new manager
|
||||
func NewManager(configs map[string]*config.TCPService) *Manager {
|
||||
func NewManager(conf *config.RuntimeConfiguration) *Manager {
|
||||
return &Manager{
|
||||
configs: configs,
|
||||
configs: conf.TCPServices,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,23 +29,23 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
|
|||
ctx := internal.AddProviderInContext(rootCtx, serviceQualifiedName)
|
||||
ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
|
||||
|
||||
// FIXME Check if the service is declared multiple times with different types
|
||||
conf, ok := m.configs[serviceQualifiedName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("the service %q does not exits", serviceQualifiedName)
|
||||
return nil, fmt.Errorf("the service %q does not exist", serviceQualifiedName)
|
||||
}
|
||||
|
||||
if conf.LoadBalancer == nil {
|
||||
return nil, fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
|
||||
conf.Err = fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
|
||||
return nil, conf.Err
|
||||
}
|
||||
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// FIXME Check if the service is declared multiple times with different types
|
||||
loadBalancer := tcp.NewRRLoadBalancer()
|
||||
|
||||
for _, server := range conf.LoadBalancer.Servers {
|
||||
if _, err := parseIP(server.Address); err != nil {
|
||||
logger.Errorf("Invalid IP address for a %q server %q: %v", serviceQualifiedName, server.Address, err)
|
||||
if _, _, err := net.SplitHostPort(server.Address); err != nil {
|
||||
logger.Errorf("In service %q: %v", serviceQualifiedName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -57,20 +57,5 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
|
|||
|
||||
loadBalancer.AddServer(handler)
|
||||
}
|
||||
|
||||
return loadBalancer, nil
|
||||
}
|
||||
|
||||
func parseIP(s string) (string, error) {
|
||||
ip, _, err := net.SplitHostPort(s)
|
||||
if err == nil {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
ipNoPort := net.ParseIP(s)
|
||||
if ipNoPort == nil {
|
||||
return "", fmt.Errorf("invalid IP Address %s", ipNoPort)
|
||||
}
|
||||
|
||||
return ipNoPort.String(), nil
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/server/internal"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -12,49 +14,166 @@ func TestManager_BuildTCP(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
serviceName string
|
||||
configs map[string]*config.TCPService
|
||||
configs map[string]*config.TCPServiceInfo
|
||||
providerName string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
desc: "without configuration",
|
||||
serviceName: "test",
|
||||
configs: nil,
|
||||
expectedError: `the service "test" does not exits`,
|
||||
expectedError: `the service "test" does not exist`,
|
||||
},
|
||||
{
|
||||
desc: "missing lb configuration",
|
||||
serviceName: "test",
|
||||
configs: map[string]*config.TCPService{
|
||||
"test": {},
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"test": {
|
||||
TCPService: &config.TCPService{},
|
||||
},
|
||||
},
|
||||
expectedError: `the service "test" doesn't have any TCP load balancer`,
|
||||
},
|
||||
{
|
||||
desc: "no such host",
|
||||
desc: "no such host, server is skipped, error is logged",
|
||||
serviceName: "test",
|
||||
configs: map[string]*config.TCPService{
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"test": {
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{Address: "test:31"},
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{Address: "test:31"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid IP address",
|
||||
desc: "invalid IP address, server is skipped, error is logged",
|
||||
serviceName: "test",
|
||||
configs: map[string]*config.TCPService{
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"test": {
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{Address: "foobar"},
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{Address: "foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Simple service name",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider",
|
||||
serviceName: "provider-1.serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider in context",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "Server with correct host:port as address",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "foobar.com:80",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "Server with correct ip:port as address",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "192.168.0.12:80",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "missing port in address with hostname, server is skipped, error is logged",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "foobar.com",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "missing port in address with ip, server is skipped, error is logged",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "192.168.0.12",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@ -62,19 +181,22 @@ func TestManager_BuildTCP(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
manager := NewManager(test.configs)
|
||||
manager := NewManager(&config.RuntimeConfiguration{
|
||||
TCPServices: test.configs,
|
||||
})
|
||||
|
||||
handler, err := manager.BuildTCP(context.Background(), test.serviceName)
|
||||
ctx := context.Background()
|
||||
if len(test.providerName) > 0 {
|
||||
ctx = internal.AddProviderInContext(ctx, test.providerName+".foobar")
|
||||
}
|
||||
|
||||
handler, err := manager.BuildTCP(ctx, test.serviceName)
|
||||
|
||||
if test.expectedError != "" {
|
||||
if err == nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expectedError)
|
||||
require.Nil(t, handler)
|
||||
}
|
||||
assert.EqualError(t, err, test.expectedError)
|
||||
require.Nil(t, handler)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, handler)
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue