Add unit test
Signed-off-by: Emile Vauge <emile@vauge.com>
This commit is contained in:
parent
d82e1342fb
commit
c0dd4c3209
8 changed files with 242 additions and 47 deletions
|
@ -2,14 +2,14 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami-x
|
||||
name: service1
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
nodePort: 30301
|
||||
nodePort: 30283
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
|
@ -19,24 +19,7 @@ spec:
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami-default
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
nodePort: 30302
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami-y
|
||||
name: service2
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
|
@ -50,6 +33,23 @@ spec:
|
|||
selector:
|
||||
app: whoami
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service3
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
nodePort: 30285
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: whoami
|
||||
---
|
||||
# A single RC matching all Services
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
|
@ -72,7 +72,7 @@ spec:
|
|||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: whoamimap
|
||||
name: whoamiIngress
|
||||
spec:
|
||||
rules:
|
||||
- host: foo.localhost
|
||||
|
@ -80,14 +80,14 @@ spec:
|
|||
paths:
|
||||
- path: /bar
|
||||
backend:
|
||||
serviceName: whoami-x
|
||||
serviceName: service1
|
||||
servicePort: 80
|
||||
- host: bar.localhost
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: whoami-y
|
||||
serviceName: service2
|
||||
servicePort: 80
|
||||
- backend:
|
||||
serviceName: whoami-x
|
||||
serviceName: service3
|
||||
servicePort: 80
|
||||
|
|
|
@ -19,13 +19,6 @@ spec:
|
|||
- image: containous/traefik:k8s
|
||||
name: traefik-ingress-lb
|
||||
imagePullPolicy: Always
|
||||
# livenessProbe:
|
||||
# httpGet:
|
||||
# path: /healthz
|
||||
# port: 10249
|
||||
# scheme: HTTP
|
||||
# initialDelaySeconds: 30
|
||||
# timeoutSeconds: 5
|
||||
ports:
|
||||
- containerPort: 80
|
||||
hostPort: 80
|
||||
|
|
|
@ -21,7 +21,13 @@ const (
|
|||
)
|
||||
|
||||
// Client is a client for the Kubernetes master.
|
||||
type Client struct {
|
||||
type Client interface {
|
||||
GetIngresses(predicate func(Ingress) bool) ([]Ingress, error)
|
||||
WatchIngresses(predicate func(Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error)
|
||||
GetServices(predicate func(Service) bool) ([]Service, error)
|
||||
}
|
||||
|
||||
type clientImpl struct {
|
||||
endpointURL string
|
||||
tls *tls.Config
|
||||
token string
|
||||
|
@ -32,12 +38,12 @@ type Client struct {
|
|||
// The provided host is an url (scheme://hostname[:port]) of a
|
||||
// Kubernetes master without any path.
|
||||
// The provided client is an authorized http.Client used to perform requests to the Kubernetes API master.
|
||||
func NewClient(baseURL string, caCert []byte, token string) (*Client, error) {
|
||||
func NewClient(baseURL string, caCert []byte, token string) (Client, error) {
|
||||
validURL, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse URL %q: %v", baseURL, err)
|
||||
}
|
||||
return &Client{
|
||||
return &clientImpl{
|
||||
endpointURL: strings.TrimSuffix(validURL.String(), "/"),
|
||||
token: token,
|
||||
caCert: caCert,
|
||||
|
@ -45,7 +51,7 @@ func NewClient(baseURL string, caCert []byte, token string) (*Client, error) {
|
|||
}
|
||||
|
||||
// GetIngresses returns all services in the cluster
|
||||
func (c *Client) GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) {
|
||||
func (c *clientImpl) GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) {
|
||||
getURL := c.endpointURL + extentionsEndpoint + defaultIngress
|
||||
request := gorequest.New().Get(getURL)
|
||||
if len(c.token) > 0 {
|
||||
|
@ -76,7 +82,7 @@ func (c *Client) GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) {
|
|||
}
|
||||
|
||||
// WatchIngresses returns all services in the cluster
|
||||
func (c *Client) WatchIngresses(predicate func(Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
func (c *clientImpl) WatchIngresses(predicate func(Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
watchCh := make(chan interface{})
|
||||
errCh := make(chan error)
|
||||
|
||||
|
@ -130,7 +136,7 @@ func (c *Client) WatchIngresses(predicate func(Ingress) bool, stopCh <-chan bool
|
|||
}
|
||||
|
||||
// GetServices returns all services in the cluster
|
||||
func (c *Client) GetServices(predicate func(Service) bool) ([]Service, error) {
|
||||
func (c *clientImpl) GetServices(predicate func(Service) bool) ([]Service, error) {
|
||||
getURL := c.endpointURL + APIEndpoint + defaultService
|
||||
|
||||
// Make request to Kubernetes API
|
||||
|
|
|
@ -249,6 +249,19 @@ type IntOrString struct {
|
|||
StrVal string
|
||||
}
|
||||
|
||||
// FromInt creates an IntOrString object with an int32 value. It is
|
||||
// your responsibility not to call this method with a value greater
|
||||
// than int32.
|
||||
// TODO: convert to (val int32)
|
||||
func FromInt(val int) IntOrString {
|
||||
return IntOrString{Type: Int, IntVal: int32(val)}
|
||||
}
|
||||
|
||||
// FromString creates an IntOrString object with a string value.
|
||||
func FromString(val string) IntOrString {
|
||||
return IntOrString{Type: String, StrVal: val}
|
||||
}
|
||||
|
||||
// String returns the string value, or the Itoa of the int value.
|
||||
func (intstr *IntOrString) String() string {
|
||||
if intstr.Type == String {
|
||||
|
|
|
@ -23,7 +23,7 @@ type Kubernetes struct {
|
|||
Endpoint string
|
||||
}
|
||||
|
||||
func (provider *Kubernetes) createClient() (*k8s.Client, error) {
|
||||
func (provider *Kubernetes) createClient() (k8s.Client, error) {
|
||||
var token string
|
||||
tokenBytes, err := ioutil.ReadFile(serviceAccountToken)
|
||||
if err == nil {
|
||||
|
@ -103,7 +103,7 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
|||
return nil
|
||||
}
|
||||
|
||||
func (provider *Kubernetes) loadIngresses(k8sClient *k8s.Client) (*types.Configuration, error) {
|
||||
func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configuration, error) {
|
||||
ingresses, err := k8sClient.GetIngresses(func(ingress k8s.Ingress) bool {
|
||||
return true
|
||||
})
|
||||
|
@ -136,7 +136,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient *k8s.Client) (*types.Configu
|
|||
}
|
||||
if len(pa.Path) > 0 {
|
||||
templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{
|
||||
Rule: pa.Path,
|
||||
Rule: "PathStrip:" + pa.Path,
|
||||
}
|
||||
}
|
||||
services, err := k8sClient.GetServices(func(service k8s.Service) bool {
|
||||
|
@ -151,13 +151,13 @@ func (provider *Kubernetes) loadIngresses(k8sClient *k8s.Client) (*types.Configu
|
|||
for _, port := range service.Spec.Ports {
|
||||
if port.Port == pa.Backend.ServicePort.IntValue() {
|
||||
protocol = port.Name
|
||||
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
|
||||
URL: protocol + "://" + service.Spec.ClusterIP + ":" + pa.Backend.ServicePort.String(),
|
||||
Weight: 1,
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
|
||||
URL: protocol + "://" + service.Spec.ClusterIP + ":" + pa.Backend.ServicePort.String(),
|
||||
Weight: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,184 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/provider/k8s"
|
||||
"github.com/containous/traefik/types"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadIngresses(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{{
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
{
|
||||
Host: "foo",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar",
|
||||
Backend: k8s.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Host: "bar",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
ServiceName: "service3",
|
||||
ServicePort: k8s.FromInt(803),
|
||||
},
|
||||
},
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
ServiceName: "service2",
|
||||
ServicePort: k8s.FromInt(802),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
services := []k8s.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
Name: "service1",
|
||||
UID: "1",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
Name: "service2",
|
||||
UID: "2",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
ClusterIP: "10.0.0.2",
|
||||
Ports: []k8s.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 802,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
Name: "service3",
|
||||
UID: "3",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
ClusterIP: "10.0.0.3",
|
||||
Ports: []k8s.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 803,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
watchChan := make(chan interface{})
|
||||
client := clientMock{
|
||||
ingresses: ingresses,
|
||||
services: services,
|
||||
watchChan: watchChan,
|
||||
}
|
||||
provider := Kubernetes{}
|
||||
actual, err := provider.loadIngresses(client)
|
||||
if err != nil {
|
||||
t.Fatalf("error %+v", err)
|
||||
}
|
||||
|
||||
expected := &types.Configuration{
|
||||
Backends: map[string]*types.Backend{
|
||||
"foo/bar": {
|
||||
Servers: map[string]types.Server{
|
||||
"1": {
|
||||
URL: "http://10.0.0.1:801",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
"bar": {
|
||||
Servers: map[string]types.Server{
|
||||
"2": {
|
||||
URL: "http://10.0.0.2:802",
|
||||
Weight: 1,
|
||||
},
|
||||
"3": {
|
||||
URL: "http://10.0.0.3:803",
|
||||
Weight: 1,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
LoadBalancer: nil,
|
||||
},
|
||||
},
|
||||
Frontends: map[string]*types.Frontend{
|
||||
"foo/bar": {
|
||||
Backend: "foo/bar",
|
||||
Routes: map[string]types.Route{
|
||||
"/bar": {
|
||||
Rule: "PathStrip:/bar",
|
||||
},
|
||||
"foo": {
|
||||
Rule: "Host:foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"bar": {
|
||||
Backend: "bar",
|
||||
Routes: map[string]types.Route{
|
||||
"bar": {
|
||||
Rule: "Host:bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(actual.Backends, expected.Backends) {
|
||||
t.Fatalf("expected %+v, got %+v", expected.Backends, actual.Backends)
|
||||
}
|
||||
if !reflect.DeepEqual(actual.Frontends, expected.Frontends) {
|
||||
t.Fatalf("expected %+v, got %+v", expected.Frontends, actual.Frontends)
|
||||
}
|
||||
}
|
||||
|
||||
type clientMock struct {
|
||||
ingresses []k8s.Ingress
|
||||
services []k8s.Service
|
||||
watchChan chan interface{}
|
||||
}
|
||||
|
||||
func (c clientMock) GetIngresses(predicate func(k8s.Ingress) bool) ([]k8s.Ingress, error) {
|
||||
return c.ingresses, nil
|
||||
}
|
||||
func (c clientMock) WatchIngresses(predicate func(k8s.Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
return c.watchChan, make(chan error), nil
|
||||
}
|
||||
func (c clientMock) GetServices(predicate func(k8s.Service) bool) ([]k8s.Service, error) {
|
||||
return c.services, nil
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
backend = "{{$frontend.Backend}}"
|
||||
{{range $routeName, $route := $frontend.Routes}}
|
||||
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
|
||||
rule = "PathStrip:{{$route.Rule}}"
|
||||
rule = "{{$route.Rule}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -40,10 +40,10 @@
|
|||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="https://github.com/containous/traefik/blob/master/docs/index.md" target="_blank">Documentation</a>
|
||||
<a href="https://docs.traefik.io" target="_blank">Documentation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://traefik.io" target="_blank"><span class="traefik-blue">traefik.io</span></a>
|
||||
<a href="https://traefik.io" target="_blank"><span class="traefik-blue">traefik.io</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue