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"
|
pruneopts = "NUT"
|
||||||
revision = "c4434f09ec131ecf30f986d5dcb1636508bfa49a"
|
revision = "c4434f09ec131ecf30f986d5dcb1636508bfa49a"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:84b9a5318d8ce3b8a9b1509bf15734f4f9dcd4decf9d9e9c7346a16c7b64d49e"
|
|
||||||
name = "github.com/thoas/stats"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "4975baf6a358ed3ddaa42133996e1959f96c9300"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:99ce99ce6d6d0cbc5f822cda92095906e01d5546d60999ac839ab008938e4e17"
|
digest = "1:99ce99ce6d6d0cbc5f822cda92095906e01d5546d60999ac839ab008938e4e17"
|
||||||
|
@ -2304,7 +2297,6 @@
|
||||||
"github.com/stretchr/testify/mock",
|
"github.com/stretchr/testify/mock",
|
||||||
"github.com/stretchr/testify/require",
|
"github.com/stretchr/testify/require",
|
||||||
"github.com/stvp/go-udp-testing",
|
"github.com/stvp/go-udp-testing",
|
||||||
"github.com/thoas/stats",
|
|
||||||
"github.com/uber/jaeger-client-go",
|
"github.com/uber/jaeger-client-go",
|
||||||
"github.com/uber/jaeger-client-go/config",
|
"github.com/uber/jaeger-client-go/config",
|
||||||
"github.com/uber/jaeger-client-go/zipkin",
|
"github.com/uber/jaeger-client-go/zipkin",
|
||||||
|
|
|
@ -10,7 +10,7 @@ Let's see how.
|
||||||
|
|
||||||
### General
|
### 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`
|
### 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) {
|
func waitForTraefik(c *check.C, containerName string) {
|
||||||
// Wait for Traefik to turn ready.
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// Expected traefik works
|
// 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)
|
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)
|
_, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK)
|
||||||
c.Assert(err, checker.IsNil)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
var services []api.ServiceRepresentation
|
var rtconf api.RunTimeRepresentation
|
||||||
err = json.NewDecoder(resp.Body).Decode(&services)
|
err = json.NewDecoder(resp.Body).Decode(&rtconf)
|
||||||
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)
|
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// check that we have only one router
|
// 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(json.Unmarshal(body, &version), checker.IsNil)
|
||||||
c.Assert(version["Version"], checker.Equals, "swarm/1.0.0")
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
s.stopAndRemoveContainerByName(c, "powpow")
|
s.stopAndRemoveContainerByName(c, "powpow")
|
||||||
|
@ -323,11 +323,11 @@ func (s *DockerSuite) TestRestartDockerContainers(c *check.C) {
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
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)
|
c.Assert(err, checker.NotNil)
|
||||||
|
|
||||||
s.startContainerWithNameAndLabels(c, "powpow", "swarm:1.0.0", labels, "manage", "token://blabla")
|
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)
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
|
@ -205,7 +205,7 @@ func (s *GRPCSuite) TestGRPCh2c(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
|
@ -247,7 +247,7 @@ func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
|
@ -289,7 +289,7 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
|
@ -336,7 +336,7 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, check.IsNil)
|
||||||
var client helloworld.Greeter_StreamExampleClient
|
var client helloworld.Greeter_StreamExampleClient
|
||||||
client, closer, err := callStreamExampleClientGRPC()
|
client, closer, err := callStreamExampleClientGRPC()
|
||||||
|
@ -364,7 +364,6 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) {
|
||||||
|
|
||||||
func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||||
stopStreamExample := make(chan bool)
|
stopStreamExample := make(chan bool)
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", ":0")
|
lis, err := net.Listen("tcp", ":0")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
_, port, err := net.SplitHostPort(lis.Addr().String())
|
_, port, err := net.SplitHostPort(lis.Addr().String())
|
||||||
|
@ -378,7 +377,7 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
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
|
CertContent string
|
||||||
KeyContent string
|
KeyContent string
|
||||||
GRPCServerPort string
|
GRPCServerPort string
|
||||||
|
@ -396,7 +395,7 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
var client helloworld.Greeter_StreamExampleClient
|
var client helloworld.Greeter_StreamExampleClient
|
||||||
|
@ -454,7 +453,7 @@ func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
var response string
|
var response string
|
||||||
|
|
|
@ -41,7 +41,7 @@ func (s *HealthCheckSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// Wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Check entrypoint http1
|
// Check entrypoint http1
|
||||||
|
@ -194,7 +194,7 @@ func (s *HealthCheckSuite) TestPortOverload(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -66,7 +66,7 @@ func (s *HTTPSSuite) TestWithSNIConfigRoute(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||||
|
@ -122,7 +122,7 @@ func (s *HTTPSSuite) TestWithSNIStrictNotMatchedRequest(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -146,7 +146,7 @@ func (s *HTTPSSuite) TestWithDefaultCertificate(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -180,7 +180,7 @@ func (s *HTTPSSuite) TestWithDefaultCertificateNoSNI(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -214,7 +214,7 @@ func (s *HTTPSSuite) TestWithOverlappingStaticCertificate(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -249,7 +249,7 @@ func (s *HTTPSSuite) TestWithOverlappingDynamicCertificate(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -282,7 +282,7 @@ func (s *HTTPSSuite) TestWithClientCertificateAuthentication(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -338,7 +338,7 @@ func (s *HTTPSSuite) TestWithClientCertificateAuthenticationMultipleCAs(c *check
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -399,7 +399,7 @@ func (s *HTTPSSuite) TestWithClientCertificateAuthenticationMultipleCAsMultipleF
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
@ -464,7 +464,7 @@ func (s *HTTPSSuite) TestWithRootCAsContentForHTTPSOnBackend(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8081/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8081/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||||
|
@ -613,7 +613,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithChange(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||||
|
@ -676,7 +676,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion(c
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
backend2 := startTestServer("9020", http.StatusResetContent)
|
backend2 := startTestServer("9020", http.StatusResetContent)
|
||||||
|
@ -740,7 +740,7 @@ func (s *HTTPSSuite) TestEntrypointHttpsRedirectAndPathModification(c *check.C)
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
|
@ -841,7 +841,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicCaseInsensitive(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for Traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
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`)"))
|
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)
|
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)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ server1:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.rt-server1.entryPoints=web
|
- traefik.http.routers.rt-server1.entryPoints=web
|
||||||
- traefik.http.routers.rt-server1.rule=Host("frontend1.docker.local")
|
- 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
|
- traefik.http.services.service1.loadbalancer.server.port=80
|
||||||
server2:
|
server2:
|
||||||
image: containous/whoami
|
image: containous/whoami
|
||||||
|
|
|
@ -65,7 +65,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/", 1000*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// This simulates a DialTimeout when connecting to the backend server.
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// This simulates a DialTimeout when connecting to the backend server.
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
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))
|
err = try.GetRequest("http://127.0.0.1:8000/test", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||||
c.Assert(err, checker.IsNil)
|
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)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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))
|
err = try.GetRequest("http://127.0.0.1:8080/api", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8001/ping", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.BodyContains(ipWhoami01))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@ -415,7 +415,7 @@ func (s *SimpleSuite) TestXForwardedHeaders(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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"))
|
try.BodyContains("override.remoteaddr.whitelist.docker.local"))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ func (s *SimpleSuite) TestMultiprovider(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
config := config.HTTPConfiguration{
|
config := config.HTTPConfiguration{
|
||||||
|
@ -474,7 +474,7 @@ func (s *SimpleSuite) TestMultiprovider(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("CustomValue"))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
//Traefik passes through, termination handled by whoami-a
|
//Traefik passes through, termination handled by whoami-a
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (s *TimeoutSuite) TestForwardingTimeouts(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// This simulates a DialTimeout when connecting to the backend server.
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443", nil)
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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))
|
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
err = try.GetRequest("http://127.0.0.1:8000/ratelimit", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
|
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)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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))
|
err = try.GetRequest("http://127.0.0.1:8000/retry", 500*time.Millisecond, try.StatusCodeIs(http.StatusBadGateway))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
@ -129,6 +137,10 @@ func (s *TracingSuite) TestZipkinAuth(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
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))
|
err = try.GetRequest("http://127.0.0.1:8000/auth", 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (s *WebsocketSuite) TestBase(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:800")
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:80")
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Add client self-signed cert
|
// Add client self-signed cert
|
||||||
|
@ -339,7 +339,7 @@ func (s *WebsocketSuite) TestBasicAuth(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
config, err := websocket.NewConfig("ws://127.0.0.1:8000/ws", "ws://127.0.0.1:8000")
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
_, resp, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws", nil)
|
_, 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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
conn, _, err := gorillawebsocket.DefaultDialer.Dial("ws://127.0.0.1:8000/ws/http%3A%2F%2Ftest", nil)
|
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()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
// Add client self-signed cert
|
// Add client self-signed cert
|
||||||
|
@ -543,7 +543,7 @@ func (s *WebsocketSuite) TestHeaderAreForwared(c *check.C) {
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
// wait for traefik
|
// 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)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
headers := http.Header{}
|
headers := http.Header{}
|
||||||
|
|
|
@ -6,79 +6,70 @@ import (
|
||||||
|
|
||||||
"github.com/containous/mux"
|
"github.com/containous/mux"
|
||||||
"github.com/containous/traefik/pkg/config"
|
"github.com/containous/traefik/pkg/config"
|
||||||
|
"github.com/containous/traefik/pkg/config/static"
|
||||||
"github.com/containous/traefik/pkg/log"
|
"github.com/containous/traefik/pkg/log"
|
||||||
"github.com/containous/traefik/pkg/safe"
|
|
||||||
"github.com/containous/traefik/pkg/types"
|
"github.com/containous/traefik/pkg/types"
|
||||||
"github.com/containous/traefik/pkg/version"
|
"github.com/containous/traefik/pkg/version"
|
||||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||||
thoasstats "github.com/thoas/stats"
|
|
||||||
"github.com/unrolled/render"
|
"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"})
|
var templateRenderer jsonRenderer = render.New(render.Options{Directory: "nowhere"})
|
||||||
|
|
||||||
type jsonRenderer interface {
|
type jsonRenderer interface {
|
||||||
JSON(w io.Writer, status int, v interface{}) error
|
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
|
// Append add api routes on a router
|
||||||
func (h Handler) Append(router *mux.Router) {
|
func (h Handler) Append(router *mux.Router) {
|
||||||
if h.Debug {
|
if h.debug {
|
||||||
DebugHandler{}.Append(router)
|
DebugHandler{}.Append(router)
|
||||||
}
|
}
|
||||||
|
|
||||||
router.Methods(http.MethodGet).Path("/api/rawdata").HandlerFunc(h.getRawData)
|
router.Methods(http.MethodGet).Path("/api/rawdata").HandlerFunc(h.getRuntimeConfiguration)
|
||||||
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)
|
|
||||||
|
|
||||||
// FIXME stats
|
// FIXME stats
|
||||||
// health route
|
// health route
|
||||||
|
@ -86,268 +77,29 @@ func (h Handler) Append(router *mux.Router) {
|
||||||
|
|
||||||
version.Handler{}.Append(router)
|
version.Handler{}.Append(router)
|
||||||
|
|
||||||
if h.Dashboard {
|
if h.dashboard {
|
||||||
DashboardHandler{Assets: h.DashboardAssets}.Append(router)
|
DashboardHandler{Assets: h.dashboardAssets}.Append(router)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Handler) getRawData(rw http.ResponseWriter, request *http.Request) {
|
func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.Request) {
|
||||||
if h.CurrentConfigurations != nil {
|
siRepr := make(map[string]*serviceInfoRepresentation, len(h.runtimeConfiguration.Services))
|
||||||
currentConfigurations, ok := h.CurrentConfigurations.Get().(config.Configurations)
|
for k, v := range h.runtimeConfiguration.Services {
|
||||||
if !ok {
|
siRepr[k] = &serviceInfoRepresentation{
|
||||||
rw.WriteHeader(http.StatusOK)
|
ServiceInfo: v,
|
||||||
return
|
ServerStatus: v.GetAllStatus(),
|
||||||
}
|
|
||||||
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) getProvidersHandler(rw http.ResponseWriter, request *http.Request) {
|
rtRepr := RunTimeRepresentation{
|
||||||
// FIXME handle currentConfiguration
|
Routers: h.runtimeConfiguration.Routers,
|
||||||
if h.CurrentConfigurations != nil {
|
Middlewares: h.runtimeConfiguration.Middlewares,
|
||||||
currentConfigurations, ok := h.CurrentConfigurations.Get().(config.Configurations)
|
Services: siRepr,
|
||||||
if !ok {
|
TCPRouters: h.runtimeConfiguration.TCPRouters,
|
||||||
rw.WriteHeader(http.StatusOK)
|
TCPServices: h.runtimeConfiguration.TCPServices,
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var providers []ResourceIdentifier
|
err := templateRenderer.JSON(rw, http.StatusOK, rtRepr)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.FromContext(request.Context()).Error(err)
|
log.FromContext(request.Context()).Error(err)
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
@ -8,189 +10,123 @@ import (
|
||||||
|
|
||||||
"github.com/containous/mux"
|
"github.com/containous/mux"
|
||||||
"github.com/containous/traefik/pkg/config"
|
"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/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata")
|
||||||
|
|
||||||
func TestHandler_Configuration(t *testing.T) {
|
func TestHandler_Configuration(t *testing.T) {
|
||||||
type expected struct {
|
type expected struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
body string
|
json string
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
path string
|
path string
|
||||||
configuration config.Configurations
|
conf config.RuntimeConfiguration
|
||||||
expected expected
|
expected expected
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "Get all the providers",
|
desc: "Get rawdata",
|
||||||
path: "/api/providers",
|
path: "/api/rawdata",
|
||||||
configuration: config.Configurations{
|
conf: config.RuntimeConfiguration{
|
||||||
"foo": {
|
Services: map[string]*config.ServiceInfo{
|
||||||
HTTP: &config.HTTPConfiguration{
|
"myprovider.foo-service": {
|
||||||
Routers: map[string]*config.Router{
|
Service: &config.Service{
|
||||||
"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": {
|
|
||||||
LoadBalancer: &config.LoadBalancerService{
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "http://127.0.0.1",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
Method: "wrr",
|
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",
|
Address: "127.0.0.1",
|
||||||
path: "/api/providers/foo",
|
Weight: 1,
|
||||||
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"}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
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",
|
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"}]`},
|
"myprovider.tcptest": {
|
||||||
},
|
TCPRouter: &config.TCPRouter{
|
||||||
{
|
EntryPoints: []string{"web"},
|
||||||
desc: "Get a service",
|
Service: "myprovider.tcpfoo-service",
|
||||||
path: "/api/providers/foo/services/foo",
|
Rule: "HostSNI(`foo.bar.other`)",
|
||||||
configuration: config.Configurations{
|
|
||||||
"foo": {
|
|
||||||
HTTP: &config.HTTPConfiguration{
|
|
||||||
Services: map[string]*config.Service{
|
|
||||||
"foo": {
|
|
||||||
LoadBalancer: &config.LoadBalancerService{
|
|
||||||
Method: "wrr",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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 {
|
for _, test := range testCases {
|
||||||
|
@ -198,15 +134,11 @@ func TestHandler_Configuration(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
currentConfiguration := &safe.Safe{}
|
rtConf := &test.conf
|
||||||
currentConfiguration.Set(test.configuration)
|
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||||
|
|
||||||
handler := Handler{
|
|
||||||
CurrentConfigurations: currentConfiguration,
|
|
||||||
}
|
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
handler.Append(router)
|
handler.Append(router)
|
||||||
|
rtConf.PopulateUsedBy()
|
||||||
|
|
||||||
server := httptest.NewServer(router)
|
server := httptest.NewServer(router)
|
||||||
|
|
||||||
|
@ -215,12 +147,30 @@ func TestHandler_Configuration(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, test.expected.statusCode, resp.StatusCode)
|
assert.Equal(t, test.expected.statusCode, resp.StatusCode)
|
||||||
|
|
||||||
content, err := ioutil.ReadAll(resp.Body)
|
contents, err := ioutil.ReadAll(resp.Body)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
require.NoError(t, err)
|
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"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/pkg/config"
|
||||||
"github.com/containous/traefik/pkg/log"
|
"github.com/containous/traefik/pkg/log"
|
||||||
"github.com/containous/traefik/pkg/safe"
|
"github.com/containous/traefik/pkg/safe"
|
||||||
"github.com/go-kit/kit/metrics"
|
"github.com/go-kit/kit/metrics"
|
||||||
"github.com/vulcand/oxy/roundrobin"
|
"github.com/vulcand/oxy/roundrobin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
serverUp = "UP"
|
||||||
|
serverDown = "DOWN"
|
||||||
|
)
|
||||||
|
|
||||||
var singleton *HealthCheck
|
var singleton *HealthCheck
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
|
@ -221,3 +227,38 @@ func checkHealth(serverURL *url.URL, backend *BackendConfig) error {
|
||||||
|
|
||||||
return nil
|
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"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/pkg/config"
|
||||||
"github.com/containous/traefik/pkg/testhelpers"
|
"github.com/containous/traefik/pkg/testhelpers"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -367,6 +368,8 @@ type testLoadBalancer struct {
|
||||||
numRemovedServers int
|
numRemovedServers int
|
||||||
numUpsertedServers int
|
numUpsertedServers int
|
||||||
servers []*url.URL
|
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) {
|
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()
|
defer lb.Unlock()
|
||||||
lb.numUpsertedServers++
|
lb.numUpsertedServers++
|
||||||
lb.servers = append(lb.servers, u)
|
lb.servers = append(lb.servers, u)
|
||||||
|
lb.options = append(lb.options, options...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,14 +397,23 @@ func (lb *testLoadBalancer) Servers() []*url.URL {
|
||||||
return lb.servers
|
return lb.servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lb *testLoadBalancer) Options() []roundrobin.ServerOption {
|
||||||
|
return lb.options
|
||||||
|
}
|
||||||
|
|
||||||
func (lb *testLoadBalancer) removeServer(u *url.URL) {
|
func (lb *testLoadBalancer) removeServer(u *url.URL) {
|
||||||
var i int
|
var i int
|
||||||
var serverURL *url.URL
|
var serverURL *url.URL
|
||||||
|
found := false
|
||||||
for i, serverURL = range lb.servers {
|
for i, serverURL = range lb.servers {
|
||||||
if *serverURL == *u {
|
if *serverURL == *u {
|
||||||
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
lb.servers = append(lb.servers[:i], lb.servers[i+1:]...)
|
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()
|
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.
|
// NewBuilder creates a builder.
|
||||||
func NewBuilder(configs map[string]*config.Middleware) *Builder {
|
func NewBuilder(configs map[string]*config.MiddlewareInfo) *Builder {
|
||||||
return &Builder{configs: configs}
|
return &Builder{configs: configs}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builder holds builder configuration.
|
// Builder holds builder configuration.
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
configs map[string]*config.Middleware
|
configs map[string]*config.MiddlewareInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build Builds the response modifier.
|
// Build Builds the response modifier.
|
||||||
|
|
|
@ -166,7 +166,12 @@ func TestBuilderBuild(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
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)
|
rm := builder.Build(context.Background(), test.middlewares)
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ const (
|
||||||
|
|
||||||
// Builder the middleware builder
|
// Builder the middleware builder
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
configs map[string]*config.Middleware
|
configs map[string]*config.MiddlewareInfo
|
||||||
serviceBuilder serviceBuilder
|
serviceBuilder serviceBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ type serviceBuilder interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBuilder creates a new Builder
|
// 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}
|
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) {
|
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||||
constructorContext := internal.AddProviderInContext(ctx, middlewareName)
|
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)
|
return nil, fmt.Errorf("middleware %q does not exist", middlewareName)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if constructorContext, err = checkRecursion(constructorContext, middlewareName); err != nil {
|
if constructorContext, err = checkRecursion(constructorContext, middlewareName); err != nil {
|
||||||
|
b.configs[middlewareName].Err = err
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor, err := b.buildConstructor(constructorContext, middlewareName, *b.configs[middlewareName])
|
constructor, err := b.buildConstructor(constructorContext, middlewareName)
|
||||||
if err != nil {
|
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
|
return &chain
|
||||||
|
@ -90,7 +99,9 @@ func checkRecursion(ctx context.Context, middlewareName string) (context.Context
|
||||||
return context.WithValue(ctx, middlewareStackKey, append(currentStack, middlewareName)), nil
|
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
|
var middleware alice.Constructor
|
||||||
badConf := errors.New("cannot create middleware: multi-types middleware not supported, consider declaring two different pieces of middleware instead")
|
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) {
|
func TestBuilder_BuildChainNilConfig(t *testing.T) {
|
||||||
testConfig := map[string]*config.Middleware{
|
testConfig := map[string]*config.MiddlewareInfo{
|
||||||
"empty": {},
|
"empty": {},
|
||||||
}
|
}
|
||||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||||
|
@ -25,7 +25,7 @@ func TestBuilder_BuildChainNilConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilder_BuildChainNonExistentChain(t *testing.T) {
|
func TestBuilder_BuildChainNonExistentChain(t *testing.T) {
|
||||||
testConfig := map[string]*config.Middleware{
|
testConfig := map[string]*config.MiddlewareInfo{
|
||||||
"foobar": {},
|
"foobar": {},
|
||||||
}
|
}
|
||||||
middlewaresBuilder := NewBuilder(testConfig, nil)
|
middlewaresBuilder := NewBuilder(testConfig, nil)
|
||||||
|
@ -264,7 +264,12 @@ func TestBuilder_BuildChainWithContext(t *testing.T) {
|
||||||
ctx = internal.AddProviderInContext(ctx, test.contextProvider+".foobar")
|
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)
|
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 {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -344,7 +354,8 @@ func TestBuilder_buildConstructor(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
middleware, err2 := constructor(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
|
middleware, err2 := constructor(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))
|
||||||
|
|
|
@ -6,10 +6,10 @@ import (
|
||||||
"github.com/containous/alice"
|
"github.com/containous/alice"
|
||||||
"github.com/containous/mux"
|
"github.com/containous/mux"
|
||||||
"github.com/containous/traefik/pkg/api"
|
"github.com/containous/traefik/pkg/api"
|
||||||
|
"github.com/containous/traefik/pkg/config"
|
||||||
"github.com/containous/traefik/pkg/config/static"
|
"github.com/containous/traefik/pkg/config/static"
|
||||||
"github.com/containous/traefik/pkg/log"
|
"github.com/containous/traefik/pkg/log"
|
||||||
"github.com/containous/traefik/pkg/metrics"
|
"github.com/containous/traefik/pkg/metrics"
|
||||||
"github.com/containous/traefik/pkg/safe"
|
|
||||||
"github.com/containous/traefik/pkg/types"
|
"github.com/containous/traefik/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ type chainBuilder interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRouteAppenderAggregator Creates a new RouteAppenderAggregator
|
// 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{}
|
aggregator := &RouteAppenderAggregator{}
|
||||||
|
|
||||||
if conf.Providers != nil && conf.Providers.Rest != nil {
|
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 {
|
if conf.API != nil && conf.API.EntryPoint == entryPointName {
|
||||||
chain := chainBuilder.BuildChain(ctx, conf.API.Middlewares)
|
chain := chainBuilder.BuildChain(ctx, conf.API.Middlewares)
|
||||||
aggregator.AddAppender(&WithMiddleware{
|
aggregator.AddAppender(&WithMiddleware{
|
||||||
appender: api.Handler{
|
appender: api.New(conf, runtimeConfiguration),
|
||||||
EntryPoint: conf.API.EntryPoint,
|
|
||||||
Dashboard: conf.API.Dashboard,
|
|
||||||
Statistics: conf.API.Statistics,
|
|
||||||
DashboardAssets: conf.API.DashboardAssets,
|
|
||||||
CurrentConfigurations: currentConfiguration,
|
|
||||||
Debug: conf.Global.Debug,
|
|
||||||
},
|
|
||||||
routerMiddlewares: chain,
|
routerMiddlewares: chain,
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.Ping != nil && conf.Ping.EntryPoint == entryPointName {
|
if conf.Ping != nil && conf.Ping.EntryPoint == entryPointName {
|
||||||
|
|
|
@ -62,7 +62,7 @@ func TestNewRouteAppenderAggregator(t *testing.T) {
|
||||||
"/wrong": http.StatusBadGateway,
|
"/wrong": http.StatusBadGateway,
|
||||||
"/ping": http.StatusOK,
|
"/ping": http.StatusOK,
|
||||||
// "/.well-known/acme-challenge/token": http.StatusNotFound, // FIXME
|
// "/.well-known/acme-challenge/token": http.StatusNotFound, // FIXME
|
||||||
"/api/providers": http.StatusUnauthorized,
|
"/api/rawdata": http.StatusUnauthorized,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,9 +3,9 @@ package router
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/pkg/config"
|
||||||
"github.com/containous/traefik/pkg/config/static"
|
"github.com/containous/traefik/pkg/config/static"
|
||||||
"github.com/containous/traefik/pkg/provider/acme"
|
"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/server/middleware"
|
||||||
"github.com/containous/traefik/pkg/types"
|
"github.com/containous/traefik/pkg/types"
|
||||||
)
|
)
|
||||||
|
@ -27,8 +27,8 @@ type RouteAppenderFactory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAppender Creates a new RouteAppender
|
// NewAppender Creates a new RouteAppender
|
||||||
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, currentConfiguration *safe.Safe) types.RouteAppender {
|
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, runtimeConfiguration *config.RuntimeConfiguration) types.RouteAppender {
|
||||||
aggregator := NewRouteAppenderAggregator(ctx, middlewaresBuilder, r.staticConfiguration, r.entryPointName, currentConfiguration)
|
aggregator := NewRouteAppenderAggregator(ctx, middlewaresBuilder, r.staticConfiguration, r.entryPointName, runtimeConfiguration)
|
||||||
|
|
||||||
if r.acmeProvider != nil && r.acmeProvider.HTTPChallenge != nil && r.acmeProvider.HTTPChallenge.EntryPoint == r.entryPointName {
|
if r.acmeProvider != nil && r.acmeProvider.HTTPChallenge != nil && r.acmeProvider.HTTPChallenge.EntryPoint == r.entryPointName {
|
||||||
aggregator.AddAppender(r.acmeProvider)
|
aggregator.AddAppender(r.acmeProvider)
|
||||||
|
|
|
@ -23,7 +23,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewManager Creates a new Manager
|
// 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,
|
serviceManager *service.Manager, middlewaresBuilder *middleware.Builder, modifierBuilder *responsemodifiers.Builder,
|
||||||
) *Manager {
|
) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
|
@ -38,7 +38,7 @@ func NewManager(routers map[string]*config.Router,
|
||||||
// Manager A route/router manager
|
// Manager A route/router manager
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
routerHandlers map[string]http.Handler
|
routerHandlers map[string]http.Handler
|
||||||
configs map[string]*config.Router
|
configs map[string]*config.RouterInfo
|
||||||
serviceManager *service.Manager
|
serviceManager *service.Manager
|
||||||
middlewaresBuilder *middleware.Builder
|
middlewaresBuilder *middleware.Builder
|
||||||
modifierBuilder *responsemodifiers.Builder
|
modifierBuilder *responsemodifiers.Builder
|
||||||
|
@ -75,8 +75,17 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
|
||||||
return entryPointHandlers
|
return entryPointHandlers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls bool) map[string]map[string]*config.Router {
|
func contains(entryPoints []string, entryPointName string) bool {
|
||||||
entryPointsRouters := make(map[string]map[string]*config.Router)
|
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 {
|
for rtName, rt := range m.configs {
|
||||||
if (tls && rt.TLS == nil) || (!tls && rt.TLS != nil) {
|
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 {
|
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||||
entryPointsRouters[entryPointName] = make(map[string]*config.Router)
|
entryPointsRouters[entryPointName] = make(map[string]*config.RouterInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
entryPointsRouters[entryPointName][rtName] = rt
|
entryPointsRouters[entryPointName][rtName] = rt
|
||||||
|
@ -105,7 +114,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string, tls
|
||||||
return entryPointsRouters
|
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()
|
router, err := rules.NewRouter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -117,12 +126,14 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||||
|
|
||||||
handler, err := m.buildRouterHandler(ctxRouter, routerName)
|
handler, err := m.buildRouterHandler(ctxRouter, routerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
routerConfig.Err = err.Error()
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
|
err = router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
routerConfig.Err = err.Error()
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -166,7 +177,7 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string) (ht
|
||||||
return m.routerHandlers[routerName], nil
|
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))
|
qualifiedNames := make([]string, len(router.Middlewares))
|
||||||
for i, name := range router.Middlewares {
|
for i, name := range router.Middlewares {
|
||||||
qualifiedNames[i] = internal.GetQualifiedName(ctx, name)
|
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)
|
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.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
serviceManager := service.NewManager(test.serviceConfig, http.DefaultTransport)
|
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||||
middlewaresBuilder := middleware.NewBuilder(test.middlewaresConfig, serviceManager)
|
HTTP: &config.HTTPConfiguration{
|
||||||
responseModifierFactory := responsemodifiers.NewBuilder(test.middlewaresConfig)
|
Services: test.serviceConfig,
|
||||||
|
Routers: test.routersConfig,
|
||||||
routerManager := NewManager(test.routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
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)
|
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||||
|
|
||||||
|
@ -413,11 +419,17 @@ func TestAccessLog(t *testing.T) {
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
|
||||||
serviceManager := service.NewManager(test.serviceConfig, http.DefaultTransport)
|
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||||
middlewaresBuilder := middleware.NewBuilder(test.middlewaresConfig, serviceManager)
|
HTTP: &config.HTTPConfiguration{
|
||||||
responseModifierFactory := responsemodifiers.NewBuilder(test.middlewaresConfig)
|
Services: test.serviceConfig,
|
||||||
|
Routers: test.routersConfig,
|
||||||
routerManager := NewManager(test.routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
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)
|
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 {
|
type staticTransport struct {
|
||||||
res *http.Response
|
res *http.Response
|
||||||
}
|
}
|
||||||
|
@ -480,11 +796,17 @@ func BenchmarkRouterServe(b *testing.B) {
|
||||||
}
|
}
|
||||||
entryPoints := []string{"web"}
|
entryPoints := []string{"web"}
|
||||||
|
|
||||||
serviceManager := service.NewManager(serviceConfig, &staticTransport{res})
|
rtConf := config.NewRuntimeConfig(config.Configuration{
|
||||||
middlewaresBuilder := middleware.NewBuilder(map[string]*config.Middleware{}, serviceManager)
|
HTTP: &config.HTTPConfiguration{
|
||||||
responseModifierFactory := responsemodifiers.NewBuilder(map[string]*config.Middleware{})
|
Services: serviceConfig,
|
||||||
|
Routers: routersConfig,
|
||||||
routerManager := NewManager(routersConfig, serviceManager, middlewaresBuilder, responseModifierFactory)
|
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)
|
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||||
|
|
||||||
|
@ -498,6 +820,7 @@ func BenchmarkRouterServe(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkService(b *testing.B) {
|
func BenchmarkService(b *testing.B) {
|
||||||
res := &http.Response{
|
res := &http.Response{
|
||||||
StatusCode: 200,
|
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()
|
w := httptest.NewRecorder()
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package tcp
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config"
|
"github.com/containous/traefik/pkg/config"
|
||||||
|
@ -14,14 +15,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewManager Creates a new Manager
|
// NewManager Creates a new Manager
|
||||||
func NewManager(routers map[string]*config.TCPRouter,
|
func NewManager(conf *config.RuntimeConfiguration,
|
||||||
serviceManager *tcpservice.Manager,
|
serviceManager *tcpservice.Manager,
|
||||||
httpHandlers map[string]http.Handler,
|
httpHandlers map[string]http.Handler,
|
||||||
httpsHandlers map[string]http.Handler,
|
httpsHandlers map[string]http.Handler,
|
||||||
tlsConfig *tls.Config,
|
tlsConfig *tls.Config,
|
||||||
) *Manager {
|
) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
configs: routers,
|
configs: conf.TCPRouters,
|
||||||
serviceManager: serviceManager,
|
serviceManager: serviceManager,
|
||||||
httpHandlers: httpHandlers,
|
httpHandlers: httpHandlers,
|
||||||
httpsHandlers: httpsHandlers,
|
httpsHandlers: httpsHandlers,
|
||||||
|
@ -31,7 +32,7 @@ func NewManager(routers map[string]*config.TCPRouter,
|
||||||
|
|
||||||
// Manager is a route/router manager
|
// Manager is a route/router manager
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
configs map[string]*config.TCPRouter
|
configs map[string]*config.TCPRouterInfo
|
||||||
serviceManager *tcpservice.Manager
|
serviceManager *tcpservice.Manager
|
||||||
httpHandlers map[string]http.Handler
|
httpHandlers map[string]http.Handler
|
||||||
httpsHandlers 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
|
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 := &tcp.Router{}
|
||||||
router.HTTPHandler(handlerHTTP)
|
router.HTTPHandler(handlerHTTP)
|
||||||
router.HTTPSHandler(handlerHTTPS, m.tlsConfig)
|
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)
|
handler, err := m.serviceManager.BuildTCP(ctxRouter, routerConfig.Service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
routerConfig.Err = err.Error()
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
domains, err := rules.ParseHostSNI(routerConfig.Rule)
|
domains, err := rules.ParseHostSNI(routerConfig.Rule)
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
logger.Debugf("Add route %s on TCP", domain)
|
logger.Debugf("Adding route %s on TCP", domain)
|
||||||
switch {
|
switch {
|
||||||
case routerConfig.TLS != nil:
|
case routerConfig.TLS != nil:
|
||||||
if routerConfig.TLS.Passthrough {
|
if routerConfig.TLS.Passthrough {
|
||||||
|
@ -101,8 +105,17 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||||
return router, nil
|
return router, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map[string]map[string]*config.TCPRouter {
|
func contains(entryPoints []string, entryPointName string) bool {
|
||||||
entryPointsRouters := make(map[string]map[string]*config.TCPRouter)
|
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 {
|
for rtName, rt := range m.configs {
|
||||||
eps := rt.EntryPoints
|
eps := rt.EntryPoints
|
||||||
|
@ -118,7 +131,7 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
if _, ok := entryPointsRouters[entryPointName]; !ok {
|
||||||
entryPointsRouters[entryPointName] = make(map[string]*config.TCPRouter)
|
entryPointsRouters[entryPointName] = make(map[string]*config.TCPRouterInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
entryPointsRouters[entryPointName][rtName] = rt
|
entryPointsRouters[entryPointName][rtName] = rt
|
||||||
|
@ -127,12 +140,3 @@ func (m *Manager) filteredRouters(ctx context.Context, entryPoints []string) map
|
||||||
|
|
||||||
return entryPointsRouters
|
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
|
// RouteAppenderFactory the route appender factory interface
|
||||||
type RouteAppenderFactory 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 {
|
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)
|
s.tlsManager.UpdateConfigs(conf.TLSStores, conf.TLSOptions, conf.TLS)
|
||||||
|
|
||||||
handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, *conf.HTTP, entryPoints)
|
rtConf := config.NewRuntimeConfig(conf)
|
||||||
|
handlersNonTLS, handlersTLS := s.createHTTPHandlers(ctx, rtConf, entryPoints)
|
||||||
routersTCP := s.createTCPRouters(ctx, conf.TCP, entryPoints, handlersNonTLS, handlersTLS, s.tlsManager.Get("default", "default"))
|
routersTCP := s.createTCPRouters(ctx, rtConf, entryPoints, handlersNonTLS, handlersTLS, s.tlsManager.Get("default", "default"))
|
||||||
|
rtConf.PopulateUsedBy()
|
||||||
|
|
||||||
return routersTCP
|
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 {
|
if configuration == nil {
|
||||||
return make(map[string]*tcpCore.Router)
|
return make(map[string]*tcpCore.Router)
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceManager := tcp.NewManager(configuration.Services)
|
serviceManager := tcp.NewManager(configuration)
|
||||||
routerManager := routertcp.NewManager(configuration.Routers, serviceManager, handlers, handlersTLS, tlsConfig)
|
routerManager := routertcp.NewManager(configuration, serviceManager, handlers, handlersTLS, tlsConfig)
|
||||||
|
|
||||||
return routerManager.BuildHandlers(ctx, entryPoints)
|
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)
|
serviceManager := service.NewManager(configuration.Services, s.defaultRoundTripper)
|
||||||
middlewaresBuilder := middleware.NewBuilder(configuration.Middlewares, serviceManager)
|
middlewaresBuilder := middleware.NewBuilder(configuration.Middlewares, serviceManager)
|
||||||
responseModifierFactory := responsemodifiers.NewBuilder(configuration.Middlewares)
|
responseModifierFactory := responsemodifiers.NewBuilder(configuration.Middlewares)
|
||||||
|
|
||||||
routerManager := router.NewManager(configuration.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
routerManager := router.NewManager(configuration.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
|
||||||
|
|
||||||
handlersNonTLS := routerManager.BuildHandlers(ctx, entryPoints, false)
|
handlersNonTLS := routerManager.BuildHandlers(ctx, entryPoints, false)
|
||||||
handlersTLS := routerManager.BuildHandlers(ctx, entryPoints, true)
|
handlersTLS := routerManager.BuildHandlers(ctx, entryPoints, true)
|
||||||
|
|
||||||
routerHandlers := make(map[string]http.Handler)
|
routerHandlers := make(map[string]http.Handler)
|
||||||
|
|
||||||
for _, entryPointName := range entryPoints {
|
for _, entryPointName := range entryPoints {
|
||||||
internalMuxRouter := mux.NewRouter().
|
internalMuxRouter := mux.NewRouter().SkipClean(true)
|
||||||
SkipClean(true)
|
|
||||||
|
|
||||||
ctx = log.With(ctx, log.Str(log.EntryPointName, entryPointName))
|
ctx = log.With(ctx, log.Str(log.EntryPointName, entryPointName))
|
||||||
|
|
||||||
factory := s.entryPointsTCP[entryPointName].RouteAppenderFactory
|
factory := s.entryPointsTCP[entryPointName].RouteAppenderFactory
|
||||||
if factory != nil {
|
if factory != nil {
|
||||||
// FIXME remove currentConfigurations
|
// FIXME remove currentConfigurations
|
||||||
appender := factory.NewAppender(ctx, middlewaresBuilder, &s.currentConfigurations)
|
appender := factory.NewAppender(ctx, middlewaresBuilder, configuration)
|
||||||
appender.Append(internalMuxRouter)
|
appender.Append(internalMuxRouter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,8 @@ func TestReuseService(t *testing.T) {
|
||||||
|
|
||||||
srv := NewServer(staticConfig, nil, entryPoints, nil)
|
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.
|
// Test that the /ok path returns a status 200.
|
||||||
responseRecorderOk := &httptest.ResponseRecorder{}
|
responseRecorderOk := &httptest.ResponseRecorder{}
|
||||||
|
|
|
@ -258,7 +258,8 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := NewServer(globalConfig, nil, entryPointsConfig, nil)
|
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{}
|
responseRecorder := &httptest.ResponseRecorder{}
|
||||||
request := httptest.NewRequest(http.MethodGet, testServer.URL+requestPath, nil)
|
request := httptest.NewRequest(http.MethodGet, testServer.URL+requestPath, nil)
|
||||||
|
|
|
@ -26,7 +26,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewManager creates a new Manager
|
// 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{
|
return &Manager{
|
||||||
bufferPool: newBufferPool(),
|
bufferPool: newBufferPool(),
|
||||||
defaultRoundTripper: defaultRoundTripper,
|
defaultRoundTripper: defaultRoundTripper,
|
||||||
|
@ -40,7 +40,7 @@ type Manager struct {
|
||||||
bufferPool httputil.BufferPool
|
bufferPool httputil.BufferPool
|
||||||
defaultRoundTripper http.RoundTripper
|
defaultRoundTripper http.RoundTripper
|
||||||
balancers map[string][]healthcheck.BalancerHandler
|
balancers map[string][]healthcheck.BalancerHandler
|
||||||
configs map[string]*config.Service
|
configs map[string]*config.ServiceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildHTTP Creates a http.Handler for a service configuration.
|
// 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)
|
serviceName = internal.GetQualifiedName(ctx, serviceName)
|
||||||
ctx = internal.AddProviderInContext(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
|
// TODO Should handle multiple service types
|
||||||
// FIXME Check if the service is declared multiple times with different types
|
// FIXME Check if the service is declared multiple times with different types
|
||||||
if conf.LoadBalancer != nil {
|
if conf.LoadBalancer == nil {
|
||||||
return m.getLoadBalancerServiceHandler(ctx, serviceName, conf.LoadBalancer, responseModifier)
|
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(
|
func (m *Manager) getLoadBalancerServiceHandler(
|
||||||
|
@ -158,7 +168,7 @@ func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
if timeout >= interval {
|
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{
|
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)
|
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 {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
serviceName string
|
serviceName string
|
||||||
configs map[string]*config.Service
|
configs map[string]*config.ServiceInfo
|
||||||
providerName string
|
providerName string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "Simple service name",
|
desc: "Simple service name",
|
||||||
serviceName: "serviceName",
|
serviceName: "serviceName",
|
||||||
configs: map[string]*config.Service{
|
configs: map[string]*config.ServiceInfo{
|
||||||
"serviceName": {
|
"serviceName": {
|
||||||
|
Service: &config.Service{
|
||||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "Service name with provider",
|
desc: "Service name with provider",
|
||||||
serviceName: "provider-1.serviceName",
|
serviceName: "provider-1.serviceName",
|
||||||
configs: map[string]*config.Service{
|
configs: map[string]*config.ServiceInfo{
|
||||||
"provider-1.serviceName": {
|
"provider-1.serviceName": {
|
||||||
|
Service: &config.Service{
|
||||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "Service name with provider in context",
|
desc: "Service name with provider in context",
|
||||||
serviceName: "serviceName",
|
serviceName: "serviceName",
|
||||||
configs: map[string]*config.Service{
|
configs: map[string]*config.ServiceInfo{
|
||||||
"provider-1.serviceName": {
|
"provider-1.serviceName": {
|
||||||
|
Service: &config.Service{
|
||||||
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
LoadBalancer: &config.LoadBalancerService{Method: "wrr"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
providerName: "provider-1",
|
providerName: "provider-1",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@ import (
|
||||||
|
|
||||||
// Manager is the TCPHandlers factory
|
// Manager is the TCPHandlers factory
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
configs map[string]*config.TCPService
|
configs map[string]*config.TCPServiceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new manager
|
// NewManager creates a new manager
|
||||||
func NewManager(configs map[string]*config.TCPService) *Manager {
|
func NewManager(conf *config.RuntimeConfiguration) *Manager {
|
||||||
return &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 := internal.AddProviderInContext(rootCtx, serviceQualifiedName)
|
||||||
ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
|
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]
|
conf, ok := m.configs[serviceQualifiedName]
|
||||||
if !ok {
|
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 {
|
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)
|
logger := log.FromContext(ctx)
|
||||||
|
|
||||||
// FIXME Check if the service is declared multiple times with different types
|
|
||||||
loadBalancer := tcp.NewRRLoadBalancer()
|
loadBalancer := tcp.NewRRLoadBalancer()
|
||||||
|
|
||||||
for _, server := range conf.LoadBalancer.Servers {
|
for _, server := range conf.LoadBalancer.Servers {
|
||||||
if _, err := parseIP(server.Address); err != nil {
|
if _, _, err := net.SplitHostPort(server.Address); err != nil {
|
||||||
logger.Errorf("Invalid IP address for a %q server %q: %v", serviceQualifiedName, server.Address, err)
|
logger.Errorf("In service %q: %v", serviceQualifiedName, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,20 +57,5 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
|
||||||
|
|
||||||
loadBalancer.AddServer(handler)
|
loadBalancer.AddServer(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
return loadBalancer, nil
|
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"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/pkg/config"
|
"github.com/containous/traefik/pkg/config"
|
||||||
|
"github.com/containous/traefik/pkg/server/internal"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,28 +14,32 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
serviceName string
|
serviceName string
|
||||||
configs map[string]*config.TCPService
|
configs map[string]*config.TCPServiceInfo
|
||||||
|
providerName string
|
||||||
expectedError string
|
expectedError string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "without configuration",
|
desc: "without configuration",
|
||||||
serviceName: "test",
|
serviceName: "test",
|
||||||
configs: nil,
|
configs: nil,
|
||||||
expectedError: `the service "test" does not exits`,
|
expectedError: `the service "test" does not exist`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "missing lb configuration",
|
desc: "missing lb configuration",
|
||||||
serviceName: "test",
|
serviceName: "test",
|
||||||
configs: map[string]*config.TCPService{
|
configs: map[string]*config.TCPServiceInfo{
|
||||||
"test": {},
|
"test": {
|
||||||
|
TCPService: &config.TCPService{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
expectedError: `the service "test" doesn't have any TCP load balancer`,
|
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",
|
serviceName: "test",
|
||||||
configs: map[string]*config.TCPService{
|
configs: map[string]*config.TCPServiceInfo{
|
||||||
"test": {
|
"test": {
|
||||||
|
TCPService: &config.TCPService{
|
||||||
LoadBalancer: &config.TCPLoadBalancerService{
|
LoadBalancer: &config.TCPLoadBalancerService{
|
||||||
Servers: []config.TCPServer{
|
Servers: []config.TCPServer{
|
||||||
{Address: "test:31"},
|
{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",
|
serviceName: "test",
|
||||||
configs: map[string]*config.TCPService{
|
configs: map[string]*config.TCPServiceInfo{
|
||||||
"test": {
|
"test": {
|
||||||
|
TCPService: &config.TCPService{
|
||||||
LoadBalancer: &config.TCPLoadBalancerService{
|
LoadBalancer: &config.TCPLoadBalancerService{
|
||||||
Servers: []config.TCPServer{
|
Servers: []config.TCPServer{
|
||||||
{Address: "foobar"},
|
{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 {
|
for _, test := range testCases {
|
||||||
|
@ -62,19 +181,22 @@ func TestManager_BuildTCP(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
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 test.expectedError != "" {
|
||||||
if err == nil {
|
assert.EqualError(t, err, test.expectedError)
|
||||||
require.Error(t, err)
|
|
||||||
} else {
|
|
||||||
require.EqualError(t, err, test.expectedError)
|
|
||||||
require.Nil(t, handler)
|
require.Nil(t, handler)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
assert.Nil(t, err)
|
||||||
require.NotNil(t, handler)
|
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