Add support for TCP (in kubernetes CRD)
Co-authored-by: Jean-Baptiste Doumenjou <jb.doumenjou@gmail.com>
This commit is contained in:
parent
c1dc783512
commit
c4df78b4b9
44 changed files with 2070 additions and 93 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
14
integration/fixtures/k8s/05-ingressroutetcp.yml
Normal file
14
integration/fixtures/k8s/05-ingressroutetcp.yml
Normal 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
|
|
@ -8,6 +8,9 @@ level = "DEBUG"
|
|||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
[entryPoints.footcp]
|
||||
address = ":8093"
|
||||
|
||||
|
||||
[api]
|
||||
|
||||
|
|
|
@ -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
102
integration/testdata/rawdata-crd.json
vendored
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
31
integration/testdata/rawdata-ingress.json
vendored
Normal file
31
integration/testdata/rawdata-ingress.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue