API: expose runtime representation
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com> Co-authored-by: Jean-Baptiste Doumenjou <jb.doumenjou@gmail.com>
This commit is contained in:
parent
5cd9396dae
commit
f6df556eb0
50 changed files with 2250 additions and 1158 deletions
8
Gopkg.lock
generated
8
Gopkg.lock
generated
|
@ -1489,13 +1489,6 @@
|
|||
pruneopts = "NUT"
|
||||
revision = "c4434f09ec131ecf30f986d5dcb1636508bfa49a"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:84b9a5318d8ce3b8a9b1509bf15734f4f9dcd4decf9d9e9c7346a16c7b64d49e"
|
||||
name = "github.com/thoas/stats"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "4975baf6a358ed3ddaa42133996e1959f96c9300"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:99ce99ce6d6d0cbc5f822cda92095906e01d5546d60999ac839ab008938e4e17"
|
||||
|
@ -2304,7 +2297,6 @@
|
|||
"github.com/stretchr/testify/mock",
|
||||
"github.com/stretchr/testify/require",
|
||||
"github.com/stvp/go-udp-testing",
|
||||
"github.com/thoas/stats",
|
||||
"github.com/uber/jaeger-client-go",
|
||||
"github.com/uber/jaeger-client-go/config",
|
||||
"github.com/uber/jaeger-client-go/zipkin",
|
||||
|
|
|
@ -10,7 +10,7 @@ Let's see how.
|
|||
|
||||
### General
|
||||
|
||||
This [documentation](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/).
|
||||
This [documentation](https://docs.traefik.io/) is built with [mkdocs](https://mkdocs.org/).
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
|
|
|
@ -603,7 +603,7 @@ func checkAccessLogExactValues(c *check.C, line string, i int, v accessLogValue)
|
|||
|
||||
func waitForTraefik(c *check.C, containerName string) {
|
||||
// Wait for Traefik to turn ready.
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/providers/docker/routers", nil)
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api/rawdata", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
|
||||
|
|
|
@ -333,7 +333,7 @@ func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// Expected traefik works
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
|
|
|
@ -62,27 +62,23 @@ func (s *DockerComposeSuite) TestComposeScale(c *check.C) {
|
|||
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
resp, err := http.Get("http://127.0.0.1:8080/api/providers/docker/services")
|
||||
resp, err := http.Get("http://127.0.0.1:8080/api/rawdata")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer resp.Body.Close()
|
||||
|
||||
var services []api.ServiceRepresentation
|
||||
err = json.NewDecoder(resp.Body).Decode(&services)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// check that we have only one service with n servers
|
||||
c.Assert(services, checker.HasLen, 1)
|
||||
c.Assert(services[0].ID, checker.Equals, composeService+"_integrationtest"+composeProject)
|
||||
c.Assert(services[0].LoadBalancer.Servers, checker.HasLen, serviceCount)
|
||||
|
||||
resp, err = http.Get("http://127.0.0.1:8080/api/providers/docker/routers")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer resp.Body.Close()
|
||||
|
||||
var routers []api.RouterRepresentation
|
||||
err = json.NewDecoder(resp.Body).Decode(&routers)
|
||||
var rtconf api.RunTimeRepresentation
|
||||
err = json.NewDecoder(resp.Body).Decode(&rtconf)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// check that we have only one router
|
||||
c.Assert(routers, checker.HasLen, 1)
|
||||
c.Assert(rtconf.Routers, checker.HasLen, 1)
|
||||
|
||||
// check that we have only one service with n servers
|
||||
services := rtconf.Services
|
||||
c.Assert(services, checker.HasLen, 1)
|
||||
for k, v := range services {
|
||||
c.Assert(k, checker.Equals, "docker."+composeService+"_integrationtest"+composeProject)
|
||||
c.Assert(v.LoadBalancer.Servers, checker.HasLen, serviceCount)
|
||||
// We could break here, but we don't just to keep us honest.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -315,7 +315,7 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
|
|||
c.Assert(json.Unmarshal(body, &version), checker.IsNil)
|
||||
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/services", 60*time.Second, try.BodyContains("powpow"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
s.stopAndRemoveContainerByName(c, "powpow")
|
||||
|
@ -323,11 +323,11 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
|
|||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/services", 10*time.Second, try.BodyContains("powpow"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("powpow"))
|
||||
c.Assert(err, checker.NotNil)
|
||||
|
||||
s.startContainerWithNameAndLabels(c, "powpow", "swarm:1.0.0", labels, "manage", "token://blabla")
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/services", 60*time.Second, try.BodyContains("powpow"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[serversTransport]
|
||||
rootCAs = [ """{{ .CertContent }}""" ]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web-secure]
|
||||
address = ":4443"
|
||||
|
||||
[api]
|
||||
|
||||
[providers]
|
||||
[providers.file]
|
||||
|
||||
[http.routers]
|
||||
[http.routers.router1]
|
||||
rule = "Host(`127.0.0.1`)"
|
||||
service = "service1"
|
||||
[http.routers.router1.tls]
|
||||
|
||||
[http.services]
|
||||
[http.services.service1.loadbalancer]
|
||||
[[http.services.service1.loadbalancer.servers]]
|
||||
url = "https://127.0.0.1:{{ .GRPCServerPort }}"
|
||||
weight = 1
|
||||
|
||||
[tlsStores.default.DefaultCertificate]
|
||||
certFile = """{{ .CertContent }}"""
|
||||
keyFile = """{{ .KeyContent }}"""
|
|
@ -167,7 +167,7 @@ func (s *GRPCSuite) TestGRPC(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
var response string
|
||||
|
@ -205,7 +205,7 @@ func (s *GRPCSuite) TestGRPCh2c(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
var response string
|
||||
|
@ -247,7 +247,7 @@ func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
var response string
|
||||
|
@ -289,7 +289,7 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
var response string
|
||||
|
@ -336,7 +336,7 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
c.Assert(err, check.IsNil)
|
||||
var client helloworld.Greeter_StreamExampleClient
|
||||
client, closer, err := callStreamExampleClientGRPC()
|
||||
|
@ -364,7 +364,6 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
|||
|
||||
func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||
stopStreamExample := make(chan bool)
|
||||
|
||||
lis, err := net.Listen("tcp", ":0")
|
||||
c.Assert(err, check.IsNil)
|
||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||
|
@ -378,7 +377,7 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
|||
c.Assert(err, check.IsNil)
|
||||
}()
|
||||
|
||||
file := s.adaptFile(c, "fixtures/grpc/config_with_flush.toml", struct {
|
||||
file := s.adaptFile(c, "fixtures/grpc/config.toml", struct {
|
||||
CertContent string
|
||||
KeyContent string
|
||||
GRPCServerPort string
|
||||
|
@ -396,7 +395,7 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
var client helloworld.Greeter_StreamExampleClient
|
||||
|
@ -454,7 +453,7 @@ func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
var response string
|
||||
|
|
|
@ -41,7 +41,7 @@ func (s *HealthCheckSuite) TestSimpleConfiguration(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||
|
@ -117,7 +117,7 @@ func (s *HealthCheckSuite) doTestMultipleEntrypoints(c *check.C, fixture string)
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// Wait for traefik
|
||||
err = try.GetRequest("http://localhost:8080/api/providers/file/routers", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||
err = try.GetRequest("http://localhost:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Check entrypoint http1
|
||||
|
@ -194,7 +194,7 @@ func (s *HealthCheckSuite) TestPortOverload(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 10*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("Host(`test.localhost`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
||||
|
|
|
@ -32,7 +32,7 @@ func (s *HTTPSSuite) TestWithSNIConfigHandshake(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("Host(`snitest.org`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.org`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -66,7 +66,7 @@ func (s *HTTPSSuite) TestWithSNIConfigRoute(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`snitest.org`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`snitest.org`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||
|
@ -122,7 +122,7 @@ func (s *HTTPSSuite) TestWithSNIStrictNotMatchedRequest(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -146,7 +146,7 @@ func (s *HTTPSSuite) TestWithDefaultCertificate(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -180,7 +180,7 @@ func (s *HTTPSSuite) TestWithDefaultCertificateNoSNI(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -214,7 +214,7 @@ func (s *HTTPSSuite) TestWithOverlappingStaticCertificate(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -249,7 +249,7 @@ func (s *HTTPSSuite) TestWithOverlappingDynamicCertificate(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -282,7 +282,7 @@ func (s *HTTPSSuite) TestWithClientCertificateAuthentication(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("Host(`snitest.org`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.org`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -338,7 +338,7 @@ func (s *HTTPSSuite) TestWithClientCertificateAuthenticationMultipleCAs(c *check
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`snitest.org`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`snitest.org`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -399,7 +399,7 @@ func (s *HTTPSSuite) TestWithClientCertificateAuthenticationMultipleCAsMultipleF
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`snitest.org`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`snitest.org`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
@ -464,7 +464,7 @@ func (s *HTTPSSuite) TestWithRootCAsContentForHTTPSOnBackend(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 1*time.Second, try.BodyContains(backend.URL))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains(backend.URL))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8081/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -486,7 +486,7 @@ func (s *HTTPSSuite) TestWithRootCAsFileForHTTPSOnBackend(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 1*time.Second, try.BodyContains(backend.URL))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains(backend.URL))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8081/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -544,7 +544,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithNoChange(c *check.C) {
|
|||
}
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`"+tr1.TLSClientConfig.ServerName+"`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`"+tr1.TLSClientConfig.ServerName+"`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||
|
@ -613,7 +613,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithChange(c *check.C) {
|
|||
}
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`"+tr2.TLSClientConfig.ServerName+"`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`"+tr2.TLSClientConfig.ServerName+"`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||
|
@ -676,7 +676,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion(c
|
|||
}
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("Host(`"+tr2.TLSClientConfig.ServerName+"`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`"+tr2.TLSClientConfig.ServerName+"`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend2 := startTestServer("9020", http.StatusResetContent)
|
||||
|
@ -740,7 +740,7 @@ func (s *HTTPSSuite) TestEntrypointHttpsRedirectAndPathModification(c *check.C)
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 5*time.Second, try.BodyContains("Host(`example.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.BodyContains("Host(`example.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
client := &http.Client{
|
||||
|
@ -841,7 +841,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicCaseInsensitive(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.BodyContains("HostRegexp(`{subdomain:[a-z1-9-]+}.www.snitest.com`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("HostRegexp(`{subdomain:[a-z1-9-]+}.www.snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
|
|
|
@ -74,9 +74,9 @@ func (s *K8sSuite) TestCRDSimple(c *check.C) {
|
|||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("PathPrefix(`/tobestripped`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/kubernetescrd/routers", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("default/stripprefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("default/stripprefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/kubernetescrd/middlewares", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("stripprefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("stripprefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ server1:
|
|||
- traefik.enable=true
|
||||
- traefik.http.routers.rt-server1.entryPoints=web
|
||||
- traefik.http.routers.rt-server1.rule=Host("frontend1.docker.local")
|
||||
- traefik.http.routers.rt-server1.service=service1
|
||||
- traefik.http.services.service1.loadbalancer.server.port=80
|
||||
server2:
|
||||
image: containous/whoami
|
||||
|
|
|
@ -65,7 +65,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/rest/routers", 1000*time.Millisecond, try.BodyContains("PathPrefix(`/`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("PathPrefix(`/`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
|
|
|
@ -31,7 +31,7 @@ func (s *RetrySuite) TestRetry(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// This simulates a DialTimeout when connecting to the backend server.
|
||||
|
@ -53,7 +53,7 @@ func (s *RetrySuite) TestRetryWebsocket(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// This simulates a DialTimeout when connecting to the backend server.
|
||||
|
|
|
@ -60,7 +60,7 @@ func (s *SimpleSuite) TestWithWebConfig(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
|
@ -173,10 +173,10 @@ func (s *SimpleSuite) TestApiOnSameEntryPoint(c *check.C) {
|
|||
err = try.GetRequest("http://127.0.0.1:8000/test", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/api/providers/docker", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
err = try.GetRequest("http://127.0.0.1:8000/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/api/providers/docker/routers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8000/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -205,7 +205,7 @@ func (s *SimpleSuite) TestStatsWithMultipleEntryPoint(c *check.C) {
|
|||
err = try.GetRequest("http://127.0.0.1:8080/api", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -230,7 +230,7 @@ func (s *SimpleSuite) TestNoAuthOnPing(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8001/api/providers", 2*time.Second, try.StatusCodeIs(http.StatusUnauthorized))
|
||||
err = try.GetRequest("http://127.0.0.1:8001/api/rawdata", 2*time.Second, try.StatusCodeIs(http.StatusUnauthorized))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8001/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -248,7 +248,7 @@ func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/routers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -266,7 +266,7 @@ func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/routers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -284,7 +284,7 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/routers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -312,7 +312,7 @@ func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.BodyContains(ipWhoami01))
|
||||
|
@ -334,10 +334,10 @@ func (s *SimpleSuite) TestIPStrategyWhitelist(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/routers", 2*time.Second, try.BodyContains("override"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("override"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/routers", 2*time.Second, try.BodyContains("override.remoteaddr.whitelist.docker.local"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("override.remoteaddr.whitelist.docker.local"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
testCases := []struct {
|
||||
|
@ -415,7 +415,7 @@ func (s *SimpleSuite) TestXForwardedHeaders(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/routers", 2*time.Second,
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
|
||||
try.BodyContains("override.remoteaddr.whitelist.docker.local"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
|
@ -450,7 +450,7 @@ func (s *SimpleSuite) TestMultiprovider(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 1000*time.Millisecond, try.BodyContains("service"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("service"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
config := config.HTTPConfiguration{
|
||||
|
@ -474,7 +474,7 @@ func (s *SimpleSuite) TestMultiprovider(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/rest/routers", 1000*time.Millisecond, try.BodyContains("PathPrefix(`/`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1000*time.Millisecond, try.BodyContains("PathPrefix(`/`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("CustomValue"))
|
||||
|
|
|
@ -31,7 +31,7 @@ func (s *TCPSuite) TestMixed(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("Path(`/test`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
//Traefik passes through, termination handled by whoami-a
|
||||
|
|
|
@ -31,7 +31,7 @@ func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/routers", 60*time.Second, try.BodyContains("Path(`/dialTimeout`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Path(`/dialTimeout`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// This simulates a DialTimeout when connecting to the backend server.
|
||||
|
|
|
@ -50,7 +50,7 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/docker/routers", 2*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443", nil)
|
||||
|
|
|
@ -59,6 +59,10 @@ func (s *TracingSuite) TestZipkinRateLimit(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||
|
@ -106,6 +110,10 @@ func (s *TracingSuite) TestZipkinRetry(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
|
@ -129,6 +137,10 @@ func (s *TracingSuite) TestZipkinAuth(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ func (s *WebsocketSuite) TestBase(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
||||
|
@ -107,7 +107,7 @@ func (s *WebsocketSuite) TestWrongOrigin(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:800")
|
||||
|
@ -157,7 +157,7 @@ func (s *WebsocketSuite) TestOrigin(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
||||
|
@ -218,7 +218,7 @@ func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:80")
|
||||
|
@ -276,7 +276,7 @@ func (s *WebsocketSuite) TestSSLTermination(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Add client self-signed cert
|
||||
|
@ -339,7 +339,7 @@ func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
||||
|
@ -383,7 +383,7 @@ func (s *WebsocketSuite) TestSpecificResponseFromBackend(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
_, resp, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
||||
|
@ -429,7 +429,7 @@ func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws/http%3A%2F%2Ftest", nil)
|
||||
|
@ -484,7 +484,7 @@ func (s *WebsocketSuite) TestSSLhttp2(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Add client self-signed cert
|
||||
|
@ -543,7 +543,7 @@ func (s *WebsocketSuite) TestHeaderAreForwared(c *check.C) {
|
|||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/file/services", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
headers := http.Header{}
|
||||
|
|
|
@ -6,79 +6,70 @@ import (
|
|||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
"github.com/containous/traefik/pkg/version"
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
thoasstats "github.com/thoas/stats"
|
||||
"github.com/unrolled/render"
|
||||
)
|
||||
|
||||
// ResourceIdentifier a resource identifier
|
||||
type ResourceIdentifier struct {
|
||||
ID string `json:"id"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// ProviderRepresentation a provider with resource identifiers
|
||||
type ProviderRepresentation struct {
|
||||
Routers []ResourceIdentifier `json:"routers,omitempty"`
|
||||
Middlewares []ResourceIdentifier `json:"middlewares,omitempty"`
|
||||
Services []ResourceIdentifier `json:"services,omitempty"`
|
||||
}
|
||||
|
||||
// RouterRepresentation extended version of a router configuration with an ID
|
||||
type RouterRepresentation struct {
|
||||
*config.Router
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// MiddlewareRepresentation extended version of a middleware configuration with an ID
|
||||
type MiddlewareRepresentation struct {
|
||||
*config.Middleware
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// ServiceRepresentation extended version of a service configuration with an ID
|
||||
type ServiceRepresentation struct {
|
||||
*config.Service
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// Handler expose api routes
|
||||
type Handler struct {
|
||||
EntryPoint string
|
||||
Dashboard bool
|
||||
Debug bool
|
||||
CurrentConfigurations *safe.Safe
|
||||
Statistics *types.Statistics
|
||||
Stats *thoasstats.Stats
|
||||
// StatsRecorder *middlewares.StatsRecorder // FIXME stats
|
||||
DashboardAssets *assetfs.AssetFS
|
||||
}
|
||||
|
||||
var templateRenderer jsonRenderer = render.New(render.Options{Directory: "nowhere"})
|
||||
|
||||
type jsonRenderer interface {
|
||||
JSON(w io.Writer, status int, v interface{}) error
|
||||
}
|
||||
|
||||
type serviceInfoRepresentation struct {
|
||||
*config.ServiceInfo
|
||||
ServerStatus map[string]string `json:"serverStatus,omitempty"`
|
||||
}
|
||||
|
||||
// RunTimeRepresentation is the configuration information exposed by the API handler.
|
||||
type RunTimeRepresentation struct {
|
||||
Routers map[string]*config.RouterInfo `json:"routers,omitempty"`
|
||||
Middlewares map[string]*config.MiddlewareInfo `json:"middlewares,omitempty"`
|
||||
Services map[string]*serviceInfoRepresentation `json:"services,omitempty"`
|
||||
TCPRouters map[string]*config.TCPRouterInfo `json:"tcpRouters,omitempty"`
|
||||
TCPServices map[string]*config.TCPServiceInfo `json:"tcpServices,omitempty"`
|
||||
}
|
||||
|
||||
// Handler serves the configuration and status of Traefik on API endpoints.
|
||||
type Handler struct {
|
||||
dashboard bool
|
||||
debug bool
|
||||
// runtimeConfiguration is the data set used to create all the data representations exposed by the API.
|
||||
runtimeConfiguration *config.RuntimeConfiguration
|
||||
statistics *types.Statistics
|
||||
// stats *thoasstats.Stats // FIXME stats
|
||||
// StatsRecorder *middlewares.StatsRecorder // FIXME stats
|
||||
dashboardAssets *assetfs.AssetFS
|
||||
}
|
||||
|
||||
// New returns a Handler defined by staticConfig, and if provided, by runtimeConfig.
|
||||
// It finishes populating the information provided in the runtimeConfig.
|
||||
func New(staticConfig static.Configuration, runtimeConfig *config.RuntimeConfiguration) *Handler {
|
||||
rConfig := runtimeConfig
|
||||
if rConfig == nil {
|
||||
rConfig = &config.RuntimeConfiguration{}
|
||||
}
|
||||
|
||||
return &Handler{
|
||||
dashboard: staticConfig.API.Dashboard,
|
||||
statistics: staticConfig.API.Statistics,
|
||||
dashboardAssets: staticConfig.API.DashboardAssets,
|
||||
runtimeConfiguration: rConfig,
|
||||
debug: staticConfig.Global.Debug,
|
||||
}
|
||||
}
|
||||
|
||||
// Append add api routes on a router
|
||||
func (h Handler) Append(router *mux.Router) {
|
||||
if h.Debug {
|
||||
if h.debug {
|
||||
DebugHandler{}.Append(router)
|
||||
}
|
||||
|
||||
router.Methods(http.MethodGet).Path("/api/rawdata").HandlerFunc(h.getRawData)
|
||||
router.Methods(http.MethodGet).Path("/api/providers").HandlerFunc(h.getProvidersHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}").HandlerFunc(h.getProviderHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/routers").HandlerFunc(h.getRoutersHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/routers/{router}").HandlerFunc(h.getRouterHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/middlewares").HandlerFunc(h.getMiddlewaresHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/middlewares/{middleware}").HandlerFunc(h.getMiddlewareHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/services").HandlerFunc(h.getServicesHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/providers/{provider}/services/{service}").HandlerFunc(h.getServiceHandler)
|
||||
router.Methods(http.MethodGet).Path("/api/rawdata").HandlerFunc(h.getRuntimeConfiguration)
|
||||
|
||||
// FIXME stats
|
||||
// health route
|
||||
|
@ -86,268 +77,29 @@ func (h Handler) Append(router *mux.Router) {
|
|||
|
||||
version.Handler{}.Append(router)
|
||||
|
||||
if h.Dashboard {
|
||||
DashboardHandler{Assets: h.DashboardAssets}.Append(router)
|
||||
if h.dashboard {
|
||||
DashboardHandler{Assets: h.dashboardAssets}.Append(router)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getRawData(rw http.ResponseWriter, request *http.Request) {
|
||||
if h.CurrentConfigurations != nil {
|
||||
currentConfigurations, ok := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
if !ok {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, currentConfigurations)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.Request) {
|
||||
siRepr := make(map[string]*serviceInfoRepresentation, len(h.runtimeConfiguration.Services))
|
||||
for k, v := range h.runtimeConfiguration.Services {
|
||||
siRepr[k] = &serviceInfoRepresentation{
|
||||
ServiceInfo: v,
|
||||
ServerStatus: v.GetAllStatus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getProvidersHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
// FIXME handle currentConfiguration
|
||||
if h.CurrentConfigurations != nil {
|
||||
currentConfigurations, ok := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
if !ok {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return
|
||||
rtRepr := RunTimeRepresentation{
|
||||
Routers: h.runtimeConfiguration.Routers,
|
||||
Middlewares: h.runtimeConfiguration.Middlewares,
|
||||
Services: siRepr,
|
||||
TCPRouters: h.runtimeConfiguration.TCPRouters,
|
||||
TCPServices: h.runtimeConfiguration.TCPServices,
|
||||
}
|
||||
|
||||
var providers []ResourceIdentifier
|
||||
for name := range currentConfigurations {
|
||||
providers = append(providers, ResourceIdentifier{
|
||||
ID: name,
|
||||
Path: "/api/providers/" + name,
|
||||
})
|
||||
}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, providers)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getProviderHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
providerID := mux.Vars(request)["provider"]
|
||||
|
||||
currentConfigurations := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
|
||||
provider, ok := currentConfigurations[providerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.HTTP == nil {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
var routers []ResourceIdentifier
|
||||
for name := range provider.HTTP.Routers {
|
||||
routers = append(routers, ResourceIdentifier{
|
||||
ID: name,
|
||||
Path: "/api/providers/" + providerID + "/routers",
|
||||
})
|
||||
}
|
||||
|
||||
var services []ResourceIdentifier
|
||||
for name := range provider.HTTP.Services {
|
||||
services = append(services, ResourceIdentifier{
|
||||
ID: name,
|
||||
Path: "/api/providers/" + providerID + "/services",
|
||||
})
|
||||
}
|
||||
|
||||
var middlewares []ResourceIdentifier
|
||||
for name := range provider.HTTP.Middlewares {
|
||||
middlewares = append(middlewares, ResourceIdentifier{
|
||||
ID: name,
|
||||
Path: "/api/providers/" + providerID + "/middlewares",
|
||||
})
|
||||
}
|
||||
|
||||
providers := ProviderRepresentation{Routers: routers, Middlewares: middlewares, Services: services}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, providers)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getRoutersHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
providerID := mux.Vars(request)["provider"]
|
||||
|
||||
currentConfigurations := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
|
||||
provider, ok := currentConfigurations[providerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.HTTP == nil {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
var routers []RouterRepresentation
|
||||
for name, router := range provider.HTTP.Routers {
|
||||
routers = append(routers, RouterRepresentation{Router: router, ID: name})
|
||||
}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, routers)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getRouterHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
providerID := mux.Vars(request)["provider"]
|
||||
routerID := mux.Vars(request)["router"]
|
||||
|
||||
currentConfigurations := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
|
||||
provider, ok := currentConfigurations[providerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.HTTP == nil {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
router, ok := provider.HTTP.Routers[routerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, router)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getMiddlewaresHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
providerID := mux.Vars(request)["provider"]
|
||||
|
||||
currentConfigurations := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
|
||||
provider, ok := currentConfigurations[providerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.HTTP == nil {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
var middlewares []MiddlewareRepresentation
|
||||
for name, middleware := range provider.HTTP.Middlewares {
|
||||
middlewares = append(middlewares, MiddlewareRepresentation{Middleware: middleware, ID: name})
|
||||
}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, middlewares)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getMiddlewareHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
providerID := mux.Vars(request)["provider"]
|
||||
middlewareID := mux.Vars(request)["middleware"]
|
||||
|
||||
currentConfigurations := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
|
||||
provider, ok := currentConfigurations[providerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.HTTP == nil {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
middleware, ok := provider.HTTP.Middlewares[middlewareID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, middleware)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getServicesHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
providerID := mux.Vars(request)["provider"]
|
||||
|
||||
currentConfigurations := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
|
||||
provider, ok := currentConfigurations[providerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.HTTP == nil {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
var services []ServiceRepresentation
|
||||
for name, service := range provider.HTTP.Services {
|
||||
services = append(services, ServiceRepresentation{Service: service, ID: name})
|
||||
}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, services)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h Handler) getServiceHandler(rw http.ResponseWriter, request *http.Request) {
|
||||
providerID := mux.Vars(request)["provider"]
|
||||
serviceID := mux.Vars(request)["service"]
|
||||
|
||||
currentConfigurations := h.CurrentConfigurations.Get().(config.Configurations)
|
||||
|
||||
provider, ok := currentConfigurations[providerID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
if provider.HTTP == nil {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
service, ok := provider.HTTP.Services[serviceID]
|
||||
if !ok {
|
||||
http.NotFound(rw, request)
|
||||
return
|
||||
}
|
||||
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, service)
|
||||
err := templateRenderer.JSON(rw, http.StatusOK, rtRepr)
|
||||
if err != nil {
|
||||
log.FromContext(request.Context()).Error(err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -8,189 +10,123 @@ import (
|
|||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata")
|
||||
|
||||
func TestHandler_Configuration(t *testing.T) {
|
||||
type expected struct {
|
||||
statusCode int
|
||||
body string
|
||||
json string
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
path string
|
||||
configuration config.Configurations
|
||||
conf config.RuntimeConfiguration
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
desc: "Get all the providers",
|
||||
path: "/api/providers",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"bar": {EntryPoints: []string{"foo", "bar"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `[{"id":"foo","path":"/api/providers/foo"}]`},
|
||||
},
|
||||
{
|
||||
desc: "Get a provider",
|
||||
path: "/api/providers/foo",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"bar": {EntryPoints: []string{"foo", "bar"}},
|
||||
},
|
||||
Middlewares: map[string]*config.Middleware{
|
||||
"bar": {
|
||||
AddPrefix: &config.AddPrefix{Prefix: "bar"},
|
||||
},
|
||||
},
|
||||
Services: map[string]*config.Service{
|
||||
"foo": {
|
||||
desc: "Get rawdata",
|
||||
path: "/api/rawdata",
|
||||
conf: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `{"routers":[{"id":"bar","path":"/api/providers/foo/routers"}],"middlewares":[{"id":"bar","path":"/api/providers/foo/middlewares"}],"services":[{"id":"foo","path":"/api/providers/foo/services"}]}`},
|
||||
},
|
||||
"myprovider.addPrefixTest": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
},
|
||||
"anotherprovider.addPrefixTest": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider.addPrefixTest"},
|
||||
},
|
||||
},
|
||||
"myprovider.test": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.tcpfoo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
desc: "Provider not found",
|
||||
path: "/api/providers/foo",
|
||||
configuration: config.Configurations{},
|
||||
expected: expected{statusCode: http.StatusNotFound, body: "404 page not found\n"},
|
||||
},
|
||||
{
|
||||
desc: "Get all routers",
|
||||
path: "/api/providers/foo/routers",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"bar": {EntryPoints: []string{"foo", "bar"}},
|
||||
Address: "127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `[{"entryPoints":["foo","bar"],"id":"bar"}]`},
|
||||
},
|
||||
{
|
||||
desc: "Get a router",
|
||||
path: "/api/providers/foo/routers/bar",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Routers: map[string]*config.Router{
|
||||
"bar": {EntryPoints: []string{"foo", "bar"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `{"entryPoints":["foo","bar"]}`},
|
||||
},
|
||||
{
|
||||
desc: "Router not found",
|
||||
path: "/api/providers/foo/routers/bar",
|
||||
configuration: config.Configurations{
|
||||
"foo": {},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusNotFound, body: "404 page not found\n"},
|
||||
},
|
||||
{
|
||||
desc: "Get all services",
|
||||
path: "/api/providers/foo/services",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: map[string]*config.Service{
|
||||
"foo": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.tcpbar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.tcpfoo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `[{"loadbalancer":{"method":"wrr","passHostHeader":false},"id":"foo"}]`},
|
||||
},
|
||||
{
|
||||
desc: "Get a service",
|
||||
path: "/api/providers/foo/services/foo",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: map[string]*config.Service{
|
||||
"foo": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Method: "wrr",
|
||||
"myprovider.tcptest": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.tcpfoo-service",
|
||||
Rule: "HostSNI(`foo.bar.other`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
expected: expected{
|
||||
statusCode: http.StatusOK,
|
||||
json: "testdata/getrawdata.json",
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `{"loadbalancer":{"method":"wrr","passHostHeader":false}}`},
|
||||
},
|
||||
{
|
||||
desc: "Service not found",
|
||||
path: "/api/providers/foo/services/bar",
|
||||
configuration: config.Configurations{
|
||||
"foo": {},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusNotFound, body: "404 page not found\n"},
|
||||
},
|
||||
{
|
||||
desc: "Get all middlewares",
|
||||
path: "/api/providers/foo/middlewares",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Middlewares: map[string]*config.Middleware{
|
||||
"bar": {
|
||||
AddPrefix: &config.AddPrefix{Prefix: "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `[{"addPrefix":{"prefix":"bar"},"id":"bar"}]`},
|
||||
},
|
||||
{
|
||||
desc: "Get a middleware",
|
||||
path: "/api/providers/foo/middlewares/bar",
|
||||
configuration: config.Configurations{
|
||||
"foo": {
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Middlewares: map[string]*config.Middleware{
|
||||
"bar": {
|
||||
AddPrefix: &config.AddPrefix{Prefix: "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusOK, body: `{"addPrefix":{"prefix":"bar"}}`},
|
||||
},
|
||||
{
|
||||
desc: "Middleware not found",
|
||||
path: "/api/providers/foo/middlewares/bar",
|
||||
configuration: config.Configurations{
|
||||
"foo": {},
|
||||
},
|
||||
expected: expected{statusCode: http.StatusNotFound, body: "404 page not found\n"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@ -198,15 +134,11 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
currentConfiguration := &safe.Safe{}
|
||||
currentConfiguration.Set(test.configuration)
|
||||
|
||||
handler := Handler{
|
||||
CurrentConfigurations: currentConfiguration,
|
||||
}
|
||||
|
||||
rtConf := &test.conf
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
router := mux.NewRouter()
|
||||
handler.Append(router)
|
||||
rtConf.PopulateUsedBy()
|
||||
|
||||
server := httptest.NewServer(router)
|
||||
|
||||
|
@ -215,12 +147,30 @@ func TestHandler_Configuration(t *testing.T) {
|
|||
|
||||
assert.Equal(t, test.expected.statusCode, resp.StatusCode)
|
||||
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
contents, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected.body, string(content))
|
||||
if test.expected.json == "" {
|
||||
return
|
||||
}
|
||||
if *updateExpected {
|
||||
var rtRepr RunTimeRepresentation
|
||||
err := json.Unmarshal(contents, &rtRepr)
|
||||
require.NoError(t, err)
|
||||
|
||||
newJSON, err := json.MarshalIndent(rtRepr, "", "\t")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ioutil.WriteFile(test.expected.json, newJSON, 0644)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(test.expected.json)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, string(data), string(contents))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
106
pkg/api/testdata/getrawdata.json
vendored
Normal file
106
pkg/api/testdata/getrawdata.json
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"routers": {
|
||||
"myprovider.bar": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"middlewares": [
|
||||
"auth",
|
||||
"anotherprovider.addPrefixTest"
|
||||
],
|
||||
"service": "myprovider.foo-service",
|
||||
"rule": "Host(`foo.bar`)"
|
||||
},
|
||||
"myprovider.test": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"middlewares": [
|
||||
"addPrefixTest",
|
||||
"auth"
|
||||
],
|
||||
"service": "myprovider.foo-service",
|
||||
"rule": "Host(`foo.bar.other`)"
|
||||
}
|
||||
},
|
||||
"middlewares": {
|
||||
"anotherprovider.addPrefixTest": {
|
||||
"addPrefix": {
|
||||
"prefix": "/toto"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider.bar"
|
||||
]
|
||||
},
|
||||
"myprovider.addPrefixTest": {
|
||||
"addPrefix": {
|
||||
"prefix": "/titi"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider.test"
|
||||
]
|
||||
},
|
||||
"myprovider.auth": {
|
||||
"basicAuth": {
|
||||
"users": [
|
||||
"admin:admin"
|
||||
]
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider.bar",
|
||||
"myprovider.test"
|
||||
]
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"myprovider.foo-service": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://127.0.0.1",
|
||||
"weight": 1
|
||||
}
|
||||
],
|
||||
"method": "wrr",
|
||||
"passHostHeader": false
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider.bar",
|
||||
"myprovider.test"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tcpRouters": {
|
||||
"myprovider.tcpbar": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"service": "myprovider.tcpfoo-service",
|
||||
"rule": "HostSNI(`foo.bar`)"
|
||||
},
|
||||
"myprovider.tcptest": {
|
||||
"entryPoints": [
|
||||
"web"
|
||||
],
|
||||
"service": "myprovider.tcpfoo-service",
|
||||
"rule": "HostSNI(`foo.bar.other`)"
|
||||
}
|
||||
},
|
||||
"tcpServices": {
|
||||
"myprovider.tcpfoo-service": {
|
||||
"loadbalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"weight": 1
|
||||
}
|
||||
],
|
||||
"method": "wrr"
|
||||
},
|
||||
"usedBy": [
|
||||
"myprovider.tcpbar",
|
||||
"myprovider.tcptest"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
210
pkg/config/runtime.go
Normal file
210
pkg/config/runtime.go
Normal file
|
@ -0,0 +1,210 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
)
|
||||
|
||||
// RuntimeConfiguration holds the information about the currently running traefik instance.
|
||||
type RuntimeConfiguration struct {
|
||||
Routers map[string]*RouterInfo `json:"routers,omitempty"`
|
||||
Middlewares map[string]*MiddlewareInfo `json:"middlewares,omitempty"`
|
||||
Services map[string]*ServiceInfo `json:"services,omitempty"`
|
||||
TCPRouters map[string]*TCPRouterInfo `json:"tcpRouters,omitempty"`
|
||||
TCPServices map[string]*TCPServiceInfo `json:"tcpServices,omitempty"`
|
||||
}
|
||||
|
||||
// NewRuntimeConfig returns a RuntimeConfiguration initialized with the given conf. It never returns nil.
|
||||
func NewRuntimeConfig(conf Configuration) *RuntimeConfiguration {
|
||||
if conf.HTTP == nil && conf.TCP == nil {
|
||||
return &RuntimeConfiguration{}
|
||||
}
|
||||
|
||||
runtimeConfig := &RuntimeConfiguration{}
|
||||
|
||||
if conf.HTTP != nil {
|
||||
routers := conf.HTTP.Routers
|
||||
if len(routers) > 0 {
|
||||
runtimeConfig.Routers = make(map[string]*RouterInfo, len(routers))
|
||||
for k, v := range routers {
|
||||
runtimeConfig.Routers[k] = &RouterInfo{Router: v}
|
||||
}
|
||||
}
|
||||
|
||||
services := conf.HTTP.Services
|
||||
if len(services) > 0 {
|
||||
runtimeConfig.Services = make(map[string]*ServiceInfo, len(services))
|
||||
for k, v := range services {
|
||||
runtimeConfig.Services[k] = &ServiceInfo{Service: v}
|
||||
}
|
||||
}
|
||||
|
||||
middlewares := conf.HTTP.Middlewares
|
||||
if len(middlewares) > 0 {
|
||||
runtimeConfig.Middlewares = make(map[string]*MiddlewareInfo, len(middlewares))
|
||||
for k, v := range middlewares {
|
||||
runtimeConfig.Middlewares[k] = &MiddlewareInfo{Middleware: v}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if conf.TCP != nil {
|
||||
if len(conf.TCP.Routers) > 0 {
|
||||
runtimeConfig.TCPRouters = make(map[string]*TCPRouterInfo, len(conf.TCP.Routers))
|
||||
for k, v := range conf.TCP.Routers {
|
||||
runtimeConfig.TCPRouters[k] = &TCPRouterInfo{TCPRouter: v}
|
||||
}
|
||||
}
|
||||
|
||||
if len(conf.TCP.Services) > 0 {
|
||||
runtimeConfig.TCPServices = make(map[string]*TCPServiceInfo, len(conf.TCP.Services))
|
||||
for k, v := range conf.TCP.Services {
|
||||
runtimeConfig.TCPServices[k] = &TCPServiceInfo{TCPService: v}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return runtimeConfig
|
||||
}
|
||||
|
||||
// PopulateUsedBy populates all the UsedBy lists of the underlying fields of r,
|
||||
// based on the relations between the included services, routers, and middlewares.
|
||||
func (r *RuntimeConfiguration) PopulateUsedBy() {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
logger := log.WithoutContext()
|
||||
|
||||
for routerName, routerInfo := range r.Routers {
|
||||
providerName := getProviderName(routerName)
|
||||
if providerName == "" {
|
||||
logger.WithField(log.RouterName, routerName).Error("router name is not fully qualified")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, midName := range routerInfo.Router.Middlewares {
|
||||
fullMidName := getQualifiedName(providerName, midName)
|
||||
if _, ok := r.Middlewares[fullMidName]; !ok {
|
||||
continue
|
||||
}
|
||||
r.Middlewares[fullMidName].UsedBy = append(r.Middlewares[fullMidName].UsedBy, routerName)
|
||||
}
|
||||
|
||||
serviceName := getQualifiedName(providerName, routerInfo.Router.Service)
|
||||
if _, ok := r.Services[serviceName]; !ok {
|
||||
continue
|
||||
}
|
||||
r.Services[serviceName].UsedBy = append(r.Services[serviceName].UsedBy, routerName)
|
||||
}
|
||||
|
||||
for k := range r.Services {
|
||||
sort.Strings(r.Services[k].UsedBy)
|
||||
}
|
||||
|
||||
for k := range r.Middlewares {
|
||||
sort.Strings(r.Middlewares[k].UsedBy)
|
||||
}
|
||||
|
||||
for routerName, routerInfo := range r.TCPRouters {
|
||||
providerName := getProviderName(routerName)
|
||||
if providerName == "" {
|
||||
logger.WithField(log.RouterName, routerName).Error("tcp router name is not fully qualified")
|
||||
continue
|
||||
}
|
||||
|
||||
serviceName := getQualifiedName(providerName, routerInfo.TCPRouter.Service)
|
||||
if _, ok := r.TCPServices[serviceName]; !ok {
|
||||
continue
|
||||
}
|
||||
r.TCPServices[serviceName].UsedBy = append(r.TCPServices[serviceName].UsedBy, routerName)
|
||||
}
|
||||
|
||||
for k := range r.TCPServices {
|
||||
sort.Strings(r.TCPServices[k].UsedBy)
|
||||
}
|
||||
}
|
||||
|
||||
// RouterInfo holds information about a currently running HTTP router
|
||||
type RouterInfo struct {
|
||||
*Router // dynamic configuration
|
||||
Err string `json:"error,omitempty"` // initialization error
|
||||
}
|
||||
|
||||
// TCPRouterInfo holds information about a currently running TCP router
|
||||
type TCPRouterInfo struct {
|
||||
*TCPRouter // dynamic configuration
|
||||
Err string `json:"error,omitempty"` // initialization error
|
||||
}
|
||||
|
||||
// MiddlewareInfo holds information about a currently running middleware
|
||||
type MiddlewareInfo struct {
|
||||
*Middleware // dynamic configuration
|
||||
Err error `json:"error,omitempty"` // initialization error
|
||||
UsedBy []string `json:"usedBy,omitempty"` // list of routers and services using that middleware
|
||||
}
|
||||
|
||||
// ServiceInfo holds information about a currently running service
|
||||
type ServiceInfo struct {
|
||||
*Service // dynamic configuration
|
||||
Err error `json:"error,omitempty"` // initialization error
|
||||
UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
|
||||
|
||||
statusMu sync.RWMutex
|
||||
status map[string]string // keyed by server URL
|
||||
}
|
||||
|
||||
// UpdateStatus sets the status of the server in the ServiceInfo.
|
||||
// It is the responsibility of the caller to check that s is not nil.
|
||||
func (s *ServiceInfo) UpdateStatus(server string, status string) {
|
||||
s.statusMu.Lock()
|
||||
defer s.statusMu.Unlock()
|
||||
|
||||
if s.status == nil {
|
||||
s.status = make(map[string]string)
|
||||
}
|
||||
s.status[server] = status
|
||||
}
|
||||
|
||||
// GetAllStatus returns all the statuses of all the servers in ServiceInfo.
|
||||
// It is the responsibility of the caller to check that s is not nil
|
||||
func (s *ServiceInfo) GetAllStatus() map[string]string {
|
||||
s.statusMu.RLock()
|
||||
defer s.statusMu.RUnlock()
|
||||
|
||||
if len(s.status) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
allStatus := make(map[string]string, len(s.status))
|
||||
for k, v := range s.status {
|
||||
allStatus[k] = v
|
||||
}
|
||||
return allStatus
|
||||
}
|
||||
|
||||
// TCPServiceInfo holds information about a currently running TCP service
|
||||
type TCPServiceInfo struct {
|
||||
*TCPService // dynamic configuration
|
||||
Err error `json:"error,omitempty"` // initialization error
|
||||
UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service
|
||||
}
|
||||
|
||||
func getProviderName(elementName string) string {
|
||||
parts := strings.Split(elementName, ".")
|
||||
if len(parts) > 1 {
|
||||
return parts[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getQualifiedName(provider, elementName string) string {
|
||||
parts := strings.Split(elementName, ".")
|
||||
if len(parts) == 1 {
|
||||
return provider + "." + elementName
|
||||
}
|
||||
return elementName
|
||||
}
|
726
pkg/config/runtime_test.go
Normal file
726
pkg/config/runtime_test.go
Normal file
|
@ -0,0 +1,726 @@
|
|||
package config_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// all the Routers/Middlewares/Services are considered fully qualified
|
||||
func TestPopulateUsedby(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
conf *config.RuntimeConfiguration
|
||||
expected config.RuntimeConfiguration
|
||||
}{
|
||||
{
|
||||
desc: "nil config",
|
||||
conf: nil,
|
||||
expected: config.RuntimeConfiguration{},
|
||||
},
|
||||
{
|
||||
desc: "One service used by two routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.foo": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1:8085",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
URL: "http://127.0.0.1:8086",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
HealthCheck: &config.HealthCheck{
|
||||
Interval: "500ms",
|
||||
Path: "/health",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.foo": {},
|
||||
"myprovider.bar": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "One service used by two routers, but one router with wrong rule",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.foo": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.foo": {},
|
||||
"myprovider.bar": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Broken Service used by one Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "2 different Services each used by a disctinct router.",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1:8085",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
URL: "http://127.0.0.1:8086",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
HealthCheck: &config.HealthCheck{
|
||||
Interval: "500ms",
|
||||
Path: "/health",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider.bar-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1:8087",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
URL: "http://127.0.0.1:8088",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
HealthCheck: &config.HealthCheck{
|
||||
Interval: "500ms",
|
||||
Path: "/health",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.foo": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {},
|
||||
"myprovider.foo": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.foo"},
|
||||
},
|
||||
"myprovider.bar-service": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "2 middlewares both used by 2 Routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider.addPrefixTest": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest"},
|
||||
},
|
||||
},
|
||||
"myprovider.test": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {},
|
||||
"myprovider.test": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.test"},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.test"},
|
||||
},
|
||||
"myprovider.addPrefixTest": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.test"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Unknown middleware is not used by the Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"unknown"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Broken middleware is used by Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"badConf"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"myprovider.auth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "2 middlewares from 2 disctinct providers both used by 2 Routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
Middleware: &config.Middleware{
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider.addPrefixTest": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/titi",
|
||||
},
|
||||
},
|
||||
},
|
||||
"anotherprovider.addPrefixTest": {
|
||||
Middleware: &config.Middleware{
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "anotherprovider.addPrefixTest"},
|
||||
},
|
||||
},
|
||||
"myprovider.test": {
|
||||
Router: &config.Router{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
Routers: map[string]*config.RouterInfo{
|
||||
"myprovider.bar": {},
|
||||
"myprovider.test": {},
|
||||
},
|
||||
Services: map[string]*config.ServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.test"},
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*config.MiddlewareInfo{
|
||||
"myprovider.auth": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.test"},
|
||||
},
|
||||
"myprovider.addPrefixTest": {
|
||||
UsedBy: []string{"myprovider.test"},
|
||||
},
|
||||
"anotherprovider.addPrefixTest": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// TCP tests from hereon
|
||||
{
|
||||
desc: "TCP, One service used by two routers",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider.bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8085",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8086",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.foo": {},
|
||||
"myprovider.bar": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TCP, One service used by two routers, but one router with wrong rule",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider.bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.foo": {},
|
||||
"myprovider.bar": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar", "myprovider.foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TCP, Broken Service used by one Router",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.bar": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TCP, 2 different Services each used by a disctinct router.",
|
||||
conf: &config.RuntimeConfiguration{
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8085",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8086",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
"myprovider.bar-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8087",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8088",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"myprovider.bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "myprovider.bar-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: config.RuntimeConfiguration{
|
||||
TCPRouters: map[string]*config.TCPRouterInfo{
|
||||
"myprovider.bar": {},
|
||||
"myprovider.foo": {},
|
||||
},
|
||||
TCPServices: map[string]*config.TCPServiceInfo{
|
||||
"myprovider.foo-service": {
|
||||
UsedBy: []string{"myprovider.foo"},
|
||||
},
|
||||
"myprovider.bar-service": {
|
||||
UsedBy: []string{"myprovider.bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runtimeConf := test.conf
|
||||
runtimeConf.PopulateUsedBy()
|
||||
|
||||
for key, expectedService := range test.expected.Services {
|
||||
require.NotNil(t, runtimeConf.Services[key])
|
||||
assert.Equal(t, expectedService.UsedBy, runtimeConf.Services[key].UsedBy)
|
||||
}
|
||||
|
||||
for key, expectedMiddleware := range test.expected.Middlewares {
|
||||
require.NotNil(t, runtimeConf.Middlewares[key])
|
||||
assert.Equal(t, expectedMiddleware.UsedBy, runtimeConf.Middlewares[key].UsedBy)
|
||||
}
|
||||
|
||||
for key, expectedTCPService := range test.expected.TCPServices {
|
||||
require.NotNil(t, runtimeConf.TCPServices[key])
|
||||
assert.Equal(t, expectedTCPService.UsedBy, runtimeConf.TCPServices[key].UsedBy)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -10,12 +10,18 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
)
|
||||
|
||||
const (
|
||||
serverUp = "UP"
|
||||
serverDown = "DOWN"
|
||||
)
|
||||
|
||||
var singleton *HealthCheck
|
||||
var once sync.Once
|
||||
|
||||
|
@ -221,3 +227,38 @@ func checkHealth(serverURL *url.URL, backend *BackendConfig) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewLBStatusUpdater returns a new LbStatusUpdater
|
||||
func NewLBStatusUpdater(bh BalancerHandler, svinfo *config.ServiceInfo) *LbStatusUpdater {
|
||||
return &LbStatusUpdater{
|
||||
BalancerHandler: bh,
|
||||
serviceInfo: svinfo,
|
||||
}
|
||||
}
|
||||
|
||||
// LbStatusUpdater wraps a BalancerHandler and a ServiceInfo,
|
||||
// so it can keep track of the status of a server in the ServiceInfo.
|
||||
type LbStatusUpdater struct {
|
||||
BalancerHandler
|
||||
serviceInfo *config.ServiceInfo // can be nil
|
||||
}
|
||||
|
||||
// RemoveServer removes the given server from the BalancerHandler,
|
||||
// and updates the status of the server to "DOWN".
|
||||
func (lb *LbStatusUpdater) RemoveServer(u *url.URL) error {
|
||||
err := lb.BalancerHandler.RemoveServer(u)
|
||||
if err == nil && lb.serviceInfo != nil {
|
||||
lb.serviceInfo.UpdateStatus(u.String(), serverDown)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UpsertServer adds the given server to the BalancerHandler,
|
||||
// and updates the status of the server to "UP".
|
||||
func (lb *LbStatusUpdater) UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error {
|
||||
err := lb.BalancerHandler.UpsertServer(u, options...)
|
||||
if err == nil && lb.serviceInfo != nil {
|
||||
lb.serviceInfo.UpdateStatus(u.String(), serverUp)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/testhelpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -367,6 +368,8 @@ type testLoadBalancer struct {
|
|||
numRemovedServers int
|
||||
numUpsertedServers int
|
||||
servers []*url.URL
|
||||
// options is just to make sure that LBStatusUpdater forwards options on Upsert to its BalancerHandler
|
||||
options []roundrobin.ServerOption
|
||||
}
|
||||
|
||||
func (lb *testLoadBalancer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
@ -386,6 +389,7 @@ func (lb *testLoadBalancer) UpsertServer(u *url.URL, options ...roundrobin.Serve
|
|||
defer lb.Unlock()
|
||||
lb.numUpsertedServers++
|
||||
lb.servers = append(lb.servers, u)
|
||||
lb.options = append(lb.options, options...)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -393,14 +397,23 @@ func (lb *testLoadBalancer) Servers() []*url.URL {
|
|||
return lb.servers
|
||||
}
|
||||
|
||||
func (lb *testLoadBalancer) Options() []roundrobin.ServerOption {
|
||||
return lb.options
|
||||
}
|
||||
|
||||
func (lb *testLoadBalancer) removeServer(u *url.URL) {
|
||||
var i int
|
||||
var serverURL *url.URL
|
||||
found := false
|
||||
for i, serverURL = range lb.servers {
|
||||
if *serverURL == *u {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
lb.servers = append(lb.servers[:i], lb.servers[i+1:]...)
|
||||
}
|
||||
|
@ -427,3 +440,32 @@ func (th *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
th.done()
|
||||
}
|
||||
}
|
||||
|
||||
func TestLBStatusUpdater(t *testing.T) {
|
||||
lb := &testLoadBalancer{RWMutex: &sync.RWMutex{}}
|
||||
svInfo := &config.ServiceInfo{}
|
||||
lbsu := NewLBStatusUpdater(lb, svInfo)
|
||||
newServer, err := url.Parse("http://foo.com")
|
||||
assert.Nil(t, err)
|
||||
err = lbsu.UpsertServer(newServer, roundrobin.Weight(1))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(lbsu.Servers()), 1)
|
||||
assert.Equal(t, len(lbsu.BalancerHandler.(*testLoadBalancer).Options()), 1)
|
||||
statuses := svInfo.GetAllStatus()
|
||||
assert.Equal(t, len(statuses), 1)
|
||||
for k, v := range statuses {
|
||||
assert.Equal(t, k, newServer.String())
|
||||
assert.Equal(t, v, serverUp)
|
||||
break
|
||||
}
|
||||
err = lbsu.RemoveServer(newServer)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(lbsu.Servers()), 0)
|
||||
statuses = svInfo.GetAllStatus()
|
||||
assert.Equal(t, len(statuses), 1)
|
||||
for k, v := range statuses {
|
||||
assert.Equal(t, k, newServer.String())
|
||||
assert.Equal(t, v, serverDown)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ import (
|
|||
)
|
||||
|
||||
// NewBuilder creates a builder.
|
||||
func NewBuilder(configs map[string]*config.Middleware) *Builder {
|
||||
func NewBuilder(configs map[string]*config.MiddlewareInfo) *Builder {
|
||||
return &Builder{configs: configs}
|
||||
}
|
||||
|
||||
// Builder holds builder configuration.
|
||||
type Builder struct {
|
||||
configs map[string]*config.Middleware
|
||||
configs map[string]*config.MiddlewareInfo
|
||||
}
|
||||
|
||||
// Build Builds the response modifier.
|
||||
|
|
|
@ -166,7 +166,12 @@ func TestBuilderBuild(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
builder := NewBuilder(test.conf)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Middlewares: test.conf,
|
||||
},
|
||||
})
|
||||
builder := NewBuilder(rtConf.Middlewares)
|
||||
|
||||
rm := builder.Build(context.Background(), test.middlewares)
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ const (
|
|||
|
||||
// Builder the middleware builder
|
||||
type Builder struct {
|
||||
configs map[string]*config.Middleware
|
||||
configs map[string]*config.MiddlewareInfo
|
||||
serviceBuilder serviceBuilder
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ type serviceBuilder interface {
|
|||
}
|
||||
|
||||
// NewBuilder creates a new Builder
|
||||
func NewBuilder(configs map[string]*config.Middleware, serviceBuilder serviceBuilder) *Builder {
|
||||
func NewBuilder(configs map[string]*config.MiddlewareInfo, serviceBuilder serviceBuilder) *Builder {
|
||||
return &Builder{configs: configs, serviceBuilder: serviceBuilder}
|
||||
}
|
||||
|
||||
|
@ -60,20 +60,29 @@ func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *alice.C
|
|||
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
constructorContext := internal.AddProviderInContext(ctx, middlewareName)
|
||||
if _, ok := b.configs[middlewareName]; !ok {
|
||||
if midInf, ok := b.configs[middlewareName]; !ok || midInf.Middleware == nil {
|
||||
return nil, fmt.Errorf("middleware %q does not exist", middlewareName)
|
||||
}
|
||||
|
||||
var err error
|
||||
if constructorContext, err = checkRecursion(constructorContext, middlewareName); err != nil {
|
||||
b.configs[middlewareName].Err = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
constructor, err := b.buildConstructor(constructorContext, middlewareName, *b.configs[middlewareName])
|
||||
constructor, err := b.buildConstructor(constructorContext, middlewareName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error during instanciation of %s: %v", middlewareName, err)
|
||||
b.configs[middlewareName].Err = err
|
||||
return nil, err
|
||||
}
|
||||
return constructor(next)
|
||||
|
||||
handler, err := constructor(next)
|
||||
if err != nil {
|
||||
b.configs[middlewareName].Err = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return handler, nil
|
||||
})
|
||||
}
|
||||
return &chain
|
||||
|
@ -90,7 +99,9 @@ func checkRecursion(ctx context.Context, middlewareName string) (context.Context
|
|||
return context.WithValue(ctx, middlewareStackKey, append(currentStack, middlewareName)), nil
|
||||
}
|
||||
|
||||
func (b *Builder) buildConstructor(ctx context.Context, middlewareName string, config config.Middleware) (alice.Constructor, error) {
|
||||
// it is the responsibility of the caller to make sure that b.configs[middlewareName].Middleware exists
|
||||
func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (alice.Constructor, error) {
|
||||
config := b.configs[middlewareName]
|
||||
var middleware alice.Constructor
|
||||
badConf := errors.New("cannot create middleware: multi-types middleware not supported, consider declaring two different pieces of middleware instead")
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
func TestBuilder_BuildChainNilConfig(t *testing.T) {
|
||||
testConfig := map[string]*config.Middleware{
|
||||
testConfig := map[string]*config.MiddlewareInfo{
|
||||
"empty": {},
|
||||
}
|
||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||
|
@ -25,7 +25,7 @@ func TestBuilder_BuildChainNilConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBuilder_BuildChainNonExistentChain(t *testing.T) {
|
||||
testConfig := map[string]*config.Middleware{
|
||||
testConfig := map[string]*config.MiddlewareInfo{
|
||||
"foobar": {},
|
||||
}
|
||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||
|
@ -264,7 +264,12 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
|||
ctx = internal.AddProviderInContext(ctx, test.contextProvider+".foobar")
|
||||
}
|
||||
|
||||
builder := NewBuilder(test.configuration, nil)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Middlewares: test.configuration,
|
||||
},
|
||||
})
|
||||
builder := NewBuilder(rtConf.Middlewares, nil)
|
||||
|
||||
result := builder.BuildChain(ctx, test.buildChain)
|
||||
|
||||
|
@ -310,7 +315,12 @@ func TestBuilder_buildConstructor(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Middlewares: testConfig,
|
||||
},
|
||||
})
|
||||
middlewaresBuilder := NewBuilder(rtConf.Middlewares, nil)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -344,7 +354,8 @@ func TestBuilder_buildConstructor(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
constructor, err := middlewaresBuilder.buildConstructor(context.Background(), test.middlewareID, *testConfig[test.middlewareID])
|
||||
constructor, err := middlewaresBuilder.buildConstructor(context.Background(), test.middlewareID)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
middleware, err2 := constructor(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
|
||||
|
|
|
@ -6,10 +6,10 @@ import (
|
|||
"github.com/containous/alice"
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/pkg/api"
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/metrics"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -19,7 +19,8 @@ type chainBuilder interface {
|
|||
}
|
||||
|
||||
// NewRouteAppenderAggregator Creates a new RouteAppenderAggregator
|
||||
func NewRouteAppenderAggregator(ctx context.Context, chainBuilder chainBuilder, conf static.Configuration, entryPointName string, currentConfiguration *safe.Safe) *RouteAppenderAggregator {
|
||||
func NewRouteAppenderAggregator(ctx context.Context, chainBuilder chainBuilder, conf static.Configuration,
|
||||
entryPointName string, runtimeConfiguration *config.RuntimeConfiguration) *RouteAppenderAggregator {
|
||||
aggregator := &RouteAppenderAggregator{}
|
||||
|
||||
if conf.Providers != nil && conf.Providers.Rest != nil {
|
||||
|
@ -29,17 +30,9 @@ func NewRouteAppenderAggregator(ctx context.Context, chainBuilder chainBuilder,
|
|||
if conf.API != nil && conf.API.EntryPoint == entryPointName {
|
||||
chain := chainBuilder.BuildChain(ctx, conf.API.Middlewares)
|
||||
aggregator.AddAppender(&WithMiddleware{
|
||||
appender: api.Handler{
|
||||
EntryPoint: conf.API.EntryPoint,
|
||||
Dashboard: conf.API.Dashboard,
|
||||
Statistics: conf.API.Statistics,
|
||||
DashboardAssets: conf.API.DashboardAssets,
|
||||
CurrentConfigurations: currentConfiguration,
|
||||
Debug: conf.Global.Debug,
|
||||
},
|
||||
appender: api.New(conf, runtimeConfiguration),
|
||||
routerMiddlewares: chain,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
if conf.Ping != nil && conf.Ping.EntryPoint == entryPointName {
|
||||
|
|
|
@ -62,7 +62,7 @@ func TestNewRouteAppenderAggregator(t *testing.T) {
|
|||
"/wrong": http.StatusBadGateway,
|
||||
"/ping": http.StatusOK,
|
||||
// "/.well-known/acme-challenge/token": http.StatusNotFound, // FIXME
|
||||
"/api/providers": http.StatusUnauthorized,
|
||||
"/api/rawdata": http.StatusUnauthorized,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -3,9 +3,9 @@ package router
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/provider/acme"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/containous/traefik/pkg/server/middleware"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
)
|
||||
|
@ -27,8 +27,8 @@ type RouteAppenderFactory struct {
|
|||
}
|
||||
|
||||
// NewAppender Creates a new RouteAppender
|
||||
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, currentConfiguration *safe.Safe) types.RouteAppender {
|
||||
aggregator := NewRouteAppenderAggregator(ctx, middlewaresBuilder, r.staticConfiguration, r.entryPointName, currentConfiguration)
|
||||
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, runtimeConfiguration *config.RuntimeConfiguration) types.RouteAppender {
|
||||
aggregator := NewRouteAppenderAggregator(ctx, middlewaresBuilder, r.staticConfiguration, r.entryPointName, runtimeConfiguration)
|
||||
|
||||
if r.acmeProvider != nil && r.acmeProvider.HTTPChallenge != nil && r.acmeProvider.HTTPChallenge.EntryPoint == r.entryPointName {
|
||||
aggregator.AddAppender(r.acmeProvider)
|
||||
|
|
|
@ -23,7 +23,7 @@ const (
|
|||
)
|
||||
|
||||
// NewManager Creates a new Manager
|
||||
func NewManager(routers map[string]*config.Router,
|
||||
func NewManager(routers map[string]*config.RouterInfo,
|
||||
serviceManager *service.Manager, middlewaresBuilder *middleware.Builder, modifierBuilder *responsemodifiers.Builder,
|
||||
) *Manager {
|
||||
return &Manager{
|
||||
|
@ -38,7 +38,7 @@ func NewManager(routers map[string]*config.Router,
|
|||
// Manager A route/router manager
|
||||
type Manager struct {
|
||||
routerHandlers map[string]http.Handler
|
||||
configs map[string]*config.Router
|
||||
configs map[string]*config.RouterInfo
|
||||
serviceManager *service.Manager
|
||||
middlewaresBuilder *middleware.Builder
|
||||
modifierBuilder *responsemodifiers.Builder
|
||||
|
@ -75,8 +75,17 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
|
|||
return entryPointHandlers
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.Router {
|
||||
entryPointsRouters := make(map[string]map[string]*config.Router)
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.RouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*config.RouterInfo)
|
||||
|
||||
for rtName, rt := range m.configs {
|
||||
if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
|
||||
|
@ -95,7 +104,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls
|
|||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.Router)
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.RouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
|
@ -105,7 +114,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls
|
|||
return entryPointsRouters
|
||||
}
|
||||
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.Router) (http.Handler, error) {
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.RouterInfo) (http.Handler, error) {
|
||||
router, err := rules.NewRouter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -117,12 +126,14 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
|
||||
handler, err := m.buildRouterHandler(ctxRouter, routerName)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
@ -166,7 +177,7 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string) (ht
|
|||
return m.routerHandlers[routerName], nil
|
||||
}
|
||||
|
||||
func (m *Manager) buildHTTPHandler(ctx context.Context, router *config.Router, routerName string) (http.Handler, error) {
|
||||
func (m *Manager) buildHTTPHandler(ctx context.Context, router *config.RouterInfo, routerName string) (http.Handler, error) {
|
||||
qualifiedNames := make([]string, len(router.Middlewares))
|
||||
for i, name := range router.Middlewares {
|
||||
qualifiedNames[i] = internal.GetQualifiedName(ctx, name)
|
||||
|
@ -186,12 +197,3 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *config.Router, r
|
|||
|
||||
return alice.New().Extend(*mHandler).Append(tHandler).Then(sHandler)
|
||||
}
|
||||
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -314,11 +314,17 @@ func TestRouterManager_Get(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
serviceManager := service.NewManager(test.serviceConfig, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(test.middlewaresConfig, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(test.middlewaresConfig)
|
||||
|
||||
routerManager := NewManager(test.routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: test.serviceConfig,
|
||||
Routers: test.routersConfig,
|
||||
Middlewares: test.middlewaresConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
|
@ -413,11 +419,17 @@ func TestAccessLog(t *testing.T) {
|
|||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
|
||||
serviceManager := service.NewManager(test.serviceConfig, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(test.middlewaresConfig, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(test.middlewaresConfig)
|
||||
|
||||
routerManager := NewManager(test.routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: test.serviceConfig,
|
||||
Routers: test.routersConfig,
|
||||
Middlewares: test.middlewaresConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
|
@ -443,6 +455,310 @@ func TestAccessLog(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRuntimeConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serviceConfig map[string]*config.Service
|
||||
routerConfig map[string]*config.Router
|
||||
middlewareConfig map[string]*config.Middleware
|
||||
expectedError int
|
||||
}{
|
||||
{
|
||||
desc: "No error",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1:8085",
|
||||
Weight: 1,
|
||||
},
|
||||
{
|
||||
URL: "http://127.0.0.1:8086",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
HealthCheck: &config.HealthCheck{
|
||||
Interval: "500ms",
|
||||
Path: "/health",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 0,
|
||||
},
|
||||
{
|
||||
desc: "One router with wrong rule",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "All router with wrong rule",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
{
|
||||
desc: "Router with unknown service",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"foo": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "wrong-service",
|
||||
Rule: "Host(`bar.foo`)",
|
||||
},
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "Router with broken service",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
{
|
||||
desc: "Router with middleware",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*config.Middleware{
|
||||
"auth": {
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
"addPrefixTest": {
|
||||
AddPrefix: &config.AddPrefix{
|
||||
Prefix: "/toto",
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth", "addPrefixTest"},
|
||||
},
|
||||
"test": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar.other`)",
|
||||
Middlewares: []string{"addPrefixTest", "auth"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Router with unknown middleware",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*config.Middleware{
|
||||
"auth": {
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"unknown"},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Router with broken middleware",
|
||||
serviceConfig: map[string]*config.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &config.LoadBalancerService{
|
||||
Servers: []config.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*config.Middleware{
|
||||
"auth": {
|
||||
BasicAuth: &config.BasicAuth{
|
||||
Users: []string{"foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
Middlewares: []string{"auth"},
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
entryPoints := []string{"web"}
|
||||
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: test.serviceConfig,
|
||||
Routers: test.routerConfig,
|
||||
Middlewares: test.middlewareConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(map[string]*config.MiddlewareInfo{})
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
// even though rtConf was passed by argument to the manager builders above,
|
||||
// it's ok to use it as the result we check, because everything worth checking
|
||||
// can be accessed by pointers in it.
|
||||
var allErrors int
|
||||
for _, v := range rtConf.Services {
|
||||
if v.Err != nil {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
for _, v := range rtConf.Routers {
|
||||
if v.Err != "" {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
for _, v := range rtConf.Middlewares {
|
||||
if v.Err != nil {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, test.expectedError, allErrors)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type staticTransport struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
@ -480,11 +796,17 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
}
|
||||
entryPoints := []string{"web"}
|
||||
|
||||
serviceManager := service.NewManager(serviceConfig, &staticTransport{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(map[string]*config.Middleware{}, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(map[string]*config.Middleware{})
|
||||
|
||||
routerManager := NewManager(routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: serviceConfig,
|
||||
Routers: routersConfig,
|
||||
Middlewares: map[string]*config.Middleware{},
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
|
||||
routerManager := NewManager(rtConf.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
|
@ -498,6 +820,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkService(b *testing.B) {
|
||||
res := &http.Response{
|
||||
StatusCode: 200,
|
||||
|
@ -518,7 +841,12 @@ func BenchmarkService(b *testing.B) {
|
|||
},
|
||||
}
|
||||
|
||||
serviceManager := service.NewManager(serviceConfig, &staticTransport{res})
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||
HTTP: &config.HTTPConfiguration{
|
||||
Services: serviceConfig,
|
||||
},
|
||||
})
|
||||
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res})
|
||||
w := httptest.NewRecorder()
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package tcp
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
|
@ -14,14 +15,14 @@ import (
|
|||
)
|
||||
|
||||
// NewManager Creates a new Manager
|
||||
func NewManager(routers map[string]*config.TCPRouter,
|
||||
func NewManager(conf *config.RuntimeConfiguration,
|
||||
serviceManager *tcpservice.Manager,
|
||||
httpHandlers map[string]http.Handler,
|
||||
httpsHandlers map[string]http.Handler,
|
||||
tlsConfig *tls.Config,
|
||||
) *Manager {
|
||||
return &Manager{
|
||||
configs: routers,
|
||||
configs: conf.TCPRouters,
|
||||
serviceManager: serviceManager,
|
||||
httpHandlers: httpHandlers,
|
||||
httpsHandlers: httpsHandlers,
|
||||
|
@ -31,7 +32,7 @@ func NewManager(routers map[string]*config.TCPRouter,
|
|||
|
||||
// Manager is a route/router manager
|
||||
type Manager struct {
|
||||
configs map[string]*config.TCPRouter
|
||||
configs map[string]*config.TCPRouterInfo
|
||||
serviceManager *tcpservice.Manager
|
||||
httpHandlers map[string]http.Handler
|
||||
httpsHandlers map[string]http.Handler
|
||||
|
@ -60,7 +61,7 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
|
|||
return entryPointHandlers
|
||||
}
|
||||
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.TCPRouter, handlerHTTP http.Handler, handlerHTTPS http.Handler) (*tcp.Router, error) {
|
||||
func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*config.TCPRouterInfo, handlerHTTP http.Handler, handlerHTTPS http.Handler) (*tcp.Router, error) {
|
||||
router := &tcp.Router{}
|
||||
router.HTTPHandler(handlerHTTP)
|
||||
router.HTTPSHandler(handlerHTTPS, m.tlsConfig)
|
||||
|
@ -71,18 +72,21 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
|
||||
handler, err := m.serviceManager.BuildTCP(ctxRouter, routerConfig.Service)
|
||||
if err != nil {
|
||||
routerConfig.Err = err.Error()
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
domains, err := rules.ParseHostSNI(routerConfig.Rule)
|
||||
if err != nil {
|
||||
logger.Debugf("Unknown rule %s", routerConfig.Rule)
|
||||
routerErr := fmt.Errorf("unknown rule %s", routerConfig.Rule)
|
||||
routerConfig.Err = routerErr.Error()
|
||||
logger.Debug(routerErr)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
logger.Debugf("Add route %s on TCP", domain)
|
||||
logger.Debugf("Adding route %s on TCP", domain)
|
||||
switch {
|
||||
case routerConfig.TLS != nil:
|
||||
if routerConfig.TLS.Passthrough {
|
||||
|
@ -101,8 +105,17 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
return router, nil
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map[string]map[string]*config.TCPRouter {
|
||||
entryPointsRouters := make(map[string]map[string]*config.TCPRouter)
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map[string]map[string]*config.TCPRouterInfo {
|
||||
entryPointsRouters := make(map[string]map[string]*config.TCPRouterInfo)
|
||||
|
||||
for rtName, rt := range m.configs {
|
||||
eps := rt.EntryPoints
|
||||
|
@ -118,7 +131,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map
|
|||
}
|
||||
|
||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.TCPRouter)
|
||||
entryPointsRouters[entryPointName] = make(map[string]*config.TCPRouterInfo)
|
||||
}
|
||||
|
||||
entryPointsRouters[entryPointName][rtName] = rt
|
||||
|
@ -127,12 +140,3 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map
|
|||
|
||||
return entryPointsRouters
|
||||
}
|
||||
|
||||
func contains(entryPoints []string, entryPointName string) bool {
|
||||
for _, name := range entryPoints {
|
||||
if name == entryPointName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
223
pkg/server/router/tcp/router_test.go
Normal file
223
pkg/server/router/tcp/router_test.go
Normal file
|
@ -0,0 +1,223 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/server/service/tcp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRuntimeConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serviceConfig map[string]*config.TCPServiceInfo
|
||||
routerConfig map[string]*config.TCPRouterInfo
|
||||
expectedError int
|
||||
}{
|
||||
{
|
||||
desc: "No error",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Port: "8085",
|
||||
Address: "127.0.0.1:8085",
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.1:8086",
|
||||
Port: "8086",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 0,
|
||||
},
|
||||
{
|
||||
desc: "One router with wrong rule",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "All router with wrong rule",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "WrongRule(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
{
|
||||
desc: "Router with unknown service",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"foo": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "wrong-service",
|
||||
Rule: "HostSNI(`bar.foo`)",
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "Router with broken service",
|
||||
serviceConfig: map[string]*config.TCPServiceInfo{
|
||||
"foo-service": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
routerConfig: map[string]*config.TCPRouterInfo{
|
||||
"bar": {
|
||||
TCPRouter: &config.TCPRouter{
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "HostSNI(`foo.bar`)",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
entryPoints := []string{"web"}
|
||||
|
||||
conf := &config.RuntimeConfiguration{
|
||||
TCPServices: test.serviceConfig,
|
||||
TCPRouters: test.routerConfig,
|
||||
}
|
||||
serviceManager := tcp.NewManager(conf)
|
||||
routerManager := NewManager(conf, serviceManager,
|
||||
nil, nil, nil)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints)
|
||||
|
||||
// even though conf was passed by argument to the manager builders above,
|
||||
// it's ok to use it as the result we check, because everything worth checking
|
||||
// can be accessed by pointers in it.
|
||||
var allErrors int
|
||||
for _, v := range conf.TCPServices {
|
||||
if v.Err != nil {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
for _, v := range conf.TCPRouters {
|
||||
if v.Err != "" {
|
||||
allErrors++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, test.expectedError, allErrors)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -50,7 +50,7 @@ type Server struct {
|
|||
|
||||
// RouteAppenderFactory the route appender factory interface
|
||||
type RouteAppenderFactory interface {
|
||||
NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, currentConfigurations *safe.Safe) types.RouteAppender
|
||||
NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, runtimeConfiguration *config.RuntimeConfiguration) types.RouteAppender
|
||||
}
|
||||
|
||||
func setupTracing(conf *static.Tracing) tracing.TrackingBackend {
|
||||
|
|
|
@ -69,47 +69,46 @@ func (s *Server) loadConfigurationTCP(configurations config.Configurations) map[
|
|||
|
||||
s.tlsManager.UpdateConfigs(conf.TLSStores, conf.TLSOptions, conf.TLS)
|
||||
|
||||
handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, *conf.HTTP, entryPoints)
|
||||
|
||||
routersTCP := s.createTCPRouters(ctx, conf.TCP, entryPoints, handlersNonTLS, handlersTLS, s.tlsManager.Get("default", "default"))
|
||||
rtConf := config.NewRuntimeConfig(conf)
|
||||
handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, rtConf, entryPoints)
|
||||
routersTCP := s.createTCPRouters(ctx, rtConf, entryPoints, handlersNonTLS, handlersTLS, s.tlsManager.Get("default", "default"))
|
||||
rtConf.PopulateUsedBy()
|
||||
|
||||
return routersTCP
|
||||
}
|
||||
|
||||
func (s *Server) createTCPRouters(ctx context.Context, configuration *config.TCPConfiguration, entryPoints []string, handlers map[string]http.Handler, handlersTLS map[string]http.Handler, tlsConfig *tls.Config) map[string]*tcpCore.Router {
|
||||
// the given configuration must not be nil. its fields will get mutated.
|
||||
func (s *Server) createTCPRouters(ctx context.Context, configuration *config.RuntimeConfiguration, entryPoints []string, handlers map[string]http.Handler, handlersTLS map[string]http.Handler, tlsConfig *tls.Config) map[string]*tcpCore.Router {
|
||||
if configuration == nil {
|
||||
return make(map[string]*tcpCore.Router)
|
||||
}
|
||||
|
||||
serviceManager := tcp.NewManager(configuration.Services)
|
||||
routerManager := routertcp.NewManager(configuration.Routers, serviceManager, handlers, handlersTLS, tlsConfig)
|
||||
serviceManager := tcp.NewManager(configuration)
|
||||
routerManager := routertcp.NewManager(configuration, serviceManager, handlers, handlersTLS, tlsConfig)
|
||||
|
||||
return routerManager.BuildHandlers(ctx, entryPoints)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) createHTTPHandlers(ctx context.Context, configuration config.HTTPConfiguration, entryPoints []string) (map[string]http.Handler, map[string]http.Handler) {
|
||||
// createHTTPHandlers returns, for the given configuration and entryPoints, the HTTP handlers for non-TLS connections, and for the TLS ones. the given configuration must not be nil. its fields will get mutated.
|
||||
func (s *Server) createHTTPHandlers(ctx context.Context, configuration *config.RuntimeConfiguration, entryPoints []string) (map[string]http.Handler, map[string]http.Handler) {
|
||||
serviceManager := service.NewManager(configuration.Services, s.defaultRoundTripper)
|
||||
middlewaresBuilder := middleware.NewBuilder(configuration.Middlewares, serviceManager)
|
||||
responseModifierFactory := responsemodifiers.NewBuilder(configuration.Middlewares)
|
||||
|
||||
routerManager := router.NewManager(configuration.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||
|
||||
handlersNonTLS := routerManager.BuildHandlers(ctx, entryPoints, false)
|
||||
handlersTLS := routerManager.BuildHandlers(ctx, entryPoints, true)
|
||||
|
||||
routerHandlers := make(map[string]http.Handler)
|
||||
|
||||
for _, entryPointName := range entryPoints {
|
||||
internalMuxRouter := mux.NewRouter().
|
||||
SkipClean(true)
|
||||
internalMuxRouter := mux.NewRouter().SkipClean(true)
|
||||
|
||||
ctx = log.With(ctx, log.Str(log.EntryPointName, entryPointName))
|
||||
|
||||
factory := s.entryPointsTCP[entryPointName].RouteAppenderFactory
|
||||
if factory != nil {
|
||||
// FIXME remove currentConfigurations
|
||||
appender := factory.NewAppender(ctx, middlewaresBuilder, &s.currentConfigurations)
|
||||
appender := factory.NewAppender(ctx, middlewaresBuilder, configuration)
|
||||
appender.Append(internalMuxRouter)
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ func TestReuseService(t *testing.T) {
|
|||
|
||||
srv := NewServer(staticConfig, nil, entryPoints, nil)
|
||||
|
||||
entrypointsHandlers, _ := srv.createHTTPHandlers(context.Background(), *dynamicConfigs, []string{"http"})
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{HTTP: dynamicConfigs})
|
||||
entrypointsHandlers, _ := srv.createHTTPHandlers(context.Background(), rtConf, []string{"http"})
|
||||
|
||||
// Test that the /ok path returns a status 200.
|
||||
responseRecorderOk := &httptest.ResponseRecorder{}
|
||||
|
|
|
@ -258,7 +258,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
|||
}
|
||||
|
||||
srv := NewServer(globalConfig, nil, entryPointsConfig, nil)
|
||||
entryPoints, _ := srv.createHTTPHandlers(context.Background(), *test.config(testServer.URL), []string{"http"})
|
||||
rtConf := config.NewRuntimeConfig(config.Configuration{HTTP: test.config(testServer.URL)})
|
||||
entryPoints, _ := srv.createHTTPHandlers(context.Background(), rtConf, []string{"http"})
|
||||
|
||||
responseRecorder := &httptest.ResponseRecorder{}
|
||||
request := httptest.NewRequest(http.MethodGet, testServer.URL+requestPath, nil)
|
||||
|
|
|
@ -26,7 +26,7 @@ const (
|
|||
)
|
||||
|
||||
// NewManager creates a new Manager
|
||||
func NewManager(configs map[string]*config.Service, defaultRoundTripper http.RoundTripper) *Manager {
|
||||
func NewManager(configs map[string]*config.ServiceInfo, defaultRoundTripper http.RoundTripper) *Manager {
|
||||
return &Manager{
|
||||
bufferPool: newBufferPool(),
|
||||
defaultRoundTripper: defaultRoundTripper,
|
||||
|
@ -40,7 +40,7 @@ type Manager struct {
|
|||
bufferPool httputil.BufferPool
|
||||
defaultRoundTripper http.RoundTripper
|
||||
balancers map[string][]healthcheck.BalancerHandler
|
||||
configs map[string]*config.Service
|
||||
configs map[string]*config.ServiceInfo
|
||||
}
|
||||
|
||||
// BuildHTTP Creates a http.Handler for a service configuration.
|
||||
|
@ -50,15 +50,25 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
|
|||
serviceName = internal.GetQualifiedName(ctx, serviceName)
|
||||
ctx = internal.AddProviderInContext(ctx, serviceName)
|
||||
|
||||
if conf, ok := m.configs[serviceName]; ok {
|
||||
conf, ok := m.configs[serviceName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("the service %q does not exist", serviceName)
|
||||
}
|
||||
|
||||
// TODO Should handle multiple service types
|
||||
// FIXME Check if the service is declared multiple times with different types
|
||||
if conf.LoadBalancer != nil {
|
||||
return m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
|
||||
if conf.LoadBalancer == nil {
|
||||
conf.Err = fmt.Errorf("the service %q doesn't have any load balancer", serviceName)
|
||||
return nil, conf.Err
|
||||
}
|
||||
return nil, fmt.Errorf("the service %q doesn't have any load balancer", serviceName)
|
||||
|
||||
lb, err := m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
|
||||
if err != nil {
|
||||
conf.Err = err
|
||||
return nil, err
|
||||
}
|
||||
return nil, fmt.Errorf("the service %q does not exits", serviceName)
|
||||
|
||||
return lb, nil
|
||||
}
|
||||
|
||||
func (m *Manager) getLoadBalancerServiceHandler(
|
||||
|
@ -158,7 +168,7 @@ func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler
|
|||
}
|
||||
|
||||
if timeout >= interval {
|
||||
logger.Warnf("Health check timeout for backend '%s' should be lower than the health check interval. Interval set to timeout + 1 second (%s).", backend)
|
||||
logger.Warnf("Health check timeout for backend '%s' should be lower than the health check interval. Interval set to timeout + 1 second (%s).", backend, interval)
|
||||
}
|
||||
|
||||
return &healthcheck.Options{
|
||||
|
@ -229,7 +239,8 @@ func (m *Manager) getLoadBalancer(ctx context.Context, serviceName string, servi
|
|||
}
|
||||
}
|
||||
|
||||
if err := m.upsertServers(ctx, lb, service.Servers); err != nil {
|
||||
lbsu := healthcheck.NewLBStatusUpdater(lb, m.configs[serviceName])
|
||||
if err := m.upsertServers(ctx, lbsu, service.Servers); err != nil {
|
||||
return nil, fmt.Errorf("error configuring load balancer for service %s: %v", serviceName, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -275,35 +275,41 @@ func TestManager_Build(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
serviceName string
|
||||
configs map[string]*config.Service
|
||||
configs map[string]*config.ServiceInfo
|
||||
providerName string
|
||||
}{
|
||||
{
|
||||
desc: "Simple service name",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.Service{
|
||||
configs: map[string]*config.ServiceInfo{
|
||||
"serviceName": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider",
|
||||
serviceName: "provider-1.serviceName",
|
||||
configs: map[string]*config.Service{
|
||||
configs: map[string]*config.ServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider in context",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.Service{
|
||||
configs: map[string]*config.ServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
Service: &config.Service{
|
||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -13,13 +13,13 @@ import (
|
|||
|
||||
// Manager is the TCPHandlers factory
|
||||
type Manager struct {
|
||||
configs map[string]*config.TCPService
|
||||
configs map[string]*config.TCPServiceInfo
|
||||
}
|
||||
|
||||
// NewManager creates a new manager
|
||||
func NewManager(configs map[string]*config.TCPService) *Manager {
|
||||
func NewManager(conf *config.RuntimeConfiguration) *Manager {
|
||||
return &Manager{
|
||||
configs: configs,
|
||||
configs: conf.TCPServices,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,23 +29,23 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
|
|||
ctx := internal.AddProviderInContext(rootCtx, serviceQualifiedName)
|
||||
ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
|
||||
|
||||
// FIXME Check if the service is declared multiple times with different types
|
||||
conf, ok := m.configs[serviceQualifiedName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("the service %q does not exits", serviceQualifiedName)
|
||||
return nil, fmt.Errorf("the service %q does not exist", serviceQualifiedName)
|
||||
}
|
||||
|
||||
if conf.LoadBalancer == nil {
|
||||
return nil, fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
|
||||
conf.Err = fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
|
||||
return nil, conf.Err
|
||||
}
|
||||
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// FIXME Check if the service is declared multiple times with different types
|
||||
loadBalancer := tcp.NewRRLoadBalancer()
|
||||
|
||||
for _, server := range conf.LoadBalancer.Servers {
|
||||
if _, err := parseIP(server.Address); err != nil {
|
||||
logger.Errorf("Invalid IP address for a %q server %q: %v", serviceQualifiedName, server.Address, err)
|
||||
if _, _, err := net.SplitHostPort(server.Address); err != nil {
|
||||
logger.Errorf("In service %q: %v", serviceQualifiedName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -57,20 +57,5 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
|
|||
|
||||
loadBalancer.AddServer(handler)
|
||||
}
|
||||
|
||||
return loadBalancer, nil
|
||||
}
|
||||
|
||||
func parseIP(s string) (string, error) {
|
||||
ip, _, err := net.SplitHostPort(s)
|
||||
if err == nil {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
ipNoPort := net.ParseIP(s)
|
||||
if ipNoPort == nil {
|
||||
return "", fmt.Errorf("invalid IP Address %s", ipNoPort)
|
||||
}
|
||||
|
||||
return ipNoPort.String(), nil
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/server/internal"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -12,28 +14,32 @@ func TestManager_BuildTCP(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
serviceName string
|
||||
configs map[string]*config.TCPService
|
||||
configs map[string]*config.TCPServiceInfo
|
||||
providerName string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
desc: "without configuration",
|
||||
serviceName: "test",
|
||||
configs: nil,
|
||||
expectedError: `the service "test" does not exits`,
|
||||
expectedError: `the service "test" does not exist`,
|
||||
},
|
||||
{
|
||||
desc: "missing lb configuration",
|
||||
serviceName: "test",
|
||||
configs: map[string]*config.TCPService{
|
||||
"test": {},
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"test": {
|
||||
TCPService: &config.TCPService{},
|
||||
},
|
||||
},
|
||||
expectedError: `the service "test" doesn't have any TCP load balancer`,
|
||||
},
|
||||
{
|
||||
desc: "no such host",
|
||||
desc: "no such host, server is skipped, error is logged",
|
||||
serviceName: "test",
|
||||
configs: map[string]*config.TCPService{
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"test": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{Address: "test:31"},
|
||||
|
@ -42,11 +48,13 @@ func TestManager_BuildTCP(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid IP address",
|
||||
desc: "invalid IP address, server is skipped, error is logged",
|
||||
serviceName: "test",
|
||||
configs: map[string]*config.TCPService{
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"test": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{Address: "foobar"},
|
||||
|
@ -55,6 +63,117 @@ func TestManager_BuildTCP(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Simple service name",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider",
|
||||
serviceName: "provider-1.serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Service name with provider in context",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{Method: "wrr"},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "Server with correct host:port as address",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "foobar.com:80",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "Server with correct ip:port as address",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "192.168.0.12:80",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "missing port in address with hostname, server is skipped, error is logged",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "foobar.com",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
{
|
||||
desc: "missing port in address with ip, server is skipped, error is logged",
|
||||
serviceName: "serviceName",
|
||||
configs: map[string]*config.TCPServiceInfo{
|
||||
"provider-1.serviceName": {
|
||||
TCPService: &config.TCPService{
|
||||
LoadBalancer: &config.TCPLoadBalancerService{
|
||||
Servers: []config.TCPServer{
|
||||
{
|
||||
Address: "192.168.0.12",
|
||||
},
|
||||
},
|
||||
Method: "wrr",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
providerName: "provider-1",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@ -62,19 +181,22 @@ func TestManager_BuildTCP(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
manager := NewManager(test.configs)
|
||||
manager := NewManager(&config.RuntimeConfiguration{
|
||||
TCPServices: test.configs,
|
||||
})
|
||||
|
||||
handler, err := manager.BuildTCP(context.Background(), test.serviceName)
|
||||
ctx := context.Background()
|
||||
if len(test.providerName) > 0 {
|
||||
ctx = internal.AddProviderInContext(ctx, test.providerName+".foobar")
|
||||
}
|
||||
|
||||
handler, err := manager.BuildTCP(ctx, test.serviceName)
|
||||
|
||||
if test.expectedError != "" {
|
||||
if err == nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expectedError)
|
||||
assert.EqualError(t, err, test.expectedError)
|
||||
require.Nil(t, handler)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, handler)
|
||||
}
|
||||
})
|
||||
|
|
22
vendor/github.com/thoas/stats/LICENSE
generated
vendored
22
vendor/github.com/thoas/stats/LICENSE
generated
vendored
|
@ -1,22 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Florent Messa
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
59
vendor/github.com/thoas/stats/options.go
generated
vendored
59
vendor/github.com/thoas/stats/options.go
generated
vendored
|
@ -1,59 +0,0 @@
|
|||
package stats
|
||||
|
||||
// Options are stats options.
|
||||
type Options struct {
|
||||
statusCode *int
|
||||
size int
|
||||
recorder ResponseWriter
|
||||
}
|
||||
|
||||
// StatusCode returns the response status code.
|
||||
func (o Options) StatusCode() int {
|
||||
if o.recorder != nil {
|
||||
return o.recorder.Status()
|
||||
}
|
||||
|
||||
return *o.statusCode
|
||||
}
|
||||
|
||||
// Size returns the response size.
|
||||
func (o Options) Size() int {
|
||||
if o.recorder != nil {
|
||||
return o.recorder.Size()
|
||||
}
|
||||
|
||||
return o.size
|
||||
}
|
||||
|
||||
// Option represents a stats option.
|
||||
type Option func(*Options)
|
||||
|
||||
// WithStatusCode sets the status code to use in stats.
|
||||
func WithStatusCode(statusCode int) Option {
|
||||
return func(o *Options) {
|
||||
o.statusCode = &statusCode
|
||||
}
|
||||
}
|
||||
|
||||
// WithSize sets the size to use in stats.
|
||||
func WithSize(size int) Option {
|
||||
return func(o *Options) {
|
||||
o.size = size
|
||||
}
|
||||
}
|
||||
|
||||
// WithRecorder sets the recorder to use in stats.
|
||||
func WithRecorder(recorder ResponseWriter) Option {
|
||||
return func(o *Options) {
|
||||
o.recorder = recorder
|
||||
}
|
||||
}
|
||||
|
||||
// newOptions takes functional options and returns options.
|
||||
func newOptions(options ...Option) *Options {
|
||||
opts := &Options{}
|
||||
for _, o := range options {
|
||||
o(opts)
|
||||
}
|
||||
return opts
|
||||
}
|
95
vendor/github.com/thoas/stats/recorder.go
generated
vendored
95
vendor/github.com/thoas/stats/recorder.go
generated
vendored
|
@ -1,95 +0,0 @@
|
|||
package stats
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
// Status returns the status code of the response or 0 if the response has not been written.
|
||||
Status() int
|
||||
// Written returns whether or not the ResponseWriter has been written.
|
||||
Written() bool
|
||||
// Size returns the size of the response body.
|
||||
Size() int
|
||||
// Before allows for a function to be called before the ResponseWriter has been written to. This is
|
||||
// useful for setting headers or any other operations that must happen before a response has been written.
|
||||
Before(func(ResponseWriter))
|
||||
}
|
||||
|
||||
type beforeFunc func(ResponseWriter)
|
||||
|
||||
type recorderResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
size int
|
||||
beforeFuncs []beforeFunc
|
||||
written bool
|
||||
}
|
||||
|
||||
func NewRecorderResponseWriter(w http.ResponseWriter, statusCode int) ResponseWriter {
|
||||
return &recorderResponseWriter{ResponseWriter: w, status: statusCode}
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) WriteHeader(code int) {
|
||||
r.written = true
|
||||
r.ResponseWriter.WriteHeader(code)
|
||||
r.status = code
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Flush() {
|
||||
flusher, ok := r.ResponseWriter.(http.Flusher)
|
||||
if ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Status() int {
|
||||
return r.status
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Write(b []byte) (int, error) {
|
||||
if !r.Written() {
|
||||
// The status will be StatusOK if WriteHeader has not been called yet
|
||||
r.WriteHeader(http.StatusOK)
|
||||
}
|
||||
size, err := r.ResponseWriter.Write(b)
|
||||
r.size += size
|
||||
return size, err
|
||||
}
|
||||
|
||||
// Proxy method to Status to add support for gocraft
|
||||
func (r *recorderResponseWriter) StatusCode() int {
|
||||
return r.Status()
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Size() int {
|
||||
return r.size
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Written() bool {
|
||||
return r.StatusCode() != 0
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) CloseNotify() <-chan bool {
|
||||
return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if !r.written {
|
||||
r.status = 0
|
||||
}
|
||||
hijacker, ok := r.ResponseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
func (r *recorderResponseWriter) Before(before func(ResponseWriter)) {
|
||||
r.beforeFuncs = append(r.beforeFuncs, before)
|
||||
}
|
227
vendor/github.com/thoas/stats/stats.go
generated
vendored
227
vendor/github.com/thoas/stats/stats.go
generated
vendored
|
@ -1,227 +0,0 @@
|
|||
package stats
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Stats data structure
|
||||
type Stats struct {
|
||||
mu sync.RWMutex
|
||||
closed chan struct{}
|
||||
Hostname string
|
||||
Uptime time.Time
|
||||
Pid int
|
||||
ResponseCounts map[string]int
|
||||
TotalResponseCounts map[string]int
|
||||
TotalResponseTime time.Time
|
||||
TotalResponseSize int64
|
||||
MetricsCounts map[string]int
|
||||
MetricsTimers map[string]time.Time
|
||||
}
|
||||
|
||||
// Label data structure
|
||||
type Label struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// New constructs a new Stats structure
|
||||
func New() *Stats {
|
||||
name, _ := os.Hostname()
|
||||
|
||||
stats := &Stats{
|
||||
closed: make(chan struct{}, 1),
|
||||
Uptime: time.Now(),
|
||||
Pid: os.Getpid(),
|
||||
ResponseCounts: map[string]int{},
|
||||
TotalResponseCounts: map[string]int{},
|
||||
TotalResponseTime: time.Time{},
|
||||
Hostname: name,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-stats.closed:
|
||||
return
|
||||
default:
|
||||
stats.ResetResponseCounts()
|
||||
|
||||
time.Sleep(time.Second * 1)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
func (mw *Stats) Close() {
|
||||
close(mw.closed)
|
||||
}
|
||||
|
||||
// ResetResponseCounts reset the response counts
|
||||
func (mw *Stats) ResetResponseCounts() {
|
||||
mw.mu.Lock()
|
||||
defer mw.mu.Unlock()
|
||||
mw.ResponseCounts = map[string]int{}
|
||||
}
|
||||
|
||||
// Handler is a MiddlewareFunc makes Stats implement the Middleware interface.
|
||||
func (mw *Stats) Handler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
beginning, recorder := mw.Begin(w)
|
||||
|
||||
h.ServeHTTP(recorder, r)
|
||||
|
||||
mw.End(beginning, WithRecorder(recorder))
|
||||
})
|
||||
}
|
||||
|
||||
// Negroni compatible interface
|
||||
func (mw *Stats) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
beginning, recorder := mw.Begin(w)
|
||||
|
||||
next(recorder, r)
|
||||
|
||||
mw.End(beginning, WithRecorder(recorder))
|
||||
}
|
||||
|
||||
// Begin starts a recorder
|
||||
func (mw *Stats) Begin(w http.ResponseWriter) (time.Time, ResponseWriter) {
|
||||
start := time.Now()
|
||||
|
||||
writer := NewRecorderResponseWriter(w, 200)
|
||||
|
||||
return start, writer
|
||||
}
|
||||
|
||||
// End closes the recorder with a specific status
|
||||
func (mw *Stats) End(start time.Time, opts ...Option) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
responseTime := time.Since(start)
|
||||
|
||||
mw.mu.Lock()
|
||||
|
||||
defer mw.mu.Unlock()
|
||||
|
||||
// If Hijacked connection do not count in response time
|
||||
if options.StatusCode() != 0 {
|
||||
statusCode := fmt.Sprintf("%d", options.StatusCode())
|
||||
mw.ResponseCounts[statusCode]++
|
||||
mw.TotalResponseCounts[statusCode]++
|
||||
mw.TotalResponseTime = mw.TotalResponseTime.Add(responseTime)
|
||||
mw.TotalResponseSize += int64(options.Size())
|
||||
}
|
||||
}
|
||||
|
||||
// MeasureSince method for execution time recording
|
||||
func (mw *Stats) MeasureSince(key string, start time.Time) {
|
||||
mw.MeasureSinceWithLabels(key, start, nil)
|
||||
}
|
||||
|
||||
// MeasureSinceWithLabels method for execution time recording with custom labels
|
||||
func (mw *Stats) MeasureSinceWithLabels(key string, start time.Time, labels []Label) {
|
||||
labels = append(labels, Label{"host", mw.Hostname})
|
||||
elapsed := time.Since(start)
|
||||
|
||||
mw.mu.Lock()
|
||||
defer mw.mu.Unlock()
|
||||
|
||||
mw.MetricsCounts[key]++
|
||||
mw.MetricsTimers[key] = mw.MetricsTimers[key].Add(elapsed)
|
||||
}
|
||||
|
||||
// Data serializable structure
|
||||
type Data struct {
|
||||
Pid int `json:"pid"`
|
||||
Hostname string `json:"hostname"`
|
||||
UpTime string `json:"uptime"`
|
||||
UpTimeSec float64 `json:"uptime_sec"`
|
||||
Time string `json:"time"`
|
||||
TimeUnix int64 `json:"unixtime"`
|
||||
StatusCodeCount map[string]int `json:"status_code_count"`
|
||||
TotalStatusCodeCount map[string]int `json:"total_status_code_count"`
|
||||
Count int `json:"count"`
|
||||
TotalCount int `json:"total_count"`
|
||||
TotalResponseTime string `json:"total_response_time"`
|
||||
TotalResponseTimeSec float64 `json:"total_response_time_sec"`
|
||||
TotalResponseSize int64 `json:"total_response_size"`
|
||||
AverageResponseSize int64 `json:"average_response_size"`
|
||||
AverageResponseTime string `json:"average_response_time"`
|
||||
AverageResponseTimeSec float64 `json:"average_response_time_sec"`
|
||||
TotalMetricsCounts map[string]int `json:"total_metrics_counts"`
|
||||
AverageMetricsTimers map[string]float64 `json:"average_metrics_timers"`
|
||||
}
|
||||
|
||||
// Data returns the data serializable structure
|
||||
func (mw *Stats) Data() *Data {
|
||||
mw.mu.RLock()
|
||||
|
||||
responseCounts := make(map[string]int, len(mw.ResponseCounts))
|
||||
totalResponseCounts := make(map[string]int, len(mw.TotalResponseCounts))
|
||||
totalMetricsCounts := make(map[string]int, len(mw.MetricsCounts))
|
||||
metricsCounts := make(map[string]float64, len(mw.MetricsCounts))
|
||||
|
||||
now := time.Now()
|
||||
|
||||
uptime := now.Sub(mw.Uptime)
|
||||
|
||||
count := 0
|
||||
for code, current := range mw.ResponseCounts {
|
||||
responseCounts[code] = current
|
||||
count += current
|
||||
}
|
||||
|
||||
totalCount := 0
|
||||
for code, count := range mw.TotalResponseCounts {
|
||||
totalResponseCounts[code] = count
|
||||
totalCount += count
|
||||
}
|
||||
|
||||
totalResponseTime := mw.TotalResponseTime.Sub(time.Time{})
|
||||
totalResponseSize := mw.TotalResponseSize
|
||||
|
||||
averageResponseTime := time.Duration(0)
|
||||
averageResponseSize := int64(0)
|
||||
if totalCount > 0 {
|
||||
avgNs := int64(totalResponseTime) / int64(totalCount)
|
||||
averageResponseTime = time.Duration(avgNs)
|
||||
averageResponseSize = int64(totalResponseSize) / int64(totalCount)
|
||||
}
|
||||
|
||||
for key, count := range mw.MetricsCounts {
|
||||
totalMetric := mw.MetricsTimers[key].Sub(time.Time{})
|
||||
avgNs := int64(totalMetric) / int64(count)
|
||||
metricsCounts[key] = time.Duration(avgNs).Seconds()
|
||||
totalMetricsCounts[key] = count
|
||||
}
|
||||
|
||||
mw.mu.RUnlock()
|
||||
|
||||
r := &Data{
|
||||
Pid: mw.Pid,
|
||||
UpTime: uptime.String(),
|
||||
UpTimeSec: uptime.Seconds(),
|
||||
Time: now.String(),
|
||||
TimeUnix: now.Unix(),
|
||||
StatusCodeCount: responseCounts,
|
||||
TotalStatusCodeCount: totalResponseCounts,
|
||||
Count: count,
|
||||
TotalCount: totalCount,
|
||||
TotalResponseTime: totalResponseTime.String(),
|
||||
TotalResponseSize: totalResponseSize,
|
||||
TotalResponseTimeSec: totalResponseTime.Seconds(),
|
||||
TotalMetricsCounts: totalMetricsCounts,
|
||||
AverageResponseSize: averageResponseSize,
|
||||
AverageResponseTime: averageResponseTime.String(),
|
||||
AverageResponseTimeSec: averageResponseTime.Seconds(),
|
||||
AverageMetricsTimers: metricsCounts,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
Loading…
Reference in a new issue