diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index c6cc9693a..39144e12c 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -264,10 +264,10 @@ - "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.expected=foobar" - "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.interval=42s" - "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.payload=foobar" -- "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.port=foobar" - "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.serverstransport=foobar" - "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.tls=true" - "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.timeout=42s" +- "traefik.tcp.services.tcpservice01.loadbalancer.healthcheck.unhealthyinterval=42s" - "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol=true" - "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol.version=42" - "traefik.tcp.services.tcpservice01.loadbalancer.serverstransport=foobar" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 1fb78471d..81a437ac4 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -465,10 +465,10 @@ address = "foobar" tls = true [tcp.services.TCPService01.loadBalancer.healthCheck] - address = "foobar" tls = true serversTransport = "foobar" interval = "42s" + unhealthyInterval = "42s" timeout = "42s" payload = "foobar" expected = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index 1b0aa6ffe..2ce0aca5a 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -528,10 +528,10 @@ tcp: serversTransport: foobar terminationDelay: 42 healthCheck: - address: foobar tls: true serversTransport: foobar interval: 42s + unhealthyInterval: 42s timeout: 42s payload: foobar expected: foobar diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index 205597059..6f86b31fa 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -393,13 +393,13 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/0` | `foobar` | | `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/1` | `foobar` | | `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/trustDomain` | `foobar` | -| `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/address` | `foobar` | | `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/expected` | `foobar` | | `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/interval` | `42s` | | `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/payload` | `foobar` | | `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/serversTransport` | `foobar` | | `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/timeout` | `42s` | | `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/tls` | `true` | +| `traefik/tcp/services/TCPService01/loadBalancer/healthCheck/unhealthyInterval` | `42s` | | `traefik/tcp/services/TCPService01/loadBalancer/proxyProtocol/version` | `42` | | `traefik/tcp/services/TCPService01/loadBalancer/servers/0/address` | `foobar` | | `traefik/tcp/services/TCPService01/loadBalancer/servers/0/tls` | `true` | diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go index 9f744195e..67765f8f0 100644 --- a/pkg/config/dynamic/tcp_config.go +++ b/pkg/config/dynamic/tcp_config.go @@ -178,10 +178,11 @@ func (t *TCPServersTransport) SetDefaults() { // TCPServerHealthCheck holds the HealthCheck configuration. type TCPServerHealthCheck struct { - TLS bool `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` - ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` - Interval ptypes.Duration `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty" export:"true"` - Timeout ptypes.Duration `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty" export:"true"` - Payload string `json:"payload,omitempty" toml:"payload,omitempty" yaml:"payload,omitempty" export:"true"` - Expected string `json:"expected,omitempty" toml:"expected,omitempty" yaml:"expected,omitempty" export:"true"` + TLS bool `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` + ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` + Interval ptypes.Duration `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty" export:"true"` + UnhealthyInterval *ptypes.Duration `json:"unhealthyInterval,omitempty" toml:"unhealthyInterval,omitempty" yaml:"unhealthyInterval,omitempty" export:"true"` + Timeout ptypes.Duration `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty" export:"true"` + Payload string `json:"payload,omitempty" toml:"payload,omitempty" yaml:"payload,omitempty" export:"true"` + Expected string `json:"expected,omitempty" toml:"expected,omitempty" yaml:"expected,omitempty" export:"true"` } diff --git a/pkg/config/runtime/runtime_tcp.go b/pkg/config/runtime/runtime_tcp.go index dfd5563b8..13f91f7f7 100644 --- a/pkg/config/runtime/runtime_tcp.go +++ b/pkg/config/runtime/runtime_tcp.go @@ -120,7 +120,7 @@ func (s *TCPServiceInfo) UpdateServerStatus(server string, status bool) { } if currentStatus, loaded := s.serverStatus.LoadOrStore(server, status); loaded && currentStatus != status { - s.serverStatus.Swap(server, status) + s.serverStatus.Store(server, status) } } diff --git a/pkg/healthcheck/tcp.go b/pkg/healthcheck/tcp.go index 4bea42493..5599e082b 100644 --- a/pkg/healthcheck/tcp.go +++ b/pkg/healthcheck/tcp.go @@ -27,16 +27,17 @@ type ServiceTCPHealthChecker struct { serviceName string } -func NewServiceTCPHealthChecker(dialerManager *tcp.DialerManager, metrics metricsHealthCheck, config *dynamic.TCPServerHealthCheck, service StatusSetter, info *runtime.TCPServiceInfo, targets map[string]*net.TCPAddr, serviceName string) *ServiceTCPHealthChecker { +func NewServiceTCPHealthChecker(ctx context.Context, dialerManager *tcp.DialerManager, metrics metricsHealthCheck, config *dynamic.TCPServerHealthCheck, service StatusSetter, info *runtime.TCPServiceInfo, targets map[string]*net.TCPAddr, serviceName string) *ServiceTCPHealthChecker { + logger := log.Ctx(ctx) interval := time.Duration(config.Interval) if interval <= 0 { - log.Error().Msg("Health check interval smaller than zero") + logger.Error().Msg("Health check interval smaller than zero") interval = time.Duration(dynamic.DefaultHealthCheckInterval) } timeout := time.Duration(config.Timeout) if timeout <= 0 { - log.Error().Msg("Health check timeout smaller than zero") + logger.Error().Msg("Health check timeout smaller than zero") timeout = time.Duration(dynamic.DefaultHealthCheckTimeout) } diff --git a/pkg/healthcheck/tcp_test.go b/pkg/healthcheck/tcp_test.go index 00454de9a..c036dc271 100644 --- a/pkg/healthcheck/tcp_test.go +++ b/pkg/healthcheck/tcp_test.go @@ -112,6 +112,22 @@ func Test_ServiceTCPHealthChecker_Check(t *testing.T) { expGaugeValue: 0, targetStatus: truntime.StatusDown, }, + { + desc: "unhealthy server becoming healthy", + server: newTCPServer(t, + false, + tcpMockSequence{accept: false}, + tcpMockSequence{accept: true}, + ), + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(time.Millisecond * 100), + Timeout: ptypes.Duration(time.Millisecond * 99), + }, + expNumRemovedServers: 1, + expNumUpsertedServers: 1, + expGaugeValue: 1, + targetStatus: truntime.StatusUp, + }, { desc: "healthy server with request and response", server: newTCPServer(t, @@ -191,7 +207,7 @@ func Test_ServiceTCPHealthChecker_Check(t *testing.T) { ServerName: "example.com", }, }}) - service := NewServiceTCPHealthChecker(dialerManager, &MetricsMock{gauge}, test.config, lb, serviceInfo, targets, "serviceName") + service := NewServiceTCPHealthChecker(ctx, dialerManager, &MetricsMock{gauge}, test.config, lb, serviceInfo, targets, "serviceName") for range test.server.StatusSequence { test.server.Next() diff --git a/pkg/server/service/tcp/service.go b/pkg/server/service/tcp/service.go index 6a76b0411..8127d6236 100644 --- a/pkg/server/service/tcp/service.go +++ b/pkg/server/service/tcp/service.go @@ -28,9 +28,10 @@ type Manager struct { // NewManager creates a new manager. func NewManager(conf *runtime.Configuration, dialerManager *tcp.DialerManager) *Manager { return &Manager{ - dialerManager: dialerManager, - configs: conf.TCPServices, - rand: rand.New(rand.NewSource(time.Now().UnixNano())), + dialerManager: dialerManager, + healthCheckers: make(map[string]*healthcheck.ServiceTCPHealthChecker), + configs: conf.TCPServices, + rand: rand.New(rand.NewSource(time.Now().UnixNano())), } } @@ -108,6 +109,7 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han if conf.LoadBalancer.HealthCheck != nil { m.healthCheckers[serviceName] = healthcheck.NewServiceTCPHealthChecker( + ctx, m.dialerManager, nil, conf.LoadBalancer.HealthCheck,