traefik/pkg/provider/rancher/config.go
2022-11-21 18:36:05 +01:00

300 lines
7.6 KiB
Go

package rancher
import (
"context"
"errors"
"fmt"
"net"
"strings"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/config/label"
"github.com/traefik/traefik/v2/pkg/provider"
"github.com/traefik/traefik/v2/pkg/provider/constraints"
)
func (p *Provider) buildConfiguration(ctx context.Context, services []rancherData) *dynamic.Configuration {
configurations := make(map[string]*dynamic.Configuration)
for _, service := range services {
logger := log.Ctx(ctx).With().Str("service", service.Name).Logger()
ctxService := logger.WithContext(ctx)
if !p.keepService(ctx, service) {
continue
}
confFromLabel, err := label.DecodeConfiguration(service.Labels)
if err != nil {
logger.Error().Err(err).Send()
continue
}
var tcpOrUDP bool
if len(confFromLabel.TCP.Routers) > 0 || len(confFromLabel.TCP.Services) > 0 {
tcpOrUDP = true
err := p.buildTCPServiceConfiguration(ctxService, service, confFromLabel.TCP)
if err != nil {
logger.Error().Err(err).Send()
continue
}
provider.BuildTCPRouterConfiguration(ctxService, confFromLabel.TCP)
}
if len(confFromLabel.UDP.Routers) > 0 || len(confFromLabel.UDP.Services) > 0 {
tcpOrUDP = true
err := p.buildUDPServiceConfiguration(ctxService, service, confFromLabel.UDP)
if err != nil {
logger.Error().Err(err).Send()
continue
}
provider.BuildUDPRouterConfiguration(ctxService, confFromLabel.UDP)
}
if tcpOrUDP && len(confFromLabel.HTTP.Routers) == 0 &&
len(confFromLabel.HTTP.Middlewares) == 0 &&
len(confFromLabel.HTTP.Services) == 0 {
configurations[service.Name] = confFromLabel
continue
}
err = p.buildServiceConfiguration(ctx, service, confFromLabel.HTTP)
if err != nil {
logger.Error().Err(err).Send()
continue
}
model := struct {
Name string
Labels map[string]string
}{
Name: service.Name,
Labels: service.Labels,
}
provider.BuildRouterConfiguration(ctx, confFromLabel.HTTP, service.Name, p.defaultRuleTpl, model)
configurations[service.Name] = confFromLabel
}
return provider.Merge(ctx, configurations)
}
func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, service rancherData, configuration *dynamic.TCPConfiguration) error {
serviceName := service.Name
if len(configuration.Services) == 0 {
configuration.Services = make(map[string]*dynamic.TCPService)
lb := &dynamic.TCPServersLoadBalancer{}
lb.SetDefaults()
configuration.Services[serviceName] = &dynamic.TCPService{
LoadBalancer: lb,
}
}
for _, confService := range configuration.Services {
err := p.addServerTCP(ctx, service, confService.LoadBalancer)
if err != nil {
return err
}
}
return nil
}
func (p *Provider) buildUDPServiceConfiguration(ctx context.Context, service rancherData, configuration *dynamic.UDPConfiguration) error {
serviceName := service.Name
if len(configuration.Services) == 0 {
configuration.Services = make(map[string]*dynamic.UDPService)
lb := &dynamic.UDPServersLoadBalancer{}
configuration.Services[serviceName] = &dynamic.UDPService{
LoadBalancer: lb,
}
}
for _, confService := range configuration.Services {
err := p.addServerUDP(ctx, service, confService.LoadBalancer)
if err != nil {
return err
}
}
return nil
}
func (p *Provider) buildServiceConfiguration(ctx context.Context, service rancherData, configuration *dynamic.HTTPConfiguration) error {
serviceName := service.Name
if len(configuration.Services) == 0 {
configuration.Services = make(map[string]*dynamic.Service)
lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults()
configuration.Services[serviceName] = &dynamic.Service{
LoadBalancer: lb,
}
}
for _, confService := range configuration.Services {
err := p.addServers(ctx, service, confService.LoadBalancer)
if err != nil {
return err
}
}
return nil
}
func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
logger := log.Ctx(ctx)
if !service.ExtraConf.Enable {
logger.Debug().Msg("Filtering disabled service")
return false
}
matches, err := constraints.MatchLabels(service.Labels, p.Constraints)
if err != nil {
logger.Error().Err(err).Msg("Error matching constraint expression")
return false
}
if !matches {
logger.Debug().Msgf("Service pruned by constraint expression: %q", p.Constraints)
return false
}
if p.EnableServiceHealthFilter {
if service.Health != "" && service.Health != healthy && service.Health != updatingHealthy {
logger.Debug().Msgf("Filtering service %s with healthState of %s", service.Name, service.Health)
return false
}
if service.State != "" && service.State != active && service.State != updatingActive && service.State != upgraded && service.State != upgrading {
logger.Debug().Msgf("Filtering service %s with state of %s", service.Name, service.State)
return false
}
}
return true
}
func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
log.Ctx(ctx).Debug().Msgf("Trying to add servers for service %s", service.Name)
if loadBalancer == nil {
return errors.New("load-balancer is not defined")
}
if len(loadBalancer.Servers) == 0 {
loadBalancer.Servers = []dynamic.TCPServer{{}}
}
port := loadBalancer.Servers[0].Port
loadBalancer.Servers[0].Port = ""
if port == "" {
port = getServicePort(service)
}
if port == "" {
return errors.New("port is missing")
}
var servers []dynamic.TCPServer
for _, containerIP := range service.Containers {
servers = append(servers, dynamic.TCPServer{
Address: net.JoinHostPort(containerIP, port),
})
}
loadBalancer.Servers = servers
return nil
}
func (p *Provider) addServerUDP(ctx context.Context, service rancherData, loadBalancer *dynamic.UDPServersLoadBalancer) error {
log.Ctx(ctx).Debug().Msgf("Trying to add servers for service %s", service.Name)
if loadBalancer == nil {
return errors.New("load-balancer is not defined")
}
if len(loadBalancer.Servers) == 0 {
loadBalancer.Servers = []dynamic.UDPServer{{}}
}
port := loadBalancer.Servers[0].Port
loadBalancer.Servers[0].Port = ""
if port == "" {
port = getServicePort(service)
}
if port == "" {
return errors.New("port is missing")
}
var servers []dynamic.UDPServer
for _, containerIP := range service.Containers {
servers = append(servers, dynamic.UDPServer{
Address: net.JoinHostPort(containerIP, port),
})
}
loadBalancer.Servers = servers
return nil
}
func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.ServersLoadBalancer) error {
log.Ctx(ctx).Debug().Msgf("Trying to add servers for service %s", service.Name)
if loadBalancer == nil {
return errors.New("load-balancer is not defined")
}
if len(loadBalancer.Servers) == 0 {
server := dynamic.Server{}
server.SetDefaults()
loadBalancer.Servers = []dynamic.Server{server}
}
port := loadBalancer.Servers[0].Port
loadBalancer.Servers[0].Port = ""
if port == "" {
port = getServicePort(service)
}
if port == "" {
return errors.New("port is missing")
}
var servers []dynamic.Server
for _, containerIP := range service.Containers {
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s", loadBalancer.Servers[0].Scheme, net.JoinHostPort(containerIP, port)),
})
}
loadBalancer.Servers = servers
return nil
}
func getServicePort(data rancherData) string {
rawPort := strings.Split(data.Port, "/")[0]
hostPort := strings.Split(rawPort, ":")
if len(hostPort) >= 2 {
return hostPort[1]
}
if len(hostPort) > 0 && hostPort[0] != "" {
return hostPort[0]
}
return rawPort
}