traefik/pkg/server/routerfactory_test.go

251 lines
8.3 KiB
Go

package server
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/config/runtime"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/traefik/traefik/v3/pkg/server/middleware"
"github.com/traefik/traefik/v3/pkg/server/service"
"github.com/traefik/traefik/v3/pkg/tcp"
th "github.com/traefik/traefik/v3/pkg/testhelpers"
"github.com/traefik/traefik/v3/pkg/tls"
)
func TestReuseService(t *testing.T) {
testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
defer testServer.Close()
staticConfig := static.Configuration{
EntryPoints: map[string]*static.EntryPoint{
"web": {},
},
}
dynamicConfigs := th.BuildConfiguration(
th.WithRouters(
th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("bar"),
th.WithRule("Path(`/ok`)")),
th.WithRouter("foo2",
th.WithEntryPoints("web"),
th.WithRule("Path(`/unauthorized`)"),
th.WithServiceName("bar"),
th.WithRouterMiddlewares("basicauth")),
),
th.WithMiddlewares(th.WithMiddleware("basicauth",
th.WithBasicAuth(&dynamic.BasicAuth{Users: []string{"foo:bar"}}),
)),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithServers(th.WithServer(testServer.URL))),
),
)
roundTripperManager := service.NewRoundTripperManager(nil)
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
managerFactory := service.NewManagerFactory(staticConfig, nil, nil, roundTripperManager, nil)
tlsManager := tls.NewManager()
dialerManager := tcp.NewDialerManager(nil)
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager)
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
// Test that the /ok path returns a status 200.
responseRecorderOk := &httptest.ResponseRecorder{}
requestOk := httptest.NewRequest(http.MethodGet, testServer.URL+"/ok", nil)
entryPointsHandlers["web"].GetHTTPHandler().ServeHTTP(responseRecorderOk, requestOk)
assert.Equal(t, http.StatusOK, responseRecorderOk.Result().StatusCode, "status code")
// Test that the /unauthorized path returns a 401 because of
// the basic authentication defined on the frontend.
responseRecorderUnauthorized := &httptest.ResponseRecorder{}
requestUnauthorized := httptest.NewRequest(http.MethodGet, testServer.URL+"/unauthorized", nil)
entryPointsHandlers["web"].GetHTTPHandler().ServeHTTP(responseRecorderUnauthorized, requestUnauthorized)
assert.Equal(t, http.StatusUnauthorized, responseRecorderUnauthorized.Result().StatusCode, "status code")
}
func TestServerResponseEmptyBackend(t *testing.T) {
const requestPath = "/path"
const routeRule = "Path(`" + requestPath + "`)"
testCases := []struct {
desc string
config func(testServerURL string) *dynamic.HTTPConfiguration
expectedStatusCode int
}{
{
desc: "Ok",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
return th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithServers(th.WithServer(testServerURL))),
),
)
},
expectedStatusCode: http.StatusOK,
},
{
desc: "No Frontend",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
return th.BuildConfiguration()
},
expectedStatusCode: http.StatusNotFound,
},
{
desc: "Empty Backend LB",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
return th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar")),
)
},
expectedStatusCode: http.StatusServiceUnavailable,
},
{
desc: "Empty Backend LB Sticky",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
return th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithSticky("test")),
),
)
},
expectedStatusCode: http.StatusServiceUnavailable,
},
{
desc: "Empty Backend LB",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
return th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar")),
)
},
expectedStatusCode: http.StatusServiceUnavailable,
},
{
desc: "Empty Backend LB Sticky",
config: func(testServerURL string) *dynamic.HTTPConfiguration {
return th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("bar"),
th.WithRule(routeRule)),
),
th.WithLoadBalancerServices(th.WithService("bar",
th.WithSticky("test")),
),
)
},
expectedStatusCode: http.StatusServiceUnavailable,
},
}
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()
staticConfig := static.Configuration{
EntryPoints: map[string]*static.EntryPoint{
"web": {},
},
}
roundTripperManager := service.NewRoundTripperManager(nil)
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
managerFactory := service.NewManagerFactory(staticConfig, nil, nil, roundTripperManager, nil)
tlsManager := tls.NewManager()
dialerManager := tcp.NewDialerManager(nil)
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
observabiltyMgr := middleware.NewObservabilityMgr(staticConfig, nil, nil, nil, nil)
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, observabiltyMgr, nil, dialerManager)
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)}))
responseRecorder := &httptest.ResponseRecorder{}
request := httptest.NewRequest(http.MethodGet, testServer.URL+requestPath, nil)
entryPointsHandlers["web"].GetHTTPHandler().ServeHTTP(responseRecorder, request)
assert.Equal(t, test.expectedStatusCode, responseRecorder.Result().StatusCode, "status code")
})
}
}
func TestInternalServices(t *testing.T) {
testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusOK)
}))
defer testServer.Close()
staticConfig := static.Configuration{
API: &static.API{},
EntryPoints: map[string]*static.EntryPoint{
"web": {},
},
}
dynamicConfigs := th.BuildConfiguration(
th.WithRouters(
th.WithRouter("foo",
th.WithEntryPoints("web"),
th.WithServiceName("api@internal"),
th.WithRule("PathPrefix(`/api`)")),
),
)
roundTripperManager := service.NewRoundTripperManager(nil)
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
managerFactory := service.NewManagerFactory(staticConfig, nil, nil, roundTripperManager, nil)
tlsManager := tls.NewManager()
dialerManager := tcp.NewDialerManager(nil)
dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}})
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, nil, nil, dialerManager)
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
// Test that the /ok path returns a status 200.
responseRecorderOk := &httptest.ResponseRecorder{}
requestOk := httptest.NewRequest(http.MethodGet, testServer.URL+"/api/rawdata", nil)
entryPointsHandlers["web"].GetHTTPHandler().ServeHTTP(responseRecorderOk, requestOk)
assert.Equal(t, http.StatusOK, responseRecorderOk.Result().StatusCode, "status code")
}