1
0
Fork 0

UDP support

Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
This commit is contained in:
mpl 2020-02-11 01:26:04 +01:00 committed by GitHub
parent 8988c8f9af
commit 115d42e0f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 4730 additions and 321 deletions

View file

@ -49,3 +49,30 @@
[tls.options.baz]
minversion = "VersionTLS11"
[tcp.routers]
[tcp.routers.router3]
entrypoints=["unknown-entrypoint"]
service = "service1"
rule = "HostSNI(`mydomain.com`)"
[tcp.routers.router4]
entrypoints=["websecure"]
service = "service1"
rule = "Host(`mydomain.com`)"
[tcp.services]
[tcp.services.service1]
[tcp.services.service1.loadBalancer]
[[tcp.services.service1.loadBalancer.servers]]
address = "127.0.0.1:9010"
[udp.routers]
[udp.routers.router3]
entrypoints=["unknown-entrypoint"]
service = "service1"
[udp.services]
[udp.services.service1]
[udp.services.service1.loadBalancer]
[[udp.services.service1.loadBalancer.servers]]
address = "127.0.0.1:9010"

View file

@ -8,6 +8,8 @@
[entryPoints]
[entryPoints.websecure]
address = ":4443"
[entryPoints.udp]
address = ":4443/udp"
[api]
insecure = true
@ -33,3 +35,35 @@
[http.services.service2.loadBalancer]
[[http.services.service2.loadBalancer.servers]]
url = "http://127.0.0.1:9010"
[tcp.routers]
[tcp.routers.router4]
service = "service1"
rule = "HostSNI(`snitest.net`)"
[tcp.routers.router5]
service = "service2"
rule = "HostSNI(`snitest.com`)"
[tcp.services]
[tcp.services.service1]
[tcp.services.service2]
[tcp.services.service2.loadBalancer]
[[tcp.services.service2.loadBalancer.servers]]
address = "127.0.0.1:9010"
[udp.routers]
[udp.routers.router4]
service = "service1"
[udp.routers.router5]
service = "service2"
[udp.services]
[udp.services.service1]
[udp.services.service2]
[udp.services.service2.loadBalancer]
[[udp.services.service2.loadBalancer.servers]]
address = "127.0.0.1:9010"

View file

@ -0,0 +1,53 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
[entryPoints]
[entryPoints.udp]
address = ":8093/udp"
[entryPoints.web]
address = ":8093"
[api]
insecure = true
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[udp]
[udp.routers]
[udp.routers.to-whoami-a]
service = "whoami"
entryPoints = [ "udp" ]
[[udp.services.whoami.weighted.services]]
name="whoami-a"
weight=3
[[udp.services.whoami.weighted.services]]
name="whoami-b"
weight=1
[udp.services.whoami-a.loadBalancer]
[[udp.services.whoami-a.loadBalancer.servers]]
address = "{{ .WhoamiAIP}}:8080"
[[udp.services.whoami-a.loadBalancer.servers]]
address = "{{ .WhoamiCIP}}:8080"
[udp.services.whoami-b.loadBalancer]
[[udp.services.whoami-b.loadBalancer.servers]]
address = "{{ .WhoamiBIP}}:8080"
[http]
[http.routers]
[http.routers.to-whoami-d]
service = "whoami"
entryPoints = [ "web" ]
rule = "PathPrefix(`/who`)"
[http.services.whoami.loadBalancer]
[[http.services.whoami.loadBalancer.servers]]
url = "http://{{ .WhoamiDIP}}"

View file

@ -60,6 +60,7 @@ func Test(t *testing.T) {
check.Suite(&TimeoutSuite{})
check.Suite(&TLSClientHeadersSuite{})
check.Suite(&TracingSuite{})
check.Suite(&UDPSuite{})
check.Suite(&WebsocketSuite{})
check.Suite(&ZookeeperSuite{})
}

View file

@ -0,0 +1,14 @@
whoami-a:
image: containous/whoamiudp:dev
command: -name whoami-a
whoami-b:
image: containous/whoamiudp:dev
command: -name whoami-b
whoami-c:
image: containous/whoamiudp:dev
command: -name whoami-c
whoami-d:
image: containous/whoami

View file

@ -549,6 +549,83 @@ func (s *SimpleSuite) TestServiceConfigErrors(c *check.C) {
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestTCPRouterConfigErrors(c *check.C) {
file := s.adaptFile(c, "fixtures/router_errors.toml", struct{}{})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
// router3 has an error because it uses an unknown entrypoint
err = try.GetRequest("http://127.0.0.1:8080/api/tcp/routers/router3@file", 1000*time.Millisecond, try.BodyContains(`entryPoint \"unknown-entrypoint\" doesn't exist`, "no valid entryPoint for this router"))
c.Assert(err, checker.IsNil)
// router4 has an unsupported Rule
err = try.GetRequest("http://127.0.0.1:8080/api/tcp/routers/router4@file", 1000*time.Millisecond, try.BodyContains("unknown rule Host(`mydomain.com`)"))
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestTCPServiceConfigErrors(c *check.C) {
file := s.adaptFile(c, "fixtures/service_errors.toml", struct{}{})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/tcp/services", 1000*time.Millisecond, try.BodyContains(`["the service \"service1@file\" does not have any type defined"]`))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/tcp/services/service1@file", 1000*time.Millisecond, try.BodyContains(`"status":"disabled"`))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/tcp/services/service2@file", 1000*time.Millisecond, try.BodyContains(`"status":"enabled"`))
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestUDPRouterConfigErrors(c *check.C) {
file := s.adaptFile(c, "fixtures/router_errors.toml", struct{}{})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/udp/routers/router3@file", 1000*time.Millisecond, try.BodyContains(`entryPoint \"unknown-entrypoint\" doesn't exist`, "no valid entryPoint for this router"))
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestUDPServiceConfigErrors(c *check.C) {
file := s.adaptFile(c, "fixtures/service_errors.toml", struct{}{})
defer os.Remove(file)
cmd, output := s.traefikCmd(withConfigFile(file))
defer output(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/udp/services", 1000*time.Millisecond, try.BodyContains(`["the udp service \"service1@file\" does not have any type defined"]`))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/udp/services/service1@file", 1000*time.Millisecond, try.BodyContains(`"status":"disabled"`))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/udp/services/service2@file", 1000*time.Millisecond, try.BodyContains(`"status":"enabled"`))
c.Assert(err, checker.IsNil)
}
func (s *SimpleSuite) TestWRR(c *check.C) {
s.createComposeProject(c, "base")
s.composeProject.Start(c)

107
integration/udp_test.go Normal file
View file

@ -0,0 +1,107 @@
package integration
import (
"net"
"net/http"
"os"
"strings"
"time"
"github.com/containous/traefik/v2/integration/try"
"github.com/go-check/check"
checker "github.com/vdemeester/shakers"
)
type UDPSuite struct{ BaseSuite }
func (s *UDPSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "udp")
s.composeProject.Start(c)
}
func guessWhoUDP(addr string) (string, error) {
var conn net.Conn
var err error
udpAddr, err2 := net.ResolveUDPAddr("udp", addr)
if err2 != nil {
return "", err2
}
conn, err = net.DialUDP("udp", nil, udpAddr)
if err != nil {
return "", err
}
_, err = conn.Write([]byte("WHO"))
if err != nil {
return "", err
}
out := make([]byte, 2048)
n, err := conn.Read(out)
if err != nil {
return "", err
}
return string(out[:n]), nil
}
func (s *UDPSuite) TestWRR(c *check.C) {
whoamiAIP := s.composeProject.Container(c, "whoami-a").NetworkSettings.IPAddress
whoamiBIP := s.composeProject.Container(c, "whoami-b").NetworkSettings.IPAddress
whoamiCIP := s.composeProject.Container(c, "whoami-c").NetworkSettings.IPAddress
whoamiDIP := s.composeProject.Container(c, "whoami-d").NetworkSettings.IPAddress
file := s.adaptFile(c, "fixtures/udp/wrr.toml", struct {
WhoamiAIP string
WhoamiBIP string
WhoamiCIP string
WhoamiDIP string
}{
WhoamiAIP: whoamiAIP,
WhoamiBIP: whoamiBIP,
WhoamiCIP: whoamiCIP,
WhoamiDIP: whoamiDIP,
})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("whoami-a"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8093/who", 5*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
stop := make(chan struct{})
go func() {
call := map[string]int{}
for i := 0; i < 4; i++ {
out, err := guessWhoUDP("127.0.0.1:8093")
c.Assert(err, checker.IsNil)
switch {
case strings.Contains(out, "whoami-a"):
call["whoami-a"]++
case strings.Contains(out, "whoami-b"):
call["whoami-b"]++
case strings.Contains(out, "whoami-c"):
call["whoami-c"]++
default:
call["unknown"]++
}
}
c.Assert(call, checker.DeepEquals, map[string]int{"whoami-a": 2, "whoami-b": 1, "whoami-c": 1})
close(stop)
}()
select {
case <-stop:
case <-time.Tick(time.Second * 5):
c.Error("Timeout")
}
}