2017-03-15 18:16:06 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2019-03-14 08:30:04 +00:00
|
|
|
"context"
|
2017-05-09 20:31:16 +00:00
|
|
|
"net/http"
|
2017-07-10 10:11:44 +00:00
|
|
|
"net/http/httptest"
|
2017-03-15 18:16:06 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2019-07-10 07:26:04 +00:00
|
|
|
"github.com/containous/traefik/pkg/config/dynamic"
|
2019-07-15 15:04:04 +00:00
|
|
|
"github.com/containous/traefik/pkg/config/runtime"
|
2019-03-15 08:42:03 +00:00
|
|
|
"github.com/containous/traefik/pkg/config/static"
|
|
|
|
th "github.com/containous/traefik/pkg/testhelpers"
|
2019-06-17 09:48:05 +00:00
|
|
|
"github.com/containous/traefik/pkg/types"
|
2017-04-30 09:22:07 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2017-03-15 18:16:06 +00:00
|
|
|
)
|
|
|
|
|
2017-11-17 09:26:03 +00:00
|
|
|
func TestListenProvidersSkipsEmptyConfigs(t *testing.T) {
|
|
|
|
server, stop, invokeStopChan := setupListenProvider(10 * time.Millisecond)
|
|
|
|
defer invokeStopChan()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-stop:
|
|
|
|
return
|
|
|
|
case <-server.configurationValidatedChan:
|
|
|
|
t.Error("An empty configuration was published but it should not")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2019-07-10 07:26:04 +00:00
|
|
|
server.configurationChan <- dynamic.Message{ProviderName: "kubernetes"}
|
2017-11-17 09:26:03 +00:00
|
|
|
|
|
|
|
// give some time so that the configuration can be processed
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
|
|
|
|
server, stop, invokeStopChan := setupListenProvider(10 * time.Millisecond)
|
|
|
|
defer invokeStopChan()
|
|
|
|
|
|
|
|
publishedConfigCount := 0
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-stop:
|
|
|
|
return
|
2018-11-14 09:18:03 +00:00
|
|
|
case conf := <-server.configurationValidatedChan:
|
2017-11-17 09:26:03 +00:00
|
|
|
// set the current configuration
|
|
|
|
// this is usually done in the processing part of the published configuration
|
2018-08-06 18:00:03 +00:00
|
|
|
// so we have to emulate the behavior here
|
2019-07-10 07:26:04 +00:00
|
|
|
currentConfigurations := server.currentConfigurations.Get().(dynamic.Configurations)
|
2018-11-14 09:18:03 +00:00
|
|
|
currentConfigurations[conf.ProviderName] = conf.Configuration
|
2017-11-17 09:26:03 +00:00
|
|
|
server.currentConfigurations.Set(currentConfigurations)
|
|
|
|
|
|
|
|
publishedConfigCount++
|
|
|
|
if publishedConfigCount > 1 {
|
|
|
|
t.Error("Same configuration should not be published multiple times")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2019-07-10 07:26:04 +00:00
|
|
|
conf := &dynamic.Configuration{}
|
2019-03-14 08:30:04 +00:00
|
|
|
conf.HTTP = th.BuildConfiguration(
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithRouters(th.WithRouter("foo")),
|
|
|
|
th.WithLoadBalancerServices(th.WithService("bar")),
|
2017-11-17 09:26:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// provide a configuration
|
2019-07-10 07:26:04 +00:00
|
|
|
server.configurationChan <- dynamic.Message{ProviderName: "kubernetes", Configuration: conf}
|
2017-11-17 09:26:03 +00:00
|
|
|
|
|
|
|
// give some time so that the configuration can be processed
|
|
|
|
time.Sleep(20 * time.Millisecond)
|
|
|
|
|
|
|
|
// provide the same configuration a second time
|
2019-07-10 07:26:04 +00:00
|
|
|
server.configurationChan <- dynamic.Message{ProviderName: "kubernetes", Configuration: conf}
|
2017-11-17 09:26:03 +00:00
|
|
|
|
|
|
|
// give some time so that the configuration can be processed
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
|
|
|
server, stop, invokeStopChan := setupListenProvider(10 * time.Millisecond)
|
|
|
|
defer invokeStopChan()
|
|
|
|
|
|
|
|
publishedProviderConfigCount := map[string]int{}
|
|
|
|
publishedConfigCount := 0
|
|
|
|
consumePublishedConfigsDone := make(chan bool)
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-stop:
|
|
|
|
return
|
|
|
|
case newConfig := <-server.configurationValidatedChan:
|
|
|
|
publishedProviderConfigCount[newConfig.ProviderName]++
|
|
|
|
publishedConfigCount++
|
|
|
|
if publishedConfigCount == 2 {
|
|
|
|
consumePublishedConfigsDone <- true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2019-07-10 07:26:04 +00:00
|
|
|
conf := &dynamic.Configuration{}
|
2019-03-14 08:30:04 +00:00
|
|
|
conf.HTTP = th.BuildConfiguration(
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithRouters(th.WithRouter("foo")),
|
|
|
|
th.WithLoadBalancerServices(th.WithService("bar")),
|
2017-11-17 09:26:03 +00:00
|
|
|
)
|
2019-07-10 07:26:04 +00:00
|
|
|
server.configurationChan <- dynamic.Message{ProviderName: "kubernetes", Configuration: conf}
|
|
|
|
server.configurationChan <- dynamic.Message{ProviderName: "marathon", Configuration: conf}
|
2017-11-17 09:26:03 +00:00
|
|
|
|
|
|
|
select {
|
|
|
|
case <-consumePublishedConfigsDone:
|
|
|
|
if val := publishedProviderConfigCount["kubernetes"]; val != 1 {
|
|
|
|
t.Errorf("Got %d configuration publication(s) for provider %q, want 1", val, "kubernetes")
|
|
|
|
}
|
|
|
|
if val := publishedProviderConfigCount["marathon"]; val != 1 {
|
|
|
|
t.Errorf("Got %d configuration publication(s) for provider %q, want 1", val, "marathon")
|
|
|
|
}
|
|
|
|
case <-time.After(100 * time.Millisecond):
|
|
|
|
t.Errorf("Published configurations were not consumed in time")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// setupListenProvider configures the Server and starts listenProviders
|
|
|
|
func setupListenProvider(throttleDuration time.Duration) (server *Server, stop chan bool, invokeStopChan func()) {
|
|
|
|
stop = make(chan bool)
|
|
|
|
invokeStopChan = func() {
|
|
|
|
stop <- true
|
|
|
|
}
|
|
|
|
|
2018-11-27 16:42:04 +00:00
|
|
|
staticConfiguration := static.Configuration{
|
|
|
|
Providers: &static.Providers{
|
2019-06-17 09:48:05 +00:00
|
|
|
ProvidersThrottleDuration: types.Duration(throttleDuration),
|
2017-11-17 09:26:03 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-03-14 08:30:04 +00:00
|
|
|
server = NewServer(staticConfiguration, nil, nil, nil)
|
2017-11-17 09:26:03 +00:00
|
|
|
go server.listenProviders(stop)
|
|
|
|
|
|
|
|
return server, stop, invokeStopChan
|
|
|
|
}
|
|
|
|
|
2017-07-10 10:11:44 +00:00
|
|
|
func TestServerResponseEmptyBackend(t *testing.T) {
|
|
|
|
const requestPath = "/path"
|
2019-01-30 15:24:07 +00:00
|
|
|
const routeRule = "Path(`" + requestPath + "`)"
|
2017-07-10 10:11:44 +00:00
|
|
|
|
|
|
|
testCases := []struct {
|
2018-04-23 13:30:03 +00:00
|
|
|
desc string
|
2019-07-10 07:26:04 +00:00
|
|
|
config func(testServerURL string) *dynamic.HTTPConfiguration
|
2018-04-23 13:30:03 +00:00
|
|
|
expectedStatusCode int
|
2017-07-10 10:11:44 +00:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
desc: "Ok",
|
2019-07-10 07:26:04 +00:00
|
|
|
config: func(testServerURL string) *dynamic.HTTPConfiguration {
|
2018-06-05 10:32:03 +00:00
|
|
|
return th.BuildConfiguration(
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithRouters(th.WithRouter("foo",
|
2018-06-05 10:32:03 +00:00
|
|
|
th.WithEntryPoints("http"),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithServiceName("bar"),
|
|
|
|
th.WithRule(routeRule)),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithLoadBalancerServices(th.WithService("bar",
|
|
|
|
th.WithServers(th.WithServer(testServerURL))),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2017-07-10 10:11:44 +00:00
|
|
|
)
|
|
|
|
},
|
2018-04-23 13:30:03 +00:00
|
|
|
expectedStatusCode: http.StatusOK,
|
2017-07-10 10:11:44 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
desc: "No Frontend",
|
2019-07-10 07:26:04 +00:00
|
|
|
config: func(testServerURL string) *dynamic.HTTPConfiguration {
|
2018-06-05 10:32:03 +00:00
|
|
|
return th.BuildConfiguration()
|
2017-07-10 10:11:44 +00:00
|
|
|
},
|
2018-04-23 13:30:03 +00:00
|
|
|
expectedStatusCode: http.StatusNotFound,
|
2017-07-10 10:11:44 +00:00
|
|
|
},
|
|
|
|
{
|
2019-06-05 20:18:06 +00:00
|
|
|
desc: "Empty Backend LB",
|
2019-07-10 07:26:04 +00:00
|
|
|
config: func(testServerURL string) *dynamic.HTTPConfiguration {
|
2018-06-05 10:32:03 +00:00
|
|
|
return th.BuildConfiguration(
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithRouters(th.WithRouter("foo",
|
2018-06-05 10:32:03 +00:00
|
|
|
th.WithEntryPoints("http"),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithServiceName("bar"),
|
|
|
|
th.WithRule(routeRule)),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2019-06-05 20:18:06 +00:00
|
|
|
th.WithLoadBalancerServices(th.WithService("bar")),
|
2017-07-10 10:11:44 +00:00
|
|
|
)
|
|
|
|
},
|
2018-04-23 13:30:03 +00:00
|
|
|
expectedStatusCode: http.StatusServiceUnavailable,
|
2017-07-10 10:11:44 +00:00
|
|
|
},
|
|
|
|
{
|
2019-06-05 20:18:06 +00:00
|
|
|
desc: "Empty Backend LB Sticky",
|
2019-07-10 07:26:04 +00:00
|
|
|
config: func(testServerURL string) *dynamic.HTTPConfiguration {
|
2018-06-05 10:32:03 +00:00
|
|
|
return th.BuildConfiguration(
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithRouters(th.WithRouter("foo",
|
2018-06-05 10:32:03 +00:00
|
|
|
th.WithEntryPoints("http"),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithServiceName("bar"),
|
|
|
|
th.WithRule(routeRule)),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithLoadBalancerServices(th.WithService("bar",
|
2019-06-05 20:18:06 +00:00
|
|
|
th.WithStickiness("test")),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2017-07-10 10:11:44 +00:00
|
|
|
)
|
|
|
|
},
|
2018-04-23 13:30:03 +00:00
|
|
|
expectedStatusCode: http.StatusServiceUnavailable,
|
2017-07-10 10:11:44 +00:00
|
|
|
},
|
|
|
|
{
|
2019-06-05 20:18:06 +00:00
|
|
|
desc: "Empty Backend LB",
|
2019-07-10 07:26:04 +00:00
|
|
|
config: func(testServerURL string) *dynamic.HTTPConfiguration {
|
2018-06-05 10:32:03 +00:00
|
|
|
return th.BuildConfiguration(
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithRouters(th.WithRouter("foo",
|
2018-06-05 10:32:03 +00:00
|
|
|
th.WithEntryPoints("http"),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithServiceName("bar"),
|
|
|
|
th.WithRule(routeRule)),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2019-06-05 20:18:06 +00:00
|
|
|
th.WithLoadBalancerServices(th.WithService("bar")),
|
2017-07-10 10:11:44 +00:00
|
|
|
)
|
|
|
|
},
|
2018-04-23 13:30:03 +00:00
|
|
|
expectedStatusCode: http.StatusServiceUnavailable,
|
2017-07-10 10:11:44 +00:00
|
|
|
},
|
|
|
|
{
|
2019-06-05 20:18:06 +00:00
|
|
|
desc: "Empty Backend LB Sticky",
|
2019-07-10 07:26:04 +00:00
|
|
|
config: func(testServerURL string) *dynamic.HTTPConfiguration {
|
2018-06-05 10:32:03 +00:00
|
|
|
return th.BuildConfiguration(
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithRouters(th.WithRouter("foo",
|
2018-06-05 10:32:03 +00:00
|
|
|
th.WithEntryPoints("http"),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithServiceName("bar"),
|
|
|
|
th.WithRule(routeRule)),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2018-11-14 09:18:03 +00:00
|
|
|
th.WithLoadBalancerServices(th.WithService("bar",
|
2019-06-05 20:18:06 +00:00
|
|
|
th.WithStickiness("test")),
|
2018-06-05 10:32:03 +00:00
|
|
|
),
|
2017-07-10 10:11:44 +00:00
|
|
|
)
|
|
|
|
},
|
2018-04-23 13:30:03 +00:00
|
|
|
expectedStatusCode: http.StatusServiceUnavailable,
|
2017-07-10 10:11:44 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range testCases {
|
|
|
|
test := test
|
|
|
|
|
|
|
|
t.Run(test.desc, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
rw.WriteHeader(http.StatusOK)
|
|
|
|
}))
|
|
|
|
defer testServer.Close()
|
|
|
|
|
2018-11-27 16:42:04 +00:00
|
|
|
globalConfig := static.Configuration{}
|
2019-03-14 08:30:04 +00:00
|
|
|
entryPointsConfig := TCPEntryPoints{
|
|
|
|
"http": &TCPEntryPoint{},
|
2017-07-10 10:11:44 +00:00
|
|
|
}
|
|
|
|
|
2019-03-14 08:30:04 +00:00
|
|
|
srv := NewServer(globalConfig, nil, entryPointsConfig, nil)
|
2019-07-15 15:04:04 +00:00
|
|
|
rtConf := runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)})
|
2019-05-16 08:58:06 +00:00
|
|
|
entryPoints, _ := srv.createHTTPHandlers(context.Background(), rtConf, []string{"http"})
|
2017-07-10 10:11:44 +00:00
|
|
|
|
|
|
|
responseRecorder := &httptest.ResponseRecorder{}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, testServer.URL+requestPath, nil)
|
|
|
|
|
2018-11-14 09:18:03 +00:00
|
|
|
entryPoints["http"].ServeHTTP(responseRecorder, request)
|
2017-07-10 10:11:44 +00:00
|
|
|
|
2018-04-23 13:30:03 +00:00
|
|
|
assert.Equal(t, test.expectedStatusCode, responseRecorder.Result().StatusCode, "status code")
|
2017-07-10 10:11:44 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|