Skip to content

Commit

Permalink
Move to client interfaces, and create fixture tests for API and Contr…
Browse files Browse the repository at this point in the history
…oller
  • Loading branch information
dtomcej committed Mar 3, 2020
1 parent a99f3fd commit ffb9836
Show file tree
Hide file tree
Showing 27 changed files with 600 additions and 198 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ replace (
replace (
github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20180112153951-65b0cdae8d7f
github.com/go-check/check => github.com/containous/check v0.0.0-20170915194414-ca0bf163426a
github.com/gorilla/mux => github.com/containous/mux v0.0.0-20181024131434-c33f32e26898
github.com/mailgun/minheap => github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595
github.com/mailgun/multibuf => github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba
github.com/rancher/go-rancher-metadata => github.com/containous/go-rancher-metadata v0.0.0-20190402144056-c6a65f8b7a28
Expand Down
5 changes: 3 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ github.com/containous/go-rancher-metadata v0.0.0-20190402144056-c6a65f8b7a28 h1:
github.com/containous/go-rancher-metadata v0.0.0-20190402144056-c6a65f8b7a28/go.mod h1:YTAhdMF+tmHPGF7v0uZJ22+XNY/jz1ZYdBCeTZnsrYU=
github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595/go.mod h1:+lHFbEasIiQVGzhVDVw/cn0ZaOzde2OwNncp1NhXV4c=
github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba/go.mod h1:zkWcASFUJEst6QwCrxLdkuw1gvaKqmflEipm+iecV5M=
github.com/containous/mux v0.0.0-20181024131434-c33f32e26898 h1:1srn9voikJGofblBhWy3WuZWqo14Ou7NaswNG/I2yWc=
github.com/containous/mux v0.0.0-20181024131434-c33f32e26898/go.mod h1:z8WW7n06n8/1xF9Jl9WmuDeZuHAhfL+bwarNjsciwwg=
github.com/containous/traefik/v2 v2.0.2 h1:0qFjHflKAkL5o8iviL45jzATYvxgnnmA2ZQdJwPNw7g=
github.com/containous/traefik/v2 v2.0.2/go.mod h1:NRaSqz/FC+5woArsgL60pEp6EgNklmAT1p16XTiXkE0=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
Expand Down Expand Up @@ -233,6 +231,9 @@ github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gravitational/trace v0.0.0-20190726142706-a535a178675f h1:68WxnfBzJRYktZ30fmIjGQ74RsXYLoeH2/NITPktTMY=
github.com/gravitational/trace v0.0.0-20190726142706-a535a178675f/go.mod h1:RvdOUHE4SHqR3oXlFFKnGzms8a5dugHygGw1bqDstYI=
Expand Down
4 changes: 2 additions & 2 deletions integration/smi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (s *SMISuite) checkHTTPServiceServerURLs(c *check.C, config *dynamic.Config

serviceName := string(name[0])

endpoints, err := s.client.KubeClient.CoreV1().Endpoints(testNamespace).Get(serviceName, metav1.GetOptions{})
endpoints, err := s.client.GetKubernetesClient().CoreV1().Endpoints(testNamespace).Get(serviceName, metav1.GetOptions{})
c.Assert(err, checker.IsNil)

for _, subset := range endpoints.Subsets {
Expand Down Expand Up @@ -161,7 +161,7 @@ func (s *SMISuite) checkTCPServiceServerURLs(c *check.C, config *dynamic.Configu
for name, service := range config.TCP.Services {
serviceName := "tcp"

endpoints, err := s.client.KubeClient.CoreV1().Endpoints(testNamespace).Get(serviceName, metav1.GetOptions{})
endpoints, err := s.client.GetKubernetesClient().CoreV1().Endpoints(testNamespace).Get(serviceName, metav1.GetOptions{})
c.Assert(err, checker.IsNil)

for _, subset := range endpoints.Subsets {
Expand Down
4 changes: 2 additions & 2 deletions integration/try/try.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (t *Try) WaitPodIPAssigned(name string, namespace string, timeout time.Dura
ebo.MaxElapsedTime = applyCIMultiplier(timeout)

if err := backoff.Retry(safe.OperationWithRecover(func() error {
pod, err := t.client.KubeClient.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
pod, err := t.client.GetKubernetesClient().CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("unable get the pod %q in namespace %q: %v", name, namespace, err)
}
Expand Down Expand Up @@ -230,7 +230,7 @@ func (t *Try) WaitClientCreated(url string, kubeConfigPath string, timeout time.
return fmt.Errorf("unable to create clients: %v", err)
}

if _, err = clients.KubeClient.ServerVersion(); err != nil {
if _, err = clients.GetKubernetesClient().Discovery().ServerVersion(); err != nil {
return fmt.Errorf("unable to get server version: %v", err)
}

Expand Down
16 changes: 13 additions & 3 deletions pkg/controller/api.go → pkg/api/api.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package controller
package api

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"github.com/containous/maesh/pkg/deploylog"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
Expand All @@ -15,14 +16,23 @@ import (
listers "k8s.io/client-go/listers/core/v1"
)

// Ensure the API fits the interface
var _ Interface = (*API)(nil)

// Interface is an interface to interact with the REST API.
type Interface interface {
Start()
EnableReadiness()
}

// API is an implementation of an api.
type API struct {
router *mux.Router
readiness bool
lastConfiguration *safe.Safe
apiPort int32
apiHost string
deployLog *DeployLog
deployLog deploylog.Interface
meshNamespace string
podLister listers.PodLister
}
Expand All @@ -34,7 +44,7 @@ type podInfo struct {
}

// NewAPI creates a new api.
func NewAPI(apiPort int32, apiHost string, lastConfiguration *safe.Safe, deployLog *DeployLog, podLister listers.PodLister, meshNamespace string) *API {
func NewAPI(apiPort int32, apiHost string, lastConfiguration *safe.Safe, deployLog deploylog.Interface, podLister listers.PodLister, meshNamespace string) *API {
a := &API{
readiness: false,
lastConfiguration: lastConfiguration,
Expand Down
231 changes: 231 additions & 0 deletions pkg/api/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package api

import (
"context"
"fmt"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/containous/maesh/pkg/deploylog"
"github.com/containous/maesh/pkg/k8s"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/testhelpers"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
)

var (
localhost = "127.0.0.1"
)

func TestEnableReadiness(t *testing.T) {
config := safe.Safe{}
api := NewAPI(9000, localhost, &config, nil, nil, "foo")

assert.Equal(t, false, api.readiness)

api.EnableReadiness()

assert.Equal(t, true, api.readiness)
}

func TestGetReadiness(t *testing.T) {
testCases := []struct {
desc string
readiness bool
expectedStatusCode int
}{
{
desc: "ready",
readiness: true,
expectedStatusCode: http.StatusOK,
},
{
desc: "not ready",
readiness: false,
expectedStatusCode: http.StatusInternalServerError,
},
}

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
config := safe.Safe{}
api := NewAPI(9000, localhost, &config, nil, nil, "foo")
api.readiness = test.readiness

res := httptest.NewRecorder()
req := testhelpers.MustNewRequest(http.MethodGet, "/api/status/readiness", nil)

api.getReadiness(res, req)

assert.Equal(t, test.expectedStatusCode, res.Code)
})
}
}

func TestGetCurrentConfiguration(t *testing.T) {
config := safe.Safe{}
api := NewAPI(9000, localhost, &config, nil, nil, "foo")

config.Set("foo")

res := httptest.NewRecorder()
req := testhelpers.MustNewRequest(http.MethodGet, "/api/configuration/current", nil)

api.getCurrentConfiguration(res, req)

assert.Equal(t, "\"foo\"\n", res.Body.String())
}

func TestGetDeployLog(t *testing.T) {
config := safe.Safe{}
log := deploylog.NewDeployLog(1000)
api := NewAPI(9000, localhost, &config, log, nil, "foo")

currentTime := time.Now()
log.LogDeploy(currentTime, "foo", "bar", true, "blabla")

data, err := currentTime.MarshalJSON()
assert.NoError(t, err)

currentTimeString := string(data)
expected := fmt.Sprintf("[{\"TimeStamp\":%s,\"PodName\":\"foo\",\"PodIP\":\"bar\",\"DeploySuccessful\":true,\"Reason\":\"blabla\"}]", currentTimeString)

res := httptest.NewRecorder()
req := testhelpers.MustNewRequest(http.MethodGet, "/api/configuration/current", nil)

api.getDeployLog(res, req)
assert.Equal(t, expected, res.Body.String())
assert.Equal(t, http.StatusOK, res.Code)
}

func TestGetMeshNodes(t *testing.T) {
testCases := []struct {
desc string
mockFile string
expectedBody string
expectedStatusCode int
podError bool
}{
{
desc: "empty mesh node list",
mockFile: "getmeshnodes_empty.yaml",
expectedBody: "[]\n",
expectedStatusCode: http.StatusOK,
},
{
desc: "one item in mesh node list",
mockFile: "getmeshnodes_one_mesh_pod.yaml",
expectedBody: "[{\"Name\":\"mesh-pod-1\",\"IP\":\"10.4.3.2\",\"Ready\":true}]\n",
expectedStatusCode: http.StatusOK,
},
{
desc: "one item in mesh node list with non ready pod",
mockFile: "getmeshnodes_one_nonready_mesh_pod.yaml",
expectedBody: "[{\"Name\":\"mesh-pod-1\",\"IP\":\"10.4.19.1\",\"Ready\":false}]\n",
expectedStatusCode: http.StatusOK,
},
}

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
config := safe.Safe{}
log := deploylog.NewDeployLog(1000)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

clientMock := k8s.NewClientMock(ctx.Done(), test.mockFile, false)
api := NewAPI(9000, localhost, &config, log, clientMock.PodLister, "foo")

res := httptest.NewRecorder()
req := testhelpers.MustNewRequest(http.MethodGet, "/api/status/nodes", nil)

api.getMeshNodes(res, req)

assert.Equal(t, test.expectedBody, res.Body.String())
assert.Equal(t, test.expectedStatusCode, res.Code)
})
}
}

func TestGetMeshNodeConfiguration(t *testing.T) {
testCases := []struct {
desc string
mockFile string
expectedBody string
expectedStatusCode int
podError bool
}{
{
desc: "simple mesh node configuration",
mockFile: "getmeshnodeconfiguration_simple.yaml",
expectedBody: "{test_configuration_json}",
expectedStatusCode: http.StatusOK,
},
{
desc: "pod not found",
mockFile: "getmeshnodeconfiguration_empty.yaml",
expectedBody: "unable to find pod: mesh-pod-1",
expectedStatusCode: http.StatusNotFound,
},
}

apiServer := startTestAPIServer("8080", http.StatusOK, []byte("{test_configuration_json}"))
defer apiServer.Close()

for _, test := range testCases {
config := safe.Safe{}
log := deploylog.NewDeployLog(1000)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

clientMock := k8s.NewClientMock(ctx.Done(), test.mockFile, false)
api := NewAPI(9000, localhost, &config, log, clientMock.PodLister, "foo")

res := httptest.NewRecorder()
req := testhelpers.MustNewRequest(http.MethodGet, "/api/status/node/mesh-pod-1/configuration", nil)

//fake gorilla/mux vars
vars := map[string]string{
"node": "mesh-pod-1",
}

req = mux.SetURLVars(req, vars)

api.getMeshNodeConfiguration(res, req)

assert.Equal(t, test.expectedBody, res.Body.String())
assert.Equal(t, test.expectedStatusCode, res.Code)
}
}

func startTestAPIServer(port string, statusCode int, bodyData []byte) (ts *httptest.Server) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(statusCode)
w.Header().Set("Content-Type", "application/json")

_, _ = w.Write(bodyData)
})
listener, err := net.Listen("tcp", "127.0.0.1:"+port)

if err != nil {
panic(err)
}

ts = &httptest.Server{
Listener: listener,
Config: &http.Server{Handler: handler},
}
ts.Start()

return ts
}
Empty file.
13 changes: 13 additions & 0 deletions pkg/api/fixtures/getmeshnodeconfiguration_simple.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Pod
metadata:
name: mesh-pod-1
namespace: foo
labels:
component: maesh-mesh
spec:
containers:
- name: example
image: busybox
status:
podIP: "127.0.0.1"
Empty file.
13 changes: 13 additions & 0 deletions pkg/api/fixtures/getmeshnodes_one_mesh_pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Pod
metadata:
name: mesh-pod-1
namespace: foo
labels:
component: maesh-mesh
spec:
containers:
- name: example
image: busybox
status:
podIP: "10.4.3.2"
16 changes: 16 additions & 0 deletions pkg/api/fixtures/getmeshnodes_one_nonready_mesh_pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: mesh-pod-1
namespace: foo
labels:
component: maesh-mesh
spec:
containers:
- name: example
image: busybox
status:
podIP: "10.4.19.1"
containerStatuses:
- name: example
ready: false

0 comments on commit ffb9836

Please sign in to comment.