mirror of
https://github.com/traefik/traefik.git
synced 2026-05-28 04:35:59 -04:00
Merge branch v2.11 into v3.6
This commit is contained in:
commit
9f3f1725d2
5 changed files with 136 additions and 46 deletions
|
|
@ -1,3 +1,10 @@
|
|||
## [v2.11.45](https://github.com/traefik/traefik/tree/v2.11.45) (2026-05-05)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.44...v2.11.45)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s/crd]** Remove cross-provider sanitization for Kubernetes service loading ([#13087](https://github.com/traefik/traefik/pull/13087) @rtribotte)
|
||||
- **[docker, ecs]** Migrate to github.com/moby/moby modules ([#13053](https://github.com/traefik/traefik/pull/13053) @mmatur)
|
||||
|
||||
## [v3.6.15](https://github.com/traefik/traefik/tree/v3.6.15) (2026-04-29)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v3.6.14...v3.6.15)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TraefikService
|
||||
metadata:
|
||||
name: errorpage-wrr
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
weighted:
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
weight: 1
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: errorpage
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
errors:
|
||||
status:
|
||||
- "404"
|
||||
- "500"
|
||||
query: query
|
||||
service:
|
||||
name: errorpage-wrr
|
||||
kind: TraefikService
|
||||
|
|
@ -266,16 +266,20 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
|||
continue
|
||||
}
|
||||
|
||||
errorPage, errorPageService, err := p.createErrorPageMiddleware(client, middleware.Namespace, middleware.Spec.Errors)
|
||||
errorPageName, errorPage, errorPageService, err := p.createErrorPageMiddleware(ctxMid, client, middleware.Namespace, middleware.Spec.Errors)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Error while reading error page middleware")
|
||||
continue
|
||||
}
|
||||
|
||||
if errorPage != nil && errorPageService != nil {
|
||||
serviceName := id + "-errorpage-service"
|
||||
errorPage.Service = serviceName
|
||||
conf.HTTP.Services[serviceName] = errorPageService
|
||||
if errorPage != nil {
|
||||
if errorPageService != nil {
|
||||
serviceName := id + "-errorpage-service"
|
||||
errorPage.Service = serviceName
|
||||
conf.HTTP.Services[serviceName] = errorPageService
|
||||
} else {
|
||||
errorPage.Service = errorPageName
|
||||
}
|
||||
}
|
||||
|
||||
plugin, err := createPluginMiddleware(client, middleware.Namespace, middleware.Spec.Plugin)
|
||||
|
|
@ -604,15 +608,9 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
|||
return conf
|
||||
}
|
||||
|
||||
func (p *Provider) createErrorPageMiddleware(client Client, namespace string, errorPage *traefikv1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
|
||||
func (p *Provider) createErrorPageMiddleware(ctx context.Context, client Client, namespace string, errorPage *traefikv1alpha1.ErrorPage) (string, *dynamic.ErrorPage, *dynamic.Service, error) {
|
||||
if errorPage == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
errorPageMiddleware := &dynamic.ErrorPage{
|
||||
Status: errorPage.Status,
|
||||
StatusRewrites: errorPage.StatusRewrites,
|
||||
Query: errorPage.Query,
|
||||
return "", nil, nil, nil
|
||||
}
|
||||
|
||||
cb := configBuilder{
|
||||
|
|
@ -622,12 +620,16 @@ func (p *Provider) createErrorPageMiddleware(client Client, namespace string, er
|
|||
allowEmptyServices: p.AllowEmptyServices,
|
||||
}
|
||||
|
||||
balancerServerHTTP, err := cb.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec)
|
||||
balancerName, balancerServerHTTP, err := cb.nameAndService(ctx, namespace, errorPage.Service.LoadBalancerSpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
return errorPageMiddleware, balancerServerHTTP, nil
|
||||
return balancerName, &dynamic.ErrorPage{
|
||||
Status: errorPage.Status,
|
||||
StatusRewrites: errorPage.StatusRewrites,
|
||||
Query: errorPage.Query,
|
||||
}, balancerServerHTTP, nil
|
||||
}
|
||||
|
||||
// getServicePort always returns a valid port, an error otherwise.
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ func (c configBuilder) buildMirroring(ctx context.Context, tService *traefikv1al
|
|||
}
|
||||
|
||||
// buildServersLB creates the configuration for the load-balancer of servers defined by svc.
|
||||
func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.LoadBalancerSpec) (*dynamic.Service, error) {
|
||||
func (c configBuilder) buildServersLB(svc traefikv1alpha1.LoadBalancerSpec) (*dynamic.Service, error) {
|
||||
lb := &dynamic.ServersLoadBalancer{}
|
||||
lb.SetDefaults()
|
||||
|
||||
|
|
@ -389,7 +389,7 @@ func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.Load
|
|||
// Here we are just logging a warning as the default value is already applied.
|
||||
case "RoundRobin":
|
||||
log.Warn().
|
||||
Str("namespace", namespace).
|
||||
Str("namespace", svc.Namespace).
|
||||
Str("service", svc.Name).
|
||||
Msgf("RoundRobin strategy value is deprecated, please use %s value instead", dynamic.BalancerStrategyWRR)
|
||||
|
||||
|
|
@ -398,7 +398,7 @@ func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.Load
|
|||
}
|
||||
}
|
||||
|
||||
servers, err := c.loadServers(namespace, svc)
|
||||
servers, err := c.loadServers(svc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -493,7 +493,7 @@ func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.Load
|
|||
}
|
||||
}
|
||||
|
||||
lb.ServersTransport, err = c.makeServersTransportKey(namespace, svc.ServersTransport)
|
||||
lb.ServersTransport, err = c.makeServersTransportKey(svc.Namespace, svc.ServersTransport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -520,21 +520,13 @@ func (c configBuilder) makeServersTransportKey(parentNamespace string, serversTr
|
|||
return provider.Normalize(makeID(parentNamespace, serversTransportName)), nil
|
||||
}
|
||||
|
||||
func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) {
|
||||
namespace := namespaceOrFallback(svc, parentNamespace)
|
||||
|
||||
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, namespace) {
|
||||
return nil, fmt.Errorf("load balancer service %s/%s is not in the parent resource namespace %s", svc.Namespace, svc.Name, parentNamespace)
|
||||
}
|
||||
|
||||
// If the service uses explicitly the provider suffix
|
||||
sanitizedName := strings.TrimSuffix(svc.Name, providerNamespaceSeparator+providerName)
|
||||
service, exists, err := c.client.GetService(namespace, sanitizedName)
|
||||
func (c configBuilder) loadServers(svc traefikv1alpha1.LoadBalancerSpec) ([]dynamic.Server, error) {
|
||||
service, exists, err := c.client.GetService(svc.Namespace, svc.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("kubernetes service not found: %s/%s", namespace, sanitizedName)
|
||||
return nil, fmt.Errorf("kubernetes service not found: %s/%s", svc.Namespace, svc.Name)
|
||||
}
|
||||
|
||||
svcPort, err := getServicePort(service, svc.Port)
|
||||
|
|
@ -543,12 +535,12 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
|
|||
}
|
||||
|
||||
if service.Spec.Type != corev1.ServiceTypeExternalName && svc.HealthCheck != nil {
|
||||
return nil, fmt.Errorf("healthCheck allowed only for ExternalName services: %s/%s", namespace, sanitizedName)
|
||||
return nil, fmt.Errorf("healthCheck allowed only for ExternalName services: %s/%s", svc.Namespace, svc.Name)
|
||||
}
|
||||
|
||||
if service.Spec.Type == corev1.ServiceTypeExternalName {
|
||||
if !c.allowExternalNameServices {
|
||||
return nil, fmt.Errorf("externalName services not allowed: %s/%s", namespace, sanitizedName)
|
||||
return nil, fmt.Errorf("externalName services not allowed: %s/%s", svc.Namespace, svc.Name)
|
||||
}
|
||||
|
||||
protocol, err := parseServiceProtocol(svc.Scheme, svcPort.Name, svcPort.Port)
|
||||
|
|
@ -590,7 +582,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
|
|||
return nil, nodesErr
|
||||
}
|
||||
if !nodesExists || len(nodes) == 0 {
|
||||
return nil, fmt.Errorf("nodes not found for NodePort service %s/%s", namespace, sanitizedName)
|
||||
return nil, fmt.Errorf("nodes not found for NodePort service %s/%s", svc.Namespace, svc.Name)
|
||||
}
|
||||
|
||||
protocol, err := parseServiceProtocol(svc.Scheme, svcPort.Name, svcPort.Port)
|
||||
|
|
@ -611,13 +603,13 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
|
|||
}
|
||||
|
||||
if len(servers) == 0 {
|
||||
return nil, fmt.Errorf("no servers were generated for service %s in namespace", sanitizedName)
|
||||
return nil, fmt.Errorf("no servers were generated for service %s in namespace", svc.Name)
|
||||
}
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
endpointSlices, err := c.client.GetEndpointSlicesForService(namespace, sanitizedName)
|
||||
endpointSlices, err := c.client.GetEndpointSlicesForService(svc.Namespace, svc.Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting endpointslices: %w", err)
|
||||
}
|
||||
|
|
@ -663,7 +655,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
|
|||
}
|
||||
|
||||
if len(servers) == 0 && !c.allowEmptyServices {
|
||||
return nil, fmt.Errorf("no servers found for %s/%s", namespace, sanitizedName)
|
||||
return nil, fmt.Errorf("no servers found for %s/%s", svc.Namespace, svc.Name)
|
||||
}
|
||||
|
||||
return servers, nil
|
||||
|
|
@ -676,25 +668,26 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
|
|||
func (c configBuilder) nameAndService(ctx context.Context, parentNamespace string, service traefikv1alpha1.LoadBalancerSpec) (string, *dynamic.Service, error) {
|
||||
svcCtx := log.Ctx(ctx).With().Str(logs.ServiceName, service.Name).Logger().WithContext(ctx)
|
||||
|
||||
namespace := namespaceOrFallback(service, parentNamespace)
|
||||
service = *service.DeepCopy()
|
||||
service.Namespace = namespaceOrFallback(service, parentNamespace)
|
||||
|
||||
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, namespace) {
|
||||
if !isNamespaceAllowed(c.allowCrossNamespace, parentNamespace, service.Namespace) {
|
||||
return "", nil, fmt.Errorf("service %s/%s not in the parent resource namespace %s", service.Namespace, service.Name, parentNamespace)
|
||||
}
|
||||
|
||||
switch service.Kind {
|
||||
case "", "Service":
|
||||
serversLB, err := c.buildServersLB(namespace, service)
|
||||
serversLB, err := c.buildServersLB(service)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
fullName := fullServiceName(svcCtx, namespace, service, service.Port)
|
||||
fullName := fullServiceName(svcCtx, service, service.Port)
|
||||
|
||||
return fullName, serversLB, nil
|
||||
|
||||
case "TraefikService":
|
||||
return fullServiceName(svcCtx, namespace, service, intstr.FromInt(0)), nil, nil
|
||||
return fullServiceName(svcCtx, service, intstr.FromInt(0)), nil, nil
|
||||
|
||||
default:
|
||||
return "", nil, fmt.Errorf("unsupported service kind %s", service.Kind)
|
||||
|
|
@ -742,18 +735,18 @@ func splitSvcNameProvider(name string) (string, string) {
|
|||
return svc, pvd
|
||||
}
|
||||
|
||||
func fullServiceName(ctx context.Context, namespace string, service traefikv1alpha1.LoadBalancerSpec, port intstr.IntOrString) string {
|
||||
func fullServiceName(ctx context.Context, service traefikv1alpha1.LoadBalancerSpec, port intstr.IntOrString) string {
|
||||
if (port.Type == intstr.Int && port.IntVal != 0) || (port.Type == intstr.String && port.StrVal != "") {
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s-%s", namespace, service.Name, &port))
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s-%s", service.Namespace, service.Name, &port))
|
||||
}
|
||||
|
||||
if !strings.Contains(service.Name, providerNamespaceSeparator) {
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s", namespace, service.Name))
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s", service.Namespace, service.Name))
|
||||
}
|
||||
|
||||
name, pName := splitSvcNameProvider(service.Name)
|
||||
if pName == providerName {
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s", namespace, name))
|
||||
return provider.Normalize(fmt.Sprintf("%s-%s", service.Namespace, name))
|
||||
}
|
||||
|
||||
if service.Namespace != "" {
|
||||
|
|
|
|||
|
|
@ -4394,6 +4394,65 @@ func TestLoadIngressRoutes(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Error page middleware referencing a TraefikService",
|
||||
paths: []string{"services.yml", "with_error_page_traefik_service.yml"},
|
||||
expected: &dynamic.Configuration{
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: map[string]*dynamic.UDPRouter{},
|
||||
Services: map[string]*dynamic.UDPService{},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
ServersTransports: map[string]*dynamic.TCPServersTransport{},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{},
|
||||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"default-errorpage": {
|
||||
Errors: &dynamic.ErrorPage{
|
||||
Status: []string{"404", "500"},
|
||||
Service: "default-errorpage-wrr",
|
||||
Query: "query",
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-errorpage-wrr": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
Name: "default-whoami-80",
|
||||
Weight: func(i int) *int { return &i }(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"default-whoami-80": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Strategy: dynamic.BalancerStrategyWRR,
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://10.10.0.1:80",
|
||||
},
|
||||
{
|
||||
URL: "http://10.10.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: ptypes.Duration(100 * time.Millisecond),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Simple Ingress Route, with options",
|
||||
paths: []string{"services.yml", "with_options.yml"},
|
||||
|
|
|
|||
Loading…
Reference in a new issue