1
0
Fork 0

Merge current v3.1 into master

This commit is contained in:
mmatur 2024-07-30 15:54:24 +02:00
commit e8324132f9
No known key found for this signature in database
GPG key ID: 2FFE42FC256CFF8E
38 changed files with 587 additions and 383 deletions

View file

@ -50,11 +50,12 @@ type ServiceHealthChecker struct {
metrics metricsHealthCheck
client *http.Client
targets map[string]*url.URL
client *http.Client
targets map[string]*url.URL
serviceName string
}
func NewServiceHealthChecker(ctx context.Context, metrics metricsHealthCheck, config *dynamic.ServerHealthCheck, service StatusSetter, info *runtime.ServiceInfo, transport http.RoundTripper, targets map[string]*url.URL) *ServiceHealthChecker {
func NewServiceHealthChecker(ctx context.Context, metrics metricsHealthCheck, config *dynamic.ServerHealthCheck, service StatusSetter, info *runtime.ServiceInfo, transport http.RoundTripper, targets map[string]*url.URL, serviceName string) *ServiceHealthChecker {
logger := log.Ctx(ctx)
interval := time.Duration(config.Interval)
@ -80,14 +81,15 @@ func NewServiceHealthChecker(ctx context.Context, metrics metricsHealthCheck, co
}
return &ServiceHealthChecker{
balancer: service,
info: info,
config: config,
interval: interval,
timeout: timeout,
targets: targets,
client: client,
metrics: metrics,
balancer: service,
info: info,
config: config,
interval: interval,
timeout: timeout,
targets: targets,
serviceName: serviceName,
client: client,
metrics: metrics,
}
}
@ -136,7 +138,7 @@ func (shc *ServiceHealthChecker) Launch(ctx context.Context) {
shc.info.UpdateServerStatus(target.String(), statusStr)
shc.metrics.ServiceServerUpGauge().
With("service", proxyName, "url", target.String()).
With("service", shc.serviceName, "url", target.String()).
Set(serverUpMetricValue)
}
}

View file

@ -64,7 +64,7 @@ func TestNewServiceHealthChecker_durations(t *testing.T) {
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
healthChecker := NewServiceHealthChecker(context.Background(), nil, test.config, nil, nil, http.DefaultTransport, nil)
healthChecker := NewServiceHealthChecker(context.Background(), nil, test.config, nil, nil, http.DefaultTransport, nil, "")
assert.Equal(t, test.expInterval, healthChecker.interval)
assert.Equal(t, test.expTimeout, healthChecker.timeout)
})
@ -289,7 +289,7 @@ func TestServiceHealthChecker_checkHealthHTTP_NotFollowingRedirects(t *testing.T
Interval: dynamic.DefaultHealthCheckInterval,
Timeout: dynamic.DefaultHealthCheckTimeout,
}
healthChecker := NewServiceHealthChecker(ctx, nil, config, nil, nil, http.DefaultTransport, nil)
healthChecker := NewServiceHealthChecker(ctx, nil, config, nil, nil, http.DefaultTransport, nil, "")
err := healthChecker.checkHealthHTTP(ctx, testhelpers.MustParseURL(server.URL))
require.NoError(t, err)
@ -426,7 +426,7 @@ func TestServiceHealthChecker_Launch(t *testing.T) {
gauge := &testhelpers.CollectingGauge{}
serviceInfo := &runtime.ServiceInfo{}
hc := NewServiceHealthChecker(ctx, &MetricsMock{gauge}, config, lb, serviceInfo, http.DefaultTransport, map[string]*url.URL{"test": targetURL})
hc := NewServiceHealthChecker(ctx, &MetricsMock{gauge}, config, lb, serviceInfo, http.DefaultTransport, map[string]*url.URL{"test": targetURL}, "foobar")
wg := sync.WaitGroup{}
wg.Add(1)
@ -449,6 +449,7 @@ func TestServiceHealthChecker_Launch(t *testing.T) {
assert.Equal(t, test.expNumRemovedServers, lb.numRemovedServers, "removed servers")
assert.Equal(t, test.expNumUpsertedServers, lb.numUpsertedServers, "upserted servers")
assert.InDelta(t, test.expGaugeValue, gauge.GaugeValue, delta, "ServerUp Gauge")
assert.Equal(t, []string{"service", "foobar", "url", targetURL.String()}, gauge.LastLabelValues)
assert.Equal(t, map[string]string{targetURL.String(): test.targetStatus}, serviceInfo.GetAllStatus())
})
}

View file

@ -1,7 +1,7 @@
package logs
import (
kitlog "github.com/go-kit/kit/log"
kitlog "github.com/go-kit/log"
"github.com/rs/zerolog"
)

View file

@ -14,6 +14,7 @@ import (
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/logs"
"github.com/traefik/traefik/v3/pkg/tls"
"golang.org/x/exp/maps"
)
// Merge merges multiple configurations.
@ -422,7 +423,7 @@ func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCP
if len(configuration.Services) > 1 {
delete(configuration.Routers, routerName)
loggerRouter.Error().
Msg("Could not define the service name for the router: too many services")
Msgf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
continue
}
@ -444,8 +445,8 @@ func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDP
if len(configuration.Services) > 1 {
delete(configuration.Routers, routerName)
loggerRouter.
Error().Msg("Could not define the service name for the router: too many services")
loggerRouter.Error().
Msgf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
continue
}
@ -493,7 +494,7 @@ func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPCo
if len(configuration.Services) > 1 {
delete(configuration.Routers, routerName)
loggerRouter.Error().
Msg("Could not define the service name for the router: too many services")
Msgf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
continue
}

View file

@ -10,6 +10,7 @@ import (
"github.com/cenkalti/backoff/v4"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
eventtypes "github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
@ -165,15 +166,15 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
}
func (p *Provider) listContainers(ctx context.Context, dockerClient client.ContainerAPIClient) ([]dockerData, error) {
containerList, err := dockerClient.ContainerList(ctx, dockertypes.ContainerListOptions{})
containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{})
if err != nil {
return nil, err
}
var inspectedContainers []dockerData
// get inspect containers
for _, container := range containerList {
dData := inspectContainers(ctx, dockerClient, container.ID)
for _, c := range containerList {
dData := inspectContainers(ctx, dockerClient, c.ID)
if len(dData.Name) == 0 {
continue
}

View file

@ -6,7 +6,6 @@ import (
"testing"
"time"
"github.com/davecgh/go-spew/spew"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/stretchr/testify/assert"
@ -74,7 +73,7 @@ func TestListTasks(t *testing.T) {
taskDockerData, _ := listTasks(context.Background(), dockerClient, test.service.ID, dockerData, test.networks, test.isGlobalSVC)
if len(test.expectedTasks) != len(taskDockerData) {
t.Errorf("expected tasks %v, got %v", spew.Sdump(test.expectedTasks), spew.Sdump(taskDockerData))
t.Errorf("expected tasks %v, got %v", test.expectedTasks, taskDockerData)
}
for i, taskID := range test.expectedTasks {

View file

@ -978,65 +978,69 @@ func createChainMiddleware(ctx context.Context, namespace string, chain *traefik
}
func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options {
tlsOptionsCRD := client.GetTLSOptions()
tlsOptionsCRDs := client.GetTLSOptions()
var tlsOptions map[string]tls.Options
if len(tlsOptionsCRD) == 0 {
if len(tlsOptionsCRDs) == 0 {
return tlsOptions
}
tlsOptions = make(map[string]tls.Options)
var nsDefault []string
for _, tlsOption := range tlsOptionsCRD {
logger := log.Ctx(ctx).With().Str("tlsOption", tlsOption.Name).Str("namespace", tlsOption.Namespace).Logger()
for _, tlsOptionsCRD := range tlsOptionsCRDs {
logger := log.Ctx(ctx).With().Str("tlsOption", tlsOptionsCRD.Name).Str("namespace", tlsOptionsCRD.Namespace).Logger()
var clientCAs []types.FileOrContent
for _, secretName := range tlsOption.Spec.ClientAuth.SecretNames {
secret, exists, err := client.GetSecret(tlsOption.Namespace, secretName)
for _, secretName := range tlsOptionsCRD.Spec.ClientAuth.SecretNames {
secret, exists, err := client.GetSecret(tlsOptionsCRD.Namespace, secretName)
if err != nil {
logger.Error().Err(err).Msgf("Failed to fetch secret %s/%s", tlsOption.Namespace, secretName)
logger.Error().Err(err).Msgf("Failed to fetch secret %s/%s", tlsOptionsCRD.Namespace, secretName)
continue
}
if !exists {
logger.Warn().Msgf("Secret %s/%s does not exist", tlsOption.Namespace, secretName)
logger.Warn().Msgf("Secret %s/%s does not exist", tlsOptionsCRD.Namespace, secretName)
continue
}
cert, err := getCABlocks(secret, tlsOption.Namespace, secretName)
cert, err := getCABlocks(secret, tlsOptionsCRD.Namespace, secretName)
if err != nil {
logger.Error().Err(err).Msgf("Failed to extract CA from secret %s/%s", tlsOption.Namespace, secretName)
logger.Error().Err(err).Msgf("Failed to extract CA from secret %s/%s", tlsOptionsCRD.Namespace, secretName)
continue
}
clientCAs = append(clientCAs, types.FileOrContent(cert))
}
id := makeID(tlsOption.Namespace, tlsOption.Name)
id := makeID(tlsOptionsCRD.Namespace, tlsOptionsCRD.Name)
// If the name is default, we override the default config.
if tlsOption.Name == tls.DefaultTLSConfigName {
id = tlsOption.Name
nsDefault = append(nsDefault, tlsOption.Namespace)
if tlsOptionsCRD.Name == tls.DefaultTLSConfigName {
id = tlsOptionsCRD.Name
nsDefault = append(nsDefault, tlsOptionsCRD.Namespace)
}
alpnProtocols := tls.DefaultTLSOptions.ALPNProtocols
if len(tlsOption.Spec.ALPNProtocols) > 0 {
alpnProtocols = tlsOption.Spec.ALPNProtocols
tlsOption := tls.Options{}
tlsOption.SetDefaults()
tlsOption.MinVersion = tlsOptionsCRD.Spec.MinVersion
tlsOption.MaxVersion = tlsOptionsCRD.Spec.MaxVersion
if tlsOptionsCRD.Spec.CipherSuites != nil {
tlsOption.CipherSuites = tlsOptionsCRD.Spec.CipherSuites
}
tlsOptions[id] = tls.Options{
MinVersion: tlsOption.Spec.MinVersion,
MaxVersion: tlsOption.Spec.MaxVersion,
CipherSuites: tlsOption.Spec.CipherSuites,
CurvePreferences: tlsOption.Spec.CurvePreferences,
ClientAuth: tls.ClientAuth{
CAFiles: clientCAs,
ClientAuthType: tlsOption.Spec.ClientAuth.ClientAuthType,
},
SniStrict: tlsOption.Spec.SniStrict,
ALPNProtocols: alpnProtocols,
PreferServerCipherSuites: tlsOption.Spec.PreferServerCipherSuites,
tlsOption.CurvePreferences = tlsOptionsCRD.Spec.CurvePreferences
tlsOption.ClientAuth = tls.ClientAuth{
CAFiles: clientCAs,
ClientAuthType: tlsOptionsCRD.Spec.ClientAuth.ClientAuthType,
}
tlsOption.SniStrict = tlsOptionsCRD.Spec.SniStrict
if tlsOptionsCRD.Spec.ALPNProtocols != nil {
tlsOption.ALPNProtocols = tlsOptionsCRD.Spec.ALPNProtocols
}
tlsOptions[id] = tlsOption
}
if len(nsDefault) > 1 {

View file

@ -886,6 +886,21 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
"http/1.1",
"acme-tls/1",
},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},
@ -942,6 +957,21 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
"http/1.1",
"acme-tls/1",
},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},
@ -3549,6 +3579,21 @@ func TestLoadIngressRoutes(t *testing.T) {
"http/1.1",
"acme-tls/1",
},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},
@ -3611,6 +3656,21 @@ func TestLoadIngressRoutes(t *testing.T) {
"http/1.1",
"acme-tls/1",
},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},
@ -6334,6 +6394,21 @@ func TestCrossNamespace(t *testing.T) {
"cross-ns-tls-options-cn": {
MinVersion: "VersionTLS12",
ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},
@ -6382,6 +6457,21 @@ func TestCrossNamespace(t *testing.T) {
"cross-ns-tls-options-cn": {
MinVersion: "VersionTLS12",
ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},
@ -6723,6 +6813,21 @@ func TestCrossNamespace(t *testing.T) {
"cross-ns-tls-options-cn": {
MinVersion: "VersionTLS12",
ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},
@ -6767,6 +6872,21 @@ func TestCrossNamespace(t *testing.T) {
"cross-ns-tls-options-cn": {
MinVersion: "VersionTLS12",
ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"},
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
},
},

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"reflect"
"slices"
"time"
@ -53,7 +54,7 @@ func (reh *resourceEventHandler) OnDelete(obj interface{}) {
type Client interface {
WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error)
UpdateGatewayStatus(ctx context.Context, gateway ktypes.NamespacedName, status gatev1.GatewayStatus) error
UpdateGatewayClassStatus(ctx context.Context, name string, condition metav1.Condition) error
UpdateGatewayClassStatus(ctx context.Context, name string, status gatev1.GatewayClassStatus) error
UpdateHTTPRouteStatus(ctx context.Context, route ktypes.NamespacedName, status gatev1.HTTPRouteStatus) error
UpdateTCPRouteStatus(ctx context.Context, route ktypes.NamespacedName, status gatev1alpha2.TCPRouteStatus) error
UpdateTLSRouteStatus(ctx context.Context, route ktypes.NamespacedName, status gatev1alpha2.TLSRouteStatus) error
@ -378,7 +379,7 @@ func (c *clientWrapper) ListGatewayClasses() ([]*gatev1.GatewayClass, error) {
return c.factoryGatewayClass.Gateway().V1().GatewayClasses().Lister().List(labels.Everything())
}
func (c *clientWrapper) UpdateGatewayClassStatus(ctx context.Context, name string, condition metav1.Condition) error {
func (c *clientWrapper) UpdateGatewayClassStatus(ctx context.Context, name string, status gatev1.GatewayClassStatus) error {
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
currentGatewayClass, err := c.factoryGatewayClass.Gateway().V1().GatewayClasses().Lister().Get(name)
if err != nil {
@ -387,23 +388,12 @@ func (c *clientWrapper) UpdateGatewayClassStatus(ctx context.Context, name strin
return err
}
currentGatewayClass = currentGatewayClass.DeepCopy()
var newConditions []metav1.Condition
for _, cond := range currentGatewayClass.Status.Conditions {
// No update for identical condition.
if cond.Type == condition.Type && cond.Status == condition.Status && cond.ObservedGeneration == condition.ObservedGeneration {
return nil
}
// Keep other condition types.
if cond.Type != condition.Type {
newConditions = append(newConditions, cond)
}
if conditionsEqual(currentGatewayClass.Status.Conditions, status.Conditions) {
return nil
}
// Append the condition to update.
newConditions = append(newConditions, condition)
currentGatewayClass.Status.Conditions = newConditions
currentGatewayClass = currentGatewayClass.DeepCopy()
currentGatewayClass.Status = status
if _, err = c.csGateway.GatewayV1().GatewayClasses().UpdateStatus(ctx, currentGatewayClass, metav1.UpdateOptions{}); err != nil {
// We have to return err itself here (not wrapped inside another error)
@ -433,7 +423,7 @@ func (c *clientWrapper) UpdateGatewayStatus(ctx context.Context, gateway ktypes.
return err
}
if gatewayStatusEquals(currentGateway.Status, status) {
if gatewayStatusEqual(currentGateway.Status, status) {
return nil
}
@ -468,16 +458,22 @@ func (c *clientWrapper) UpdateHTTPRouteStatus(ctx context.Context, route ktypes.
return err
}
// TODO: keep statuses for gateways managed by other Traefik instances.
var parentStatuses []gatev1.RouteParentStatus
for _, currentParentStatus := range currentRoute.Status.Parents {
if currentParentStatus.ControllerName != controllerName {
parentStatuses = append(parentStatuses, currentParentStatus)
parentStatuses := make([]gatev1.RouteParentStatus, len(status.Parents))
copy(parentStatuses, status.Parents)
// keep statuses added by other gateway controllers.
// TODO: we should also keep statuses for gateways managed by other Traefik instances.
for _, parentStatus := range currentRoute.Status.Parents {
if parentStatus.ControllerName != controllerName {
parentStatuses = append(parentStatuses, parentStatus)
continue
}
}
parentStatuses = append(parentStatuses, status.Parents...)
// do not update status when nothing has changed.
if routeParentStatusesEqual(currentRoute.Status.Parents, parentStatuses) {
return nil
}
currentRoute = currentRoute.DeepCopy()
currentRoute.Status = gatev1.HTTPRouteStatus{
@ -514,16 +510,22 @@ func (c *clientWrapper) UpdateTCPRouteStatus(ctx context.Context, route ktypes.N
return err
}
// TODO: keep statuses for gateways managed by other Traefik instances.
var parentStatuses []gatev1alpha2.RouteParentStatus
for _, currentParentStatus := range currentRoute.Status.Parents {
if currentParentStatus.ControllerName != controllerName {
parentStatuses = append(parentStatuses, currentParentStatus)
parentStatuses := make([]gatev1.RouteParentStatus, len(status.Parents))
copy(parentStatuses, status.Parents)
// keep statuses added by other gateway controllers.
// TODO: we should also keep statuses for gateways managed by other Traefik instances.
for _, parentStatus := range currentRoute.Status.Parents {
if parentStatus.ControllerName != controllerName {
parentStatuses = append(parentStatuses, parentStatus)
continue
}
}
parentStatuses = append(parentStatuses, status.Parents...)
// do not update status when nothing has changed.
if routeParentStatusesEqual(currentRoute.Status.Parents, parentStatuses) {
return nil
}
currentRoute = currentRoute.DeepCopy()
currentRoute.Status = gatev1alpha2.TCPRouteStatus{
@ -560,16 +562,22 @@ func (c *clientWrapper) UpdateTLSRouteStatus(ctx context.Context, route ktypes.N
return err
}
// TODO: keep statuses for gateways managed by other Traefik instances.
var parentStatuses []gatev1alpha2.RouteParentStatus
for _, currentParentStatus := range currentRoute.Status.Parents {
if currentParentStatus.ControllerName != controllerName {
parentStatuses = append(parentStatuses, currentParentStatus)
parentStatuses := make([]gatev1.RouteParentStatus, len(status.Parents))
copy(parentStatuses, status.Parents)
// keep statuses added by other gateway controllers.
// TODO: we should also keep statuses for gateways managed by other Traefik instances.
for _, parentStatus := range currentRoute.Status.Parents {
if parentStatus.ControllerName != controllerName {
parentStatuses = append(parentStatuses, parentStatus)
continue
}
}
parentStatuses = append(parentStatuses, status.Parents...)
// do not update status when nothing has changed.
if routeParentStatusesEqual(currentRoute.Status.Parents, parentStatuses) {
return nil
}
currentRoute = currentRoute.DeepCopy()
currentRoute.Status = gatev1alpha2.TLSRouteStatus{
@ -675,12 +683,12 @@ func translateNotFoundError(err error) (bool, error) {
return err == nil, err
}
func gatewayStatusEquals(statusA, statusB gatev1.GatewayStatus) bool {
func gatewayStatusEqual(statusA, statusB gatev1.GatewayStatus) bool {
if len(statusA.Listeners) != len(statusB.Listeners) {
return false
}
if !conditionsEquals(statusA.Conditions, statusB.Conditions) {
if !conditionsEqual(statusA.Conditions, statusB.Conditions) {
return false
}
@ -688,7 +696,7 @@ func gatewayStatusEquals(statusA, statusB gatev1.GatewayStatus) bool {
for _, newListener := range statusB.Listeners {
for _, oldListener := range statusA.Listeners {
if newListener.Name == oldListener.Name {
if !conditionsEquals(newListener.Conditions, oldListener.Conditions) {
if !conditionsEqual(newListener.Conditions, oldListener.Conditions) {
return false
}
@ -704,22 +712,48 @@ func gatewayStatusEquals(statusA, statusB gatev1.GatewayStatus) bool {
return listenerMatches == len(statusA.Listeners)
}
func conditionsEquals(conditionsA, conditionsB []metav1.Condition) bool {
if len(conditionsA) != len(conditionsB) {
func routeParentStatusesEqual(routeParentStatusesA, routeParentStatusesB []gatev1alpha2.RouteParentStatus) bool {
if len(routeParentStatusesA) != len(routeParentStatusesB) {
return false
}
conditionMatches := 0
for _, conditionA := range conditionsA {
for _, conditionB := range conditionsB {
if conditionA.Type == conditionB.Type {
if conditionA.Reason != conditionB.Reason || conditionA.Status != conditionB.Status || conditionA.Message != conditionB.Message || conditionA.ObservedGeneration != conditionB.ObservedGeneration {
return false
}
conditionMatches++
}
for _, sA := range routeParentStatusesA {
if !slices.ContainsFunc(routeParentStatusesB, func(sB gatev1alpha2.RouteParentStatus) bool {
return routeParentStatusEqual(sB, sA)
}) {
return false
}
}
return conditionMatches == len(conditionsA)
for _, sB := range routeParentStatusesB {
if !slices.ContainsFunc(routeParentStatusesA, func(sA gatev1alpha2.RouteParentStatus) bool {
return routeParentStatusEqual(sA, sB)
}) {
return false
}
}
return true
}
func routeParentStatusEqual(sA, sB gatev1alpha2.RouteParentStatus) bool {
if !reflect.DeepEqual(sA.ParentRef, sB.ParentRef) {
return false
}
if sA.ControllerName != sB.ControllerName {
return false
}
return conditionsEqual(sA.Conditions, sB.Conditions)
}
func conditionsEqual(conditionsA, conditionsB []metav1.Condition) bool {
return slices.EqualFunc(conditionsA, conditionsB, func(cA metav1.Condition, cB metav1.Condition) bool {
return cA.Type == cB.Type &&
cA.Reason == cB.Reason &&
cA.Status == cB.Status &&
cA.Message == cB.Message &&
cA.ObservedGeneration == cB.ObservedGeneration
})
}

View file

@ -268,7 +268,7 @@ func Test_gatewayStatusEquals(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
result := gatewayStatusEquals(test.statusA, test.statusB)
result := gatewayStatusEqual(test.statusA, test.statusB)
assert.Equal(t, test.expected, result)
})

View file

@ -318,15 +318,18 @@ func (p *Provider) loadConfigurationFromGateways(ctx context.Context) *dynamic.C
gatewayClassNames[gatewayClass.Name] = struct{}{}
err := p.client.UpdateGatewayClassStatus(ctx, gatewayClass.Name, metav1.Condition{
Type: string(gatev1.GatewayClassConditionStatusAccepted),
Status: metav1.ConditionTrue,
ObservedGeneration: gatewayClass.Generation,
Reason: "Handled",
Message: "Handled by Traefik controller",
LastTransitionTime: metav1.Now(),
})
if err != nil {
status := gatev1.GatewayClassStatus{
Conditions: upsertGatewayClassConditionAccepted(gatewayClass.Status.Conditions, metav1.Condition{
Type: string(gatev1.GatewayClassConditionStatusAccepted),
Status: metav1.ConditionTrue,
ObservedGeneration: gatewayClass.Generation,
Reason: "Handled",
Message: "Handled by Traefik controller",
LastTransitionTime: metav1.Now(),
}),
}
if err := p.client.UpdateGatewayClassStatus(ctx, gatewayClass.Name, status); err != nil {
log.Ctx(ctx).
Warn().
Err(err).
@ -1228,3 +1231,14 @@ func upsertRouteConditionResolvedRefs(conditions []metav1.Condition, condition m
}
return append(conds, condition)
}
func upsertGatewayClassConditionAccepted(conditions []metav1.Condition, condition metav1.Condition) []metav1.Condition {
var conds []metav1.Condition
for _, c := range conditions {
if c.Type == string(gatev1.GatewayClassConditionStatusAccepted) {
continue
}
conds = append(conds, c)
}
return append(conds, condition)
}

View file

@ -27,7 +27,10 @@ func (p *Provider) loadTCPRoutes(ctx context.Context, gatewayListeners []gateway
}
for _, route := range routes {
logger := log.Ctx(ctx).With().Str("tcproute", route.Name).Str("namespace", route.Namespace).Logger()
logger := log.Ctx(ctx).With().
Str("tcp_route", route.Name).
Str("namespace", route.Namespace).
Logger()
var parentStatuses []gatev1alpha2.RouteParentStatus
for _, parentRef := range route.Spec.ParentRefs {

View file

@ -24,7 +24,9 @@ func (p *Provider) loadTLSRoutes(ctx context.Context, gatewayListeners []gateway
}
for _, route := range routes {
logger := log.Ctx(ctx).With().Str("tlsroute", route.Name).Str("namespace", route.Namespace).Logger()
logger := log.Ctx(ctx).With().
Str("tls_route", route.Name).
Str("namespace", route.Namespace).Logger()
var parentStatuses []gatev1alpha2.RouteParentStatus
for _, parentRef := range route.Spec.ParentRefs {

View file

@ -514,24 +514,31 @@ type connectionTracker struct {
// AddConnection add a connection in the tracked connections list.
func (c *connectionTracker) AddConnection(conn net.Conn) {
defer c.syncOpenConnectionGauge()
c.connsMu.Lock()
c.conns[conn] = struct{}{}
c.connsMu.Unlock()
if c.openConnectionsGauge != nil {
c.openConnectionsGauge.Add(1)
}
}
// RemoveConnection remove a connection from the tracked connections list.
func (c *connectionTracker) RemoveConnection(conn net.Conn) {
defer c.syncOpenConnectionGauge()
c.connsMu.Lock()
delete(c.conns, conn)
c.connsMu.Unlock()
}
if c.openConnectionsGauge != nil {
c.openConnectionsGauge.Add(-1)
// syncOpenConnectionGauge updates openConnectionsGauge value with the conns map length.
func (c *connectionTracker) syncOpenConnectionGauge() {
if c.openConnectionsGauge == nil {
return
}
c.connsMu.RLock()
c.openConnectionsGauge.Set(float64(len(c.conns)))
c.connsMu.RUnlock()
}
func (c *connectionTracker) isEmpty() bool {

View file

@ -359,6 +359,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
info,
roundTripper,
healthCheckTargets,
serviceName,
)
}

View file

@ -34,6 +34,7 @@ type Options struct {
func (o *Options) SetDefaults() {
// ensure http2 enabled
o.ALPNProtocols = DefaultTLSOptions.ALPNProtocols
o.CipherSuites = DefaultTLSOptions.CipherSuites
}
// +k8s:deepcopy-gen=true

View file

@ -100,7 +100,7 @@ func CheckNewVersion() {
}
if releaseVersion.GreaterThan(currentVersion) {
log.Warn().Err(err).Msgf("A new release has been found: %s. Please consider updating.", releaseVersion.String())
log.Warn().Err(err).Msgf("A new release of Traefik has been found: %s. Please consider updating.", releaseVersion.String())
return
}
}