Merge current v3.1 into master
This commit is contained in:
commit
e8324132f9
38 changed files with 587 additions and 383 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package logs
|
||||
|
||||
import (
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
kitlog "github.com/go-kit/log"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -359,6 +359,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
|
|||
info,
|
||||
roundTripper,
|
||||
healthCheckTargets,
|
||||
serviceName,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue