1
0
Fork 0

Merge branch v3.4 into master

This commit is contained in:
kevinpollet 2025-06-02 16:54:12 +02:00
commit 289d6e5dca
No known key found for this signature in database
GPG key ID: 0C9A5DDD1B292453
195 changed files with 1963 additions and 892 deletions

View file

@ -1,7 +1,6 @@
package crd
import (
"context"
"os"
"path/filepath"
"strings"
@ -1677,7 +1676,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
AllowEmptyServices: test.allowEmptyServices,
}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}
@ -5313,7 +5312,7 @@ func TestLoadIngressRoutes(t *testing.T) {
AllowEmptyServices: test.allowEmptyServices,
}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}
@ -5389,7 +5388,7 @@ func TestLoadIngressRoutes_multipleEndpointAddresses(t *testing.T) {
}
p := Provider{}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
service, ok := conf.HTTP.Services["default-test-route-6b204d94623b3df4370c"]
require.True(t, ok)
@ -5904,7 +5903,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
AllowEmptyServices: test.allowEmptyServices,
}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}
@ -7402,7 +7401,7 @@ func TestCrossNamespace(t *testing.T) {
p := Provider{AllowCrossNamespace: test.allowCrossNamespace}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}
@ -7672,7 +7671,7 @@ func TestExternalNameService(t *testing.T) {
p := Provider{AllowExternalNameServices: test.allowExternalNameService}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}
@ -7854,7 +7853,7 @@ func TestNativeLB(t *testing.T) {
p := Provider{}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}
@ -8122,7 +8121,7 @@ func TestNodePortLB(t *testing.T) {
DisableClusterScopeResources: test.disableClusterScope,
}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}
@ -8634,7 +8633,7 @@ func TestGlobalNativeLB(t *testing.T) {
p := Provider{NativeLBByDefault: test.NativeLBByDefault}
conf := p.loadConfigurationFromCRD(context.Background(), client)
conf := p.loadConfigurationFromCRD(t.Context(), client)
assert.Equal(t, test.expected, conf)
})
}

View file

@ -81,7 +81,7 @@ type ForwardingTimeouts struct {
// RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
// If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
// +kubebuilder:validation:XValidation:rule="has(self.secret) && has(self.configMap)",message="RootCA cannot have both Secret and ConfigMap defined."
// +kubebuilder:validation:XValidation:rule="!has(self.secret) || !has(self.configMap)",message="RootCA cannot have both Secret and ConfigMap defined."
type RootCA struct {
// Secret defines the name of a Secret that holds a CA certificate.
// The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.

View file

@ -1,7 +1,6 @@
package gateway
import (
"context"
"errors"
"net/http"
"os"
@ -75,14 +74,14 @@ func TestGatewayClassLabelSelector(t *testing.T) {
client: client,
}
_ = p.loadConfigurationFromGateways(context.Background())
_ = p.loadConfigurationFromGateways(t.Context())
gw, err := gwClient.GatewayV1().Gateways("default").Get(context.Background(), "traefik-external", metav1.GetOptions{})
gw, err := gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-external", metav1.GetOptions{})
require.NoError(t, err)
assert.Empty(t, gw.Status.Addresses)
gw, err = gwClient.GatewayV1().Gateways("default").Get(context.Background(), "traefik-internal", metav1.GetOptions{})
gw, err = gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-internal", metav1.GetOptions{})
require.NoError(t, err)
require.Len(t, gw.Status.Addresses, 1)
require.NotNil(t, gw.Status.Addresses[0].Type)
@ -2556,7 +2555,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
client: client,
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -2983,7 +2982,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) {
p.RegisterBackendFuncs(group, kind, backendFunc)
}
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -3269,7 +3268,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
p.RegisterFilterFuncs(group, kind, filterFunc)
}
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -3561,7 +3560,7 @@ func TestLoadGRPCRoutes_filterExtensionRef(t *testing.T) {
p.RegisterFilterFuncs(group, kind, filterFunc)
}
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -4463,7 +4462,7 @@ func TestLoadTCPRoutes(t *testing.T) {
client: client,
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -5742,7 +5741,7 @@ func TestLoadTLSRoutes(t *testing.T) {
client: client,
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -6798,7 +6797,7 @@ func TestLoadMixedRoutes(t *testing.T) {
client: client,
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -7134,7 +7133,7 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) {
client: client,
}
conf := p.loadConfigurationFromGateways(context.Background())
conf := p.loadConfigurationFromGateways(t.Context())
assert.Equal(t, test.expected, conf)
})
}
@ -8186,7 +8185,7 @@ func newGatewaySimpleClientSet(t *testing.T, objects ...runtime.Object) *gatefak
continue
}
_, err := client.GatewayV1().Gateways(gateway.Namespace).Create(context.Background(), gateway, metav1.CreateOptions{})
_, err := client.GatewayV1().Gateways(gateway.Namespace).Create(t.Context(), gateway, metav1.CreateOptions{})
require.NoError(t, err)
}

View file

@ -1,7 +1,6 @@
package ingress
import (
"context"
"errors"
"testing"
"time"
@ -249,7 +248,7 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) {
assert.Fail(t, "expected to receive event for endpointslices")
}
emptyEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(context.TODO(), "empty-endpointslice", metav1.GetOptions{})
emptyEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(t.Context(), "empty-endpointslice", metav1.GetOptions{})
assert.NoError(t, err)
// Update endpoint annotation and resource version (apparently not done by fake client itself)
@ -257,7 +256,7 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) {
// This reflects the behavior of kubernetes controllers which use endpoint annotations for leader election.
emptyEndpointSlice.Annotations["test-annotation"] = "___"
emptyEndpointSlice.ResourceVersion = "1245"
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), emptyEndpointSlice, metav1.UpdateOptions{})
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), emptyEndpointSlice, metav1.UpdateOptions{})
require.NoError(t, err)
select {
@ -269,12 +268,12 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) {
case <-time.After(50 * time.Millisecond):
}
filledEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(context.TODO(), "filled-endpointslice", metav1.GetOptions{})
filledEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(t.Context(), "filled-endpointslice", metav1.GetOptions{})
assert.NoError(t, err)
filledEndpointSlice.Endpoints[0].Addresses[0] = "10.13.37.2"
filledEndpointSlice.ResourceVersion = "1235"
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), filledEndpointSlice, metav1.UpdateOptions{})
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), filledEndpointSlice, metav1.UpdateOptions{})
require.NoError(t, err)
select {
@ -296,7 +295,7 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) {
newPortNumber := int32(42)
filledEndpointSlice.Ports[0].Port = &newPortNumber
filledEndpointSlice.ResourceVersion = "1236"
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), filledEndpointSlice, metav1.UpdateOptions{})
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), filledEndpointSlice, metav1.UpdateOptions{})
require.NoError(t, err)
select {

View file

@ -0,0 +1,18 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
spec:
rules:
- host: traefik.tchouk
http:
paths:
- path: /bar
backend:
resource:
kind: Service
name: service1
pathType: Prefix

View file

@ -0,0 +1,15 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
spec:
rules:
- host: traefik.tchouk
http:
paths:
- path: /bar
backend: {}
pathType: Prefix

View file

@ -316,6 +316,17 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
}
for _, pa := range rule.HTTP.Paths {
if pa.Backend.Resource != nil {
// https://kubernetes.io/docs/concepts/services-networking/ingress/#resource-backend
logger.Error().Msg("Resource backends are not supported")
continue
}
if pa.Backend.Service == nil {
logger.Error().Msg("Missing service definition")
continue
}
service, err := p.loadService(client, ingress.Namespace, pa.Backend)
if err != nil {
logger.Error().
@ -493,15 +504,6 @@ func (p *Provider) shouldProcessIngress(ingress *netv1.Ingress, ingressClasses [
}
func (p *Provider) loadService(client Client, namespace string, backend netv1.IngressBackend) (*dynamic.Service, error) {
if backend.Resource != nil {
// https://kubernetes.io/docs/concepts/services-networking/ingress/#resource-backend
return nil, errors.New("resource backends are not supported")
}
if backend.Service == nil {
return nil, errors.New("missing service definition")
}
service, exists, err := client.GetService(namespace, backend.Service.Name)
if err != nil {
return nil, err

View file

@ -1,7 +1,6 @@
package ingress
import (
"context"
"errors"
"fmt"
"math"
@ -527,6 +526,28 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
},
},
},
{
desc: "Ingress with backend resource",
allowEmptyServices: true,
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{},
Services: map[string]*dynamic.Service{},
},
},
},
{
desc: "Ingress without backend",
allowEmptyServices: true,
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{},
Services: map[string]*dynamic.Service{},
},
},
},
{
desc: "Ingress with one service without endpoint",
expected: &dynamic.Configuration{
@ -1675,7 +1696,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
DefaultRuleSyntax: test.defaultRuleSyntax,
StrictPrefixMatching: test.strictPrefixMatching,
}
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
conf := p.loadConfigurationFromIngresses(t.Context(), clientMock)
assert.Equal(t, test.expected, conf)
})
@ -1801,7 +1822,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
p := Provider{IngressClass: test.ingressClass}
p.AllowExternalNameServices = test.allowExternalNameServices
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
conf := p.loadConfigurationFromIngresses(t.Context(), clientMock)
assert.Equal(t, test.expected, conf)
})
@ -1851,7 +1872,7 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) {
clientMock := newClientMock(generateTestFilename(test.desc))
p := Provider{IngressClass: test.ingressClass}
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
conf := p.loadConfigurationFromIngresses(t.Context(), clientMock)
assert.Equal(t, test.expected, conf)
})
@ -1912,7 +1933,7 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
clientMock := newClientMock(generateTestFilename(test.desc))
p := Provider{DisableClusterScopeResources: test.clusterScopeDisabled}
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
conf := p.loadConfigurationFromIngresses(t.Context(), clientMock)
assert.Equal(t, test.expected, conf)
})
@ -2084,7 +2105,7 @@ func TestGetCertificates(t *testing.T) {
t.Parallel()
tlsConfigs := map[string]*tls.CertAndStores{}
err := getCertificates(context.Background(), test.ingress, test.client, tlsConfigs)
err := getCertificates(t.Context(), test.ingress, test.client, tlsConfigs)
if test.errResult != "" {
assert.EqualError(t, err, test.errResult)
@ -2170,7 +2191,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) {
IngressClass: test.ingressClass,
NativeLBByDefault: true,
}
conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
conf := p.loadConfigurationFromIngresses(t.Context(), clientMock)
assert.Equal(t, test.expected, conf)
})
@ -2270,9 +2291,9 @@ func TestIngressEndpointPublishedService(t *testing.T) {
PublishedService: "default/published-service",
},
}
p.loadConfigurationFromIngresses(context.Background(), client)
p.loadConfigurationFromIngresses(t.Context(), client)
ingress, err := kubeClient.NetworkingV1().Ingresses(metav1.NamespaceDefault).Get(context.Background(), "foo", metav1.GetOptions{})
ingress, err := kubeClient.NetworkingV1().Ingresses(metav1.NamespaceDefault).Get(t.Context(), "foo", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, test.expected, ingress.Status.LoadBalancer.Ingress)
@ -2380,9 +2401,11 @@ func TestStrictPrefixMatchingRule(t *testing.T) {
t.Parallel()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
muxer, err := traefikhttp.NewMuxer()
parser, err := traefikhttp.NewSyntaxParser()
require.NoError(t, err)
muxer := traefikhttp.NewMuxer(parser)
rule := buildStrictPrefixMatchingRule(tt.path)
err = muxer.AddRoute(rule, "", 0, handler)
require.NoError(t, err)