2016-01-13 21:45:49 +00:00
/ *
Copyright
* /
package main
import (
"crypto/tls"
2016-01-29 19:34:17 +00:00
"encoding/json"
2016-01-13 21:45:49 +00:00
"errors"
2016-02-26 14:29:53 +00:00
"net/http"
"net/url"
"os"
"os/signal"
"reflect"
"regexp"
"sort"
"syscall"
"time"
2016-02-19 22:55:23 +00:00
2016-01-13 21:45:49 +00:00
log "github.com/Sirupsen/logrus"
"github.com/codegangsta/negroni"
2016-06-03 15:58:33 +00:00
"github.com/containous/mux"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/provider"
2016-03-31 16:57:08 +00:00
"github.com/containous/traefik/safe"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/types"
2016-01-13 21:45:49 +00:00
"github.com/mailgun/manners"
2016-04-13 18:36:23 +00:00
"github.com/streamrail/concurrent-map"
2016-06-15 17:07:33 +00:00
"github.com/vulcand/oxy/cbreaker"
"github.com/vulcand/oxy/connlimit"
"github.com/vulcand/oxy/forward"
"github.com/vulcand/oxy/roundrobin"
"github.com/vulcand/oxy/utils"
2016-01-13 21:45:49 +00:00
)
2016-04-20 18:54:57 +00:00
var oxyLogger = & OxyLogger { }
2016-01-13 21:46:44 +00:00
2016-01-13 21:45:49 +00:00
// Server is the reverse-proxy/load-balancer engine
type Server struct {
2016-02-25 17:30:13 +00:00
serverEntryPoints serverEntryPoints
2016-01-13 21:45:49 +00:00
configurationChan chan types . ConfigMessage
2016-01-29 19:34:17 +00:00
configurationValidatedChan chan types . ConfigMessage
signals chan os . Signal
2016-01-13 21:45:49 +00:00
stopChan chan bool
providers [ ] provider . Provider
2016-04-13 18:36:23 +00:00
currentConfigurations safe . Safe
2016-01-13 21:45:49 +00:00
globalConfiguration GlobalConfiguration
loggerMiddleware * middlewares . Logger
2016-04-13 18:36:23 +00:00
routinesPool safe . Pool
2016-01-13 21:45:49 +00:00
}
2016-02-25 17:30:13 +00:00
type serverEntryPoints map [ string ] * serverEntryPoint
2016-01-29 19:34:17 +00:00
type serverEntryPoint struct {
httpServer * manners . GracefulServer
2016-03-04 10:32:23 +00:00
httpRouter * middlewares . HandlerSwitcher
2016-01-29 19:34:17 +00:00
}
2016-03-27 00:05:17 +00:00
type serverRoute struct {
2016-04-06 11:06:31 +00:00
route * mux . Route
stripPrefixes [ ] string
2016-03-27 00:05:17 +00:00
}
2016-01-13 21:45:49 +00:00
// NewServer returns an initialized Server.
func NewServer ( globalConfiguration GlobalConfiguration ) * Server {
server := new ( Server )
2016-02-25 17:30:13 +00:00
server . serverEntryPoints = make ( map [ string ] * serverEntryPoint )
2016-05-19 18:09:01 +00:00
server . configurationChan = make ( chan types . ConfigMessage , 100 )
server . configurationValidatedChan = make ( chan types . ConfigMessage , 100 )
2016-01-29 19:34:17 +00:00
server . signals = make ( chan os . Signal , 1 )
2016-04-13 18:36:23 +00:00
server . stopChan = make ( chan bool , 1 )
2016-01-13 21:45:49 +00:00
server . providers = [ ] provider . Provider { }
2016-01-29 19:34:17 +00:00
signal . Notify ( server . signals , syscall . SIGINT , syscall . SIGTERM )
2016-04-13 18:36:23 +00:00
currentConfigurations := make ( configs )
server . currentConfigurations . Set ( currentConfigurations )
2016-01-13 21:45:49 +00:00
server . globalConfiguration = globalConfiguration
server . loggerMiddleware = middlewares . NewLogger ( globalConfiguration . AccessLogsFile )
return server
}
// Start starts the server and blocks until server is shutted down.
func ( server * Server ) Start ( ) {
2016-02-25 17:30:13 +00:00
server . startHTTPServers ( )
2016-04-13 18:36:23 +00:00
server . routinesPool . Go ( func ( stop chan bool ) {
server . listenProviders ( stop )
2016-03-31 16:57:08 +00:00
} )
2016-04-13 18:36:23 +00:00
server . routinesPool . Go ( func ( stop chan bool ) {
server . listenConfigurations ( stop )
2016-03-31 16:57:08 +00:00
} )
2016-01-13 21:45:49 +00:00
server . configureProviders ( )
server . startProviders ( )
go server . listenSignals ( )
<- server . stopChan
}
// Stop stops the server
func ( server * Server ) Stop ( ) {
2016-01-29 19:34:17 +00:00
for _ , serverEntryPoint := range server . serverEntryPoints {
2016-03-04 10:32:23 +00:00
serverEntryPoint . httpServer . BlockingClose ( )
2016-01-29 19:34:17 +00:00
}
2016-01-13 21:45:49 +00:00
server . stopChan <- true
}
// Close destroys the server
func ( server * Server ) Close ( ) {
2016-04-13 18:36:23 +00:00
server . routinesPool . Stop ( )
2016-01-13 21:46:44 +00:00
close ( server . configurationChan )
2016-01-29 19:34:17 +00:00
close ( server . configurationValidatedChan )
2016-07-14 08:22:18 +00:00
signal . Stop ( server . signals )
2016-01-29 19:34:17 +00:00
close ( server . signals )
2016-01-13 21:46:44 +00:00
close ( server . stopChan )
server . loggerMiddleware . Close ( )
2016-01-13 21:45:49 +00:00
}
2016-02-25 17:30:13 +00:00
func ( server * Server ) startHTTPServers ( ) {
server . serverEntryPoints = server . buildEntryPoints ( server . globalConfiguration )
for newServerEntryPointName , newServerEntryPoint := range server . serverEntryPoints {
2016-03-21 10:10:18 +00:00
newsrv , err := server . prepareServer ( newServerEntryPointName , newServerEntryPoint . httpRouter , server . globalConfiguration . EntryPoints [ newServerEntryPointName ] , nil , server . loggerMiddleware , metrics )
2016-02-25 17:30:13 +00:00
if err != nil {
log . Fatal ( "Error preparing server: " , err )
}
serverEntryPoint := server . serverEntryPoints [ newServerEntryPointName ]
serverEntryPoint . httpServer = newsrv
go server . startServer ( serverEntryPoint . httpServer , server . globalConfiguration )
}
}
2016-04-13 18:36:23 +00:00
func ( server * Server ) listenProviders ( stop chan bool ) {
lastReceivedConfiguration := safe . New ( time . Unix ( 0 , 0 ) )
lastConfigs := cmap . New ( )
2016-01-13 21:45:49 +00:00
for {
2016-04-13 18:36:23 +00:00
select {
case <- stop :
return
case configMsg , ok := <- server . configurationChan :
if ! ok {
return
}
2016-06-22 16:31:14 +00:00
server . defaultConfigurationValues ( configMsg . Configuration )
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
2016-04-13 18:36:23 +00:00
jsonConf , _ := json . Marshal ( configMsg . Configuration )
log . Debugf ( "Configuration received from provider %s: %s" , configMsg . ProviderName , string ( jsonConf ) )
2016-06-22 16:31:14 +00:00
if configMsg . Configuration == nil || configMsg . Configuration . Backends == nil && configMsg . Configuration . Frontends == nil {
log . Infof ( "Skipping empty Configuration for provider %s" , configMsg . ProviderName )
} else if reflect . DeepEqual ( currentConfigurations [ configMsg . ProviderName ] , configMsg . Configuration ) {
log . Infof ( "Skipping same configuration for provider %s" , configMsg . ProviderName )
2016-04-13 18:36:23 +00:00
} else {
2016-06-22 16:31:14 +00:00
lastConfigs . Set ( configMsg . ProviderName , & configMsg )
lastReceivedConfigurationValue := lastReceivedConfiguration . Get ( ) . ( time . Time )
if time . Now ( ) . After ( lastReceivedConfigurationValue . Add ( time . Duration ( server . globalConfiguration . ProvidersThrottleDuration ) ) ) {
log . Debugf ( "Last %s config received more than %s, OK" , configMsg . ProviderName , server . globalConfiguration . ProvidersThrottleDuration )
// last config received more than n s ago
server . configurationValidatedChan <- configMsg
} else {
log . Debugf ( "Last %s config received less than %s, waiting..." , configMsg . ProviderName , server . globalConfiguration . ProvidersThrottleDuration )
safe . Go ( func ( ) {
<- time . After ( server . globalConfiguration . ProvidersThrottleDuration )
lastReceivedConfigurationValue := lastReceivedConfiguration . Get ( ) . ( time . Time )
if time . Now ( ) . After ( lastReceivedConfigurationValue . Add ( time . Duration ( server . globalConfiguration . ProvidersThrottleDuration ) ) ) {
log . Debugf ( "Waited for %s config, OK" , configMsg . ProviderName )
if lastConfig , ok := lastConfigs . Get ( configMsg . ProviderName ) ; ok {
server . configurationValidatedChan <- * lastConfig . ( * types . ConfigMessage )
}
2016-04-13 18:36:23 +00:00
}
2016-06-22 16:31:14 +00:00
} )
}
lastReceivedConfiguration . Set ( time . Now ( ) )
2016-04-13 18:36:23 +00:00
}
2016-06-22 16:31:14 +00:00
}
}
}
func ( server * Server ) defaultConfigurationValues ( configuration * types . Configuration ) {
if configuration == nil || configuration . Frontends == nil {
return
}
for _ , frontend := range configuration . Frontends {
// default endpoints if not defined in frontends
if len ( frontend . EntryPoints ) == 0 {
frontend . EntryPoints = server . globalConfiguration . DefaultEntryPoints
2016-01-13 21:45:49 +00:00
}
}
2016-07-04 17:30:32 +00:00
for backendName , backend := range configuration . Backends {
_ , err := types . NewLoadBalancerMethod ( backend . LoadBalancer )
if err != nil {
log . Warnf ( "Error loading load balancer method '%+v' for backend %s: %v. Using default wrr." , backend . LoadBalancer , backendName , err )
backend . LoadBalancer = & types . LoadBalancer { Method : "wrr" }
}
}
2016-01-13 21:45:49 +00:00
}
2016-04-13 18:36:23 +00:00
func ( server * Server ) listenConfigurations ( stop chan bool ) {
2016-01-13 21:45:49 +00:00
for {
2016-04-13 18:36:23 +00:00
select {
case <- stop :
return
case configMsg , ok := <- server . configurationValidatedChan :
if ! ok {
return
2016-01-13 21:45:49 +00:00
}
2016-04-13 18:36:23 +00:00
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
2016-06-22 16:31:14 +00:00
// Copy configurations to new map so we don't change current if LoadConfig fails
newConfigurations := make ( configs )
for k , v := range currentConfigurations {
newConfigurations [ k ] = v
}
newConfigurations [ configMsg . ProviderName ] = configMsg . Configuration
newServerEntryPoints , err := server . loadConfig ( newConfigurations , server . globalConfiguration )
if err == nil {
for newServerEntryPointName , newServerEntryPoint := range newServerEntryPoints {
server . serverEntryPoints [ newServerEntryPointName ] . httpRouter . UpdateHandler ( newServerEntryPoint . httpRouter . GetHandler ( ) )
log . Infof ( "Server configuration reloaded on %s" , server . serverEntryPoints [ newServerEntryPointName ] . httpServer . Addr )
2016-04-13 18:36:23 +00:00
}
2016-06-22 16:31:14 +00:00
server . currentConfigurations . Set ( newConfigurations )
} else {
log . Error ( "Error loading new configuration, aborted " , err )
2016-01-13 21:45:49 +00:00
}
}
}
}
func ( server * Server ) configureProviders ( ) {
// configure providers
if server . globalConfiguration . Docker != nil {
server . providers = append ( server . providers , server . globalConfiguration . Docker )
}
if server . globalConfiguration . Marathon != nil {
server . providers = append ( server . providers , server . globalConfiguration . Marathon )
}
if server . globalConfiguration . File != nil {
server . providers = append ( server . providers , server . globalConfiguration . File )
}
if server . globalConfiguration . Web != nil {
server . globalConfiguration . Web . server = server
server . providers = append ( server . providers , server . globalConfiguration . Web )
}
if server . globalConfiguration . Consul != nil {
server . providers = append ( server . providers , server . globalConfiguration . Consul )
}
2016-02-02 17:03:40 +00:00
if server . globalConfiguration . ConsulCatalog != nil {
server . providers = append ( server . providers , server . globalConfiguration . ConsulCatalog )
}
2016-01-13 21:45:49 +00:00
if server . globalConfiguration . Etcd != nil {
server . providers = append ( server . providers , server . globalConfiguration . Etcd )
}
if server . globalConfiguration . Zookeeper != nil {
server . providers = append ( server . providers , server . globalConfiguration . Zookeeper )
}
if server . globalConfiguration . Boltdb != nil {
server . providers = append ( server . providers , server . globalConfiguration . Boltdb )
}
2016-02-08 20:57:32 +00:00
if server . globalConfiguration . Kubernetes != nil {
server . providers = append ( server . providers , server . globalConfiguration . Kubernetes )
}
2016-01-13 21:45:49 +00:00
}
func ( server * Server ) startProviders ( ) {
// start providers
for _ , provider := range server . providers {
2016-01-29 19:34:17 +00:00
jsonConf , _ := json . Marshal ( provider )
log . Infof ( "Starting provider %v %s" , reflect . TypeOf ( provider ) , jsonConf )
2016-01-13 21:45:49 +00:00
currentProvider := provider
2016-03-31 16:57:08 +00:00
safe . Go ( func ( ) {
2016-06-01 08:29:55 +00:00
err := currentProvider . Provide ( server . configurationChan , & server . routinesPool , server . globalConfiguration . Constraints )
2016-01-13 21:45:49 +00:00
if err != nil {
log . Errorf ( "Error starting provider %s" , err )
}
2016-03-31 16:57:08 +00:00
} )
2016-01-13 21:45:49 +00:00
}
}
func ( server * Server ) listenSignals ( ) {
2016-01-29 19:34:17 +00:00
sig := <- server . signals
2016-01-13 21:45:49 +00:00
log . Infof ( "I have to go... %+v" , sig )
log . Info ( "Stopping server" )
server . Stop ( )
}
// creates a TLS config that allows terminating HTTPS for multiple domains using SNI
2016-03-21 10:10:18 +00:00
func ( server * Server ) createTLSConfig ( entryPointName string , tlsOption * TLS , router * middlewares . HandlerSwitcher ) ( * tls . Config , error ) {
2016-01-29 19:34:17 +00:00
if tlsOption == nil {
return nil , nil
}
2016-01-13 21:45:49 +00:00
config := & tls . Config { }
2016-03-21 10:10:18 +00:00
config . Certificates = [ ] tls . Certificate { }
for _ , v := range tlsOption . Certificates {
cert , err := tls . LoadX509KeyPair ( v . CertFile , v . KeyFile )
2016-01-13 21:45:49 +00:00
if err != nil {
return nil , err
}
2016-03-21 10:10:18 +00:00
config . Certificates = append ( config . Certificates , cert )
}
if server . globalConfiguration . ACME != nil {
if _ , ok := server . serverEntryPoints [ server . globalConfiguration . ACME . EntryPoint ] ; ok {
if entryPointName == server . globalConfiguration . ACME . EntryPoint {
checkOnDemandDomain := func ( domain string ) bool {
2016-07-11 14:43:23 +00:00
routeMatch := & mux . RouteMatch { }
router := router . GetHandler ( )
match := router . Match ( & http . Request { URL : & url . URL { } , Host : domain } , routeMatch )
if match && routeMatch . Route != nil {
2016-03-21 10:10:18 +00:00
return true
}
return false
}
2016-03-22 00:32:02 +00:00
err := server . globalConfiguration . ACME . CreateConfig ( config , checkOnDemandDomain )
2016-03-21 10:10:18 +00:00
if err != nil {
return nil , err
}
}
} else {
return nil , errors . New ( "Unknown entrypoint " + server . globalConfiguration . ACME . EntryPoint + " for ACME configuration" )
}
}
if len ( config . Certificates ) == 0 {
return nil , errors . New ( "No certificates found for TLS entrypoint " + entryPointName )
2016-01-13 21:45:49 +00:00
}
// BuildNameToCertificate parses the CommonName and SubjectAlternateName fields
// in each certificate and populates the config.NameToCertificate map.
config . BuildNameToCertificate ( )
return config , nil
}
func ( server * Server ) startServer ( srv * manners . GracefulServer , globalConfiguration GlobalConfiguration ) {
2016-02-25 17:30:13 +00:00
log . Infof ( "Starting server on %s" , srv . Addr )
2016-01-13 21:45:49 +00:00
if srv . TLSConfig != nil {
2016-02-25 17:30:13 +00:00
if err := srv . ListenAndServeTLSWithConfig ( srv . TLSConfig ) ; err != nil {
2016-01-13 21:45:49 +00:00
log . Fatal ( "Error creating server: " , err )
}
} else {
2016-02-25 17:30:13 +00:00
if err := srv . ListenAndServe ( ) ; err != nil {
2016-01-13 21:45:49 +00:00
log . Fatal ( "Error creating server: " , err )
}
}
log . Info ( "Server stopped" )
}
2016-03-21 10:10:18 +00:00
func ( server * Server ) prepareServer ( entryPointName string , router * middlewares . HandlerSwitcher , entryPoint * EntryPoint , oldServer * manners . GracefulServer , middlewares ... negroni . Handler ) ( * manners . GracefulServer , error ) {
log . Infof ( "Preparing server %s %+v" , entryPointName , entryPoint )
2016-01-13 21:45:49 +00:00
// middlewares
var negroni = negroni . New ( )
for _ , middleware := range middlewares {
negroni . Use ( middleware )
}
negroni . UseHandler ( router )
2016-03-21 10:10:18 +00:00
tlsConfig , err := server . createTLSConfig ( entryPointName , entryPoint . TLS , router )
2016-01-13 21:45:49 +00:00
if err != nil {
log . Fatalf ( "Error creating TLS config %s" , err )
return nil , err
}
if oldServer == nil {
return manners . NewWithServer (
& http . Server {
2016-01-29 19:34:17 +00:00
Addr : entryPoint . Address ,
2016-01-13 21:45:49 +00:00
Handler : negroni ,
TLSConfig : tlsConfig ,
} ) , nil
}
gracefulServer , err := oldServer . HijackListener ( & http . Server {
2016-01-29 19:34:17 +00:00
Addr : entryPoint . Address ,
2016-01-13 21:45:49 +00:00
Handler : negroni ,
TLSConfig : tlsConfig ,
} , tlsConfig )
if err != nil {
log . Fatalf ( "Error hijacking server %s" , err )
return nil , err
}
return gracefulServer , nil
}
2016-02-25 17:30:13 +00:00
func ( server * Server ) buildEntryPoints ( globalConfiguration GlobalConfiguration ) map [ string ] * serverEntryPoint {
serverEntryPoints := make ( map [ string ] * serverEntryPoint )
2016-01-29 19:34:17 +00:00
for entryPointName := range globalConfiguration . EntryPoints {
router := server . buildDefaultHTTPRouter ( )
2016-02-25 17:30:13 +00:00
serverEntryPoints [ entryPointName ] = & serverEntryPoint {
2016-03-04 10:32:23 +00:00
httpRouter : middlewares . NewHandlerSwitcher ( router ) ,
2016-01-29 19:34:17 +00:00
}
}
return serverEntryPoints
}
2016-01-13 21:45:49 +00:00
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
// provider configurations.
2016-02-25 17:30:13 +00:00
func ( server * Server ) loadConfig ( configurations configs , globalConfiguration GlobalConfiguration ) ( map [ string ] * serverEntryPoint , error ) {
2016-01-29 19:34:17 +00:00
serverEntryPoints := server . buildEntryPoints ( globalConfiguration )
redirectHandlers := make ( map [ string ] http . Handler )
2016-01-13 21:45:49 +00:00
backends := map [ string ] http . Handler { }
2016-04-19 23:45:59 +00:00
backend2FrontendMap := map [ string ] string { }
2016-01-13 21:45:49 +00:00
for _ , configuration := range configurations {
2016-02-19 22:55:23 +00:00
frontendNames := sortedFrontendNamesForConfig ( configuration )
2016-06-20 13:19:52 +00:00
frontend :
2016-02-19 22:55:23 +00:00
for _ , frontendName := range frontendNames {
frontend := configuration . Frontends [ frontendName ]
2016-01-29 19:34:17 +00:00
log . Debugf ( "Creating frontend %s" , frontendName )
2016-07-04 17:30:32 +00:00
fwd , err := forward . New ( forward . Logger ( oxyLogger ) , forward . PassHostHeader ( frontend . PassHostHeader ) )
if err != nil {
log . Errorf ( "Error creating forwarder for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2016-04-19 23:45:59 +00:00
saveBackend := middlewares . NewSaveBackend ( fwd )
2016-03-22 00:32:02 +00:00
if len ( frontend . EntryPoints ) == 0 {
2016-06-20 13:19:52 +00:00
log . Errorf ( "No entrypoint defined for frontend %s, defaultEntryPoints:%s" , frontendName , globalConfiguration . DefaultEntryPoints )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 00:32:02 +00:00
}
2016-01-29 19:34:17 +00:00
for _ , entryPointName := range frontend . EntryPoints {
log . Debugf ( "Wiring frontend %s to entryPoint %s" , frontendName , entryPointName )
if _ , ok := serverEntryPoints [ entryPointName ] ; ! ok {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Undefined entrypoint '%s' for frontend %s" , entryPointName , frontendName )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-13 21:45:49 +00:00
}
2016-03-27 00:05:17 +00:00
newServerRoute := & serverRoute { route : serverEntryPoints [ entryPointName ] . httpRouter . GetHandler ( ) . NewRoute ( ) . Name ( frontendName ) }
2016-01-29 19:34:17 +00:00
for routeName , route := range frontend . Routes {
2016-03-30 17:05:43 +00:00
err := getRoute ( newServerRoute , & route )
2016-01-29 19:34:17 +00:00
if err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error creating route for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
}
2016-03-30 17:05:43 +00:00
log . Debugf ( "Creating route %s %s" , routeName , route . Rule )
2016-01-13 21:45:49 +00:00
}
2016-01-29 19:34:17 +00:00
entryPoint := globalConfiguration . EntryPoints [ entryPointName ]
if entryPoint . Redirect != nil {
if redirectHandlers [ entryPointName ] != nil {
2016-03-27 00:05:17 +00:00
newServerRoute . route . Handler ( redirectHandlers [ entryPointName ] )
2016-01-29 19:34:17 +00:00
} else if handler , err := server . loadEntryPointConfig ( entryPointName , entryPoint ) ; err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error loading entrypoint configuration for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
} else {
2016-03-27 00:05:17 +00:00
newServerRoute . route . Handler ( handler )
2016-01-29 19:34:17 +00:00
redirectHandlers [ entryPointName ] = handler
2016-01-13 21:45:49 +00:00
}
2016-01-29 19:34:17 +00:00
} else {
if backends [ frontend . Backend ] == nil {
log . Debugf ( "Creating backend %s" , frontend . Backend )
var lb http . Handler
2016-04-19 23:45:59 +00:00
rr , _ := roundrobin . New ( saveBackend )
2016-01-29 19:34:17 +00:00
if configuration . Backends [ frontend . Backend ] == nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Undefined backend '%s' for frontend %s" , frontend . Backend , frontendName )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
}
lbMethod , err := types . NewLoadBalancerMethod ( configuration . Backends [ frontend . Backend ] . LoadBalancer )
2016-01-13 21:45:49 +00:00
if err != nil {
2016-07-04 17:30:32 +00:00
log . Errorf ( "Error loading load balancer method '%+v' for frontend %s: %v" , configuration . Backends [ frontend . Backend ] . LoadBalancer , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
}
switch lbMethod {
case types . Drr :
log . Debugf ( "Creating load-balancer drr" )
rebalancer , _ := roundrobin . NewRebalancer ( rr , roundrobin . RebalancerLogger ( oxyLogger ) )
lb = rebalancer
for serverName , server := range configuration . Backends [ frontend . Backend ] . Servers {
url , err := url . Parse ( server . URL )
if err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error parsing server URL %s: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
}
2016-04-19 23:45:59 +00:00
backend2FrontendMap [ url . String ( ) ] = frontendName
2016-01-29 19:34:17 +00:00
log . Debugf ( "Creating server %s at %s with weight %d" , serverName , url . String ( ) , server . Weight )
2016-03-22 00:32:02 +00:00
if err := rebalancer . UpsertServer ( url , roundrobin . Weight ( server . Weight ) ) ; err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error adding server %s to load balancer: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 00:32:02 +00:00
}
2016-01-29 19:34:17 +00:00
}
case types . Wrr :
log . Debugf ( "Creating load-balancer wrr" )
2016-03-15 22:27:07 +00:00
lb = rr
2016-01-29 19:34:17 +00:00
for serverName , server := range configuration . Backends [ frontend . Backend ] . Servers {
url , err := url . Parse ( server . URL )
if err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error parsing server URL %s: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
}
2016-04-19 23:45:59 +00:00
backend2FrontendMap [ url . String ( ) ] = frontendName
2016-01-29 19:34:17 +00:00
log . Debugf ( "Creating server %s at %s with weight %d" , serverName , url . String ( ) , server . Weight )
2016-03-22 00:32:02 +00:00
if err := rr . UpsertServer ( url , roundrobin . Weight ( server . Weight ) ) ; err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error adding server %s to load balancer: %v" , server . URL , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 00:32:02 +00:00
}
2016-01-29 19:34:17 +00:00
}
}
2016-04-13 08:11:36 +00:00
maxConns := configuration . Backends [ frontend . Backend ] . MaxConn
if maxConns != nil && maxConns . Amount != 0 {
extractFunc , err := utils . NewExtractor ( maxConns . ExtractorFunc )
if err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error creating connlimit: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-04-13 08:11:36 +00:00
}
log . Debugf ( "Creating loadd-balancer connlimit" )
lb , err = connlimit . New ( lb , extractFunc , maxConns . Amount , connlimit . Logger ( oxyLogger ) )
if err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error creating connlimit: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-04-13 08:11:36 +00:00
}
}
2016-03-29 20:25:32 +00:00
// retry ?
if globalConfiguration . Retry != nil {
2016-04-06 12:07:51 +00:00
retries := len ( configuration . Backends [ frontend . Backend ] . Servers )
2016-03-29 20:25:32 +00:00
if globalConfiguration . Retry . Attempts > 0 {
retries = globalConfiguration . Retry . Attempts
}
2016-06-15 17:07:33 +00:00
lb = middlewares . NewRetry ( retries , lb )
2016-03-29 20:25:32 +00:00
log . Debugf ( "Creating retries max attempts %d" , retries )
}
2016-01-29 19:34:17 +00:00
var negroni = negroni . New ( )
if configuration . Backends [ frontend . Backend ] . CircuitBreaker != nil {
log . Debugf ( "Creating circuit breaker %s" , configuration . Backends [ frontend . Backend ] . CircuitBreaker . Expression )
2016-06-22 20:14:40 +00:00
cbreaker , err := middlewares . NewCircuitBreaker ( lb , configuration . Backends [ frontend . Backend ] . CircuitBreaker . Expression , cbreaker . Logger ( oxyLogger ) )
if err != nil {
log . Errorf ( "Error creating circuit breaker: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
negroni . Use ( cbreaker )
2016-01-29 19:34:17 +00:00
} else {
negroni . UseHandler ( lb )
2016-01-13 21:45:49 +00:00
}
2016-01-29 19:34:17 +00:00
backends [ frontend . Backend ] = negroni
} else {
log . Debugf ( "Reusing backend %s" , frontend . Backend )
2016-01-13 21:45:49 +00:00
}
2016-06-03 15:58:33 +00:00
if frontend . Priority > 0 {
newServerRoute . route . Priority ( frontend . Priority )
}
2016-03-27 00:05:17 +00:00
server . wireFrontendBackend ( newServerRoute , backends [ frontend . Backend ] )
2016-01-13 21:45:49 +00:00
}
2016-03-27 00:05:17 +00:00
err := newServerRoute . route . GetError ( )
2016-01-29 19:34:17 +00:00
if err != nil {
log . Errorf ( "Error building route: %s" , err )
2016-01-13 21:45:49 +00:00
}
}
2016-01-29 19:34:17 +00:00
}
}
2016-04-19 23:45:59 +00:00
middlewares . SetBackend2FrontendMap ( & backend2FrontendMap )
2016-06-03 15:58:33 +00:00
//sort routes
for _ , serverEntryPoint := range serverEntryPoints {
serverEntryPoint . httpRouter . GetHandler ( ) . SortRoutes ( )
}
2016-01-29 19:34:17 +00:00
return serverEntryPoints , nil
}
2016-01-13 21:45:49 +00:00
2016-03-27 00:05:17 +00:00
func ( server * Server ) wireFrontendBackend ( serverRoute * serverRoute , handler http . Handler ) {
2016-02-26 14:29:53 +00:00
// strip prefix
2016-04-06 11:06:31 +00:00
if len ( serverRoute . stripPrefixes ) > 0 {
2016-03-27 00:05:17 +00:00
serverRoute . route . Handler ( & middlewares . StripPrefix {
2016-04-06 11:06:31 +00:00
Prefixes : serverRoute . stripPrefixes ,
Handler : handler ,
2016-03-27 00:05:17 +00:00
} )
} else {
serverRoute . route . Handler ( handler )
2016-02-26 14:29:53 +00:00
}
}
2016-01-29 19:34:17 +00:00
func ( server * Server ) loadEntryPointConfig ( entryPointName string , entryPoint * EntryPoint ) ( http . Handler , error ) {
regex := entryPoint . Redirect . Regex
replacement := entryPoint . Redirect . Replacement
if len ( entryPoint . Redirect . EntryPoint ) > 0 {
2016-03-09 17:59:08 +00:00
regex = "^(?:https?:\\/\\/)?([\\da-z\\.-]+)(?::\\d+)?(.*)$"
2016-01-29 19:34:17 +00:00
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] == nil {
2016-03-03 20:29:52 +00:00
return nil , errors . New ( "Unknown entrypoint " + entryPoint . Redirect . EntryPoint )
2016-01-29 19:34:17 +00:00
}
protocol := "http"
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . TLS != nil {
protocol = "https"
2016-01-13 21:45:49 +00:00
}
2016-01-29 19:34:17 +00:00
r , _ := regexp . Compile ( "(:\\d+)" )
match := r . FindStringSubmatch ( server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
if len ( match ) == 0 {
return nil , errors . New ( "Bad Address format: " + server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
}
replacement = protocol + "://$1" + match [ 0 ] + "$2"
2016-01-13 21:45:49 +00:00
}
2016-01-29 19:34:17 +00:00
rewrite , err := middlewares . NewRewrite ( regex , replacement , true )
if err != nil {
return nil , err
}
log . Debugf ( "Creating entryPoint redirect %s -> %s : %s -> %s" , entryPointName , entryPoint . Redirect . EntryPoint , regex , replacement )
negroni := negroni . New ( )
negroni . Use ( rewrite )
return negroni , nil
}
func ( server * Server ) buildDefaultHTTPRouter ( ) * mux . Router {
router := mux . NewRouter ( )
router . NotFoundHandler = http . HandlerFunc ( notFoundHandler )
2016-02-26 14:29:53 +00:00
router . StrictSlash ( true )
2016-01-29 19:34:17 +00:00
return router
2016-01-13 21:45:49 +00:00
}
2016-02-19 22:55:23 +00:00
2016-03-30 17:05:43 +00:00
func getRoute ( serverRoute * serverRoute , route * types . Route ) error {
// ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠
// TODO: backwards compatibility with DEPRECATED rule.Value
if len ( route . Value ) > 0 {
route . Rule += ":" + route . Value
2016-03-31 10:59:50 +00:00
log . Warnf ( "Value %s is DEPRECATED (will be removed in v1.0.0), please refer to the new frontend notation: https://github.com/containous/traefik/blob/master/docs/index.md#-frontends" , route . Value )
2016-03-30 17:05:43 +00:00
}
// ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠
2016-03-27 00:05:17 +00:00
rules := Rules { route : serverRoute }
newRoute , err := rules . Parse ( route . Rule )
if err != nil {
return err
2016-02-26 14:29:53 +00:00
}
2016-06-03 15:58:33 +00:00
newRoute . Priority ( serverRoute . route . GetPriority ( ) + len ( route . Rule ) )
2016-03-27 00:05:17 +00:00
serverRoute . route = newRoute
return nil
2016-02-26 14:29:53 +00:00
}
2016-02-19 22:55:23 +00:00
func sortedFrontendNamesForConfig ( configuration * types . Configuration ) [ ] string {
keys := [ ] string { }
for key := range configuration . Frontends {
keys = append ( keys , key )
}
sort . Strings ( keys )
return keys
}