2019-11-14 18:40:05 +03:00
package server
import (
"context"
2024-02-07 19:14:07 +03:00
"errors"
2019-11-14 18:40:05 +03:00
"strconv"
2022-02-07 13:58:04 +03:00
"sync"
2019-11-14 18:40:05 +03:00
"testing"
"time"
"github.com/stretchr/testify/assert"
2020-09-16 16:46:04 +03:00
"github.com/traefik/traefik/v2/pkg/config/dynamic"
2022-02-07 13:58:04 +03:00
"github.com/traefik/traefik/v2/pkg/provider/aggregator"
2020-09-16 16:46:04 +03:00
"github.com/traefik/traefik/v2/pkg/safe"
th "github.com/traefik/traefik/v2/pkg/testhelpers"
"github.com/traefik/traefik/v2/pkg/tls"
2019-11-14 18:40:05 +03:00
)
type mockProvider struct {
2022-02-07 13:58:04 +03:00
messages [ ] dynamic . Message
wait time . Duration
first chan struct { }
throttleDuration time . Duration
2019-11-14 18:40:05 +03:00
}
2024-01-22 18:52:05 +03:00
func ( p * mockProvider ) Provide ( configurationChan chan <- dynamic . Message , _ * safe . Pool ) error {
2022-02-07 13:58:04 +03:00
wait := p . wait
if wait == 0 {
wait = 20 * time . Millisecond
}
2019-11-14 18:40:05 +03:00
2022-02-07 13:58:04 +03:00
if len ( p . messages ) == 0 {
2024-02-07 19:14:07 +03:00
return errors . New ( "no messages available" )
2022-02-07 13:58:04 +03:00
}
configurationChan <- p . messages [ 0 ]
2019-11-14 18:40:05 +03:00
2022-02-07 13:58:04 +03:00
if p . first != nil {
<- p . first
}
for _ , message := range p . messages [ 1 : ] {
2019-11-14 18:40:05 +03:00
time . Sleep ( wait )
2022-02-07 13:58:04 +03:00
configurationChan <- message
2019-11-14 18:40:05 +03:00
}
return nil
}
2022-02-07 13:58:04 +03:00
// ThrottleDuration returns the throttle duration.
2024-01-22 18:52:05 +03:00
func ( p * mockProvider ) ThrottleDuration ( ) time . Duration {
2022-02-07 13:58:04 +03:00
return p . throttleDuration
}
2019-11-14 18:40:05 +03:00
func ( p * mockProvider ) Init ( ) error {
2022-02-07 13:58:04 +03:00
return nil
2019-11-14 18:40:05 +03:00
}
func TestNewConfigurationWatcher ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
2022-12-07 12:56:05 +03:00
t . Cleanup ( routinesPool . Stop )
2022-02-07 13:58:04 +03:00
2019-11-14 18:40:05 +03:00
pvd := & mockProvider {
messages : [ ] dynamic . Message { {
ProviderName : "mock" ,
Configuration : & dynamic . Configuration {
HTTP : th . BuildConfiguration (
th . WithRouters (
th . WithRouter ( "test" ,
th . WithEntryPoints ( "e" ) ,
th . WithServiceName ( "scv" ) ) ) ,
) ,
} ,
} } ,
}
2022-02-07 13:58:04 +03:00
watcher := NewConfigurationWatcher ( routinesPool , pvd , [ ] string { } , "" )
2019-11-14 18:40:05 +03:00
run := make ( chan struct { } )
watcher . AddListener ( func ( conf dynamic . Configuration ) {
expected := dynamic . Configuration {
HTTP : th . BuildConfiguration (
th . WithRouters (
th . WithRouter ( "test@mock" ,
th . WithEntryPoints ( "e" ) ,
th . WithServiceName ( "scv" ) ) ) ,
th . WithMiddlewares ( ) ,
th . WithLoadBalancerServices ( ) ,
) ,
TCP : & dynamic . TCPConfiguration {
2021-06-11 16:30:05 +03:00
Routers : map [ string ] * dynamic . TCPRouter { } ,
Middlewares : map [ string ] * dynamic . TCPMiddleware { } ,
Services : map [ string ] * dynamic . TCPService { } ,
2019-11-14 18:40:05 +03:00
} ,
TLS : & dynamic . TLSConfiguration {
Options : map [ string ] tls . Options {
2022-09-08 11:56:08 +03:00
"default" : tls . DefaultTLSOptions ,
2019-11-14 18:40:05 +03:00
} ,
Stores : map [ string ] tls . Store { } ,
} ,
2020-02-11 03:26:04 +03:00
UDP : & dynamic . UDPConfiguration {
Routers : map [ string ] * dynamic . UDPRouter { } ,
Services : map [ string ] * dynamic . UDPService { } ,
} ,
2019-11-14 18:40:05 +03:00
}
assert . Equal ( t , expected , conf )
close ( run )
} )
watcher . Start ( )
<- run
}
2022-02-07 13:58:04 +03:00
func TestWaitForRequiredProvider ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
pvdAggregator := & mockProvider {
wait : 5 * time . Millisecond ,
}
config := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
}
pvdAggregator . messages = append ( pvdAggregator . messages , dynamic . Message {
ProviderName : "mock" ,
Configuration : config ,
} )
pvdAggregator . messages = append ( pvdAggregator . messages , dynamic . Message {
ProviderName : "required" ,
Configuration : config ,
} )
pvdAggregator . messages = append ( pvdAggregator . messages , dynamic . Message {
ProviderName : "mock2" ,
Configuration : config ,
} )
watcher := NewConfigurationWatcher ( routinesPool , pvdAggregator , [ ] string { } , "required" )
publishedConfigCount := 0
watcher . AddListener ( func ( _ dynamic . Configuration ) {
publishedConfigCount ++
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2022-02-07 13:58:04 +03:00
// give some time so that the configuration can be processed
time . Sleep ( 20 * time . Millisecond )
// after 20 milliseconds we should have 2 configs published
assert . Equal ( t , 2 , publishedConfigCount , "times configs were published" )
}
func TestIgnoreTransientConfiguration ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
config := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
}
2024-01-23 18:46:05 +03:00
expectedConfig := dynamic . Configuration {
HTTP : th . BuildConfiguration (
th . WithRouters ( th . WithRouter ( "foo@mock" , th . WithEntryPoints ( "ep" ) ) ) ,
th . WithLoadBalancerServices ( th . WithService ( "bar@mock" ) ) ,
th . WithMiddlewares ( ) ,
) ,
TCP : & dynamic . TCPConfiguration {
Routers : map [ string ] * dynamic . TCPRouter { } ,
Middlewares : map [ string ] * dynamic . TCPMiddleware { } ,
Services : map [ string ] * dynamic . TCPService { } ,
} ,
UDP : & dynamic . UDPConfiguration {
Routers : map [ string ] * dynamic . UDPRouter { } ,
Services : map [ string ] * dynamic . UDPService { } ,
} ,
TLS : & dynamic . TLSConfiguration {
Options : map [ string ] tls . Options {
"default" : tls . DefaultTLSOptions ,
} ,
Stores : map [ string ] tls . Store { } ,
} ,
}
expectedConfig3 := dynamic . Configuration {
HTTP : th . BuildConfiguration (
th . WithRouters ( th . WithRouter ( "foo@mock" , th . WithEntryPoints ( "ep" ) ) ) ,
th . WithLoadBalancerServices ( th . WithService ( "bar-config3@mock" ) ) ,
th . WithMiddlewares ( ) ,
) ,
TCP : & dynamic . TCPConfiguration {
Routers : map [ string ] * dynamic . TCPRouter { } ,
Middlewares : map [ string ] * dynamic . TCPMiddleware { } ,
Services : map [ string ] * dynamic . TCPService { } ,
} ,
UDP : & dynamic . UDPConfiguration {
Routers : map [ string ] * dynamic . UDPRouter { } ,
Services : map [ string ] * dynamic . UDPService { } ,
} ,
TLS : & dynamic . TLSConfiguration {
Options : map [ string ] tls . Options {
"default" : tls . DefaultTLSOptions ,
} ,
Stores : map [ string ] tls . Store { } ,
} ,
}
2022-02-07 13:58:04 +03:00
config2 := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "baz" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "toto" ) ) ,
) ,
}
2024-01-23 18:46:05 +03:00
config3 := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
th . WithLoadBalancerServices ( th . WithService ( "bar-config3" ) ) ,
) ,
}
2024-01-22 18:52:05 +03:00
watcher := NewConfigurationWatcher ( routinesPool , & mockProvider { } , [ ] string { } , "" )
2022-02-07 13:58:04 +03:00
2024-01-23 18:46:05 +03:00
// To be able to "block" the writes, we change the chan to remove buffering.
watcher . allProvidersConfigs = make ( chan dynamic . Message )
2022-02-07 13:58:04 +03:00
publishedConfigCount := 0
2024-01-23 18:46:05 +03:00
firstConfigHandled := make ( chan struct { } )
2022-02-07 13:58:04 +03:00
blockConfConsumer := make ( chan struct { } )
2024-01-23 18:46:05 +03:00
blockConfConsumerAssert := make ( chan struct { } )
2022-02-07 13:58:04 +03:00
watcher . AddListener ( func ( config dynamic . Configuration ) {
publishedConfigCount ++
2024-01-23 18:46:05 +03:00
if publishedConfigCount > 2 {
t . Fatal ( "More than 2 published configuration" )
}
if publishedConfigCount == 1 {
assert . Equal ( t , expectedConfig , config )
close ( firstConfigHandled )
<- blockConfConsumer
time . Sleep ( 500 * time . Millisecond )
}
if publishedConfigCount == 2 {
assert . Equal ( t , expectedConfig3 , config )
close ( blockConfConsumerAssert )
}
2022-02-07 13:58:04 +03:00
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2022-02-07 13:58:04 +03:00
watcher . allProvidersConfigs <- dynamic . Message {
ProviderName : "mock" ,
Configuration : config ,
}
2024-01-23 18:46:05 +03:00
<- firstConfigHandled
2022-02-07 13:58:04 +03:00
watcher . allProvidersConfigs <- dynamic . Message {
ProviderName : "mock" ,
Configuration : config2 ,
}
watcher . allProvidersConfigs <- dynamic . Message {
ProviderName : "mock" ,
Configuration : config ,
}
close ( blockConfConsumer )
2024-01-23 18:46:05 +03:00
watcher . allProvidersConfigs <- dynamic . Message {
ProviderName : "mock" ,
Configuration : config3 ,
2022-02-07 13:58:04 +03:00
}
2024-01-23 18:46:05 +03:00
select {
case <- blockConfConsumerAssert :
case <- time . After ( 10 * time . Second ) :
t . Fatal ( "Timeout" )
}
2022-02-07 13:58:04 +03:00
}
2019-11-14 18:40:05 +03:00
func TestListenProvidersThrottleProviderConfigReload ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
pvd := & mockProvider {
2022-02-07 13:58:04 +03:00
wait : 10 * time . Millisecond ,
throttleDuration : 30 * time . Millisecond ,
2019-11-14 18:40:05 +03:00
}
2024-02-19 17:44:03 +03:00
for i := range 5 {
2019-11-14 18:40:05 +03:00
pvd . messages = append ( pvd . messages , dynamic . Message {
ProviderName : "mock" ,
Configuration : & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" + strconv . Itoa ( i ) , th . WithEntryPoints ( "ep" ) ) ) ,
2019-11-14 18:40:05 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
} ,
} )
}
2024-11-12 12:56:06 +03:00
providerAggregator := & aggregator . ProviderAggregator { }
2022-02-07 13:58:04 +03:00
err := providerAggregator . AddProvider ( pvd )
2023-11-17 03:50:06 +03:00
assert . NoError ( t , err )
2022-02-07 13:58:04 +03:00
watcher := NewConfigurationWatcher ( routinesPool , providerAggregator , [ ] string { } , "" )
2019-11-14 18:40:05 +03:00
publishedConfigCount := 0
watcher . AddListener ( func ( _ dynamic . Configuration ) {
publishedConfigCount ++
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2019-11-14 18:40:05 +03:00
2022-02-07 13:58:04 +03:00
// Give some time so that the configuration can be processed.
2019-11-14 18:40:05 +03:00
time . Sleep ( 100 * time . Millisecond )
2022-02-07 13:58:04 +03:00
// To load 5 new configs it would require 150ms (5 configs * 30ms).
// In 100ms, we should only have time to load 3 configs.
assert . LessOrEqual ( t , publishedConfigCount , 3 , "config was applied too many times" )
2024-05-27 10:46:08 +03:00
assert . Positive ( t , publishedConfigCount , "config was not applied at least once" )
2019-11-14 18:40:05 +03:00
}
func TestListenProvidersSkipsEmptyConfigs ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
2022-02-07 13:58:04 +03:00
2019-11-14 18:40:05 +03:00
pvd := & mockProvider {
messages : [ ] dynamic . Message { { ProviderName : "mock" } } ,
}
2022-02-07 13:58:04 +03:00
watcher := NewConfigurationWatcher ( routinesPool , pvd , [ ] string { } , "" )
2019-11-14 18:40:05 +03:00
watcher . AddListener ( func ( _ dynamic . Configuration ) {
t . Error ( "An empty configuration was published but it should not" )
} )
2022-02-07 13:58:04 +03:00
2019-11-14 18:40:05 +03:00
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2019-11-14 18:40:05 +03:00
// give some time so that the configuration can be processed
time . Sleep ( 100 * time . Millisecond )
}
func TestListenProvidersSkipsSameConfigurationForProvider ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
2022-02-07 13:58:04 +03:00
2019-11-14 18:40:05 +03:00
message := dynamic . Message {
ProviderName : "mock" ,
Configuration : & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
2019-11-14 18:40:05 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
} ,
}
2022-02-07 13:58:04 +03:00
2019-11-14 18:40:05 +03:00
pvd := & mockProvider {
messages : [ ] dynamic . Message { message , message } ,
}
2022-02-07 13:58:04 +03:00
watcher := NewConfigurationWatcher ( routinesPool , pvd , [ ] string { } , "" )
2019-11-14 18:40:05 +03:00
2022-02-07 13:58:04 +03:00
var configurationReloads int
2019-11-14 18:40:05 +03:00
watcher . AddListener ( func ( _ dynamic . Configuration ) {
2022-02-07 13:58:04 +03:00
configurationReloads ++
2019-11-14 18:40:05 +03:00
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2019-11-14 18:40:05 +03:00
// give some time so that the configuration can be processed
time . Sleep ( 100 * time . Millisecond )
2023-11-17 03:50:06 +03:00
assert . Equal ( t , 1 , configurationReloads , "Same configuration should not be published multiple times" )
2019-11-14 18:40:05 +03:00
}
2020-02-10 23:40:06 +03:00
func TestListenProvidersDoesNotSkipFlappingConfiguration ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
configuration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
2020-02-10 23:40:06 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
}
transientConfiguration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "bad" , th . WithEntryPoints ( "ep" ) ) ) ,
2020-02-10 23:40:06 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bad" ) ) ,
) ,
}
pvd := & mockProvider {
2022-02-07 13:58:04 +03:00
wait : 5 * time . Millisecond , // The last message needs to be received before the second has been fully processed
throttleDuration : 15 * time . Millisecond ,
2020-02-10 23:40:06 +03:00
messages : [ ] dynamic . Message {
{ ProviderName : "mock" , Configuration : configuration } ,
{ ProviderName : "mock" , Configuration : transientConfiguration } ,
{ ProviderName : "mock" , Configuration : configuration } ,
} ,
}
2024-01-22 18:52:05 +03:00
watcher := NewConfigurationWatcher ( routinesPool , pvd , [ ] string { } , "" )
2020-02-10 23:40:06 +03:00
var lastConfig dynamic . Configuration
watcher . AddListener ( func ( conf dynamic . Configuration ) {
lastConfig = conf
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2020-02-10 23:40:06 +03:00
// give some time so that the configuration can be processed
2021-03-08 11:58:04 +03:00
time . Sleep ( 100 * time . Millisecond )
2020-02-10 23:40:06 +03:00
expected := dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo@mock" , th . WithEntryPoints ( "ep" ) ) ) ,
2020-02-10 23:40:06 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar@mock" ) ) ,
th . WithMiddlewares ( ) ,
) ,
TCP : & dynamic . TCPConfiguration {
2021-06-11 16:30:05 +03:00
Routers : map [ string ] * dynamic . TCPRouter { } ,
Middlewares : map [ string ] * dynamic . TCPMiddleware { } ,
Services : map [ string ] * dynamic . TCPService { } ,
2020-02-10 23:40:06 +03:00
} ,
2020-02-29 02:13:44 +03:00
UDP : & dynamic . UDPConfiguration {
Routers : map [ string ] * dynamic . UDPRouter { } ,
Services : map [ string ] * dynamic . UDPService { } ,
} ,
2020-02-10 23:40:06 +03:00
TLS : & dynamic . TLSConfiguration {
Options : map [ string ] tls . Options {
2022-09-08 11:56:08 +03:00
"default" : tls . DefaultTLSOptions ,
2020-02-10 23:40:06 +03:00
} ,
Stores : map [ string ] tls . Store { } ,
} ,
}
assert . Equal ( t , expected , lastConfig )
}
2022-02-07 13:58:04 +03:00
func TestListenProvidersIgnoreSameConfig ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
configuration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
}
transientConfiguration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "bad" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bad" ) ) ,
) ,
}
// The transient configuration is sent alternatively with the configuration we want to be applied.
// It is intended to show that even if the configurations are different,
// those transient configurations will be ignored if they are sent in a time frame
// lower than the provider throttle duration.
pvd := & mockProvider {
wait : 1 * time . Microsecond , // Enqueue them fast
throttleDuration : time . Millisecond ,
first : make ( chan struct { } ) ,
messages : [ ] dynamic . Message {
{ ProviderName : "mock" , Configuration : configuration } ,
{ ProviderName : "mock" , Configuration : transientConfiguration } ,
{ ProviderName : "mock" , Configuration : configuration } ,
{ ProviderName : "mock" , Configuration : transientConfiguration } ,
{ ProviderName : "mock" , Configuration : configuration } ,
} ,
}
2024-11-12 12:56:06 +03:00
providerAggregator := & aggregator . ProviderAggregator { }
2022-02-07 13:58:04 +03:00
err := providerAggregator . AddProvider ( pvd )
2023-11-17 03:50:06 +03:00
assert . NoError ( t , err )
2022-02-07 13:58:04 +03:00
2024-01-22 18:52:05 +03:00
watcher := NewConfigurationWatcher ( routinesPool , providerAggregator , [ ] string { } , "" )
2022-02-07 13:58:04 +03:00
var configurationReloads int
var lastConfig dynamic . Configuration
var once sync . Once
watcher . AddListener ( func ( conf dynamic . Configuration ) {
configurationReloads ++
lastConfig = conf
2022-12-07 12:56:05 +03:00
// Allows next configurations to be sent by the mock provider as soon as the first configuration message is applied.
2022-02-07 13:58:04 +03:00
once . Do ( func ( ) {
pvd . first <- struct { } { }
// Wait for all configuration messages to pile in
time . Sleep ( 5 * time . Millisecond )
} )
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2022-02-07 13:58:04 +03:00
// Wait long enough
time . Sleep ( 50 * time . Millisecond )
expected := dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo@mock" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar@mock" ) ) ,
th . WithMiddlewares ( ) ,
) ,
TCP : & dynamic . TCPConfiguration {
Routers : map [ string ] * dynamic . TCPRouter { } ,
Middlewares : map [ string ] * dynamic . TCPMiddleware { } ,
Services : map [ string ] * dynamic . TCPService { } ,
} ,
UDP : & dynamic . UDPConfiguration {
Routers : map [ string ] * dynamic . UDPRouter { } ,
Services : map [ string ] * dynamic . UDPService { } ,
} ,
TLS : & dynamic . TLSConfiguration {
Options : map [ string ] tls . Options {
2022-09-08 11:56:08 +03:00
"default" : tls . DefaultTLSOptions ,
2022-02-07 13:58:04 +03:00
} ,
Stores : map [ string ] tls . Store { } ,
} ,
}
assert . Equal ( t , expected , lastConfig )
assert . Equal ( t , 1 , configurationReloads )
}
func TestApplyConfigUnderStress ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
2024-01-22 18:52:05 +03:00
watcher := NewConfigurationWatcher ( routinesPool , & mockProvider { } , [ ] string { } , "" )
2022-02-07 13:58:04 +03:00
routinesPool . GoCtx ( func ( ctx context . Context ) {
i := 0
for {
select {
case <- ctx . Done ( ) :
return
case watcher . allProvidersConfigs <- dynamic . Message { ProviderName : "mock" , Configuration : & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" + strconv . Itoa ( i ) , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
} } :
}
i ++
}
} )
var configurationReloads int
watcher . AddListener ( func ( conf dynamic . Configuration ) {
configurationReloads ++
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2022-02-07 13:58:04 +03:00
time . Sleep ( 100 * time . Millisecond )
// Ensure that at least two configurations have been applied
2022-12-07 12:56:05 +03:00
// if we simulate being spammed configuration changes by the provider(s).
// In theory, checking at least one would be sufficient,
// but checking for two also ensures that we're looping properly,
2022-02-07 13:58:04 +03:00
// and that the whole algo holds, etc.
t . Log ( configurationReloads )
assert . GreaterOrEqual ( t , configurationReloads , 2 )
}
func TestListenProvidersIgnoreIntermediateConfigs ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
configuration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
}
transientConfiguration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "bad" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bad" ) ) ,
) ,
}
transientConfiguration2 := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "bad2" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bad2" ) ) ,
) ,
}
finalConfiguration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "final" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "final" ) ) ,
) ,
}
pvd := & mockProvider {
wait : 10 * time . Microsecond , // Enqueue them fast
throttleDuration : 10 * time . Millisecond ,
messages : [ ] dynamic . Message {
{ ProviderName : "mock" , Configuration : configuration } ,
{ ProviderName : "mock" , Configuration : transientConfiguration } ,
{ ProviderName : "mock" , Configuration : transientConfiguration2 } ,
{ ProviderName : "mock" , Configuration : finalConfiguration } ,
} ,
}
2024-11-12 12:56:06 +03:00
providerAggregator := & aggregator . ProviderAggregator { }
2022-02-07 13:58:04 +03:00
err := providerAggregator . AddProvider ( pvd )
2023-11-17 03:50:06 +03:00
assert . NoError ( t , err )
2022-02-07 13:58:04 +03:00
2024-01-22 18:52:05 +03:00
watcher := NewConfigurationWatcher ( routinesPool , providerAggregator , [ ] string { } , "" )
2022-02-07 13:58:04 +03:00
var configurationReloads int
var lastConfig dynamic . Configuration
watcher . AddListener ( func ( conf dynamic . Configuration ) {
configurationReloads ++
lastConfig = conf
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2022-02-07 13:58:04 +03:00
// Wait long enough
time . Sleep ( 500 * time . Millisecond )
expected := dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "final@mock" , th . WithEntryPoints ( "ep" ) ) ) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices ( th . WithService ( "final@mock" ) ) ,
th . WithMiddlewares ( ) ,
) ,
TCP : & dynamic . TCPConfiguration {
Routers : map [ string ] * dynamic . TCPRouter { } ,
Middlewares : map [ string ] * dynamic . TCPMiddleware { } ,
Services : map [ string ] * dynamic . TCPService { } ,
} ,
UDP : & dynamic . UDPConfiguration {
Routers : map [ string ] * dynamic . UDPRouter { } ,
Services : map [ string ] * dynamic . UDPService { } ,
} ,
TLS : & dynamic . TLSConfiguration {
Options : map [ string ] tls . Options {
2022-09-08 11:56:08 +03:00
"default" : tls . DefaultTLSOptions ,
2022-02-07 13:58:04 +03:00
} ,
Stores : map [ string ] tls . Store { } ,
} ,
}
assert . Equal ( t , expected , lastConfig )
assert . Equal ( t , 2 , configurationReloads )
}
2019-11-14 18:40:05 +03:00
func TestListenProvidersPublishesConfigForEachProvider ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
configuration := & dynamic . Configuration {
HTTP : th . BuildConfiguration (
2024-01-22 18:52:05 +03:00
th . WithRouters ( th . WithRouter ( "foo" , th . WithEntryPoints ( "ep" ) ) ) ,
2019-11-14 18:40:05 +03:00
th . WithLoadBalancerServices ( th . WithService ( "bar" ) ) ,
) ,
}
pvd := & mockProvider {
messages : [ ] dynamic . Message {
{ ProviderName : "mock" , Configuration : configuration } ,
{ ProviderName : "mock2" , Configuration : configuration } ,
} ,
}
2024-01-22 18:52:05 +03:00
watcher := NewConfigurationWatcher ( routinesPool , pvd , [ ] string { } , "" )
2019-11-14 18:40:05 +03:00
var publishedProviderConfig dynamic . Configuration
watcher . AddListener ( func ( conf dynamic . Configuration ) {
publishedProviderConfig = conf
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2019-11-14 18:40:05 +03:00
// give some time so that the configuration can be processed
time . Sleep ( 100 * time . Millisecond )
expected := dynamic . Configuration {
HTTP : th . BuildConfiguration (
2020-03-05 14:46:05 +03:00
th . WithRouters (
2024-01-22 18:52:05 +03:00
th . WithRouter ( "foo@mock" , th . WithEntryPoints ( "ep" ) ) ,
th . WithRouter ( "foo@mock2" , th . WithEntryPoints ( "ep" ) ) ,
2020-03-05 14:46:05 +03:00
) ,
2022-02-07 13:58:04 +03:00
th . WithLoadBalancerServices (
th . WithService ( "bar@mock" ) ,
th . WithService ( "bar@mock2" ) ,
) ,
2019-11-14 18:40:05 +03:00
th . WithMiddlewares ( ) ,
) ,
TCP : & dynamic . TCPConfiguration {
2021-06-11 16:30:05 +03:00
Routers : map [ string ] * dynamic . TCPRouter { } ,
Middlewares : map [ string ] * dynamic . TCPMiddleware { } ,
Services : map [ string ] * dynamic . TCPService { } ,
2019-11-14 18:40:05 +03:00
} ,
TLS : & dynamic . TLSConfiguration {
Options : map [ string ] tls . Options {
2022-09-08 11:56:08 +03:00
"default" : tls . DefaultTLSOptions ,
2019-11-14 18:40:05 +03:00
} ,
Stores : map [ string ] tls . Store { } ,
} ,
2020-02-11 03:26:04 +03:00
UDP : & dynamic . UDPConfiguration {
Routers : map [ string ] * dynamic . UDPRouter { } ,
Services : map [ string ] * dynamic . UDPService { } ,
} ,
2019-11-14 18:40:05 +03:00
}
assert . Equal ( t , expected , publishedProviderConfig )
}
2020-07-02 12:18:04 +03:00
func TestPublishConfigUpdatedByProvider ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
pvdConfiguration := dynamic . Configuration {
TCP : & dynamic . TCPConfiguration {
Routers : map [ string ] * dynamic . TCPRouter {
"foo" : { } ,
} ,
} ,
}
pvd := & mockProvider {
wait : 10 * time . Millisecond ,
messages : [ ] dynamic . Message {
{
ProviderName : "mock" ,
Configuration : & pvdConfiguration ,
} ,
{
ProviderName : "mock" ,
Configuration : & pvdConfiguration ,
} ,
} ,
}
2022-02-07 13:58:04 +03:00
watcher := NewConfigurationWatcher ( routinesPool , pvd , [ ] string { } , "" )
2020-07-02 12:18:04 +03:00
publishedConfigCount := 0
watcher . AddListener ( func ( configuration dynamic . Configuration ) {
publishedConfigCount ++
2022-12-07 12:56:05 +03:00
// Update the provider configuration published in next dynamic Message which should trigger a new publishing.
2020-07-02 12:18:04 +03:00
pvdConfiguration . TCP . Routers [ "bar" ] = & dynamic . TCPRouter { }
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2020-07-02 12:18:04 +03:00
// give some time so that the configuration can be processed.
time . Sleep ( 100 * time . Millisecond )
assert . Equal ( t , 2 , publishedConfigCount )
}
func TestPublishConfigUpdatedByConfigWatcherListener ( t * testing . T ) {
routinesPool := safe . NewPool ( context . Background ( ) )
pvd := & mockProvider {
wait : 10 * time . Millisecond ,
messages : [ ] dynamic . Message {
{
ProviderName : "mock" ,
Configuration : & dynamic . Configuration {
TCP : & dynamic . TCPConfiguration {
Routers : map [ string ] * dynamic . TCPRouter {
"foo" : { } ,
} ,
} ,
} ,
} ,
{
ProviderName : "mock" ,
Configuration : & dynamic . Configuration {
TCP : & dynamic . TCPConfiguration {
Routers : map [ string ] * dynamic . TCPRouter {
"foo" : { } ,
} ,
} ,
} ,
} ,
} ,
}
2022-02-07 13:58:04 +03:00
watcher := NewConfigurationWatcher ( routinesPool , pvd , [ ] string { } , "" )
2020-07-02 12:18:04 +03:00
publishedConfigCount := 0
watcher . AddListener ( func ( configuration dynamic . Configuration ) {
publishedConfigCount ++
2022-12-07 12:56:05 +03:00
// Modify the provided configuration.
// This should not modify the configuration stored in the configuration watcher and therefore there will be no new publishing.
2020-07-02 12:18:04 +03:00
configuration . TCP . Routers [ "foo@mock" ] . Rule = "bar"
} )
watcher . Start ( )
2022-12-07 12:56:05 +03:00
t . Cleanup ( watcher . Stop )
t . Cleanup ( routinesPool . Stop )
2020-07-02 12:18:04 +03:00
// give some time so that the configuration can be processed.
time . Sleep ( 100 * time . Millisecond )
assert . Equal ( t , 1 , publishedConfigCount )
}