From e27a6abbd646765255d7e3e34590bb9858b8f9da Mon Sep 17 00:00:00 2001 From: Frank B Greco Jr Date: Fri, 29 Sep 2017 16:11:40 -0500 Subject: [PATCH] Upgrade Kubernetes Client and Move to CRDs (#70) * adding tests * linting --- crds/apikey.go | 20 +++++ crds/apikeybinding.go | 20 +++++ crds/apiproxy.go | 20 +++++ handlers/handler_test.go | 62 +++++++++++++++ handlers/incoming_test.go | 157 ++++++++++++++++++++++++++++++++++++++ handlers/logger_test.go | 61 +++++++++++++++ 6 files changed, 340 insertions(+) create mode 100644 handlers/handler_test.go create mode 100644 handlers/incoming_test.go create mode 100644 handlers/logger_test.go diff --git a/crds/apikey.go b/crds/apikey.go index 436ae57..5f6c0ae 100644 --- a/crds/apikey.go +++ b/crds/apikey.go @@ -1,3 +1,23 @@ +// Copyright (c) 2017 Northwestern Mutual. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package crds import ( diff --git a/crds/apikeybinding.go b/crds/apikeybinding.go index 8e577e7..308f259 100644 --- a/crds/apikeybinding.go +++ b/crds/apikeybinding.go @@ -1,3 +1,23 @@ +// Copyright (c) 2017 Northwestern Mutual. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package crds import ( diff --git a/crds/apiproxy.go b/crds/apiproxy.go index 1556bef..f493d7e 100644 --- a/crds/apiproxy.go +++ b/crds/apiproxy.go @@ -1,3 +1,23 @@ +// Copyright (c) 2017 Northwestern Mutual. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + package crds import ( diff --git a/handlers/handler_test.go b/handlers/handler_test.go new file mode 100644 index 0000000..4fcbac1 --- /dev/null +++ b/handlers/handler_test.go @@ -0,0 +1,62 @@ +// Copyright (c) 2017 Northwestern Mutual. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package handlers + +import ( + "net/http" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNormalize(t *testing.T) { + r1 := &http.Request{ + URL: &url.URL{ + Path: "///foo//bar/car", + }, + } + r2 := &http.Request{ + URL: &url.URL{ + Path: "foo//bar/car/", + }, + } + r3 := &http.Request{ + URL: &url.URL{ + Path: "", + }, + } + r4 := &http.Request{ + URL: &url.URL{ + Path: "////", + }, + } + normalize(r1) + normalize(r2) + normalize(r3) + normalize(r4) + + assert.Equal(t, "/foo/bar/car", r1.URL.Path) + assert.Equal(t, "/foo/bar/car", r2.URL.Path) + assert.Equal(t, "/", r3.URL.Path) + assert.Equal(t, "/", r4.URL.Path) + +} diff --git a/handlers/incoming_test.go b/handlers/incoming_test.go new file mode 100644 index 0000000..0d11aed --- /dev/null +++ b/handlers/incoming_test.go @@ -0,0 +1,157 @@ +// Copyright (c) 2017 Northwestern Mutual. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package handlers + +import ( + "context" + "encoding/json" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/northwesternmutual/kanali/config" + "github.com/northwesternmutual/kanali/metrics" + "github.com/northwesternmutual/kanali/spec" + "github.com/northwesternmutual/kanali/tracer" + "github.com/northwesternmutual/kanali/utils" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/pkg/api/v1" +) + +func TestIncomingRequest(t *testing.T) { + mock, _ := json.Marshal([]spec.Route{ + { + Route: "/foo", + Code: 200, + Method: "GET", + Body: map[string]interface{}{ + "foo": "bar", + }, + }, + }) + spec.MockResponseStore.Set(v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mockConfigMap", + Namespace: "foo", + }, + Data: map[string]string{ + "response": string(mock), + }, + }) + spec.ProxyStore.Set(spec.APIProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "exampleAPIProxyOne", + Namespace: "foo", + }, + Spec: spec.APIProxySpec{ + Path: "/api/v1/accounts", + Target: "/", + Service: spec.Service{ + Name: "dummyService", + Port: 8080, + }, + Mock: &spec.Mock{ + ConfigMapName: "mockConfigMap", + }, + }, + }) + metrics := &metrics.Metrics{} + writer := httptest.NewRecorder() + request, _ := http.NewRequest("GET", "http://foo.bar.com/api/v1/accounts/foo", nil) + mockTracer := mocktracer.New() + + viper.SetDefault(config.FlagProxyEnableMockResponses.GetLong(), true) + defer viper.Reset() + span := mockTracer.StartSpan("test span") + err := IncomingRequest(context.Background(), &spec.APIProxy{}, metrics, writer, request, span) + span.Finish() + assert.Nil(t, err) + assert.Equal(t, mockTracer.FinishedSpans()[0].Tags()[tracer.KanaliProxyName], "exampleAPIProxyOne") + assert.Equal(t, mockTracer.FinishedSpans()[0].Tags()[tracer.KanaliProxyNamespace], "foo") + assert.Equal(t, (*metrics)[0].Name, "proxy_name") + assert.Equal(t, (*metrics)[0].Value, "exampleAPIProxyOne") + assert.Equal(t, (*metrics)[1].Name, "proxy_namespace") + assert.Equal(t, (*metrics)[1].Value, "foo") + assert.Equal(t, (*metrics)[2].Name, "http_response_code") + assert.Equal(t, (*metrics)[2].Value, "200") + + response := writer.Result() + assert.Equal(t, response.StatusCode, 200) + assert.Equal(t, response.Header, http.Header{"Content-Type": []string{"application/json"}}) + body, _ := ioutil.ReadAll(response.Body) + assert.Equal(t, string(body), `{"foo":"bar"}`) + + viper.SetDefault(config.FlagProxyEnableMockResponses.GetLong(), false) + span = mockTracer.StartSpan("test span") + defer span.Finish() + err = IncomingRequest(context.Background(), &spec.APIProxy{}, metrics, writer, request, span) + statusErr := err.(utils.Error) + assert.Equal(t, statusErr.Status(), 404) + assert.Equal(t, statusErr.Error(), "no matching services") +} + +func TestMockIsDefined(t *testing.T) { + spec.ProxyStore.Set(spec.APIProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "exampleAPIProxyOne", + Namespace: "foo", + }, + Spec: spec.APIProxySpec{ + Path: "/api/v1/accounts", + Target: "/", + Service: spec.Service{ + Name: "dummyService", + Port: 8080, + }, + Mock: &spec.Mock{ + ConfigMapName: "mockConfigMap", + }, + }, + }) + spec.ProxyStore.Set(spec.APIProxy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "exampleAPIProxyTwo", + Namespace: "foo", + }, + Spec: spec.APIProxySpec{ + Path: "/api/v1/properties", + Target: "/", + Service: spec.Service{ + Name: "dummyService", + Port: 8080, + }, + }, + }) + + result := mockIsDefined("/api/v1/accounts/foo") + assert.True(t, result) + + result = mockIsDefined("/api/v1/clients/foo") + assert.False(t, result) + + result = mockIsDefined("/api/v1/properties/foo") + assert.False(t, result) + +} diff --git a/handlers/logger_test.go b/handlers/logger_test.go new file mode 100644 index 0000000..9729fa6 --- /dev/null +++ b/handlers/logger_test.go @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Northwestern Mutual. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package handlers + +import ( + "bytes" + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + "strings" + "testing" + + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestLogger(t *testing.T) { + server := &http.Server{Addr: "127.0.0.1:40123", Handler: Logger(Handler{InfluxController: nil, H: IncomingRequest})} + listener, _ := net.Listen("tcp4", "127.0.0.1:40123") + go server.Serve(listener) + defer server.Close() + + writer := new(bytes.Buffer) + logrus.SetOutput(writer) + resp, err := http.Get("http://127.0.0.1:40123/") + logrus.SetOutput(os.Stdout) + assert.Nil(t, err) + assert.Equal(t, resp.Header.Get("Content-Type"), "application/json") + + body, _ := ioutil.ReadAll(resp.Body) + assert.Equal(t, string(body), fmt.Sprintf("%s\n", `{"code":404,"msg":"proxy not found"}`)) + assert.Equal(t, resp.StatusCode, 404) + + logOutput := writer.String() + assert.True(t, strings.Contains(logOutput, `msg="proxy not found"`)) + assert.True(t, strings.Contains(logOutput, `msg="request details"`)) + assert.True(t, strings.Contains(logOutput, `level=error`)) + assert.True(t, strings.Contains(logOutput, `client ip=127.0.0.1`)) + assert.True(t, strings.Contains(logOutput, `method=GET`)) + assert.True(t, strings.Contains(logOutput, `uri="/"`)) +}