481 lines
16 KiB
Go
481 lines
16 KiB
Go
package provider
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"text/template"
|
|
"unicode"
|
|
|
|
"github.com/Masterminds/sprig/v3"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
|
"github.com/traefik/traefik/v3/pkg/logs"
|
|
)
|
|
|
|
// Merge Merges multiple configurations.
|
|
func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration) *dynamic.Configuration {
|
|
logger := log.Ctx(ctx)
|
|
|
|
configuration := &dynamic.Configuration{
|
|
HTTP: &dynamic.HTTPConfiguration{
|
|
Routers: make(map[string]*dynamic.Router),
|
|
Middlewares: make(map[string]*dynamic.Middleware),
|
|
Services: make(map[string]*dynamic.Service),
|
|
ServersTransports: make(map[string]*dynamic.ServersTransport),
|
|
},
|
|
TCP: &dynamic.TCPConfiguration{
|
|
Routers: make(map[string]*dynamic.TCPRouter),
|
|
Services: make(map[string]*dynamic.TCPService),
|
|
Middlewares: make(map[string]*dynamic.TCPMiddleware),
|
|
ServersTransports: make(map[string]*dynamic.TCPServersTransport),
|
|
},
|
|
UDP: &dynamic.UDPConfiguration{
|
|
Routers: make(map[string]*dynamic.UDPRouter),
|
|
Services: make(map[string]*dynamic.UDPService),
|
|
},
|
|
}
|
|
|
|
servicesToDelete := map[string]struct{}{}
|
|
services := map[string][]string{}
|
|
|
|
routersToDelete := map[string]struct{}{}
|
|
routers := map[string][]string{}
|
|
|
|
servicesTCPToDelete := map[string]struct{}{}
|
|
servicesTCP := map[string][]string{}
|
|
|
|
routersTCPToDelete := map[string]struct{}{}
|
|
routersTCP := map[string][]string{}
|
|
|
|
servicesUDPToDelete := map[string]struct{}{}
|
|
servicesUDP := map[string][]string{}
|
|
|
|
routersUDPToDelete := map[string]struct{}{}
|
|
routersUDP := map[string][]string{}
|
|
|
|
middlewaresToDelete := map[string]struct{}{}
|
|
middlewares := map[string][]string{}
|
|
|
|
middlewaresTCPToDelete := map[string]struct{}{}
|
|
middlewaresTCP := map[string][]string{}
|
|
|
|
transportsToDelete := map[string]struct{}{}
|
|
transports := map[string][]string{}
|
|
|
|
transportsTCPToDelete := map[string]struct{}{}
|
|
transportsTCP := 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.HTTP.Services {
|
|
services[serviceName] = append(services[serviceName], root)
|
|
if !AddService(configuration.HTTP, serviceName, service) {
|
|
servicesToDelete[serviceName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for routerName, router := range conf.HTTP.Routers {
|
|
routers[routerName] = append(routers[routerName], root)
|
|
if !AddRouter(configuration.HTTP, routerName, router) {
|
|
routersToDelete[routerName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for transportName, transport := range conf.HTTP.ServersTransports {
|
|
transports[transportName] = append(transports[transportName], root)
|
|
if !AddTransport(configuration.HTTP, transportName, transport) {
|
|
transportsToDelete[transportName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
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{}{}
|
|
}
|
|
}
|
|
|
|
for transportName, transport := range conf.TCP.ServersTransports {
|
|
transportsTCP[transportName] = append(transportsTCP[transportName], root)
|
|
if !AddTransportTCP(configuration.TCP, transportName, transport) {
|
|
transportsTCPToDelete[transportName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for serviceName, service := range conf.UDP.Services {
|
|
servicesUDP[serviceName] = append(servicesUDP[serviceName], root)
|
|
if !AddServiceUDP(configuration.UDP, serviceName, service) {
|
|
servicesUDPToDelete[serviceName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for routerName, router := range conf.UDP.Routers {
|
|
routersUDP[routerName] = append(routersUDP[routerName], root)
|
|
if !AddRouterUDP(configuration.UDP, routerName, router) {
|
|
routersUDPToDelete[routerName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for middlewareName, middleware := range conf.HTTP.Middlewares {
|
|
middlewares[middlewareName] = append(middlewares[middlewareName], root)
|
|
if !AddMiddleware(configuration.HTTP, middlewareName, middleware) {
|
|
middlewaresToDelete[middlewareName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for middlewareName, middleware := range conf.TCP.Middlewares {
|
|
middlewaresTCP[middlewareName] = append(middlewaresTCP[middlewareName], root)
|
|
if !AddMiddlewareTCP(configuration.TCP, middlewareName, middleware) {
|
|
middlewaresTCPToDelete[middlewareName] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
for serviceName := range servicesToDelete {
|
|
logger.Error().Str(logs.ServiceName, serviceName).
|
|
Interface("configuration", services[serviceName]).
|
|
Msg("Service defined multiple times with different configurations")
|
|
delete(configuration.HTTP.Services, serviceName)
|
|
}
|
|
|
|
for routerName := range routersToDelete {
|
|
logger.Error().Str(logs.RouterName, routerName).
|
|
Interface("configuration", routers[routerName]).
|
|
Msg("Router defined multiple times with different configurations")
|
|
delete(configuration.HTTP.Routers, routerName)
|
|
}
|
|
|
|
for transportName := range transportsToDelete {
|
|
logger.Error().Str(logs.ServersTransportName, transportName).
|
|
Interface("configuration", transports[transportName]).
|
|
Msg("ServersTransport defined multiple times with different configurations")
|
|
delete(configuration.HTTP.ServersTransports, transportName)
|
|
}
|
|
|
|
for serviceName := range servicesTCPToDelete {
|
|
logger.Error().Str(logs.ServiceName, serviceName).
|
|
Interface("configuration", servicesTCP[serviceName]).
|
|
Msg("Service TCP defined multiple times with different configurations")
|
|
delete(configuration.TCP.Services, serviceName)
|
|
}
|
|
|
|
for routerName := range routersTCPToDelete {
|
|
logger.Error().Str(logs.RouterName, routerName).
|
|
Interface("configuration", routersTCP[routerName]).
|
|
Msg("Router TCP defined multiple times with different configurations")
|
|
delete(configuration.TCP.Routers, routerName)
|
|
}
|
|
|
|
for transportName := range transportsTCPToDelete {
|
|
logger.Error().Str(logs.ServersTransportName, transportName).
|
|
Interface("configuration", transportsTCP[transportName]).
|
|
Msg("ServersTransport TCP defined multiple times with different configurations")
|
|
delete(configuration.TCP.ServersTransports, transportName)
|
|
}
|
|
|
|
for serviceName := range servicesUDPToDelete {
|
|
logger.Error().Str(logs.ServiceName, serviceName).
|
|
Interface("configuration", servicesUDP[serviceName]).
|
|
Msg("UDP service defined multiple times with different configurations")
|
|
delete(configuration.UDP.Services, serviceName)
|
|
}
|
|
|
|
for routerName := range routersUDPToDelete {
|
|
logger.Error().Str(logs.RouterName, routerName).
|
|
Interface("configuration", routersUDP[routerName]).
|
|
Msg("UDP router defined multiple times with different configurations")
|
|
delete(configuration.UDP.Routers, routerName)
|
|
}
|
|
|
|
for middlewareName := range middlewaresToDelete {
|
|
logger.Error().Str(logs.MiddlewareName, middlewareName).
|
|
Interface("configuration", middlewares[middlewareName]).
|
|
Msg("Middleware defined multiple times with different configurations")
|
|
delete(configuration.HTTP.Middlewares, middlewareName)
|
|
}
|
|
|
|
for middlewareName := range middlewaresTCPToDelete {
|
|
logger.Error().Str(logs.MiddlewareName, middlewareName).
|
|
Interface("configuration", middlewaresTCP[middlewareName]).
|
|
Msg("TCP Middleware defined multiple times with different configurations")
|
|
delete(configuration.TCP.Middlewares, middlewareName)
|
|
}
|
|
|
|
return configuration
|
|
}
|
|
|
|
// AddServiceTCP adds a service to a configuration.
|
|
func AddServiceTCP(configuration *dynamic.TCPConfiguration, serviceName string, service *dynamic.TCPService) bool {
|
|
if _, ok := configuration.Services[serviceName]; !ok {
|
|
configuration.Services[serviceName] = service
|
|
return true
|
|
}
|
|
|
|
if !configuration.Services[serviceName].LoadBalancer.Mergeable(service.LoadBalancer) {
|
|
return false
|
|
}
|
|
|
|
uniq := map[string]struct{}{}
|
|
for _, server := range configuration.Services[serviceName].LoadBalancer.Servers {
|
|
uniq[server.Address] = struct{}{}
|
|
}
|
|
|
|
for _, server := range service.LoadBalancer.Servers {
|
|
if _, ok := uniq[server.Address]; !ok {
|
|
configuration.Services[serviceName].LoadBalancer.Servers = append(configuration.Services[serviceName].LoadBalancer.Servers, server)
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// AddRouterTCP adds a router to a configuration.
|
|
func AddRouterTCP(configuration *dynamic.TCPConfiguration, routerName string, router *dynamic.TCPRouter) bool {
|
|
if _, ok := configuration.Routers[routerName]; !ok {
|
|
configuration.Routers[routerName] = router
|
|
return true
|
|
}
|
|
|
|
return reflect.DeepEqual(configuration.Routers[routerName], router)
|
|
}
|
|
|
|
// AddMiddlewareTCP adds a middleware to a configuration.
|
|
func AddMiddlewareTCP(configuration *dynamic.TCPConfiguration, middlewareName string, middleware *dynamic.TCPMiddleware) bool {
|
|
if _, ok := configuration.Middlewares[middlewareName]; !ok {
|
|
configuration.Middlewares[middlewareName] = middleware
|
|
return true
|
|
}
|
|
|
|
return reflect.DeepEqual(configuration.Middlewares[middlewareName], middleware)
|
|
}
|
|
|
|
// AddTransportTCP adds a servers transport to a configuration.
|
|
func AddTransportTCP(configuration *dynamic.TCPConfiguration, transportName string, transport *dynamic.TCPServersTransport) bool {
|
|
if _, ok := configuration.ServersTransports[transportName]; !ok {
|
|
configuration.ServersTransports[transportName] = transport
|
|
return true
|
|
}
|
|
|
|
return reflect.DeepEqual(configuration.ServersTransports[transportName], transport)
|
|
}
|
|
|
|
// AddServiceUDP adds a service to a configuration.
|
|
func AddServiceUDP(configuration *dynamic.UDPConfiguration, serviceName string, service *dynamic.UDPService) bool {
|
|
if _, ok := configuration.Services[serviceName]; !ok {
|
|
configuration.Services[serviceName] = service
|
|
return true
|
|
}
|
|
|
|
if !configuration.Services[serviceName].LoadBalancer.Mergeable(service.LoadBalancer) {
|
|
return false
|
|
}
|
|
|
|
uniq := map[string]struct{}{}
|
|
for _, server := range configuration.Services[serviceName].LoadBalancer.Servers {
|
|
uniq[server.Address] = struct{}{}
|
|
}
|
|
|
|
for _, server := range service.LoadBalancer.Servers {
|
|
if _, ok := uniq[server.Address]; !ok {
|
|
configuration.Services[serviceName].LoadBalancer.Servers = append(configuration.Services[serviceName].LoadBalancer.Servers, server)
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// AddRouterUDP adds a router to a configuration.
|
|
func AddRouterUDP(configuration *dynamic.UDPConfiguration, routerName string, router *dynamic.UDPRouter) bool {
|
|
if _, ok := configuration.Routers[routerName]; !ok {
|
|
configuration.Routers[routerName] = router
|
|
return true
|
|
}
|
|
|
|
return reflect.DeepEqual(configuration.Routers[routerName], router)
|
|
}
|
|
|
|
// AddService adds a service to a configuration.
|
|
func AddService(configuration *dynamic.HTTPConfiguration, serviceName string, service *dynamic.Service) bool {
|
|
if _, ok := configuration.Services[serviceName]; !ok {
|
|
configuration.Services[serviceName] = service
|
|
return true
|
|
}
|
|
|
|
if !configuration.Services[serviceName].LoadBalancer.Mergeable(service.LoadBalancer) {
|
|
return false
|
|
}
|
|
|
|
uniq := map[string]struct{}{}
|
|
for _, server := range configuration.Services[serviceName].LoadBalancer.Servers {
|
|
uniq[server.URL] = struct{}{}
|
|
}
|
|
|
|
for _, server := range service.LoadBalancer.Servers {
|
|
if _, ok := uniq[server.URL]; !ok {
|
|
configuration.Services[serviceName].LoadBalancer.Servers = append(configuration.Services[serviceName].LoadBalancer.Servers, server)
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// AddRouter adds a router to a configuration.
|
|
func AddRouter(configuration *dynamic.HTTPConfiguration, routerName string, router *dynamic.Router) bool {
|
|
if _, ok := configuration.Routers[routerName]; !ok {
|
|
configuration.Routers[routerName] = router
|
|
return true
|
|
}
|
|
|
|
return reflect.DeepEqual(configuration.Routers[routerName], router)
|
|
}
|
|
|
|
// AddTransport adds a servers transport to a configuration.
|
|
func AddTransport(configuration *dynamic.HTTPConfiguration, transportName string, transport *dynamic.ServersTransport) bool {
|
|
if _, ok := configuration.ServersTransports[transportName]; !ok {
|
|
configuration.ServersTransports[transportName] = transport
|
|
return true
|
|
}
|
|
|
|
return reflect.DeepEqual(configuration.ServersTransports[transportName], transport)
|
|
}
|
|
|
|
// AddMiddleware adds a middleware to a configuration.
|
|
func AddMiddleware(configuration *dynamic.HTTPConfiguration, middlewareName string, middleware *dynamic.Middleware) bool {
|
|
if _, ok := configuration.Middlewares[middlewareName]; !ok {
|
|
configuration.Middlewares[middlewareName] = middleware
|
|
return true
|
|
}
|
|
|
|
return reflect.DeepEqual(configuration.Middlewares[middlewareName], middleware)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// BuildTCPRouterConfiguration Builds a router configuration.
|
|
func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCPConfiguration) {
|
|
for routerName, router := range configuration.Routers {
|
|
loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
|
|
|
|
if len(router.Rule) == 0 {
|
|
delete(configuration.Routers, routerName)
|
|
loggerRouter.Error().Msg("Empty rule")
|
|
continue
|
|
}
|
|
|
|
if len(router.Service) == 0 {
|
|
if len(configuration.Services) > 1 {
|
|
delete(configuration.Routers, routerName)
|
|
loggerRouter.Error().
|
|
Msg("Could not define the service name for the router: too many services")
|
|
continue
|
|
}
|
|
|
|
for serviceName := range configuration.Services {
|
|
router.Service = serviceName
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// BuildUDPRouterConfiguration Builds a router configuration.
|
|
func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDPConfiguration) {
|
|
for routerName, router := range configuration.Routers {
|
|
loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
|
|
|
|
if len(router.Service) > 0 {
|
|
continue
|
|
}
|
|
|
|
if len(configuration.Services) > 1 {
|
|
delete(configuration.Routers, routerName)
|
|
loggerRouter.
|
|
Error().Msg("Could not define the service name for the router: too many services")
|
|
continue
|
|
}
|
|
|
|
for serviceName := range configuration.Services {
|
|
router.Service = serviceName
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// BuildRouterConfiguration Builds a router configuration.
|
|
func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPConfiguration, defaultRouterName string, defaultRuleTpl *template.Template, model interface{}) {
|
|
if len(configuration.Routers) == 0 {
|
|
if len(configuration.Services) > 1 {
|
|
log.Ctx(ctx).Info().Msg("Could not create a router for the container: too many services")
|
|
} else {
|
|
configuration.Routers = make(map[string]*dynamic.Router)
|
|
configuration.Routers[defaultRouterName] = &dynamic.Router{}
|
|
}
|
|
}
|
|
|
|
for routerName, router := range configuration.Routers {
|
|
loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
|
|
|
|
if len(router.Rule) == 0 {
|
|
writer := &bytes.Buffer{}
|
|
if err := defaultRuleTpl.Execute(writer, model); err != nil {
|
|
loggerRouter.Error().Err(err).Msg("Error while parsing default rule")
|
|
delete(configuration.Routers, routerName)
|
|
continue
|
|
}
|
|
|
|
router.Rule = writer.String()
|
|
if len(router.Rule) == 0 {
|
|
loggerRouter.Error().Msg("Undefined rule")
|
|
delete(configuration.Routers, routerName)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(router.Service) == 0 {
|
|
if len(configuration.Services) > 1 {
|
|
delete(configuration.Routers, routerName)
|
|
loggerRouter.Error().
|
|
Msg("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), "-")
|
|
}
|