Extract internal router creation from server

This commit is contained in:
SALLEYRON Julien 2018-04-23 15:30:03 +02:00 committed by Traefiker Bot
parent 05968eb232
commit 9daae9c705
8 changed files with 697 additions and 254 deletions

View file

@ -24,6 +24,7 @@ import (
"github.com/containous/mux"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/configuration/router"
"github.com/containous/traefik/healthcheck"
"github.com/containous/traefik/log"
"github.com/containous/traefik/metrics"
@ -75,6 +76,13 @@ type Server struct {
metricsRegistry metrics.Registry
provider provider.Provider
configurationListeners []func(types.Configuration)
entryPoints map[string]EntryPoint
}
// EntryPoint entryPoint information (configuration + internalRouter)
type EntryPoint struct {
InternalRouter types.InternalRouter
Configuration *configuration.EntryPoint
}
type serverEntryPoints map[string]*serverEntryPoint
@ -88,9 +96,10 @@ type serverEntryPoint struct {
}
// NewServer returns an initialized Server.
func NewServer(globalConfiguration configuration.GlobalConfiguration, provider provider.Provider) *Server {
func NewServer(globalConfiguration configuration.GlobalConfiguration, provider provider.Provider, entrypoints map[string]EntryPoint) *Server {
server := new(Server)
server.entryPoints = entrypoints
server.provider = provider
server.serverEntryPoints = make(map[string]*serverEntryPoint)
server.configurationChan = make(chan types.ConfigMessage, 100)
@ -210,9 +219,6 @@ func (s *Server) StartWithContext(ctx context.Context) {
<-ctx.Done()
log.Info("I have to go...")
reqAcceptGraceTimeOut := time.Duration(s.globalConfiguration.LifeCycle.RequestAcceptGraceTimeout)
if s.globalConfiguration.Ping != nil && reqAcceptGraceTimeOut > 0 {
s.globalConfiguration.Ping.SetTerminating()
}
if reqAcceptGraceTimeOut > 0 {
log.Infof("Waiting %s for incoming requests to cease", reqAcceptGraceTimeOut)
time.Sleep(reqAcceptGraceTimeOut)
@ -291,17 +297,16 @@ func (s *Server) stopLeadership() {
}
func (s *Server) startHTTPServers() {
s.serverEntryPoints = s.buildEntryPoints(s.globalConfiguration)
s.serverEntryPoints = s.buildEntryPoints()
for newServerEntryPointName, newServerEntryPoint := range s.serverEntryPoints {
serverEntryPoint := s.setupServerEntryPoint(newServerEntryPointName, newServerEntryPoint)
go s.startServer(serverEntryPoint, s.globalConfiguration)
go s.startServer(serverEntryPoint)
}
}
func (s *Server) setupServerEntryPoint(newServerEntryPointName string, newServerEntryPoint *serverEntryPoint) *serverEntryPoint {
serverMiddlewares := []negroni.Handler{middlewares.NegroniRecoverHandler()}
serverInternalMiddlewares := []negroni.Handler{middlewares.NegroniRecoverHandler()}
if s.tracingMiddleware.IsEnabled() {
serverMiddlewares = append(serverMiddlewares, s.tracingMiddleware.NewEntryPoint(newServerEntryPointName))
@ -310,9 +315,11 @@ func (s *Server) setupServerEntryPoint(newServerEntryPointName string, newServer
if s.accessLoggerMiddleware != nil {
serverMiddlewares = append(serverMiddlewares, s.accessLoggerMiddleware)
}
if s.metricsRegistry.IsEnabled() {
serverMiddlewares = append(serverMiddlewares, middlewares.NewEntryPointMetricsMiddleware(s.metricsRegistry, newServerEntryPointName))
}
if s.globalConfiguration.API != nil {
if s.globalConfiguration.API.Stats == nil {
s.globalConfiguration.API.Stats = thoas_stats.New()
@ -326,34 +333,33 @@ func (s *Server) setupServerEntryPoint(newServerEntryPointName string, newServer
}
}
if s.globalConfiguration.EntryPoints[newServerEntryPointName].Auth != nil {
authMiddleware, err := mauth.NewAuthenticator(s.globalConfiguration.EntryPoints[newServerEntryPointName].Auth, s.tracingMiddleware)
if s.entryPoints[newServerEntryPointName].Configuration.Auth != nil {
authMiddleware, err := mauth.NewAuthenticator(s.entryPoints[newServerEntryPointName].Configuration.Auth, s.tracingMiddleware)
if err != nil {
log.Fatal("Error starting server: ", err)
}
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(authMiddleware, fmt.Sprintf("Auth for entrypoint %s", newServerEntryPointName)))
serverInternalMiddlewares = append(serverInternalMiddlewares, authMiddleware)
}
if s.globalConfiguration.EntryPoints[newServerEntryPointName].Compress {
if s.entryPoints[newServerEntryPointName].Configuration.Compress {
serverMiddlewares = append(serverMiddlewares, &middlewares.Compress{})
}
ipWhitelistMiddleware, err := buildIPWhiteLister(
s.globalConfiguration.EntryPoints[newServerEntryPointName].WhiteList,
s.globalConfiguration.EntryPoints[newServerEntryPointName].WhitelistSourceRange)
s.entryPoints[newServerEntryPointName].Configuration.WhiteList,
s.entryPoints[newServerEntryPointName].Configuration.WhitelistSourceRange)
if err != nil {
log.Fatal("Error starting server: ", err)
}
if ipWhitelistMiddleware != nil {
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(ipWhitelistMiddleware, fmt.Sprintf("ipwhitelister for entrypoint %s", newServerEntryPointName)))
serverInternalMiddlewares = append(serverInternalMiddlewares, ipWhitelistMiddleware)
}
newSrv, listener, err := s.prepareServer(newServerEntryPointName, s.globalConfiguration.EntryPoints[newServerEntryPointName], newServerEntryPoint.httpRouter, serverMiddlewares, serverInternalMiddlewares)
newSrv, listener, err := s.prepareServer(newServerEntryPointName, s.entryPoints[newServerEntryPointName].Configuration, newServerEntryPoint.httpRouter, serverMiddlewares)
if err != nil {
log.Fatal("Error preparing server: ", err)
}
serverEntryPoint := s.serverEntryPoints[newServerEntryPointName]
serverEntryPoint.httpServer = newSrv
serverEntryPoint.listener = listener
@ -467,7 +473,7 @@ func (s *Server) loadConfiguration(configMsg types.ConfigMessage) {
s.metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
for newServerEntryPointName, newServerEntryPoint := range newServerEntryPoints {
s.serverEntryPoints[newServerEntryPointName].httpRouter.UpdateHandler(newServerEntryPoint.httpRouter.GetHandler())
if s.globalConfiguration.EntryPoints[newServerEntryPointName].TLS == nil {
if s.entryPoints[newServerEntryPointName].Configuration.TLS == nil {
if newServerEntryPoint.certs.Get() != nil {
log.Debugf("Certificates not added to non-TLS entryPoint %s.", newServerEntryPointName)
}
@ -550,7 +556,7 @@ func (s *Server) postLoadConfiguration() {
// and is configured with ACME
acmeEnabled := false
for _, entryPoint := range frontend.EntryPoints {
if s.globalConfiguration.ACME.EntryPoint == entryPoint && s.globalConfiguration.EntryPoints[entryPoint].TLS != nil {
if s.globalConfiguration.ACME.EntryPoint == entryPoint && s.entryPoints[entryPoint].Configuration.TLS != nil {
acmeEnabled = true
break
}
@ -665,8 +671,7 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
if entryPointName == s.globalConfiguration.ACME.EntryPoint {
checkOnDemandDomain := func(domain string) bool {
routeMatch := &mux.RouteMatch{}
router := router.GetHandler()
match := router.Match(&http.Request{URL: &url.URL{}, Host: domain}, routeMatch)
match := router.GetHandler().Match(&http.Request{URL: &url.URL{}, Host: domain}, routeMatch)
if match && routeMatch.Route != nil {
return true
}
@ -699,15 +704,16 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
}
// Set the minimum TLS version if set in the config TOML
if minConst, exists := traefiktls.MinVersion[s.globalConfiguration.EntryPoints[entryPointName].TLS.MinVersion]; exists {
if minConst, exists := traefiktls.MinVersion[s.entryPoints[entryPointName].Configuration.TLS.MinVersion]; exists {
config.PreferServerCipherSuites = true
config.MinVersion = minConst
}
// Set the list of CipherSuites if set in the config TOML
if s.globalConfiguration.EntryPoints[entryPointName].TLS.CipherSuites != nil {
if s.entryPoints[entryPointName].Configuration.TLS.CipherSuites != nil {
// if our list of CipherSuites is defined in the entrypoint config, we can re-initilize the suites list as empty
config.CipherSuites = make([]uint16, 0)
for _, cipher := range s.globalConfiguration.EntryPoints[entryPointName].TLS.CipherSuites {
for _, cipher := range s.entryPoints[entryPointName].Configuration.TLS.CipherSuites {
if cipherConst, exists := traefiktls.CipherSuites[cipher]; exists {
config.CipherSuites = append(config.CipherSuites, cipherConst)
} else {
@ -719,7 +725,7 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
return config, nil
}
func (s *Server) startServer(serverEntryPoint *serverEntryPoint, globalConfiguration configuration.GlobalConfiguration) {
func (s *Server) startServer(serverEntryPoint *serverEntryPoint) {
log.Infof("Starting server on %s", serverEntryPoint.httpServer.Addr)
var err error
if serverEntryPoint.httpServer.TLSConfig != nil {
@ -732,39 +738,7 @@ func (s *Server) startServer(serverEntryPoint *serverEntryPoint, globalConfigura
}
}
func (s *Server) addInternalRoutes(entryPointName string, router *mux.Router) {
if s.globalConfiguration.Metrics != nil && s.globalConfiguration.Metrics.Prometheus != nil && s.globalConfiguration.Metrics.Prometheus.EntryPoint == entryPointName {
metrics.PrometheusHandler{}.AddRoutes(router)
}
if s.globalConfiguration.Rest != nil && s.globalConfiguration.Rest.EntryPoint == entryPointName {
s.globalConfiguration.Rest.AddRoutes(router)
}
if s.globalConfiguration.API != nil && s.globalConfiguration.API.EntryPoint == entryPointName {
s.globalConfiguration.API.AddRoutes(router)
}
}
func (s *Server) addInternalPublicRoutes(entryPointName string, router *mux.Router) {
if s.globalConfiguration.Ping != nil && s.globalConfiguration.Ping.EntryPoint != "" && s.globalConfiguration.Ping.EntryPoint == entryPointName {
s.globalConfiguration.Ping.AddRoutes(router)
}
if s.globalConfiguration.API != nil && s.globalConfiguration.API.EntryPoint == entryPointName && s.leadership != nil {
s.leadership.AddRoutes(router)
}
}
func (s *Server) addACMERoutes(entryPointName string, router *mux.Router) {
if s.globalConfiguration.ACME != nil && s.globalConfiguration.ACME.HTTPChallenge != nil && s.globalConfiguration.ACME.HTTPChallenge.EntryPoint == entryPointName {
s.globalConfiguration.ACME.AddRoutes(router)
} else if acme.IsEnabled() && acme.Get().HTTPChallenge != nil && acme.Get().HTTPChallenge.EntryPoint == entryPointName {
acme.Get().AddRoutes(router)
}
}
func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.EntryPoint, router *middlewares.HandlerSwitcher, middlewares []negroni.Handler, internalMiddlewares []negroni.Handler) (*http.Server, net.Listener, error) {
func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.EntryPoint, router *middlewares.HandlerSwitcher, middlewares []negroni.Handler) (*http.Server, net.Listener, error) {
readTimeout, writeTimeout, idleTimeout := buildServerTimeouts(s.globalConfiguration)
log.Infof("Preparing server %s %+v with readTimeout=%s writeTimeout=%s idleTimeout=%s", entryPointName, entryPoint, readTimeout, writeTimeout, idleTimeout)
@ -775,12 +749,7 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.
}
n.UseHandler(router)
path := "/"
if s.globalConfiguration.Web != nil && s.globalConfiguration.Web.Path != "" {
path = s.globalConfiguration.Web.Path
}
internalMuxRouter := s.buildInternalRouter(entryPointName, path, internalMiddlewares)
internalMuxRouter := s.buildInternalRouter(entryPointName)
internalMuxRouter.NotFoundHandler = n
tlsConfig, err := s.createTLSConfig(entryPointName, entryPoint.TLS, router)
@ -826,34 +795,27 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.
nil
}
func (s *Server) buildInternalRouter(entryPointName, path string, internalMiddlewares []negroni.Handler) *mux.Router {
func (s *Server) buildInternalRouter(entryPointName string) *mux.Router {
internalMuxRouter := mux.NewRouter()
internalMuxRouter.StrictSlash(true)
internalMuxRouter.SkipClean(true)
internalMuxSubrouter := internalMuxRouter.PathPrefix(path).Subrouter()
internalMuxSubrouter.StrictSlash(true)
internalMuxSubrouter.SkipClean(true)
if entryPoint, ok := s.entryPoints[entryPointName]; ok && entryPoint.InternalRouter != nil {
entryPoint.InternalRouter.AddRoutes(internalMuxRouter)
s.addInternalRoutes(entryPointName, internalMuxSubrouter)
internalMuxRouter.Walk(wrapRoute(internalMiddlewares))
s.addInternalPublicRoutes(entryPointName, internalMuxSubrouter)
s.addACMERoutes(entryPointName, internalMuxRouter)
if s.globalConfiguration.API != nil && s.globalConfiguration.API.EntryPoint == entryPointName && s.leadership != nil {
if s.globalConfiguration.Web != nil && s.globalConfiguration.Web.Path != "" {
rt := router.WithPrefix{Router: s.leadership, PathPrefix: s.globalConfiguration.Web.Path}
rt.AddRoutes(internalMuxRouter)
} else {
s.leadership.AddRoutes(internalMuxRouter)
}
}
}
return internalMuxRouter
}
// wrapRoute with middlewares
func wrapRoute(middlewares []negroni.Handler) func(*mux.Route, *mux.Router, []*mux.Route) error {
return func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
middles := append(middlewares, negroni.Wrap(route.GetHandler()))
route.Handler(negroni.New(middles...))
return nil
}
}
func buildServerTimeouts(globalConfig configuration.GlobalConfiguration) (readTimeout, writeTimeout, idleTimeout time.Duration) {
readTimeout = time.Duration(0)
writeTimeout = time.Duration(0)
@ -875,12 +837,11 @@ func buildServerTimeouts(globalConfig configuration.GlobalConfiguration) (readTi
return readTimeout, writeTimeout, idleTimeout
}
func (s *Server) buildEntryPoints(globalConfiguration configuration.GlobalConfiguration) map[string]*serverEntryPoint {
func (s *Server) buildEntryPoints() map[string]*serverEntryPoint {
serverEntryPoints := make(map[string]*serverEntryPoint)
for entryPointName := range globalConfiguration.EntryPoints {
router := s.buildDefaultHTTPRouter()
for entryPointName := range s.entryPoints {
serverEntryPoints[entryPointName] = &serverEntryPoint{
httpRouter: middlewares.NewHandlerSwitcher(router),
httpRouter: middlewares.NewHandlerSwitcher(s.buildDefaultHTTPRouter()),
}
}
return serverEntryPoints
@ -907,7 +868,7 @@ func (s *Server) getRoundTripper(entryPointName string, globalConfiguration conf
// loadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
// provider configurations.
func (s *Server) loadConfig(configurations types.Configurations, globalConfiguration configuration.GlobalConfiguration) (map[string]*serverEntryPoint, error) {
serverEntryPoints := s.buildEntryPoints(globalConfiguration)
serverEntryPoints := s.buildEntryPoints()
redirectHandlers := make(map[string]negroni.Handler)
backends := map[string]http.Handler{}
backendsHealthCheck := map[string]*healthcheck.BackendHealthCheck{}
@ -952,7 +913,7 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
log.Debugf("Creating route %s %s", routeName, route.Rule)
}
entryPoint := globalConfiguration.EntryPoints[entryPointName]
entryPoint := s.entryPoints[entryPointName].Configuration
n := negroni.New()
if entryPoint.Redirect != nil && entryPointName != entryPoint.Redirect.EntryPoint {
if redirectHandlers[entryPointName] != nil {
@ -1341,7 +1302,7 @@ func (s *Server) wireFrontendBackend(serverRoute *types.ServerRoute, handler htt
func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redirect) (negroni.Handler, error) {
// entry point redirect
if len(opt.EntryPoint) > 0 {
entryPoint := s.globalConfiguration.EntryPoints[opt.EntryPoint]
entryPoint := s.entryPoints[opt.EntryPoint].Configuration
if entryPoint == nil {
return nil, fmt.Errorf("unknown target entrypoint %q", srcEntryPointName)
}
@ -1360,11 +1321,11 @@ func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redir
}
func (s *Server) buildDefaultHTTPRouter() *mux.Router {
router := mux.NewRouter()
router.NotFoundHandler = s.wrapHTTPHandlerWithAccessLog(http.HandlerFunc(notFoundHandler), "backend not found")
router.StrictSlash(true)
router.SkipClean(true)
return router
rt := mux.NewRouter()
rt.NotFoundHandler = s.wrapHTTPHandlerWithAccessLog(http.HandlerFunc(notFoundHandler), "backend not found")
rt.StrictSlash(true)
rt.SkipClean(true)
return rt
}
func parseHealthCheckOptions(lb healthcheck.LoadBalancer, backend string, hc *types.HealthCheck, hcConfig *configuration.HealthCheckConfig) *healthcheck.Options {