300 lines
7.6 KiB
Go
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
|
|
}
|