2019-01-18 17:18:04 +03:00
package provider
import (
2019-01-21 21:06:02 +03:00
"bytes"
2019-01-18 17:18:04 +03:00
"context"
"reflect"
"sort"
2019-01-21 21:06:02 +03:00
"strings"
"text/template"
"unicode"
2019-01-18 17:18:04 +03:00
2019-01-21 21:06:02 +03:00
"github.com/Masterminds/sprig"
2019-01-18 17:18:04 +03:00
"github.com/containous/traefik/config"
"github.com/containous/traefik/log"
)
// Merge Merges multiple configurations.
func Merge ( ctx context . Context , configurations map [ string ] * config . Configuration ) * config . Configuration {
logger := log . FromContext ( ctx )
configuration := & config . Configuration {
Routers : make ( map [ string ] * config . Router ) ,
Middlewares : make ( map [ string ] * config . Middleware ) ,
Services : make ( map [ string ] * config . Service ) ,
}
servicesToDelete := map [ string ] struct { } { }
services := map [ string ] [ ] string { }
routersToDelete := map [ string ] struct { } { }
routers := map [ string ] [ ] string { }
middlewaresToDelete := map [ string ] struct { } { }
middlewares := map [ string ] [ ] string { }
var sortedKeys [ ] string
for key := range configurations {
sortedKeys = append ( sortedKeys , key )
}
sort . Strings ( sortedKeys )
for _ , root := range sortedKeys {
conf := configurations [ root ]
for serviceName , service := range conf . Services {
services [ serviceName ] = append ( services [ serviceName ] , root )
if ! AddService ( configuration , serviceName , service ) {
servicesToDelete [ serviceName ] = struct { } { }
}
}
for routerName , router := range conf . Routers {
routers [ routerName ] = append ( routers [ routerName ] , root )
if ! AddRouter ( configuration , routerName , router ) {
routersToDelete [ routerName ] = struct { } { }
}
}
for middlewareName , middleware := range conf . Middlewares {
middlewares [ middlewareName ] = append ( middlewares [ middlewareName ] , root )
if ! AddMiddleware ( configuration , middlewareName , middleware ) {
middlewaresToDelete [ middlewareName ] = struct { } { }
}
}
}
for serviceName := range servicesToDelete {
logger . WithField ( log . ServiceName , serviceName ) .
Errorf ( "Service defined multiple times with different configurations in %v" , services [ serviceName ] )
delete ( configuration . Services , serviceName )
}
for routerName := range routersToDelete {
logger . WithField ( log . RouterName , routerName ) .
Errorf ( "Router defined multiple times with different configurations in %v" , routers [ routerName ] )
delete ( configuration . Routers , routerName )
}
for middlewareName := range middlewaresToDelete {
logger . WithField ( log . MiddlewareName , middlewareName ) .
Errorf ( "Middleware defined multiple times with different configurations in %v" , middlewares [ middlewareName ] )
delete ( configuration . Middlewares , middlewareName )
}
return configuration
}
// AddService Adds a service to a configurations.
func AddService ( configuration * config . Configuration , serviceName string , service * config . Service ) bool {
if _ , ok := configuration . Services [ serviceName ] ; ! ok {
configuration . Services [ serviceName ] = service
return true
}
if ! configuration . Services [ serviceName ] . LoadBalancer . Mergeable ( service . LoadBalancer ) {
return false
}
configuration . Services [ serviceName ] . LoadBalancer . Servers = append ( configuration . Services [ serviceName ] . LoadBalancer . Servers , service . LoadBalancer . Servers ... )
return true
}
// AddRouter Adds a router to a configurations.
func AddRouter ( configuration * config . Configuration , routerName string , router * config . Router ) bool {
if _ , ok := configuration . Routers [ routerName ] ; ! ok {
configuration . Routers [ routerName ] = router
return true
}
return reflect . DeepEqual ( configuration . Routers [ routerName ] , router )
}
// AddMiddleware Adds a middleware to a configurations.
func AddMiddleware ( configuration * config . Configuration , middlewareName string , middleware * config . Middleware ) bool {
if _ , ok := configuration . Middlewares [ middlewareName ] ; ! ok {
configuration . Middlewares [ middlewareName ] = middleware
return true
}
return reflect . DeepEqual ( configuration . Middlewares [ middlewareName ] , middleware )
}
2019-01-21 21:06:02 +03:00
// MakeDefaultRuleTemplate Creates the default rule template.
func MakeDefaultRuleTemplate ( defaultRule string , funcMap template . FuncMap ) ( * template . Template , error ) {
defaultFuncMap := sprig . TxtFuncMap ( )
defaultFuncMap [ "normalize" ] = Normalize
for k , fn := range funcMap {
defaultFuncMap [ k ] = fn
}
return template . New ( "defaultRule" ) . Funcs ( defaultFuncMap ) . Parse ( defaultRule )
}
// BuildRouterConfiguration Builds a router configuration.
func BuildRouterConfiguration ( ctx context . Context , configuration * config . Configuration , defaultRouterName string , defaultRuleTpl * template . Template , model interface { } ) {
logger := log . FromContext ( ctx )
if len ( configuration . Routers ) == 0 {
if len ( configuration . Services ) > 1 {
log . FromContext ( ctx ) . Info ( "Could not create a router for the container: too many services" )
} else {
configuration . Routers = make ( map [ string ] * config . Router )
configuration . Routers [ defaultRouterName ] = & config . Router { }
}
}
for routerName , router := range configuration . Routers {
loggerRouter := logger . WithField ( log . RouterName , routerName )
if len ( router . Rule ) == 0 {
writer := & bytes . Buffer { }
if err := defaultRuleTpl . Execute ( writer , model ) ; err != nil {
loggerRouter . Errorf ( "Error while parsing default rule: %v" , err )
delete ( configuration . Routers , routerName )
continue
}
router . Rule = writer . String ( )
if len ( router . Rule ) == 0 {
loggerRouter . Error ( "Undefined rule" )
delete ( configuration . Routers , routerName )
continue
}
}
if len ( router . Service ) == 0 {
if len ( configuration . Services ) > 1 {
delete ( configuration . Routers , routerName )
loggerRouter .
Error ( "Could not define the service name for the router: too many services" )
continue
}
for serviceName := range configuration . Services {
router . Service = serviceName
}
}
}
}
// Normalize Replace all special chars with `-`.
func Normalize ( name string ) string {
fargs := func ( c rune ) bool {
return ! unicode . IsLetter ( c ) && ! unicode . IsNumber ( c )
}
// get function
return strings . Join ( strings . FieldsFunc ( name , fargs ) , "-" )
}