Add unit test

Signed-off-by: Emile Vauge <emile@vauge.com>
This commit is contained in:
Emile Vauge 2016-04-20 13:26:51 +02:00
parent d82e1342fb
commit c0dd4c3209
No known key found for this signature in database
GPG key ID: D808B4C167352E59
8 changed files with 242 additions and 47 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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,
}
}
}
}

View file

@ -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
}