Add support for TCP (in kubernetes CRD)

Co-authored-by: Jean-Baptiste Doumenjou <jb.doumenjou@gmail.com>
This commit is contained in:
mpl 2019-06-11 15:12:04 +02:00 committed by Traefiker Bot
parent c1dc783512
commit c4df78b4b9
44 changed files with 2070 additions and 93 deletions

View file

@ -26,3 +26,18 @@ spec:
plural: middlewares
singular: middleware
scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: ingressroutetcps.traefik.containo.us
spec:
group: traefik.containo.us
version: v1alpha1
names:
kind: IngressRouteTCP
plural: ingressroutetcps
singular: ingressroutetcp
scope: Namespaced

View file

@ -39,3 +39,46 @@ spec:
selector:
app: containous
task: whoami
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: whoamitcp
namespace: default
labels:
app: containous
name: whoamitcp
spec:
replicas: 2
selector:
matchLabels:
app: containous
task: whoamitcp
template:
metadata:
labels:
app: containous
task: whoamitcp
spec:
containers:
- name: containouswhoamitcp
image: containous/whoamitcp
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: whoamitcp
namespace: default
spec:
ports:
- name: footcp
port: 8080
selector:
app: containous
task: whoamitcp

View file

@ -0,0 +1,14 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
name: test3.crd
namespace: default
spec:
entryPoints:
- footcp
routes:
- match: HostSNI(`*`)
services:
- name: whoamitcp
port: 8080

View file

@ -8,6 +8,9 @@ level = "DEBUG"
[entryPoints]
[entryPoints.web]
address = ":8000"
[entryPoints.footcp]
address = ":8093"
[api]

View file

@ -1,16 +1,28 @@
package integration
import (
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"regexp"
"time"
"github.com/containous/traefik/integration/try"
"github.com/containous/traefik/pkg/api"
"github.com/go-check/check"
"github.com/pmezard/go-difflib/difflib"
checker "github.com/vdemeester/shakers"
)
var updateExpected = flag.Bool("update_expected", false, "Update expected files in testdata")
// K8sSuite
type K8sSuite struct{ BaseSuite }
@ -48,7 +60,7 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
}
}
func (s *K8sSuite) TestIngressSimple(c *check.C) {
func (s *K8sSuite) TestIngressConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
defer display(c)
@ -56,11 +68,10 @@ func (s *K8sSuite) TestIngressSimple(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Host(`whoami.test`)"))
c.Assert(err, checker.IsNil)
testConfiguration(c, "testdata/rawdata-ingress.json")
}
func (s *K8sSuite) TestCRDSimple(c *check.C) {
func (s *K8sSuite) TestCRDConfiguration(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
defer display(c)
@ -68,15 +79,105 @@ func (s *K8sSuite) TestCRDSimple(c *check.C) {
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Host(`foo.com`)"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("PathPrefix(`/tobestripped`)"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("default/stripprefix"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("stripprefix"))
c.Assert(err, checker.IsNil)
testConfiguration(c, "testdata/rawdata-crd.json")
}
func testConfiguration(c *check.C, path string) {
expectedJSON := filepath.FromSlash(path)
if *updateExpected {
fi, err := os.Create(expectedJSON)
c.Assert(err, checker.IsNil)
err = fi.Close()
c.Assert(err, checker.IsNil)
}
var buf bytes.Buffer
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 20*time.Second, try.StatusCodeIs(http.StatusOK), matchesConfig(expectedJSON, &buf))
if !*updateExpected {
if err != nil {
c.Error(err)
}
return
}
if err != nil {
c.Logf("In file update mode, got expected error: %v", err)
}
var rtRepr api.RunTimeRepresentation
err = json.Unmarshal(buf.Bytes(), &rtRepr)
c.Assert(err, checker.IsNil)
newJSON, err := json.MarshalIndent(rtRepr, "", "\t")
c.Assert(err, checker.IsNil)
err = ioutil.WriteFile(expectedJSON, newJSON, 0644)
c.Assert(err, checker.IsNil)
c.Errorf("We do not want a passing test in file update mode")
}
func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition {
return func(res *http.Response) error {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %s", err)
}
if err := res.Body.Close(); err != nil {
return err
}
var obtained api.RunTimeRepresentation
err = json.Unmarshal(body, &obtained)
if err != nil {
return err
}
if buf != nil {
buf.Reset()
if _, err := io.Copy(buf, bytes.NewReader(body)); err != nil {
return err
}
}
got, err := json.MarshalIndent(obtained, "", "\t")
if err != nil {
return err
}
expected, err := ioutil.ReadFile(wantConfig)
if err != nil {
return err
}
// The pods IPs are dynamic, so we cannot predict them,
// which is why we have to ignore them in the comparison.
rxURL := regexp.MustCompile(`"(url|address)":\s+(".*")`)
sanitizedExpected := rxURL.ReplaceAll(expected, []byte(`"$1": "XXXX"`))
sanitizedGot := rxURL.ReplaceAll(got, []byte(`"$1": "XXXX"`))
rxServerStatus := regexp.MustCompile(`"http://.*?":\s+(".*")`)
sanitizedExpected = rxServerStatus.ReplaceAll(sanitizedExpected, []byte(`"http://XXXX": $1`))
sanitizedGot = rxServerStatus.ReplaceAll(sanitizedGot, []byte(`"http://XXXX": $1`))
if bytes.Equal(sanitizedExpected, sanitizedGot) {
return nil
}
diff := difflib.UnifiedDiff{
FromFile: "Expected",
A: difflib.SplitLines(string(sanitizedExpected)),
ToFile: "Got",
B: difflib.SplitLines(string(sanitizedGot)),
Context: 3,
}
text, err := difflib.GetUnifiedDiffString(diff)
if err != nil {
return err
}
return errors.New(text)
}
}

102
integration/testdata/rawdata-crd.json vendored Normal file
View file

@ -0,0 +1,102 @@
{
"routers": {
"kubernetescrd.default/test-crd-6b204d94623b3df4370c": {
"entryPoints": [
"web"
],
"service": "default/test-crd-6b204d94623b3df4370c",
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/bar`)",
"priority": 12
},
"kubernetescrd.default/test2-crd-23c7f4c450289ee29016": {
"entryPoints": [
"web"
],
"middlewares": [
"default/stripprefix"
],
"service": "default/test2-crd-23c7f4c450289ee29016",
"rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/tobestripped`)"
}
},
"middlewares": {
"kubernetescrd.default/stripprefix": {
"stripPrefix": {
"prefixes": [
"/tobestripped"
]
},
"usedBy": [
"kubernetescrd.default/test2-crd-23c7f4c450289ee29016"
]
}
},
"services": {
"kubernetescrd.default/test-crd-6b204d94623b3df4370c": {
"loadbalancer": {
"servers": [
{
"url": "http://10.42.0.4:80"
},
{
"url": "http://10.42.0.5:80"
}
],
"passHostHeader": true
},
"usedBy": [
"kubernetescrd.default/test-crd-6b204d94623b3df4370c"
],
"serverStatus": {
"http://10.42.0.4:80": "UP",
"http://10.42.0.5:80": "UP"
}
},
"kubernetescrd.default/test2-crd-23c7f4c450289ee29016": {
"loadbalancer": {
"servers": [
{
"url": "http://10.42.0.4:80"
},
{
"url": "http://10.42.0.5:80"
}
],
"passHostHeader": true
},
"usedBy": [
"kubernetescrd.default/test2-crd-23c7f4c450289ee29016"
],
"serverStatus": {
"http://10.42.0.4:80": "UP",
"http://10.42.0.5:80": "UP"
}
}
},
"tcpRouters": {
"kubernetescrd.default/test3-crd-673acf455cb2dab0b43a": {
"entryPoints": [
"footcp"
],
"service": "default/test3-crd-673acf455cb2dab0b43a",
"rule": "HostSNI(`*`)"
}
},
"tcpServices": {
"kubernetescrd.default/test3-crd-673acf455cb2dab0b43a": {
"loadbalancer": {
"servers": [
{
"address": "10.42.0.2:8080"
},
{
"address": "10.42.0.3:8080"
}
]
},
"usedBy": [
"kubernetescrd.default/test3-crd-673acf455cb2dab0b43a"
]
}
}
}

View file

@ -0,0 +1,31 @@
{
"routers": {
"kubernetes.whoami-test/whoami": {
"entryPoints": null,
"service": "default/whoami/http",
"rule": "Host(`whoami.test`) \u0026\u0026 PathPrefix(`/whoami`)"
}
},
"services": {
"kubernetes.default/whoami/http": {
"loadbalancer": {
"servers": [
{
"url": "http://10.42.0.2:80"
},
{
"url": "http://10.42.0.5:80"
}
],
"passHostHeader": true
},
"usedBy": [
"kubernetes.whoami-test/whoami"
],
"serverStatus": {
"http://10.42.0.2:80": "UP",
"http://10.42.0.5:80": "UP"
}
}
}
}