2019-01-18 15:18:04 +01:00
package provider
import (
2019-01-21 19:06:02 +01:00
"bytes"
2019-01-18 15:18:04 +01:00
"context"
"reflect"
"sort"
2019-01-21 19:06:02 +01:00
"strings"
"text/template"
"unicode"
2019-01-18 15:18:04 +01:00
2019-01-21 19:06:02 +01:00
"github.com/Masterminds/sprig"
2019-08-03 03:58:23 +02:00
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
2019-01-18 15:18:04 +01:00
)
// Merge Merges multiple configurations.
2019-07-10 09:26:04 +02:00
func Merge ( ctx context . Context , configurations map [ string ] * dynamic . Configuration ) * dynamic . Configuration {
2019-01-18 15:18:04 +01:00
logger := log . FromContext ( ctx )
2019-07-10 09:26:04 +02:00
configuration := & dynamic . Configuration {
HTTP : & dynamic . HTTPConfiguration {
Routers : make ( map [ string ] * dynamic . Router ) ,
Middlewares : make ( map [ string ] * dynamic . Middleware ) ,
Services : make ( map [ string ] * dynamic . Service ) ,
2019-03-14 09:30:04 +01:00
} ,
2019-07-10 09:26:04 +02:00
TCP : & dynamic . TCPConfiguration {
Routers : make ( map [ string ] * dynamic . TCPRouter ) ,
Services : make ( map [ string ] * dynamic . TCPService ) ,
2019-03-14 09:30:04 +01:00
} ,
2020-02-11 01:26:04 +01:00
UDP : & dynamic . UDPConfiguration {
Routers : make ( map [ string ] * dynamic . UDPRouter ) ,
Services : make ( map [ string ] * dynamic . UDPService ) ,
} ,
2019-01-18 15:18:04 +01:00
}
servicesToDelete := map [ string ] struct { } { }
services := map [ string ] [ ] string { }
routersToDelete := map [ string ] struct { } { }
routers := map [ string ] [ ] string { }
2019-03-21 15:22:06 +01:00
servicesTCPToDelete := map [ string ] struct { } { }
servicesTCP := map [ string ] [ ] string { }
routersTCPToDelete := map [ string ] struct { } { }
routersTCP := map [ string ] [ ] string { }
2019-01-18 15:18:04 +01:00
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 ]
2019-03-14 09:30:04 +01:00
for serviceName , service := range conf . HTTP . Services {
2019-01-18 15:18:04 +01:00
services [ serviceName ] = append ( services [ serviceName ] , root )
2019-03-14 09:30:04 +01:00
if ! AddService ( configuration . HTTP , serviceName , service ) {
2019-01-18 15:18:04 +01:00
servicesToDelete [ serviceName ] = struct { } { }
}
}
2019-03-14 09:30:04 +01:00
for routerName , router := range conf . HTTP . Routers {
2019-01-18 15:18:04 +01:00
routers [ routerName ] = append ( routers [ routerName ] , root )
2019-03-14 09:30:04 +01:00
if ! AddRouter ( configuration . HTTP , routerName , router ) {
2019-01-18 15:18:04 +01:00
routersToDelete [ routerName ] = struct { } { }
}
}
2019-03-21 15:22:06 +01:00
for serviceName , service := range conf . TCP . Services {
servicesTCP [ serviceName ] = append ( servicesTCP [ serviceName ] , root )
if ! AddServiceTCP ( configuration . TCP , serviceName , service ) {
servicesTCPToDelete [ serviceName ] = struct { } { }
}
}
for routerName , router := range conf . TCP . Routers {
routersTCP [ routerName ] = append ( routersTCP [ routerName ] , root )
if ! AddRouterTCP ( configuration . TCP , routerName , router ) {
routersTCPToDelete [ routerName ] = struct { } { }
}
}
2019-03-14 09:30:04 +01:00
for middlewareName , middleware := range conf . HTTP . Middlewares {
2019-01-18 15:18:04 +01:00
middlewares [ middlewareName ] = append ( middlewares [ middlewareName ] , root )
2019-03-14 09:30:04 +01:00
if ! AddMiddleware ( configuration . HTTP , middlewareName , middleware ) {
2019-01-18 15:18:04 +01:00
middlewaresToDelete [ middlewareName ] = struct { } { }
}
}
}
for serviceName := range servicesToDelete {
logger . WithField ( log . ServiceName , serviceName ) .
Errorf ( "Service defined multiple times with different configurations in %v" , services [ serviceName ] )
2019-03-14 09:30:04 +01:00
delete ( configuration . HTTP . Services , serviceName )
2019-01-18 15:18:04 +01:00
}
for routerName := range routersToDelete {
logger . WithField ( log . RouterName , routerName ) .
Errorf ( "Router defined multiple times with different configurations in %v" , routers [ routerName ] )
2019-03-14 09:30:04 +01:00
delete ( configuration . HTTP . Routers , routerName )
2019-01-18 15:18:04 +01:00
}
2019-03-21 15:22:06 +01:00
for serviceName := range servicesTCPToDelete {
logger . WithField ( log . ServiceName , serviceName ) .
Errorf ( "Service TCP defined multiple times with different configurations in %v" , servicesTCP [ serviceName ] )
delete ( configuration . TCP . Services , serviceName )
}
for routerName := range routersTCPToDelete {
logger . WithField ( log . RouterName , routerName ) .
Errorf ( "Router TCP defined multiple times with different configurations in %v" , routersTCP [ routerName ] )
delete ( configuration . TCP . Routers , routerName )
}
2019-01-18 15:18:04 +01:00
for middlewareName := range middlewaresToDelete {
logger . WithField ( log . MiddlewareName , middlewareName ) .
Errorf ( "Middleware defined multiple times with different configurations in %v" , middlewares [ middlewareName ] )
2019-03-14 09:30:04 +01:00
delete ( configuration . HTTP . Middlewares , middlewareName )
2019-01-18 15:18:04 +01:00
}
return configuration
}
2019-03-21 15:22:06 +01:00
// AddServiceTCP Adds a service to a configurations.
2019-07-10 09:26:04 +02:00
func AddServiceTCP ( configuration * dynamic . TCPConfiguration , serviceName string , service * dynamic . TCPService ) bool {
2019-03-21 15:22:06 +01:00
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
}
// AddRouterTCP Adds a router to a configurations.
2019-07-10 09:26:04 +02:00
func AddRouterTCP ( configuration * dynamic . TCPConfiguration , routerName string , router * dynamic . TCPRouter ) bool {
2019-03-21 15:22:06 +01:00
if _ , ok := configuration . Routers [ routerName ] ; ! ok {
configuration . Routers [ routerName ] = router
return true
}
return reflect . DeepEqual ( configuration . Routers [ routerName ] , router )
}
2019-01-18 15:18:04 +01:00
// AddService Adds a service to a configurations.
2019-07-10 09:26:04 +02:00
func AddService ( configuration * dynamic . HTTPConfiguration , serviceName string , service * dynamic . Service ) bool {
2019-01-18 15:18:04 +01:00
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.
2019-07-10 09:26:04 +02:00
func AddRouter ( configuration * dynamic . HTTPConfiguration , routerName string , router * dynamic . Router ) bool {
2019-01-18 15:18:04 +01:00
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.
2019-07-10 09:26:04 +02:00
func AddMiddleware ( configuration * dynamic . HTTPConfiguration , middlewareName string , middleware * dynamic . Middleware ) bool {
2019-01-18 15:18:04 +01:00
if _ , ok := configuration . Middlewares [ middlewareName ] ; ! ok {
configuration . Middlewares [ middlewareName ] = middleware
return true
}
return reflect . DeepEqual ( configuration . Middlewares [ middlewareName ] , middleware )
}
2019-01-21 19:06:02 +01: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 )
}
2019-03-21 15:22:06 +01:00
// BuildTCPRouterConfiguration Builds a router configuration.
2019-07-10 09:26:04 +02:00
func BuildTCPRouterConfiguration ( ctx context . Context , configuration * dynamic . TCPConfiguration ) {
2019-03-21 15:22:06 +01:00
for routerName , router := range configuration . Routers {
loggerRouter := log . FromContext ( ctx ) . WithField ( log . RouterName , routerName )
if len ( router . Rule ) == 0 {
delete ( configuration . Routers , routerName )
loggerRouter . Errorf ( "Empty rule" )
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
}
}
}
}
2019-01-21 19:06:02 +01:00
// BuildRouterConfiguration Builds a router configuration.
2019-07-10 09:26:04 +02:00
func BuildRouterConfiguration ( ctx context . Context , configuration * dynamic . HTTPConfiguration , defaultRouterName string , defaultRuleTpl * template . Template , model interface { } ) {
2019-01-21 19:06:02 +01:00
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 {
2019-07-10 09:26:04 +02:00
configuration . Routers = make ( map [ string ] * dynamic . Router )
configuration . Routers [ defaultRouterName ] = & dynamic . Router { }
2019-01-21 19:06:02 +01:00
}
}
for routerName , router := range configuration . Routers {
2019-03-21 15:22:06 +01:00
loggerRouter := log . FromContext ( ctx ) . WithField ( log . RouterName , routerName )
2019-01-21 19:06:02 +01:00
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 ) , "-" )
}